Skip to content

Instantly share code, notes, and snippets.

@nickbalch
Last active August 21, 2018 14:39
Show Gist options
  • Save nickbalch/81e46f41914360b67a770574b8e1deed to your computer and use it in GitHub Desktop.
Save nickbalch/81e46f41914360b67a770574b8e1deed to your computer and use it in GitHub Desktop.
Moteino-Python-MQTT Bridge. Assumes a connected (via USB) moteino, and supports a number of specific formats for the data received. Refer to the arduino files where I have shared versions that use external temp sensors, or motion detectors. I relied very heavily on the sample sketches provided for the Moteinos plus code written by Gareth Naylor -
// Moteino Library and code by Felix Rusu - felix@lowpowerlab.com
// Get the RFM69 and SPIFlash library at: https://github.com/LowPowerLab/
// -------------------------------------------------------------------
// Revised from code from Gareth Naylor - https://github.com/naylogj/heatingpi
// Gateway Program for Moteino
// This script receives data from other Moteinos (running a different script)
// and sends upstream to connected device
// A local temp reading is taken when "t" is received as input on Serial connection
// All readings and received data are sent to the serial port "as received"
// so logic is required on the receiving host to decode data
// I made this choice as I found it easier to update my python program than to reflash the moteino each time
// a change was needed
// ------------------------------------------------------------------
//
// import libraries needed
#include <RFM69.h> // Moteino Radio Libs
#include <RFM69_ATC.h>
#include <SPI.h> // SPI lib for Flash comms
#include <SPIFlash.h> // FLASH libs
// defines
#define DEBUG 1 // set to 1 for additional serial port output
#define VERSION 2
#define OFFSET -1
#define NODEID 0 // this is "0" because this is the GATEWAY moteino
#define NETWORKID 100 // the same on all nodes that talk to each other
#define FREQUENCY RF69_915MHZ
//#define FREQUENCY RF69_868MHZ // Transmit Frequency for UK
#define ENCRYPTKEY "" // exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
#define ACK_TIME 30 // max # of ms to wait for an ack
#define SERIAL_BAUD 115200 // baud rate on serial port.
#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
#define LED 9 // Moteinos have LEDs on D9
#define FLASH_SS 8 // and FLASH SS on D8
// Setup Radio
#ifdef ENABLE_ATC
RFM69_ATC radio;
#else
RFM69 radio;
#endif
SPIFlash flash(FLASH_SS, 0xEF30); // EF30 for 4mbit Windbond chip (W25X40CL)
bool promiscuousMode = true; // set to 'true' to sniff all packets on the same network
// Setup Run Once
void setup() {
Serial.begin(SERIAL_BAUD);
delay(10);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); // only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
radio.promiscuous(promiscuousMode);
//Auto Transmission Control - dials down transmit power to save battery (-100 is the noise floor, -90 is still pretty good)
//For indoor nodes that are pretty static and at pretty stable temperatures (like a MotionMote) -90dBm is quite safe
//For more variable nodes that can expect to move or experience larger temp drifts a lower margin like -70 to -80 would probably be better
//Always test your ATC mote in the edge cases in your own environment to ensure ATC will perform as you expect
#ifdef ENABLE_ATC
radio.enableAutoPower(-70);
#endif
char buff[50];
sprintf(buff, "\nGateway Listening at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
if (DEBUG) Serial.println(buff);
if (flash.initialize())
{
if (DEBUG) Serial.print("SPI Flash Init OK ... UniqueID (MAC): ");
flash.readUniqueId();
for (byte i=0;i<8;i++)
{
if (DEBUG) Serial.print(flash.UNIQUEID[i], HEX);
if (DEBUG) Serial.print(' ');
}
}
else
if (DEBUG) Serial.println("SPI Flash Init FAIL! (is chip present?)");
#ifdef ENABLE_ATC
Serial.println("RFM69_ATC Enabled (Auto Transmission Control)\n");
#endif
}
//Main Loop
byte ackCount=0;
void loop() {
// process any serial input to gateway from Pi
if (Serial.available() > 0)
{
char input = Serial.read();
if (input == 'r') //d=dump all register values
radio.readAllRegs();
if (input == 'E') //E=enable encryption
radio.encrypt(ENCRYPTKEY);
if (input == 'e') //e=disable encryption
radio.encrypt(null);
if (input == 'i')
{
Serial.print("DeviceID: ");
word jedecid = flash.readDeviceId();
Serial.println(jedecid, HEX);
}
if (input == 't')
{
byte temperature = radio.readTemperature(OFFSET); // -1 = user cal factor, adjust for correct ambient
long celcius=long(temperature)*100;
long mv = readVcc();
char payload[15];
sprintf(payload,"%d:%d:%ld:%ld",NODEID,VERSION,mv,celcius);
Serial.print(payload);Serial.print(":-00");
}
}
// Process any DATA received from Remote Moteinos and send upstream on serial port.
if (radio.receiveDone())
{
// Send recieved data up to the Pi on the serial Port.
//for (byte i = 0; i < radio.DATALEN; i++)
// Serial.print((char)radio.DATA[i]);
for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA[i]);
Serial.print(":");Serial.print(radio.RSSI);Serial.flush();
if (DEBUG) Serial.print("\n [RX_RSSI ");Serial.print(radio.RSSI);Serial.print("]");
if (radio.ACKRequested()) // Ensure an Ack is sent back to sender
{
byte theNodeID = radio.SENDERID;
radio.sendACK();
if (DEBUG) Serial.print(" - ACK sent.");
// When a node requests an ACK, respond to the ACK
// and also send a packet requesting an ACK (every 3rd one only)
// This way both TX/RX NODE functions are tested on 1 end at the GATEWAY
//if (ackCount++%3==0)
//{
// Serial.print(" Pinging node ");
//Serial.print(theNodeID);
//Serial.print(" - ACK...");
//delay(3); //need this when sending right after reception .. ?
//if (radio.sendWithRetry(theNodeID, "ACK TEST", 8, 0)) // 0 = only 1 attempt, no retries
//Serial.print("ok!");
//else Serial.print("nothing");
//}
}
Serial.println();
Blink(LED,3);
}
// End of Loop
}
// Function to BLINK LED on Moteino
void Blink(byte PIN, int DELAY_MS)
{
pinMode(PIN, OUTPUT);
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
}
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#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);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
# include libs as necessary
import serial
import schedule
import time
import paho.mqtt.client as paho
import json
DEBUG=1 # set to 1 for extra output to console
PERIOD=5 # time period in munutes to ask for a local temp
SPORT="/dev/ttyUSB0" # Serial port Moteino is attached to.
MQTTHOST=""
lastalert = time.time()
if DEBUG==1:
print(SPORT)
print(MQTTHOST)
# Function to update mqtt with data
def add_reading (subtopic, payload, R):
topic = 'home/'+subtopic
if DEBUG==1: print("["+str(time.strftime('%x %X'))+"] [Publish]\t"+topic+" "+str(payload));
mosq.publish(topic,payload,retain=R)
# get temperature from node 0 - usb attached moteino
def get_local_temp():
if DEBUG==1: print("["+str(time.strftime('%x %X'))+"] [Request]\t Asking for temperature...");
usb.write(('t\n').encode('utf-8')) # send a t\n encoded as bytes
# functions to load data from moteinos into json - moteino reports a version number which allows for different sensor readings
def version1(data):
jsondata= {
'node' : int(data[0]),
'version' : int(data[1]),
'battery' : float(data[2])/1000.00,
'temp' : float(data[3])/100.00,
'signal' : float(data[4]),
'update' : time.strftime('%Y-%m-%dT%X'),
}
return jsondata
def version2(data):
jsondata= {
'node' : int(data[0]),
'version' : int(data[1]),
'battery' : float(data[2])/1000.00,
'temp' : float(data[3])/100.00,
'ext-temp' : float(data[4])/100.00,
'ext-humidity' : float(data[5])/100.00,
'ext-heatindex' : float(data[6])/100.00,
'signal' : float(data[7]),
'update' : time.strftime('%Y-%m-%dT%X'),
}
return jsondata
def version3(data):
jsondata= {
'node' : int(data[0]),
'version' : int(data[1]),
'battery' : float(data[2])/1000.00,
'temp' : float(data[3])/100.00,
'ext-temp' : float(data[4])/100.00,
'ext-humidity' : float(data[5])/100.00,
'signal' : float(data[6]),
'update' : time.strftime('%Y-%m-%dT%X'),
}
return jsondata
# open serial port to gateway moteino
# open the Serial port at 115200 baud, with a timeout of 0 seconds
# /dev/ttyUSB0 is normally the first (lower) USB connection on the Raspberry Pi
# Check ls -l /dev/ttyUS* after plugging in the USB cable and
# ammend accordingly
# ensure the sketch running on the moteino has the same BAUD rate!
try:
usb = serial.Serial(port=SPORT,
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=3)
except:
print("\nSerial Port cannot be opened. Is Moteino connected ?\n")
exit()
# if we have reached here USB is OK, look for data"
if DEBUG: print("["+str(time.strftime('%x %X'))+"] [Status]\tPort and DB opened, listening for data ...")
# setup scheduler to ask for local temp every PERIOD minutes
schedule.every(PERIOD).minutes.do(get_local_temp)
# setup mqtt
mosq = paho.Client()
mosq.will_set("home/services/moteinopython/online","0",1,True)
mosq.connect(MQTTHOST)
mosq.publish("home/services/moteinopython/online","1",1,True)
mosq.loop_start()
while 1:
# try:
schedule.run_pending()
line = usb.readline()
# need to convert values read from moteino into str data
datastring=line.decode('utf-8')
if "*" in datastring: # alert type report - from a PIR or similar
alert=datastring.split("*")
add_reading("state/moteino_"+alert[2],alert[1],False)
if(time.time()-lastalert)>10:
add_reading("alert/moteino_"+alert[2]+"/"+alert[1],time.strftime('%x %X'),True)
lastalert = time.time()
elif ":" in datastring: # standard sensor type report
if DEBUG==1: print("["+str(time.strftime('%x %X'))+"] [Decoded]\t"+datastring);
data=str(datastring).split(':')
if data[1]=="1" or data[0] == "0" : jsondata=version1(data)
if data[1]=="2" and data[0] != "0": jsondata=version2(data)
if data[1]=="3" and data[0] != "0": jsondata=version3(data)
j=json.loads(json.dumps(jsondata))
add_reading("moteino/"+str(j['node']),json.dumps(jsondata),True)
for key,value in j.items():
add_reading("status/moteino_"+str(j['node'])+"_"+key,value,True)
else:
if len(line)>1 & DEBUG==1: print("["+str(time.strftime('%x %X'))+"] [Ignored]\t"+line),;
time.sleep(0.5)
# except:
# usb.close()
# exit()

These are samples of my moteino to MQTT solution. Various moteino nodes send data to a central moteino node (Gateway) that is connected to a RaspberryPI via USB This Pi runs a python program that parses the recieved data and sends to various MQTT topics Other systems subscribe to the MQTT topics (for example, Openhab)

Moteinos are battery operated, run for many months on a 9V cell

// Sample RFM69 sender/node sketch for the MotionMote
// http://lowpowerlab.com/motionmote
// PIR motion sensor connected to D3 (INT1)
// When RISE happens on D3, the sketch transmits a "MOTION" msg to receiver Moteino and goes back to sleep
// In sleep mode, Moteino + PIR motion sensor use about ~60uA
// IMPORTANT: adjust the settings in the configuration section below !!!
// This also contains revised from code from Gareth Naylor - https://github.com/naylogj/heatingpi
// Node program using SI7021 sensor to capture humidity and temperature plus PIR
// Sends data in following formats:
// 1: scheduled : <node id>:<code version>:<battery volts>,<temp in celcius>
// 2: When motion is detected: *motion*:<node id>:<code version>*
// code version is an arbitrary value set to enable alternative data formats and payloads to be sent and correctly parsed
// This data is sent through to the gateway node
// A python program then publishes this via MQTT
//
// **********************************************************************************
// Copyright Felix Rusu of LowPowerLab.com, 2016
// RFM69 library and sample code by Felix Rusu - lowpowerlab.com/contact
// **********************************************************************************
// License
// **********************************************************************************
// 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/>.
//
// Licence can be viewed at
// http://www.gnu.org/licenses/gpl-3.0.txt
//
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
// **********************************************************************************
#include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69
#include <RFM69_ATC.h>//get it here: https://www.github.com/lowpowerlab/rfm69
#include <SPI.h> //comes with Arduino IDE (www.arduino.cc)
#include <LowPower.h> //get library from: https://github.com/lowpowerlab/lowpower
//writeup here: http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/
#include <SPIFlash.h> //get it here: https://www.github.com/lowpowerlab/spiflash
//#include <SparkFunBME280.h> //get it here: https://github.com/sparkfun/SparkFun_BME280_Breakout_Board/tree/master/Libraries/Arduino/src
#include <Wire.h> //comes with Arduino
#include <SI7021.h>
//*********************************************************************************************
//************ IMPORTANT SETTINGS - YOU MUST CHANGE/CONFIGURE TO FIT YOUR HARDWARE *************
//*********************************************************************************************
#define NODEID 2 //unique for each node on same network
#define VERSION 3
#define NETWORKID 100 //the same on all nodes that talk to each other
#define GATEWAYID 0
#define OFFSET -1
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
#define IS_RFM69HW //uncomment only for RFM69HW! Remove/comment if you have RFM69W!
#define ENCRYPTKEY "0000000000000017" //exactly the same 16 characters/bytes on all nodes!
#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
#define ATC_RSSI -75
//#define ENABLE_BME280 //uncomment to allow reading the BME280 (if present)
//*********************************************************************************************
#define ACK_TIME 30 // max # of ms to wait for an ack
#define ONBOARDLED 9 // Moteinos have LEDs on D9
#define LED 5 // MotionOLEDMote has an external LED on D5
#define MOTION_PIN 3 // D3
#define MOTION_IRQ 1 // hardware interrupt 1 (D3) - where motion sensors OUTput is connected, this will generate an interrupt every time there is MOTION
#define BATT_MONITOR A7 // Sense VBAT_COND signal (when powered externally should read ~3.25v/3.3v (1000-1023), when external power is cutoff it should start reading around 2.85v/3.3v * 1023 ~= 883 (ratio given by 10k+4.7K divider from VBAT_COND = 1.47 multiplier)
#define BATT_FORMULA(reading) reading * 0.00322 * 1.49 // >>> fine tune this parameter to match your voltage when fully charged
// details on how this works: https://lowpowerlab.com/forum/index.php/topic,1206.0.html
#define DUPLICATE_INTERVAL 180000 //avoid duplicates in 3 minute intervals (ie mailman sometimes spends 30+ seconds at mailbox)
#define BATT_INTERVAL 300000 // read and report battery voltage every this many ms (approx)
#define SERIAL_EN //comment this out when deploying to an installed Mote to save a few KB of sketch size
#define SERIAL_BAUD 115200
#ifdef SERIAL_EN
#define DEBUG(input) {Serial.print(input); delay(1);}
#define DEBUGln(input) {Serial.println(input); delay(1);}
#define DEBUGFlush() { Serial.flush(); }
#else
#define DEBUG(input);
#define DEBUGln(input);
#define DEBUGFlush();
#endif
#ifdef ENABLE_ATC
RFM69_ATC radio;
#else
RFM69 radio;
#endif
#define FLASH_SS 8 // and FLASH SS on D8 on regular Moteinos (D23 on MoteinoMEGA)
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for 4mbit Windbond chip (W25X40CL)
#ifdef ENABLE_BME280
BME280 bme280;
#endif
volatile boolean motionDetected=false;
volatile boolean motionSent=false;
float batteryVolts = 5;
char BATstr[10]; //longest battery voltage reading message = 9chars
char sendBuf[32];
byte sendLen;
#ifdef ENABLE_BME280
float temperature=0;
char Fstr[10];
#endif
int t;
int h;
SI7021 sensor;
void motionIRQ(void);
// void checkBattery(void);
void setup() {
Serial.begin(SERIAL_BAUD);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); //uncomment only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
//Auto Transmission Control - dials down transmit power to save battery (-100 is the noise floor, -90 is still pretty good)
//For indoor nodes that are pretty static and at pretty stable temperatures (like a MotionMote) -90dBm is quite safe
//For more variable nodes that can expect to move or experience larger temp drifts a lower margin like -70 to -80 would probably be better
//Always test your ATC mote in the edge cases in your own environment to ensure ATC will perform as you expect
#ifdef ENABLE_ATC
radio.enableAutoPower(ATC_RSSI);
#endif
pinMode(MOTION_PIN, INPUT);
attachInterrupt(MOTION_IRQ, motionIRQ, RISING);
// attachInterrupt(MOTION_IRQ, motionIRQ_1, FALLING);
char buff[50];
Serial.print("Moteino node ");
Serial.print(NODEID);
Serial.print(" Version ");
Serial.print(VERSION);
Serial.print("\n");
sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
DEBUGln(buff);
pinMode(ONBOARDLED, OUTPUT);
pinMode(LED, OUTPUT);
// radio.sendWithRetry(GATEWAYID, "START", 5);
#ifdef ENABLE_ATC
DEBUGln("RFM69_ATC Enabled (Auto Transmission Control)\n");
#endif
if (flash.initialize()) flash.sleep(); //if Moteino has FLASH-MEM, make sure it sleeps
#ifdef ENABLE_BME280
bme280.settings.commInterface = I2C_MODE;
bme280.settings.I2CAddress = 0x77;
bme280.settings.runMode = 3; //Normal mode
bme280.settings.tStandby = 0;
bme280.settings.filter = 0;
bme280.settings.tempOverSample = 1;
bme280.settings.pressOverSample = 1;
bme280.settings.humidOverSample = 1;
#endif
sensor.begin();
}
void motionIRQ()
{
motionDetected=true;
DEBUGln("IRQ");
// DEBUGln(digitalRead(MOTION_PIN));
}
uint16_t batteryReportCycles=0;
uint32_t time=0, now=0, MLO=0, BLO=0;
byte motionRecentlyCycles=0;
void loop() {
long dhthum;
long dhttmp;
long mv;
long celcius;
now = millis();
//checkBattery();
//DEBUG("Slept: ");DEBUG(now-lastSleepTime);DEBUGln("ms");
if (motionDetected)
{
DEBUG("Time = ");
DEBUGln(time);
DEBUGln(MLO);
DEBUGln(time-MLO);
DEBUGln(DUPLICATE_INTERVAL);
DEBUGln(motionSent);
}
if (time-MLO > DUPLICATE_INTERVAL && motionSent)
{
motionSent = false; //reset so that a new notification will be sent
MLO = time; // set timestamp of event
DEBUGln("reset motion notifier");
}
if (motionDetected && (!motionSent))
{
sprintf(sendBuf, "*motion*%d*",NODEID,VERSION);
//sprintf(sendBuf, "MOTION");
sendLen = strlen(sendBuf);
DEBUGln(sendBuf);
if (radio.sendWithRetry(GATEWAYID, sendBuf, sendLen))
{
DEBUG("MOTION ACK:OK! RSSI:");
DEBUG(radio.RSSI);
DEBUG("\n");
batteryReportCycles = 0;
motionSent = true;
}
else DEBUG("MOTION ACK:NOK...\n");
// DEBUG(" VIN: ");
// DEBUGln(BATstr);
radio.sleep();
//digitalWrite(LED, LOW);
}
else if (time-BLO > BATT_INTERVAL)
{
mv = (readVcc());
si7021_env data = sensor.getHumidityAndTemperature();
int t = data.celsiusHundredths/100;
int h = data.humidityBasisPoints/100;
dhthum=long(h)*100;
dhttmp=long(t)*100;
// Read the temp of the radio
byte temperature = radio.readTemperature(OFFSET); // adjust for correct ambient temp
celcius=long(temperature)*100;
sprintf(sendBuf, "%d:%d:%ld:%ld:%ld:%ld",NODEID,VERSION,mv,celcius,dhttmp,dhthum);
//#ifdef ENABLE_BME280
// //read BME sensor
// bme280.begin();
// dtostrf(bme280.readTempF(), 3,2, Fstr);
// bme280.writeRegister(BME280_CTRL_MEAS_REG, 0x00); //sleep the BME280
// sprintf(sendBuf, "BAT:%sv F:%s", BATstr, Fstr);
//#else
// sprintf(sendBuf, "BAT:%sv", BATstr);
//#endif
sendLen = strlen(sendBuf);
BLO = time;
DEBUGln(sendBuf);
radio.sendWithRetry(GATEWAYID, sendBuf, sendLen);
radio.sleep();
batteryReportCycles=0;
}
DEBUGFlush();
//while motion recently happened sleep for small slots of time to better approximate last motion event
//this helps with debouncing a "MOTION" event more accurately for sensors that fire the IRQ very rapidly (ie panasonic sensors)
if (motionDetected ||motionRecentlyCycles>0)
{
if (motionDetected) motionRecentlyCycles=8;
else motionRecentlyCycles--;
motionDetected=false; //do NOT move this after the SLEEP line below or motion will never be detected
time = time + 250 + millis()-now;
radio.sleep();
LowPower.powerDown(SLEEP_250MS, ADC_OFF, BOD_OFF);
//delay(250);
DEBUG(digitalRead(MOTION_PIN));
DEBUG(" WAKEUP250ms\n");
}
else
{
time = time + 8000 + millis()-now;
radio.sleep();
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
//delay(4000);
DEBUG(digitalRead(MOTION_PIN));
DEBUG(" WAKEUP8s\n");
}
batteryReportCycles++;
}
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#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);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
// Sample RFM69 receiver/gateway sketch, with ACK and optional encryption
// Passes through any wireless received messages to the serial port & responds to ACKs
// It also looks for an onboard FLASH chip, if present
// Library and code by Felix Rusu - felix@lowpowerlab.com
// Get the RFM69 and SPIFlash library at: https://github.com/LowPowerLab/
// Revised from code from Gareth Naylor - https://github.com/naylogj/heatingpi
// Node program using internal temp sensor of radio
// Sends data in following format <node id>:<code version>:<battery volts>,<temp in celcius>
// code version is an arbitrary value set to enable alternative data formats and payloads to be sent and correctly parsed
// This data is sent through to the gateway node
// A python program then publishes this via MQTT
//
#include <RFM69.h>
#include <RFM69_ATC.h>
//#include <SPI.h>
//#include <SPIFlash.h>
#include <LowPower.h>
#define OFFSET -1 // offset in degrees to normalise temp sensor
#define VERSION 1
#define DEBUG 1 // set to 1 for additional serial port output
#define NODEID 3 //unique for each node on same network
#define NETWORKID 100 //the same on all nodes that talk to each other
#define GATEWAYID 0 // the ID of the GATEWAY RX node
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
#define ENCRYPTKEY "" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
#define ACK_TIME 30 // max # of ms to wait for an ack
#define SERIAL_BAUD 115200
#define ENABLE_ATC
#ifdef __AVR_ATmega1284P__
#define LED 15 // Moteino MEGAs have LEDs on D15
#define FLASH_SS 23 // and FLASH SS on D23
#else
#define LED 9 // Moteinos have LEDs on D9
#define FLASH_SS 8 // and FLASH SS on D8
#endif
#ifdef ENABLE_ATC
RFM69_ATC radio;
#else
RFM69 radio;
#endif
bool promiscuousMode = false; //set to 'true' to sniff all packets on the same network
void setup() {
Serial.begin(SERIAL_BAUD);
delay(10);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); //only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
radio.promiscuous(promiscuousMode);
//Auto Transmission Control - dials down transmit power to save battery (-100 is the noise floor, -90 is still pretty good)
//For indoor nodes that are pretty static and at pretty stable temperatures (like a MotionMote) -90dBm is quite safe
//For more variable nodes that can expect to move or experience larger temp drifts a lower margin like -70 to -80 would probably be better
//Always test your ATC mote in the edge cases in your own environment to ensure ATC will perform as you expect
#ifdef ENABLE_ATC
radio.enableAutoPower(-70);
#endif
char buff[50];
if (DEBUG) Serial.print("Node Version");
if (DEBUG) Serial.print(VERSION);
if (DEBUG) Serial.print("\n");
sprintf(buff, "\nListening at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
if (DEBUG) Serial.println(buff);
#ifdef ENABLE_ATC
Serial.println("RFM69_ATC Enabled (Auto Transmission Control)\n");
#endif
}
byte ackCount=0;
void loop() {
char payload[13];
long celcius;
long mv;
int sendSize;
// Read the temp of the radio
byte temperature = radio.readTemperature(OFFSET); // adjust for correct ambient temp
celcius=long(temperature)*100;
if (DEBUG) Serial.print( "Radio Temp is ");
if (DEBUG) Serial.print(temperature);
if (DEBUG) Serial.println();
if (DEBUG) Serial.print("Temperature is ");
if (DEBUG) Serial.print(":");
if (DEBUG) Serial.print(celcius);
if (DEBUG) Serial.print("\n");
// Read voltage
mv = (readVcc());
if (DEBUG) Serial.print( "mvolts voltage is ");
if (DEBUG) Serial.print(mv);
if (DEBUG) Serial.print("\n");
// build comms payload to send via radio
// format nodeid:temp
// and set sendSize to the correct (non zero) value?
sprintf(payload,"%d:%d:%ld:%ld",NODEID,VERSION,mv,celcius);
sendSize=sizeof(payload);
if (DEBUG) Serial.print("Payload is: ");
if (DEBUG) Serial.print(payload);
if (DEBUG) Serial.print("\n");
if (DEBUG) Serial.print("Sendsize is: ");
if (DEBUG) Serial.print(sendSize);
if (DEBUG) Serial.print("\n");
if (sendSize != 0)
{
// Send data over Radio to gateway
if (radio.sendWithRetry(GATEWAYID, payload, sendSize))
Serial.print("ok!\n");
else Serial.print(" nothing...\n");
sendSize = 0;
Serial.println();
Blink(LED,3);
}
// GO to SLEEP FOR A PERIOD OF TIME
// PUT RADIO into sleep and then MICRCONCONTROLLER INTO HYBERNATION
if (DEBUG) Serial.print("Sleeping");
if (DEBUG) Serial.print("\n");
radio.sleep(); // put radio to sleep
// put Moteino to sleep for loop limit * 8 seconds - params for ATMega328p
// for 10 mins use 75
// for 5 mins use ~38
// for 15 mins use 112
for(byte c = 0; c < 112 ; c++)
LowPower.powerDown(SLEEP_8S,ADC_OFF, BOD_OFF);
}
void Blink(byte PIN, int DELAY_MS)
{
pinMode(PIN, OUTPUT);
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
}
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#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);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
// Sample RFM69 receiver/gateway sketch, with ACK and optional encryption
// Passes through any wireless received messages to the serial port & responds to ACKs
// It also looks for an onboard FLASH chip, if present
// Library and code by Felix Rusu - felix@lowpowerlab.com
// Get the RFM69 and SPIFlash library at: https://github.com/LowPowerLab/
// Revised from code from Gareth Naylor - https://github.com/naylogj/heatingpi
// Node program using SI7021 sensor to capture humidity and temperature
// Sends data in following format <node id>:<code version>:<battery volts>,<temp in celcius>
// code version is an arbitrary value set to enable alternative data formats and payloads to be sent and correctly parsed
// This data is sent through to the gateway node
// A python program then publishes this via MQTT
//
#include <RFM69.h>
#include <RFM69_ATC.h>
#include <SPI.h>
#include <SPIFlash.h>
#include <LowPower.h>
#include <Wire.h>
#include <SI7021.h>
SI7021 sensor;
int led1 = 3;
int led2 = 4;
#define OFFSET +4 // offset in degrees to normalise temp sensor
#define VERSION 3
#define DEBUG 1 // set to 1 for additional serial port output
#define TPIN 4 // Digital Pin4 PD4 D4 connected to DS18B20
#define NODEID 6 //unique for each node on same network should match ZONE
#define NETWORKID 100 //the same on all nodes that talk to each other
#define GATEWAYID 0 // the ID of the GATEWAY RX node
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
#define ENCRYPTKEY "0000000000000017" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
#define ACK_TIME 30 // max # of ms to wait for an ack
#define SERIAL_BAUD 115200
#define ENABLE_ATC
#ifdef __AVR_ATmega1284P__
#define LED 15 // Moteino MEGAs have LEDs on D15
#define FLASH_SS 23 // and FLASH SS on D23
#else
#define LED 9 // Moteinos have LEDs on D9
#define FLASH_SS 8 // and FLASH SS on D8
#endif
#ifdef ENABLE_ATC
RFM69_ATC radio;
#else
RFM69 radio;
#endif
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for 4mbit Windbond chip (W25X40CL)
bool promiscuousMode = false; //set to 'true' to sniff all packets on the same network
void setup() {
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
Serial.begin(SERIAL_BAUD);
delay(10);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); //only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
radio.promiscuous(promiscuousMode);
//Auto Transmission Control - dials down transmit power to save battery (-100 is the noise floor, -90 is still pretty good)
//For indoor nodes that are pretty static and at pretty stable temperatures (like a MotionMote) -90dBm is quite safe
//For more variable nodes that can expect to move or experience larger temp drifts a lower margin like -70 to -80 would probably be better
//Always test your ATC mote in the edge cases in your own environment to ensure ATC will perform as you expect
#ifdef ENABLE_ATC
radio.enableAutoPower(-70);
#endif
char buff[50];
Serial.print("Moteino Node ");
Serial.print (NODEID);
Serial.print(" Version ");
Serial.print(VERSION);
Serial.print("\n");
sprintf(buff, "\nListening at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
if (DEBUG) Serial.println(buff);
if (flash.initialize())
{
if (DEBUG) Serial.print("SPI Flash Init OK ... UniqueID (MAC): ");
flash.readUniqueId();
for (byte i=0;i<8;i++)
{
if (DEBUG) Serial.print(flash.UNIQUEID[i], HEX);
if (DEBUG) Serial.print(' ');
}
}
else
if (DEBUG) Serial.println("SPI Flash Init FAIL! (is chip present?)");
#ifdef ENABLE_ATC
Serial.println("RFM69_ATC Enabled (Auto Transmission Control)\n");
#endif
sensor.begin();
}
byte ackCount=0;
void loop() {
char payload[23];
long celcius;
long mv;
long dhthum;
long dhttmp;
long dhthi;
int sendSize;
si7021_env data = sensor.getHumidityAndTemperature();
// for (int i = 0; i < data.celsiusHundredths/100; i++) {
// pulse(led1);
// }
// for (int i = 0; i < data.humidityBasisPoints/100; i++) {
// pulse(led2);
// }
int t = data.celsiusHundredths/100;
int h = data.humidityBasisPoints/100;
if (DEBUG) Serial.print("Humidity: ");
if (DEBUG) Serial.print(h);
if (DEBUG) Serial.print("\n");
if (DEBUG) Serial.print(" %\t");
if (DEBUG) Serial.print("Temperature: ");
if (DEBUG) Serial.print(t);
if (DEBUG) Serial.print(" *C ");
if (DEBUG) Serial.print("\n");
//if (DEBUG) Serial.print(f);
//if (DEBUG) Serial.print(" *F\t");
//if (DEBUG) Serial.print("Heat index: ");
//if (DEBUG) Serial.print(hic);
//if (DEBUG) Serial.print(" *C ");
//if (DEBUG) Serial.print(hif);
//if (DEBUG) Serial.println(" *F");
dhthum=long(h)*100;
dhttmp=long(t)*100;
//dhthi=long(hic)*100;
// Read the temp of the radio
byte temperature = radio.readTemperature(OFFSET); // adjust for correct ambient temp
celcius=long(temperature)*100;
if (DEBUG) Serial.print( "Radio Temp is ");
if (DEBUG) Serial.print(temperature);
if (DEBUG) Serial.println();
if (DEBUG) Serial.print("Temperature is ");
if (DEBUG) Serial.print(":");
if (DEBUG) Serial.print(celcius);
if (DEBUG) Serial.print("\n");
// Read voltage
mv = (readVcc());
if (DEBUG) Serial.print( "mvolts voltage is ");
if (DEBUG) Serial.print(mv);
if (DEBUG) Serial.print("\n");
// build comms payload to send via radio
// format nodeid:temp
// and set sendSize to the correct (non zero) value?
//sprintf(payload,"%d:%d:%ld:%ld",NODEID,VERSION,mv,celcius);
sprintf(payload,"%d:%d:%ld:%ld:%ld:%ld",NODEID,VERSION,mv,celcius,dhttmp,dhthum);
sendSize=sizeof(payload);
if (DEBUG) Serial.print("Payload is: ");
if (DEBUG) Serial.print(payload);
if (DEBUG) Serial.print("\n");
if (DEBUG) Serial.print("Sendsize is: ");
if (DEBUG) Serial.print(sendSize);
if (DEBUG) Serial.print("\n");
if (sendSize != 0)
{
// Send data over Radio to gateway
if (radio.sendWithRetry(GATEWAYID, payload, sendSize))
Serial.print("ok!\n");
else Serial.print(" nothing...\n");
sendSize = 0;
Serial.println();
Blink(LED,3);
}
// GO to SLEEP FOR A PERIOD OF TIME
// PUT RADIO into sleep and then MICRCONCONTROLLER INTO HYBERNATION
if (DEBUG) Serial.print("Sleeping");
if (DEBUG) Serial.print("\n");
radio.sleep(); // put radio to sleep
// put Moteino to sleep for loop limit * 8 seconds - params for ATMega328p
// for 10 mins use 75
// for 5 mins use ~38
// for 15 mins use 112
for(byte c = 0; c < 112 ; c++)
LowPower.powerDown(SLEEP_8S,ADC_OFF, BOD_OFF);
}
void Blink(byte PIN, int DELAY_MS)
{
pinMode(PIN, OUTPUT);
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
}
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#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);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment