Merge branch 'development'

Conflicts:
	libraries/MySensors/Config.h
	libraries/MySensors/Version.h
This commit is contained in:
Henrik Ekblad
2014-08-29 20:48:20 +02:00
291 changed files with 9768 additions and 39496 deletions
+259
View File
@@ -0,0 +1,259 @@
/*
Emon.cpp - Library for openenergymonitor
Created by Trystan Lea, April 27 2010
GNU GPL
modified to use up to 12 bits ADC resolution (ex. Arduino Due)
by boredman@boredomprojects.net 26.12.2013
*/
//#include "WProgram.h" un-comment for use on older versions of Arduino IDE
#include "EmonLib.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltage(int _inPinV, double _VCAL, double _PHASECAL)
{
inPinV = _inPinV;
VCAL = _VCAL;
PHASECAL = _PHASECAL;
}
void EnergyMonitor::current(int _inPinI, double _ICAL)
{
inPinI = _inPinI;
ICAL = _ICAL;
}
//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors based on emontx pin map
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL)
{
inPinV = 2;
VCAL = _VCAL;
PHASECAL = _PHASECAL;
}
void EnergyMonitor::currentTX(int _channel, double _ICAL)
{
if (_channel == 1) inPinI = 3;
if (_channel == 2) inPinI = 0;
if (_channel == 3) inPinI = 1;
ICAL = _ICAL;
}
//--------------------------------------------------------------------------------------
// emon_calc procedure
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kwh increment
// From a sample window of the mains AC voltage and current.
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
//--------------------------------------------------------------------------------------
void EnergyMonitor::calcVI(int crossings, int timeout)
{
#if defined emonTxV3
int SUPPLYVOLTAGE=3300;
#else
int SUPPLYVOLTAGE = readVcc();
#endif
int crossCount = 0; //Used to measure number of times threshold is crossed.
int numberOfSamples = 0; //This is now incremented
//-------------------------------------------------------------------------------------------------------------------------
// 1) Waits for the waveform to be close to 'zero' (500 adc) part in sin curve.
//-------------------------------------------------------------------------------------------------------------------------
boolean st=false; //an indicator to exit the while loop
unsigned long start = millis(); //millis()-start makes sure it doesnt get stuck in the loop if there is an error.
while(st==false) //the while loop...
{
startV = analogRead(inPinV); //using the voltage waveform
if ((startV < (ADC_COUNTS/2+50)) && (startV > (ADC_COUNTS/2-50))) st=true; //check its within range
if ((millis()-start)>timeout) st = true;
}
//-------------------------------------------------------------------------------------------------------------------------
// 2) Main measurment loop
//-------------------------------------------------------------------------------------------------------------------------
start = millis();
while ((crossCount < crossings) && ((millis()-start)<timeout))
{
numberOfSamples++; //Count number of times looped.
lastSampleV=sampleV; //Used for digital high pass filter
lastSampleI=sampleI; //Used for digital high pass filter
lastFilteredV = filteredV; //Used for offset removal
lastFilteredI = filteredI; //Used for offset removal
//-----------------------------------------------------------------------------
// A) Read in raw voltage and current samples
//-----------------------------------------------------------------------------
sampleV = analogRead(inPinV); //Read in raw voltage signal
sampleI = analogRead(inPinI); //Read in raw current signal
//-----------------------------------------------------------------------------
// B) Apply digital high pass filters to remove 2.5V DC offset (centered on 0V).
//-----------------------------------------------------------------------------
filteredV = 0.996*(lastFilteredV+(sampleV-lastSampleV));
filteredI = 0.996*(lastFilteredI+(sampleI-lastSampleI));
//-----------------------------------------------------------------------------
// C) Root-mean-square method voltage
//-----------------------------------------------------------------------------
sqV= filteredV * filteredV; //1) square voltage values
sumV += sqV; //2) sum
//-----------------------------------------------------------------------------
// D) Root-mean-square method current
//-----------------------------------------------------------------------------
sqI = filteredI * filteredI; //1) square current values
sumI += sqI; //2) sum
//-----------------------------------------------------------------------------
// E) Phase calibration
//-----------------------------------------------------------------------------
phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
//-----------------------------------------------------------------------------
// F) Instantaneous power calc
//-----------------------------------------------------------------------------
instP = phaseShiftedV * filteredI; //Instantaneous Power
sumP +=instP; //Sum
//-----------------------------------------------------------------------------
// G) Find the number of times the voltage has crossed the initial voltage
// - every 2 crosses we will have sampled 1 wavelength
// - so this method allows us to sample an integer number of half wavelengths which increases accuracy
//-----------------------------------------------------------------------------
lastVCross = checkVCross;
if (sampleV > startV) checkVCross = true;
else checkVCross = false;
if (numberOfSamples==1) lastVCross = checkVCross;
if (lastVCross != checkVCross) crossCount++;
}
//-------------------------------------------------------------------------------------------------------------------------
// 3) Post loop calculations
//-------------------------------------------------------------------------------------------------------------------------
//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coeficients applied.
double V_RATIO = VCAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS));
Vrms = V_RATIO * sqrt(sumV / numberOfSamples);
double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS));
Irms = I_RATIO * sqrt(sumI / numberOfSamples);
//Calculation power values
realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
apparentPower = Vrms * Irms;
powerFactor=realPower / apparentPower;
//Reset accumulators
sumV = 0;
sumI = 0;
sumP = 0;
//--------------------------------------------------------------------------------------
}
//--------------------------------------------------------------------------------------
double EnergyMonitor::calcIrms(int NUMBER_OF_SAMPLES)
{
#if defined emonTxV3
int SUPPLYVOLTAGE=3300;
#else
int SUPPLYVOLTAGE = readVcc();
#endif
for (int n = 0; n < NUMBER_OF_SAMPLES; n++)
{
lastSampleI = sampleI;
sampleI = analogRead(inPinI);
lastFilteredI = filteredI;
filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI);
// Root-mean-square method current
// 1) square current values
sqI = filteredI * filteredI;
// 2) sum
sumI += sqI;
}
double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS));
Irms = I_RATIO * sqrt(sumI / NUMBER_OF_SAMPLES);
//Reset accumulators
sumI = 0;
//--------------------------------------------------------------------------------------
return Irms;
}
void EnergyMonitor::serialprint()
{
Serial.print(realPower);
Serial.print(' ');
Serial.print(apparentPower);
Serial.print(' ');
Serial.print(Vrms);
Serial.print(' ');
Serial.print(Irms);
Serial.print(' ');
Serial.print(powerFactor);
Serial.println(' ');
delay(100);
}
//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino
//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
long EnergyMonitor::readVcc() {
long result;
//not used on emonTx V3 - as Vcc is always 3.3V - eliminates bandgap error and need for calibration http://harizanov.com/2013/09/thoughts-on-avr-adc-accuracy/
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328P__)
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#endif
#if defined(__AVR__)
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result; //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186
return result;
#elif defined(__arm__)
return (3300); //Arduino Due
#else
return (3300); //Guess that other un-supported architectures will be running a 3.3V!
#endif
}
+89
View File
@@ -0,0 +1,89 @@
/*
Emon.h - Library for openenergymonitor
Created by Trystan Lea, April 27 2010
GNU GPL
modified to use up to 12 bits ADC resolution (ex. Arduino Due)
by boredman@boredomprojects.net 26.12.2013
*/
#ifndef EmonLib_h
#define EmonLib_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
// to enable 12-bit ADC resolution on Arduino Due,
// include the following line in main sketch inside setup() function:
// analogReadResolution(ADC_BITS);
// otherwise will default to 10 bits, as in regular Arduino-based boards.
#if defined(__arm__)
#define ADC_BITS 12
#else
#define ADC_BITS 10
#endif
#define ADC_COUNTS (1<<ADC_BITS)
class EnergyMonitor
{
public:
void voltage(int _inPinV, double _VCAL, double _PHASECAL);
void current(int _inPinI, double _ICAL);
void voltageTX(double _VCAL, double _PHASECAL);
void currentTX(int _channel, double _ICAL);
void calcVI(int crossings, int timeout);
double calcIrms(int NUMBER_OF_SAMPLES);
void serialprint();
long readVcc();
//Useful value variables
double realPower,
apparentPower,
powerFactor,
Vrms,
Irms;
private:
//Set Voltage and current input pins
int inPinV;
int inPinI;
//Calibration coeficients
//These need to be set in order to obtain accurate results
double VCAL;
double ICAL;
double PHASECAL;
//--------------------------------------------------------------------------------------
// Variable declaration for emon_calc procedure
//--------------------------------------------------------------------------------------
int lastSampleV,sampleV; //sample_ holds the raw analog read value, lastSample_ holds the last sample
int lastSampleI,sampleI;
double lastFilteredV,filteredV; //Filtered_ is the raw analog value minus the DC offset
double lastFilteredI, filteredI;
double phaseShiftedV; //Holds the calibrated phase shifted voltage.
double sqV,sumV,sqI,sumI,instP,sumP; //sq = squared, sum = Sum, inst = instantaneous
int startV; //Instantaneous voltage at start of sample window.
boolean lastVCross, checkVCross; //Used to measure number of times threshold is crossed.
int crossCount; // ''
};
#endif
+25
View File
@@ -0,0 +1,25 @@
_ _ _
| | (_) |
___ _ __ ___ ___ _ __ | | _| |__
/ _ \ '_ ` _ \ / _ \| '_ \| | | | '_ \
| __/ | | | | | (_) | | | | |____| | |_) |
\___|_| |_| |_|\___/|_| |_|______|_|_.__/
Arduino Energy Monitoring Library - compatible with Arduino 1.0
*****************************************************************
Designed for use with emonTx: http://openenergymonitor.org/emon/Modules
Download to Arduino IDE 'libraries' folder. Restart of IDE required.
Git Clone and Git Pull can be easily used to keep the library up-to-date and manage changes.
JeeLabs has done a good post on the topic: http://jeelabs.org/2011/12/29/out-with-the-old-in-with-the-new/
Update: 5th January 2014: Support Added for Arduino Due (ARM Cortex-M3, 12-bit ADC) by icboredman.
To enable this feature on Arduino Due, add the following statement to setup() function in main sketch:
analogReadResolution(ADC_BITS); This will set ADC_BITS to 12 (Arduino Due), EmonLib will otherwise default to 10 analogReadResolution(ADC_BITS);.
See blog post on using Arduino Due as energy monitor: http://boredomprojects.net/index.php/projects/home-energy-monitor
@@ -0,0 +1,20 @@
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
#include "EmonLib.h" // Include Emon Library
EnergyMonitor emon1; // Create an instance
void setup()
{
Serial.begin(9600);
emon1.current(1, 111.1); // Current: input pin, calibration.
}
void loop()
{
double Irms = emon1.calcIrms(1480); // Calculate Irms only
Serial.print(Irms*230.0); // Apparent power
Serial.print(" ");
Serial.println(Irms); // Irms
}
@@ -0,0 +1,24 @@
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
#include "EmonLib.h" // Include Emon Library
EnergyMonitor emon1; // Create an instance
void setup()
{
Serial.begin(9600);
emon1.voltage(2, 234.26, 1.7); // Voltage: input pin, calibration, phase_shift
emon1.current(1, 111.1); // Current: input pin, calibration.
}
void loop()
{
emon1.calcVI(20,2000); // Calculate all. No.of half wavelengths (crossings), time-out
emon1.serialprint(); // Print out all variables (realpower, apparent power, Vrms, Irms, power factor)
float realPower = emon1.realPower; //extract Real Power into variable
float apparentPower = emon1.apparentPower; //extract Apparent Power into variable
float powerFActor = emon1.powerFactor; //extract Power Factor into Variable
float supplyVoltage = emon1.Vrms; //extract Vrms into Variable
float Irms = emon1.Irms; //extract Irms into Variable
}
@@ -0,0 +1,31 @@
// **** INCLUDES *****
#include "LowPower.h"
void setup()
{
// No setup is required for this library
}
void loop()
{
// Enter idle state for 8 s with the rest of peripherals turned off
// Each microcontroller comes with different number of peripherals
// Comment off line of code where necessary
// ATmega328P, ATmega168
LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF,
SPI_OFF, USART0_OFF, TWI_OFF);
// ATmega32U4
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF, TIMER3_OFF, TIMER1_OFF,
// TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
// ATmega2560
//LowPower.idle(SLEEP_8S, ADC_OFF, TIMER5_OFF, TIMER4_OFF, TIMER3_OFF,
// TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART3_OFF,
// USART2_OFF, USART1_OFF, USART0_OFF, TWI_OFF);
// Do something here
// Example: Read sensor, data logging, data transmission.
}
@@ -0,0 +1,33 @@
// **** INCLUDES *****
#include "LowPower.h"
// Use pin 2 as wake up pin
const int wakeUpPin = 2;
void wakeUp()
{
// Just a handler for the pin interrupt.
}
void setup()
{
// Configure wake up pin as input.
// This will consumes few uA of current.
pinMode(wakeUpPin, INPUT);
}
void loop()
{
// Allow wake up pin to trigger interrupt on low.
attachInterrupt(0, wakeUp, LOW);
// Enter power down state with ADC and BOD module disabled.
// Wake up when wake up pin is low.
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
// Disable external pin interrupt on wake up pin.
detachInterrupt(0);
// Do something here
// Example: Read sensor, data logging, data transmission.
}
@@ -0,0 +1,16 @@
// **** INCLUDES *****
#include "LowPower.h"
void setup()
{
// No setup is required for this library
}
void loop()
{
// Enter power down state for 8 s with ADC and BOD module disabled
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
// Do something here
// Example: Read sensor, data logging, data transmission.
}
+856
View File
@@ -0,0 +1,856 @@
/*******************************************************************************
* LowPower Library
* Version: 1.30
* Date: 22-05-2013
* Company: Rocket Scream Electronics
* Website: www.rocketscream.com
*
* This is a lightweight low power library for Arduino. Please check our wiki
* (www.rocketscream.com/wiki) for more information on using this piece of
* library.
*
* This library is licensed under Creative Commons Attribution-ShareAlike 3.0
* Unported License.
*
* Revision Description
* ======== ===========
* 1.30 Added support for ATMega168, ATMega2560, ATMega1280 & ATMega32U4.
* Tested to work with Arduino IDE 1.0.1 - 1.0.4.
* 1.20 Remove typo error in idle method for checking whether Timer 0 was
* turned off.
* Remove dependecy on WProgram.h which is not required.
* Tested to work with Arduino IDE 1.0.
* 1.10 Added #ifndef for sleep_bod_disable() for compatibility with future
* Arduino IDE release.
* 1.00 Initial public release.
*******************************************************************************/
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include "LowPower.h"
// Only Pico Power devices can change BOD settings through software
#if defined __AVR_ATmega328P__
#ifndef sleep_bod_disable
#define sleep_bod_disable() \
do { \
unsigned char tempreg; \
__asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
"ori %[tempreg], %[bods_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" "\n\t" \
"andi %[tempreg], %[not_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" \
: [tempreg] "=&d" (tempreg) \
: [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
[bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
[not_bodse] "i" (~_BV(BODSE))); \
} while (0)
#endif
#endif
#define lowPowerBodOn(mode) \
do { \
set_sleep_mode(mode); \
cli(); \
sleep_enable(); \
sei(); \
sleep_cpu(); \
sleep_disable(); \
sei(); \
} while (0);
// Only Pico Power devices can change BOD settings through software
#if defined __AVR_ATmega328P__
#define lowPowerBodOff(mode)\
do { \
set_sleep_mode(mode); \
cli(); \
sleep_enable(); \
sleep_bod_disable(); \
sei(); \
sleep_cpu(); \
sleep_disable(); \
sei(); \
} while (0);
#endif
// Some macros is still missing from AVR GCC distribution for ATmega32U4
#if defined __AVR_ATmega32U4__
// Timer 4 PRR bit is currently not defined in iom32u4.h
#ifndef PRTIM4
#define PRTIM4 4
#endif
// Timer 4 power reduction macro is not defined currently in power.h
#ifndef power_timer4_disable
#define power_timer4_disable() (PRR1 |= (uint8_t)(1 << PRTIM4))
#endif
#ifndef power_timer4_enable
#define power_timer4_enable() (PRR1 &= (uint8_t)~(1 << PRTIM4))
#endif
#endif
/*******************************************************************************
* Name: idle
* Description: Putting ATmega328P/168 into idle state. Please make sure you
* understand the implication and result of disabling module.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control:
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
* 4. timer1 Timer 1 module disable control:
* (a) TIMER1_OFF - Turn off Timer 1 module
* (b) TIMER1_ON - Leave Timer 1 module in its default state
*
* 5. timer0 Timer 0 module disable control:
* (a) TIMER0_OFF - Turn off Timer 0 module
* (b) TIMER0_ON - Leave Timer 0 module in its default state
*
* 6. spi SPI module disable control:
* (a) SPI_OFF - Turn off SPI module
* (b) SPI_ON - Leave SPI module in its default state
*
* 7. usart0 USART0 module disable control:
* (a) USART0_OFF - Turn off USART0 module
* (b) USART0_ON - Leave USART0 module in its default state
*
* 8. twi TWI module disable control:
* (a) TWI_OFF - Turn off TWI module
* (b) TWI_ON - Leave TWI module in its default state
*
*******************************************************************************/
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__)
void LowPowerClass::idle(period_t period, adc_t adc, timer2_t timer2,
timer1_t timer1, timer0_t timer0,
spi_t spi, usart0_t usart0, twi_t twi)
{
// Temporary clock source variable
unsigned char clockSource = 0;
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
power_timer2_disable();
}
if (adc == ADC_OFF)
{
ADCSRA &= ~(1 << ADEN);
power_adc_disable();
}
if (timer1 == TIMER1_OFF) power_timer1_disable();
if (timer0 == TIMER0_OFF) power_timer0_disable();
if (spi == SPI_OFF) power_spi_disable();
if (usart0 == USART0_OFF) power_usart0_disable();
if (twi == TWI_OFF) power_twi_disable();
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_IDLE);
if (adc == ADC_OFF)
{
power_adc_enable();
ADCSRA |= (1 << ADEN);
}
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
power_timer2_enable();
}
if (timer1 == TIMER1_OFF) power_timer1_enable();
if (timer0 == TIMER0_OFF) power_timer0_enable();
if (spi == SPI_OFF) power_spi_enable();
if (usart0 == USART0_OFF) power_usart0_enable();
if (twi == TWI_OFF) power_twi_enable();
}
#endif
/*******************************************************************************
* Name: idle
* Description: Putting ATmega32U4 into idle state. Please make sure you
* understand the implication and result of disabling module.
* Take note that Timer 2 is not available and USART0
* is replaced with USART1 on ATmega32U4.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control:
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer4 Timer 4 module disable control:
* (a) TIMER4_OFF - Turn off Timer 4 module
* (b) TIMER4_ON - Leave Timer 4 module in its default state
*
* 4. timer3 Timer 3 module disable control:
* (a) TIMER3_OFF - Turn off Timer 3 module
* (b) TIMER3_ON - Leave Timer 3 module in its default state
*
* 5. timer1 Timer 1 module disable control:
* (a) TIMER1_OFF - Turn off Timer 1 module
* (b) TIMER1_ON - Leave Timer 1 module in its default state
*
* 6. timer0 Timer 0 module disable control:
* (a) TIMER0_OFF - Turn off Timer 0 module
* (b) TIMER0_ON - Leave Timer 0 module in its default state
*
* 7. spi SPI module disable control:
* (a) SPI_OFF - Turn off SPI module
* (b) SPI_ON - Leave SPI module in its default state
*
* 8. usart1 USART1 module disable control:
* (a) USART1_OFF - Turn off USART1 module
* (b) USART1_ON - Leave USART1 module in its default state
*
* 9. twi TWI module disable control:
* (a) TWI_OFF - Turn off TWI module
* (b) TWI_ON - Leave TWI module in its default state
*
* 10.usb USB module disable control:
* (a) USB_OFF - Turn off USB module
* (b) USB_ON - Leave USB module in its default state
*******************************************************************************/
#if defined __AVR_ATmega32U4__
void LowPowerClass::idle(period_t period, adc_t adc,
timer4_t timer4, timer3_t timer3,
timer1_t timer1, timer0_t timer0,
spi_t spi, usart1_t usart1, twi_t twi, usb_t usb)
{
if (adc == ADC_OFF)
{
ADCSRA &= ~(1 << ADEN);
power_adc_disable();
}
if (timer4 == TIMER4_OFF) power_timer4_disable();
if (timer3 == TIMER3_OFF) power_timer3_disable();
if (timer1 == TIMER1_OFF) power_timer1_disable();
if (timer0 == TIMER0_OFF) power_timer0_disable();
if (spi == SPI_OFF) power_spi_disable();
if (usart1 == USART1_OFF) power_usart1_disable();
if (twi == TWI_OFF) power_twi_disable();
if (usb == USB_OFF) power_usb_disable();
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_IDLE);
if (adc == ADC_OFF)
{
power_adc_enable();
ADCSRA |= (1 << ADEN);
}
if (timer4 == TIMER4_OFF) power_timer4_enable();
if (timer3 == TIMER3_OFF) power_timer3_enable();
if (timer1 == TIMER1_OFF) power_timer1_enable();
if (timer0 == TIMER0_OFF) power_timer0_enable();
if (spi == SPI_OFF) power_spi_enable();
if (usart1 == USART1_OFF) power_usart1_enable();
if (twi == TWI_OFF) power_twi_enable();
if (usb == USB_OFF) power_usb_enable();
}
#endif
/*******************************************************************************
* Name: idle
* Description: Putting ATmega2560 & ATmega1280 into idle state. Please make sure
* you understand the implication and result of disabling module.
* Take note that extra Timer 5, 4, 3 compared to an ATmega328P/168.
* Also take note that extra USART 3, 2, 1 compared to an
* ATmega328P/168.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control:
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer5 Timer 5 module disable control:
* (a) TIMER5_OFF - Turn off Timer 5 module
* (b) TIMER5_ON - Leave Timer 5 module in its default state
*
* 4. timer4 Timer 4 module disable control:
* (a) TIMER4_OFF - Turn off Timer 4 module
* (b) TIMER4_ON - Leave Timer 4 module in its default state
*
* 5. timer3 Timer 3 module disable control:
* (a) TIMER3_OFF - Turn off Timer 3 module
* (b) TIMER3_ON - Leave Timer 3 module in its default state
*
* 6. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
* 7. timer1 Timer 1 module disable control:
* (a) TIMER1_OFF - Turn off Timer 1 module
* (b) TIMER1_ON - Leave Timer 1 module in its default state
*
* 8. timer0 Timer 0 module disable control:
* (a) TIMER0_OFF - Turn off Timer 0 module
* (b) TIMER0_ON - Leave Timer 0 module in its default state
*
* 9. spi SPI module disable control:
* (a) SPI_OFF - Turn off SPI module
* (b) SPI_ON - Leave SPI module in its default state
*
* 10.usart3 USART3 module disable control:
* (a) USART3_OFF - Turn off USART3 module
* (b) USART3_ON - Leave USART3 module in its default state
*
* 11.usart2 USART2 module disable control:
* (a) USART2_OFF - Turn off USART2 module
* (b) USART2_ON - Leave USART2 module in its default state
*
* 12.usart1 USART1 module disable control:
* (a) USART1_OFF - Turn off USART1 module
* (b) USART1_ON - Leave USART1 module in its default state
*
* 13.usart0 USART0 module disable control:
* (a) USART0_OFF - Turn off USART0 module
* (b) USART0_ON - Leave USART0 module in its default state
*
* 14.twi TWI module disable control:
* (a) TWI_OFF - Turn off TWI module
* (b) TWI_ON - Leave TWI module in its default state
*
*******************************************************************************/
#if defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__)
void LowPowerClass::idle(period_t period, adc_t adc, timer5_t timer5,
timer4_t timer4, timer3_t timer3, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart3_t usart3, usart2_t usart2, usart1_t usart1,
usart0_t usart0, twi_t twi)
{
// Temporary clock source variable
unsigned char clockSource = 0;
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
power_timer2_disable();
}
if (adc == ADC_OFF)
{
ADCSRA &= ~(1 << ADEN);
power_adc_disable();
}
if (timer5 == TIMER5_OFF) power_timer5_disable();
if (timer4 == TIMER4_OFF) power_timer4_disable();
if (timer3 == TIMER3_OFF) power_timer3_disable();
if (timer1 == TIMER1_OFF) power_timer1_disable();
if (timer0 == TIMER0_OFF) power_timer0_disable();
if (spi == SPI_OFF) power_spi_disable();
if (usart3 == USART3_OFF) power_usart3_disable();
if (usart2 == USART2_OFF) power_usart2_disable();
if (usart1 == USART1_OFF) power_usart1_disable();
if (usart0 == USART0_OFF) power_usart0_disable();
if (twi == TWI_OFF) power_twi_disable();
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_IDLE);
if (adc == ADC_OFF)
{
power_adc_enable();
ADCSRA |= (1 << ADEN);
}
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
power_timer2_enable();
}
if (timer5 == TIMER5_OFF) power_timer5_enable();
if (timer4 == TIMER4_OFF) power_timer4_enable();
if (timer3 == TIMER3_OFF) power_timer3_enable();
if (timer1 == TIMER1_OFF) power_timer1_enable();
if (timer0 == TIMER0_OFF) power_timer0_enable();
if (spi == SPI_OFF) power_spi_enable();
if (usart3 == USART3_OFF) power_usart3_enable();
if (usart2 == USART2_OFF) power_usart2_enable();
if (usart1 == USART1_OFF) power_usart1_enable();
if (usart0 == USART0_OFF) power_usart0_enable();
if (twi == TWI_OFF) power_twi_enable();
}
#endif
/*******************************************************************************
* Name: adcNoiseReduction
* Description: Putting microcontroller into ADC noise reduction state. This is
* a very useful state when using the ADC to achieve best and low
* noise signal.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
*******************************************************************************/
void LowPowerClass::adcNoiseReduction(period_t period, adc_t adc,
timer2_t timer2)
{
// Temporary clock source variable
unsigned char clockSource = 0;
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
}
#endif
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_ADC);
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
}
#endif
}
/*******************************************************************************
* Name: powerDown
* Description: Putting microcontroller into power down state. This is
* the lowest current consumption state. Use this together with
* external pin interrupt to wake up through external event
* triggering (example: RTC clockout pin, SD card detect pin).
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
*******************************************************************************/
void LowPowerClass::powerDown(period_t period, adc_t adc, bod_t bod)
{
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_PWR_DOWN);
#else
lowPowerBodOn(SLEEP_MODE_PWR_DOWN);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_PWR_DOWN);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
}
/*******************************************************************************
* Name: powerSave
* Description: Putting microcontroller into power save state. This is
* the lowest current consumption state after power down.
* Use this state together with an external 32.768 kHz crystal (but
* 8/16 MHz crystal/resonator need to be removed) to provide an
* asynchronous clock source to Timer 2. Please take note that
* Timer 2 is also used by the Arduino core for PWM operation.
* Please refer to wiring.c for explanation. Removal of the external
* 8/16 MHz crystal/resonator requires the microcontroller to run
* on its internal RC oscillator which is not so accurate for time
* critical operation.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
* 4. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
*******************************************************************************/
void LowPowerClass::powerSave(period_t period, adc_t adc, bod_t bod,
timer2_t timer2)
{
// Temporary clock source variable
unsigned char clockSource = 0;
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
}
#endif
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_PWR_SAVE);
#else
lowPowerBodOn(SLEEP_MODE_PWR_SAVE);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_PWR_SAVE);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
}
#endif
}
/*******************************************************************************
* Name: powerStandby
* Description: Putting microcontroller into power standby state.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
*******************************************************************************/
void LowPowerClass::powerStandby(period_t period, adc_t adc, bod_t bod)
{
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_STANDBY);
#else
lowPowerBodOn(SLEEP_MODE_STANDBY);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_STANDBY);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
}
/*******************************************************************************
* Name: powerExtStandby
* Description: Putting microcontroller into power extended standby state. This
* is different from the power standby state as it has the
* capability to run Timer 2 asynchronously.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
* 4. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
*******************************************************************************/
void LowPowerClass::powerExtStandby(period_t period, adc_t adc, bod_t bod,
timer2_t timer2)
{
// Temporary clock source variable
unsigned char clockSource = 0;
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
}
#endif
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_EXT_STANDBY);
#else
lowPowerBodOn(SLEEP_MODE_EXT_STANDBY);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_EXT_STANDBY);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
}
#endif
}
/*******************************************************************************
* Name: ISR (WDT_vect)
* Description: Watchdog Timer interrupt service routine. This routine is
* required to allow automatic WDIF and WDIE bit clearance in
* hardware.
*
*******************************************************************************/
ISR (WDT_vect)
{
// WDIE & WDIF is cleared in hardware upon entering this ISR
wdt_disable();
}
LowPowerClass LowPower;
+137
View File
@@ -0,0 +1,137 @@
#ifndef LowPower_h
#define LowPower_h
enum period_t
{
SLEEP_15Ms,
SLEEP_30MS,
SLEEP_60MS,
SLEEP_120MS,
SLEEP_250MS,
SLEEP_500MS,
SLEEP_1S,
SLEEP_2S,
SLEEP_4S,
SLEEP_8S,
SLEEP_FOREVER
};
enum bod_t
{
BOD_OFF,
BOD_ON
};
enum adc_t
{
ADC_OFF,
ADC_ON
};
enum timer5_t
{
TIMER5_OFF,
TIMER5_ON
};
enum timer4_t
{
TIMER4_OFF,
TIMER4_ON
};
enum timer3_t
{
TIMER3_OFF,
TIMER3_ON
};
enum timer2_t
{
TIMER2_OFF,
TIMER2_ON
};
enum timer1_t
{
TIMER1_OFF,
TIMER1_ON
};
enum timer0_t
{
TIMER0_OFF,
TIMER0_ON
};
enum spi_t
{
SPI_OFF,
SPI_ON
};
enum usart0_t
{
USART0_OFF,
USART0_ON
};
enum usart1_t
{
USART1_OFF,
USART1_ON
};
enum usart2_t
{
USART2_OFF,
USART2_ON
};
enum usart3_t
{
USART3_OFF,
USART3_ON
};
enum twi_t
{
TWI_OFF,
TWI_ON
};
enum usb_t
{
USB_OFF,
USB_ON
};
class LowPowerClass
{
public:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__)
void idle(period_t period, adc_t adc, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega2560__
void idle(period_t period, adc_t adc, timer5_t timer5,
timer4_t timer4, timer3_t timer3, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart3_t usart3, usart2_t usart2, usart1_t usart1,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega32U4__
void idle(period_t period, adc_t adc, timer4_t timer4, timer3_t timer3,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart1_t usart1, twi_t twi, usb_t usb);
#else
#error "Please ensure chosen MCU is either 328P, 32U4 or 2560."
#endif
void adcNoiseReduction(period_t period, adc_t adc, timer2_t timer2);
void powerDown(period_t period, adc_t adc, bod_t bod);
void powerSave(period_t period, adc_t adc, bod_t bod, timer2_t timer2);
void powerStandby(period_t period, adc_t adc, bod_t bod);
void powerExtStandby(period_t period, adc_t adc, bod_t bod, timer2_t timer2);
};
extern LowPowerClass LowPower;
#endif
+68
View File
@@ -0,0 +1,68 @@
#######################################
# Syntax Coloring Map LowPower
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
#######################################
# Methods and Functions (KEYWORD2)
#######################################
idle KEYWORD2
adcNoiseReduction KEYWORD2
powerDown KEYWORD2
powerSave KEYWORD2
powerStandby KEYWORD2
powerExtStandby KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
LowPower KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
SLEEP_15MS LITERAL1
SLEEP_30MS LITERAL1
SLEEP_60MS LITERAL1
SLEEP_120MS LITERAL1
SLEEP_250MS LITERAL1
SLEEP_500MS LITERAL1
SLEEP_1S LITERAL1
SLEEP_2S LITERAL1
SLEEP_4S LITERAL1
SLEEP_8S LITERAL1
SLEEP_FOREVER LITERAL1
ADC_OFF LITERAL1
ADC_ON LITERAL1
BOD_OFF LITERAL1
BOD_ON LITERAL1
TIMER4_OFF LITERAL1
TIMER4_ON LITERAL1
TIMER3_OFF LITERAL1
TIMER3_ON LITERAL1
TIMER2_OFF LITERAL1
TIMER2_ON LITERAL1
TIMER1_OFF LITERAL1
TIMER1_ON LITERAL1
TIMER0_OFF LITERAL1
TIMER0_ON LITERAL1
USART3_OFF LITERAL1
USART3_ON LITERAL1
USART2_OFF LITERAL1
USART2_ON LITERAL1
USART1_OFF LITERAL1
USART1_ON LITERAL1
USART0_OFF LITERAL1
USART0_ON LITERAL1
SPI_OFF LITERAL1
SPI_ON LITERAL1
TWI_OFF LITERAL1
TWI_ON LITERAL1
USB_OFF LITERAL1
USB_ON LITERAL1
@@ -1,42 +0,0 @@
char dummyvar; // to get Arduinoi IDE to include core headers properly
/*
MsTimer2 is a small and very easy to use library to interface Timer2 with
humans. It's called MsTimer2 because it "hardcodes" a resolution of 1
millisecond on timer2
For Details see: http://www.arduino.cc/playground/Main/MsTimer2
*/
#include <MsTimer2.h>
// Switch on LED on and off each half second
#if defined(ARDUINO) && ARDUINO >= 100
const int led_pin = LED_BUILTIN; // 1.0 built in LED pin var
#else
#if defined(CORE_LED0_PIN)
const int led_pin = CORE_LED0_PIN; // 3rd party LED pin define
#else
const int led_pin = 13; // default to pin 13
#endif
#endif
void flash()
{
static boolean output = HIGH;
digitalWrite(led_pin, output);
output = !output;
}
void setup()
{
pinMode(led_pin, OUTPUT);
MsTimer2::set(500, flash); // 500ms period
MsTimer2::start();
}
void loop()
{
}
-4
View File
@@ -1,4 +0,0 @@
MsTimer2 KEYWORD1
set KEYWORD2
start KEYWORD2
stop KEYWORD2
-17
View File
@@ -1,17 +0,0 @@
#ifndef Config_h
#define Config_h
/***
* Configure Sensor Network
*/
#define RF24_CHANNEL 76 //RF channel for the sensor net, 0-127
#define RF24_DATARATE RF24_2MBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps
#define RF24_PA_LEVEL RF24_PA_MAX //Senor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm
#define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway.
/***
* Enable/Disable debug logging
*/
//#define DEBUG
#endif
-260
View File
@@ -1,260 +0,0 @@
/*
The MySensors Arduino library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#include "Gateway.h"
Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time) : Relay(_cepin, _cspin) {
ledMode = false;
isRelay = true;
inclusionTime = _inclusion_time;
}
Gateway::Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : Relay(_cepin, _cspin) {
ledMode = true;
isRelay = true;
pinInclusion = _inclusion_pin;
inclusionTime = _inclusion_time;
pinRx = _rx;
pinTx = _tx;
pinEr = _er;
}
void Gateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) {
Serial.begin(BAUD_RATE);
if (inDataCallback != NULL) {
useWriteCallback = true;
dataCallback = inDataCallback;
} else {
useWriteCallback = false;
}
radioId = 0;
distance = 0;
inclusionMode = 0;
buttonTriggeredInclusion = false;
countRx = 0;
countTx = 0;
countErr = 0;
if (ledMode) {
// Setup led pins
pinMode(pinRx, OUTPUT);
pinMode(pinTx, OUTPUT);
pinMode(pinEr, OUTPUT);
digitalWrite(pinRx, LOW);
digitalWrite(pinTx, LOW);
digitalWrite(pinEr, LOW);
// Setup digital in that triggers inclusion mode
pinMode(pinInclusion, INPUT);
digitalWrite(pinInclusion, HIGH);
// Set initial state of leds
digitalWrite(pinRx, HIGH);
digitalWrite(pinTx, HIGH);
digitalWrite(pinEr, HIGH);
}
// Start up the radio library
setupRadio(paLevel, channel, dataRate);
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID);
RF24::startListening();
// Send startup log message on serial
serial(PSTR("0;0;%d;%d;Arduino startup complete.\n"), M_INTERNAL, I_LOG_MESSAGE);
}
boolean Gateway::isLedMode() {
return ledMode;
}
void Gateway::startInclusionInterrupt() {
buttonTriggeredInclusion = true;
}
void Gateway::checkButtonTriggeredInclusion() {
if (buttonTriggeredInclusion) {
// Ok, someone pressed the inclusion button on the gateway
// start inclusion mode for 1 munute.
serial(PSTR("0;0;%d;%d;Inclusion started by button.\n"), M_INTERNAL, I_LOG_MESSAGE);
buttonTriggeredInclusion = false;
setInclusionMode(true);
#ifdef DEBUG
sendChildren();
#endif
}
}
void Gateway::checkInclusionFinished() {
if (inclusionMode && millis()-inclusionStartTime>60000UL*inclusionTime) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
setInclusionMode(false);
}
}
void Gateway::parseAndSend(char *commandBuffer) {
boolean ok = false;
char *str, *p, *value=NULL;
int i = 0;
uint16_t sensorRadioId = 0;
uint8_t childId = 0;
uint8_t messageType = 0;
uint8_t type = 0;
// Extract command data coming on serial line
for (str = strtok_r(commandBuffer, ";", &p); // split using semicolon
str && i < 5; // loop while str is not null an max 5 times
str = strtok_r(NULL, ";", &p) // get subsequent tokens
) {
switch (i) {
case 0: // Radioid (destination)
sensorRadioId = atoi(str);
break;
case 1: // Childid
childId = atoi(str);
break;
case 2: // Message type
messageType = atoi(str);
break;
case 3: // Data type
type = atoi(str);
break;
case 4: // Variable value
value = str;
break;
}
i++;
}
if (sensorRadioId==GATEWAY_ADDRESS && messageType==M_INTERNAL) {
// Handle messages directed to gateway
if (type == I_VERSION) {
// Request for version
serial(PSTR("0;0;%d;%d;%s\n"),M_INTERNAL, I_VERSION, LIBRARY_VERSION);
} else if (type == I_INCLUSION_MODE) {
// Request to change inclusion mode
setInclusionMode(atoi(value) == 1);
}
} else {
txBlink(1);
ok = sendData(GATEWAY_ADDRESS, sensorRadioId, childId, messageType, type, value, strlen(value), false);
if (!ok) {
errBlink(1);
}
}
}
void Gateway::setInclusionMode(boolean newMode) {
if (newMode != inclusionMode)
inclusionMode = newMode;
// Send back mode change on serial line to ack command
serial(PSTR("0;0;%d;%d;%d\n"), M_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0);
if (inclusionMode) {
inclusionStartTime = millis();
}
}
// Override normal validate to add error blink if crc check fails
uint8_t Gateway::validate(uint8_t length) {
uint8_t res = Sensor::validate(length);
if (res == VALIDATE_BAD_CRC) {
errBlink(1);
}
return res;
}
void Gateway::processRadioMessage() {
if (messageAvailable()) {
// A new message was received from one of the sensors
message_s msg = getMessage();
if (msg.header.messageType == M_PRESENTATION && inclusionMode) {
rxBlink(3);
} else {
rxBlink(1);
}
// Pass along the message from sensors to serial line
serial(msg);
}
checkButtonTriggeredInclusion();
checkInclusionFinished();
}
void Gateway::serial(const char *fmt, ... ) {
va_list args;
va_start (args, fmt );
vsnprintf_P(serialBuffer, MAX_SEND_LENGTH, fmt, args);
va_end (args);
Serial.print(serialBuffer);
if (useWriteCallback) {
// We have a registered write callback (probably Ethernet)
dataCallback(serialBuffer);
}
}
void Gateway::serial(message_s msg) {
serial(PSTR("%d;%d;%d;%d;%s\n"),msg.header.from, msg.header.childId, msg.header.messageType, msg.header.type, msg.data);
}
void Gateway::ledTimersInterrupt() {
if(countRx && countRx != 255) {
// switch led on
digitalWrite(pinRx, LOW);
} else if(!countRx) {
// switching off
digitalWrite(pinRx, HIGH);
}
if(countRx != 255) { countRx--; }
if(countTx && countTx != 255) {
// switch led on
digitalWrite(pinTx, LOW);
} else if(!countTx) {
// switching off
digitalWrite(pinTx, HIGH);
}
if(countTx != 255) { countTx--; }
else if(inclusionMode) { countTx = 8; }
if(countErr && countErr != 255) {
// switch led on
digitalWrite(pinEr, LOW);
} else if(!countErr) {
// switching off
digitalWrite(pinEr, HIGH);
}
if(countErr != 255) { countErr--; }
}
void Gateway::rxBlink(uint8_t cnt) {
if(countRx == 255) { countRx = cnt; }
}
void Gateway::txBlink(uint8_t cnt) {
if(countTx == 255 && !inclusionMode) { countTx = cnt; }
}
void Gateway::errBlink(uint8_t cnt) {
if(countErr == 255) { countErr = cnt; }
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef MyConfig_h
#define MyConfig_h
/***
* Configure Sensor Network
*/
#define RF24_CHANNEL 76 //RF channel for the sensor net, 0-127
#define RF24_DATARATE RF24_250KBPS //RF24_250KBPS for 250kbs, RF24_1MBPS for 1Mbps, or RF24_2MBPS for 2Mbps
#define RF24_PA_LEVEL RF24_PA_MAX //Sensor PA Level == RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBM, and RF24_PA_MAX=0dBm
#define RF24_PA_LEVEL_GW RF24_PA_LEVEL //Gateway PA Level, defaults to Sensor net PA Level. Tune here if using an amplified nRF2401+ in your gateway.
#define BASE_RADIO_ID ((uint64_t)0xA8A8E1FC00LL) // This is also act as base value for sensor nodeId addresses. Change this (or channel) if you have more than one sensor network.
// MySensors online examples defaults
#define DEFAULT_CE_PIN 9
#define DEFAULT_CS_PIN 10
/***
* Enable/Disable debug logging
*/
#define DEBUG
#endif
+294
View File
@@ -0,0 +1,294 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#include "MyGateway.h"
#include "utility/MsTimer2.h"
#include "utility/PinChangeInt.h"
uint8_t pinRx;
uint8_t pinTx;
uint8_t pinEr;
boolean buttonTriggeredInclusion;
volatile uint8_t countRx;
volatile uint8_t countTx;
volatile uint8_t countErr;
boolean inclusionMode; // Keeps track on inclusion mode
MyGateway::MyGateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er) : MySensor(_cepin, _cspin) {
pinInclusion = _inclusion_pin;
inclusionTime = _inclusion_time;
pinRx = _rx;
pinTx = _tx;
pinEr = _er;
}
void MyGateway::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)(char *)) {
Serial.begin(BAUD_RATE);
repeaterMode = true;
isGateway = true;
setupRepeaterMode();
if (inDataCallback != NULL) {
useWriteCallback = true;
dataCallback = inDataCallback;
} else {
useWriteCallback = false;
}
nc.nodeId = 0;
nc.distance = 0;
inclusionMode = 0;
buttonTriggeredInclusion = false;
countRx = 0;
countTx = 0;
countErr = 0;
// Setup led pins
pinMode(pinRx, OUTPUT);
pinMode(pinTx, OUTPUT);
pinMode(pinEr, OUTPUT);
digitalWrite(pinRx, LOW);
digitalWrite(pinTx, LOW);
digitalWrite(pinEr, LOW);
// Setup digital in that triggers inclusion mode
pinMode(pinInclusion, INPUT);
digitalWrite(pinInclusion, HIGH);
// Set initial state of leds
digitalWrite(pinRx, HIGH);
digitalWrite(pinTx, HIGH);
digitalWrite(pinEr, HIGH);
// Start up the radio library
setupRadio(paLevel, channel, dataRate);
RF24::openReadingPipe(WRITE_PIPE, BASE_RADIO_ID);
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID);
RF24::startListening();
// Add led timer interrupt
MsTimer2::set(300, ledTimersInterrupt);
MsTimer2::start();
// Add interrupt for inclusion button to pin
PCintPort::attachInterrupt(pinInclusion, startInclusionInterrupt, RISING);
// Send startup log message on serial
serial(PSTR("0;0;%d;0;%d;Gateway startup complete.\n"), C_INTERNAL, I_GATEWAY_READY);
}
void startInclusionInterrupt() {
buttonTriggeredInclusion = true;
}
void MyGateway::checkButtonTriggeredInclusion() {
if (buttonTriggeredInclusion) {
// Ok, someone pressed the inclusion button on the gateway
// start inclusion mode for 1 munute.
serial(PSTR("0;0;%d;0;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE);
buttonTriggeredInclusion = false;
setInclusionMode(true);
}
}
void MyGateway::checkInclusionFinished() {
if (inclusionMode && millis()-inclusionStartTime>60000UL*inclusionTime) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
setInclusionMode(false);
}
}
uint8_t MyGateway::h2i(char c) {
uint8_t i = 0;
if (c <= '9')
i += c - '0';
else if (c >= 'a')
i += c - 'a' + 10;
else
i += c - 'A' + 10;
return i;
}
void MyGateway::parseAndSend(char *commandBuffer) {
boolean ok = false;
char *str, *p, *value=NULL;
uint8_t bvalue[MAX_PAYLOAD];
uint8_t blen = 0;
int i = 0;
uint16_t destination = 0;
uint8_t sensor = 0;
uint8_t command = 0;
uint8_t type = 0;
uint8_t ack = 0;
// Extract command data coming on serial line
for (str = strtok_r(commandBuffer, ";", &p); // split using semicolon
str && i < 6; // loop while str is not null an max 5 times
str = strtok_r(NULL, ";", &p) // get subsequent tokens
) {
switch (i) {
case 0: // Radioid (destination)
destination = atoi(str);
break;
case 1: // Childid
sensor = atoi(str);
break;
case 2: // Message type
command = atoi(str);
break;
case 3: // Should we request ack from destination?
ack = atoi(str);
break;
case 4: // Data type
type = atoi(str);
break;
case 5: // Variable value
if (command == C_STREAM) {
blen = 0;
uint8_t val;
while (*str) {
val = h2i(*str++) << 4;
val += h2i(*str++);
bvalue[blen] = val;
blen++;
}
} else {
value = str;
}
break;
}
i++;
}
if (destination==GATEWAY_ADDRESS && command==C_INTERNAL) {
// Handle messages directed to gateway
if (type == I_VERSION) {
// Request for version
serial(PSTR("0;0;%d;0;%d;%s\n"),C_INTERNAL, I_VERSION, LIBRARY_VERSION);
} else if (type == I_INCLUSION_MODE) {
// Request to change inclusion mode
setInclusionMode(atoi(value) == 1);
}
} else {
txBlink(1);
msg.sender = GATEWAY_ADDRESS;
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,ack?1:0);
mSetAck(msg,false);
msg.set(value);
if (command == C_STREAM)
msg.set(bvalue, blen);
else
msg.set(value);
ok = sendRoute(msg);
if (!ok) {
errBlink(1);
}
}
}
void MyGateway::setInclusionMode(boolean newMode) {
if (newMode != inclusionMode)
inclusionMode = newMode;
// Send back mode change on serial line to ack command
serial(PSTR("0;0;%d;0;%d;%d\n"), C_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0);
if (inclusionMode) {
inclusionStartTime = millis();
}
}
void MyGateway::processRadioMessage() {
if (process()) {
// A new message was received from one of the sensors
MyMessage message = getLastMessage();
if (mGetCommand(message) == C_PRESENTATION && inclusionMode) {
rxBlink(3);
} else {
rxBlink(1);
}
// Pass along the message from sensors to serial line
serial(message);
}
checkButtonTriggeredInclusion();
checkInclusionFinished();
}
void MyGateway::serial(const char *fmt, ... ) {
va_list args;
va_start (args, fmt );
vsnprintf_P(serialBuffer, MAX_SEND_LENGTH, fmt, args);
va_end (args);
Serial.print(serialBuffer);
if (useWriteCallback) {
// We have a registered write callback (probably Ethernet)
dataCallback(serialBuffer);
}
}
void MyGateway::serial(MyMessage &msg) {
serial(PSTR("%d;%d;%d;%d;%d;%s\n"),msg.sender, msg.sensor, mGetCommand(msg), mGetAck(msg), msg.type, msg.getString(convBuf));
}
void ledTimersInterrupt() {
if(countRx && countRx != 255) {
// switch led on
digitalWrite(pinRx, LOW);
} else if(!countRx) {
// switching off
digitalWrite(pinRx, HIGH);
}
if(countRx != 255) { countRx--; }
if(countTx && countTx != 255) {
// switch led on
digitalWrite(pinTx, LOW);
} else if(!countTx) {
// switching off
digitalWrite(pinTx, HIGH);
}
if(countTx != 255) { countTx--; }
else if(inclusionMode) { countTx = 8; }
if(countErr && countErr != 255) {
// switch led on
digitalWrite(pinEr, LOW);
} else if(!countErr) {
// switching off
digitalWrite(pinEr, HIGH);
}
if(countErr != 255) { countErr--; }
}
void MyGateway::rxBlink(uint8_t cnt) {
if(countRx == 255) { countRx = cnt; }
}
void MyGateway::txBlink(uint8_t cnt) {
if(countTx == 255 && !inclusionMode) { countTx = cnt; }
}
void MyGateway::errBlink(uint8_t cnt) {
if(countErr == 255) { countErr = cnt; }
}
@@ -1,5 +1,5 @@
/*
The MySensors Arduino library adds a new layer on top of the RF24 library.
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
@@ -9,21 +9,21 @@
version 2 as published by the Free Software Foundation.
*/
#ifndef Gateway_h
#define Gateway_h
#ifndef MyGateway_h
#define MyGateway_h
#include "Relay.h"
#include "MySensor.h"
#define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from vera
#define MAX_SEND_LENGTH 120 // Max buffersize needed for messages coming from vera
#define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from controller
#define MAX_SEND_LENGTH 120 // Max buffersize needed for messages destined for controller
class Gateway : public Relay
class MyGateway : public MySensor
{
public:
/**
* Constructor
*
* Creates a new instance of Gateway class.
* Creates a new instance of MyGateway class.
* If you don't use status leds and/or inclusion mode button on your arduino gateway
* you can disable this functionality by calling the 2 argument constructor.
*
@@ -36,41 +36,27 @@ class Gateway : public Relay
* @param _er Digital pin for error led
*
*/
Gateway(uint8_t _cepin=9, uint8_t _cspin=10, uint8_t _inclusion_time = 1);
Gateway(uint8_t _cepin, uint8_t _cspin, uint8_t _inclusion_time, uint8_t _inclusion_pin, uint8_t _rx, uint8_t _tx, uint8_t _er);
MyGateway(uint8_t _cepin=DEFAULT_CE_PIN, uint8_t _cspin=DEFAULT_CS_PIN, uint8_t _inclusion_time = 1, uint8_t _inclusion_pin = 3, uint8_t _rx=6, uint8_t _tx=5, uint8_t _er=4);
/* Use this and pass a function that should be called when you want to process commands that arrive from radio network */
void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback)(char *)=NULL);
void processRadioMessage();
void parseAndSend(char *inputString);
boolean isLedMode();
void ledTimersInterrupt();
void startInclusionInterrupt();
private:
char convBuf[MAX_PAYLOAD*2+1];
char serialBuffer[MAX_SEND_LENGTH]; // Buffer for building string when sending data to vera
unsigned long inclusionStartTime;
boolean inclusionMode; // Keeps track on inclusion mode
boolean buttonTriggeredInclusion;
volatile uint8_t countRx;
volatile uint8_t countTx;
volatile uint8_t countErr;
boolean ledMode;
boolean useWriteCallback;
void (*dataCallback)(char *);
uint8_t pinInclusion;
uint8_t inclusionTime;
uint8_t pinRx;
uint8_t pinTx;
uint8_t pinEr;
uint8_t h2i(char c);
void serial(const char *fmt, ... );
uint8_t validate(uint8_t length);
void serial(message_s msg);
void interruptStartInclusion();
void serial(MyMessage &msg);
void checkButtonTriggeredInclusion();
void setInclusionMode(boolean newMode);
void checkInclusionFinished();
@@ -80,6 +66,7 @@ class Gateway : public Relay
void errBlink(uint8_t cnt);
};
void ledTimersInterrupt();
void startInclusionInterrupt();
#endif
+326
View File
@@ -0,0 +1,326 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
Modified by Daniel Wiegert
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#include "MyMQTT.h"
#include "utility/MsTimer2.h"
char V_0[] PROGMEM = "TEMP"; //V_TEMP
char V_1[] PROGMEM = "HUM"; //V_HUM
char V_2[] PROGMEM = "LIGHT"; //V_LIGHT
char V_3[] PROGMEM = "DIMMER"; //V_DIMMER
char V_4[] PROGMEM = "PRESSURE"; //V_PRESSURE
char V_5[] PROGMEM = "FORECAST"; //V_FORECAST
char V_6[] PROGMEM = "RAIN"; //V_RAIN
char V_7[] PROGMEM = "RAINRATE"; //V_RAINRATE
char V_8[] PROGMEM = "WIND"; //V_WIND
char V_9[] PROGMEM = "GUST"; //V_GUST
char V_10[] PROGMEM = "DIRECTON"; //V_DIRECTON
char V_11[] PROGMEM = "UV"; //V_UV
char V_12[] PROGMEM = "WEIGHT"; //V_WEIGHT
char V_13[] PROGMEM = "DISTANCE"; //V_DISTANCE
char V_14[] PROGMEM = "IMPEDANCE"; //V_IMPEDANCE
char V_15[] PROGMEM = "ARMED"; //V_ARMED
char V_16[] PROGMEM = "TRIPPED"; //V_TRIPPED
char V_17[] PROGMEM = "WATT"; //V_WATT
char V_18[] PROGMEM = "KWH"; //V_KWH
char V_19[] PROGMEM = "SCENE_ON"; //V_SCENE_ON
char V_20[] PROGMEM = "SCENE_OFF"; //V_SCENE_OFF
char V_21[] PROGMEM = "HEATER"; //V_HEATER
char V_22[] PROGMEM = "HEATER_SW"; //V_HEATER_SW
char V_23[] PROGMEM = "LIGHT_LEVEL"; //V_LIGHT_LEVEL
char V_24[] PROGMEM = "VAR1"; //V_VAR1
char V_25[] PROGMEM = "VAR2"; //V_VAR2
char V_26[] PROGMEM = "VAR3"; //V_VAR3
char V_27[] PROGMEM = "VAR4"; //V_VAR4
char V_28[] PROGMEM = "VAR5"; //V_VAR5
char V_29[] PROGMEM = "UP"; //V_UP
char V_30[] PROGMEM = "DOWN"; //V_DOWN
char V_31[] PROGMEM = "STOP"; //V_STOP
char V_32[] PROGMEM = "IR_SEND"; //V_IR_SEND
char V_33[] PROGMEM = "IR_RECEIVE"; //V_IR_RECEIVE
char V_34[] PROGMEM = "FLOW"; //V_FLOW
char V_35[] PROGMEM = "VOLUME"; //V_VOLUME
char V_36[] PROGMEM = "LOCK_STATUS"; //V_LOCK_STATUS
char V_37[] PROGMEM = "DUST_LEVEL"; //V_DUST_LEVEL
char V_38[] PROGMEM = "VOLTAGE"; //V_VOLTAGE
char V_39[] PROGMEM = "CURRENT"; //V_CURRENT
char V_40[] PROGMEM = ""; //
char V_41[] PROGMEM = ""; //
char V_42[] PROGMEM = ""; //
char V_43[] PROGMEM = ""; //
char V_44[] PROGMEM = ""; //
char V_45[] PROGMEM = ""; //
char V_46[] PROGMEM = ""; //
char V_47[] PROGMEM = ""; //
char V_48[] PROGMEM = ""; //
char V_49[] PROGMEM = ""; //
char V_50[] PROGMEM = ""; //
char V_51[] PROGMEM = ""; //
char V_52[] PROGMEM = ""; //
char V_53[] PROGMEM = ""; //
char V_54[] PROGMEM = ""; //
char V_55[] PROGMEM = ""; //
char V_56[] PROGMEM = ""; //
char V_57[] PROGMEM = ""; //
char V_58[] PROGMEM = ""; //
char V_59[] PROGMEM = ""; //
char V_60[] PROGMEM = "Started!\n"; //Custom for MQTTGateway
char V_61[] PROGMEM = "SKETCH_NAME"; //Custom for MQTTGateway
char V_62[] PROGMEM = "SKETCH_VERSION"; //Custom for MQTTGateway
char V_63[] PROGMEM = "UNKNOWN"; //Custom for MQTTGateway
//////////////////////////////////////////////////////////////////
PROGMEM const char *vType[] = {
V_0, V_1, V_2, V_3, V_4, V_5, V_6, V_7, V_8, V_9, V_10,
V_11, V_12, V_13, V_14, V_15, V_16, V_17, V_18, V_19, V_20,
V_21, V_22, V_23, V_24, V_25, V_26, V_27, V_28, V_29, V_30,
V_31, V_32, V_33, V_34, V_35, V_36, V_37, V_38, V_39, V_40,
V_41, V_42, V_43, V_44, V_45, V_46, V_47, V_48, V_49, V_50,
V_51, V_52, V_53, V_54, V_55, V_56, V_57, V_58, V_59, V_60,
V_61, V_62, V_63
};
char broker[] PROGMEM = MQTT_BROKER_PREFIX;
#define S_FIRSTCUSTOM 60
#define TYPEMAXLEN 20
#define V_TOTAL (sizeof(vType)/sizeof(char *))-1
extern volatile uint8_t countRx;
extern volatile uint8_t countTx;
extern volatile uint8_t countErr;
extern uint8_t pinRx;
extern uint8_t pinTx;
extern uint8_t pinEr;
MyMQTT::MyMQTT(uint8_t _cepin, uint8_t _cspin) :
MySensor(_cepin, _cspin) {
}
inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
msg.destination = destination;
msg.sender = sender;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,enableAck);
mSetAck(msg,false);
return msg;
}
char *MyMQTT::getType(char *b, const char **index) {
char *q = b;
char *p = (char *)pgm_read_word(index);
while (*q++ = pgm_read_byte(p++));
*q=0;
return b;
}
void MyMQTT::begin(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate, void (*inDataCallback)
(const char *, uint8_t *), uint8_t _rx, uint8_t _tx, uint8_t _er) {
Serial.begin(BAUD_RATE);
repeaterMode = true;
isGateway = true;
MQTTClientConnected = false;
setupRepeaterMode();
dataCallback = inDataCallback;
nc.nodeId = 0;
nc.distance = 0;
// Start up the radio library
setupRadio(paLevel, channel, dataRate);
RF24::openReadingPipe(WRITE_PIPE, BASE_RADIO_ID);
RF24::openReadingPipe(CURRENT_NODE_PIPE, BASE_RADIO_ID);
RF24::startListening();
pinRx = _rx;
pinMode(pinRx, OUTPUT);
pinTx = _tx;
pinMode(pinTx, OUTPUT);
pinEr = _er;
pinMode(pinEr, OUTPUT);
MsTimer2::set(200, ledTimersInterrupt);
MsTimer2::start();
Serial.print(getType(convBuf, &vType[S_FIRSTCUSTOM]));
}
void MyMQTT::processRadioMessage() {
if (process()) {
// A new message was received from one of the sensors
MyMessage message = getLastMessage();
// Pass along the message from sensors to serial line
rxBlink(1);
SendMQTT(message);
}
}
void MyMQTT::processMQTTMessage(char *inputString, uint8_t inputPos) {
char *str, *p;
uint8_t i = 0;
buffer[0]= 0;
buffsize = 0;
if ((uint8_t)inputString[0] >> 4 == MQTTCONNECT) {
buffer[buffsize++] = MQTTCONNACK << 4;
buffer[buffsize++] = 0x02; // Remaining length
buffer[buffsize++] = 0x00; // Connection accepted
buffer[buffsize++] = 0x00; // Reserved
MQTTClientConnected=true; // We have connection!
}
if ((uint8_t)inputString[0] >> 4 == MQTTPINGREQ) {
buffer[buffsize++] = MQTTPINGRESP << 4;
buffer[buffsize++] = 0x00;
}
if ((uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE) {
buffer[buffsize++] = MQTTSUBACK << 4; // Just ack everything, we actually dont really care!
buffer[buffsize++] = 0x03; // Remaining length
buffer[buffsize++] = (uint8_t)inputString[2]; // Message ID MSB
buffer[buffsize++] = (uint8_t)inputString[3]; // Message ID LSB
buffer[buffsize++] = MQTTQOS0; // QOS level
}
if ((uint8_t)inputString[0] >> 4 == MQTTUNSUBSCRIBE) {
buffer[buffsize++] = MQTTUNSUBACK << 4;
buffer[buffsize++] = 0x02; // Remaining length
buffer[buffsize++] = (uint8_t)inputString[2]; // Message ID MSB
buffer[buffsize++] = (uint8_t)inputString[3]; // Message ID LSB
}
if ((uint8_t)inputString[0] >> 4 == MQTTDISCONNECT) {
MQTTClientConnected=false; // We lost connection!
}
if (buffsize > 0) {
dataCallback(buffer,&buffsize);
}
// We publish everything we get, we dont care if its subscribed or not!
if ((uint8_t)inputString[0] >> 4 == MQTTPUBLISH || (MQTT_SEND_SUBSCRIPTION && (uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE)) {
buffer[0]= 0;
buffsize = 0;
// Cut out address and payload depending on message type.
if ((uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE) {
strncat(buffer,inputString+6,inputString[5]);
} else {
strncat(buffer,inputString+4,inputString[3]);
}
// TODO: Check if we should send ack or not.
for (str = strtok_r(buffer,"/",&p) ; str && i<4 ; str = strtok_r(NULL,"/",&p)) {
if (i == 0) {
if (strcmp_P(str,broker)!=0) { //look for MQTT_BROKER_PREFIX
return; //Message not for us or malformatted!
}
} else if (i==1) {
msg.destination = atoi(str); //NodeID
} else if (i==2) {
msg.sensor = atoi(str); //SensorID
} else if (i==3) {
char match=0; //SensorType
//strcpy(str,(char*)&str[2]); //Strip V_
for (uint8_t j=0; strcpy_P(convBuf, (char*)pgm_read_word(&(vType[j]))) ; j++) {
if (strcmp((char*)&str[2],convBuf)==0) { //Strip V_ and compare
match=j;
break;
}
if (j >= V_TOTAL) { // No match found!
match=V_TOTAL; // last item.
break;
}
}
msg.type = match;
}
i++;
} //Check if packge has payload
if ((uint8_t)inputString[1] > (uint8_t)(inputString[3]+2) && !((uint8_t)inputString[0] >> 4 == MQTTSUBSCRIBE)) {
strcpy(convBuf,inputString+(inputString[3]+4));
msg.set(convBuf); //Payload
} else {
msg.set(""); //No payload
}
txBlink(1);
if (!sendRoute(build(msg, GATEWAY_ADDRESS, msg.destination, msg.sensor, C_SET, msg.type, 0))) errBlink(1);
}
}
void MyMQTT::SendMQTT(MyMessage &msg) {
buffsize = 0;
if (!MQTTClientConnected) return; //We have no connections - return
if (msg.isAck()) {
Serial.println("msg is ack!");
if (msg.sender==255 && mGetCommand(msg)==C_INTERNAL && msg.type==I_ID_REQUEST) {
// TODO: sending ACK request on id_response fucks node up. doesn't work.
// The idea was to confirm id and save to EEPROM_LATEST_NODE_ADDRESS.
}
} else {
// we have to check every message if its a newly assigned id or not.
// Ack on I_ID_RESPONSE does not work, and checking on C_PRESENTATION isn't reliable.
uint8_t newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1;
if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID;
if (msg.sender==newNodeID) {
saveState(EEPROM_LATEST_NODE_ADDRESS,newNodeID);
}
if (mGetCommand(msg)==C_INTERNAL) {
if (msg.type==I_CONFIG) {
txBlink(1);
if (!sendRoute(build(msg, GATEWAY_ADDRESS, msg.sender, 255, C_INTERNAL, I_CONFIG, 0).set("M"))) errBlink(1);
} else if (msg.type==I_ID_REQUEST && msg.sender==255) {
uint8_t newNodeID = loadState(EEPROM_LATEST_NODE_ADDRESS)+1;
if (newNodeID <= MQTT_FIRST_SENSORID) newNodeID = MQTT_FIRST_SENSORID;
if (newNodeID >= MQTT_LAST_SENSORID) return; // Sorry no more id's left :(
txBlink(1);
if (!sendRoute(build(msg, GATEWAY_ADDRESS, msg.sender, 255, C_INTERNAL, I_ID_RESPONSE, 0).set(newNodeID))) errBlink(1);
}
} else if (mGetCommand(msg)!=0) {
if (mGetCommand(msg)==3) msg.type=msg.type+(S_FIRSTCUSTOM-10); //Special message
buffer[buffsize++] = MQTTPUBLISH << 4; // 0:
buffer[buffsize++] = 0x09; // 1: Remaining length with no payload, we'll set this later to correct value, buffsize -2
buffer[buffsize++] = 0x00; // 2: Length MSB (Remaing length can never exceed ff,so MSB must be 0!)
buffer[buffsize++] = 0x08; // 3: Length LSB (ADDR), We'll set this later
if (msg.type > V_TOTAL) msg.type=V_TOTAL;// If type > defined types set to unknown.
strcpy_P(buffer+4, broker);
buffsize+=strlen_P(broker);
buffsize+=sprintf(&buffer[buffsize],"/%i/%i/V_%s",msg.sender,msg.sensor,getType(convBuf, &vType[msg.type]));
buffer[3]=buffsize-4; // Set correct address length on byte 4.
#ifdef DEBUG
Serial.println((char*)&buffer[4]);
#endif
msg.getString(convBuf);
for (uint8_t a=0; a<strlen(convBuf); a++) {// Payload
buffer[buffsize++] = convBuf[a];
}
buffer[1]=buffsize-2; // Set correct Remaining length on byte 2.
dataCallback(buffer,&buffsize);
}
}
}
void MyMQTT::rxBlink(uint8_t cnt) {
if(countRx == 255) { countRx = cnt; }
}
void MyMQTT::txBlink(uint8_t cnt) {
if(countTx == 255) { countTx = cnt; }
}
void MyMQTT::errBlink(uint8_t cnt) {
if(countErr == 255) { countErr = cnt; }
}
+83
View File
@@ -0,0 +1,83 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
Modified by Daniel Wiegert
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef MyMQTT_h
#define MyMQTT_h
#include "MySensor.h"
//////////////////////////////////////////////////////////////////
#ifdef DEBUG
#define TCPDUMP // Dump TCP packages.
#endif
#define MQTT_FIRST_SENSORID 20 // If you want manually configured nodes below this value. 255 = Disable
#define MQTT_LAST_SENSORID 254 // 254 is max! 255 reserved.
#define MQTT_BROKER_PREFIX "MyMQTT" // First prefix in MQTT tree, keep short!
#define MQTT_SEND_SUBSCRIPTION 1 // Send empty payload (request) to node upon MQTT client subscribe request.
// NOTE above : Beware to check if there is any length on payload in your incommingMessage code:
// Example: if (msg.type==V_LIGHT && strlen(msg.getString())>0) otherwise the code might do strange things.
//////////////////////////////////////////////////////////////////
#define EEPROM_LATEST_NODE_ADDRESS ((uint8_t)EEPROM_LOCAL_CONFIG_ADDRESS)
#define MQTT_MAX_PACKET_SIZE 100
#define MQTTPROTOCOLVERSION 3
#define MQTTCONNECT 1 // Client request to connect to Server
#define MQTTCONNACK 2 // Connect Acknowledgment
#define MQTTPUBLISH 3 // Publish message
#define MQTTPUBACK 4 // Publish Acknowledgment
#define MQTTPUBREC 5 // Publish Received (assured delivery part 1)
#define MQTTPUBREL 6 // Publish Release (assured delivery part 2)
#define MQTTPUBCOMP 7 // Publish Complete (assured delivery part 3)
#define MQTTSUBSCRIBE 8 // Client Subscribe request
#define MQTTSUBACK 9 // Subscribe Acknowledgment
#define MQTTUNSUBSCRIBE 10 // Client Unsubscribe request
#define MQTTUNSUBACK 11 // Unsubscribe Acknowledgment
#define MQTTPINGREQ 12 // PING Request
#define MQTTPINGRESP 13 // PING Response
#define MQTTDISCONNECT 14 // Client is Disconnecting
#define MQTTReserved 15 // Reserved
#define MQTTQOS0 (0 << 1)
#define MQTTQOS1 (1 << 1)
#define MQTTQOS2 (2 << 1)
class MyMQTT :
public MySensor {
public:
MyMQTT(uint8_t _cepin=5, uint8_t _cspin=6);
void begin(rf24_pa_dbm_e paLevel=RF24_PA_LEVEL_GW, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE, void (*dataCallback)
(const char *, uint8_t *)=NULL, uint8_t _rx=6, uint8_t _tx=5, uint8_t _er=4 );
void processRadioMessage();
void processMQTTMessage(char *inputString, uint8_t inputPos);
private:
void (*dataCallback)(const char *, uint8_t *);
void SendMQTT(MyMessage &msg);
void ledTimers();
void rxBlink(uint8_t cnt);
void txBlink(uint8_t cnt);
void errBlink(uint8_t cnt);
bool MQTTClientConnected;
char buffer[MQTT_MAX_PACKET_SIZE];
char convBuf[MAX_PAYLOAD*2+1];
uint8_t buffsize;
char *getType(char *b, const char **index);
};
extern void ledTimersInterrupt();
#endif
+231
View File
@@ -0,0 +1,231 @@
#include "MyMessage.h"
#include <stdio.h>
#include <stdlib.h>
MyMessage::MyMessage() {
destination = 0; // Gateway is default destination
}
MyMessage::MyMessage(uint8_t _sensor, uint8_t _type) {
destination = 0; // Gateway is default destination
sensor = _sensor;
type = _type;
}
bool MyMessage::isAck() const {
return miGetAck();
}
/* Getters for payload converted to desired form */
void* MyMessage::getCustom() const {
return (void *)data;
}
const char* MyMessage::getString() const {
uint8_t payloadType = miGetPayloadType();
if (payloadType == P_STRING) {
return data;
} else {
return NULL;
}
}
// handles single character hex (0 - 15)
char MyMessage::i2h(uint8_t i) const {
uint8_t k = i & 0x0F;
if (k <= 9)
return '0' + k;
else
return 'A' + k - 10;
}
char* MyMessage::getStream(char *buffer) const {
uint8_t cmd = miGetCommand();
if ((cmd == C_STREAM) && (buffer != NULL)) {
for (uint8_t i = 0; i < miGetLength(); i++)
{
buffer[i * 2] = i2h(data[i] >> 4);
buffer[(i * 2) + 1] = i2h(data[i]);
}
buffer[miGetLength() * 2] = '\0';
return buffer;
} else {
return NULL;
}
}
char* MyMessage::getString(char *buffer) const {
uint8_t payloadType = miGetPayloadType();
if (payloadType == P_STRING) {
strncpy(buffer, data, miGetLength());
buffer[miGetLength()] = 0;
return buffer;
} else if (buffer != NULL) {
if (payloadType == P_BYTE) {
itoa(bValue, buffer, 10);
} else if (payloadType == P_INT16) {
itoa(iValue, buffer, 10);
} else if (payloadType == P_UINT16) {
utoa(uiValue, buffer, 10);
} else if (payloadType == P_LONG32) {
ltoa(lValue, buffer, 10);
} else if (payloadType == P_ULONG32) {
ultoa(ulValue, buffer, 10);
} else if (payloadType == P_FLOAT32) {
dtostrf(fValue,2,fPrecision,buffer);
} else if (payloadType == P_CUSTOM) {
return getStream(buffer);
}
return buffer;
} else {
return NULL;
}
}
uint8_t MyMessage::getByte() const {
if (miGetPayloadType() == P_BYTE) {
return data[0];
} else if (miGetPayloadType() == P_STRING) {
return atoi(data);
} else {
return 0;
}
}
bool MyMessage::getBool() const {
return getInt();
}
float MyMessage::getFloat() const {
if (miGetPayloadType() == P_FLOAT32) {
return fValue;
} else if (miGetPayloadType() == P_STRING) {
return atof(data);
} else {
return 0;
}
}
long MyMessage::getLong() const {
if (miGetPayloadType() == P_LONG32) {
return lValue;
} else if (miGetPayloadType() == P_STRING) {
return atol(data);
} else {
return 0;
}
}
unsigned long MyMessage::getULong() const {
if (miGetPayloadType() == P_ULONG32) {
return ulValue;
} else if (miGetPayloadType() == P_STRING) {
return atol(data);
} else {
return 0;
}
}
int MyMessage::getInt() const {
if (miGetPayloadType() == P_INT16) {
return iValue;
} else if (miGetPayloadType() == P_STRING) {
return atoi(data);
} else {
return 0;
}
}
unsigned int MyMessage::getUInt() const {
if (miGetPayloadType() == P_UINT16) {
return uiValue;
} else if (miGetPayloadType() == P_STRING) {
return atoi(data);
} else {
return 0;
}
}
MyMessage& MyMessage::setType(uint8_t _type) {
type = _type;
return *this;
}
MyMessage& MyMessage::setSensor(uint8_t _sensor) {
sensor = _sensor;
return *this;
}
MyMessage& MyMessage::setDestination(uint8_t _destination) {
destination = _destination;
return *this;
}
// Set payload
MyMessage& MyMessage::set(void* value, uint8_t length) {
miSetPayloadType(P_CUSTOM);
miSetLength(length);
memcpy(data, value, min(length, MAX_PAYLOAD));
return *this;
}
MyMessage& MyMessage::set(const char* value) {
uint8_t length = strlen(value);
miSetLength(length);
miSetPayloadType(P_STRING);
strncpy(data, value, min(length, MAX_PAYLOAD));
return *this;
}
MyMessage& MyMessage::set(uint8_t value) {
miSetLength(1);
miSetPayloadType(P_BYTE);
data[0] = value;
return *this;
}
MyMessage& MyMessage::set(float value, uint8_t decimals) {
miSetLength(5); // 32 bit float + persi
miSetPayloadType(P_FLOAT32);
fValue=value;
fPrecision = decimals;
return *this;
}
MyMessage& MyMessage::set(unsigned long value) {
miSetPayloadType(P_ULONG32);
miSetLength(4);
ulValue = value;
return *this;
}
MyMessage& MyMessage::set(long value) {
miSetPayloadType(P_LONG32);
miSetLength(4);
lValue = value;
return *this;
}
MyMessage& MyMessage::set(unsigned int value) {
miSetPayloadType(P_UINT16);
miSetLength(2);
uiValue = value;
return *this;
}
MyMessage& MyMessage::set(int value) {
miSetPayloadType(P_INT16);
miSetLength(2);
iValue = value;
return *this;
}
+215
View File
@@ -0,0 +1,215 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef MyMessage_h
#define MyMessage_h
#ifdef __cplusplus
#include <Arduino.h>
#include <string.h>
#include <stdint.h>
#endif
#define PROTOCOL_VERSION 2
#define MAX_MESSAGE_LENGTH 32
#define HEADER_SIZE 7
#define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE)
// Message types
typedef enum {
C_PRESENTATION = 0,
C_SET = 1,
C_REQ = 2,
C_INTERNAL = 3,
C_STREAM = 4 // For Firmware and other larger chunks of data that need to be divided into pieces.
} command;
// Type of sensor data (for set/req/ack messages)
typedef enum {
V_TEMP,V_HUM, V_LIGHT, V_DIMMER, V_PRESSURE, V_FORECAST, V_RAIN,
V_RAINRATE, V_WIND, V_GUST, V_DIRECTION, V_UV, V_WEIGHT, V_DISTANCE,
V_IMPEDANCE, V_ARMED, V_TRIPPED, V_WATT, V_KWH, V_SCENE_ON, V_SCENE_OFF,
V_HEATER, V_HEATER_SW, V_LIGHT_LEVEL, V_VAR1, V_VAR2, V_VAR3, V_VAR4, V_VAR5,
V_UP, V_DOWN, V_STOP, V_IR_SEND, V_IR_RECEIVE, V_FLOW, V_VOLUME, V_LOCK_STATUS,
V_DUST_LEVEL, V_VOLTAGE, V_CURRENT
} data;
// Type of internal messages (for internal messages)
typedef enum {
I_BATTERY_LEVEL, I_TIME, I_VERSION, I_ID_REQUEST, I_ID_RESPONSE,
I_INCLUSION_MODE, I_CONFIG, I_FIND_PARENT, I_FIND_PARENT_RESPONSE,
I_LOG_MESSAGE, I_CHILDREN, I_SKETCH_NAME, I_SKETCH_VERSION,
I_REBOOT, I_GATEWAY_READY
} internal;
// Type of sensor (for presentation message)
typedef enum {
S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, S_DIMMER, S_COVER, S_TEMP, S_HUM, S_BARO, S_WIND,
S_RAIN, S_UV, S_WEIGHT, S_POWER, S_HEATER, S_DISTANCE, S_LIGHT_LEVEL, S_ARDUINO_NODE,
S_ARDUINO_REPEATER_NODE, S_LOCK, S_IR, S_WATER, S_AIR_QUALITY, S_CUSTOM, S_DUST,
S_SCENE_CONTROLLER
} sensor;
// Type of data stream (for streamed message)
typedef enum {
ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE, ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE,
ST_SOUND, ST_IMAGE
} stream;
typedef enum {
P_STRING, P_BYTE, P_INT16, P_UINT16, P_LONG32, P_ULONG32, P_CUSTOM, P_FLOAT32
} payload;
#define BIT(n) ( 1<<(n) )
// Create a bitmask of length len.
#define BIT_MASK(len) ( BIT(len)-1 )
// Create a bitfield mask of length starting at bit 'start'.
#define BF_MASK(start, len) ( BIT_MASK(len)<<(start) )
// Prepare a bitmask for insertion or combining.
#define BF_PREP(x, start, len) ( ((x)&BIT_MASK(len)) << (start) )
// Extract a bitfield of length len starting at bit 'start' from y.
#define BF_GET(y, start, len) ( ((y)>>(start)) & BIT_MASK(len) )
// Insert a new bitfield value x into y.
#define BF_SET(y, x, start, len) ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) )
// Getters/setters for special bit fields in header
#define mSetVersion(_msg,_version) BF_SET(_msg.version_length, _version, 0, 3)
#define mGetVersion(_msg) BF_GET(_msg.version_length, 0, 3)
#define mSetLength(_msg,_length) BF_SET(_msg.version_length, _length, 3, 5)
#define mGetLength(_msg) BF_GET(_msg.version_length, 3, 5)
#define mSetCommand(_msg,_command) BF_SET(_msg.command_ack_payload, _command, 0, 3)
#define mGetCommand(_msg) BF_GET(_msg.command_ack_payload, 0, 3)
#define mSetRequestAck(_msg,_rack) BF_SET(_msg.command_ack_payload, _rack, 3, 1)
#define mGetRequestAck(_msg) BF_GET(_msg.command_ack_payload, 3, 1)
#define mSetAck(_msg,_ackMsg) BF_SET(_msg.command_ack_payload, _ackMsg, 4, 1)
#define mGetAck(_msg) BF_GET(_msg.command_ack_payload, 4, 1)
#define mSetPayloadType(_msg, _pt) BF_SET(_msg.command_ack_payload, _pt, 5, 3)
#define mGetPayloadType(_msg) BF_GET(_msg.command_ack_payload, 5, 3)
// internal access for special fields
#define miGetCommand() BF_GET(command_ack_payload, 0, 3)
#define miSetLength(_length) BF_SET(version_length, _length, 3, 5)
#define miGetLength() BF_GET(version_length, 3, 5)
#define miSetRequestAck(_rack) BF_SET(command_ack_payload, _rack, 3, 1)
#define miGetRequestAck() BF_GET(command_ack_payload, 3, 1)
#define miSetAck(_ack) BF_SET(command_ack_payload, _ack, 4, 1)
#define miGetAck() BF_GET(command_ack_payload, 4, 1)
#define miSetPayloadType(_pt) BF_SET(command_ack_payload, _pt, 5, 3)
#define miGetPayloadType() BF_GET(command_ack_payload, 5, 3)
#ifdef __cplusplus
class MyMessage
{
public:
// Constructors
MyMessage();
MyMessage(uint8_t sensor, uint8_t type);
char i2h(uint8_t i) const;
/**
* If payload is something else than P_STRING you can have the payload value converted
* into string representation by supplying a buffer with the minimum size of
* 2*MAX_PAYLOAD+1. This is to be able to fit hex-conversion of a full binary payload.
*/
char* getStream(char *buffer) const;
char* getString(char *buffer) const;
const char* getString() const;
void* getCustom() const;
uint8_t getByte() const;
bool getBool() const;
float getFloat() const;
long getLong() const;
unsigned long getULong() const;
int getInt() const;
unsigned int getUInt() const;
// Getter for ack-flag. True if this is an ack message.
bool isAck() const;
// Setters for building message "on the fly"
MyMessage& setType(uint8_t type);
MyMessage& setSensor(uint8_t sensor);
MyMessage& setDestination(uint8_t destination);
// Setters for payload
MyMessage& set(void* payload, uint8_t length);
MyMessage& set(const char* value);
MyMessage& set(uint8_t value);
MyMessage& set(float value, uint8_t decimals);
MyMessage& set(unsigned long value);
MyMessage& set(long value);
MyMessage& set(unsigned int value);
MyMessage& set(int value);
#else
typedef union {
struct
{
#endif
uint8_t last; // 8 bit - Id of last node this message passed
uint8_t sender; // 8 bit - Id of sender node (origin)
uint8_t destination; // 8 bit - Id of destination node
uint8_t version_length; // 3 bit - Protocol version
// 5 bit - Length of payload
uint8_t command_ack_payload; // 3 bit - Command type
// 1 bit - Request an ack - Indicator that receiver should send an ack back.
// 1 bit - Is ack messsage - Indicator that this is the actual ack message.
// 3 bit - Payload data type
uint8_t type; // 8 bit - Type varies depending on command
uint8_t sensor; // 8 bit - Id of sensor that this message concerns.
// Each message can transfer a payload. We add one extra byte for string
// terminator \0 to be "printable" this is not transferred OTA
// This union is used to simplify the construction of the binary data types transferred.
union {
uint8_t bValue;
unsigned long ulValue;
long lValue;
unsigned int uiValue;
int iValue;
struct { // Float messages
float fValue;
uint8_t fPrecision; // Number of decimals when serializing
};
struct { // Presentation messages
uint8_t version; // Library version
uint8_t sensorType; // Sensor type hint for controller, see table above
};
char data[MAX_PAYLOAD + 1];
} __attribute__((packed));
#ifdef __cplusplus
} __attribute__((packed));
#else
};
uint8_t array[HEADER_SIZE + MAX_PAYLOAD + 1];
} __attribute__((packed)) MyMessage;
#endif
#endif
+549
View File
@@ -0,0 +1,549 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#include "MySensor.h"
#include "utility/LowPower.h"
#include "utility/RF24.h"
#include "utility/RF24_config.h"
// Inline function and macros
inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) {
msg.sender = sender;
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
mSetCommand(msg,command);
mSetRequestAck(msg,enableAck);
mSetAck(msg,false);
return msg;
}
MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) {
}
void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, uint8_t _parentNodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
Serial.begin(BAUD_RATE);
isGateway = false;
repeaterMode = _repeaterMode;
msgCallback = _msgCallback;
if (repeaterMode) {
setupRepeaterMode();
}
setupRadio(paLevel, channel, dataRate);
// Read settings from EEPROM
eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
// Read latest received controller configuration from EEPROM
eeprom_read_block((void*)&cc, (void*)EEPROM_LOCAL_CONFIG_ADDRESS, sizeof(ControllerConfig));
if (cc.isMetric == 0xff) {
// Eeprom empty, set default to metric
cc.isMetric = 0x01;
}
if (_parentNodeId != AUTO) {
nc.parentNodeId = _parentNodeId;
autoFindParent = false;
} else {
autoFindParent = true;
}
if (_nodeId != AUTO) {
// Set static id
nc.nodeId = _nodeId;
}
// If no parent was found in eeprom. Try to find one.
if (autoFindParent && nc.parentNodeId == 0xff) {
findParentNode();
}
// Try to fetch node-id from gateway
if (nc.nodeId == AUTO) {
requestNodeId();
}
debug(PSTR("%s started, id %d\n"), repeaterMode?"repeater":"sensor", nc.nodeId);
// Open reading pipe for messages directed to this node (set write pipe to same)
RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId));
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
// Send presentation for this radio node (attach
present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE);
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
// which is picked up in process()
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(nc.parentNodeId));
// Wait configuration reply.
waitForReply();
}
void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
failedTransmissions = 0;
// Start up the radio library
RF24::begin();
if (!RF24::isPVariant()) {
debug(PSTR("check wires\n"));
while(1);
}
RF24::setAutoAck(1);
RF24::setAutoAck(BROADCAST_PIPE,false); // Turn off auto ack for broadcast
RF24::enableAckPayload();
RF24::setChannel(channel);
RF24::setPALevel(paLevel);
RF24::setDataRate(dataRate);
RF24::setRetries(5,15);
RF24::setCRCLength(RF24_CRC_16);
RF24::enableDynamicPayloads();
// All nodes listen to broadcast pipe (for FIND_PARENT_RESPONSE messages)
RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
}
void MySensor::setupRepeaterMode(){
childNodeTable = new uint8_t[256];
eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256);
}
uint8_t MySensor::getNodeId() {
return nc.nodeId;
}
ControllerConfig MySensor::getConfig() {
return cc;
}
void MySensor::requestNodeId() {
debug(PSTR("req node id\n"));
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""));
waitForReply();
}
void MySensor::findParentNode() {
failedTransmissions = 0;
// Set distance to max
nc.distance = 255;
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
build(msg, nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
sendWrite(BROADCAST_ADDRESS, msg, true);
// Wait for ping response.
waitForReply();
}
void MySensor::waitForReply() {
unsigned long enter = millis();
// Wait a couple of seconds for response
while (millis() - enter < 2000) {
process();
}
}
boolean MySensor::sendRoute(MyMessage &message) {
// Make sure to process any incoming messages before sending (could this end up in recursive loop?)
// process();
bool isInternal = mGetCommand(message) == C_INTERNAL;
// If we still don't have any node id, re-request and skip this message.
if (nc.nodeId == AUTO && !(isInternal && message.type == I_ID_REQUEST)) {
requestNodeId();
return false;
}
if (repeaterMode) {
uint8_t dest = message.destination;
uint8_t route = getChildRoute(dest);
if (route>GATEWAY_ADDRESS && route<BROADCAST_ADDRESS && dest != GATEWAY_ADDRESS) {
// --- debug(PSTR("route %d.\n"), route);
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
return sendWrite(route, message);
} else if (isInternal && message.type == I_ID_RESPONSE && dest==BROADCAST_ADDRESS) {
// Node has not yet received any id. We need to send it
// by doing a broadcast sending,
return sendWrite(BROADCAST_ADDRESS, message, true);
}
}
if (!isGateway) {
// --- debug(PSTR("route parent\n"));
// Should be routed back to gateway.
bool ok = sendWrite(nc.parentNodeId, message);
if (!ok) {
// Failure when sending to parent node. The parent node might be down and we
// need to find another route to gateway.
if (autoFindParent && failedTransmissions > SEARCH_FAILURES) {
findParentNode();
}
failedTransmissions++;
} else {
failedTransmissions = 0;
}
return ok;
}
return false;
}
boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) {
uint8_t length = mGetLength(message);
message.last = nc.nodeId;
mSetVersion(message, PROTOCOL_VERSION);
// Make sure radio has powered up
RF24::powerUp();
RF24::stopListening();
RF24::openWritingPipe(TO_ADDR(next));
bool ok = RF24::write(&message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length), broadcast);
RF24::startListening();
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,st=%s:%s\n"),
message.sender,message.last, next, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), ok?"ok":"fail", message.getString(convBuf));
return ok;
}
bool MySensor::send(MyMessage &message, bool enableAck) {
message.sender = nc.nodeId;
mSetCommand(message,C_SET);
mSetRequestAck(message,enableAck);
return sendRoute(message);
}
void MySensor::sendBatteryLevel(uint8_t value, bool enableAck) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
}
void MySensor::present(uint8_t childSensorId, uint8_t sensorType, bool enableAck) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(LIBRARY_VERSION));
}
void MySensor::sendSketchInfo(const char *name, const char *version, bool enableAck) {
if (name != NULL) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
}
if (version != NULL) {
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version));
}
}
void MySensor::request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
sendRoute(build(msg, nc.nodeId, destination, childSensorId, C_REQ, variableType, false));
}
void MySensor::requestTime(void (* _timeCallback)(unsigned long)) {
timeCallback = _timeCallback;
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false));
}
boolean MySensor::process() {
uint8_t pipe;
boolean available = RF24::available(&pipe);
if (!available || pipe>6)
return false;
uint8_t len = RF24::getDynamicPayloadSize();
RF24::read(&msg, len);
RF24::writeAckPayload(pipe,&pipe, 1 );
// Add string termination, good if we later would want to print it.
msg.data[mGetLength(msg)] = '\0';
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d:%s\n"),
msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), msg.getString(convBuf));
if(!(mGetVersion(msg) == PROTOCOL_VERSION)) {
debug(PSTR("version mismatch\n"));
return false;
}
uint8_t command = mGetCommand(msg);
uint8_t type = msg.type;
uint8_t sender = msg.sender;
uint8_t last = msg.last;
uint8_t destination = msg.destination;
if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) {
// Relaying nodes should always answer ping messages
// Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes
delay(millis() & 0x3ff);
sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true);
return false;
} else if (destination == nc.nodeId) {
// Check if sender requests an ack back.
if (mGetRequestAck(msg)) {
// Copy message
ack = msg;
mSetRequestAck(ack,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(ack,true);
ack.sender = nc.nodeId;
ack.destination = msg.sender;
sendRoute(ack);
}
// This message is addressed to this node
if (repeaterMode && last != nc.parentNodeId) {
// Message is from one of the child nodes. Add it to routing table.
addChildRoute(sender, last);
}
if (command == C_INTERNAL) {
if (type == I_FIND_PARENT_RESPONSE && !isGateway) {
// We've received a reply to a FIND_PARENT message. Check if the distance is
// shorter than we already have.
uint8_t distance = msg.getByte();
if (distance<nc.distance-1) {
// Found a neighbor closer to GW than previously found
nc.distance = distance + 1;
nc.parentNodeId = msg.sender;
eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId);
eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance);
debug(PSTR("new parent=%d, d=%d\n"), nc.parentNodeId, nc.distance);
}
return false;
} else if (sender == GATEWAY_ADDRESS) {
bool isMetric;
if (type == I_REBOOT) {
wdt_enable(WDTO_15MS);
for (;;);
} else if (type == I_ID_RESPONSE) {
if (nc.nodeId == AUTO) {
nc.nodeId = msg.getByte();
// Write id to EEPROM
if (nc.nodeId == AUTO) {
// sensor net gateway will return max id if all sensor id are taken
debug(PSTR("full\n"));
while (1); // Wait here. Nothing else we can do...
} else {
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId));
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId);
}
debug(PSTR("id=%d\n"), nc.nodeId);
}
} else if (type == I_CONFIG) {
// Pick up configuration from controller (currently only metric/imperial)
// and store it in eeprom if changed
isMetric = msg.getByte() == 'M' ;
if (cc.isMetric != isMetric) {
cc.isMetric = isMetric;
eeprom_write_byte((uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
//eeprom_write_block((const void*)&cc, (uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
}
} else if (type == I_CHILDREN) {
if (repeaterMode && msg.getByte() == 'C') {
// Clears child relay data for this node
debug(PSTR("rd=clear\n"));
for (uint8_t i=0;i< sizeof(childNodeTable); i++) {
removeChildRoute(i);
}
sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set(""));
}
} else if (type == I_TIME) {
if (timeCallback != NULL) {
// Deliver time to callback
timeCallback(msg.getULong());
}
}
return false;
}
}
// Call incoming message callback if available
if (msgCallback != NULL) {
msgCallback(msg);
}
// Return true if message was addressed for this node...
return true;
} else if (repeaterMode && pipe == CURRENT_NODE_PIPE) {
// We should try to relay this message to another node
uint8_t route = getChildRoute(msg.destination);
if (route>0 && route<255) {
// This message should be forwarded to a child node. If we send message
// to this nodes pipe then all children will receive it because the are
// all listening to this nodes pipe.
//
// +----B
// -A
// +----C------D
//
// We're node C, Message comes from A and has destination D
//
// lookup route in table and send message there
sendWrite(route, msg);
} else {
// A message comes from a child node and we have no
// route for it.
//
// +----B
// -A
// +----C------D <-- Message comes from D
//
// We're node C
//
// Message should be passed to node A (this nodes relay)
// This message should be routed back towards sensor net gateway
sendWrite(nc.parentNodeId, msg);
// Add this child to our "routing table" if it not already exist
addChildRoute(sender, last);
}
}
return false;
}
MyMessage& MySensor::getLastMessage() {
return msg;
}
void MySensor::saveState(uint8_t pos, uint8_t value) {
if (loadState(pos) != value) {
eeprom_write_byte((uint8_t*)(EEPROM_LOCAL_CONFIG_ADDRESS+pos), value);
}
}
uint8_t MySensor::loadState(uint8_t pos) {
return eeprom_read_byte((uint8_t*)(EEPROM_LOCAL_CONFIG_ADDRESS+pos));
}
void MySensor::addChildRoute(uint8_t childId, uint8_t route) {
if (childNodeTable[childId] != route) {
childNodeTable[childId] = route;
eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, route);
}
}
void MySensor::removeChildRoute(uint8_t childId) {
if (childNodeTable[childId] != 0xff) {
childNodeTable[childId] = 0xff;
eeprom_write_byte((uint8_t*)EEPROM_ROUTES_ADDRESS+childId, 0xff);
}
}
uint8_t MySensor::getChildRoute(uint8_t childId) {
return childNodeTable[childId];
}
int MySensor::getInternalTemp(void)
{
long result;
// Read internal temp sensor against 1.1V reference
ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
delay(20); // Wait until Vref has settled
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = (result - 125) * 1075 + 500; // add 500 to round to nearest full degree
return result/10000;
}
int continueTimer = true;
void wakeUp() //place to send the interrupts
{
continueTimer = false;
}
void MySensor::internalSleep(unsigned long ms) {
while (continueTimer && ms >= 8000) { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); ms -= 8000; }
if (continueTimer && ms >= 4000) { LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); ms -= 4000; }
if (continueTimer && ms >= 2000) { LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); ms -= 2000; }
if (continueTimer && ms >= 1000) { LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); ms -= 1000; }
if (continueTimer && ms >= 500) { LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF); ms -= 500; }
if (continueTimer && ms >= 250) { LowPower.powerDown(SLEEP_250MS, ADC_OFF, BOD_OFF); ms -= 250; }
if (continueTimer && ms >= 125) { LowPower.powerDown(SLEEP_120MS, ADC_OFF, BOD_OFF); ms -= 120; }
if (continueTimer && ms >= 64) { LowPower.powerDown(SLEEP_60MS, ADC_OFF, BOD_OFF); ms -= 60; }
if (continueTimer && ms >= 32) { LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_OFF); ms -= 30; }
if (continueTimer && ms >= 16) { LowPower.powerDown(SLEEP_15Ms, ADC_OFF, BOD_OFF); ms -= 15; }
}
void MySensor::sleep(unsigned long ms) {
// Let serial prints finish (debug, log etc)
Serial.flush();
RF24::powerDown();
continueTimer = true;
internalSleep(ms);
}
bool MySensor::sleep(int interrupt, int mode, unsigned long ms) {
// Let serial prints finish (debug, log etc)
bool pinTriggeredWakeup = true;
Serial.flush();
RF24::powerDown();
attachInterrupt(interrupt, wakeUp, mode); //Interrupt on pin 3 for any change in solar power
if (ms>0) {
continueTimer = true;
sleep(ms);
pinTriggeredWakeup = !continueTimer;
} else {
Serial.flush();
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}
detachInterrupt(interrupt);
return pinTriggeredWakeup;
}
#ifdef DEBUG
void MySensor::debugPrint(const char *fmt, ... ) {
char fmtBuffer[300];
if (isGateway) {
// prepend debug message to be handled correctly by gw (C_INTERNAL, I_LOG_MESSAGE)
snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
Serial.print(fmtBuffer);
}
va_list args;
va_start (args, fmt );
va_end (args);
if (isGateway) {
// Truncate message if this is gateway node
vsnprintf_P(fmtBuffer, 60, fmt, args);
fmtBuffer[59] = '\n';
fmtBuffer[60] = '\0';
} else {
vsnprintf_P(fmtBuffer, 299, fmt, args);
}
va_end (args);
Serial.print(fmtBuffer);
Serial.flush();
//Serial.write(freeRam());
}
#endif
#ifdef DEBUG
int MySensor::freeRam (void) {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif
+266
View File
@@ -0,0 +1,266 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef MySensor_h
#define MySensor_h
#include "Version.h" // Auto generated by bot
#include "MyConfig.h"
#include "MyMessage.h"
#include <stddef.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <stdarg.h>
#ifdef __cplusplus
#include <Arduino.h>
#include <SPI.h>
#include "utility/LowPower.h"
#include "utility/RF24.h"
#include "utility/RF24_config.h"
#endif
#ifdef DEBUG
#define debug(x,...) debugPrint(x, ##__VA_ARGS__)
#else
#define debug(x,...)
#endif
#define BAUD_RATE 115200
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId.
#define NODE_SENSOR_ID 0xFF // Node child id is always created for when a node
// EEPROM start address for mysensors library data
#define EEPROM_START 0
// EEPROM location of node id
#define EEPROM_NODE_ID_ADDRESS EEPROM_START
// EEPROM location of parent id
#define EEPROM_PARENT_NODE_ID_ADDRESS (EEPROM_START+1)
// EEPROM location of distance to gateway
#define EEPROM_DISTANCE_ADDRESS (EEPROM_PARENT_NODE_ID_ADDRESS+1)
#define EEPROM_ROUTES_ADDRESS (EEPROM_DISTANCE_ADDRESS+1) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
#define EEPROM_CONTROLLER_CONFIG_ADDRESS (EEPROM_ROUTES_ADDRESS+256) // Location of controller sent configuration (we allow one payload of config data from controller)
#define EEPROM_FIRMWARE_TYPE_ADDRESS (EEPROM_CONTROLLER_CONFIG_ADDRESS+24)
#define EEPROM_FIRMWARE_VERSION_ADDRESS (EEPROM_FIRMWARE_TYPE_ADDRESS+2)
#define EEPROM_FIRMWARE_BLOCKS_ADDRESS (EEPROM_FIRMWARE_VERSION_ADDRESS+2)
#define EEPROM_FIRMWARE_CRC_ADDRESS (EEPROM_FIRMWARE_BLOCKS_ADDRESS+2)
#define EEPROM_LOCAL_CONFIG_ADDRESS (EEPROM_FIRMWARE_CRC_ADDRESS+2) // First free address for sketch static configuration
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
#define GATEWAY_ADDRESS ((uint8_t)0)
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
#define TO_ADDR(x) (BASE_RADIO_ID + x)
#define WRITE_PIPE ((uint8_t)0)
#define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2)
// Search for a new parent node after this many transmission failures
#define SEARCH_FAILURES 5
struct NodeConfig
{
uint8_t nodeId; // Current node id
uint8_t parentNodeId; // Where this node sends its messages
uint8_t distance; // This nodes distance to sensor net gateway (number of hops)
};
struct ControllerConfig {
uint8_t isMetric;
};
#ifdef __cplusplus
class MySensor : public RF24
{
public:
/**
* Constructor
*
* Creates a new instance of Sensor class.
*
* @param _cepin The pin attached to RF24 Chip Enable on the RF module (default 9)
* @param _cspin The pin attached to RF24 Chip Select (default 10)
*/
MySensor(uint8_t _cepin=DEFAULT_CE_PIN, uint8_t _cspin=DEFAULT_CS_PIN);
/**
* Begin operation of the MySensors library
*
* Call this in setup(), before calling any other sensor net library methods.
* @param incomingMessageCallback Callback function for incoming messages from other nodes or controller and request responses. Default is NULL.
* @param nodeId The unique id (1-254) for this sensor. Default is AUTO(255) which means sensor tries to fetch an id from controller.
* @param repeaterMode Activate repeater mode. This node will forward messages to other nodes in the radio network. Make sure to call process() regularly. Default in false
* @param parentNodeId Use this to force node to always communicate with a certain parent node. Default is AUTO which means node automatically tries to find a parent.
* @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX
* @param channel Radio channel. Default is channel 76
* @param dataRate Radio transmission speed. Default RF24_1MBPS
*/
void begin(void (* msgCallback)(const MyMessage &)=NULL, uint8_t nodeId=AUTO, boolean repeaterMode=false, uint8_t parentNodeId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE);
/**
* Return the nodes nodeId.
*/
uint8_t getNodeId();
/**
* Each node must present all attached sensors before any values can be handled correctly by the controller.
* It is usually good to present all attached sensors after power-up in setup().
*
* @param sensorId Select a unique sensor id for this sensor. Choose a number between 0-254.
* @param sensorType The sensor type. See sensor typedef in MyMessage.h.
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*/
void present(uint8_t sensorId, uint8_t sensorType, bool ack=false);
/**
* Sends sketch meta information to the gateway. Not mandatory but a nice thing to do.
* @param name String containing a short Sketch name or NULL if not applicable
* @param version String containing a short Sketch version or NULL if not applicable
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*
*/
void sendSketchInfo(const char *name, const char *version, bool ack=false);
/**
* Sends a message to gateway or one of the other nodes in the radio network
*
* @param msg Message to send
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
* @return true Returns true if message reached the first stop on its way to destination.
*/
bool send(MyMessage &msg, bool ack=false);
/**
* Send this nodes battery level to gateway.
* @param level Level between 0-100(%)
* @param ack Set this to true if you want destination node to send ack back to this node. Default is not to request any ack.
*
*/
void sendBatteryLevel(uint8_t level, bool ack=false);
/**
* Requests a value from gateway or some other sensor in the radio network.
* Make sure to add callback-method in begin-method to handle request responses.
*
* @param childSensorId The unique child id for the different sensors connected to this Arduino. 0-254.
* @param variableType The variableType to fetch
* @param destination The nodeId of other node in radio network. Default is gateway
*/
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination=GATEWAY_ADDRESS);
/**
* Requests time from controller. Answer will be delivered to callback.
*
* @param callback for time request. Incoming argument is seconds since 1970.
*/
void requestTime(void (* timeCallback)(unsigned long));
/**
* Processes incoming messages to this node. If this is a relaying node it will
* Returns true if there is a message addressed for this node just was received.
* Use callback to handle incoming messages.
*/
boolean process();
/**
* Returns the most recent node configuration received from controller
*/
ControllerConfig getConfig();
/**
* Save a state (in local EEPROM). Good for actuators to "remember" state between
* power cycles.
*
* You have 256 bytes to play with. Note that there is a limitation on the number
* of writes the EEPROM can handle (~100 000 cycles).
*
* @param pos The position to store value in (0-255)
* @param Value to store in position
*/
void saveState(uint8_t pos, uint8_t value);
/**
* Load a state (from local EEPROM).
*
* @param pos The position to fetch value from (0-255)
* @return Value to store in position
*/
uint8_t loadState(uint8_t pos);
/**
* Returns the last received message
*/
MyMessage& getLastMessage(void);
/**
* Sleep (PowerDownMode) the Arduino and radio. Wake up on timer.
* @param ms Number of milliseconds to sleep.
*/
void sleep(unsigned long ms);
/**
* Sleep (PowerDownMode) the Arduino and radio. Wake up on timer or pin change.
* See: http://arduino.cc/en/Reference/attachInterrupt for details on modes and which pin
* is assigned to what interrupt. On Nano/Pro Mini: 0=Pin2, 1=Pin3
* @param interrupt Interrupt that should trigger the wakeup
* @param mode RISING, FALLING, CHANGE
* @param ms Number of milliseconds to sleep or 0 to sleep forever
* @return true if wake up was triggered by pin change and false means timer woke it up.
*/
bool sleep(int interrupt, int mode, unsigned long ms=0);
#ifdef DEBUG
void debugPrint(const char *fmt, ... );
int freeRam();
#endif
protected:
NodeConfig nc; // Essential settings for node to work
ControllerConfig cc; // Configuration coming from controller
bool repeaterMode;
bool autoFindParent;
bool isGateway;
MyMessage msg; // Buffer for incoming messages.
MyMessage ack; // Buffer for ack messages.
void setupRepeaterMode();
void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate);
boolean sendRoute(MyMessage &message);
boolean sendWrite(uint8_t dest, MyMessage &message, bool broadcast=false);
private:
#ifdef DEBUG
char convBuf[MAX_PAYLOAD];
#endif
uint8_t failedTransmissions;
uint8_t *childNodeTable; // In memory buffer for routing information to other nodes. also stored in EEPROM
void (*timeCallback)(unsigned long); // Callback for requested time messages
void (*msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway.
void waitForReply();
void requestNodeId();
void findParentNode();
uint8_t crc8Message(MyMessage &message);
uint8_t getChildRoute(uint8_t childId);
void addChildRoute(uint8_t childId, uint8_t route);
void removeChildRoute(uint8_t childId);
void internalSleep(unsigned long ms);
};
#endif
#endif
-256
View File
@@ -1,256 +0,0 @@
/*
The MySensors Arduino library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#include "Relay.h"
Relay::Relay(uint8_t _cepin, uint8_t _cspin) : Sensor(_cepin, _cspin) {
isRelay = true;
}
void Relay::begin(uint8_t radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
Sensor::begin(radioId, paLevel, channel, dataRate);
// Read routing table from EEPROM
for (unsigned int i=0;i<sizeof(childNodeTable);i++) {
childNodeTable[i] = EEPROM.read(EEPROM_ROUTES_ADDRESS+i);
}
}
boolean Relay::sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary) {
bool ok = false;
if (length < sizeof(msg.data)) {
uint8_t route = getChildRoute(to);
if (route>0) {
debug(PSTR("Found child in routing table. Sending to %d\n"),route);
buildMsg(from, to, childId, messageType, type, data, length, binary);
// Found node in route table
ok = sendWrite(route, msg, length);
} else if (radioId == GATEWAY_ADDRESS) {
// If we're GW (no parent...). As a last resort try sending message directly to node.
debug(PSTR("No route... try sending direct.\n"));
buildMsg(from, to, childId, messageType, type, data, length, binary);
// Found node in route table
ok = sendWrite(to, msg, length);
} else {
// We are probably a repeater node which should send message back to relay
ok = Sensor::sendData(from, to, childId, messageType, type, data, length, binary);
}
} else {
debug(PSTR("Message too large\n"));
}
return ok;
}
boolean Relay::send(message_s message, int length) {
bool ok = true;
uint8_t route = getChildRoute(msg.header.to);
if (route>0 && route<255 && message.header.to != GATEWAY_ADDRESS) {
debug(PSTR("Routing message to %d.\n"), route);
// Message destination is not gateway and is in routing table for this node.
// Send it downstream
ok = sendWrite(route, message, length);
} else if (radioId != GATEWAY_ADDRESS) {
debug(PSTR("Sending message back towards gw.\n"));
// Should be routed back to gateway. Sensor code knows how to do this.
ok = Sensor::send(message, length);
}
if (!ok) {
debug(PSTR("No ack received.\n"));
}
return ok;
}
boolean Relay::messageAvailable() {
uint8_t pipe;
boolean available = RF24::available(&pipe);
if (available) {
debug(PSTR("Message available on pipe %d\n"),pipe);
}
if (available && pipe<7) {
uint8_t len = RF24::getDynamicPayloadSize()-sizeof(msg.header);
boolean ok = readMessage();
if (ok) {
if (msg.header.messageType == M_INTERNAL &&
msg.header.type == I_PING) {
// We always answer ping messages
// Wait a random delay of 0-2 seconds to minimize collision
// between ping ack messages from other relaying nodes
randomSeed(millis());
delay(random(2000));
ltoa(distance, convBuffer, 10);
uint8_t to = msg.header.from;
debug(PSTR("Answer ping message. %d\n"), strlen(convBuffer));
buildMsg(radioId, to, NODE_CHILD_ID, M_INTERNAL, I_PING_ACK, convBuffer,strlen(convBuffer), false);
sendWrite(to, msg, strlen(convBuffer));
} else if (msg.header.to == radioId) {
// This message is addressed to this node
if (msg.header.messageType == M_INTERNAL) {
// Handle all internal messages to this node
if (msg.header.type == I_RELAY_NODE && msg.header.to != GATEWAY_ADDRESS) {
// Someone at sensor net gateway side wants this node to refresh its relay node
findRelay();
// Send relay information back to sensor net gateway node device.
sendInternal(I_RELAY_NODE, ltoa(relayId, convBuffer, 10));
return false;
} else if (msg.header.type == I_CHILDREN && msg.header.to != GATEWAY_ADDRESS) {
debug(PSTR("Route command received"));
if (strncmp(msg.data,"F", 1) == 0) {
// Send in as many children we can fit in a message (binary)
sendChildren();
} else if (strncmp(msg.data,"C", 1) == 0) {
// Clears child relay data for this node
clearChildRoutes();
}
return false;
}
// Return rest of internal messages back to sketch...
if (msg.header.last != GATEWAY_ADDRESS)
addChildRoute(msg.header.from, msg.header.last);
return true;
} else {
// If this is variable message from sensor net gateway. Send ack back.
debug(PSTR("Message addressed for this node.\n"));
if (msg.header.from == GATEWAY_ADDRESS &&
msg.header.messageType == M_SET_VARIABLE) {
// Send back ack message to sensor net gateway
sendVariableAck();
}
// Return message to waiting sketch...
if (msg.header.last != GATEWAY_ADDRESS)
addChildRoute(msg.header.from, msg.header.last);
return true;
}
} else {
// We should probably try to relay this message
relayMessage(len, pipe);
}
}
}
return false;
}
void Relay::relayMessage(uint8_t length, uint8_t pipe) {
uint8_t route = getChildRoute(msg.header.to);
if (route>0 && route<255) {
debug(PSTR("Routing message to child node.\n"));
// This message should be forwarded to a child node. If we send message
// to this nodes pipe then all children will receive it because the are
// all listening to this nodes pipe.
//
// +----B
// -A
// +----C------D
//
// We're node C, Message comes from A and has destination D
//
// lookup route in table and send message there
sendWrite(route, msg, length);
} else if (pipe == CURRENT_NODE_PIPE) {
// A message comes from a child node and we have no
// route for it.
//
// +----B
// -A
// +----C------D <-- Message comes from D
//
// We're node C
//
// Message should be passed to node A (this nodes relay)
debug(PSTR("Routing message to relay.\n"));
// This message should be routed back towards sensor net gateway
sendWrite(relayId, msg, length);
// Add this child to our "routing table" if it not already exist
addChildRoute(msg.header.from, msg.header.last);
} else {
// We're snooped a message directed to gateway from another branch
// Make sure to remove the sender node from our routing table.
//
// +-----B <-- Message comes from here
// -A
// +-----C <-- We're here
//
// So the sender of message should never be in our
// routing table.
//
// We could also end up in this else-statement if message
// destination has no route.
// This can happen if a node never has sent any message to
// sensor net gateway (which will build up routing information in all nodes
// that gets passed).
if (radioId == GATEWAY_ADDRESS) {
// We should perhaps inform gateway about the no-route situation?!
debug(PSTR("Unknown route from GW\n"));
} else {
debug(PSTR("Remove child node from routing table.\n"));
removeChildRoute(msg.header.from);
}
}
}
void Relay::addChildRoute(uint8_t childId, uint8_t route) {
if (childNodeTable[childId] != route) {
childNodeTable[childId] = route;
EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, route);
}
}
void Relay::removeChildRoute(uint8_t childId) {
if (childNodeTable[childId] != 0xff) {
childNodeTable[childId] = 0xff;
EEPROM.write(EEPROM_ROUTES_ADDRESS+childId, 0xff);
}
}
uint8_t Relay::getChildRoute(uint8_t childId) {
return childNodeTable[childId];
}
void Relay::clearChildRoutes() {
debug(PSTR("Clear child routing data\n"));
for (unsigned int i=0;i< sizeof(childNodeTable); i++) {
removeChildRoute(i);
}
sendInternal(I_CHILDREN, "");
}
void Relay::sendChildren() {
// Send in which children that is using this node as an relay.
// TODO: Fix this
debug(PSTR("Send child info to sensor gateway.\n"));
for (int i=0;i< 10; i++) {
// Serial.println(childNodeTable[i]);
debug(PSTR("rt:%d, %d\n"), i, getChildRoute(i) );
}
//sendInternal(I_CHILDREN, "not implemented");
}
-62
View File
@@ -1,62 +0,0 @@
/*
The MySensors Arduino library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef Relay_h
#define Relay_h
#include "Sensor.h"
#ifdef DEBUG
#define debug(x,...) debugPrint(x, ##__VA_ARGS__)
#else
#define debug(x,...)
#endif
#define EEPROM_ROUTES_ADDRESS ((uint8_t)3) // Where to start storing routing information in EEPROM. Will allocate 256 bytes.
class Relay : public Sensor
{
public:
/**
* Constructor
*
* Creates a new instance of Relay class.
*
* @param _cepin The pin attached to RF24 Chip Enable on the RF module (default 9)
* @param _cspin The pin attached to RF24 Chip Select (default 10)
*/
Relay(uint8_t _cepin = 9, uint8_t _cspin = 10);
void begin(uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE);
boolean messageAvailable();
boolean send(message_s message, int length);
boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary);
protected:
void sendChildren();
private:
uint8_t childNodeTable[256]; // Buffer to store child node information. Store this in EEPROM
uint8_t getChildRoute(uint8_t childId);
void addChildRoute(uint8_t childId, uint8_t route);
void removeChildRoute(uint8_t childId);
void clearChildRoutes();
void relayMessage(uint8_t length, uint8_t pipe);
};
#endif
-567
View File
@@ -1,567 +0,0 @@
/*
The MySensors library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#include "Sensor.h"
Sensor::Sensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) {
isRelay = false;
}
void Sensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
failedTransmissions = 0;
// Start up the radio library
RF24::begin();
RF24::enableDynamicPayloads();
RF24::setAutoAck(false);
RF24::setRetries(15, 15);
RF24::setPALevel(paLevel);
RF24::setChannel(channel);
RF24::setDataRate(dataRate);
RF24::setCRCLength(RF24_CRC_16);
// All repeater nodes and gateway listen to broadcast pipe (for PING messages)
if (isRelay) {
RF24::openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS));
}
}
void Sensor::begin(uint8_t _radioId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) {
Serial.begin(BAUD_RATE);
radioId = _radioId;
debug(PSTR("Started %s.\n"), isRelay?"relay":"sensor");
setupRadio(paLevel, channel, dataRate);
// Fetch relay from EEPROM
relayId = EEPROM.read(EEPROM_RELAY_ID_ADDRESS);
// Fetch distance from eeprom
distance = EEPROM.read(EEPROM_DISTANCE_ADDRESS);
if (relayId == 0xff) {
// No relay previously fetched and stored in eeprom. Try to find one.
findRelay();
}
debug(PSTR("Relay=%d, distance=%d\n"), relayId, distance);
initializeRadioId();
// Open reading pipe for messages directed to this node
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId));
// Send presentation for this radio node
sendSensorPresentation(NODE_CHILD_ID, isRelay? S_ARDUINO_RELAY : S_ARDUINO_NODE);
// Send relay information back to sensor net gateway.
sendInternal(I_RELAY_NODE, itoa(relayId, convBuffer, 10));
// Initialize ack message header
ack.header.version = PROTOCOL_VERSION;
ack.header.binary = 0;
ack.header.from = radioId;
ack.header.to = GATEWAY_ADDRESS;
ack.header.messageType = M_SET_VARIABLE;
}
uint8_t Sensor::getRadioId() {
return radioId;
}
void Sensor::initializeRadioId() {
if (radioId == AUTO) {
radioId = EEPROM.read(EEPROM_RADIO_ID_ADDRESS);
if (radioId == 0xFF || radioId == 0) {
radioId = AUTO;
debug(PSTR("No radio id found in EEPROM fetching one from sensor net gateway\n"));
// No radio id has been fetched yet ant EEPROM is unwritten.
// Request new id from sensor net gateway. Use radioId 4095 temporarily
// to be able to receive my correct nodeId.
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId));
radioId = atoi(getInternal(I_REQUEST_ID));
// Write id to EEPROM
if (radioId == AUTO) { // sensor net gateway will return max id if all sensor id are taken
debug(PSTR("Sensor network is full! You already have the maximum of sensors!\n"));
while (1) {} // Wait here. Nothing else we can do...
} else {
debug(PSTR("Radio id received: %d\n"), radioId);
EEPROM.write(EEPROM_RADIO_ID_ADDRESS, radioId);
}
}
debug(PSTR("Radio id stored in EEPROM was: %d\n"), radioId);
}
}
void Sensor::findRelay() {
// This can be improved by using status = read_register(OBSERVE_TX,&observe_tx,1) on the
// sending side.. the problem is getting this information here without too much fuss.
// Stay in this method until one or more relay nodes answers ping message.
if (radioId == GATEWAY_ADDRESS)
return; // Gateway has no business here!
failedTransmissions = 0;
// Open a reading pipe for current radioId (if it differs from broadcast)
if (radioId != BROADCAST_PIPE) {
RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(radioId));
debug(PSTR("Open ping reading pipe: %d\n"), radioId);
}
distance = 255;
uint8_t oldRelayId = relayId;
while (distance == 255) {
// Send ping message to BROADCAST_ADDRESS (to which all nodes listens and should reply to)
buildMsg(radioId, BROADCAST_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_PING, "", 0, false);
sendWrite(BROADCAST_ADDRESS, msg, 0);
// Wait for replies for max 10 seconds (or when buffer for all relay nodes have been filled up)
unsigned long enter = millis();
uint8_t neighborDistanceToGW;
// Wait for ack responses 5 seconds
while (millis() - enter < 5000) {
if (messageAvailable()) {
if (msg.header.messageType == M_INTERNAL &&
msg.header.type == I_PING_ACK &&
msg.header.to == radioId) {
neighborDistanceToGW = atoi(msg.data);
if (neighborDistanceToGW<distance-1) {
// Found a neighbor closer to GW than previously found
distance = neighborDistanceToGW + 1;
relayId = msg.header.from;
debug(PSTR("Using relay %d. Distance is %d\n"), msg.header.from, neighborDistanceToGW);
if (neighborDistanceToGW == 0) // We found gateway. Search no more.
break;
} else {
debug(PSTR("Discarded relay %d. Distance is %d\n"), msg.header.from, neighborDistanceToGW);
}
}
}
}
if (distance == 255) {
debug(PSTR("No relay nodes was found. Trying again in 10 seconds.\n"));
delay(10000);
}
}
// Store new relay address in EEPROM
if (relayId != oldRelayId) {
EEPROM.write(EEPROM_RELAY_ID_ADDRESS, relayId);
EEPROM.write(EEPROM_DISTANCE_ADDRESS, distance);
}
}
void Sensor::buildMsg(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary) {
msg.header.version = PROTOCOL_VERSION;
msg.header.binary = binary;
msg.header.from = from;
msg.header.to = to;
msg.header.childId = childId;
msg.header.messageType = messageType;
msg.header.type = type;
strncpy(msg.data, data, length);
if(length < sizeof(msg.data)-1) {
memset(&msg.data[length], 0, sizeof(msg.data) - 1 - length);
}
}
boolean Sensor::sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary) {
if (length < sizeof(msg.data)) {
buildMsg(from, to, childId, messageType, type, data, length, binary);
return send(msg,length);
} else {
debug(PSTR("Message too large\n"));
}
return false;
}
boolean Sensor::sendVariableAck() {
ack.header.childId = msg.header.childId;
ack.header.type = msg.header.type;
strncpy(ack.data, msg.data, sizeof(ack.data)-1); // will terminate properly not exceeding message.data
return send(ack,strlen(ack.data));
}
boolean Sensor::send(message_s message, int length) {
debug(PSTR("Relaying message back to gateway.\n"));
// We're a sensor node. Always send messages back to relay node
bool ok = sendWrite(relayId, message, length);
if (!ok && radioId != GATEWAY_ADDRESS) {
// Failure when sending to relay node. The relay node might be down and we
// need to find another route to gateway. Max 20 retries before giving up.
if (failedTransmissions > FIND_RELAY_RETRIES) {
findRelay();
}
failedTransmissions++;
} else {
failedTransmissions = 0;
}
return ok;
}
boolean Sensor::sendWrite(uint8_t dest, message_s message, int length) {
message.header.last = radioId;
message.header.crc = crc8Message(message, length);
debug(PSTR("Tx: fr=%d,to=%d,la=%d,ne=%d,ci=%d,mt=%d,ty=%d,cr=%d: %s\n"),
message.header.from,message.header.to, message.header.last, dest, message.header.childId, message.header.messageType, message.header.type, message.header.crc, message.data);
bool ok = true;
bool broadcast = message.header.messageType == M_INTERNAL && message.header.type == I_PING;
// int retry = WRITE_RETRY;
RF24::stopListening();
RF24::openWritingPipe(TO_ADDR(dest));
RF24::write(&message, min(MAX_MESSAGE_LENGTH, sizeof(message.header) + length), broadcast);
RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit
RF24::startListening();
if (!broadcast) {
// ---------------- WAIT FOR ACK ------------------
unsigned long startedWaiting = millis();
bool timeout = false;
// Wait for ack message maximum 50 ms
while ( !RF24::available() && !timeout ) {
if (millis() - startedWaiting > ACK_MAX_WAIT ) {
timeout = true;
debug(PSTR("Ack: receive timeout\n"));
ok = false;
}
}
// Check payload size and content
if (!timeout) {
// Check payload size and content
if (RF24::getDynamicPayloadSize()==sizeof(uint8_t)) {
uint8_t idest;
RF24::read( &idest, sizeof(uint8_t));
if (dest != idest) {
debug(PSTR("Ack: received ack from the wrong sensor\n"));
ok = false;
} else {
debug(PSTR("Ack: received OK\n"));
}
} else {
ok = false;
debug(PSTR("Ack: received none ack msg.\n"));
}
}
}
return ok;
}
void Sensor::sendInternal(uint8_t variableType, const char *value) {
sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, variableType, value, strlen(value), false);
}
// This will be added in 1.4
/*void Sensor::sendVariable(uint8_t childId, uint8_t variableType,
const char *value, length) {
sendData(radioId, GATEWAY_ADDRESS, childId, M_SET_VARIABLE, variableType, value, length, true);
}*/
void Sensor::sendVariable(uint8_t childId, uint8_t variableType,
const char *value) {
sendData(radioId, GATEWAY_ADDRESS, childId, M_SET_VARIABLE, variableType, value, strlen(value), false);
}
void Sensor::sendVariable(uint8_t childId, uint8_t variableType, double value, int decimals) {
sendVariable(childId, variableType, dtostrf(value,2,decimals,convBuffer));
}
void Sensor::sendVariable(uint8_t childId, uint8_t variableType, int value) {
sendVariable(childId, variableType, itoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t childId, uint8_t variableType, long value) {
sendVariable(childId, variableType, ltoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t childId, uint8_t variableType, unsigned long value) {
sendVariable(childId, variableType, ltoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t childId, uint8_t variableType, unsigned int value) {
sendVariable(childId, variableType, ltoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType,
const char *value) {
sendData(radioId, nodeId, childId, M_SET_VARIABLE, variableType, value, strlen(value), false);
}
void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, double value, int decimals) {
sendVariable(nodeId, childId, variableType, dtostrf(value,2,decimals,convBuffer));
}
void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, int value) {
sendVariable(nodeId, childId, variableType, itoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, long value) {
sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, unsigned long value) {
sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10));
}
void Sensor::sendVariable(uint8_t nodeId, uint8_t childId, uint8_t variableType, unsigned int value) {
sendVariable(nodeId, childId, variableType, ltoa(value, convBuffer, 10));
}
void Sensor::sendSensorPresentation(uint8_t childId, uint8_t sensorType) {
sendData(radioId, GATEWAY_ADDRESS, childId, M_PRESENTATION, sensorType, LIBRARY_VERSION, strlen(LIBRARY_VERSION), false);
}
void Sensor::requestStatus(uint8_t childId, uint8_t variableType) {
sendData(radioId, GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, variableType, "", 0, false);
}
void Sensor::requestStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) {
sendData(radioId, nodeId, childId, M_REQ_VARIABLE, variableType, "", 0, false);
}
void Sensor::sendSketchInfo(const char *name, const char *version) {
sendSketchName(name);
sendSketchVersion(version);
}
void Sensor::sendSketchName(const char *name) {
if (name != NULL)
sendInternal(I_SKETCH_NAME, name);
}
void Sensor::sendSketchVersion(const char *version) {
if (version != NULL)
sendInternal(I_SKETCH_VERSION, version);
}
void Sensor::sendBatteryLevel(int value) {
sendInternal(I_BATTERY_LEVEL, ltoa(value, convBuffer, 10));
}
char* Sensor::get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType) {
while (1) {
sendData(radioId, nodeId, childId, sendType, variableType, "", 0, false);
uint8_t i = 0;
while (i < 100) { // 5 seconds timeout before re-sending status request
while (messageAvailable()) {
// Check that it is right type of message and not a routing message
if (msg.header.messageType == receiveType &&
msg.header.type == variableType &&
msg.header.childId == childId) {
return msg.data;
}
}
delay(50);
i++;
}
}
return NULL;
}
char* Sensor::getStatus(uint8_t childId, uint8_t variableType) {
return get(GATEWAY_ADDRESS, childId, M_REQ_VARIABLE, M_ACK_VARIABLE, variableType);
}
char * Sensor::getStatus(uint8_t nodeId, int8_t childId, uint8_t variableType) {
return get(nodeId, childId, M_REQ_VARIABLE, M_ACK_VARIABLE, variableType);
}
char* Sensor::getInternal(uint8_t variableType) {
return get(GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, M_INTERNAL, variableType);
}
unsigned long Sensor::getTime() {
return atol(getInternal(I_TIME));
}
void Sensor::requestTime() {
sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_TIME, "", 0, false);
}
bool Sensor::isMetricSystem() {
return getInternal(I_UNIT)[0] == 'M';
}
void Sensor::requestIsMetricSystem() {
sendData(radioId, GATEWAY_ADDRESS, NODE_CHILD_ID, M_INTERNAL, I_UNIT, "", 0, false);
}
boolean Sensor::messageAvailable() {
uint8_t pipe;
boolean available = RF24::available(&pipe);
if (available) {
debug(PSTR("Message available on pipe %d\n"),pipe);
}
if (available && pipe<7) {
boolean ok = readMessage();
if (ok && msg.header.to == radioId) {
// This message is addressed to this node
debug(PSTR("Message addressed for this node.\n"));
if (msg.header.from == GATEWAY_ADDRESS &&
// If this is variable message from sensor net gateway. Send ack back.
msg.header.messageType == M_SET_VARIABLE) {
// Send back ack message to sensor net gateway
sendVariableAck();
}
// Return message to waiting sketch...
return true;
}
}
return false;
}
message_s Sensor::waitForMessage() {
while (1) {
if (messageAvailable()) {
return msg;
}
}
}
message_s Sensor::getMessage() {
return msg;
}
boolean Sensor::readMessage() {
uint8_t len = RF24::getDynamicPayloadSize();
RF24::read(&msg, len);
uint8_t valid = validate(len-sizeof(header_s));
boolean ok = valid == VALIDATE_OK;
if (ok && !(msg.header.messageType==M_INTERNAL && msg.header.type == I_PING_ACK)) {
delay(ACK_SEND_DELAY); // Small delay here to let other side switch to reading mode
RF24::stopListening();
RF24::openWritingPipe(TO_ADDR(msg.header.last));
RF24::write(&radioId, sizeof(uint8_t));
RF24::closeReadingPipe(WRITE_PIPE); // Stop listening to write-pipe after transmit
RF24::startListening();
debug(PSTR("Sent ack msg to %d\n"), msg.header.last);
}
// Make sure string gets terminated ok for full sized messages.
msg.data[len - sizeof(header_s) ] = '\0';
debug(PSTR("Rx: fr=%d,to=%d,la=%d,ci=%d,mt=%d,t=%d,cr=%d(%s): %s\n"),
msg.header.from,msg.header.to, msg.header.last, msg.header.childId, msg.header.messageType, msg.header.type, msg.header.crc, valid==0?"ok":valid==1?"ec":"ev", msg.data);
return ok;
}
/*
* calculate CRC8 on message_s data taking care of data structure and protocol version
*/
uint8_t Sensor::crc8Message(message_s var_msg, uint8_t len) {
uint8_t crc = 0x00;
uint8_t loop_count;
uint8_t bit_counter;
uint8_t data;
uint8_t feedback_bit;
uint8_t number_of_bytes_to_read = (uint8_t)sizeof(var_msg);
// Must set crc to a constant value.
var_msg.header.crc = 0;
// fill unused space by zeroes for string data only
if(len>=0 && len < sizeof(var_msg.data)-1) {
memset(&var_msg.data[len], 0, sizeof(var_msg.data) - 1 - len);
}
for (loop_count = 0; loop_count != number_of_bytes_to_read; loop_count++)
{
data = ((uint8_t*)&var_msg)[loop_count];
bit_counter = 8;
do {
feedback_bit = (crc ^ data) & 0x01;
if ( feedback_bit == 0x01 ) {
crc = crc ^ 0x18; //0X18 = X^8+X^5+X^4+X^0
}
crc = (crc >> 1) & 0x7F;
if ( feedback_bit == 0x01 ) {
crc = crc | 0x80;
}
data = data >> 1;
bit_counter--;
} while (bit_counter > 0);
}
return crc;
}
uint8_t Sensor::validate(uint8_t length) {
uint8_t oldCrc = msg.header.crc;
uint8_t newCrc = crc8Message(msg, length);
if(!(msg.header.version == PROTOCOL_VERSION)) return VALIDATE_BAD_VERSION;
if(!(oldCrc == newCrc)) return VALIDATE_BAD_CRC;
return VALIDATE_OK;
}
#ifdef DEBUG
void Sensor::debugPrint(const char *fmt, ... ) {
char fmtBuffer[300];
if (radioId == GATEWAY_ADDRESS) {
// prepend debug message to be handled correctly by gw
Serial.print("0;0;4;11;");
}
va_list args;
va_start (args, fmt );
vsnprintf_P(fmtBuffer, 300, fmt, args);
va_end (args);
if (radioId == GATEWAY_ADDRESS) {
// Truncate message if this is gateway node
vsnprintf_P(fmtBuffer, 60, fmt, args);
fmtBuffer[59] = '\n';
fmtBuffer[60] = '\0';
} else {
vsnprintf_P(fmtBuffer, 299, fmt, args);
}
va_end (args);
Serial.print(fmtBuffer);
//Serial.print("0;0;4;11;Mem free:");
//Serial.println(freeRam());
}
#endif
#ifdef DEBUG
int Sensor::freeRam (void) {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif
-301
View File
@@ -1,301 +0,0 @@
/*
The Sensor Net Arduino library adds a new layer on top of the RF24 library.
It handles radio network routing, relaying and ids.
Created by Henrik Ekblad <henrik.ekblad@gmail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef Sensor_h
#define Sensor_h
#include "Config.h"
#include "Version.h"
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
#include <Arduino.h>
#include <stddef.h>
#include <SPI.h>
#include <EEPROM.h>
#include <avr/pgmspace.h>
#include <stdarg.h>
#ifdef DEBUG
#define debug(x,...) debugPrint(x, ##__VA_ARGS__)
#else
#define debug(x,...)
#endif
#define PROTOCOL_VERSION 1
#define BAUD_RATE 115200
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of radioId.
#define NODE_CHILD_ID 0xFF // Node child id is always created for when a new sensor is detected
#define EEPROM_RADIO_ID_ADDRESS 0 // Where to store radio id in EEPROM
#define EEPROM_RELAY_ID_ADDRESS 1 // Where to store relay id in EEPROM
#define EEPROM_DISTANCE_ADDRESS 2 // Where to store distance to gateway in EEPROM
// This is the radioId for sensor net gateway receiver sketch (where all sensors should send their data).
// This is also act as base value for sensor radioId
#define BASE_RADIO_ID ((uint64_t)0xABCDABC000LL)
#define GATEWAY_ADDRESS ((uint8_t)0)
#define BROADCAST_ADDRESS ((uint8_t)0xFF)
#define TO_ADDR(x) (BASE_RADIO_ID + x)
#define WRITE_PIPE ((uint8_t)0)
#define CURRENT_NODE_PIPE ((uint8_t)1)
#define BROADCAST_PIPE ((uint8_t)2)
#define ACK_MAX_WAIT 50
#define ACK_SEND_DELAY 10
#define WRITE_RETRY 5
#define FIND_RELAY_RETRIES 20
#define MAX_MESSAGE_LENGTH 32
// Message types
typedef enum {
M_PRESENTATION = 0,
M_SET_VARIABLE = 1,
M_REQ_VARIABLE = 2,
M_ACK_VARIABLE = 3,
M_INTERNAL = 4
} messageType;
// Sensor variables that can be used in sketches
typedef enum {
V_TEMP,V_HUM, V_LIGHT, V_DIMMER, V_PRESSURE, V_FORECAST, V_RAIN,
V_RAINRATE, V_WIND, V_GUST, V_DIRECTION, V_UV, V_WEIGHT, V_DISTANCE,
V_IMPEDANCE, V_ARMED, V_TRIPPED, V_WATT, V_KWH, V_SCENE_ON, V_SCENE_OFF,
V_HEATER, V_HEATER_SW, V_LIGHT_LEVEL, V_VAR1, V_VAR2, V_VAR3, V_VAR4, V_VAR5,
V_UP, V_DOWN, V_STOP, V_IR_SEND, V_IR_RECEIVE, V_FLOW, V_VOLUME, V_LOCK_STATUS
} variableType;
// Internal messages
typedef enum {
I_BATTERY_LEVEL, I_BATTERY_DATE, I_LAST_TRIP, I_TIME, I_VERSION, I_REQUEST_ID,
I_INCLUSION_MODE, I_RELAY_NODE, I_LAST_UPDATE, I_PING, I_PING_ACK,
I_LOG_MESSAGE, I_CHILDREN, I_UNIT, I_SKETCH_NAME, I_SKETCH_VERSION
} internalMessageType;
// Sensor types
typedef enum {
S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, S_DIMMER, S_COVER, S_TEMP, S_HUM, S_BARO, S_WIND,
S_RAIN, S_UV, S_WEIGHT, S_POWER, S_HEATER, S_DISTANCE, S_LIGHT_LEVEL, S_ARDUINO_NODE,
S_ARDUINO_RELAY, S_LOCK, S_IR, S_WATER
} sensor;
// Possible return values by validate() when doing crc check of received message.
enum {
VALIDATE_OK, VALIDATE_BAD_CRC, VALIDATE_BAD_VERSION
};
// The message structure
typedef struct {
uint8_t crc : 8; // 8 bits crc
uint8_t version : 3; // 3 bits protocol version
uint8_t binary : 1; // 1 bit. Data is binary and should be encoded when sent to sensor net gateway
uint8_t from : 8; // 8 bits. RadioId of sender node
uint8_t to : 8; // 8 bits. RadioId of destination node
uint8_t last : 8; // 8 bits. RadioId of last node this message passed
uint8_t childId : 8; // 1 byte. Up to MAX_CHILD_DEVICES child sensors per radioId
uint8_t messageType : 4; // 4 bits. Type of message. See messageType
uint8_t type : 8; // 8 bits. variableType or deviceType depending on messageType
} header_s;
typedef struct {
header_s header;
char data[MAX_MESSAGE_LENGTH - sizeof(header_s) + 1]; // Each message can transfer a payload. Add one extra byte for \0
} message_s;
class Sensor : public RF24
{
public:
/**
* Constructor
*
* Creates a new instance of Sensor class.
*
* @param _cepin The pin attached to RF24 Chip Enable on the RF module (defualt 9)
* @param _cspin The pin attached to RF24 Chip Select (default 10)
*/
Sensor(uint8_t _cepin=9, uint8_t _cspin=10);
/**
* Begin operation of the MySensors library
*
* Call this in setup(), before calling any other sensor net library methods.
* @param radioId The unique id (1-254) for this sensor. Default 255 (auto mode).
* @param paLevel Radio PA Level for this sensor. Default RF24_PA_MAX
* @param channel Radio channel. Default is channel 76
* @param dataRate Radio transmission speed. Default RF24_2MBPS
*/
void begin(uint8_t radioId=AUTO, rf24_pa_dbm_e paLevel=RF24_PA_LEVEL, uint8_t channel=RF24_CHANNEL, rf24_datarate_e dataRate=RF24_DATARATE);
uint8_t getRadioId();
/**
* The arduino node must send a presentation of all the sensors connected before any
* variable changes will be registered on sensor net gateway side.
* Usually it's good to present all sensors when arduino starts up (setup).
* Note that gateway must be in include mode to register new devices.
*
* @param childId The unique child id for the different sensors connected to this arduino. 0-254.
* @param sensorType Sensor types to create. They will be numbered from 0 up to 127.
*/
void sendSensorPresentation(uint8_t childId, uint8_t sensorType);
/**
* Sends a variable change to sensor net gateway (or another sensor if radioId is specified)
*
* @param radioId The radioId of other node in radio network (used for communicating between sensors)
* @param childId The child id for which to update variable. Value can be 0-254.
* @param variableType The variableType to update
* @param value New value of the variable
*/
void sendVariable(uint8_t childId, uint8_t variableType, const char *value);
void sendVariable(uint8_t childId, uint8_t variableType, double value, int decimals);
void sendVariable(uint8_t childId, uint8_t variableType, unsigned long value);
void sendVariable(uint8_t childId, uint8_t variableType, long value);
void sendVariable(uint8_t childId, uint8_t variableType, unsigned int value);
void sendVariable(uint8_t childId, uint8_t variableType, int value);
void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, const char *value);
void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, double value, int decimals);
void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, unsigned long value);
void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, long value);
void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, unsigned int value);
void sendVariable(uint8_t radioId, uint8_t childId, uint8_t variableType, int value);
/**
* Sends sketch info to sensor net gateway for this radio node.
* @param name String containing a short Sketch name
* @param version String containing a short Sketch version
*/
void sendSketchInfo(const char *name, const char *version);
/**
* Sends sketch name to sensor net gateway for this radio node.
* @param info String containing a short Sketch name and version
*/
void sendSketchName(const char *name);
/**
* Sends sketch version to sensor net gateway for this radio node.
* @param info String containing a short Sketch name and version
*/
void sendSketchVersion(const char *version);
/**
* Sends battery status to sensor net gateway for this radio node.
* @param value Set a value between 0-100(%)
*/
void sendBatteryLevel(int value);
/**
* Requests a variable value from sensor net gateway (or another sensor). This method will not wait for an answer.
* You should use mesageAvailable/getMessage to pick up the response.
*
* @param radioId The radioId of other node in radio network (used when fetching from other node)
* @param childId The unique child id for the different sensors connected to this arduino. 0-254.
* @param variableType The variableType to fetch
*/
void requestStatus(uint8_t childId, uint8_t variableType);
void requestStatus(uint8_t radioId, int8_t childId, uint8_t variableType);
/**
* Requests status for a sensor variable from sensor net gateway. Waits until message arrives.
*
* @param radioId The radioId of other node in radio network (used when fetching from other node)
* @param childId The unique child id for the different sensors connected to this arduino. 0-254.
* @param variableType The variableType to fetch
* @return The variable value
*/
char *getStatus(uint8_t childId, uint8_t variableType);
char *getStatus(uint8_t radioId, int8_t childId, uint8_t variableType);
/**
* Fetches time from sensor net gateway
*/
void requestTime();
unsigned long getTime();
/**
* Fetches unit setting from sensor net gateway. Returns true if metric system has been selected which means
* that sensor should report it's information in: celsius, meter, cm, gram, km/h, m/s etc..
* If false is returned the sensor should report data in imperial system which means
* fahrenheit, feet, gallon, mph etc...
*/
void requestIsMetricSystem();
bool isMetricSystem();
/**
* Busy waits until there is a message for this node available to be read
*/
message_s waitForMessage(void);
/**
* Returns true if there is a message addressed for this node is available to be read
*/
boolean messageAvailable(void);
/**
* Returns the last received message
*/
message_s getMessage(void);
/**
* Validates consistency of the message including CRC and protocol version
*/
uint8_t validate(uint8_t length);
#ifdef DEBUG
void debugPrint(const char *fmt, ... );
int freeRam();
#endif
protected:
uint8_t failedTransmissions;
boolean isRelay;
uint8_t radioId;
uint8_t distance; // This nodes distance to sensor net gateway (number of hops)
uint8_t relayId;
message_s msg; // Buffer for incoming messages.
char convBuffer[20];
void setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate);
void findRelay();
boolean send(message_s message, int length);
boolean sendWrite(uint8_t dest, message_s message, int length);
boolean readMessage();
void buildMsg(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binary);
void sendInternal(uint8_t variableType, const char *value);
boolean sendVariableAck();
boolean sendData(uint8_t from, uint8_t to, uint8_t childId, uint8_t messageType, uint8_t type, const char *data, uint8_t length, boolean binaryMessage);
private:
message_s ack; // Buffer for ack messages.
void initializeRadioId();
uint8_t crc8Message(message_s, uint8_t length);
char* get(uint8_t nodeId, uint8_t childId, uint8_t sendType, uint8_t receiveType, uint8_t variableType);
char *getInternal(uint8_t variableType);
};
#endif
+1 -1
View File
@@ -6,6 +6,6 @@
#ifndef Version_h
#define Version_h
#define LIBRARY_VERSION "1.3b3 (67f4ca1)"
#define LIBRARY_VERSION "1.4"
#endif
@@ -0,0 +1,196 @@
/*
Vera Arduino MQ2
connect the MQ2 sensor as follows :
A H A >>> 5V
B >>> A0
H >>> GND
B >>> 10K ohm >>> GND
Contribution: epierre
Based on http://sandboxelectronics.com/?p=165
License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)
Modified by HEK to work in 1.4
*/
#include <SPI.h>
#include <MySensor.h>
#include <Wire.h>
#define CHILD_ID_MQ 0
/************************Hardware Related Macros************************************/
#define MQ_SENSOR_ANALOG_PIN (0) //define which analog input channel you are going to use
#define RL_VALUE (5) //define the load resistance on the board, in kilo ohms
#define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO,
//which is derived from the chart in datasheet
/***********************Software Related Macros************************************/
#define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase
#define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the
//cablibration phase
#define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation
#define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in
//normal operation
/**********************Application Related Macros**********************************/
#define GAS_LPG (0)
#define GAS_CO (1)
#define GAS_SMOKE (2)
/*****************************Globals***********************************************/
unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
//VARIABLES
float Ro = 10000.0; // this has to be tuned 10K Ohm
int val = 0; // variable to store the value coming from the sensor
float valMQ =0.0;
float lastMQ =0.0;
float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve.
//with these two points, a line is formed which is "approximately equivalent"
//to the original curve.
//data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59)
float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve.
//with these two points, a line is formed which is "approximately equivalent"
//to the original curve.
//data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15)
float SmokeCurve[3] ={2.3,0.53,-0.44}; //two points are taken from the curve.
//with these two points, a line is formed which is "approximately equivalent"
//to the original curve.
//data format:{ x, y, slope}; point1: (lg200, 0.53), point2:(lg10000,-0.22)
MySensor gw;
MyMessage msg(CHILD_ID_MQ, V_VAR1);
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Air Quality Sensor", "1.0");
// Register all sensors to gateway (they will be created as child devices)
gw.present(CHILD_ID_MQ, S_AIR_QUALITY);
Ro = MQCalibration(MQ_SENSOR_ANALOG_PIN); //Calibrating the sensor. Please make sure the sensor is in clean air
//when you perform the calibration
}
void loop()
{
uint16_t valMQ = MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO);
Serial.println(val);
Serial.print("LPG:");
Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_LPG) );
Serial.print( "ppm" );
Serial.print(" ");
Serial.print("CO:");
Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO) );
Serial.print( "ppm" );
Serial.print(" ");
Serial.print("SMOKE:");
Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_SMOKE) );
Serial.print( "ppm" );
Serial.print("\n");
if (valMQ != lastMQ) {
gw.send(msg.set((int)ceil(valMQ)));
lastMQ = ceil(valMQ);
}
gw.sleep(SLEEP_TIME); //sleep for: sleepTime
}
/****************** MQResistanceCalculation ****************************************
Input: raw_adc - raw value read from adc, which represents the voltage
Output: the calculated sensor resistance
Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
across the load resistor and its resistance, the resistance of the sensor
could be derived.
************************************************************************************/
float MQResistanceCalculation(int raw_adc)
{
return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));
}
/***************************** MQCalibration ****************************************
Input: mq_pin - analog channel
Output: Ro of the sensor
Remarks: This function assumes that the sensor is in clean air. It use
MQResistanceCalculation to calculates the sensor resistance in clean air
and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about
10, which differs slightly between different sensors.
************************************************************************************/
float MQCalibration(int mq_pin)
{
int i;
float val=0;
for (i=0;i<CALIBARAION_SAMPLE_TIMES;i++) { //take multiple samples
val += MQResistanceCalculation(analogRead(mq_pin));
delay(CALIBRATION_SAMPLE_INTERVAL);
}
val = val/CALIBARAION_SAMPLE_TIMES; //calculate the average value
val = val/RO_CLEAN_AIR_FACTOR; //divided by RO_CLEAN_AIR_FACTOR yields the Ro
//according to the chart in the datasheet
return val;
}
/***************************** MQRead *********************************************
Input: mq_pin - analog channel
Output: Rs of the sensor
Remarks: This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
The Rs changes as the sensor is in the different consentration of the target
gas. The sample times and the time interval between samples could be configured
by changing the definition of the macros.
************************************************************************************/
float MQRead(int mq_pin)
{
int i;
float rs=0;
for (i=0;i<READ_SAMPLE_TIMES;i++) {
rs += MQResistanceCalculation(analogRead(mq_pin));
delay(READ_SAMPLE_INTERVAL);
}
rs = rs/READ_SAMPLE_TIMES;
return rs;
}
/***************************** MQGetGasPercentage **********************************
Input: rs_ro_ratio - Rs divided by Ro
gas_id - target gas type
Output: ppm of the target gas
Remarks: This function passes different curves to the MQGetPercentage function which
calculates the ppm (parts per million) of the target gas.
************************************************************************************/
int MQGetGasPercentage(float rs_ro_ratio, int gas_id)
{
if ( gas_id == GAS_LPG ) {
return MQGetPercentage(rs_ro_ratio,LPGCurve);
} else if ( gas_id == GAS_CO ) {
return MQGetPercentage(rs_ro_ratio,COCurve);
} else if ( gas_id == GAS_SMOKE ) {
return MQGetPercentage(rs_ro_ratio,SmokeCurve);
}
return 0;
}
/***************************** MQGetPercentage **********************************
Input: rs_ro_ratio - Rs divided by Ro
pcurve - pointer to the curve of the target gas
Output: ppm of the target gas
Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
of the line could be derived if y(rs_ro_ratio) is provided. As it is a
logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
value.
************************************************************************************/
int MQGetPercentage(float rs_ro_ratio, float *pcurve)
{
return (pow(10,( ((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
}
@@ -2,17 +2,12 @@
// Instructions for measuring battery capacity on A0 are available in the follwoing forum
// thread: http://forum.micasaverde.com/index.php/topic,20078.0.html
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
int BATTERY_SENSE_PIN = A0; // select the input pin for the battery sense point
Sensor gw;
Sleep sleep;
MySensor gw;
unsigned long SLEEP_TIME = 900000; // sleep time between reads (seconds * 1000 milliseconds)
int oldBatteryPcnt = 0;
@@ -31,7 +26,7 @@ void loop()
// get the battery Voltage
int sensorValue = analogRead(BATTERY_SENSE_PIN);
Serial.println(sensorValue);
// 1M, 470K divider across battery and using internal ADC ref of 1.1V
// Sense point is bypassed with 0.1 uF cap to reduce noise at that point
// ((1e6+470e3)/470e3)*1.1 = Vmax = 3.44 Volts
@@ -52,14 +47,5 @@ void loop()
gw.sendBatteryLevel(batteryPcnt);
oldBatteryPcnt = batteryPcnt;
}
// delay to allow transmissions to gateway to be completed before sleep
delay(500);
// Power down the radio. Note that the radio will get powered back up
// on the next write() call
gw.powerDown();
sleep.pwrDownMode(); // set sleep mode
sleep.sleepDelay(SLEEP_TIME); // sleep for SLEEP_TIME
gw.sleep(SLEEP_TIME);
}
@@ -2,18 +2,20 @@
// Connect button or door/window reed switch between
// digitial I/O pin 3 (BUTTON_PIN below) and GND.
#include <Sensor.h>
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Bounce2.h>
#define CHILD_ID 3
#define BUTTON_PIN 3 // Arduino Digital I/O pin for button/reed switch
Sensor gw;
MySensor gw;
Bounce debouncer = Bounce();
int oldValue=-1;
// Change to V_LIGHT if you use S_LIGHT in presentation below
MyMessage msg(CHILD_ID,V_TRIPPED);
void setup()
{
gw.begin();
@@ -29,8 +31,8 @@ void setup()
// Register binary input sensor to gw (they will be created as child devices)
// You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
// If S_LIGHT is used, remember to update variable type you send in below.
gw.sendSensorPresentation(BUTTON_PIN, S_DOOR);
// If S_LIGHT is used, remember to update variable type you send in. See "msg" above.
gw.present(CHILD_ID, S_DOOR);
}
@@ -43,7 +45,7 @@ void loop()
if (value != oldValue) {
// Send in the new value
gw.sendVariable(BUTTON_PIN, V_TRIPPED, value==HIGH ? "1" : "0"); // Change to V_LIGHT if you use S_LIGHT in presentation above
gw.send(msg.set(value==HIGH ? 1 : 0));
oldValue = value;
}
}
@@ -1,61 +1,67 @@
#include <Sleep_n0m1.h>
// Example sketch showing how to send in OneWire temperature readings
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include <RF24.h>
#include <Sensor.h>
#define ONE_WIRE_BUS 3 // Pin where dallase sensor is connected
#define MAX_ATTACHED_DS18B20 16
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
Sensor gw;
Sleep sleep;
MySensor gw;
float lastTemperature[MAX_ATTACHED_DS18B20];
int numSensors=0;
boolean receivedConfig = false;
boolean metric = true;
// Initialize temperature message
MyMessage msg(0,V_TEMP);
void setup()
{
// Startup OneWire
sensors.begin();
// Startup and initialize MySensors library. Set callback for incoming messages.
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Temperature Sensor", "1.0");
// Fetch the number of attached sensors
// Fetch the number of attached temperature sensors
numSensors = sensors.getDeviceCount();
// Register all sensors to gw (they will be created as child devices)
// Present all sensors to controller
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
gw.sendSensorPresentation(i, S_TEMP);
gw.present(i, S_TEMP);
}
metric = gw.isMetricSystem();
}
void loop()
{
sensors.requestTemperatures(); // Fetch temperatures from Dallas
delay(100);
// Process incoming messages (like config from server)
gw.process();
// Fetch temperatures from Dallas sensors
sensors.requestTemperatures();
// Read temperatures and send them to controller
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
// Fetch and round temperature to one decimal
float temperature = static_cast<float>(static_cast<int>((metric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
float temperature = static_cast<float>(static_cast<int>((gw.getConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
// Only send data if temperature has changed and no error
if (lastTemperature[i] != temperature && temperature != -127.00) {
gw.powerUp(); // Powerup introduces a small delay (which is missing in radio.write powerup)
// Send variable (using registered shortcut) to gw
gw.sendVariable(i, V_TEMP, temperature, 1);
// Send in the new temperature
gw.send(msg.setSensor(i).set(temperature,1));
lastTemperature[i]=temperature;
}
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(500);
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
gw.sleep(SLEEP_TIME);
}
@@ -0,0 +1,107 @@
/***
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* DESCRIPTION
* This sketch provides a Dimmable LED Light using PWM and based Henrik Ekblad
* <henrik.ekblad@gmail.com> Vera Arduino Sensor project.
* Developed by Bruce Lacey, inspired by Hek's MySensor's example sketches.
*
* The circuit uses a MOSFET for Pulse-Wave-Modulation to dim the attached LED or LED strip.
* The MOSFET Gate pin is connected to Arduino pin 3 (LED_PIN), the MOSFET Drain pin is connected
* to the LED negative terminal and the MOSFET Source pin is connected to ground.
*
* This sketch is extensible to support more than one MOSFET/PWM dimmer per circuit.
*
* REVISION HISTORY
* Version 1.0 - February 15, 2014 - Bruce Lacey
* Version 1.1 - August 13, 2014 - Converted to 1.4 (hek)
*
***/
#define SN "DimmableLED"
#define SV "1.1"
#include <MySensor.h>
#include <SPI.h>
#define LED_PIN 3 // Arduino pin attached to MOSFET Gate pin
#define FADE_DELAY 10 // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
MySensor gw(9,10);
static int currentLevel = 0; // Current dim level...
MyMessage dimmerMsg(0, V_DIMMER);
MyMessage lightMsg(0, V_LIGHT);
/***
* Dimmable LED initialization method
*/
void setup()
{
Serial.println( SN );
gw.begin( incomingMessage );
// Register the LED Dimmable Light with the gateway
gw.present( 0, S_DIMMER );
gw.sendSketchInfo(SN, SV);
// Pull the gateway's current dim level - restore light level upon sendor node power-up
gw.request( 0, V_DIMMER );
}
/***
* Dimmable LED main processing loop
*/
void loop()
{
gw.process();
}
void incomingMessage(const MyMessage &message) {
if (message.type == V_LIGHT || message.type == V_DIMMER) {
// Retrieve the power or dim level from the incoming request message
int requestedLevel = atoi( message.data );
// Adjust incoming level if this is a V_LIGHT variable update [0 == off, 1 == on]
requestedLevel *= ( message.type == V_LIGHT ? 100 : 1 );
// Clip incoming level to valid range of 0 to 100
requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
requestedLevel = requestedLevel < 0 ? 0 : requestedLevel;
Serial.print( "Changing level to " );
Serial.print( requestedLevel );
Serial.print( ", from " );
Serial.println( currentLevel );
fadeToLevel( requestedLevel );
// Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
gw.send(lightMsg.set(currentLevel > 0 ? 1 : 0));
// hek comment: Is this really nessesary?
gw.send( dimmerMsg.set(currentLevel) );
}
}
/***
* This method provides a graceful fade up/down effect
*/
void fadeToLevel( int toLevel ) {
int delta = ( toLevel - currentLevel ) < 0 ? -1 : 1;
while ( currentLevel != toLevel ) {
currentLevel += delta;
analogWrite( LED_PIN, (int)(currentLevel / 100. * 255) );
delay( FADE_DELAY );
}
}
@@ -1,19 +1,16 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
#include <NewPing.h>
#define CHILD_ID 1
#define TRIGGER_PIN 6 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 5 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 300 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
unsigned long SLEEP_TIME = 5; // Sleep time between reads (in seconds)
unsigned long SLEEP_TIME = 5000; // Sleep time between reads (in milliseconds)
Sensor gw;
MySensor gw;
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.
Sleep sleep;
MyMessage msg(CHILD_ID, V_DISTANCE);
int lastDist;
boolean metric = true;
@@ -25,30 +22,23 @@ void setup()
gw.sendSketchInfo("Distance Sensor", "1.0");
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(0, S_DISTANCE);
metric = gw.isMetricSystem();
gw.present(CHILD_ID, S_DISTANCE);
boolean metric = gw.getConfig().isMetric;
}
void loop()
{
int dist = metric?sonar.ping_cm():sonar.ping_in();
Serial.print("Ping: ");
Serial.print(dist); // Convert ping time to distance in cm and print result (0 = outside set distance range)
Serial.println(metric?" cm":" in");
if (dist != lastDist) {
gw.sendVariable(0, V_DISTANCE, dist);
gw.send(msg.set(dist));
lastDist = dist;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
gw.sleep(SLEEP_TIME);
}
@@ -0,0 +1,80 @@
/*
Arduino Dust Sensort
connect the sensor as follows :
VCC >>> 5V
A >>> A0
GND >>> GND
Based on: http://www.dfrobot.com/wiki/index.php/Sharp_GP2Y1010AU
Authors: Cyrille Médard de Chardon (serialC), Christophe Trefois (Trefex)
Contribution: epierre
COnverted to 1.4 by Henrik Ekblad
The dust sensor used (see purchase guide for latest link):
http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=171259125886&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg
*/
#include <MySensor.h>
#include <SPI.h>
#define CHILD_ID_DUST 0
#define DUST_SENSOR_ANALOG_PIN 1
unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds)
//VARIABLES
int val = 0; // variable to store the value coming from the sensor
float valDUST =0.0;
float lastDUST =0.0;
int samplingTime = 280;
int deltaTime = 40;
int sleepTime = 9680;
float voMeasured = 0;
float calcVoltage = 0;
float dustDensity = 0;
MySensor gw;
MyMessage dustMsg(CHILD_ID_DUST, V_DUST_LEVEL);
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Dust Sensor", "1.1");
// Register all sensors to gateway (they will be created as child devices)
gw.present(CHILD_ID_DUST, S_DUST);
}
void loop()
{
uint16_t voMeasured = analogRead(DUST_SENSOR_ANALOG_PIN);// Get DUST value
// 0 - 5V mapped to 0 - 1023 integer values
// recover voltage
calcVoltage = voMeasured * (5.0 / 1024.0);
// linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/
// Chris Nafis (c) 2012
dustDensity = (0.17 * calcVoltage - 0.1)*1000;
Serial.print("Raw Signal Value (0-1023): ");
Serial.print(voMeasured);
Serial.print(" - Voltage: ");
Serial.print(calcVoltage);
Serial.print(" - Dust Density: ");
Serial.println(dustDensity); // unit: ug/m3
if (ceil(dustDensity) != lastDUST) {
gw.send(dustMsg.set((int)ceil(dustDensity)));
lastDUST = ceil(dustDensity);
}
gw.sleep(SLEEP_TIME);
}
@@ -7,26 +7,19 @@
// sleepmode. So we cannot make this sensor sleep if we also want
// to calculate/report watt-number.
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your light sensor. (Only 2 and 3 generates interrupt!)
#define PULSE_FACTOR 1000 // Nummber of blinks per KWH of your meeter
#define SLEEP_MODE false // Watt-value can only be reported when sleep mode is false.
#define MAX_WATT 10000 // Max watt value to report. This filetrs outliers.
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 1 // Id of the sensor child
unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't wnat to spam the gateway.
Sensor gw;
Sleep sleep;
//double kwhPerBlink = 1.0/((double)PULSE_FACTOR);
#define CHILD_ID 1 // Id of the sensor child
unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway.
MySensor gw;
double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour
boolean pcReceived = false;
volatile unsigned long pulseCount = 0;
volatile unsigned long lastBlink = 0;
volatile unsigned long watt = 0;
@@ -34,21 +27,24 @@ unsigned long oldPulseCount = 0;
unsigned long oldWatt = 0;
double oldKwh;
unsigned long lastSend;
MyMessage wattMsg(CHILD_ID,V_WATT);
MyMessage kwhMsg(CHILD_ID,V_KWH);
MyMessage pcMsg(CHILD_ID,V_VAR1);
void setup()
{
gw.begin();
gw.begin(incomingMessage);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Energy Meter", "1.0");
// Register this device as power sensor
gw.sendSensorPresentation(CHILD_ID, S_POWER);
gw.present(CHILD_ID, S_POWER);
// Fetch last known pulse count value from gw
pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1));
Serial.print("Last pulse count from gw:");
Serial.println(pulseCount);
gw.request(CHILD_ID, V_VAR1);
attachInterrupt(INTERRUPT, onPulse, RISING);
lastSend=millis();
}
@@ -56,15 +52,17 @@ void setup()
void loop()
{
unsigned long now = millis();
gw.process();
unsigned long now = millis();
// Only send values at a maximum frequency or woken up from sleep
if (SLEEP_MODE || now - lastSend > 1000*SEND_FREQUENCY) {
bool sendTime = now - lastSend > SEND_FREQUENCY;
if (pcReceived && (SLEEP_MODE || sendTime)) {
// New watt value has been calculated
if (!SLEEP_MODE && watt != oldWatt) {
// Check that we dont get unresonable large watt value.
// could hapen when long wraps or false interrupt triggered
if (watt<((unsigned long)MAX_WATT)) {
gw.sendVariable(CHILD_ID, V_WATT, watt); // Send watt value to gw
gw.send(wattMsg.set(watt)); // Send watt value to gw
}
Serial.print("Watt:");
Serial.println(watt);
@@ -73,26 +71,31 @@ void loop()
// Pulse cout has changed
if (pulseCount != oldPulseCount) {
gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send kwh value to gw
gw.send(pcMsg.set(pulseCount)); // Send pulse count value to gw
double kwh = ((double)pulseCount/((double)PULSE_FACTOR));
oldPulseCount = pulseCount;
//Serial.print("Pulse count:");
//Serial.println(pulseCount);
if (kwh != oldKwh) {
gw.sendVariable(CHILD_ID, V_KWH, kwh, 4); // Send kwh value to gw
//Serial.print("KWH:");
//Serial.println(kwh);
gw.send(kwhMsg.set(kwh, 4)); // Send kwh value to gw
oldKwh = kwh;
}
}
}
lastSend = now;
} else if (sendTime && !pcReceived) {
// No count received. Try requesting it again
gw.request(CHILD_ID, V_VAR1);
}
if (SLEEP_MODE) {
delay(300); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime
gw.sleep(SEND_FREQUENCY);
}
}
void incomingMessage(const MyMessage &message) {
if (message.type==V_VAR1) {
pulseCount = oldPulseCount = message.getLong();
Serial.print("Received last pulse count from gw:");
Serial.println(pulseCount);
pcReceived = true;
}
}
@@ -0,0 +1,149 @@
/*
* Copyright (C) 2013 Henrik Ekblad <henrik.ekblad@gmail.com>
*
* Contribution by a-lurker
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* DESCRIPTION
* The EthernetGateway sends data received from sensors to the ethernet link.
* The gateway also accepts input on ethernet interface, which is then sent out to the radio network.
*
* The GW code is designed for Arduino 328p / 16MHz. ATmega168 does not have enough memory to run this program.
*
* COMPILING
* You must make sure to disable DEBUG in Sensor.h before compiling this sketch. Othervise the sketch won't fit in program space when downloading.
* For UIPEthernet(ENC28J60) usage: Note that I had to disable UDP and DHCP support in uipethernet-conf.h to reduce space. (which meas you ave to choose a static IP)
* For WizNET usage: Do *not* install the provided UIPEthernet-library. Remove UIPEthernet-include below and uncomment the Ethernet.h.
*
* VERA CONFIGURATION:
* Enter "ip-number:port" in the ip-field of the Arduino GW device. This will temporarily override any serial configuration for the Vera plugin.
* E.g. If you want to use the defualt values in this sketch enter: 192.168.178.66:5003
*
* LED purposes:
* - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved
* - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly
* - ERR (red) - fast blink on error during transmission error or recieve crc error
*
* ----------- Connection guide ---------------------------------------------------------------------------
* 13 Radio & Ethernet SPI SCK
* 12 Radio & Ethernet SPI MISO (SO)
* 11 Radio & Ethernet SPI MOSI (SI)
* 10 Ethernet SPI Slave Select (CS) Pin 10, the SS pin, must be an o/p to put SPI in master mode
* 9 Radio TX LED using on board LED (optional) +5V -> LED -> 270-330 Ohm resistor -> pin 9.
* 8 Radio RX LED (optional) +5V -> LED -> 270-330 Ohm resistor -> pin 8.
* 7 Radio error LED (optional) +5V -> LED -> 270-330 Ohm resistor -> pin 7.
* 6 Radio SPI Slave Select
* 5 Radio Chip Enable
* 3 Inclusion mode button (optional), 10K pull down to GND, button to VCC)
* 2 Radio IRQ pin (optional), W5100 Int, if linked to pin 2)
* -----------------------------------------------------------------------------------------------------------
* Powering: both NRF24l01 radio and Ethernet(ENC28J60) uses 3.3V
*/
#include <SPI.h>
#include <MySensor.h>
#include <MyGateway.h>
#include <stdarg.h>
// Use this if you have attached a Ethernet ENC28J60 shields
//#include <UIPEthernet.h>
// Use this fo WizNET module and Arduino Ethernet Shield
#include <Ethernet.h>
#define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled
#define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button
#define RADIO_CE_PIN 5 // radio chip enable
#define RADIO_SPI_SS_PIN 6 // radio SPI serial select
#define RADIO_ERROR_LED_PIN 7 // Error led pin
#define RADIO_RX_LED_PIN 8 // Receive led pin
#define RADIO_TX_LED_PIN 9 // the PCB, on board LED
#define IP_PORT 5003 // The port you want to open
IPAddress myIp (192, 168, 178, 66); // Configure your static ip-address here
// The MAC address can be anything you want but should be unique on your network.
// Newer boards have a MAC address printed on the underside of the PCB, which you can (optionally) use.
// Note that most of the Ardunio examples use "DEAD BEEF FEED" for the MAC address.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // DEAD BEEF FEED
// a R/W server on the port
EthernetServer server = EthernetServer(IP_PORT);
// No blink or button functionality. Use the vanilla constructor.
MyGateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME);
// Uncomment this constructor if you have leds and include button attached to your gateway
//MyGateway gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN);
char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface
int inputPos = 0;
void setup()
{
// Initialize gateway at maximum PA level, channel 70 and callback for write operations
gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet);
Ethernet.begin(mac, myIp);
// give the Ethernet interface a second to initialize
delay(1000);
// start listening for clients
server.begin();
}
// This will be called when data should be written to ethernet
void writeEthernet(char *writeBuffer) {
server.write(writeBuffer);
}
void loop()
{
// if an incoming client connects, there will be
// bytes available to read via the client object
EthernetClient client = server.available();
if (client) {
// if got 1 or more bytes
if (client.available()) {
// read the bytes incoming from the client
char inChar = client.read();
if (inputPos<MAX_RECEIVE_LENGTH-1) {
// if newline then command is complete
if (inChar == '\n') {
// a command was issued by the client
// we will now try to send it to the actuator
inputString[inputPos] = 0;
// echo the string to the serial port
Serial.print(inputString);
gw.parseAndSend(inputString);
// clear the string:
inputPos = 0;
} else {
// add it to the inputString:
inputString[inputPos] = inChar;
inputPos++;
}
} else {
// Incoming message too long. Throw away
inputPos = 0;
}
}
}
gw.processRadioMessage();
}
@@ -1,21 +1,20 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
#include <DHT.h>
#define CHILD_ID_HUM 0
#define CHILD_ID_TEMP 1
#define HUMIDITY_SENSOR_DIGITAL_PIN 3
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
Sensor gw;
MySensor gw;
DHT dht;
Sleep sleep;
float lastTemp;
float lastHum;
boolean metric = true;
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
void setup()
{
@@ -26,10 +25,10 @@ void setup()
gw.sendSketchInfo("Humidity", "1.0");
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID_HUM, S_HUM);
gw.sendSensorPresentation(CHILD_ID_TEMP, S_TEMP);
gw.present(CHILD_ID_HUM, S_HUM);
gw.present(CHILD_ID_TEMP, S_TEMP);
metric = gw.isMetricSystem();
metric = gw.getConfig().isMetric;
}
void loop()
@@ -44,9 +43,9 @@ void loop()
if (!metric) {
temperature = dht.toFahrenheit(temperature);
}
gw.sendVariable(CHILD_ID_TEMP, V_TEMP, temperature, 1);
Serial.print("T: ");
Serial.println(temperature);
gw.send(msgTemp.set(temperature, 1));
Serial.print("T: ");
Serial.println(temperature);
}
float humidity = dht.getHumidity();
@@ -54,18 +53,12 @@ void loop()
Serial.println("Failed reading humidity from DHT");
} else if (humidity != lastHum) {
lastHum = humidity;
gw.sendVariable(CHILD_ID_HUM, V_HUM, humidity, 1);
gw.send(msgHum.set(humidity, 1));
Serial.print("H: ");
Serial.println(humidity);
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
gw.sleep(SLEEP_TIME); //sleep a bit
}
@@ -5,10 +5,8 @@
// When binary light on is clicked - sketch will send volume up ir command
// When binary light off is clicked - sketch will send volume down ir command
#include <Sensor.h>
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <IRLib.h>
int RECV_PIN = 8;
@@ -20,39 +18,26 @@ IRrecv irrecv(RECV_PIN);
IRdecode decoder;
//decode_results results;
unsigned int Buffer[RAWBUF];
Sensor gw;
MySensor gw;
MyMessage msg(CHILD_1, V_VAR1);
void setup()
{
irrecv.enableIRIn(); // Start the ir receiver
decoder.UseExtnBuf(Buffer);
gw.begin();
gw.begin(incomingMessage);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("IR Sensor", "1.0");
// Register a sensors to gw. Use binary light for test purposes.
gw.sendSensorPresentation(CHILD_1, S_LIGHT);
gw.present(CHILD_1, S_LIGHT);
}
void loop()
{
if (gw.messageAvailable()) {
message_s message = gw.getMessage();
int incomingRelayStatus = atoi(message.data);
if (incomingRelayStatus == 1) {
irsend.send(NEC, 0x1EE17887, 32); // Vol up yamaha ysp-900
} else {
irsend.send(NEC, 0x1EE1F807, 32); // Vol down yamaha ysp-900
}
// Start receiving ir again...
irrecv.enableIRIn();
}
gw.process();
if (irrecv.GetResults(&decoder)) {
irrecv.resume();
decoder.decode();
@@ -61,11 +46,26 @@ void loop()
char buffer[10];
sprintf(buffer, "%08lx", decoder.value);
// Send ir result to gw
gw.sendVariable(CHILD_1, V_VAR1, buffer);
gw.send(msg.set(buffer));
}
}
void incomingMessage(const MyMessage &message) {
// We only expect one type of message from controller. But we better check anyway.
if (message.type==V_LIGHT) {
int incomingRelayStatus = message.getInt();
if (incomingRelayStatus == 1) {
irsend.send(NEC, 0x1EE17887, 32); // Vol up yamaha ysp-900
} else {
irsend.send(NEC, 0x1EE1F807, 32); // Vol down yamaha ysp-900
}
// Start receiving ir again...
irrecv.enableIRIn();
}
}
// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
// void * to work around compiler issue
@@ -16,23 +16,19 @@
*/
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <RF24.h>
#include <EEPROM.h>
#include <Sensor.h>
#include <MySensor.h>
#include <BH1750.h>
#include <Wire.h>
#define CHILD_ID_LIGHT 0
#define LIGHT_SENSOR_ANALOG_PIN 0
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
BH1750 lightSensor;
Sensor gw;
Sleep sleep;
int lastlux;
BH1750 lightSensor;
MySensor gw;
MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
uint16_t lastlux;
void setup()
{
@@ -42,7 +38,7 @@ void setup()
gw.sendSketchInfo("Light Lux Sensor", "1.0");
// Register all sensors to gateway (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
lightSensor.begin();
}
@@ -52,14 +48,9 @@ void loop()
uint16_t lux = lightSensor.readLightLevel();// Get Lux value
Serial.println(lux);
if (lux != lastlux) {
gw.sendVariable(CHILD_ID_LIGHT, V_LIGHT_LEVEL, lux);
gw.send(msg.set(lux));
lastlux = lux;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
gw.sleep(SLEEP_TIME);
}
@@ -1,17 +1,14 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <RF24.h>
#include <EEPROM.h>
#include <Sensor.h>
#include <MySensor.h>
#define CHILD_ID_LIGHT 0
#define LIGHT_SENSOR_ANALOG_PIN 0
unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds)
unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
Sensor gw;
MySensor gw;
MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
int lastLightLevel;
Sleep sleep;
void setup()
{
@@ -21,7 +18,7 @@ void setup()
gw.sendSketchInfo("Light Sensor", "1.0");
// Register all sensors to gateway (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
}
void loop()
@@ -29,16 +26,10 @@ void loop()
int lightLevel = (1023-analogRead(LIGHT_SENSOR_ANALOG_PIN))/10.23;
Serial.println(lightLevel);
if (lightLevel != lastLightLevel) {
gw.sendVariable(CHILD_ID_LIGHT, V_LIGHT_LEVEL, lightLevel);
gw.send(msg.set(lightLevel));
lastLightLevel = lightLevel;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime
gw.sleep(SLEEP_TIME);
}
@@ -0,0 +1,129 @@
/* MyMQTT Broker Gateway 0.1b
Created by Daniel Wiegert <daniel.wiegert@gmail.com>
Based on MySensors Ethernet Gateway by Henrik Ekblad <henrik.ekblad@gmail.com>
http://www.mysensors.org
Requires MySensors lib 1.4b
* Change below; TCP_IP, TCP_PORT, TCP_MAC
This will listen on your selected TCP_IP:TCP_PORT below, Please change TCP_MAC your liking also.
*1 -> NOTE: Keep first byte at x2, x6, xA or xE (replace x with any hex value) for using Local Ranges.
*2 You can use standard pin set-up as MySensors recommends or if you own a IBOARD you may change
the radio-pins below if you hardware mod your iBoard. see [URL BELOW] for more details.
http://forum.mysensors.org/topic/224/iboard-cheap-single-board-ethernet-arduino-with-radio/5
* Don't forget to look at the definitions in libraries\MySensors\MyMQTT.h!
define TCPDUMP and connect serial interface if you have problems, please write on
http://forum.mysensors.org/ and explain your problem, include serial output. Don't forget to
turn on DEBUG in libraries\MySensors\MyConfig.h also.
MQTT_FIRST_SENSORID is for 'DHCP' server in MyMQTT. You may limit the ID's with FIRST and LAST definition.
If you want your manually configured below 20 set MQTT_FIRST_SENSORID to 20.
To disable: set MQTT_FIRST_SENSORID to 255.
MQTT_BROKER_PREFIX is the leading prefix for your nodes. This can be only one char if like.
MQTT_SEND_SUBSCRIPTION is if you want the MyMQTT to send a empty payload message to your nodes.
This can be useful if you want to send latest state back to the MQTT client. Just check if incoming
message has any length or not.
Example: if (msg.type==V_LIGHT && strlen(msg.getString())>0) otherwise the code might do strange things.
* Address-layout is : [MQTT_BROKER_PREFIX]/[NodeID]/[SensorID]/V_[SensorType]
NodeID and SensorID is uint8 (0-255) number.
Last segment is translation of the sensor type, look inside MyMQTT.cpp for the definitions.
User can change this to their needs. We have also left some space for custom types.
Special: (sensor 255 reserved for special commands)
You can receive a node sketch name with MyMQTT/20/255/V_Sketch_name (or version with _version)
To-do:
Special commands : clear or set EEPROM Values, Send REBOOT and Receive reboot for MyMQTT itself.
Be able to send ACK so client returns the data being sent.
... Please come with ideas!
What to do with publish messages.
Test in more MQTT clients, So far tested in openhab and MyMQTT for Android (Not my creation)
- http://www.openhab.org/
- https://play.google.com/store/apps/details?id=at.tripwire.mqtt.client&hl=en
... Please notify me if you use this broker with other software.
* How to set-up Openhab and MQTTGateway:
http://forum.mysensors.org/topic/303/mqtt-broker-gateway
*/
#include <SPI.h>
#include <MySensor.h>
#include <MyMQTT.h>
#include <Ethernet.h>
// * Use this for IBOARD modded to use standard MISO/MOSI/SCK, see note *1 above!
/*
#define RADIO_CE_PIN 3 // radio chip enable
#define RADIO_SPI_SS_PIN 8 // radio SPI serial select
#define RADIO_ERROR_LED_PIN A2 // Error led pin
#define RADIO_RX_LED_PIN A1 // Receive led pin
#define RADIO_TX_LED_PIN A0 // the PCB, on board LED*/
// * Use this for default configured pro mini / nano etc :
///*
#define RADIO_CE_PIN 5 // radio chip enable
#define RADIO_SPI_SS_PIN 6 // radio SPI serial select
#define RADIO_ERROR_LED_PIN 7 // Error led pin
#define RADIO_RX_LED_PIN 8 // Receive led pin
#define RADIO_TX_LED_PIN 9 // the PCB, on board LED*/
#define TCP_PORT 1883 // Set your MQTT Broker Listening port.
IPAddress TCP_IP ( 192, 168, 0, 234 ); // Configure your static ip-address here
uint8_t TCP_MAC[] = { 0x02, 0xDE, 0xAD, 0x00, 0x00, 0x42 }; // Mac-address - You should change this! see note *2 above!
//////////////////////////////////////////////////////////////////
EthernetServer server = EthernetServer(TCP_PORT);
MyMQTT gw(RADIO_CE_PIN, RADIO_SPI_SS_PIN);
void processEthernetMessages() {
char inputString[MQTT_MAX_PACKET_SIZE] = "";
uint8_t inputSize = 0;
EthernetClient client = server.available();
if (client) {
while (client.available()) {
char inChar = client.read();
inputString[inputSize] = inChar;
inputSize++;
}
#ifdef TCPDUMP
Serial.print("<<");
char buf[4];
for (uint8_t a=0; a<inputSize; a++) { sprintf(buf,"%02X ", (uint8_t)inputString[a]); Serial.print(buf); } Serial.println("");
#endif
gw.processMQTTMessage(inputString, inputSize);
}
}
void writeEthernet(const char *writeBuffer, uint8_t *writeSize) {
#ifdef TCPDUMP
Serial.print(">>");
char buf[4];
for (uint8_t a=0; a<*writeSize; a++) { sprintf(buf,"%02X ",(uint8_t)writeBuffer[a]); Serial.print(buf); } Serial.println("");
#endif
server.write((const uint8_t *)writeBuffer, *writeSize);
}
int main(void) {
init();
Ethernet.begin(TCP_MAC, TCP_IP);
delay(1000); // Wait for Ethernet to get configured.
gw.begin(RF24_PA_LEVEL_GW, RF24_CHANNEL, RF24_DATARATE, writeEthernet, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN);
server.begin();
while (1) {
processEthernetMessages();
gw.processRadioMessage();
}
}
@@ -1,15 +1,14 @@
#include <Sleep_n0m1.h>
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
unsigned long SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds)
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!)
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 0 // Id of the sensor child
#define CHILD_ID 1 // Id of the sensor child
Sensor gw;
Sleep sleep;
MySensor gw;
// Initialize motion message
MyMessage msg(CHILD_ID, V_TRIPPED);
void setup()
{
@@ -20,7 +19,8 @@ void setup()
pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
gw.present(CHILD_ID, S_MOTION);
}
void loop()
@@ -29,14 +29,10 @@ void loop()
boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
Serial.println(tripped);
gw.sendVariable(CHILD_ID, V_TRIPPED, tripped?"1":"0"); // Send tripped value to gw
gw.send(msg.set(tripped?"1":"0")); // Send tripped value to gw
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(200); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepInterrupt(INTERRUPT,CHANGE);
// Sleep until interrupt comes in on motion sensor. Send update every two minute.
gw.sleep(INTERRUPT,CHANGE, SLEEP_TIME);
}
@@ -1,21 +1,19 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
#define LIGHT_SENSOR_ANALOG_PIN 0
unsigned long SLEEP_TIME = 60; // Sleep time between reads (in seconds)
#define BARO_CHILD 0
#define TEMP_CHILD 1
unsigned long SLEEP_TIME = 60000; // Sleep time between reads (in seconds)
Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor
Sensor gw;
MySensor gw;
float lastPressure = -1;
float lastTemp = -1;
int lastForecast = -1;
Sleep sleep;
char *weather[] = {"stable","sunny","cloudy","unstable","thunderstorm","unknown"};
int minutes;
float pressureSamples[180];
@@ -24,6 +22,9 @@ bool firstRound = true;
float pressureAvg[7];
float dP_dt;
boolean metric;
MyMessage tempMsg(TEMP_CHILD, V_TEMP);
MyMessage pressureMsg(BARO_CHILD, V_PRESSURE);
MyMessage forecastMsg(BARO_CHILD, V_FORECAST);
void setup() {
gw.begin();
@@ -37,14 +38,13 @@ void setup() {
}
// Register sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(0, S_BARO);
gw.sendSensorPresentation(1, S_TEMP);
metric = gw.isMetricSystem();
gw.present(BARO_CHILD, S_BARO);
gw.present(TEMP_CHILD, S_TEMP);
metric = gw.getConfig().isMetric;
}
void loop() {
float pressure = bmp.readPressure();
float pressure = bmp.readPressure()/100;
float temperature = bmp.readTemperature();
if (!metric) {
@@ -64,17 +64,17 @@ void loop() {
if (temperature != lastTemp) {
gw.sendVariable(1, V_TEMP, temperature,1);
gw.send(tempMsg.set(temperature,1));
lastTemp = temperature;
}
if (pressure != lastPressure) {
gw.sendVariable(0, V_PRESSURE, pressure, 0);
gw.send(pressureMsg.set(pressure, 0));
lastPressure = pressure;
}
if (forecast != lastForecast) {
gw.sendVariable(0, V_FORECAST, weather[forecast]);
gw.send(forecastMsg.set(weather[forecast]));
lastForecast = forecast;
}
@@ -89,12 +89,7 @@ void loop() {
5 = "Unknown (More Time needed)
*/
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
delay(1000); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SLEEP_TIME * 1000); // sleep to conserve power
gw.sleep(SLEEP_TIME);
}
int sample(float pressure) {
@@ -0,0 +1,161 @@
/*
RFID Lock sensor/actuator
Henrik Ekblad <henrik.ekblad@mysensors.org>
Use RFID tag to lock/unlock a door or trigger a scene on your controller.
This example sketch allows you to add an optional relay or solenoid
which can be activated/opened by RFID or controller.
Use the I2C wiring option for your RFID module and connect to the following Arduino pins.
RFID Arduino
----- -------
GND -> GND
VCC -> +5V
SCL -> A5
SDA -> A4
Use normal wiring for NRF24L01 radio
Attach a optional relay or solonoid lock to pin 4
*/
#include <MySensor.h>
#include <SPI.h>
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
// Add your valid rfid keys here. To find you your key just run sketch; hold your new RFID tag in fron ot the reader;
// and copy the key from serial output of this sketch.
const uint8_t maxKeyLength = 7;
uint8_t validKeys[][maxKeyLength] = {
{ 0xB3, 0xC6, 0xD9, 0x80, 0x00, 0x00, 0x00 },
{ 0, 0, 0, 0, 0, 0, 0 }, // ADD YOUR KEYS HERE!
{ 0, 0, 0, 0, 0, 0, 0 }};
int keyCount = sizeof validKeys / maxKeyLength;
#define CHILD_ID 99 // Id of the sensor child
/*Pin definitions*/
const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock.
bool lockStatus;
MySensor gw;
MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS);
PN532_I2C pn532i2c(Wire);
PN532 nfc(pn532i2c);
void setup() {
pinMode(lockPin, OUTPUT);
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
Serial.print("Couldn't find PN53x board");
while (1); // halt
}
Serial.print("Found NFC chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
// Set the max number of retry attempts to read from a card
// This prevents us from waiting forever for a card, which is
// the default behaviour of the PN532.
nfc.setPassiveActivationRetries(0x3);
// configure board to read RFID tags
nfc.SAMConfig();
// Init mysensors library
gw.begin(incomingMessage);
gw.sendSketchInfo("RFID Lock", "1.0");
gw.present(CHILD_ID, S_LOCK);
lockStatus = gw.loadState(0); // Read last lock status from eeprom
setLockState(lockStatus, true); // Now set the last known state and send it to controller
}
void loop() {
gw.process(); // Process incomming messages
boolean success;
uint8_t key[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t currentKeyLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &key[0], &currentKeyLength);
if (success) {
Serial.print("Found tag id: ");
for (uint8_t i=0; i < currentKeyLength; i++)
{
if (i>0) Serial.print(",");
Serial.print("0x");Serial.print(key[i], HEX);
}
for (uint8_t i=currentKeyLength; i < maxKeyLength; i++)
{
Serial.print(",0x00");
}
Serial.println("");
boolean valid = false;
// Compare this key to the valid once registered here in sketch
for (int i=0;i<keyCount && !valid;i++) {
for (int j=0;i<currentKeyLength && !valid;j++) {
if (key[j] != validKeys[i][j]) {
break;
}
if (j==currentKeyLength-1) {
valid = true;
}
}
}
if (valid) {
// Switch lock status
setLockState(!lockStatus, true);
}
// Wait for card/tag to leave reader
while(nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &key[0], &currentKeyLength));
}
}
// Unlocks the door.
void setLockState(bool state, bool send){
if (state)
Serial.println("open lock");
else
Serial.println("close lock");
if (send)
gw.send(lockMsg.set(state));
digitalWrite(lockPin, state);
gw.saveState(0,state);
lockStatus = state;
}
void incomingMessage(const MyMessage &message) {
// We only expect one type of message from controller. But we better check anyway.
if (message.type==V_LOCK_STATUS) {
// Change relay state
setLockState(message.getBool(), false);
// Write some debug info
Serial.print("Incoming lock status:");
Serial.println(message.getBool());
}
}
@@ -1,64 +1,53 @@
// Example sketch showing how to control physical relays.
// This example will remember relay state even after power failure.
#include <Relay.h>
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#define RELAY_1 3 // Arduino Digital I/O pin number for first relay (second on pin+1 etc)
#define NUMBER_OF_RELAYS 1
#define RELAY_ON 0
#define RELAY_OFF 1
#define NUMBER_OF_RELAYS 1 // Total number of attached relays
#define RELAY_ON 1 // GPIO value to write to turn on attached relay
#define RELAY_OFF 0 // GPIO value to write to turn off attached relay
Sensor gw;
MySensor gw;
void setup()
{
gw.begin();
{
// Initialize library and add callback for incoming messages
gw.begin(incomingMessage, AUTO, true);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Relay", "1.0");
// Register all sensors to gw (they will be created as child devices)
for (int i=0; i<NUMBER_OF_RELAYS;i++) {
gw.sendSensorPresentation(RELAY_1+i, S_LIGHT);
}
// Fetch relay status
for (int i=0; i<NUMBER_OF_RELAYS;i++) {
// Make sure relays are off when starting up
digitalWrite(RELAY_1+i, RELAY_OFF);
for (int sensor=1, pin=RELAY_1; sensor<=NUMBER_OF_RELAYS;sensor++, pin++) {
// Register all sensors to gw (they will be created as child devices)
gw.present(sensor, S_LIGHT);
// Then set relay pins in output mode
pinMode(RELAY_1+i, OUTPUT);
// Request/wait for relay status
gw.getStatus(RELAY_1+i, V_LIGHT);
setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw
pinMode(pin, OUTPUT);
// Set relay to last known state (using eeprom storage)
digitalWrite(pin, gw.loadState(sensor)?RELAY_ON:RELAY_OFF);
}
}
/*
* Example on how to asynchronously check for new messages from gw
*/
void loop()
{
if (gw.messageAvailable()) {
message_s message = gw.getMessage();
setRelayStatus(message);
}
// Alway process incoming messages whenever possible
gw.process();
}
void setRelayStatus(message_s message) {
if (message.header.messageType==M_SET_VARIABLE &&
message.header.type==V_LIGHT) {
int incomingRelayStatus = atoi(message.data);
void incomingMessage(const MyMessage &message) {
// We only expect one type of message from controller. But we better check anyway.
if (message.type==V_LIGHT) {
// Change relay state
digitalWrite(message.header.childId, incomingRelayStatus==1?RELAY_ON:RELAY_OFF);
digitalWrite(message.sensor-1+RELAY_1, message.getBool()?RELAY_ON:RELAY_OFF);
// Store state in eeprom
gw.saveState(message.sensor, message.getBool());
// Write some debug info
Serial.print("Incoming change for relay on pin:");
Serial.print(message.header.childId);
Serial.print("Incoming change for sensor:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(incomingRelayStatus);
}
Serial.println(message.getBool());
}
}
@@ -1,50 +1,51 @@
// Example sketch för a "light switch" where you can control light or something
// else from both vera and a local physical button (connected between digital
// pin 3 and +5V).
// pin 3 and GND).
// This node also works as a repeader for other nodes
#include <Relay.h>
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Bounce2.h>
#define RELAY_PIN 4 // Arduino Digital I/O pin number for relay
#define BUTTON_PIN 3 // Arduino Digital I/O pin number for button (with 10k pulldown)
#define BUTTON_PIN 3 // Arduino Digital I/O pin number for button
#define CHILD_ID 1 // Id of the sensor child
#define RELAY_ON 0
#define RELAY_OFF 1
#define RELAY_ON 1
#define RELAY_OFF 0
int buttVal;
int previousVal;
int state;
int switched = 0;
long pressTime = 0; // the last time the button pin was pressed
long debounce = 200; // the debounce time, increase if the output flickers
Relay gw;
Bounce debouncer = Bounce();
int oldValue=0;
bool state;
MySensor gw;
MyMessage msg(CHILD_ID,V_LIGHT);
void setup()
{
gw.begin();
gw.begin(incomingMessage, AUTO, true);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Relay & Button", "1.0");
// Set buttonPin as Input
pinMode(BUTTON_PIN, INPUT);
buttVal = digitalRead(BUTTON_PIN); //read initial state
// Setup the button
pinMode(BUTTON_PIN,INPUT);
// Activate internal pull-up
digitalWrite(BUTTON_PIN,HIGH);
// After setting up the button, setup debouncer
debouncer.attach(BUTTON_PIN);
debouncer.interval(5);
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_LIGHT);
gw.present(CHILD_ID, S_LIGHT);
// Make sure relays are off when starting up
digitalWrite(RELAY_PIN, RELAY_OFF);
// Then set relay pins in output mode
pinMode(RELAY_PIN, OUTPUT);
// Request/wait for relay status
gw.getStatus(CHILD_ID, V_LIGHT);
setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw
// Set relay to last known state (using eeprom storage)
state = gw.loadState(CHILD_ID);
digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
}
@@ -53,40 +54,34 @@ void setup()
*/
void loop()
{
if (gw.messageAvailable()) {
// ot new messsage from gw
message_s message = gw.getMessage();
setRelayStatus(message);
gw.process();
debouncer.update();
// Get the update value
int value = debouncer.read();
if (value != oldValue && value==0) {
gw.send(msg.set(state?false:true), true); // Send new state and request ack back
}
buttVal = digitalRead(BUTTON_PIN); // read input value and store it in val
if (buttVal == HIGH && previousVal == LOW) {
// Start counter from when button was pressed
pressTime = millis();
switched = 0;
}
if (buttVal == HIGH && switched == 0 && millis() - pressTime > debounce) {
// Switch state if button pressed more than 200 msec
state = state==1?0:1;
gw.sendVariable(CHILD_ID, V_LIGHT, state); // We will receive an ack message
switched = 1; // No more switches until button is released
}
previousVal = buttVal;
oldValue = value;
}
void incomingMessage(const MyMessage &message) {
// We only expect one type of message from controller. But we better check anyway.
if (message.isAck()) {
Serial.println("This is an ack from gateway");
}
void setRelayStatus(message_s message) {
if (message.header.type==V_LIGHT) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE
state = atoi(message.data);
if (message.type == V_LIGHT && strlen(msg.getString()) != 0) {
// Change relay state
digitalWrite(RELAY_PIN, state==1?RELAY_ON:RELAY_OFF);
state = message.getBool();
digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
// Store state in eeprom
gw.saveState(CHILD_ID, state);
// Write some debug info
Serial.print(message.header.messageType == M_ACK_VARIABLE ? "Button":"Gateway");
Serial.print(" change. New state: ");
Serial.println(state == 1 ?"on":"off" );
}
Serial.print("Incoming change for sensor:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(message.getBool());
}
}
@@ -1,30 +0,0 @@
// Example sketch showing how to create a node thay relays messages
// from nodes far from gateway back to gateway.
// Important here is that node uses Relay-class and calls
// gw.messageAvailable() frequently. This sketch should not
// sleep.
#include <Relay.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
Relay gw;
void setup()
{
gw.begin();
//Send the sensor node sketch version information to the gateway
gw.sendSketchInfo("Relaying Node", "1.0");
}
void loop()
{
// By calling this regularely you route messages in the background
if (gw.messageAvailable()) {
// Handleincoming message for this node here...
}
}
@@ -0,0 +1,25 @@
// Example sketch showing how to create a node thay repeates messages
// from nodes far from gateway back to gateway.
// It is important that nodes that has enabled repeater mode calls
// gw.preocess() frequently. This node should never sleep.
#include <MySensor.h>
#include <SPI.h>
MySensor gw;
void setup()
{
// The third argument enables repeater mode.
gw.begin(NULL, AUTO, true);
//Send the sensor node sketch version information to the gateway
gw.sendSketchInfo("Repeater Node", "1.0");
}
void loop()
{
// By calling process() you route messages in the background
gw.process();
}
@@ -0,0 +1,349 @@
/* Secret Knock Sensor
See original instructions here (note: The MySensors adopted code might differ in wiring. The instructions below is correct):
https://learn.adafruit.com/secret-knock-activated-drawer-lock/
Version 13.10.31 Built with Arduino IDE 1.0.5
By Steve Hoefer http://grathio.com
Adapted to MySensors by Henrik Ekblad
Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
(In short: Do what you want, as long as you credit me, don't relicense it, and don't sell it or use it in anything you sell without contacting me.)
------Wiring------
Pin 0: Program button used for recording a new Knock (connect Pin0 -> button -> GND)
Pin 1: Optional: Connect LED here (remember resisor in series)
Pin 2: Optional: Piezo element (for beeps).
Pin 5: A sound sensor (digital output) for sensing knocks. See MySensors purchase guide. I used this: http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=200941260251&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg
Pin 4: Connects to either 1. Relay which open door or lock or
2. transistor that opens a solenoid lock when HIGH (see adafruit guide for this option).
Connect radio according as usual(you can skip IRQ pin)
http://www.mysensors.org/build/connect_radio
*/
#include <MySensor.h>
#include <SPI.h>
#define CHILD_ID 99 // Id of the sensor child
const byte eepromValid = 121; // If the first byte in eeprom is this then the data is valid.
/*Pin definitions*/
const int programButton = 0; // (Digital 0) Record A New Knock button.
const int ledPin = 1; // (Digital 1) The LED pin (if any)
const int knockSensor = 5; // (Digital 5) for using the microphone digital output (tune knob to register knock)
const int audioOut = 2; // (Digital 2) for using the peizo as an output device. (Thing that goes beep.)
const int lockPin = 4; // (Digital 4) The pin that activates the relay/solenoid lock.
/*Tuning constants. Changing the values below changes the behavior of the device.*/
int threshold = 3; // Minimum signal from the piezo to register as a knock. Higher = less sensitive. Typical values 1 - 10
const int rejectValue = 25; // If an individual knock is off by this percentage of a knock we don't unlock. Typical values 10-30
const int averageRejectValue = 15; // If the average timing of all the knocks is off by this percent we don't unlock. Typical values 5-20
const int knockFadeTime = 150; // Milliseconds we allow a knock to fade before we listen for another one. (Debounce timer.)
const int lockOperateTime = 2500; // Milliseconds that we operate the lock solenoid latch before releasing it.
const int maximumKnocks = 20; // Maximum number of knocks to listen for.
const int knockComplete = 1200; // Longest time to wait for a knock before we assume that it's finished. (milliseconds)
byte secretCode[maximumKnocks] = {50, 25, 25, 50, 100, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initial setup: "Shave and a Hair Cut, two bits."
int knockReadings[maximumKnocks]; // When someone knocks this array fills with the delays between knocks.
int knockSensorValue = 0; // Last reading of the knock sensor.
boolean programModeActive = false; // True if we're trying to program a new knock.
bool lockStatus;
MySensor gw;
MyMessage lockMsg(CHILD_ID, V_LOCK_STATUS);
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(knockSensor, INPUT);
pinMode(lockPin, OUTPUT);
pinMode(programButton, INPUT);
digitalWrite(programButton, HIGH); // Enable internal pull up
gw.begin(incomingMessage);
gw.sendSketchInfo("Secret Knock", "1.0");
gw.present(CHILD_ID, S_LOCK);
readSecretKnock(); // Load the secret knock (if any) from EEPROM.
digitalWrite(lockPin, HIGH); // Unlock the door for a bit when we power up. For system check and to allow a way in if the key is forgotten
delay(500); // Wait a short time
lockStatus = gw.loadState(0); // Read last lock status from eeprom
setLockState(lockStatus, true); // Now set the last known state and send it to controller
delay(500); // This delay is here because the solenoid lock returning to place can otherwise trigger and inadvertent knock.
}
void loop() {
gw.process(); // Process incomming messages
// Listen for any knock at all.
knockSensorValue = digitalRead(knockSensor);
if (digitalRead(programButton) == LOW){ // is the program button pressed?
delay(100); // Cheap debounce.
if (digitalRead(programButton) == LOW){
if (programModeActive == false){ // If we're not in programming mode, turn it on.
programModeActive = true; // Remember we're in programming mode.
digitalWrite(ledPin, HIGH); // Turn on the red light too so the user knows we're programming.
chirp(500, 1500); // And play a tone in case the user can't see the LED.
chirp(500, 1000);
} else { // If we are in programing mode, turn it off.
programModeActive = false;
digitalWrite(ledPin, LOW);
chirp(500, 1000); // Turn off the programming LED and play a sad note.
chirp(500, 1500);
delay(500);
}
while (digitalRead(programButton) == LOW){
delay(10); // Hang around until the button is released.
}
}
delay(250); // Another cheap debounce. Longer because releasing the button can sometimes be sensed as a knock.
}
if (knockSensorValue == 0) {
if (programModeActive == true){ // Blink the LED when we sense a knock.
digitalWrite(ledPin, LOW);
} else {
digitalWrite(ledPin, HIGH);
}
knockDelay();
if (programModeActive == true){ // Un-blink the LED.
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
listenToSecretKnock(); // We have our first knock. Go and see what other knocks are in store...
}
}
// Records the timing of knocks.
void listenToSecretKnock(){
int i = 0;
// First reset the listening array.
for (i=0; i < maximumKnocks; i++){
knockReadings[i] = 0;
}
int currentKnockNumber = 0; // Position counter for the array.
int startTime = millis(); // Reference for when this knock started.
int now = millis();
do { // Listen for the next knock or wait for it to timeout.
knockSensorValue = digitalRead(knockSensor);
if (knockSensorValue == 0){ // Here's another knock. Save the time between knocks.
Serial.println("knock");
now=millis();
knockReadings[currentKnockNumber] = now - startTime;
currentKnockNumber ++;
startTime = now;
if (programModeActive==true){ // Blink the LED when we sense a knock.
digitalWrite(ledPin, LOW);
} else {
digitalWrite(ledPin, HIGH);
}
knockDelay();
if (programModeActive == true){ // Un-blink the LED.
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
now = millis();
// Stop listening if there are too many knocks or there is too much time between knocks.
} while ((now-startTime < knockComplete) && (currentKnockNumber < maximumKnocks));
Serial.println("end");
//we've got our knock recorded, lets see if it's valid
if (programModeActive == false){ // Only do this if we're not recording a new knock.
if (validateKnock() == true){
// Lock/unlock door
chirp(500, 1500); // And play a tone in case the user can't see the LED.
chirp(500, 1000);
setLockState(!lockStatus, true);
} else {
Serial.println("fail unlock");
// knock is invalid. Blink the LED as a warning to others.
for (i=0; i < 4; i++){
digitalWrite(ledPin, HIGH);
delay(50);
digitalWrite(ledPin, LOW);
delay(50);
}
}
} else { // If we're in programming mode we still validate the lock because it makes some numbers we need, we just don't do anything with the return.
validateKnock();
}
}
// Unlocks the door.
void setLockState(bool state, bool send){
if (state)
Serial.println("open lock");
else
Serial.println("close lock");
if (send)
gw.send(lockMsg.set(state));
digitalWrite(ledPin, state);
digitalWrite(lockPin, state);
gw.saveState(0,state);
lockStatus = state;
delay(500); // This delay is here because releasing the latch can cause a vibration that will be sensed as a knock.
}
// Checks to see if our knock matches the secret.
// Returns true if it's a good knock, false if it's not.
boolean validateKnock(){
int i = 0;
int currentKnockCount = 0;
int secretKnockCount = 0;
int maxKnockInterval = 0; // We use this later to normalize the times.
for (i=0;i<maximumKnocks;i++){
if (knockReadings[i] > 0){
currentKnockCount++;
}
if (secretCode[i] > 0){
secretKnockCount++;
}
if (knockReadings[i] > maxKnockInterval){ // Collect normalization data while we're looping.
maxKnockInterval = knockReadings[i];
}
}
// If we're recording a new knock, save the info and get out of here.
if (programModeActive == true){
for (i=0; i < maximumKnocks; i++){ // Normalize the time between knocks. (the longest time = 100)
secretCode[i] = map(knockReadings[i], 0, maxKnockInterval, 0, 100);
}
saveSecretKnock(); // save the result to EEPROM
programModeActive = false;
playbackKnock(maxKnockInterval);
return false;
}
if (currentKnockCount != secretKnockCount){ // Easiest check first. If the number of knocks is wrong, don't unlock.
return false;
}
/* Now we compare the relative intervals of our knocks, not the absolute time between them.
(ie: if you do the same pattern slow or fast it should still open the door.)
This makes it less picky, which while making it less secure can also make it
less of a pain to use if you're tempo is a little slow or fast.
*/
int totaltimeDifferences = 0;
int timeDiff = 0;
for (i=0; i < maximumKnocks; i++){ // Normalize the times
knockReadings[i]= map(knockReadings[i], 0, maxKnockInterval, 0, 100);
timeDiff = abs(knockReadings[i] - secretCode[i]);
if (timeDiff > rejectValue){ // Individual value too far out of whack. No access for this knock!
return false;
}
totaltimeDifferences += timeDiff;
}
// It can also fail if the whole thing is too inaccurate.
if (totaltimeDifferences / secretKnockCount > averageRejectValue){
return false;
}
return true;
}
// reads the secret knock from EEPROM. (if any.)
void readSecretKnock(){
byte reading;
int i;
reading = gw.loadState(1);
if (reading == eepromValid){ // only read EEPROM if the signature byte is correct.
for (int i=0; i < maximumKnocks ;i++){
secretCode[i] = gw.loadState(i+2);
}
}
}
//saves a new pattern too eeprom
void saveSecretKnock(){
gw.saveState(1, 0); // clear out the signature. That way we know if we didn't finish the write successfully.
for (int i=0; i < maximumKnocks; i++){
gw.saveState(i+2, secretCode[i]);
}
gw.saveState(1, eepromValid); // all good. Write the signature so we'll know it's all good.
}
// Plays back the pattern of the knock in blinks and beeps
void playbackKnock(int maxKnockInterval){
digitalWrite(ledPin, LOW);
delay(1000);
digitalWrite(ledPin, HIGH);
chirp(200, 1800);
for (int i = 0; i < maximumKnocks ; i++){
digitalWrite(ledPin, LOW);
// only turn it on if there's a delay
if (secretCode[i] > 0){
delay(map(secretCode[i], 0, 100, 0, maxKnockInterval)); // Expand the time back out to what it was. Roughly.
digitalWrite(ledPin, HIGH);
chirp(200, 1800);
}
}
digitalWrite(ledPin, LOW);
}
// Deals with the knock delay thingy.
void knockDelay(){
int itterations = (knockFadeTime / 20); // Wait for the peak to dissipate before listening to next one.
for (int i=0; i < itterations; i++){
delay(10);
analogRead(knockSensor); // This is done in an attempt to defuse the analog sensor's capacitor that will give false readings on high impedance sensors.
delay(10);
}
}
// Plays a non-musical tone on the piezo.
// playTime = milliseconds to play the tone
// delayTime = time in microseconds between ticks. (smaller=higher pitch tone.)
void chirp(int playTime, int delayTime){
long loopTime = (playTime * 1000L) / delayTime;
pinMode(audioOut, OUTPUT);
for(int i=0; i < loopTime; i++){
digitalWrite(audioOut, HIGH);
delayMicroseconds(delayTime);
digitalWrite(audioOut, LOW);
}
pinMode(audioOut, INPUT);
}
void incomingMessage(const MyMessage &message) {
// We only expect one type of message from controller. But we better check anyway.
if (message.type==V_LOCK_STATUS) {
// Change relay state
setLockState(message.getBool(), false);
// Write some debug info
Serial.print("Incoming lock status:");
Serial.println(message.getBool());
}
}
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2013 Henrik Ekblad <henrik.ekblad@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* DESCRIPTION
* The ArduinoGateway prints data received from sensors on the serial link.
* The gateway accepts input on seral which will be sent out on radio network.
*
* The GW code is designed for Arduino Nano 328p / 16MHz
*
* Wire connections (OPTIONAL):
* - Inclusion button should be connected between digital pin 3 and GND
* - RX/TX/ERR leds need to be connected between +5V (anode) and digital ping 6/5/4 with resistor 270-330R in a series
*
* LEDs (OPTIONAL):
* - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved
* - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly
* - ERR (red) - fast blink on error during transmission error or recieve crc error
*/
#include <SPI.h>
#include <MySensor.h>
#include <MyGateway.h>
#include <stdarg.h>
#define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled
#define INCLUSION_MODE_PIN 3 // Digital pin used for inclusion mode button
MyGateway gw(DEFAULT_CE_PIN, DEFAULT_CS_PIN, INCLUSION_MODE_TIME, INCLUSION_MODE_PIN, 6, 5, 4);
char inputString[MAX_RECEIVE_LENGTH] = ""; // A string to hold incoming commands from serial/ethernet interface
int inputPos = 0;
boolean commandComplete = false; // whether the string is complete
void setup()
{
gw.begin();
}
void loop()
{
gw.processRadioMessage();
if (commandComplete) {
// A command wass issued from serial interface
// We will now try to send it to the actuator
gw.parseAndSend(inputString);
commandComplete = false;
inputPos = 0;
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inputPos<MAX_RECEIVE_LENGTH-1 && !commandComplete) {
if (inChar == '\n') {
inputString[inputPos] = 0;
commandComplete = true;
} else {
// add it to the inputString:
inputString[inputPos] = inChar;
inputPos++;
}
} else {
// Incoming message too long. Throw away
inputPos = 0;
}
}
}
@@ -6,11 +6,9 @@
// Contribution by: Derek Macias
#include <Sensor.h>
#include <MySensor.h>
#include <SPI.h>
#include <EEPROM.h>
#include <Servo.h>
#include <RF24.h>
#define SERVO_DIGITAL_OUT_PIN 3
#define SERVO_MIN 0 // Fine tune your servos min. 0-180
@@ -18,59 +16,55 @@
#define DETACH_DELAY 900 // Tune this to let your movement finish before detaching the servo
#define CHILD_ID 10 // Id of the sensor child
Sensor gw;
MySensor gw;
MyMessage msg(CHILD_ID, V_DIMMER);
Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created Sensor gw(9,10);
unsigned long timeOfLastChange = 0;
bool attachedServo = false;
void setup()
{
gw.begin();
// Attach method for incoming messages
gw.begin(incomingMessage);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Servo", "1.0");
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_COVER);
gw.present(CHILD_ID, S_COVER);
// Fetch servo state at startup
gw.getStatus(CHILD_ID, V_DIMMER);
setRelayStatus(gw.getMessage()); // Wait here until status message arrive from gw
// Request last servo state at startup
gw.request(CHILD_ID, V_DIMMER);
}
void loop()
{
if (gw.messageAvailable()) {
// New messsage from gw
message_s message = gw.getMessage();
setRelayStatus(message);
}
gw.process();
if (attachedServo && millis() - timeOfLastChange > DETACH_DELAY) {
myservo.detach();
attachedServo = false;
}
}
void setRelayStatus(message_s message) {
void incomingMessage(const MyMessage &message) {
myservo.attach(SERVO_DIGITAL_OUT_PIN);
attachedServo = true;
if (message.header.type==V_DIMMER) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE
int val = atoi(message.data);
if (message.type==V_DIMMER) { // This could be M_ACK_VARIABLE or M_SET_VARIABLE
int val = message.getInt();
myservo.write(SERVO_MAX + (SERVO_MIN-SERVO_MAX)/100 * val); // sets the servo position 0-180
// Write some debug info
Serial.print("Servo changed. new state: ");
Serial.println(val);
} else if (message.header.type==V_UP) {
} else if (message.type==V_UP) {
Serial.println("Servo UP command");
myservo.write(SERVO_MIN);
gw.sendVariable(CHILD_ID, V_DIMMER, 100);
} else if (message.header.type==V_DOWN) {
gw.send(msg.set(100));
} else if (message.type==V_DOWN) {
Serial.println("Servo DOWN command");
myservo.write(SERVO_MAX);
gw.sendVariable(CHILD_ID, V_DIMMER, 0);
} else if (message.header.type==V_STOP) {
gw.send(msg.set(0));
} else if (message.type==V_STOP) {
Serial.println("Servo STOP command");
myservo.detach();
attachedServo = false;
@@ -1,15 +1,12 @@
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
#define DIGITAL_INPUT_SOIL_SENSOR 3 // Digital input did you attach your soil sensor.
#define INTERRUPT DIGITAL_INPUT_SOIL_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 0 // Id of the sensor child
Sensor gw;
Sleep sleep;
MySensor gw;
MyMessage msg(CHILD_ID, V_TRIPPED);
int lastSoilValue = -1;
void setup()
@@ -18,10 +15,10 @@ void setup()
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Soil Moisture Sensor", "1.0");
pinMode(DIGITAL_INPUT_SOIL_SENSOR, INPUT); // sets the soil sensor digital pin as input
// sets the soil sensor digital pin as input
pinMode(DIGITAL_INPUT_SOIL_SENSOR, INPUT);
// Register all sensors to gw (they will be created as child devices)
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
gw.present(CHILD_ID, S_MOTION);
}
void loop()
@@ -30,14 +27,11 @@ void loop()
int soilValue = digitalRead(DIGITAL_INPUT_SOIL_SENSOR); // 1 = Not triggered, 0 = In soil with water
if (soilValue != lastSoilValue) {
Serial.println(soilValue);
gw.sendVariable(CHILD_ID, V_TRIPPED, soilValue==0?"1":"0"); // Send the inverse to gw as tripped should be when no water in soil
gw.send(msg.set(soilValue==0?1:0)); // Send the inverse to gw as tripped should be when no water in soil
lastSoilValue = soilValue;
}
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepInterrupt(INTERRUPT,CHANGE);
// Power down the radio and arduino until digital input changes.
gw.sleep(INTERRUPT,CHANGE);
}
@@ -1,66 +0,0 @@
// Example sketch showing how to request time from vera
// and how to set a cutom variable on a sensor
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <Time.h>
#define CHILD_ID 0 // Id of the sensor child
Sensor gw;
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Clock", "1.0");
// Register any sensortype. This example we just create a motion sensor.
gw.sendSensorPresentation(CHILD_ID, S_MOTION);
setSyncProvider(getTime);
setSyncInterval(3600); // get time from gw every hour
}
unsigned long getTime() {
return gw.getTime(); // Vera is time provider
}
void loop()
{
// long time = gw.getTime();
// setTime(time);
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
// Ok now send time back to gw and store in in Variable1
// of the motion sensor device
gw.sendVariable(CHILD_ID, V_VAR1, minute());
// wait 10 seconds...
delay(10000);
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
@@ -0,0 +1,55 @@
// Example sketch showing how to request time from controller
#include <SPI.h>
#include <MySensor.h>
#include <Time.h>
MySensor gw;
boolean timeReceived = false;
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Clock", "1.0");
// Request time from controller.
gw.requestTime(receiveTime);
}
// This is called when a new time value was received
void receiveTime(unsigned long time) {
// Ok, set incoming time
setTime(time);
timeReceived = true;
}
void loop()
{
gw.process();
// Print time
if (timeReceived) {
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
}
void printDigits(int digits){
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
@@ -0,0 +1,64 @@
/*
Vera Arduino UVM-30A
connect the sensor as follows :
+ >>> 5V
- >>> GND
out >>> A0
Contribution: epierre, bulldoglowell
Converted to 1.4 by Henrik Ekblad
License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)
*/
#include <MySensor.h>
#include <SPI.h>
#define CHILD_ID_UV 0
#define UV_SENSOR_ANALOG_PIN 0
unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds)
MySensor gw;
MyMessage uvMsg(CHILD_ID_UV, V_UV);
int lastUV = -1;
int uvIndexValue [13] = { 50, 227, 318, 408, 503, 606, 696, 795, 881, 976, 1079, 1170, 3000};
int uvIndex;
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("UV Sensor", "1.1");
// Register all sensors to gateway (they will be created as child devices)
gw.present(CHILD_ID_UV, S_UV);
}
void loop()
{
uint16_t uv = analogRead(0);// Get UV value
Serial.print("Uv reading: ");
Serial.println(uv);
for (int i = 0; i < 13; i++)
{
if (uv <= uvIndexValue[i])
{
uvIndex = i;
break;
}
}
Serial.print("Uv index: ");
Serial.println(uvIndex);
if (uvIndex != lastUV) {
gw.send(uvMsg.set(uvIndex));
lastUV = uvIndex;
}
gw.sleep(SLEEP_TIME);
}
@@ -8,15 +8,9 @@
// sleepmode. So we cannot make this sensor sleep if we also want
// to calculate/report flow.
//
// Sensor on pin 3
#include <Relay.h>
#include <Sleep_n0m1.h>
#include <SPI.h>
#include <EEPROM.h>
#include <RF24.h>
#include <Sensor.h>
#include <MySensor.h>
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
#define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter)
@@ -24,16 +18,16 @@
#define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers.
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID 5 // Id of the sensor child
unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't want to spam the gateway.
unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't want to spam the gateway.
Sensor gw;
Sleep sleep;
MySensor gw;
double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter
volatile unsigned long pulseCount = 0;
volatile unsigned long lastBlink = 0;
volatile double flow = 0;
volatile double flow = 0;
boolean pcReceived = false;
unsigned long oldPulseCount = 0;
unsigned long newBlink = 0;
double oldflow = 0;
@@ -43,21 +37,24 @@ unsigned long lastSend;
unsigned long lastPulse;
unsigned long currentTime;
boolean metric;
MyMessage flowMsg(CHILD_ID,V_FLOW);
MyMessage volumeMsg(CHILD_ID,V_VOLUME);
MyMessage pcMsg(CHILD_ID,V_VAR1);
void setup()
{
gw.begin();
gw.begin(incomingMessage);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Water Meter", "1.0");
// Register this device as Waterflow sensor
gw.sendSensorPresentation(CHILD_ID, S_WATER);
gw.present(CHILD_ID, S_WATER);
// Fetch last known pulse count value from gw
pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1));
//Serial.print("Last pulse count from gw:");
//Serial.println(pulseCount);
gw.request(CHILD_ID, V_VAR1);
attachInterrupt(INTERRUPT, onPulse, RISING);
lastSend = millis();
}
@@ -65,16 +62,18 @@ void setup()
void loop()
{
currentTime = millis();
gw.process();
currentTime = millis();
// Only send values at a maximum frequency or woken up from sleep
if (SLEEP_MODE || currentTime - lastSend > 1000*SEND_FREQUENCY) {
bool sendTime = currentTime - lastSend > SEND_FREQUENCY;
if (pcReceived && (SLEEP_MODE || sendTime)) {
// New flow value has been calculated
if (!SLEEP_MODE && flow != oldflow) {
// Check that we dont get unresonable large flow value.
// could hapen when long wraps or false interrupt triggered
if (flow<((unsigned long)MAX_FLOW)) {
gw.sendVariable(CHILD_ID, V_FLOW, flow, 2); // Send flow value to gw
gw.send(flowMsg.set(flow, 2)); // Send flow value to gw
}
//Serial.print("l/min:");
//Serial.println(flow);
@@ -82,39 +81,38 @@ void loop()
}
// No Pulse count in 2min
//Serial.print("currentTime");
//Serial.println(currentTime);
//Serial.print("lastPulse");
//Serial.println(lastPulse);
if(currentTime - lastPulse > 120000){
flow = 0;
}
flow = 0;
}
// Pulse count has changed
if (pulseCount != oldPulseCount) {
gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send volumevalue to gw VAR1
gw.send(pcMsg.set(pulseCount)); // Send volumevalue to gw VAR1
double volume = ((double)pulseCount/((double)PULSE_FACTOR));
oldPulseCount = pulseCount;
//Serial.print("Pulse count:");
//Serial.println(pulseCount);
if (volume != oldvolume) {
gw.sendVariable(CHILD_ID, V_VOLUME, volume, 3); // Send volume value to gw
//Serial.print("m3:");
//Serial.println(volume, 3);
gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw
oldvolume = volume;
}
}
lastSend = currentTime;
} else if (sendTime) {
// No count received. Try requesting it again
gw.request(CHILD_ID, V_VAR1);
}
if (SLEEP_MODE) {
delay(300); //delay to allow serial to fully print before sleep
gw.powerDown();
sleep.pwrDownMode(); //set sleep mode
sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime
gw.sleep(SEND_FREQUENCY);
}
}
void incomingMessage(const MyMessage &message) {
if (message.type==V_VAR1) {
pulseCount = oldPulseCount = message.getLong();
Serial.print("Received last pulse count from gw:");
Serial.println(pulseCount);
pcReceived = true;
}
}
@@ -129,10 +127,8 @@ void onPulse()
// Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min)
return;
}
flow = (60000000.0 /interval) / ppl;
lastBlink = newBlink;
//Serial.println(flow, 4);
}
pulseCount++;
}
+856
View File
@@ -0,0 +1,856 @@
/*******************************************************************************
* LowPower Library
* Version: 1.30
* Date: 22-05-2013
* Company: Rocket Scream Electronics
* Website: www.rocketscream.com
*
* This is a lightweight low power library for Arduino. Please check our wiki
* (www.rocketscream.com/wiki) for more information on using this piece of
* library.
*
* This library is licensed under Creative Commons Attribution-ShareAlike 3.0
* Unported License.
*
* Revision Description
* ======== ===========
* 1.30 Added support for ATMega168, ATMega2560, ATMega1280 & ATMega32U4.
* Tested to work with Arduino IDE 1.0.1 - 1.0.4.
* 1.20 Remove typo error in idle method for checking whether Timer 0 was
* turned off.
* Remove dependecy on WProgram.h which is not required.
* Tested to work with Arduino IDE 1.0.
* 1.10 Added #ifndef for sleep_bod_disable() for compatibility with future
* Arduino IDE release.
* 1.00 Initial public release.
*******************************************************************************/
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include "LowPower.h"
// Only Pico Power devices can change BOD settings through software
#if defined __AVR_ATmega328P__
#ifndef sleep_bod_disable
#define sleep_bod_disable() \
do { \
unsigned char tempreg; \
__asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
"ori %[tempreg], %[bods_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" "\n\t" \
"andi %[tempreg], %[not_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" \
: [tempreg] "=&d" (tempreg) \
: [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
[bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
[not_bodse] "i" (~_BV(BODSE))); \
} while (0)
#endif
#endif
#define lowPowerBodOn(mode) \
do { \
set_sleep_mode(mode); \
cli(); \
sleep_enable(); \
sei(); \
sleep_cpu(); \
sleep_disable(); \
sei(); \
} while (0);
// Only Pico Power devices can change BOD settings through software
#if defined __AVR_ATmega328P__
#define lowPowerBodOff(mode)\
do { \
set_sleep_mode(mode); \
cli(); \
sleep_enable(); \
sleep_bod_disable(); \
sei(); \
sleep_cpu(); \
sleep_disable(); \
sei(); \
} while (0);
#endif
// Some macros is still missing from AVR GCC distribution for ATmega32U4
#if defined __AVR_ATmega32U4__
// Timer 4 PRR bit is currently not defined in iom32u4.h
#ifndef PRTIM4
#define PRTIM4 4
#endif
// Timer 4 power reduction macro is not defined currently in power.h
#ifndef power_timer4_disable
#define power_timer4_disable() (PRR1 |= (uint8_t)(1 << PRTIM4))
#endif
#ifndef power_timer4_enable
#define power_timer4_enable() (PRR1 &= (uint8_t)~(1 << PRTIM4))
#endif
#endif
/*******************************************************************************
* Name: idle
* Description: Putting ATmega328P/168 into idle state. Please make sure you
* understand the implication and result of disabling module.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control:
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
* 4. timer1 Timer 1 module disable control:
* (a) TIMER1_OFF - Turn off Timer 1 module
* (b) TIMER1_ON - Leave Timer 1 module in its default state
*
* 5. timer0 Timer 0 module disable control:
* (a) TIMER0_OFF - Turn off Timer 0 module
* (b) TIMER0_ON - Leave Timer 0 module in its default state
*
* 6. spi SPI module disable control:
* (a) SPI_OFF - Turn off SPI module
* (b) SPI_ON - Leave SPI module in its default state
*
* 7. usart0 USART0 module disable control:
* (a) USART0_OFF - Turn off USART0 module
* (b) USART0_ON - Leave USART0 module in its default state
*
* 8. twi TWI module disable control:
* (a) TWI_OFF - Turn off TWI module
* (b) TWI_ON - Leave TWI module in its default state
*
*******************************************************************************/
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__)
void LowPowerClass::idle(period_t period, adc_t adc, timer2_t timer2,
timer1_t timer1, timer0_t timer0,
spi_t spi, usart0_t usart0, twi_t twi)
{
// Temporary clock source variable
unsigned char clockSource = 0;
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
power_timer2_disable();
}
if (adc == ADC_OFF)
{
ADCSRA &= ~(1 << ADEN);
power_adc_disable();
}
if (timer1 == TIMER1_OFF) power_timer1_disable();
if (timer0 == TIMER0_OFF) power_timer0_disable();
if (spi == SPI_OFF) power_spi_disable();
if (usart0 == USART0_OFF) power_usart0_disable();
if (twi == TWI_OFF) power_twi_disable();
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_IDLE);
if (adc == ADC_OFF)
{
power_adc_enable();
ADCSRA |= (1 << ADEN);
}
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
power_timer2_enable();
}
if (timer1 == TIMER1_OFF) power_timer1_enable();
if (timer0 == TIMER0_OFF) power_timer0_enable();
if (spi == SPI_OFF) power_spi_enable();
if (usart0 == USART0_OFF) power_usart0_enable();
if (twi == TWI_OFF) power_twi_enable();
}
#endif
/*******************************************************************************
* Name: idle
* Description: Putting ATmega32U4 into idle state. Please make sure you
* understand the implication and result of disabling module.
* Take note that Timer 2 is not available and USART0
* is replaced with USART1 on ATmega32U4.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control:
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer4 Timer 4 module disable control:
* (a) TIMER4_OFF - Turn off Timer 4 module
* (b) TIMER4_ON - Leave Timer 4 module in its default state
*
* 4. timer3 Timer 3 module disable control:
* (a) TIMER3_OFF - Turn off Timer 3 module
* (b) TIMER3_ON - Leave Timer 3 module in its default state
*
* 5. timer1 Timer 1 module disable control:
* (a) TIMER1_OFF - Turn off Timer 1 module
* (b) TIMER1_ON - Leave Timer 1 module in its default state
*
* 6. timer0 Timer 0 module disable control:
* (a) TIMER0_OFF - Turn off Timer 0 module
* (b) TIMER0_ON - Leave Timer 0 module in its default state
*
* 7. spi SPI module disable control:
* (a) SPI_OFF - Turn off SPI module
* (b) SPI_ON - Leave SPI module in its default state
*
* 8. usart1 USART1 module disable control:
* (a) USART1_OFF - Turn off USART1 module
* (b) USART1_ON - Leave USART1 module in its default state
*
* 9. twi TWI module disable control:
* (a) TWI_OFF - Turn off TWI module
* (b) TWI_ON - Leave TWI module in its default state
*
* 10.usb USB module disable control:
* (a) USB_OFF - Turn off USB module
* (b) USB_ON - Leave USB module in its default state
*******************************************************************************/
#if defined __AVR_ATmega32U4__
void LowPowerClass::idle(period_t period, adc_t adc,
timer4_t timer4, timer3_t timer3,
timer1_t timer1, timer0_t timer0,
spi_t spi, usart1_t usart1, twi_t twi, usb_t usb)
{
if (adc == ADC_OFF)
{
ADCSRA &= ~(1 << ADEN);
power_adc_disable();
}
if (timer4 == TIMER4_OFF) power_timer4_disable();
if (timer3 == TIMER3_OFF) power_timer3_disable();
if (timer1 == TIMER1_OFF) power_timer1_disable();
if (timer0 == TIMER0_OFF) power_timer0_disable();
if (spi == SPI_OFF) power_spi_disable();
if (usart1 == USART1_OFF) power_usart1_disable();
if (twi == TWI_OFF) power_twi_disable();
if (usb == USB_OFF) power_usb_disable();
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_IDLE);
if (adc == ADC_OFF)
{
power_adc_enable();
ADCSRA |= (1 << ADEN);
}
if (timer4 == TIMER4_OFF) power_timer4_enable();
if (timer3 == TIMER3_OFF) power_timer3_enable();
if (timer1 == TIMER1_OFF) power_timer1_enable();
if (timer0 == TIMER0_OFF) power_timer0_enable();
if (spi == SPI_OFF) power_spi_enable();
if (usart1 == USART1_OFF) power_usart1_enable();
if (twi == TWI_OFF) power_twi_enable();
if (usb == USB_OFF) power_usb_enable();
}
#endif
/*******************************************************************************
* Name: idle
* Description: Putting ATmega2560 & ATmega1280 into idle state. Please make sure
* you understand the implication and result of disabling module.
* Take note that extra Timer 5, 4, 3 compared to an ATmega328P/168.
* Also take note that extra USART 3, 2, 1 compared to an
* ATmega328P/168.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control:
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer5 Timer 5 module disable control:
* (a) TIMER5_OFF - Turn off Timer 5 module
* (b) TIMER5_ON - Leave Timer 5 module in its default state
*
* 4. timer4 Timer 4 module disable control:
* (a) TIMER4_OFF - Turn off Timer 4 module
* (b) TIMER4_ON - Leave Timer 4 module in its default state
*
* 5. timer3 Timer 3 module disable control:
* (a) TIMER3_OFF - Turn off Timer 3 module
* (b) TIMER3_ON - Leave Timer 3 module in its default state
*
* 6. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
* 7. timer1 Timer 1 module disable control:
* (a) TIMER1_OFF - Turn off Timer 1 module
* (b) TIMER1_ON - Leave Timer 1 module in its default state
*
* 8. timer0 Timer 0 module disable control:
* (a) TIMER0_OFF - Turn off Timer 0 module
* (b) TIMER0_ON - Leave Timer 0 module in its default state
*
* 9. spi SPI module disable control:
* (a) SPI_OFF - Turn off SPI module
* (b) SPI_ON - Leave SPI module in its default state
*
* 10.usart3 USART3 module disable control:
* (a) USART3_OFF - Turn off USART3 module
* (b) USART3_ON - Leave USART3 module in its default state
*
* 11.usart2 USART2 module disable control:
* (a) USART2_OFF - Turn off USART2 module
* (b) USART2_ON - Leave USART2 module in its default state
*
* 12.usart1 USART1 module disable control:
* (a) USART1_OFF - Turn off USART1 module
* (b) USART1_ON - Leave USART1 module in its default state
*
* 13.usart0 USART0 module disable control:
* (a) USART0_OFF - Turn off USART0 module
* (b) USART0_ON - Leave USART0 module in its default state
*
* 14.twi TWI module disable control:
* (a) TWI_OFF - Turn off TWI module
* (b) TWI_ON - Leave TWI module in its default state
*
*******************************************************************************/
#if defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__)
void LowPowerClass::idle(period_t period, adc_t adc, timer5_t timer5,
timer4_t timer4, timer3_t timer3, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart3_t usart3, usart2_t usart2, usart1_t usart1,
usart0_t usart0, twi_t twi)
{
// Temporary clock source variable
unsigned char clockSource = 0;
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
power_timer2_disable();
}
if (adc == ADC_OFF)
{
ADCSRA &= ~(1 << ADEN);
power_adc_disable();
}
if (timer5 == TIMER5_OFF) power_timer5_disable();
if (timer4 == TIMER4_OFF) power_timer4_disable();
if (timer3 == TIMER3_OFF) power_timer3_disable();
if (timer1 == TIMER1_OFF) power_timer1_disable();
if (timer0 == TIMER0_OFF) power_timer0_disable();
if (spi == SPI_OFF) power_spi_disable();
if (usart3 == USART3_OFF) power_usart3_disable();
if (usart2 == USART2_OFF) power_usart2_disable();
if (usart1 == USART1_OFF) power_usart1_disable();
if (usart0 == USART0_OFF) power_usart0_disable();
if (twi == TWI_OFF) power_twi_disable();
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_IDLE);
if (adc == ADC_OFF)
{
power_adc_enable();
ADCSRA |= (1 << ADEN);
}
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
power_timer2_enable();
}
if (timer5 == TIMER5_OFF) power_timer5_enable();
if (timer4 == TIMER4_OFF) power_timer4_enable();
if (timer3 == TIMER3_OFF) power_timer3_enable();
if (timer1 == TIMER1_OFF) power_timer1_enable();
if (timer0 == TIMER0_OFF) power_timer0_enable();
if (spi == SPI_OFF) power_spi_enable();
if (usart3 == USART3_OFF) power_usart3_enable();
if (usart2 == USART2_OFF) power_usart2_enable();
if (usart1 == USART1_OFF) power_usart1_enable();
if (usart0 == USART0_OFF) power_usart0_enable();
if (twi == TWI_OFF) power_twi_enable();
}
#endif
/*******************************************************************************
* Name: adcNoiseReduction
* Description: Putting microcontroller into ADC noise reduction state. This is
* a very useful state when using the ADC to achieve best and low
* noise signal.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
*******************************************************************************/
void LowPowerClass::adcNoiseReduction(period_t period, adc_t adc,
timer2_t timer2)
{
// Temporary clock source variable
unsigned char clockSource = 0;
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
}
#endif
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
lowPowerBodOn(SLEEP_MODE_ADC);
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
}
#endif
}
/*******************************************************************************
* Name: powerDown
* Description: Putting microcontroller into power down state. This is
* the lowest current consumption state. Use this together with
* external pin interrupt to wake up through external event
* triggering (example: RTC clockout pin, SD card detect pin).
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
*******************************************************************************/
void LowPowerClass::powerDown(period_t period, adc_t adc, bod_t bod)
{
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_PWR_DOWN);
#else
lowPowerBodOn(SLEEP_MODE_PWR_DOWN);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_PWR_DOWN);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
}
/*******************************************************************************
* Name: powerSave
* Description: Putting microcontroller into power save state. This is
* the lowest current consumption state after power down.
* Use this state together with an external 32.768 kHz crystal (but
* 8/16 MHz crystal/resonator need to be removed) to provide an
* asynchronous clock source to Timer 2. Please take note that
* Timer 2 is also used by the Arduino core for PWM operation.
* Please refer to wiring.c for explanation. Removal of the external
* 8/16 MHz crystal/resonator requires the microcontroller to run
* on its internal RC oscillator which is not so accurate for time
* critical operation.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
* 4. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
*******************************************************************************/
void LowPowerClass::powerSave(period_t period, adc_t adc, bod_t bod,
timer2_t timer2)
{
// Temporary clock source variable
unsigned char clockSource = 0;
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
}
#endif
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_PWR_SAVE);
#else
lowPowerBodOn(SLEEP_MODE_PWR_SAVE);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_PWR_SAVE);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
}
#endif
}
/*******************************************************************************
* Name: powerStandby
* Description: Putting microcontroller into power standby state.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control. Turning off the ADC module is
* basically removing the purpose of this low power mode.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
*******************************************************************************/
void LowPowerClass::powerStandby(period_t period, adc_t adc, bod_t bod)
{
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_STANDBY);
#else
lowPowerBodOn(SLEEP_MODE_STANDBY);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_STANDBY);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
}
/*******************************************************************************
* Name: powerExtStandby
* Description: Putting microcontroller into power extended standby state. This
* is different from the power standby state as it has the
* capability to run Timer 2 asynchronously.
*
* Argument Description
* ========= ===========
* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake
* up resource:
* (a) SLEEP_15MS - 15 ms sleep
* (b) SLEEP_30MS - 30 ms sleep
* (c) SLEEP_60MS - 60 ms sleep
* (d) SLEEP_120MS - 120 ms sleep
* (e) SLEEP_250MS - 250 ms sleep
* (f) SLEEP_500MS - 500 ms sleep
* (g) SLEEP_1S - 1 s sleep
* (h) SLEEP_2S - 2 s sleep
* (i) SLEEP_4S - 4 s sleep
* (j) SLEEP_8S - 8 s sleep
* (k) SLEEP_FOREVER - Sleep without waking up through WDT
*
* 2. adc ADC module disable control.
* (a) ADC_OFF - Turn off ADC module
* (b) ADC_ON - Leave ADC module in its default state
*
* 3. bod Brown Out Detector (BOD) module disable control:
* (a) BOD_OFF - Turn off BOD module
* (b) BOD_ON - Leave BOD module in its default state
*
* 4. timer2 Timer 2 module disable control:
* (a) TIMER2_OFF - Turn off Timer 2 module
* (b) TIMER2_ON - Leave Timer 2 module in its default state
*
*******************************************************************************/
void LowPowerClass::powerExtStandby(period_t period, adc_t adc, bod_t bod,
timer2_t timer2)
{
// Temporary clock source variable
unsigned char clockSource = 0;
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (TCCR2B & CS22) clockSource |= (1 << CS22);
if (TCCR2B & CS21) clockSource |= (1 << CS21);
if (TCCR2B & CS20) clockSource |= (1 << CS20);
// Remove the clock source to shutdown Timer2
TCCR2B &= ~(1 << CS22);
TCCR2B &= ~(1 << CS21);
TCCR2B &= ~(1 << CS20);
}
#endif
if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);
if (period != SLEEP_FOREVER)
{
wdt_enable(period);
WDTCSR |= (1 << WDIE);
}
if (bod == BOD_OFF)
{
#if defined __AVR_ATmega328P__
lowPowerBodOff(SLEEP_MODE_EXT_STANDBY);
#else
lowPowerBodOn(SLEEP_MODE_EXT_STANDBY);
#endif
}
else
{
lowPowerBodOn(SLEEP_MODE_EXT_STANDBY);
}
if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
#if !defined(__AVR_ATmega32U4__)
if (timer2 == TIMER2_OFF)
{
if (clockSource & CS22) TCCR2B |= (1 << CS22);
if (clockSource & CS21) TCCR2B |= (1 << CS21);
if (clockSource & CS20) TCCR2B |= (1 << CS20);
}
#endif
}
/*******************************************************************************
* Name: ISR (WDT_vect)
* Description: Watchdog Timer interrupt service routine. This routine is
* required to allow automatic WDIF and WDIE bit clearance in
* hardware.
*
*******************************************************************************/
ISR (WDT_vect)
{
// WDIE & WDIF is cleared in hardware upon entering this ISR
wdt_disable();
}
LowPowerClass LowPower;
+137
View File
@@ -0,0 +1,137 @@
#ifndef LowPower_h
#define LowPower_h
enum period_t
{
SLEEP_15Ms,
SLEEP_30MS,
SLEEP_60MS,
SLEEP_120MS,
SLEEP_250MS,
SLEEP_500MS,
SLEEP_1S,
SLEEP_2S,
SLEEP_4S,
SLEEP_8S,
SLEEP_FOREVER
};
enum bod_t
{
BOD_OFF,
BOD_ON
};
enum adc_t
{
ADC_OFF,
ADC_ON
};
enum timer5_t
{
TIMER5_OFF,
TIMER5_ON
};
enum timer4_t
{
TIMER4_OFF,
TIMER4_ON
};
enum timer3_t
{
TIMER3_OFF,
TIMER3_ON
};
enum timer2_t
{
TIMER2_OFF,
TIMER2_ON
};
enum timer1_t
{
TIMER1_OFF,
TIMER1_ON
};
enum timer0_t
{
TIMER0_OFF,
TIMER0_ON
};
enum spi_t
{
SPI_OFF,
SPI_ON
};
enum usart0_t
{
USART0_OFF,
USART0_ON
};
enum usart1_t
{
USART1_OFF,
USART1_ON
};
enum usart2_t
{
USART2_OFF,
USART2_ON
};
enum usart3_t
{
USART3_OFF,
USART3_ON
};
enum twi_t
{
TWI_OFF,
TWI_ON
};
enum usb_t
{
USB_OFF,
USB_ON
};
class LowPowerClass
{
public:
#if defined (__AVR_ATmega328P__) || defined (__AVR_ATmega168__)
void idle(period_t period, adc_t adc, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega2560__
void idle(period_t period, adc_t adc, timer5_t timer5,
timer4_t timer4, timer3_t timer3, timer2_t timer2,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart3_t usart3, usart2_t usart2, usart1_t usart1,
usart0_t usart0, twi_t twi);
#elif defined __AVR_ATmega32U4__
void idle(period_t period, adc_t adc, timer4_t timer4, timer3_t timer3,
timer1_t timer1, timer0_t timer0, spi_t spi,
usart1_t usart1, twi_t twi, usb_t usb);
#else
#error "Please ensure chosen MCU is either 328P, 32U4 or 2560."
#endif
void adcNoiseReduction(period_t period, adc_t adc, timer2_t timer2);
void powerDown(period_t period, adc_t adc, bod_t bod);
void powerSave(period_t period, adc_t adc, bod_t bod, timer2_t timer2);
void powerStandby(period_t period, adc_t adc, bod_t bod);
void powerExtStandby(period_t period, adc_t adc, bod_t bod, timer2_t timer2);
};
extern LowPowerClass LowPower;
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
/*
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
Added Arduino Due support from https://github.com/mcrosson/
*/
#ifndef __RF24_CONFIG_H__
#define __RF24_CONFIG_H__
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
#include <stddef.h>
//TMRh20:
//#define MINIMAL
// Define _BV for non-Arduino platforms and for Arduino DUE
#if defined (ARDUINO)
#include <SPI.h>
#else
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern HardwareSPI SPI;
#define _BV(x) (1<<(x))
#endif
#undef SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define IF_SERIAL_DEBUG(x) ({x;})
#else
#define IF_SERIAL_DEBUG(x)
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__)
#define printf_P(...)
#endif
#endif
// Avoid spurious warnings
// Arduino DUE is arm and uses traditional PROGMEM constructs
#if 1
#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__)
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#endif
#endif
// Progmem is Arduino-specific
// Arduino DUE is arm and does not include avr/pgmspace
#if defined(ARDUINO) && ! defined(__arm__)
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#else
#if ! defined(ARDUINO) // This doesn't work on Arduino DUE
typedef char const char;
#else // Fill in pgm_read_byte that is used, but missing from DUE
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
typedef uint16_t prog_uint16_t;
#define PSTR(x) (x)
#define printf_P printf
#define strlen_P strlen
#define PROGMEM
#define pgm_read_word(p) (*(p))
#define PRIPSTR "%s"
#endif
// ATTiny support code is from https://github.com/jscrane/RF24
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
#include <stdio.h>
#include <Arduino.h>
#include <avr/pgmspace.h>
#define SPI_CLOCK_DIV4 0x00
#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05
#define SPI_CLOCK_DIV32 0x06
//#define SPI_CLOCK_DIV64 0x07
#define SPI_MODE0 0x00
#define SPI_MODE1 0x04
#define SPI_MODE2 0x08
#define SPI_MODE3 0x0C
#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR
class SPIClass {
public:
static byte transfer(byte _data);
// SPI Configuration methods
inline static void attachInterrupt();
inline static void detachInterrupt(); // Default
static void begin(); // Default
static void end();
static void setBitOrder(uint8_t);
static void setDataMode(uint8_t);
static void setClockDivider(uint8_t);
};
extern SPIClass SPI;
#endif //ATTiny
#endif // __RF24_CONFIG_H__
@@ -1,25 +1,25 @@
/*
Copyright (c) 2007 Stefan Engelke <mbox@stefanengelke.de>
Portions Copyright (C) 2011 Greg Copeland
Portions Copyright (C) 2011 Greg Copeland
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
@@ -117,7 +117,7 @@
#define LNA_HCURR 0
/* P model memory Map */
#define RPD 0x09
#define RPD 0x09
#define W_TX_PAYLOAD_NO_ACK 0xB0
/* P model bit Mnemonics */
-190
View File
@@ -1,190 +0,0 @@
#include "PN532_HSU.h"
#include "PN532_debug.h"
PN532_HSU::PN532_HSU(HardwareSerial &serial)
{
_serial = &serial;
command = 0;
}
void PN532_HSU::begin()
{
_serial->begin((long)115200);
}
void PN532_HSU::wakeup()
{
_serial->write((uint8_t)0x55);
_serial->write((uint8_t)0x55);
_serial->write((uint8_t)0x00);
_serial->write((uint8_t)0x00);
_serial->write((uint8_t)0x00);
/** dump serial buffer */
if(_serial->available()){
DMSG("Dump serial buffer: ");
}
while(_serial->available()){
uint8_t ret = _serial->read();
DMSG_HEX(ret);
}
}
int8_t PN532_HSU::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{
/** dump serial buffer */
if(_serial->available()){
DMSG("Dump serial buffer: ");
}
while(_serial->available()){
uint8_t ret = _serial->read();
DMSG_HEX(ret);
}
command = header[0];
_serial->write((uint8_t)PN532_PREAMBLE);
_serial->write((uint8_t)PN532_STARTCODE1);
_serial->write((uint8_t)PN532_STARTCODE2);
uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA
_serial->write(length);
_serial->write(~length + 1); // checksum of length
_serial->write((uint8_t)PN532_HOSTTOPN532);
uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA
_serial->write(header, hlen);
for (uint8_t i = 0; i < hlen; i++) {
sum += header[i];
}
_serial->write(body, blen);
for (uint8_t i = 0; i < blen; i++) {
sum += body[i];
}
uint8_t checksum = ~sum + 1; // checksum of TFI + DATA
_serial->write(checksum);
_serial->write((uint8_t)PN532_POSTAMBLE);
return readAckFrame();
}
int16_t PN532_HSU::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout)
{
uint8_t tmp[3];
DMSG("Read response\n");
/** Frame Preamble and Start Code */
if(receive(tmp, 3, timeout)<=0){
return PN532_TIMEOUT;
}
if(0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]){
DMSG("Preamble error");
return PN532_INVALID_FRAME;
}
/** receive length and check */
uint8_t length[2];
if(receive(length, 2, timeout) <= 0){
return PN532_TIMEOUT;
}
if( 0 != (uint8_t)(length[0] + length[1]) ){
DMSG("Length error");
return PN532_INVALID_FRAME;
}
length[0] -= 2;
if( length[0] > len){
return PN532_NO_SPACE;
}
/** receive command byte */
uint8_t cmd = command + 1; // response command
if(receive(tmp, 2, timeout) <= 0){
return PN532_TIMEOUT;
}
if( PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]){
DMSG("Command error");
return PN532_INVALID_FRAME;
}
if(receive(buf, length[0], timeout) != length[0]){
return PN532_TIMEOUT;
}
uint8_t sum = PN532_PN532TOHOST + cmd;
for(uint8_t i=0; i<length[0]; i++){
sum += buf[i];
}
/** checksum and postamble */
if(receive(tmp, 2, timeout) <= 0){
return PN532_TIMEOUT;
}
if( 0 != (uint8_t)(sum + tmp[0]) || 0 != tmp[1] ){
DMSG("Checksum error");
return PN532_INVALID_FRAME;
}
return length[0];
}
int8_t PN532_HSU::readAckFrame()
{
const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0};
uint8_t ackBuf[sizeof(PN532_ACK)];
DMSG("Read Ack\n");
if( receive(ackBuf, sizeof(PN532_ACK)) <= 0 ){
DMSG("Read ACK Timeout\n");
return PN532_TIMEOUT;
}
if( memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK)) ){
DMSG("Invalid ACK\n");
return PN532_INVALID_ACK;
}
return 0;
}
/**
@brief receive data .
@param buf --> return value buffer.
len --> length expect to receive.
timeout --> time of reveiving
@retval number of received bytes, 0 means no data received.
*/
int8_t PN532_HSU::receive(uint8_t *buf, int len, uint16_t timeout)
{
int read_bytes = 0;
int ret;
unsigned long start_millis;
while (read_bytes < len) {
start_millis = millis();
do {
ret = _serial->read();
if (ret >= 0) {
break;
}
} while( (millis()- start_millis ) < timeout);
if (ret < 0) {
if(read_bytes){
return read_bytes;
}else{
return PN532_TIMEOUT;
}
}
buf[read_bytes] = (uint8_t)ret;
DMSG_HEX(ret);
read_bytes++;
}
return read_bytes;
}
-29
View File
@@ -1,29 +0,0 @@
#ifndef __PN532_HSU_H__
#define __PN532_HSU_H__
#include "PN532Interface.h"
#define PN532_HSU_DEBUG
#define PN532_HSU_READ_TIMEOUT (1000)
class PN532_HSU : public PN532Interface {
public:
PN532_HSU(HardwareSerial &serial);
void begin();
void wakeup();
virtual int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0);
int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout);
private:
HardwareSerial* _serial;
uint8_t command;
int8_t readAckFrame();
int8_t receive(uint8_t *buf, int len, uint16_t timeout=PN532_HSU_READ_TIMEOUT);
};
#endif
-209
View File
@@ -1,209 +0,0 @@
#include "PN532_SPI.h"
#include "PN532_debug.h"
#define STATUS_READ 2
#define DATA_WRITE 1
#define DATA_READ 3
PN532_SPI::PN532_SPI(SPIClass &spi, uint8_t ss)
{
command = 0;
_spi = &spi;
_ss = ss;
}
void PN532_SPI::begin()
{
pinMode(_ss, OUTPUT);
_spi->begin();
_spi->setDataMode(SPI_MODE0); // PN532 only supports mode0
_spi->setBitOrder(LSBFIRST);
#ifndef __SAM3X8E__
_spi->setClockDivider(SPI_CLOCK_DIV8); // set clock 2MHz(max: 5MHz)
#else
/** DUE spi library does not support SPI_CLOCK_DIV8 macro */
_spi->setClockDivider(42); // set clock 2MHz(max: 5MHz)
#endif
}
void PN532_SPI::wakeup()
{
digitalWrite(_ss, LOW);
delay(2);
digitalWrite(_ss, HIGH);
}
int8_t PN532_SPI::writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{
command = header[0];
writeFrame(header, hlen, body, blen);
uint8_t timeout = PN532_ACK_WAIT_TIME;
while (!isReady()) {
delay(1);
timeout--;
if (0 == timeout) {
DMSG("Time out when waiting for ACK\n");
return -2;
}
}
if (readAckFrame()) {
DMSG("Invalid ACK\n");
return PN532_INVALID_ACK;
}
return 0;
}
int16_t PN532_SPI::readResponse(uint8_t buf[], uint8_t len, uint16_t timeout)
{
uint16_t time = 0;
while (!isReady()) {
delay(1);
time++;
if (timeout > 0 && time > timeout) {
return PN532_TIMEOUT;
}
}
digitalWrite(_ss, LOW);
delay(1);
int16_t result;
do {
write(DATA_READ);
if (0x00 != read() || // PREAMBLE
0x00 != read() || // STARTCODE1
0xFF != read() // STARTCODE2
) {
result = PN532_INVALID_FRAME;
break;
}
uint8_t length = read();
if (0 != (uint8_t)(length + read())) { // checksum of length
result = PN532_INVALID_FRAME;
break;
}
uint8_t cmd = command + 1; // response command
if (PN532_PN532TOHOST != read() || (cmd) != read()) {
result = PN532_INVALID_FRAME;
break;
}
DMSG("read: ");
DMSG_HEX(cmd);
length -= 2;
if (length > len) {
for (uint8_t i = 0; i < length; i++) {
DMSG_HEX(read()); // dump message
}
DMSG("\nNot enough space\n");
read();
read();
result = PN532_NO_SPACE; // not enough space
break;
}
uint8_t sum = PN532_PN532TOHOST + cmd;
for (uint8_t i = 0; i < length; i++) {
buf[i] = read();
sum += buf[i];
DMSG_HEX(buf[i]);
}
DMSG('\n');
uint8_t checksum = read();
if (0 != (uint8_t)(sum + checksum)) {
DMSG("checksum is not ok\n");
result = PN532_INVALID_FRAME;
break;
}
read(); // POSTAMBLE
result = length;
} while (0);
digitalWrite(_ss, HIGH);
return result;
}
boolean PN532_SPI::isReady()
{
digitalWrite(_ss, LOW);
write(STATUS_READ);
uint8_t status = read() & 1;
digitalWrite(_ss, HIGH);
return status;
}
void PN532_SPI::writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body, uint8_t blen)
{
digitalWrite(_ss, LOW);
delay(2); // wake up PN532
write(DATA_WRITE);
write(PN532_PREAMBLE);
write(PN532_STARTCODE1);
write(PN532_STARTCODE2);
uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA
write(length);
write(~length + 1); // checksum of length
write(PN532_HOSTTOPN532);
uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA
DMSG("write: ");
for (uint8_t i = 0; i < hlen; i++) {
write(header[i]);
sum += header[i];
DMSG_HEX(header[i]);
}
for (uint8_t i = 0; i < blen; i++) {
write(body[i]);
sum += body[i];
DMSG_HEX(body[i]);
}
uint8_t checksum = ~sum + 1; // checksum of TFI + DATA
write(checksum);
write(PN532_POSTAMBLE);
digitalWrite(_ss, HIGH);
DMSG('\n');
}
int8_t PN532_SPI::readAckFrame()
{
const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0};
uint8_t ackBuf[sizeof(PN532_ACK)];
digitalWrite(_ss, LOW);
delay(1);
write(DATA_READ);
for (uint8_t i = 0; i < sizeof(PN532_ACK); i++) {
ackBuf[i] = read();
}
digitalWrite(_ss, HIGH);
return memcmp(ackBuf, PN532_ACK, sizeof(PN532_ACK));
}
-36
View File
@@ -1,36 +0,0 @@
#ifndef __PN532_SPI_H__
#define __PN532_SPI_H__
#include <SPI.h>
#include "PN532Interface.h"
class PN532_SPI : public PN532Interface {
public:
PN532_SPI(SPIClass &spi, uint8_t ss);
void begin();
void wakeup();
int8_t writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0);
int16_t readResponse(uint8_t buf[], uint8_t len, uint16_t timeout);
private:
SPIClass* _spi;
uint8_t _ss;
uint8_t command;
boolean isReady();
void writeFrame(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0);
int8_t readAckFrame();
inline void write(uint8_t data) {
_spi->transfer(data);
};
inline uint8_t read() {
return _spi->transfer(0);
};
};
#endif
@@ -1,272 +0,0 @@
/*
ByteBuffer.cpp - A circular buffer implementation for Arduino
Created by Sigurdur Orn, July 19, 2010.
siggi@mit.edu
Updated by GreyGnome (aka Mike Schwager) Thu Feb 23 17:25:14 CST 2012
added the putString() method and the fillError variable.
added the checkError() and resetError() methods. The checkError() method resets the fillError variable
to false as a side effect.
added the ByteBuffer(unsigned int buf_size) constructor.
added the init() method, and had the constructor call it automagically.
Also made the capacity, position, length, and fillError variables volatile, for safe use by interrupts.
*/
#include "ByteBuffer.h"
void ByteBuffer::init(){
ByteBuffer::init(DEFAULTBUFSIZE);
}
void ByteBuffer::init(unsigned int buf_length){
data = (byte*)malloc(sizeof(byte)*buf_length);
capacity = buf_length;
position = 0;
length = 0;
fillError=false;
}
void ByteBuffer::deAllocate(){
free(data);
}
void ByteBuffer::clear(){
position = 0;
length = 0;
}
void ByteBuffer::resetError(){
fillError=false;
}
boolean ByteBuffer::checkError(){
/*
if (fillError) {
Serial.print("E: checkError: length ");
Serial.println(length, DEC);
}
*/
boolean result=fillError;
fillError=false;
return(result);
}
int ByteBuffer::getSize(){
return length;
}
int ByteBuffer::getCapacity(){
return capacity;
}
byte ByteBuffer::peek(unsigned int index){
byte b = data[(position+index)%capacity];
return b;
}
uint8_t ByteBuffer::put(byte in){
if(length < capacity){
// save data byte at end of buffer
data[(position+length) % capacity] = in;
// increment the length
length++;
return 1;
}
// return failure
//Serial.print("E: put: ");
//Serial.println(length, DEC);
fillError=true;
return 0;
}
uint8_t ByteBuffer::putString(char *in){
uint8_t count=0;
char *inString;
inString=in;
uint8_t oldSREG = SREG; cli();
while(length <= capacity){
if (length == capacity) {
fillError=true;
return count;
}
// save data byte at end of buffer
data[(position+length) % capacity] = *inString;
// increment the length
length++;
inString++;
count++;
if (*inString == 0) {
if (count==0) fillError=true; // Serial.println("E: putString"); };
SREG = oldSREG; // Restore register; reenables interrupts
return count;
}
}
SREG = oldSREG; // Restore register; reenables interrupts
return count;
}
uint8_t ByteBuffer::putInFront(byte in){
uint8_t oldSREG = SREG; cli();
if(length < capacity){
// save data byte at end of buffer
if( position == 0 )
position = capacity-1;
else
position = (position-1)%capacity;
data[position] = in;
// increment the length
length++;
SREG = oldSREG; // Restore register; reenables interrupts
return 1;
}
// return failure
//Serial.println("E: putInFront");
fillError=true;
SREG = oldSREG; // Restore register; reenables interrupts
return 0;
}
byte ByteBuffer::get(){
uint8_t oldSREG = SREG; cli();
byte b = 0;
if(length > 0){
b = data[position];
// move index down and decrement length
position = (position+1)%capacity;
length--;
}
SREG = oldSREG; // Restore register; reenables interrupts
return b;
}
byte ByteBuffer::getFromBack(){
byte b = 0;
if(length > 0){
uint8_t oldSREG = SREG; cli();
b = data[(position+length-1)%capacity];
length--;
SREG = oldSREG; // Restore register; reenables interrupts
}
return b;
}
//
// Ints
//
void ByteBuffer::putIntInFront(int in){
byte *pointer = (byte *)&in;
putInFront(pointer[0]);
putInFront(pointer[1]);
}
void ByteBuffer::putInt(int in){
byte *pointer = (byte *)&in;
put(pointer[1]);
put(pointer[0]);
}
int ByteBuffer::getInt(){
int ret;
byte *pointer = (byte *)&ret;
pointer[1] = get();
pointer[0] = get();
return ret;
}
int ByteBuffer::getIntFromBack(){
int ret;
byte *pointer = (byte *)&ret;
pointer[0] = getFromBack();
pointer[1] = getFromBack();
return ret;
}
//
// Longs
//
void ByteBuffer::putLongInFront(long in){
byte *pointer = (byte *)&in;
putInFront(pointer[0]);
putInFront(pointer[1]);
putInFront(pointer[2]);
putInFront(pointer[3]);
}
void ByteBuffer::putLong(long in){
byte *pointer = (byte *)&in;
put(pointer[3]);
put(pointer[2]);
put(pointer[1]);
put(pointer[0]);
}
long ByteBuffer::getLong(){
long ret;
byte *pointer = (byte *)&ret;
pointer[3] = get();
pointer[2] = get();
pointer[1] = get();
pointer[0] = get();
return ret;
}
long ByteBuffer::getLongFromBack(){
long ret;
byte *pointer = (byte *)&ret;
pointer[0] = getFromBack();
pointer[1] = getFromBack();
pointer[2] = getFromBack();
pointer[3] = getFromBack();
return ret;
}
//
// Floats
//
void ByteBuffer::putFloatInFront(float in){
byte *pointer = (byte *)&in;
putInFront(pointer[0]);
putInFront(pointer[1]);
putInFront(pointer[2]);
putInFront(pointer[3]);
}
void ByteBuffer::putFloat(float in){
byte *pointer = (byte *)&in;
put(pointer[3]);
put(pointer[2]);
put(pointer[1]);
put(pointer[0]);
}
float ByteBuffer::getFloat(){
float ret;
byte *pointer = (byte *)&ret;
pointer[3] = get();
pointer[2] = get();
pointer[1] = get();
pointer[0] = get();
return ret;
}
float ByteBuffer::getFloatFromBack(){
float ret;
byte *pointer = (byte *)&ret;
pointer[0] = getFromBack();
pointer[1] = getFromBack();
pointer[2] = getFromBack();
pointer[3] = getFromBack();
return ret;
}
@@ -1,104 +0,0 @@
/*
ByteBuffer.h - A circular buffer implementation for Arduino
Created by Sigurdur Orn, July 19, 2010. siggi@mit.edu
Updated by GreyGnome (aka Mike Schwager) Thu Feb 23 17:25:14 CST 2012
added the putString() method and the fillError variable.
added the checkError() and resetError() methods. The checkError() method resets the fillError variable
to false as a side effect.
added the ByteBuffer(unsigned int buf_size) constructor.
added the init() method, and had the constructor call it automagically.
protected certain sections of the code with cli()/sei() calls, for safe use by interrupts.
Also made the capacity, position, length, and fillError variables volatile, for safe use by interrupts.
*/
#ifndef ByteBuffer_h
#define ByteBuffer_h
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
//#include <util/atomic.h>
#define DEFAULTBUFSIZE 32
class ByteBuffer
{
public:
ByteBuffer() {
init();
};
ByteBuffer(unsigned int buf_size) {
init(buf_size);
};
// This method initializes the datastore of the buffer to a certain size.
void init(unsigned int buf_size);
// This method initializes the datastore of the buffer to the default size.
void init();
// This method resets the buffer into an original state (with no data)
void clear();
// This method resets the fillError variable to false.
void resetError();
// This method tells you if your buffer overflowed at some time since the last
// check. The error state will be reset to false.
boolean checkError();
// This releases resources for this buffer, after this has been called the buffer should NOT be used
void deAllocate();
// Returns how much space is used in the buffer
int getSize();
// Returns the maximum capacity of the buffer
int getCapacity();
// This method returns the byte that is located at index in the buffer but doesn't modify the buffer like the get methods (doesn't remove the retured byte from the buffer)
byte peek(unsigned int index);
//
// Put methods, either a regular put in back or put in front
//
uint8_t putInFront(byte in);
uint8_t put(byte in);
uint8_t putString(char *in);
void putIntInFront(int in);
void putInt(int in);
void putLongInFront(long in);
void putLong(long in);
void putFloatInFront(float in);
void putFloat(float in);
//
// Get methods, either a regular get from front or from back
//
byte get();
byte getFromBack();
int getInt();
int getIntFromBack();
long getLong();
long getLongFromBack();
float getFloat();
float getFloatFromBack();
private:
byte* data;
volatile unsigned int capacity;
volatile unsigned int position;
volatile unsigned int length;
volatile boolean fillError;
};
#endif
@@ -1,21 +0,0 @@
#ifndef INCLUDE_GETPSTR
#define INCLUDE_GETPSTR
#if defined(ARDUINO) && ARDUINO >= 100
#include <Arduino.h>
#else
#include "pins_arduino.h"
#include "WProgram.h"
#include "wiring.h"
#endif
#define getPSTR(s) pgmStrToRAM(PSTR(s))
char *_pstr_to_print;
char *pgmStrToRAM(PROGMEM char *theString) {
free(_pstr_to_print);
_pstr_to_print=(char *) malloc(strlen_P(theString));
strcpy_P(_pstr_to_print, theString);
return (_pstr_to_print);
}
#endif
@@ -1,77 +0,0 @@
// PinChangeIntExample, version 1.1 Sun Jan 15 06:24:19 CST 2012
// See the Wiki at http://code.google.com/p/arduino-pinchangeint/wiki for more information.
//-------- define these in your sketch, if applicable ----------------------------------------------------------
// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
// on any one or two of the three ports. If only a single port remains, the handler will be declared inline
// reducing the size and latency of the handler.
//#define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
//#define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts
// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts
// if there is only one PCInt vector in use the code can be inlined
// reducing latency and code size
// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation.
// #define DISABLE_PCINT_MULTI_SERVICE
//-------- define the above in your sketch, if applicable ------------------------------------------------------
#include <PinChangeInt.h>
// This example demonstrates a configuration of 3 interrupting pins and 2 interrupt functions.
// All interrupts are serviced immediately, but one of the pins (pin 4) will show you immediately
// on the Terminal. The other function connected to 2 pins sets an array member that is queried in loop().
// You can then query the array at your leisure.
// This makes loop timing non-critical.
// Add more Pins at your leisure.
// For the Analog Input pins used as digital input pins, and you can use 14, 15, 16, etc.
// or you can use A0, A1, A2, etc. (the Arduino code comes with #define's
// for the Analog Input pins and will properly recognize e.g., pinMode(A0, INPUT);
#define PIN1 2
#define PIN2 3
#define PIN3 4
uint8_t latest_interrupted_pin;
uint8_t interrupt_count[20]={0}; // 20 possible arduino pins
void quicfunc() {
latest_interrupted_pin=PCintPort::arduinoPin;
interrupt_count[latest_interrupted_pin]++;
};
// You can assign any number of functions to any number of pins.
// How cool is that?
void pin3func() {
Serial.print("Pin "); Serial.print(PIN3, DEC); Serial.println("!");
}
void setup() {
pinMode(PIN1, INPUT); digitalWrite(PIN1, HIGH);
PCintPort::attachInterrupt(PIN1, &quicfunc, FALLING); // add more attachInterrupt code as required
pinMode(PIN2, INPUT); digitalWrite(PIN2, HIGH);
PCintPort::attachInterrupt(PIN2, &quicfunc, FALLING);
pinMode(PIN3, INPUT); digitalWrite(PIN3, HIGH);
PCintPort::attachInterrupt(PIN3, &pin3func, CHANGE);
Serial.begin(115200);
Serial.println("---------------------------------------");
}
uint8_t i;
void loop() {
uint8_t count;
Serial.print(".");
delay(1000);
for (i=0; i < 20; i++) {
if (interrupt_count[i] != 0) {
count=interrupt_count[i];
interrupt_count[i]=0;
Serial.print("Count for pin ");
if (i < 14) {
Serial.print("D");
Serial.print(i, DEC);
} else {
Serial.print("A");
Serial.print(i-14, DEC);
}
Serial.print(" is ");
Serial.println(count, DEC);
}
}
}
@@ -1,265 +0,0 @@
// PinChangeIntSpeedTest by GreyGnome aka Mike Schwager. Version numbers here refer to this sketch.
// Version 1.0 - initial version
// Version 1.1 - added code to test digitalRead()
// Version 1.2 - added new comments for the #define's for the NO_PORTx_PINCHANGES.
// Version 1.3 - includes cbiface.h with ooPinChangeInt, rather than cb.h
// Version 1.4 - testing version 2.10Beta with robtillaart's optimization
// Also added a #define/#undef INLINE_PCINTFUNC for inlining of the function called by the interrupt.
// Default: #undef for using the function as per usual. Changed PCIVERSION so that
// ooPinChangeInt starts at 1000 instead of 200. Modified the "Start" message to show "Start..", pause
// for 1 second, show "*\n" (where \n is a newline), pause for 1 second, then run the test.
// Version 1.4 - made this compatible with version 1.5 of PinChangeInt
// Version 1.5 - modified it to use #define OOPCIVERSION for ooPinChangeInt
// This version number is for ooPinChangeInt
//#define OOPCIVERSION 1030
#ifndef OOPCIVERSION
#define PCIVERSION 217 // 110 if using PinChangeInt-1.1, 120 for version 1.2
// 1000 for ooPinChangeIntversion 1.00, 1001 for ooPinChangeInt version 1.01, etc.
#endif
//-------- define these in your sketch, if applicable ----------------------------------------------------------
// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
// on any one or two of the three ports. If only a single port remains, the handler will be declared inline
// reducing the size and latency of the handler.
#undef NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
#undef NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts
// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts
// You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine
// slightly by declaring that you don't care if the static variables PCintPort::pinState and/or
// PCintPort::arduinoPin are set and made available to your interrupt routine.
// #define NO_PIN_STATE // to indicate that you don't need the pinState
// #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin
// if there is only one PCInt vector in use the code can be inlined
// reducing latency and code size
// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation.
//#define DISABLE_PCINT_MULTI_SERVICE
//-------- define the above in your sketch, if applicable ------------------------------------------------------
#if defined(OOPCIVERSION)
#define LIBRARYUNDERTEST "ooPinChangeInt"
#include <ooPinChangeInt.h>
#if PCIVERSION == 1001
#include <cb.h>
#else
#include <cbiface.h>
#endif
#else
#define LIBRARYUNDERTEST "PinChangeInt"
#include <PinChangeInt.h>
#endif
#define SERIALSTUFF // undef to take out all serial statements. Default: #define for measuring time.
#undef MEMTEST // undef to take out memory tests. Default: #undef for measuring time.
#undef INLINE_PCINTFUNC // define to inline the function called from the interrupt. This should have no effect,
// because the compiler will store the registers upon calling the interrupt routine, just
// like calling a function. Still, we test all assumptions.
//-----------------------
// NOTE: BECAUSE OF COLLISIONS in these libraries, you CANNOT have both libraries: PinChangeInt
// and ooPinChangeInt in the libraries directory at the same time. That said, under UNIX-y operating
// systems, it's easy to move the library directory to a name such as "PinChangeInt-1.3", which the
// Arduino will not recognize, and then create a symbolic link when you want to use a library. Such as:
// cd ~/Documents/Arduino/libaries
// mv PinChangeInt PinChangeInt-1.30
// mv ooPinChangeInt ooPinChangeInt-1.00
// ln -s PinChangeInt-1.30 PinChangeInt
#undef FLASH // to flash LED on pin 13 during test
#ifdef MEMTEST
#include <MemoryFree.h>
#endif
#define TEST 6
#if TEST == 1
#define PTEST 2 // pin to trigger interrupt. pins 0 and 1 are used
#define PLOW 2 // by Serial, so steer clear of them!
#define PHIGH 2 // Interrupts are attached to these pins
#elif TEST == 2 // see the #if TEST == 2 || TEST == 3 code, below
#define PTEST 2
#define PLOW 2
#define PHIGH 2 // need to attachInterrupt to 5 in the code
#elif TEST == 3 // see the #if TEST == 2 || TEST == 3 code, below
#define PTEST 5
#define PLOW 2
#define PHIGH 2 // need to attachInterrupt to 5 in the code
#elif TEST == 4
#define PTEST 2
#define PLOW 2
#define PHIGH 5
#elif TEST == 5
#define PTEST 3
#define PLOW 2
#define PHIGH 5
#elif TEST == 6
#define PTEST 4
#define PLOW 2
#define PHIGH 5
#elif TEST == 7
#define PTEST 5
#define PLOW 2
#define PHIGH 5
#endif
uint8_t qf0;
#ifdef INLINE_PCINTFUNC
#define INLINE_PCINTFUNC inline
#else
#define INLINE_PCINTFUNC
#endif
INLINE_PCINTFUNC void quicfunc();
void quicfunc() {
qf0=TCNT0;
}
#if defined(OOPCIVERSION)
class speedy : public CallBackInterface
{
public:
uint8_t id;
static uint8_t var0;
speedy () { id=0; };
speedy (uint8_t _i): id(_i) {};
void cbmethod() {
speedy::var0=TCNT0;
//Serial.print("Speedy method "); // debugging
//Serial.println(id, DEC);
};
};
uint8_t speedy::var0=0;
#endif
volatile uint8_t *led_port;
volatile uint8_t *pinT_OP;
volatile uint8_t *pinT_IP;
uint8_t led_mask, not_led_mask;
uint8_t pinT_M, not_pinT_M;
volatile uint8_t pintest, pinIntLow, pinIntHigh;
uint8_t totalpins;
#if defined(OOPCIVERSION)
speedy speedster[8]={speedy(0), speedy(1), speedy(2), speedy(3), speedy(4), speedy(5), speedy(6), speedy(7) };
#endif
#ifdef MEMTEST
int freemem;
#endif
int i=0;
#define PINLED 13
void setup()
{
#ifdef SERIALSTUFF
Serial.begin(115200); Serial.println("---------------------------------------");
#endif // SERIALSTUFF
// set up ports for trigger
pinMode(0, OUTPUT); digitalWrite(0, HIGH);
pinMode(1, OUTPUT); digitalWrite(1, HIGH);
pinMode(2, OUTPUT); digitalWrite(2, HIGH);
pinMode(3, OUTPUT); digitalWrite(3, HIGH);
pinMode(4, OUTPUT); digitalWrite(4, HIGH);
pinMode(5, OUTPUT); digitalWrite(5, HIGH);
pinMode(6, OUTPUT); digitalWrite(6, HIGH);
pinMode(7, OUTPUT); digitalWrite(7, HIGH);
#ifdef FLASH
led_port=portOutputRegister(digitalPinToPort(PINLED));
led_mask=digitalPinToBitMask(PINLED);
not_led_mask=led_mask^0xFF;
pinMode(PINLED, OUTPUT); digitalWrite(PINLED, LOW);
#endif
// *****************************************************************************
// set up ports for output ************ PIN TO TEST IS GIVEN HERE **************
// *****************************************************************************
pintest=PTEST;
pinIntLow=PLOW; pinIntHigh=PHIGH; // Interrupts are attached to these pins
// *****************************************************************************
// *****************************************************************************
pinT_OP=portOutputRegister(digitalPinToPort(pintest)); // output port
pinT_IP=portInputRegister(digitalPinToPort(pintest)); // input port
pinT_M=digitalPinToBitMask(pintest); // mask
not_pinT_M=pinT_M^0xFF; // not-mask
*pinT_OP|=pinT_M;
for (i=pinIntLow; i <= pinIntHigh; i++) {
#if defined(OOPCIVERSION)
PCintPort::attachInterrupt(i, &speedster[i], CHANGE); // C++ technique; v1.3 or better
#endif
#if defined(PCIVERSION)
PCintPort::attachInterrupt((uint8_t) i, &quicfunc, CHANGE); // C technique; v1.2 or earlier
#endif
}
#if TEST == 2 || TEST == 3
i=5; totalpins=2;
#if defined(OOPCIVERSION)
PCintPort::attachInterrupt(i, &speedster[i], CHANGE); // C++ technique; v1.3 or better
#endif
#if defined(PCIVERSION)
PCintPort::attachInterrupt(i, &quicfunc, CHANGE); // C technique; v1.2 or earlier
#endif
#else
totalpins=pinIntHigh - pinIntLow + 1;
#endif
i=0;
} // end setup()
uint8_t k=0;
unsigned long milliStart, milliEnd, elapsed;
void loop() {
k=0;
*pinT_OP|=pinT_M; // pintest to 1
#ifdef SERIALSTUFF
Serial.print(LIBRARYUNDERTEST); Serial.print(" ");
Serial.print("TEST: "); Serial.print(TEST, DEC); Serial.print(" ");
#ifndef MEMTEST
Serial.print("test pin mask: "); Serial.print(pinT_M, HEX);
Serial.print(". Total of "); Serial.print(totalpins, DEC); Serial.println(" pins enabled.");
#endif
#ifdef MEMTEST
freemem=freeMemory(); Serial.print("Free memory: "); Serial.println(freemem, DEC);
#endif
#endif
delay(1000);
Serial.print("Start..");
delay(1000); Serial.print("*");
#ifdef FLASH
*led_port|=led_mask;
#endif
milliStart=millis();
while (k < 10) {
i=0;
while (i < 10000) {
*pinT_OP&=not_pinT_M; // pintest to 0 ****************************** 16.8 us
*pinT_OP|=pinT_M; // pintest to 1 ****************************** ...to get here
i++;
}
k++;
}
milliEnd=millis();
#ifdef FLASH
*led_port&=not_led_mask;
#endif
elapsed=milliEnd-milliStart;
#ifndef MEMTEST
Serial.print(" Elapsed: ");
Serial.println(elapsed, DEC);
#endif
#ifdef SERIALSTUFF
Serial.print("Interrupted pin: ");
#if defined(OOPCIVERSION)
Serial.println(speedster[pintest].id, DEC);
#else
Serial.println(PCintPort::arduinoPin, DEC);
#endif
#ifdef MEMTEST
freemem=freeMemory(); Serial.print("END-Free memory: "); Serial.println(freemem, DEC);
#endif
#endif
delay(500);
}
@@ -1,344 +0,0 @@
// PinChangeIntTest
// version 1.0 Wed Feb 15 07:25:09 CST 2012
// Version 1.1 Fri Jun 22 19:10:50 CDT 2012 minor tweaks to eliminate compiler warnings. Also, there were bugfixes in ByteBuffer.
// I had some "cli()" without "sei()" in there.
// See the Wiki at http://code.google.com/p/arduino-pinchangeint/wiki for more information.
// This sketch requires the ByteBuffer library, which is found in the PinChangeInt zipfile.
//-------- define these in your sketch, if applicable ----------------------------------------------------------
//-------- This must go ahead of the #include statement --------------------------------------------------------
// You can reduce the memory footprint of this handler by declaring that there will be no pin change interrupts
// on any one or two of the three ports. If only a single port remains, the handler will be declared inline
// reducing the size and latency of the handler.
// #define NO_PORTB_PINCHANGES // to indicate that port b will not be used for pin change interrupts
// #define NO_PORTC_PINCHANGES // to indicate that port c will not be used for pin change interrupts
// #define NO_PORTD_PINCHANGES // to indicate that port d will not be used for pin change interrupts
// You can reduce the code size by 20-50 bytes, and you can speed up the interrupt routine
// slightly by declaring that you don't care if the static variables PCintPort::pinState and/or
// PCintPort::arduinoPin are set and made available to your interrupt routine.
// #define NO_PIN_STATE // to indicate that you don't need the pinState
// #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin
// if there is only one PCInt vector in use the code can be inlined
// reducing latency and code size
// define DISABLE_PCINT_MULTI_SERVICE below to limit the handler to servicing a single interrupt per invocation.
// #define DISABLE_PCINT_MULTI_SERVICE
// The following is intended for testing purposes. If defined, then a variable PCintPort::pinMode can be read
// in your interrupt subroutine. It is not defined by default:
// #define PINMODE
//-------- define the above in your sketch, if applicable ------------------------------------------------------
#define PINMODE
#define FLASH
#include <ByteBuffer.h>
#include <PinChangeInt.h>
// This example demonstrates a configuration of 6 interrupting pins and 3 interrupt functions.
// A variety of interrupting pins have been chosen, so as to test all PORTs on the Arduino.
// The pins are as follows:
#define tPIN1 2 // port D
#define tPIN2 3
#define tPIN3 11 // Port B
#define tPIN4 12
#define tPIN5 A3 // Port C, also can be given as "17"
#define tPIN6 A4 // starts and stops the count
// All pins send interrupts. Arduino pins 2 and A4 (tPIN1,6) interrupt on FALLING.
// Arduino pins 3 and 12 (tPIN2,4) interrupt on RISING.
// Arduino pins 11 and A3 (tPIN5) interrupts on CHANGE.
// quicfunc0 is attached to Arduino pins 2, 3, 11, and 12 (tPIN1-4)
// quicfunc1 is attached to Arduino pin A3 (tPIN5)
// quicfunc2 is attached to Arduino pin A4 (tPIN6).
// NOTE:
// For the Analog Input pins used as digital input pins, you can use numbers such as 14, 15, 16, etc.
// or you can use A0, A1, A2, etc. (the Arduino code comes with #define's for the Analog Input pin
// names and will properly recognize e.g., pinMode(A0, INPUT));
// HOW IT WORKS
// The interrupt on Arduino pin A4 (tPIN6) will, when triggered, start the counting of interrupts.
// The array interrupt_count0[20] is updated in the interrupts; each cell keeps track of the number
// of interrupts on one of the 20 available interrupt pins on the Arduino. Every second in the main
// loop the array is scanned and registered interrupts are reported for all pins interrupted since
// the previous second. If no interrupts, the output is quiet.
// tPIN6 is special. Not only does it start the counting of the interrups, but it turns on and off
// interrupts on pins 2, 11, and A3/17 (tPIN1, tPIN3, tPIN5). All pins start by interrupting, but after
// the count is turned on and then turned off, the 3 pins are detached from interrupts.
// Everytime thereafter when the count is turned off the 3 pins are detached. They are reattached
// when turned on.
// Output is copied to a buffer, because we can't do a Serial.print() statement in an interrupt
// routine. The main loop checks for entries in the buffer and prints them if found.
// Output looks like this:
// -F- - an interrupt triggered by a falling signal occurred.
// +R+ - an interrupt triggered by a rising signal occurred.
// *C* - an interrupt triggered by a change in signal occurred.
// f#p#-P# - f# shows the interrupt subroutine that was called: 0, 1, or 2
// - p# shows the pin number that triggered the interrupt
// - P# shows the port that this pin number is attached to. 2 is PORTB, 3 is PORTC, 4 is PORTD
// HOW TO CONNECT
// Each pin gets a momentary contact switch connected to it. One side of the switch should connect
// to ground. The other side of the switch connects to the Arduino pin. For my purposes, I am using
// two rotary encoders. Each encoder contains 3 switches. But 6 regular pushbuttons would work, too.
/* WHAT TO LOOK FOR
Output is sent to the serial line, so the Arduino IDE's serial terminal should be opened.
Upon startup, press tPINS1-5. You will see output like this:
-F-f0p2-P4 (counting off)
..*C*f0p11-P2 (counting off)
+R+f0p3-P4 (counting off)
This shows that
1. an interrupt was triggered on a falling signal (*F*). It called (f0) function 0, which is quicfunc0.
The triggering pin was (p2) Arduuino pin 2, which is on (P4) Port 4 (PORTD). Counting of this interrupt is
off, so you will not see any output from the main loop.
2. Two dots appeared. Dots came from iterations of loop(), so these 2 dots show that the two interrupts happened 2 seconds apart.
3. an interrupt was triggered on a change in signal (*C*). It called quicfunc0, from Arduino pin 11, on Port 2 (PORTB).
The interrupt was not counted.
4. an interrupt was triggered on a rising signal (+R+). It called quicfunc0, from Arduino pin 3, on Purt 4 (PORTD).
The pin should have started out at the high level, so likely the signal fell during onother interrupt, and now
the rise has been caught.
Now press the button attached to tPIN6 (in our case, A4 or D18). You will see something like this:
-F-START! f2p18-P3
.Count for pin A4 is 1
This shows that
1. The counting machanism (START!) was triggered by a folling signal (-F-) on pin 18 (p18) which is in Port 3 (P3) (which == PORTC) and
function f2 was called (f2).
2. A dot appeared, which came from loop() because a second passed.
3. The count for p18 or A4 was displayed.
Now you will see messages for all the pins that you manipulate, for example:
*C*f0p11-P2
+R+f0p3-P4
*C*f0p11-P2
+R+f0p3-P4
*C*f0p11-P2
.Count for pin D3 is 6
Count for pin D11 is 9
.+R+f0p3-P4
-F-f0p2-P4
.Count for pin D2 is 1
Count for pin D3 is 1
These codes reflect the interrupts, as described above. This output will take place until you press tPIN6:
-F-f2: STOP! Counting off.
Interrupt OFF on tPIN1 (2) tPIN3 (11) tPIN5 (17)
Then you will see output like this:
.....................+R+f0p12-P2 (counting off)
.+R+f0p12-P2 (counting off)
+R+f0p12-P2 (counting off)
+R+f0p12-P2 (counting off)
and tPIN1, tPIN3, and tPIN5 will not trigger interrupts.
*/
// NOTES
// Output overwrites:
// It's possible during moderately fast interrupts to see your print output get garbled; eg,
// +R+f0p12-P2 (+R+f0p12-P2 (counting +R+f0p12-P2 (cou+R+f0p12-P+R+f0p12
// This is because the print of the buffer takes place inside a while loop, and it can
// be interrupted and new data inserted into the buffer at a midpoint of the buffer's text.
// Just by spinning my rotary encoders I can readily generate over 200 interrupts per second
// on a pin, which is easily fast enough to overrun Serial output at 115,200 bps.
// The lesson here? ...Interrupts are tricky, and interrupt service routines should be fast.
// Just sayin'.
// Pins:
// We want to use pins from each of ports B, C and D. So choose wisely. Ports are shown in
// this diagram of the ATmega328P chip. PD0 means "Port D, pin 0". PC3 means "Port C, Pin 3",
// PB2 means "Port B, pin 2" and so on. The corresponding Arduino pins are in parentheses.
// So PB2 is Arduino pin D 10, for example.
/*
+-\/-+
PC6 1| |28 PC5 (AI 5)
(D 0) PD0 2| |27 PC4 (AI 4)
(D 1) PD1 3| |26 PC3 (AI 3)
(D 2) PD2 4| |25 PC2 (AI 2)
PWM+ (D 3) PD3 5| |24 PC1 (AI 1)
(D 4) PD4 6| |23 PC0 (AI 0)
VCC 7| |22 GND
GND 8| |21 AREF
PB6 9| |20 AVCC
PB7 10| |19 PB5 (D 13)
PWM+ (D 5) PD5 11| |18 PB4 (D 12)
PWM+ (D 6) PD6 12| |17 PB3 (D 11) PWM
(D 7) PD7 13| |16 PB2 (D 10) PWM
(D 8) PB0 14| |15 PB1 (D 9) PWM
+----+
*/
uint8_t pins[6]={ tPIN1, tPIN2, tPIN3, tPIN4, tPIN5, tPIN6 };
uint8_t ports[6]={ 0, 0, 0, 0, 0, 0 };
uint8_t latest_interrupted_pin;
uint8_t interrupt_count[20]={0}; // 20 possible arduino pins
uint8_t port;
uint8_t mode;
ByteBuffer printBuffer(80);
char charArray[16];
char numBuffer[4] = { 0, 0, 0, 0 };
uint8_t printFull=0;
volatile boolean start=0;
volatile boolean initial=true;
long begintime=0;
long now=0;
void uint8ToString(char *outString, uint8_t number) {
uint8_t hundreds=0;
uint8_t tens=0;
uint8_t ones=0;
while (number >= 100 ) {
hundreds++;
number-=100;
}
while (number >= 10 ) {
tens++;
number-=10;
}
ones=number;
ones+=48;
if (hundreds > 0) { hundreds+=48; tens+=48; outString[0]=hundreds; outString[1]=tens; outString[2]=ones; outString[3]=0; }
else if (tens > 0) { tens+=48; outString[0]=tens; outString[1]=ones; outString[2]=0; }
else { outString[0]=ones; outString[1]=0; };
}
void showMode() {
switch (mode) {
case FALLING:
printBuffer.putString((char *) "-F-");
break;
case RISING:
printBuffer.putString((char *) "+R+");
break;
case CHANGE:
printBuffer.putString((char *) "*C*");
break;
}
}
void quicfunc0() {
latest_interrupted_pin=PCintPort::arduinoPin;
mode=PCintPort::pinmode;
showMode();
if (start==1) {
interrupt_count[latest_interrupted_pin]++;
}
uint8ToString(numBuffer, latest_interrupted_pin);
printBuffer.putString((char *) "f0p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
printBuffer.putString(numBuffer);
if (start !=1) printBuffer.putString((char *) " (counting off)");
printBuffer.putString((char *) "\n");
};
void quicfunc1() {
latest_interrupted_pin=PCintPort::arduinoPin;
mode=PCintPort::pinmode;
showMode();
if (start==1) {
interrupt_count[latest_interrupted_pin]++;
}
uint8ToString(numBuffer, latest_interrupted_pin);
printBuffer.putString((char *) "f1p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
printBuffer.putString(numBuffer);
if (start !=1) printBuffer.putString((char *) " (counting off)");
printBuffer.putString((char *) "\n");
};
void quicfunc2() {
latest_interrupted_pin=PCintPort::arduinoPin;
mode=PCintPort::pinmode;
showMode();
if (start == 1) {
printBuffer.putString((char *) "f2: STOP! Counting off.\n");
printBuffer.putString((char *) "Interrupt OFF on tPIN1 ("); uint8ToString(numBuffer, tPIN1), printBuffer.putString(numBuffer);
printBuffer.putString((char *) ") tPIN3 (");uint8ToString(numBuffer, tPIN3), printBuffer.putString(numBuffer);
printBuffer.putString((char *) ") tPIN5 (");uint8ToString(numBuffer, tPIN5), printBuffer.putString(numBuffer);
printBuffer.putString((char *) ")\n");
PCintPort::detachInterrupt(tPIN1); PCintPort::detachInterrupt(tPIN3); PCintPort::detachInterrupt(tPIN5);
start=0;
} else {
start=1;
interrupt_count[latest_interrupted_pin]++;
printBuffer.putString((char *) "START! f2p");
uint8ToString(numBuffer, latest_interrupted_pin);
printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
printBuffer.putString(numBuffer); printBuffer.putString((char *) "\n");
if (! initial) {
PCintPort::attachInterrupt(tPIN1, &quicfunc0, FALLING);
PCintPort::attachInterrupt(tPIN3, &quicfunc0, CHANGE);
PCintPort::attachInterrupt(tPIN5, &quicfunc1, CHANGE);
} else {
initial=false;
}
}
};
uint8_t i;
void setup() {
Serial.begin(115200);
delay(250);
Serial.println("Test");
delay(500);
for (i=0; i < 7; i++) {
pinMode(pins[i], INPUT); digitalWrite(pins[i], HIGH);
ports[i]=digitalPinToPort(pins[i]);
switch (pins[i]) {
case tPIN1:
PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING);
break;
case tPIN3:
PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE);
break;
case tPIN2:
case tPIN4:
PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING);
break;
case tPIN5:
PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE);
break;
case tPIN6:
PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING);
break;
}
}
//Serial.println(printBuffer.getCapacity(), DEC);
//Serial.println("*---------------------------------------*");
Serial.print("*---*");
delay(250);
begintime=millis();
}
void loop() {
now=millis();
uint8_t count;
char outChar;
// uint8_t bufsize;
//if (printBuffer.getSize() != 0) { Serial.print("SZ:"); Serial.println (printBuffer.getSize(), DEC); };
//bufsize=printBuffer.getSize();
//if (bufsize > 0) { Serial.print("S:"); Serial.println(bufsize); }
while ((outChar=(char)printBuffer.get()) != 0) Serial.print(outChar);
if ((now - begintime) > 1000) {
Serial.print(".");
if (printBuffer.checkError()) {
Serial.println("NOTICE: Some output lost due to filled buffer.");
}
for (i=0; i < 20; i++) {
if (interrupt_count[i] != 0) {
count=interrupt_count[i];
interrupt_count[i]=0;
Serial.print("Count for pin ");
if (i < 14) {
Serial.print("D");
Serial.print(i, DEC);
} else {
Serial.print("A");
Serial.print(i-14, DEC);
}
Serial.print(" is ");
Serial.println(count, DEC);
}
}
begintime=millis();
}
}
@@ -1,275 +0,0 @@
//#define DISABLE_PCINT_MULTI_SERVICE
#define PINMODE
#define FLASH
#include <GetPSTR.h>
#include <ByteBuffer.h>
#include <PinChangeInt.h>
// This example demonstrates a configuration of 6 interrupting pins and 3 interrupt functions.
// A variety of interrupting pins have been chosen, so as to test all PORTs on the Arduino.
// The pins are as follows:
#define tPIN1 2 // port D
#define tPIN2 3
#define tPIN3 11 // Port B
#define tPIN4 12
#define tPIN5 A3 // Port C, also can be given as "17"
#define tPIN6 A4 // starts and stops the count
uint8_t pins[6]={ tPIN1, tPIN2, tPIN3, tPIN4, tPIN5, tPIN6 };
uint8_t ports[6]={ 0, 0, 0, 0, 0, 0 };
uint8_t latest_interrupted_pin;
uint8_t interrupt_count[20]={0}; // 20 possible arduino pins
uint8_t port;
uint8_t mode;
ByteBuffer printBuffer(200);
char charArray[16];
char numBuffer[5] = { 0, 0, 0, 0, 0 };
uint8_t printFull=0;
volatile boolean start=0;
volatile boolean initial=true;
long begintime=0;
long now=0;
void uint8ToHexString(char *outString, uint8_t theByte) {
outString[0]='0'; outString[1]='x';
uint8_t hinybble=theByte>>4;
uint8_t lonybble=theByte & 0x0F;
if (hinybble < 0x0a) outString[2]=hinybble+48;
else outString[2]=hinybble+55;
if (lonybble < 0x0a) outString[3]=lonybble+48;
else outString[3]=lonybble+55;
outString[4]=0;
}
void uint8ToString(char *outString, uint8_t number) {
uint8_t hundreds=0;
uint8_t tens=0;
uint8_t ones=0;
while (number >= 100 ) {
hundreds++;
number-=100;
}
while (number >= 10 ) {
tens++;
number-=10;
}
ones=number;
ones+=48;
if (hundreds > 0) { hundreds+=48; tens+=48; outString[0]=hundreds; outString[1]=tens; outString[2]=ones; outString[3]=0; }
else if (tens > 0) { tens+=48; outString[0]=tens; outString[1]=ones; outString[2]=0; }
else { outString[0]=ones; outString[1]=0; };
}
void showMode() {
switch (mode) {
case FALLING:
printBuffer.putString(getPSTR("-F-"));
break;
case RISING:
printBuffer.putString(getPSTR("+R+"));
break;
case CHANGE:
printBuffer.putString(getPSTR("*C*"));
break;
}
}
/*
void quicfunc0() {
latest_interrupted_pin=PCintPort::arduinoPin;
mode=PCintPort::pinmode;
showMode();
if (start==1) {
interrupt_count[latest_interrupted_pin]++;
}
uint8ToString(numBuffer, latest_interrupted_pin);
printBuffer.putString((char *) "f0p"); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
printBuffer.putString(numBuffer);
if (start !=1) printBuffer.putString(getPSTR(" no count"));
printBuffer.putString((char *) "\n");
};
void quicfunc1() {
latest_interrupted_pin=PCintPort::arduinoPin;
mode=PCintPort::pinmode;
showMode();
if (start==1) {
interrupt_count[latest_interrupted_pin]++;
}
uint8ToString(numBuffer, latest_interrupted_pin);
printBuffer.putString(getPSTR("f1p")); printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
printBuffer.putString(numBuffer);
if (start !=1) printBuffer.putString(getPSTR(" (counting off)"));
printBuffer.putString((char *) "\n");
};
*/
void quicfunc2() {
//*led_port|=led_mask;
//*led_port&=not_led_mask; // 2 micros to here (ie, 2 micros used to push registers and call subroutine)
latest_interrupted_pin=PCintPort::arduinoPin;
mode=PCintPort::pinmode;
showMode();
*led_port|=led_mask; // 73 micros to get here from above. Used in "Rigol Timing Example"
*led_port&=not_led_mask;
//uint8ToString(numBuffer, PCintPort::s_count); printBuffer.putString(numBuffer);
*led_port|=led_mask; // 73 micros to get here from above. Second pulse in "Rigol Timing Example"
*led_port&=not_led_mask;
printBuffer.putString(getPSTR(" f2: P"));/*
uint8ToHexString(numBuffer, *portInputRegister(3)); printBuffer.putString(numBuffer);// C port
printBuffer.putString(getPSTR(" pin:")); uint8ToString(numBuffer, latest_interrupted_pin); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" c")); uint8ToHexString(numBuffer, PCintPort::curr); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" l")); uint8ToHexString(numBuffer, PCintPort::s_lastPinView); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" r")); uint8ToHexString(numBuffer, PCintPort::s_portRisingPins); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" f")); uint8ToHexString(numBuffer, PCintPort::s_portFallingPins); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" m")); uint8ToHexString(numBuffer, PCintPort::s_pmask); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" P")); printBuffer.put(PCintPort::s_PORT); printBuffer.putString("\r\n");
printBuffer.putString(getPSTR("cp")); uint8ToHexString(numBuffer, PCintPort::s_changedPins); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" cXORlpv")); uint8ToHexString(numBuffer, PCintPort::s_currXORlastPinView); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" rp_nCurr")); uint8ToHexString(numBuffer, PCintPort::s_portRisingPins_nCurr); printBuffer.putString(numBuffer);
printBuffer.putString(getPSTR(" fp_nNCurr")); uint8ToHexString(numBuffer, PCintPort::s_portFallingPins_nNCurr); printBuffer.putString(numBuffer);
*/printBuffer.putString("\r\n");
if (PCintPort::pcint_multi > 0) {
printBuffer.putString("MULTI!\n"); PCintPort::pcint_multi=0;
}
if (PCintPort::PCIFRbug > 0) { printBuffer.putString("ERROR: BUG- PCIFR should be reset!"); PCintPort::PCIFRbug=0; }
//s_registers, if it existed, could be used to keep a running queue of the latest interrupts that have
//been serviced by the PCint(). But generally I don't think it's necessary for debugging at this point (famous last words?)
/*if (PCintPort::s_count > 2) {
for (uint8_t i=0; i < PCintPort::s_count; i++) {
uint8ToHexString(numBuffer, PCintPort::s_registers[i]); printBuffer.putString(numBuffer); printBuffer.putString(" ");
}
}
PCintPort::s_count=0;*/
/*
if (start == 1) {
printBuffer.putString(getPSTR("STOP Count off\n"));
printBuffer.putString(getPSTR("Intr OFF: (")); uint8ToString(numBuffer, tPIN1), printBuffer.putString(numBuffer);
printBuffer.putString((char *) " "); uint8ToString(numBuffer, tPIN3), printBuffer.putString(numBuffer);
printBuffer.putString((char *) " "); uint8ToString(numBuffer, tPIN5), printBuffer.putString(numBuffer);
printBuffer.putString((char *) ")\n");
PCintPort::detachInterrupt(tPIN1); PCintPort::detachInterrupt(tPIN3); PCintPort::detachInterrupt(tPIN5);
start=0;
} else {
start=1;
interrupt_count[latest_interrupted_pin]++;
printBuffer.putString(getPSTR("START! p"));
uint8ToString(numBuffer, latest_interrupted_pin);
printBuffer.putString(numBuffer); printBuffer.putString((char *) "-P");
// MIKE put the REAL PORT HERE
uint8ToString(numBuffer, digitalPinToPort(latest_interrupted_pin));
printBuffer.putString(numBuffer); printBuffer.putString((char *) "\n");
if (! initial) {
PCintPort::attachInterrupt(tPIN1, &quicfunc0, FALLING);
PCintPort::attachInterrupt(tPIN3, &quicfunc0, CHANGE);
PCintPort::attachInterrupt(tPIN5, &quicfunc1, CHANGE);
} else {
initial=false;
}
}*/
};
uint8_t i;
char hexBuffer[5];
void setup() {
int8_t returncode=1;
Serial.begin(115200);
Serial.println("Test");
delay(500);
for (i=5; i < 6; i++) {
pinMode(pins[i], INPUT); digitalWrite(pins[i], HIGH);
ports[i]=digitalPinToPort(pins[i]);
switch (pins[i]) {
/*case tPIN1:
#if PCINT_VERSION > 2100
returncode=PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING);
#else
PCintPort::attachInterrupt(pins[i], &quicfunc0, FALLING);
#endif
Serial.println(getPSTR("FIRST FAILURE OK."));
break;
case tPIN3:
#if PCINT_VERSION > 2100
returncode=PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE);
#else
PCintPort::attachInterrupt(pins[i], &quicfunc0, CHANGE);
#endif
break;
case tPIN2:
case tPIN4:
#if PCINT_VERSION > 2100
returncode=PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING);
#else
PCintPort::attachInterrupt(pins[i], &quicfunc0, RISING);
#endif
break;
case tPIN5:
#if PCINT_VERSION > 2100
returncode=PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE);
#else
PCintPort::attachInterrupt(pins[i], &quicfunc1, CHANGE);
#endif
break;*/
case tPIN6:
#if PCINT_VERSION > 2100
returncode=PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING);
#else
PCintPort::attachInterrupt(pins[i], &quicfunc2, FALLING);
#endif
break;
}
#if PCINT_VERSION > 2100
Serial.print(getPSTR("setup(): Interrupt attach "));
if (returncode != 1) Serial.print(getPSTR("unsuccessful "));
else Serial.print(getPSTR("GOOD "));
Serial.print(pins[i], DEC);
Serial.print(getPSTR(":pin, code: ")); Serial.println(returncode, DEC);
#endif
}
//Serial.println(printBuffer.getCapacity(), DEC);
//Serial.println("*---------------------------------------*");
Serial.print("*---*");
delay(250);
begintime=millis();
}
void loop() {
now=millis();
uint8_t count;
char outChar;
// uint8_t bufsize;
//if (printBuffer.getSize() != 0) { Serial.print("SZ:"); Serial.println (printBuffer.getSize(), DEC); };
//bufsize=printBuffer.getSize();
//if (bufsize > 0) { Serial.print("S:"); Serial.println(bufsize); }
while ((outChar=(char)printBuffer.get()) != 0) Serial.print(outChar);
if ((now - begintime) > 1000) {
Serial.print(".");
if (printBuffer.checkError()) {
Serial.println(getPSTR("!Some output lost due to full buffer!"));
}
for (i=0; i < 20; i++) {
if (interrupt_count[i] != 0) {
count=interrupt_count[i];
interrupt_count[i]=0;
Serial.print(getPSTR("Count for pin "));
if (i < 14) {
Serial.print("D");
Serial.print(i, DEC);
} else {
Serial.print("A");
Serial.print(i-14, DEC);
}
Serial.print(" is ");
Serial.println(count, DEC);
}
}
begintime=millis();
}
}
-332
View File
@@ -1,332 +0,0 @@
******************************************************************************
PinChangeInt
---- RELEASE NOTES ---
Version 2.19 (beta) Tue Nov 20 07:33:37 CST 2012
SANGUINO SUPPORT! ...And Mioduino!
...The ATmega644 chip is so cool, how can I not? 4 full ports of Pin Change Interrupt bliss! 32 i/o pins! 64k Flash! 4k RAM! Well I wish I had one. That said, Sanguino users, PLEASE send in your bug or bliss reports! Your interrupt-loving brethren and sistren are depending on you, so I can assure everyone that my changes work on that platform. Thanks.
Modified the addPin() method to save 12 bytes; thanks again robtilllart!
if (firstPin != NULL) {
tmp=firstPin;
do {
if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); }
if (tmp->next == NULL) break;
tmp=tmp->next;
} while (true);
}
Also changed the goto in the PCint() loop to be a while/break combination. No change to
code speed, but it looks better and passes the cleanliness "gut check".
Includes PinChangeIntTest 1.5 sketch.
...Ooops! Forgot the GetPSTR library, needed for the PinChangeIntTest code! Now it's included.
******************************************************************************
Version 2.17 (beta) Sat Nov 17 09:46:50 CST 2012
Another bugfix in the PCINT_MULTI_SERVICE section. I was using sbi(PCIFR, PCICRbit);
I didn't realize that was for I/O ports, and not ATmega registers.
But according to "deprecated.h",
"These macros became obsolete, as reading and writing IO ports can
be done by simply using the IO port name in an expression, and all
bit manipulation (including those on IO ports) can be done using
generic C bit manipulation operators."
So now I do:
PCIFR |= PCICRbit;
******************************************************************************
Version 2.15 (beta) Sat Nov 17 01:17:44 CST 2012
Fixed it so that attachInterrupt() will now follow your changes to the user function,
as well as the mode. detachInterrupt() still does not delete the PCintPin object but
at least you can detach and reattach at will, using different modes (RISING, FALLING,
CHANGE) and different functions as you wish.
******************************************************************************
Version 2.13 (beta) Mon Nov 12 09:33:06 CST 2012
SIGNIFICANT BUGFIX release! Significant changes:
1. PCintPort::curr bug. Interrupts that occur rapidly will likely not get serviced properly by PCint().
2. PCint() interrupt handler optimization.
3. PCIFR port bit set bug fix.
4. Many static variables added for debugging; used only when #define PINMODE is on.
5. detachInterrupt() no longer does a delete(), since that wasn't working anyway. When you detachInterrupt(), the PORT just disables interrupts for that pin; the PCintPin object remains in memory and in the linked list of pins (possibly slowing down your interrupts a couple of micros). You can reenable a detached interrupt- but you must do it within the PinChangeInt library (would anyone ever enable an interrupt on a pin, then disable it, then have need to reenable it but not using the library?).
6. attachInterrupt() now returns a uint8_t value: 1 on successful attach, 0 on successful attach but using an already-enabled pin, and -1 if the new() operator failed to create a PCintPin object.
Also, modified these release notes.
Details:
Uncovered a nasty bug, thanks to robtillaart on the Arduino Forums and Andre' Franken who posted to the PinChangeInt groups. This bug was introduced by me when I assigned PCintPort::curr early in the interrupt handler:
ISR(PCINT0_vect) {
PCintPort::curr = portB.portInputReg;
portB.PCint();
}
Later, in the interrupt handler PCint(), we loop as long as PCIFR indicates a new interrupt wants to be triggered, provided DISABLE_PCINT_MULTI_SERVICE is not defined (it is not by default):
#ifndef DISABLE_PCINT_MULTI_SERVICE
pcifr = PCIFR & PCICRbit;
PCIFR = pcifr; // clear the interrupt if we will process it (no effect if bit is zero)
} while(pcifr);
#endif
...Well. Problem is, if a pin pops up and causes the PCIFR to change, we have to reread the port and look at how it is now! I wasn't doing that before, so if a new interrupt appeared while I was still servicing the old one, odd behavior would take place. For example, an interrupt would register but then the userFunc would not be called upon to service it. The code needs to be:
pcifr = PCIFR & PCICRbit;
PCIFR = pcifr; // clear the interrupt if we will process it (no effect if bit is zero)
PCintPort::curr=portInputReg; // ...Fixed in 2.11beta.
} while(pcifr);
Also, made the interrupt handler even faster with an optimization from robtillaart to take out the checks for changed pins from the while() loop that steps through the pins:
uint8_t changedPins = (PCintPort::curr ^ lastPinView) &
((portRisingPins & PCintPort::curr ) | ( portFallingPins & ~PCintPort::curr ));
...This speedup is offset by more changes in the PCint() handler, which now looks like the following; there are two bug fixes:
----------------------------
FIX 1: sbi(PCIFR, PCICRbit);
FIX 2: ...the aforementioned PCintPort::curr=portInputReg;
Here's the new code:
----------------------------
#ifndef DISABLE_PCINT_MULTI_SERVICE
pcifr = PCIFR & PCICRbit;
if (pcifr) {
//if (PCIFR & PCICRbit) { // believe it or not, this adds .6 micros
sbi(PCIFR, PCICRbit); // This was a BUG: PCIFR = pcifr ...And here is the fix.
#ifdef PINMODE
PCintPort::pcint_multi++;
if (PCIFR & PCICRbit) PCintPort::PCIFRbug=1; // PCIFR & PCICRbit should ALWAYS be 0 here!
#endif
PCintPort::curr=portInputReg; // ...Fixed in 2.11beta.
goto loop; // A goto!!! Don't want to look at the portInputReg gratuitously, so the while() will not do.
}
#endif
Also I added a lot of variables for debugging when PINMODE is defined, for routing out nasty bugs. I may need them in the future... :-(
Finally, I am not putting newlines in this commentary so I can make it easier to paste online.
******************************************************************************
Version 2.11 (beta) Mon Nov 12 09:33:06 CST 2012
See version 2.13 (beta) above. No change other than tidying up the release notes.
******************************************************************************
Version 2.01 (beta) Thu Jun 28 12:35:48 CDT 2012
...Wow, Version 2! What? Why?
Modified the way that the pin is tested inside the interrupt subroutine (ISR) PCintPort::PCint(),
to make the interrupt quicker and slightly reduce the memory footprint. The interrupt's time is
reduced by 2 microseconds or about 7%. Instead of using the mode variable, two bitmasks are maintained
for each port. One bitmask contains all the pins that are configured to work on RISING signals, the other
on FALLING signals. A pin configured to work on CHANGE signals will appear in both bitmasks of the port.
Then, the test for a change goes like this:
if (thisChangedPin) {
if ((thisChangedPin & portRisingPins & PCintPort::curr ) ||
(thisChangedPin & portFallingPins & ~PCintPort::curr )) {
where portRisingPins is the bitmask for the pins configured to interrupt on RISING signals, and
portFallingPins is the bitmask for the pins configured to interrupt on FALLING signals. Each port includes
these two bitmask variables.
This is a significant change to some core functionality to the library, and it saves an appreciable amount
of time (2 out of 36 or so micros). Hence, the 2.00 designation.
Tue Jun 26 12:42:20 CDT 2012
I was officially given permission to use the PCint library:
Re: PCint library
« Sent to: GreyGnome on: Today at 08:10:33 AM »
« You have forwarded or responded to this message. »
Quote Reply Remove
HI,
Yeah, I wrote the original PCint library. It was a bit of a hack and the new one has better features.
I intended the code to be freely usable. Didn't really think about a license. Feel free to use it in
your code: I hereby grant you permission.
I'll investigate the MIT license, and see if it is appropriate.
Chris J. Kiick
Robot builder and all around geek.
Version 1.81 (beta) Tue Jun 19 07:29:08 CDT 2012
Created the getPCIntVersion function, and its associated GET_PCINT_VERSION preprocessor macro. The version
is a 16-bit int, therefore versions are represented as a 4-digit integer. 1810, then, is the first beta
release of 1.81x series. 1811 would be a bugfix of 1.810. 1820 would be the production release.
Reversed the order of this list, so the most recent notes come first.
Made some variables "volatile", because they are changed in the interrupt code. Thanks, Tony Cappellini!
Added support for the Arduino Mega! Thanks to cserveny...@gmail.com!
NOTE: I don't have a Mega, so I rely on you to give me error (or working) reports!
To sum it up for the Mega: No Port C, no Port D. Instead, you get Port J and Port K. Port B remains.
Port J, however, is practically useless because there is only 1 pin available for interrupts.
Most of the Port J pins are not even connected to a header connector. Caveat Programmer.
Created a function to report the version of this code. Put this #define ahead of the #include of this file,
in your sketch:
#define GET_PCINT_VERSION
Then you can call
uint16_t getPCIntVersion ();
and it will return a 16-bit integer representation of the version of this library. That is, version 1.73beta
will be reported as "1730". 1.74, then, will return "1740". And so on, for whatever version of the library
this happens to be. The odd number in the 10's position will indicate a beta version, as per usual, and the
number in the 1s place will indicate the beta revision (bugs may necessitate a 1.731, 1.732, etc.).
Here are some of his notes based on his changes:
Mega and friends are using port B, J and K for interrupts. B is working without any modifications.
J is mostly useless, because of the hardware UART. I was not able to get pin change notifications from
the TX pin (14), so only 15 left. All other (PORT J) pins are not connected on the Arduino boards.
K controls Arduino pin A8-A15, working fine.
328/168 boards use C and D. So in case the lib is compiled with Mega target, the C and D will be
disabled. Also you cannot see port J/K with other targets. For J and K new flags introduced:
NO_PORTJ_PINCHANGES and NO_PORTK_PINCHANGES.
Maybe we should have PORTJ_PINCHANGES to enable PJ, because they will be most likely unused.
Enjoy!
Note: To remain consistent, I have not included PORTJ_PINCHANGES. All ports behave the same,
no matter how trivial those ports may seem... no surprises...
Version 1.72 Wed Mar 14 18:57:55 CDT 2012
Release.
Version 1.71beta Sat Mar 10 12:57:05 CST 2012
Code reordering: Starting in version 1.3 of this library, I put the code that enables
interrupts for the given pin, and the code that enables Pin Change Interrupts, ahead of actually
setting the user's function for the pin. Thus in the small interval between turning on the
interrupts and actually creating a valid link to an interrupt handler, it is possible to get an
interrupt. At that point the value of the pointer is 0, so this means that the Arduino
will start over again from memory location 0- just as if you'd pressed the reset button. Oops!
I corrected it so the code now operates in the proper order.
(EDITORIAL NOTE: If you want to really learn something, teach it!)
Minor code clean-up: All references to PCintPort::curr are now explicit. This changes the compiled
hex code not one whit. I just sleep better at night.
Numbering: Changed the numbering scheme. Beta versions will end with an odd number in the hundredths
place- because they may be odd- and continue to be marked "beta". I'll just sleep better at night. :-)
Version 1.70beta Mon Feb 27 07:20:42 CST 2012
Happy Birthday to me! Happy Birthday tooooo meee! Happy Birthday, Dear Meeeeee-eeeee!
Happy Birthday to me!
Yes, it is on this auspicious occasion of mine (and Elizabeth Taylor's [R.I.P.]) birthday that I
humbly submit to you, gracious Arduino PinChangeInt user, version 1.70beta of the PinChangeInt
library. I hope you enjoy it.
New in this release:
The PinChangeIntTest sketch was created, which can be found in the Examples directory. It exercises:
* Two interrupting pins, one on each of the Arduino's PORTs.
* detachInterrupt() (and subsequent attachInterrupt()s).
Hopefully this will help avoid the embarrassing bugs that I have heretofore missed.
As well, it has come to this author's (GreyGnome) attention that the Serial class in Arduino 1.0
uses an interrupt that, if you attempt to print from an interrupt (which is what I was doing in my
tests) can easily lock up the Arduino. So I have taken SigurðurOrn's excellent ByteBuffer library
and modified it for my own nefarious purposes. (see http://siggiorn.com/?p=460). The zipfile
comes complete with the ByteBuffer library; see the ByteBuffer/ByteBuffer.h file for a list of
changes, and see the PinChangeIntTest sketch for a usage scenario. Now the (interrupt-less and)
relatively fast operation of filling a circular buffer is used in the interrupt routines. The buffer
is then printed from loop().
The library has been modified so it can be used in other libraries, such as my AdaEncoder library
(http://code.google.com/p/adaencoder/). When #include'd by another library you should #define
the LIBCALL_PINCHANGEINT macro. For example:
#ifndef PinChangeInt_h
#define LIBCALL_PINCHANGEINT
#include "../PinChangeInt/PinChangeInt.h"
#endif
This is necessary because the IDE compiles both your sketch and the .cpp file of your library, and
the .h file is included in both places. But since the .h file actually contains the code, any variable
or function definitions would occur twice and cause compilation errors- unless #ifdef'ed out.
Version 1.6beta Fri Feb 10 08:48:35 CST 2012
Set the value of the current register settings, first thing in each ISR; e.g.,
ISR(PCINT0_vect) {
PCintPort::curr = portB.portInputReg; // version 1.6
...
...instead of at the beginning of the PCintPort::PCint() static method. This means that the port is read
closer to the beginning of the interrupt, and may be slightly more accurate- only by a couple of microseconds,
really, but it's a cheap win.
Fixed a bug- a BUG!- in the attachInterrupt() and detachInterrupt() methods. I didn't have breaks in my
switch statements! Augh! What am I, a (UNIX) shell programmer? ...Uh, generally, yes...
Added the PINMODE define and the PCintPort::pinmode variable.
Version 1.51 Sun Feb 5 23:28:02 CST 2012
Crap, a bug! Changed line 392 from this:
PCintPort::pinState=curr & changedPins ? HIGH : LOW;
to this:
PCintPort::pinState=curr & p->mask ? HIGH : LOW;
Also added a few lines of (commented-out) debug code.
Version 1.5 Thu Feb 2 18:09:49 CST 2012
Added the PCintPort::pinState static variable to allow the programmer to query the state of the pin
at the time of interrupt.
Added two new #defines, NO_PIN_STATE and NO_PIN_NUMBER so as to reduce the code size by 20-50 bytes,
and to speed up the interrupt routine slightly by declaring that you don't care if the static variables
PCintPort::pinState and/or PCintPort::arduinoPin are set and made available to your interrupt routine.
// #define NO_PIN_STATE // to indicate that you don't need the pinState
// #define NO_PIN_NUMBER // to indicate that you don't need the arduinoPin
Version 1.4 Tue Jan 10 09:41:14 CST 2012
All the code has been moved into this .h file, so as to allow #define's to work from the user's
sketch. Thanks to Paul Stoffregen from pjrc.com for the inspiration! (Check out his website for
some nice [lots more memory] Arduino-like boards at really good prices. ...This has been an unsolicited
plug. Now back to our regular programming. ...Hehe, "programming", get it?)
As a result, we no longer use the PinChangeIntConfig.h file. The user must #define things in his/her
sketch. Which is better anyway.
Removed the pcIntPorts[] array, which created all the ports by default no matter what. Now, only
those ports (PCintPort objects) that you need will get created if you use the NO_PORTx_PINCHANGES #defines.
This saves flash memory, and actually we get a bit of a memory savings anyway even if all the ports are
left enabled.
The attachInterrupt and detachInterrupt routines were modified to handle the new PCintPort objects.
Version 1.3 Sat Dec 3 22:56:20 CST 2011
Significant internal changes:
Tested and modified to work with Arduino 1.0.
Modified to use the new() operator and symbolic links instead of creating a pre-populated
PCintPins[]. Renamed some variables to simplify or make their meaning more obvious (IMHO anyway).
Modified the PCintPort::PCint() code (ie, the interrupt code) to loop over a linked-list. For
those who love arrays, I have left some code in there that should work to loop over an array
instead. But it is commented out in the release version.
For Arduino versions prior to 1.0: The new() operator requires the cppfix.h library, which is
included with this package. For Arduino 1.0 and above: new.h comes with the distribution, and
that is #included.
Version 1.2 Sat Dec 3 Sat Dec 3 09:15:52 CST 2011
Modified Thu Sep 8 07:33:17 CDT 2011 by GreyGnome. Fixes a bug with the initial port
value. Now it sets the initial value to be the state of the port at the time of
attachInterrupt(). The line is port.PCintLast=port.portInputReg; in attachInterrupt().
See GreyGnome comment, below.
Added the "arduinoPin" variable, so the user's function will know exactly which pin on
the Arduino was triggered.
Version 1.1 Sat Dec 3 00:06:03 CST 2011
...updated to fix the "delPin" function as per "pekka"'s bug report. Thanks!
---- ^^^ VERSIONS ^^^ (NOTE TO SELF: Update the PCINT_VERSION define, below) -------------
See google code project for latest, bugs and info http://code.google.com/p/arduino-pinchangeint/
For more information Refer to avr-gcc header files, arduino source and atmega datasheet.
This library was inspired by and derived from "johnboiles" (it seems)
PCInt Arduino Playground example here: http://www.arduino.cc/playground/Main/PcInt
If you are the original author, please let us know at the google code page
It provides an extension to the interrupt support for arduino by
adding pin change interrupts, giving a way for users to have
interrupts drive off of any pin.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-674
View File
@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
-10
View File
@@ -1,10 +0,0 @@
# LITERAL1 specifies constants
# KEYWORD1 specifies datatypes and C/C++ keywords
pinState KEYWORD1 PinState
arduinoPin KEYWORD1 ArduinoPin
PCintPort KEYWORD1 PCInterruptPort
# KEYWORD2 specifies methods and functions
attachInterrupt KEYWORD2 AttachInterrupt
detachInterrupt KEYWORD2 DetachInterrupt
-10
View File
@@ -1,10 +0,0 @@
*.bak
*.o
.*.swp
*.orig
.swp
docs/
output/
ojam/
version.h
Session.vim
File diff suppressed because it is too large Load Diff
-56
View File
@@ -1,56 +0,0 @@
/**
* @page FAQ Frequently Asked Questions
*
* @ref starting
*
* @ref hardware
*
* @ref range
*
* @ref tests
*
* @section starting Where do I start?
*
* Start with the pingpair example. Follow the instructions in the comments
* of that sketch.
*
* @section hardware Where can I buy some hardware?
*
* I've been using modules from mdfly.com.
*
* Mostly these units as the workhorse of the system:
*
* @li 2.4Ghz Wireless nRF24L01 Transceiver Module $6.95
* http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=82
*
* And then also experimenting with these for greater range
*
* @li nRF24L01 2.4GHz Transceiver Module w/ Power Amplifier $12.95
* http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=394
* @li 2.4GHz Transceiver Module w/ Power Amplifier $19.95
* http://www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=583
*
* However, I also noticed that iTeadStudio has nRF24L01+ units for $4!
*
* @section range What is the range of these units?
*
* Here are some results from measurements I have taken. I recommend that everyone
* take their own measurements in their particular circumstances.
*
* @li non-plus unit, 2MBps (worst case), 41+ ft line of sight indoors, immediate dropoff with any deviation from LOS. (41 ft is as far as I can go in my house without turning a corner)
* @li Plus unit, 250kbps (best case), 46 ft around two corners indoors, 49 ft around one corner. More importantly, at 250k, packet loss is almost negligible through almost all of that range.
* @li Both units at 1MBps, plus unit gets about 10% range improvement over non-plus in almost all situations.
*
* @section tests Why are the examples in the 'tests' directory failing?
*
* The sketches in the 'tests' directory are not for general use.
* Please use the examples in the 'examples' directory instead.
*
* The 'tests' directory is only for people making changes to the library
* to ensure that their changes do not break anything.
*
*
*
*
*
*/
-29
View File
@@ -1,29 +0,0 @@
# Arduino driver for nRF24L01(+) 2.4GHz Wireless Transceiver
Design Goals: This library is designed to be...
* Maximally compliant with the intended operation of the chip
* Easy for beginners to use
* Consumed with a public interface that's similiar to other Arduino standard libraries
* Built against the standard SPI library.
* Modifications to the RF24 library in this fork is backward compatible. A single
enhancement which may cause issue, is code which relies on the driver to power down the
radio, as a side effect. The radio is no longer powered down after each transmit. Rather,
the application must take responsibility for power management. Normally this is
achieved by use of powerDown and powerUp. If you wish to maximize power efficiency,
you must call powerDown after transmit (write, startWrite).
Please refer to:
* [Documentation Main Page](http://maniacbug.github.com/RF24)
* [RF24 Class Documentation](http://maniacbug.github.com/RF24/classRF24.html)
* [Source Code](https://github.com/maniacbug/RF24)
* [Downloads](https://github.com/maniacbug/RF24/archives/master)
* [Chip Datasheet](http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01_Product_Specification_v2_0.pdf)
This chip uses the SPI bus, plus two chip control pins. Remember that pin 10 must still remain an output, or
the SPI hardware will go into 'slave' mode. This is because the 'SS', or slave select, pin on the arduino
controls if the arduino is the slave. For RF24 use, the arduino is the master and the RF24 is the slave.
-65
View File
@@ -1,65 +0,0 @@
/*
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
#ifndef __RF24_CONFIG_H__
#define __RF24_CONFIG_H__
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
#include <stddef.h>
// Stuff that is normally provided by Arduino
#ifdef ARDUINO
#include <SPI.h>
#else
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern HardwareSPI SPI;
#define _BV(x) (1<<(x))
#endif
#undef SERIAL_DEBUG
#ifdef SERIAL_DEBUG
#define IF_SERIAL_DEBUG(x) ({x;})
#else
#define IF_SERIAL_DEBUG(x)
#endif
// Avoid spurious warnings
#if 1
#if ! defined( NATIVE ) && defined( ARDUINO )
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
#endif
#endif
// Progmem is Arduino-specific
#ifdef ARDUINO
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#else
typedef char const char;
typedef uint16_t prog_uint16_t;
#define PSTR(x) (x)
#define printf_P printf
#define strlen_P strlen
#define PROGMEM
#define pgm_read_word(p) (*(p))
#define PRIPSTR "%s"
#endif
#endif // __RF24_CONFIG_H__
// vim:ai:cin:sts=2 sw=2 ft=cpp
-835
View File
@@ -1,835 +0,0 @@
/* The standard CSS for doxygen */
body, table, div, p, dl {
font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
font-size: 12px;
}
/* @group Heading Levels */
h1 {
font-size: 150%;
}
.title {
font-size: 150%;
font-weight: bold;
margin: 10px 2px;
}
h2 {
font-size: 120%;
}
h3 {
font-size: 100%;
}
dt {
font-weight: bold;
}
div.multicol {
-moz-column-gap: 1em;
-webkit-column-gap: 1em;
-moz-column-count: 3;
-webkit-column-count: 3;
}
p.startli, p.startdd, p.starttd {
margin-top: 2px;
}
p.endli {
margin-bottom: 0px;
}
p.enddd {
margin-bottom: 4px;
}
p.endtd {
margin-bottom: 2px;
}
/* @end */
caption {
font-weight: bold;
}
span.legend {
font-size: 70%;
text-align: center;
}
h3.version {
font-size: 90%;
text-align: center;
}
div.qindex, div.navtab{
background-color: #EBEFF6;
border: 1px solid #A3B4D7;
text-align: center;
margin: 2px;
padding: 2px;
}
div.qindex, div.navpath {
width: 100%;
line-height: 140%;
}
div.navtab {
margin-right: 15px;
}
/* @group Link Styling */
a {
color: #3D578C;
font-weight: normal;
text-decoration: none;
}
.contents a:visited {
color: #4665A2;
}
a:hover {
text-decoration: underline;
}
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #9CAFD4;
color: #ffffff;
border: 1px double #869DCA;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.el {
font-weight: bold;
}
a.elRef {
}
a.code {
color: #4665A2;
}
a.codeRef {
color: #4665A2;
}
/* @end */
dl.el {
margin-left: -1cm;
}
.fragment {
font-family: monospace, fixed;
font-size: 105%;
}
pre.fragment {
border: 1px solid #C4CFE5;
background-color: #FBFCFD;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
}
div.ah {
background-color: black;
font-weight: bold;
color: #ffffff;
margin-bottom: 3px;
margin-top: 3px;
padding: 0.2em;
border: solid thin #333;
border-radius: 0.5em;
-webkit-border-radius: .5em;
-moz-border-radius: .5em;
box-shadow: 2px 2px 3px #999;
-webkit-box-shadow: 2px 2px 3px #999;
-moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
font-weight: bold;
}
div.groupText {
margin-left: 16px;
font-style: italic;
}
body {
background: white;
color: black;
margin: 0;
}
div.contents {
margin-top: 10px;
margin-left: 10px;
margin-right: 5px;
}
td.indexkey {
background-color: #EBEFF6;
font-weight: bold;
border: 1px solid #C4CFE5;
margin: 2px 0px 2px 0;
padding: 2px 10px;
}
td.indexvalue {
background-color: #EBEFF6;
border: 1px solid #C4CFE5;
padding: 2px 10px;
margin: 2px 0px;
}
tr.memlist {
background-color: #EEF1F7;
}
p.formulaDsp {
text-align: center;
}
img.formulaDsp {
}
img.formulaInl {
vertical-align: middle;
}
div.center {
text-align: center;
margin-top: 0px;
margin-bottom: 0px;
padding: 0px;
}
div.center img {
border: 0px;
}
address.footer {
text-align: right;
padding-right: 12px;
}
img.footer {
border: 0px;
vertical-align: middle;
}
/* @group Code Colorization */
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
span.vhdldigit {
color: #ff00ff
}
span.vhdlchar {
color: #000000
}
span.vhdlkeyword {
color: #700070
}
span.vhdllogic {
color: #ff0000
}
/* @end */
/*
.search {
color: #003399;
font-weight: bold;
}
form.search {
margin-bottom: 0px;
margin-top: 0px;
}
input.search {
font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #e8eef2;
}
*/
td.tiny {
font-size: 75%;
}
.dirtab {
padding: 4px;
border-collapse: collapse;
border: 1px solid #A3B4D7;
}
th.dirtab {
background: #EBEFF6;
font-weight: bold;
}
hr {
height: 0px;
border: none;
border-top: 1px solid #4A6AAA;
}
hr.footer {
height: 1px;
}
/* @group Member Descriptions */
table.memberdecls {
border-spacing: 0px;
padding: 0px;
}
.mdescLeft, .mdescRight,
.memItemLeft, .memItemRight,
.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
background-color: #F9FAFC;
border: none;
margin: 4px;
padding: 1px 0 0 8px;
}
.mdescLeft, .mdescRight {
padding: 0px 8px 4px 8px;
color: #555;
}
.memItemLeft, .memItemRight, .memTemplParams {
border-top: 1px solid #C4CFE5;
}
.memItemLeft, .memTemplItemLeft {
white-space: nowrap;
}
.memItemRight {
width: 100%;
}
.memTemplParams {
color: #4665A2;
white-space: nowrap;
}
/* @end */
/* @group Member Details */
/* Styles for detailed member documentation */
.memtemplate {
font-size: 80%;
color: #4665A2;
font-weight: normal;
margin-left: 9px;
}
.memnav {
background-color: #EBEFF6;
border: 1px solid #A3B4D7;
text-align: center;
margin: 2px;
margin-right: 15px;
padding: 2px;
}
.mempage {
width: 100%;
}
.memitem {
padding: 0;
margin-bottom: 10px;
margin-right: 5px;
}
.memname {
white-space: nowrap;
font-weight: bold;
margin-left: 6px;
}
.memproto {
border-top: 1px solid #A8B8D9;
border-left: 1px solid #A8B8D9;
border-right: 1px solid #A8B8D9;
padding: 6px 0px 6px 0px;
color: #253555;
font-weight: bold;
text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
/* opera specific markup */
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
border-top-right-radius: 8px;
border-top-left-radius: 8px;
/* firefox specific markup */
-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
-moz-border-radius-topright: 8px;
-moz-border-radius-topleft: 8px;
/* webkit specific markup */
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
-webkit-border-top-right-radius: 8px;
-webkit-border-top-left-radius: 8px;
background-image:url('nav_f.png');
background-repeat:repeat-x;
background-color: #E2E8F2;
}
.memdoc {
border-bottom: 1px solid #A8B8D9;
border-left: 1px solid #A8B8D9;
border-right: 1px solid #A8B8D9;
padding: 2px 5px;
background-color: #FBFCFD;
border-top-width: 0;
/* opera specific markup */
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
/* firefox specific markup */
-moz-border-radius-bottomleft: 8px;
-moz-border-radius-bottomright: 8px;
-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7);
/* webkit specific markup */
-webkit-border-bottom-left-radius: 8px;
-webkit-border-bottom-right-radius: 8px;
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7));
}
.paramkey {
text-align: right;
}
.paramtype {
white-space: nowrap;
}
.paramname {
color: #602020;
white-space: nowrap;
}
.paramname em {
font-style: normal;
}
.params, .retval, .exception, .tparams {
border-spacing: 6px 2px;
}
.params .paramname, .retval .paramname {
font-weight: bold;
vertical-align: top;
}
.params .paramtype {
font-style: italic;
vertical-align: top;
}
.params .paramdir {
font-family: "courier new",courier,monospace;
vertical-align: top;
}
/* @end */
/* @group Directory (tree) */
/* for the tree view */
.ftvtree {
font-family: sans-serif;
margin: 0px;
}
/* these are for tree view when used as main index */
.directory {
font-size: 9pt;
font-weight: bold;
margin: 5px;
}
.directory h3 {
margin: 0px;
margin-top: 1em;
font-size: 11pt;
}
/*
The following two styles can be used to replace the root node title
with an image of your choice. Simply uncomment the next two styles,
specify the name of your image and be sure to set 'height' to the
proper pixel height of your image.
*/
/*
.directory h3.swap {
height: 61px;
background-repeat: no-repeat;
background-image: url("yourimage.gif");
}
.directory h3.swap span {
display: none;
}
*/
.directory > h3 {
margin-top: 0;
}
.directory p {
margin: 0px;
white-space: nowrap;
}
.directory div {
display: none;
margin: 0px;
}
.directory img {
vertical-align: -30%;
}
/* these are for tree view when not used as main index */
.directory-alt {
font-size: 100%;
font-weight: bold;
}
.directory-alt h3 {
margin: 0px;
margin-top: 1em;
font-size: 11pt;
}
.directory-alt > h3 {
margin-top: 0;
}
.directory-alt p {
margin: 0px;
white-space: nowrap;
}
.directory-alt div {
display: none;
margin: 0px;
}
.directory-alt img {
vertical-align: -30%;
}
/* @end */
div.dynheader {
margin-top: 8px;
}
address {
font-style: normal;
color: #2A3D61;
}
table.doxtable {
border-collapse:collapse;
}
table.doxtable td, table.doxtable th {
border: 1px solid #2D4068;
padding: 3px 7px 2px;
}
table.doxtable th {
background-color: #374F7F;
color: #FFFFFF;
font-size: 110%;
padding-bottom: 4px;
padding-top: 5px;
text-align:left;
}
.tabsearch {
top: 0px;
left: 10px;
height: 36px;
background-image: url('tab_b.png');
z-index: 101;
overflow: hidden;
font-size: 13px;
}
.navpath ul
{
font-size: 11px;
background-image:url('tab_b.png');
background-repeat:repeat-x;
height:30px;
line-height:30px;
color:#8AA0CC;
border:solid 1px #C2CDE4;
overflow:hidden;
margin:0px;
padding:0px;
}
.navpath li
{
list-style-type:none;
float:left;
padding-left:10px;
padding-right:15px;
background-image:url('bc_s.png');
background-repeat:no-repeat;
background-position:right;
color:#364D7C;
}
.navpath li.navelem a
{
height:32px;
display:block;
text-decoration: none;
outline: none;
}
.navpath li.navelem a:hover
{
color:#6884BD;
}
.navpath li.footer
{
list-style-type:none;
float:right;
padding-left:10px;
padding-right:15px;
background-image:none;
background-repeat:no-repeat;
background-position:right;
color:#364D7C;
font-size: 8pt;
}
div.summary
{
float: right;
font-size: 8pt;
padding-right: 5px;
width: 50%;
text-align: right;
}
div.summary a
{
white-space: nowrap;
}
div.ingroups
{
font-size: 8pt;
padding-left: 5px;
width: 50%;
text-align: left;
}
div.ingroups a
{
white-space: nowrap;
}
div.header
{
background-image:url('nav_h.png');
background-repeat:repeat-x;
background-color: #F9FAFC;
margin: 0px;
border-bottom: 1px solid #C4CFE5;
}
div.headertitle
{
padding: 5px 5px 5px 10px;
}
dl
{
padding: 0 0 0 10px;
}
dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug
{
border-left:4px solid;
padding: 0 0 0 6px;
}
dl.note
{
border-color: #D0C000;
}
dl.warning, dl.attention
{
border-color: #FF0000;
}
dl.pre, dl.post, dl.invariant
{
border-color: #00D000;
}
dl.deprecated
{
border-color: #505050;
}
dl.todo
{
border-color: #00C0E0;
}
dl.test
{
border-color: #3030E0;
}
dl.bug
{
border-color: #C08050;
}
#projectlogo
{
text-align: center;
vertical-align: bottom;
border-collapse: separate;
}
#projectlogo img
{
border: 0px none;
}
#projectname
{
font: 300% Tahoma, Arial,sans-serif;
margin: 0px;
padding: 2px 0px;
}
#projectbrief
{
font: 120% Tahoma, Arial,sans-serif;
margin: 0px;
padding: 0px;
}
#projectnumber
{
font: 50% Tahoma, Arial,sans-serif;
margin: 0px;
padding: 0px;
}
#titlearea
{
padding: 0px;
margin: 0px;
width: 100%;
border-bottom: 1px solid #5373B4;
}
.image
{
text-align: left;
}
.dotgraph
{
text-align: center;
}
.mscgraph
{
text-align: center;
}
.caption
{
font-weight: bold;
}
@@ -1,227 +0,0 @@
/*
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
/**
* Example for Getting Started with nRF24L01+ radios.
*
* This is an example of how to use the RF24 class. Write this sketch to two
* different nodes. Put one of the nodes into 'transmit' mode by connecting
* with the serial monitor and sending a 'T'. The ping node sends the current
* time to the pong node, which responds by sending the value back. The ping
* node can then see how long the whole cycle took.
*/
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"
//
// Hardware configuration
//
// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
RF24 radio(9,10);
//
// Topology
//
// Radio pipe addresses for the 2 nodes to communicate.
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };
//
// Role management
//
// Set up role. This sketch uses the same software for all the nodes
// in this system. Doing so greatly simplifies testing.
//
// The various roles supported by this sketch
typedef enum { role_ping_out = 1, role_pong_back } role_e;
// The debug-friendly names of those roles
const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};
// The role of the current running sketch
role_e role = role_pong_back;
void setup(void)
{
//
// Print preamble
//
Serial.begin(57600);
printf_begin();
printf("\n\rRF24/examples/GettingStarted/\n\r");
printf("ROLE: %s\n\r",role_friendly_name[role]);
printf("*** PRESS 'T' to begin transmitting to the other node\n\r");
//
// Setup and configure rf radio
//
radio.begin();
// optionally, increase the delay between retries & # of retries
radio.setRetries(15,15);
// optionally, reduce the payload size. seems to
// improve reliability
//radio.setPayloadSize(8);
//
// Open pipes to other nodes for communication
//
// This simple sketch opens two pipes for these two nodes to communicate
// back and forth.
// Open 'our' pipe for writing
// Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading)
//if ( role == role_ping_out )
{
//radio.openWritingPipe(pipes[0]);
radio.openReadingPipe(1,pipes[1]);
}
//else
{
//radio.openWritingPipe(pipes[1]);
//radio.openReadingPipe(1,pipes[0]);
}
//
// Start listening
//
radio.startListening();
//
// Dump the configuration of the rf unit for debugging
//
radio.printDetails();
}
void loop(void)
{
//
// Ping out role. Repeatedly send the current time
//
if (role == role_ping_out)
{
// First, stop listening so we can talk.
radio.stopListening();
// Take the time, and send it. This will block until complete
unsigned long time = millis();
printf("Now sending %lu...",time);
bool ok = radio.write( &time, sizeof(unsigned long) );
if (ok)
printf("ok...");
else
printf("failed.\n\r");
// Now, continue listening
radio.startListening();
// Wait here until we get a response, or timeout (250ms)
unsigned long started_waiting_at = millis();
bool timeout = false;
while ( ! radio.available() && ! timeout )
if (millis() - started_waiting_at > 200 )
timeout = true;
// Describe the results
if ( timeout )
{
printf("Failed, response timed out.\n\r");
}
else
{
// Grab the response, compare, and send to debugging spew
unsigned long got_time;
radio.read( &got_time, sizeof(unsigned long) );
// Spew it
printf("Got response %lu, round-trip delay: %lu\n\r",got_time,millis()-got_time);
}
// Try again 1s later
delay(1000);
}
//
// Pong back role. Receive each packet, dump it out, and send it back
//
if ( role == role_pong_back )
{
// if there is data ready
if ( radio.available() )
{
// Dump the payloads until we've gotten everything
unsigned long got_time;
bool done = false;
while (!done)
{
// Fetch the payload, and see if this was the last one.
done = radio.read( &got_time, sizeof(unsigned long) );
// Spew it
printf("Got payload %lu...",got_time);
// Delay just a little bit to let the other unit
// make the transition to receiver
delay(20);
}
// First, stop listening so we can talk
radio.stopListening();
// Send the final one back.
radio.write( &got_time, sizeof(unsigned long) );
printf("Sent response.\n\r");
// Now, resume listening so we catch the next packets.
radio.startListening();
}
}
//
// Change roles
//
if ( Serial.available() )
{
char c = toupper(Serial.read());
if ( c == 'T' && role == role_pong_back )
{
printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r");
// Become the primary transmitter (ping out)
role = role_ping_out;
radio.openWritingPipe(pipes[0]);
radio.openReadingPipe(1,pipes[1]);
}
else if ( c == 'R' && role == role_ping_out )
{
printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r");
// Become the primary receiver (pong back)
role = role_pong_back;
radio.openWritingPipe(pipes[1]);
radio.openReadingPipe(1,pipes[0]);
}
}
}
// vim:cin:ai:sts=2 sw=2 ft=cpp
@@ -1,210 +0,0 @@
# (1) Project Information
PROJECT_LIBS = SPI RF24 ;
# (2) Board Information
UPLOAD_PROTOCOL ?= arduino ;
UPLOAD_SPEED ?= 57600 ;
MCU ?= atmega328p ;
F_CPU ?= 16000000 ;
CORE ?= arduino ;
VARIANT ?= standard ;
ARDUINO_VERSION ?= 100 ;
# (3) USB Ports
PORTS = p4 p6 p9 u0 u1 u2 ;
PORT_p6 = /dev/tty.usbserial-A600eHIs ;
PORT_p4 = /dev/tty.usbserial-A40081RP ;
PORT_p9 = /dev/tty.usbserial-A9007LmI ;
PORT_u0 = /dev/ttyUSB0 ;
PORT_u1 = /dev/ttyUSB1 ;
PORT_u2 = /dev/ttyUSB2 ;
# (4) Location of AVR tools
#
# This configuration assumes using avr-tools that were obtained separate from the Arduino
# distribution.
if $(OS) = MACOSX
{
AVR_BIN = /usr/local/avrtools/bin ;
AVR_ETC = /usr/local/avrtools/etc ;
AVR_INCLUDE = /usr/local/avrtools/include ;
}
else
{
AVR_BIN ?= /usr/bin ;
AVR_INCLUDE ?= /usr/lib/avr/include ;
AVR_ETC = /etc ;
}
# (5) Directories where Arduino core and libraries are located
ARDUINO_DIR ?= /opt/Arduino ;
ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ;
ARDUINO_LIB = $(ARDUINO_DIR)/libraries ;
SKETCH_LIB = $(HOME)/Source/Arduino/libraries ;
#
# --------------------------------------------------
# Below this line usually never needs to be modified
#
# Tool locations
CC = $(AVR_BIN)/avr-gcc ;
C++ = $(AVR_BIN)/avr-g++ ;
LINK = $(AVR_BIN)/avr-gcc ;
OBJCOPY = $(AVR_BIN)/avr-objcopy ;
AVRDUDE = $(AVR_BIN)/avrdude ;
# Flags
DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ;
OPTIM = -Os ;
CCFLAGS = -Wall -Wextra -mmcu=$(MCU) -ffunction-sections -fdata-sections ;
C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ;
LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ;
AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ;
# Search everywhere for headers
HDRS = $(PWD) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ;
# Output locations
LOCATE_TARGET = $(F_CPU) ;
LOCATE_SOURCE = $(F_CPU) ;
#
# Custom rules
#
rule GitVersion
{
Always $(<) ;
Depends all : $(<) ;
}
actions GitVersion
{
echo "const char program_version[] = \"\\" > $(<)
git log -1 --pretty=format:%h >> $(<)
echo "\";" >> $(<)
}
GitVersion version.h ;
rule Pde
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
if ( $(ARDUINO_VERSION) < 100 )
{
ARDUINO_H = WProgram.h ;
}
else
{
ARDUINO_H = Arduino.h ;
}
actions Pde
{
echo "#include <$(ARDUINO_H)>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule C++Pde
{
local _CPP = $(>:B).cpp ;
Pde $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule UserObject
{
switch $(>:S)
{
case .ino : C++Pde $(<) : $(>) ;
case .pde : C++Pde $(<) : $(>) ;
}
}
rule Objects
{
local _i ;
for _i in [ FGristFiles $(<) ]
{
local _b = $(_i:B)$(SUFOBJ) ;
local _o = $(_b:G=$(SOURCE_GRIST:E)) ;
Object $(_o) : $(_i) ;
Depends obj : $(_o) ;
}
}
rule Main
{
MainFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Hex
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions Hex
{
$(OBJCOPY) -O ihex -R .eeprom $(>) $(<)
}
rule Upload
{
Depends $(1) : $(2) ;
Depends $(2) : $(3) ;
NotFile $(1) ;
Always $(1) ;
Always $(2) ;
UploadAction $(2) : $(3) ;
}
actions UploadAction
{
$(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i
}
#
# Targets
#
# Grab everything from the core directory
CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ;
# Grab everything from libraries. To avoid this "grab everything" behaviour, you
# can specify specific modules to pick up in PROJECT_MODULES
LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ;
# Grab everything from the current dir
PROJECT_MODULES += [ GLOB $(PWD) : *.c *.cpp *.pde *.ino ] ;
# Main output executable
MAIN = $(PWD:B).elf ;
Main $(MAIN) : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ;
Hex $(MAIN:B).hex : $(MAIN) ;
# Upload targets
for _p in $(PORTS)
{
Upload $(_p) : $(PORT_$(_p)) : $(MAIN:B).hex ;
}
@@ -1,37 +0,0 @@
/*
Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
#ifdef ARDUINO
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#else
#error This example is only for use on Arduino.
#endif // ARDUINO
#endif // __PRINTF_H__
-206
View File
@@ -1,206 +0,0 @@
PROJECT_NAME = $(PWD:B) ;
PROJECT_DIR = . ;
PROJECT_LIBS = SPI RF24 ;
OUT_DIR = ojam ;
F_CPU = 16000000 ;
MCU = atmega328p ;
PORTS = /dev/tty.usbserial-A600eHIs /dev/tty.usbserial-A40081RP /dev/tty.usbserial-A9007LmI ;
UPLOAD_RATE = 57600 ;
AVRDUDE_PROTOCOL = stk500v1 ;
COM = 33 ;
# Host-specific overrides for locations
if $(OS) = MACOSX
{
ARDUINO_VERSION = 22 ;
OLD_DIR = /opt/arduino-0021 ;
AVR_TOOLS_PATH = $(OLD_DIR)/hardware/tools/avr/bin ;
AVRDUDECONFIG_PATH = $(OLD_DIR)/hardware/tools/avr/etc ;
ARDUINO_DIR = /opt/Arduino ;
ARDUINO_AVR = /usr/lib/avr/include ;
}
# Where is everything?
ARDUINO_VERSION ?= 22 ;
AVR_TOOLS_PATH ?= /usr/bin ;
ARDUINO_DIR ?= /opt/arduino-00$(ARDUINO_VERSION) ;
ARDUINO_AVR ?= $(ARDUINO_DIR)/hardware/tools/avr/avr/include/avr ;
AVRDUDECONFIG_PATH ?= $(ARDUINO_DIR)/hardware/tools ;
ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/arduino ;
ARDUINO_LIB = $(ARDUINO_DIR)/libraries ;
SKETCH_LIB = $(HOME)/Source/Arduino/libraries ;
AVR_CC = $(AVR_TOOLS_PATH)/avr-gcc ;
AVR_CXX = $(AVR_TOOLS_PATH)/avr-g++ ;
AVR_LD = $(AVR_TOOLS_PATH)/avr-gcc ;
AVR_OBJCOPY = $(AVR_TOOLS_PATH)/avr-objcopy ;
AVRDUDE = $(AVR_TOOLS_PATH)/avrdude ;
DEFINES = F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ;
CTUNING = -ffunction-sections -fdata-sections ;
CXXTUNING = -fno-exceptions -fno-strict-aliasing ;
CFLAGS = -Os -Wall -Wextra -mmcu=$(MCU) $(CTUNING) ;
CXXFLAGS = $(CFLAGS) $(CXXTUNING) ;
LDFLAGS = -Os -lm -Wl,--gc-sections -mmcu=atmega328p ;
# Search everywhere for headers
HDRS = $(PROJECT_DIR) $(ARDUINO_AVR) $(ARDUINO_CORE) [ GLOB $(ARDUINO_LIB) $(SKETCH_LIB) : [^.]* ] ;
# Grab everything from the core directory
CORE_MODULES = [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ;
# Grab everything from libraries. To avoid this "grab everything" behaviour, you
# can specify specific modules to pick up in PROJECT_MODULES
LIB_MODULES = [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp ] ;
# In addition to explicitly-specified program modules, pick up anything from the current
# dir.
PROJECT_MODULES += [ GLOB $(PROJECT_DIR) : *.c *.cpp *.pde ] ;
# Shortcut for the out files
OUT = $(OUT_DIR)/$(PROJECT_NAME) ;
# AvrDude setup
AVRDUDE_FLAGS = -V -F -D -C $(AVRDUDECONFIG_PATH)/avrdude.conf -p $(MCU) -c $(AVRDUDE_PROTOCOL) -b $(UPLOAD_RATE) ;
rule GitVersion
{
Always $(<) ;
Depends all : $(<) ;
}
actions GitVersion
{
echo "const char program_version[] = \"\\" > $(<)
git log -1 --pretty=format:%h >> $(<)
echo "\";" >> $(<)
}
GitVersion version.h ;
rule AvrCc
{
Depends $(<) : $(>) ;
Depends $(<) : $(<:D) ;
Clean clean : $(<) ;
CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;
CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;
}
actions AvrCc
{
$(AVR_CC) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CFLAGS) $(>)
}
rule AvrC++
{
Depends $(<) : $(>) ;
Depends $(<) : $(<:D) ;
Clean clean : $(<) ;
CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] ;
CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;
}
actions AvrC++
{
$(AVR_CXX) -c -o $(<) $(CCHDRS) $(CCDEFS) $(CXXFLAGS) $(>)
}
rule Pde
{
Depends $(<) : $(>) ;
Depends $(<) : $(<:D) ;
Clean clean : $(<) ;
}
actions Pde
{
echo "#include <WProgram.h>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule AvrPde
{
local _CPP = $(OUT_DIR)/$(_I:B).cpp ;
Pde $(_CPP) : $(>) ;
AvrC++ $(<) : $(_CPP) ;
}
rule AvrObject
{
switch $(>:S)
{
case .c : AvrCc $(<) : $(>) ;
case .cpp : AvrC++ $(<) : $(>) ;
case .pde : AvrPde $(<) : $(>) ;
}
}
rule AvrObjects
{
for _I in $(<)
{
AvrObject $(OUT_DIR)/$(_I:B).o : $(_I) ;
}
}
rule AvrMainFromObjects
{
Depends $(<) : $(>) ;
Depends $(<) : $(<:D) ;
MkDir $(<:D) ;
Depends all : $(<) ;
Clean clean : $(<) ;
}
actions AvrMainFromObjects
{
$(AVR_LD) $(LDFLAGS) -o $(<) $(>)
}
rule AvrMain
{
AvrMainFromObjects $(<) : $(OUT_DIR)/$(>:B).o ;
AvrObjects $(>) ;
}
rule AvrHex
{
Depends $(<) : $(>) ;
Depends $(<) : $(<:D) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions AvrHex
{
$(AVR_OBJCOPY) -O ihex -R .eeprom $(>) $(<)
}
rule AvrUpload
{
Depends $(1) : $(2) ;
Depends $(2) : $(3) ;
NotFile $(1) ;
Always $(1) ;
Always $(2) ;
AvrUploadAction $(2) : $(3) ;
}
actions AvrUploadAction
{
$(AVRDUDE) $(AVRDUDE_FLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i
}
AvrMain $(OUT).elf : $(CORE_MODULES) $(LIB_MODULES) $(PROJECT_MODULES) ;
AvrHex $(OUT).hex : $(OUT).elf ;
AvrUpload p6 : /dev/tty.usbserial-A600eHIs : $(OUT).hex ;
AvrUpload p4 : /dev/tty.usbserial-A40081RP : $(OUT).hex ;
AvrUpload p9 : /dev/tty.usbserial-A9007LmI : $(OUT).hex ;
@@ -1,254 +0,0 @@
/*
Copyright (C) 2011 James Coliz, Jr. <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
/**
* Example LED Remote
*
* This is an example of how to use the RF24 class to control a remote
* bank of LED's using buttons on a remote control.
*
* On the 'remote', connect any number of buttons or switches from
* an arduino pin to ground. Update 'button_pins' to reflect the
* pins used.
*
* On the 'led' board, connect the same number of LED's from an
* arduino pin to a resistor to ground. Update 'led_pins' to reflect
* the pins used. Also connect a separate pin to ground and change
* the 'role_pin'. This tells the sketch it's running on the LED board.
*
* Every time the buttons change on the remote, the entire state of
* buttons is send to the led board, which displays the state.
*/
#include <SPI.h>
#include "RF24.h"
#include "printf.h"
//
// Hardware configuration
//
// Set up nRF24L01 radio on SPI bus plus pins 8 & 9
RF24 radio(8,9);
// sets the role of this unit in hardware. Connect to GND to be the 'led' board receiver
// Leave open to be the 'remote' transmitter
const int role_pin = A4;
// Pins on the remote for buttons
const uint8_t button_pins[] = { 2,3,4,5,6,7 };
const uint8_t num_button_pins = sizeof(button_pins);
// Pins on the LED board for LED's
const uint8_t led_pins[] = { 2,3,4,5,6,7 };
const uint8_t num_led_pins = sizeof(led_pins);
//
// Topology
//
// Single radio pipe address for the 2 nodes to communicate.
const uint64_t pipe = 0xE8E8F0F0E1LL;
//
// Role management
//
// Set up role. This sketch uses the same software for all the nodes in this
// system. Doing so greatly simplifies testing. The hardware itself specifies
// which node it is.
//
// This is done through the role_pin
//
// The various roles supported by this sketch
typedef enum { role_remote = 1, role_led } role_e;
// The debug-friendly names of those roles
const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"};
// The role of the current running sketch
role_e role;
//
// Payload
//
uint8_t button_states[num_button_pins];
uint8_t led_states[num_led_pins];
//
// Setup
//
void setup(void)
{
//
// Role
//
// set up the role pin
pinMode(role_pin, INPUT);
digitalWrite(role_pin,HIGH);
delay(20); // Just to get a solid reading on the role pin
// read the address pin, establish our role
if ( digitalRead(role_pin) )
role = role_remote;
else
role = role_led;
//
// Print preamble
//
Serial.begin(57600);
printf_begin();
printf("\n\rRF24/examples/led_remote/\n\r");
printf("ROLE: %s\n\r",role_friendly_name[role]);
//
// Setup and configure rf radio
//
radio.begin();
//
// Open pipes to other nodes for communication
//
// This simple sketch opens a single pipes for these two nodes to communicate
// back and forth. One listens on it, the other talks to it.
if ( role == role_remote )
{
radio.openWritingPipe(pipe);
}
else
{
radio.openReadingPipe(1,pipe);
}
//
// Start listening
//
if ( role == role_led )
radio.startListening();
//
// Dump the configuration of the rf unit for debugging
//
radio.printDetails();
//
// Set up buttons / LED's
//
// Set pull-up resistors for all buttons
if ( role == role_remote )
{
int i = num_button_pins;
while(i--)
{
pinMode(button_pins[i],INPUT);
digitalWrite(button_pins[i],HIGH);
}
}
// Turn LED's ON until we start getting keys
if ( role == role_led )
{
int i = num_led_pins;
while(i--)
{
pinMode(button_pins[i],OUTPUT);
led_states[i] = HIGH;
digitalWrite(led_pins[i],led_states[i]);
}
}
}
//
// Loop
//
void loop(void)
{
//
// Remote role. If the state of any button has changed, send the whole state of
// all buttons.
//
if ( role == role_remote )
{
// Get the current state of buttons, and
// Test if the current state is different from the last state we sent
int i = num_button_pins;
bool different = false;
while(i--)
{
uint8_t state = ! digitalRead(button_pins[i]);
if ( state != button_states[i] )
{
different = true;
button_states[i] = state;
}
}
// Send the state of the buttons to the LED board
if ( different )
{
printf("Now sending...");
bool ok = radio.write( button_states, num_button_pins );
if (ok)
printf("ok\n\r");
else
printf("failed\n\r");
}
// Try again in a short while
delay(20);
}
//
// LED role. Receive the state of all buttons, and reflect that in the LEDs
//
if ( role == role_led )
{
// if there is data ready
if ( radio.available() )
{
// Dump the payloads until we've gotten everything
bool done = false;
while (!done)
{
// Fetch the payload, and see if this was the last one.
done = radio.read( button_states, num_button_pins );
// Spew it
printf("Got buttons\n\r");
// For each button, if the button now on, then toggle the LED
int i = num_led_pins;
while(i--)
{
if ( button_states[i] )
{
led_states[i] ^= HIGH;
digitalWrite(led_pins[i],led_states[i]);
}
}
}
}
}
}
// vim:ai:cin:sts=2 sw=2 ft=cpp
@@ -1,33 +0,0 @@
/*
Copyright (C) 2011 James Coliz, Jr. <maniacbug@ymail.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
*/
/**
* @file printf.h
*
* Setup necessary to direct stdout to the Arduino Serial library, which
* enables 'printf'
*/
#ifndef __PRINTF_H__
#define __PRINTF_H__
#include "WProgram.h"
int serial_putc( char c, FILE * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
#endif // __PRINTF_H__
-219
View File
@@ -1,219 +0,0 @@
# (1) Project Information
PROJECT_LIBS = RF24 SPI ;
PROJECT_DIRS = $(PWD) ;
# (2) Board Information
UPLOAD_PROTOCOL ?= stk500v1 ;
UPLOAD_SPEED ?= 115200 ;
MCU ?= atmega328p ;
F_CPU ?= 16000000 ;
CORE ?= arduino ;
VARIANT ?= standard ;
ARDUINO_VERSION ?= 100 ;
# (3) USB Ports
PORTS = p4 p6 p9 u0 u1 u2 ;
PORT_p6 = /dev/tty.usbserial-A600eHIs ;
PORT_p4 = /dev/tty.usbserial-A40081RP ;
PORT_p9 = /dev/tty.usbserial-A9007LmI ;
PORT_u0 = /dev/ttyUSB0 ;
PORT_u1 = /dev/ttyUSB1 ;
PORT_u2 = /dev/ttyUSB2 ;
# (4) Location of AVR tools
#
# This configuration assumes using avr-tools that were obtained separate from the Arduino
# distribution.
if $(OS) = MACOSX
{
AVR_BIN = /usr/local/avrtools/bin ;
AVR_ETC = /usr/local/avrtools/etc ;
AVR_INCLUDE = /usr/local/avrtools/include ;
}
else
{
AVR_BIN = /usr/bin ;
AVR_INCLUDE = /usr/lib/avr/include ;
AVR_ETC = /etc ;
}
# (5) Directories where Arduino core and libraries are located
ARDUINO_DIR ?= /opt/Arduino ;
ARDUINO_CORE = $(ARDUINO_DIR)/hardware/arduino/cores/$(CORE) $(ARDUINO_DIR)/hardware/arduino/variants/$(VARIANT) ;
ARDUINO_LIB = $(ARDUINO_DIR)/libraries ;
SKETCH_LIB = $(HOME)/Source/Arduino/libraries ;
#
# --------------------------------------------------
# Below this line usually never needs to be modified
#
# Tool locations
CC = $(AVR_BIN)/avr-gcc ;
C++ = $(AVR_BIN)/avr-g++ ;
LINK = $(AVR_BIN)/avr-gcc ;
AR = $(AVR_BIN)/avr-ar rcs ;
RANLIB = ;
OBJCOPY = $(AVR_BIN)/avr-objcopy ;
AVRDUDE = $(AVR_BIN)/avrdude ;
# Flags
DEFINES += F_CPU=$(F_CPU)L ARDUINO=$(ARDUINO_VERSION) VERSION_H ;
OPTIM = -Os ;
CCFLAGS = -Wall -Wextra -Wno-strict-aliasing -mmcu=$(MCU) -ffunction-sections -fdata-sections ;
C++FLAGS = $(CCFLAGS) -fno-exceptions -fno-strict-aliasing ;
LINKFLAGS = $(OPTIM) -lm -Wl,--gc-sections -mmcu=$(MCU) ;
AVRDUDEFLAGS = -V -F -D -C $(AVR_ETC)/avrdude.conf -p $(MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) ;
# Search everywhere for headers
HDRS = $(PROJECT_DIRS) $(AVR_INCLUDE) $(ARDUINO_CORE) $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) ;
# Output locations
LOCATE_TARGET = $(F_CPU) ;
LOCATE_SOURCE = $(F_CPU) ;
#
# Custom rules
#
rule GitVersion
{
Always $(<) ;
Depends all : $(<) ;
}
actions GitVersion
{
echo "const char program_version[] = \"\\" > $(<)
git log -1 --pretty=format:%h >> $(<)
echo "\";" >> $(<)
}
GitVersion version.h ;
rule Pde
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_SOURCE) ;
Clean clean : $(<) ;
}
if ( $(ARDUINO_VERSION) < 100 )
{
ARDUINO_H = WProgram.h ;
}
else
{
ARDUINO_H = Arduino.h ;
}
actions Pde
{
echo "#include <$(ARDUINO_H)>" > $(<)
echo "#line 1 \"$(>)\"" >> $(<)
cat $(>) >> $(<)
}
rule C++Pde
{
local _CPP = $(>:B).cpp ;
Pde $(_CPP) : $(>) ;
C++ $(<) : $(_CPP) ;
}
rule UserObject
{
switch $(>:S)
{
case .ino : C++Pde $(<) : $(>) ;
case .pde : C++Pde $(<) : $(>) ;
}
}
rule Objects
{
local _i ;
for _i in [ FGristFiles $(<) ]
{
local _b = $(_i:B)$(SUFOBJ) ;
local _o = $(_b:G=$(SOURCE_GRIST:E)) ;
Object $(_o) : $(_i) ;
Depends obj : $(_o) ;
}
}
rule Library
{
LibraryFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Main
{
MainFromObjects $(<) : $(>:B)$(SUFOBJ) ;
Objects $(>) ;
}
rule Hex
{
Depends $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Depends hex : $(<) ;
Clean clean : $(<) ;
}
actions Hex
{
$(OBJCOPY) -O ihex -R .eeprom $(>) $(<)
}
rule Upload
{
Depends $(1) : $(2) ;
Depends $(2) : $(3) ;
NotFile $(1) ;
Always $(1) ;
Always $(2) ;
UploadAction $(2) : $(3) ;
}
actions UploadAction
{
$(AVRDUDE) $(AVRDUDEFLAGS) -P $(<) $(AVRDUDE_WRITE_FLASH) -U flash:w:$(>):i
}
rule Arduino
{
LINKFLAGS on $(<) = $(LINKFLAGS) -Wl,-Map=$(LOCATE_TARGET)/$(<:B).map ;
Main $(<) : $(>) ;
LinkLibraries $(<) : libs core ;
Hex $(<:B).hex : $(<) ;
for _p in $(PORTS)
{
Upload $(_p) : $(PORT_$(_p)) : $(<:B).hex ;
}
}
#
# Targets
#
# Grab everything from the core directory
Library core : [ GLOB $(ARDUINO_CORE) : *.c *.cpp ] ;
# Grab everything from libraries. To avoid this "grab everything" behaviour, you
# can specify specific modules to pick up in PROJECT_MODULES
Library libs : [ GLOB $(ARDUINO_LIB)/$(PROJECT_LIBS) $(ARDUINO_LIB)/$(PROJECT_LIBS)/utility $(SKETCH_LIB)/$(PROJECT_LIBS) : *.cpp *.c ] ;
# Main output executable
Arduino $(PWD:B).elf : $(PROJECT_MODULES) [ GLOB $(PROJECT_DIRS) : *.c *.cpp *.pde *.ino ] ;

Some files were not shown because too many files have changed in this diff Show More