diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0ee52d --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +Examples/LLAPSensor/LLAPSensor.sln +Examples/LLAPSensor/LLAPSensor.suo +Examples/LLAPSensor/LLAPSensor.vcxproj +Examples/LLAPSensor/LLAPSensor.vcxproj.filters +Examples/LLAPSensor/LLAPSensor.vcxproj.user +Examples/LLAPSensor/Visual Micro/.LLAPSensor.vsarduino.h +Examples/LLAPSensor/Visual Micro/Compile.vmps.xml +Examples/LLAPSensor/Visual Micro/Configuration.Debug.vmps.xml +Examples/LLAPSensor/Visual Micro/Upload.vmps.xml +Examples/LLAP_DHT22/LLAP_DHT22.sln +Examples/LLAP_DHT22/LLAP_DHT22.suo +Examples/LLAP_DHT22/LLAP_DHT22.vcxproj +Examples/LLAP_DHT22/LLAP_DHT22.vcxproj.filters +Examples/LLAP_DHT22/LLAP_DHT22.vcxproj.user +Examples/LLAP_DHT22/Visual Micro/.LLAP_DHT22.vsarduino.h +Examples/LLAP_DHT22/Visual Micro/Compile.vmps.xml +Examples/LLAP_DHT22/Visual Micro/Configuration.Debug.vmps.xml +Examples/LLAP_DHT22/Visual Micro/Upload.vmps.xml diff --git a/Examples/LLAPSensor/LLAPSensor.ino b/Examples/LLAPSensor/LLAPSensor.ino new file mode 100644 index 0000000..6a5103d --- /dev/null +++ b/Examples/LLAPSensor/LLAPSensor.ino @@ -0,0 +1,22 @@ + +#include "LLAPSerial.h" // include the library + +void setup() { + // initialise serial: + Serial.begin(9600); + // Initialise the LLAPSerial library + LLAP.init(); +} + +void loop() { + // print the string when a newline arrives: + if (LLAP.bMsgReceived) { + Serial.print("message is:"); + Serial.println(LLAP.sMessage); + LLAP.bMsgReceived = false; // if we do not clear the message flag then message processing will be blocked + } +} + + + + diff --git a/Examples/LLAP_DHT22/LLAP_DHT22.ino b/Examples/LLAP_DHT22/LLAP_DHT22.ino new file mode 100644 index 0000000..a6a87d5 --- /dev/null +++ b/Examples/LLAP_DHT22/LLAP_DHT22.ino @@ -0,0 +1,73 @@ +////////////////////////////////////////////////////////////////////////// +// LLAP temperature and humidity sensor using a DHT22 +// +// Reading temperature or humidity takes about 250 milliseconds! +// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) +// +// will work with any Arduino compatible however the target boards are the +// Ciseco XinoRF and RFu-328, for LLAP over radio +// +// +// Uses the Ciseco LLAPSerial library +// Uses the Adafruit DHT library https://github.com/adafruit/DHT-sensor-library +////////////////////////////////////////////////////////////////////////// + +#include +#include + +#define DEVICEID "DH" // this is the LLAP device ID + +#define DHTPIN 2 // what I/O the DHT-22 data pin is connected to +#define DHTTYPE DHT22 // DHT 22 (AM2302) + +// Connect pin 1 (on the left) of the sensor to +5V +// Connect pin 2 of the sensor to whatever your DHTPIN is +// Connect pin 4 (on the right) of the sensor to GROUND +// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor + +DHT dht(DHTPIN, DHTTYPE); + +void setup() { + Serial.begin(115200); + + pinMode(8,OUTPUT); // switch on the radio + digitalWrite(8,HIGH); + delay(1000); // allow the radio to startup + + LLAP.init(DEVICEID); + + dht.begin(); + + LLAP.sendMessage("STARTED"); + //tst code + Serial.print("ABCDEFGHIJKLMNOPQRSTUVWX"); + Serial.flush(); +} + +void loop() { + // print the string when a newline arrives: + if (LLAP.bMsgReceived) { + Serial.print("message is:"); + Serial.println(LLAP.sMessage); + LLAP.bMsgReceived = false; // if we do not clear the message flag then message processing will be blocked + } + + // every 10 seconds + static unsigned long lastTime = millis(); + if (millis() - lastTime >= 30000) + { + lastTime = millis(); + int h = dht.readHumidity() * 10; + int t = dht.readTemperature() * 10; + + // check if returns are valid, if they are NaN (not a number) then something went wrong! + if (isnan(t) || isnan(h)) { + LLAP.sendMessage("ERROR"); + } else { + LLAP.sendIntWithDP("HUM",h,1); + //delay(100); + LLAP.sendIntWithDP("TMP",t,1); + } + //Serial.println(); + } +} diff --git a/LLAPSerial.cpp b/LLAPSerial.cpp new file mode 100644 index 0000000..7080578 --- /dev/null +++ b/LLAPSerial.cpp @@ -0,0 +1,269 @@ +// +// +// + +#include "LLAPSerial.h" + +/* +const CTable __code Commands[] = { + {9,{'A','C','K','-','-','-','-','-','-'},cmdAck}, // must be the first entry + {0,{'A','P','V','E','R','-','-','-','-'},cmdPVer}, // Protocol version + {0,{'D','E','V','T','Y','P','E','-','-'},cmdDevType}, // Device Type + {0,{'D','E','V','N','A','M','E','-','-'},cmdDevName}, // Device Name + {0,{'H','E','L','L','O','-','-','-','-'},cmdHello}, // Echo + {3,{'S','E','R','#','#','#','#','#','#'},cmdSer}, // Serial Number + {0,{'$','E','R','-','-','-','-','-','-'},cmdSerReset}, // Serial Number + {0,{'F','V','E','R','-','-','-','-','-'},cmdFVer}, // Software revision + {7,{'C','H','D','E','V','I','D','#','#'},cmdDevID}, // Device ID + {5,{'P','A','N','I','D','#','#','#','#'},cmdPanID}, // PANID + {0,{'R','E','B','O','O','T','-','-','-'},cmdReset}, // reset + {7,{'R','E','T','R','I','E','S','#','#'},cmdRetries}, // set # retrys + {4,{'B','A','T','T','-','-','-','-','-'},cmdBatt}, // request battery voltage + {4,{'S','A','V','E','-','-','-','-','-'},cmdSave}, // Save config to flash +#if defined(APSLEEP) // cyclic sleep + {0,{'I','N','T','V','L','#','#','#','#'},cmdInterval}, // SET cyclic sleep interval - 999S - three digits + timescale + // T=mS, S=S, M=mins, H=Hours, D=days + {0,{'C','Y','C','L','E','-','-','-','-'},cmdCyclic}, // activate cyclic sleep + {0,{'W','A','K','E','-','-','-','-','-'},cmdDeactivate}, // deactivate programmed behaviour (cyclic sleep etc) + {5,{'W','A','K','E','C','#','#','#','-'},cmdSetSleepCount}, // set the sleep count until awake is sent +#endif +// allow all device to do a one shot sleep (including DALLAS) + {0,{'S','L','E','E','P','#','#','#','#'},cmdActivate}, // activate Sleeping mode - one shot or sleep until interrupt +*/ + +void LLAPSerial::init() +{ + sMessage.reserve(10); + bMsgReceived = false; + deviceId[0] = '-'; + deviceId[1] = '-'; +} + +void LLAPSerial::init(char* dID) +{ + init(); + bMsgReceived = false; + setDeviceId(dID); +} + +void LLAPSerial::processMessage(){ + //if (LLAP.cMessage[0] != 'a') return; //not needed as already checked + if (cMessage[1] != deviceId[0]) return; + if (cMessage[2] != deviceId[1]) return; + // now we have LLAP.cMessage[3] to LLAP.cMessage[11] as the actual message + if (0 == strncmp_P(&cMessage[3],PSTR("HELLO----"),9)) { + Serial.print(cMessage); // echo the message + } else if (0 == strncmp_P(&cMessage[3],PSTR("CHDEVID"),7)) { + deviceId[0] = cMessage[10]; + deviceId[1] = cMessage[11]; + Serial.print(cMessage); // echo the message + } else { + sMessage = String(&cMessage[3]); // let the main program deal with it + bMsgReceived = true; + } +} + +void LLAPSerial::SerialEvent() +{ + if (bMsgReceived) return; //get out if previous message not yet processed + if (Serial.available() >= 12) { + // get the new byte: + char inChar = (char)Serial.peek(); + if (inChar == 'a') { + for (byte i = 0; i<12; i++) { + inChar = (char)Serial.read(); + cMessage[i] = inChar; + if (i < 11 && Serial.peek() == 'a') { + // out of synch so abort and pick it up next time round + return; + } + } + cMessage[12]=0; + processMessage(); + } + } +} + +void LLAPSerial::sendMessage(String sToSend) +{ + cMessage[0] = 'a'; + cMessage[1] = deviceId[0]; + cMessage[2] = deviceId[1]; + for (byte i = 0; i<9; i++) { + if (i < sToSend.length()) + cMessage[i+3] = sToSend.charAt(i); + else + cMessage[i+3] = '-'; + } + + Serial.print(cMessage); + Serial.flush(); +} + +void LLAPSerial::sendInt(String sToSend, int value) +{ + char cValue[7]; // long enough for -32767 and the trailing zero + itoa(value, cValue,10); + byte cValuePtr = 0; + + cMessage[0] = 'a'; + cMessage[1] = deviceId[0]; + cMessage[2] = deviceId[1]; + for (byte i = 0; i<9; i++) { + if (i < sToSend.length()) + cMessage[i+3] = sToSend.charAt(i); + else if (cValuePtr < 7 && cValue[cValuePtr] !=0) + cMessage[i+3] = cValue[cValuePtr++]; + else + cMessage[i+3] = '-'; + } + + Serial.print(cMessage); + Serial.flush(); +} + +void LLAPSerial::sendIntWithDP(String sToSend, int value, byte decimalPlaces) +{ + char cValue[8]; // long enough for -3276.7 and the trailing zero + byte cValuePtr; + itoa(value, cValue,10); + char* cp = &cValue[strlen(cValue)]; + *(cp+1) = 0; // new terminator + while (decimalPlaces-- && --cp ) + { + *(cp+1) = *cp; + } + *cp = '.'; + + cMessage[0] = 'a'; + cMessage[1] = deviceId[0]; + cMessage[2] = deviceId[1]; + for (byte i = 0; i<9; i++) { + if (i < sToSend.length()) + cMessage[i+3] = sToSend.charAt(i); + else if (cValuePtr < 7 && cValue[cValuePtr] !=0) + cMessage[i+3] = cValue[cValuePtr++]; + else + cMessage[i+3] = '-'; + } + + Serial.print(cMessage); + Serial.flush(); +} + +void LLAPSerial::setDeviceId(char* cId) +{ + deviceId[0] = cId[0]; + deviceId[1] = cId[1]; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// +// +// This power-saving code was shamelessly stolen from the Jeelabs library with slight modification. +// see https://github.com/jcw/jeelib +// The watchdog timer is only about 10% accurate - varies between chips + + +#include +#include + +static volatile byte watchdogCounter; + +void watchdogEvent() { + ++watchdogCounter; +} + +ISR(WDT_vect) { watchdogEvent(); } + + +void watchdogInterrupts (char mode) { + // correct for the fact that WDP3 is *not* in bit position 3! + if (mode & bit(3)) + mode ^= bit(3) | bit(WDP3); + // pre-calculate the WDTCSR value, can't do it inside the timed sequence + // we only generate interrupts, no reset + byte wdtcsr = mode >= 0 ? bit(WDIE) | mode : 0; + MCUSR &= ~(1<= 16) { + char wdp = 0; // wdp 0..9 corresponds to roughly 16..8192 ms + // calc wdp as log2(msleft/16), i.e. loop & inc while next value is ok + for (word m = msleft; m >= 32; m >>= 1) + if (++wdp >= 9) + break; + watchdogCounter = 0; + watchdogInterrupts(wdp); + powerDown(); + watchdogInterrupts(-1); // off + // when interrupted, our best guess is that half the time has passed + word halfms = 8 << wdp; + msleft -= halfms; + if (watchdogCounter == 0) { + ok = 0; // lost some time, but got interrupted + break; + } + msleft -= halfms; + } + // adjust the milli ticks, since we will have missed several +#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined (__AVR_ATtiny44__) || defined (__AVR_ATtiny45__) + extern volatile unsigned long millis_timer_millis; + millis_timer_millis += msecs - msleft; +#else + extern volatile unsigned long timer0_millis; + timer0_millis += msecs - msleft; +#endif + return ok; // true if we lost approx the time planned +} + +// End of power-saving code. +// +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + 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() { + LLAP.SerialEvent(); +} + + +LLAPSerial LLAP; // declare the instance + diff --git a/LLAPSerial.h b/LLAPSerial.h new file mode 100644 index 0000000..8cd1794 --- /dev/null +++ b/LLAPSerial.h @@ -0,0 +1,34 @@ +// LLAPSerial.h + +#ifndef _LLAPSERIAL_h +#define _LLAPSERIAL_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +class LLAPSerial +{ + private: + char cMessage[13]; + void processMessage(); + public: + void init(); + void init(char* cI); + char deviceId[2]; + String sMessage; + boolean bMsgReceived; + void SerialEvent(); + void sendMessage(String sToSend); + void sendInt(String sToSend, int value); + void sendIntWithDP(String sToSend, int value, byte decimalPlaces); + void setDeviceId(char* cId); + byte sleepForaWhile (word msecs); +}; + +extern LLAPSerial LLAP; + +#endif + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29