Skip to content

Commit

Permalink
New NanodeRF example: fetched time from http reply and broadcasts it …
Browse files Browse the repository at this point in the history
…to listening nodes such as emonglcd, relays temperature from emonglcd to emoncms
  • Loading branch information
Trystan committed Jan 3, 2012
1 parent de8d2e7 commit 444315d
Show file tree
Hide file tree
Showing 3 changed files with 369 additions and 0 deletions.
@@ -0,0 +1,272 @@
/* _ _
| | | |
___ _ __ ___ ___ _ __ | |__ __ _ ___ ___ _ __ __ _ _ __ ___ __| | ___
/ _ \ '_ ` _ \ / _ \| '_ \| '_ \ / _` / __|/ _ \ | '_ \ / _` | '_ \ / _ \ / _` |/ _ \
| __/ | | | | | (_) | | | | |_) | (_| \__ \ __/ _ | | | | (_| | | | | (_) | (_| | __/
\___|_| |_| |_|\___/|_| |_|_.__/ \__,_|___/\___| (_) |_| |_|\__,_|_| |_|\___/ \__,_|\___|
*/
//--------------------------------------------------------------------------------------
// Relay's data recieved by emontx up to emoncms
// Relay's data recieved by emonglcd up to emoncms
// Decodes reply from server to set software real time clock
// Relay's time data to emonglcd - and any other listening nodes.
// Looks for 'ok' reply from http request to verify data reached emoncms

// emonBase Documentation: http://openenergymonitor.org/emon/emonbase

// Authors: Trystan Lea and Glyn Hudson
// Part of the: openenergymonitor.org project
// Licenced under GNU GPL V3
//http://openenergymonitor.org/emon/license

// EtherCard Library by Jean-Claude Wippler and Andrew Lindsay
// JeeLib Library by Jean-Claude Wippler
//--------------------------------------------------------------------------------------

#define DEBUG

#include <Wire.h>
#include <RTClib.h>
RTC_Millis RTC;

#include <JeeLib.h> //https://github.com/jcw/jeelib
#include <avr/wdt.h>

#define MYNODE 15 // node ID 30 reserved for base station
#define freq RF12_433MHZ // frequency
#define group 210 // network group

//---------------------------------------------------
// Data structures for transfering data between units
//---------------------------------------------------
typedef struct { int power, battery; } PayloadTX;
PayloadTX emontx;

typedef struct { int temperature; } PayloadGLCD;
PayloadGLCD emonglcd;

typedef struct { int hour, mins, sec; } PayloadBase;
PayloadBase emonbase;
//---------------------------------------------------

//---------------------------------------------------------------------
// The PacketBuffer class is used to generate the json string that is send via ethernet - JeeLabs
//---------------------------------------------------------------------
class PacketBuffer : public Print {
public:
PacketBuffer () : fill (0) {}
const char* buffer() { return buf; }
byte length() { return fill; }
void reset()
{
memset(buf,NULL,sizeof(buf));
fill = 0;
}
virtual size_t write (uint8_t ch)
{ if (fill < sizeof buf) buf[fill++] = ch; }
byte fill;
char buf[150];
private:
};
PacketBuffer str;

//--------------------------------------------------------------------------
// Ethernet
//--------------------------------------------------------------------------
#include <EtherCard.h> //https://github.com/jcw/ethercard

// ethernet interface mac address, must be unique on the LAN
static byte mymac[] = { 0x44,0x29,0x49,0x21,0x30,0x31 };

//IP address of remote sever, only needed when posting to a server that has not got a dns domain name (staticIP e.g local server)
byte Ethernet::buffer[700];
static uint32_t timer;

//Domain name of remote webserver - leave blank if posting to IP address
char website[] PROGMEM = "vis.openenergymonitor.org";
//static byte hisip[] = { 213,138,101,177 }; // un-comment for posting to static IP server (no domain name)

const int redLED = 6; // NanodeRF RED indicator LED
const int greenLED = 5; // NanodeRF GREEN indicator LED

int ethernet_error = 0; // Etherent (controller/DHCP) error flag
int rf_error = 0; // RF error flag - high when no data received
int ethernet_requests = 0; // count ethernet requests without reply

int emonglcd_rx = 0; // Used to indicate that emonglcd data is available
int data_ready=0; // Used to signal that emontx data is ready to be sent
unsigned long last_rf; // Used to check for regular emontx data - otherwise error

char line_buf[50]; // Used to store line of http reply header

//**********************************************************************************************************************
// SETUP
//**********************************************************************************************************************
void setup () {

//Nanode RF LED indictor setup - green flashing means good - red on for a long time means bad!
//High means off since NanodeRF tri-state buffer inverts signal
pinMode(redLED, OUTPUT); digitalWrite(redLED,LOW);
pinMode(greenLED, OUTPUT); digitalWrite(greenLED,LOW);
delay(100); digitalWrite(redLED,HIGH); // turn off redLED

Serial.begin(9600);
Serial.println("\n[webClient]");

if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) {
Serial.println( "Failed to access Ethernet controller");
ethernet_error = 1;
}

rf12_initialize(MYNODE, freq,group);
last_rf = millis()-40000; // setting lastRF back 40s is useful as it forces the ethernet code to run straight away

digitalWrite(greenLED,HIGH); // Green LED off - indicate that setup has finished

wdt_enable(WDTO_8S); // Turn on crash watchdog
}

//**********************************************************************************************************************
// LOOP
//**********************************************************************************************************************
void loop () {

wdt_reset(); // reset crash watchdog

dhcp_dns(); // handle dhcp and dns setup - see dhcp_dns tab

// Display error states on status LED
if (ethernet_error==1 || rf_error==1 || ethernet_requests > 0) digitalWrite(redLED,LOW);
else digitalWrite(redLED,HIGH);

//-----------------------------------------------------------------------------------------------------------------
// 1) On RF recieve
//-----------------------------------------------------------------------------------------------------------------
if (rf12_recvDone()){
if (rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0)
{
int node_id = (rf12_hdr & 0x1F);

if (node_id == 10) // EMONTX
{
emontx = *(PayloadTX*) rf12_data; // get emontx data
Serial.println(); // print emontx data to serial
Serial.print("1 emontx: ");
Serial.print(emontx.power);
Serial.print(' ');
Serial.print(emontx.battery);
Serial.print(" | time: ");
Serial.println(millis()-last_rf);
last_rf = millis(); // reset lastRF timer

delay(50); // make sure serial printing finished

// JSON creation: JSON sent are of the format: {key1:value1,key2:value2} and so on

str.reset(); // Reset json string
str.print("{rf_fail:0"); // RF recieved so no failure
str.print(",power:"); str.print(emontx.power); // Add power reading
str.print(",battery:"); str.print(emontx.battery); // Add emontx battery voltage reading

data_ready = 1; // data is ready
rf_error = 0;
}

if (node_id == 20) // EMONGLCD
{
emonglcd = *(PayloadGLCD*) rf12_data; // get emonglcd data
Serial.print("5 emonglcd: "); // print output
Serial.println(emonglcd.temperature);
emonglcd_rx = 1;
}
}
}

//-----------------------------------------------------------------------------------------------------------------
// 2) If no data is recieved from rf12 module the server is updated every 30s with RFfail = 1 indicator for debugging
//-----------------------------------------------------------------------------------------------------------------
if ((millis()-last_rf)>30000)
{
last_rf = millis(); // reset lastRF timer
str.reset(); // reset json string
str.print("{rf_fail:1"); // No RF received in 30 seconds so send failure
data_ready = 1; // Ok, data is ready
rf_error=1;
}


//-----------------------------------------------------------------------------------------------------------------
// 3) Send data via ethernet
//-----------------------------------------------------------------------------------------------------------------
ether.packetLoop(ether.packetReceive());

if (data_ready) {

// include temperature data from emonglcd if it has been recieved
if (emonglcd_rx) {
str.print(",temperature:");
str.print(emonglcd.temperature/100.0);
emonglcd_rx = 0;
}

str.print("}\0"); // End of json string

Serial.print("2 "); Serial.println(str.buf); // print to serial json string

// Example of posting to emoncms v3 demo account goto http://vis.openenergymonitor.org/emoncms3
// and login with sandbox:sandbox
// To point to your account just enter your WRITE APIKEY
ethernet_requests ++;
ether.browseUrl(PSTR("/emoncms3/api/post.json?apikey=2d177d7311daf401d054948ce29efe74&json="),str.buf, website, my_callback);
data_ready =0;
}

if (ethernet_requests > 10) delay(10000); // Reset the nanode if more than 10 request attempts have been tried without a reply

}
//**********************************************************************************************************************

//-----------------------------------------------------------------------------------
// Ethernet callback
// recieve reply and decode
//-----------------------------------------------------------------------------------
static void my_callback (byte status, word off, word len) {

get_header_line(2,off); // Get the date and time from the header
Serial.print("3 ok | "); // Print out the date and time
Serial.println(line_buf); // Print out the date and time

// Decode date time string to get integers for hour, min, sec, day
// We just search for the characters and hope they are in the right place
char val[1];
val[0] = line_buf[23]; val[1] = line_buf[24];
int hour = atoi(val);
val[0] = line_buf[26]; val[1] = line_buf[27];
int mins = atoi(val);
val[0] = line_buf[29]; val[1] = line_buf[30];
int sec = atoi(val);
val[0] = line_buf[11]; val[1] = line_buf[12];
int day = atoi(val);

RTC.adjust(DateTime(2012, 1, day, hour, mins, sec));

DateTime now = RTC.now();
emonbase.hour = now.hour();
emonbase.mins = now.minute();
emonbase.sec = now.second();

//-----------------------------------------------------------------------------

delay(100);

// Send time data
int i = 0; while (!rf12_canSend() && i<10) {rf12_recvDone(); i++;} // if can send - exit if it gets stuck, as it seems too
rf12_sendStart(0, &emonbase, sizeof emonbase); // send payload
rf12_sendWait(0);

Serial.println("4 emonbase sent");

get_reply_data(off);
if (strcmp(line_buf,"ok")) {ethernet_requests = 0; ethernet_error = 0;} // check for ok reply from emoncms to verify data post request
}
51 changes: 51 additions & 0 deletions NanodeRF_singleCT_RTCrelay_GLCDtemp/decode_reply.ino
@@ -0,0 +1,51 @@
int get_header_line(int line,word off)
{
memset(line_buf,NULL,sizeof(line_buf));
if (off != 0)
{
uint16_t pos = off;
int line_num = 0;
int line_pos = 0;

while (Ethernet::buffer[pos])
{
if (Ethernet::buffer[pos]=='\n')
{
line_num++; line_buf[line_pos] = '\0';
line_pos = 0;
if (line_num == line) return 1;
}
else
{
if (line_pos<49) {line_buf[line_pos] = Ethernet::buffer[pos]; line_pos++;}
}
pos++;
}
}
return 0;
}

int get_reply_data(word off)
{
memset(line_buf,NULL,sizeof(line_buf));
if (off != 0)
{
uint16_t pos = off;
int line_num = 0;
int line_pos = 0;

// Skip over header until data part is found
while (Ethernet::buffer[pos]) {
if (Ethernet::buffer[pos-1]=='\n' && Ethernet::buffer[pos]=='\r') break;
pos++;
}
pos+=4;
while (Ethernet::buffer[pos])
{
if (line_pos<49) {line_buf[line_pos] = Ethernet::buffer[pos]; line_pos++;} else break;
pos++;
}
line_buf[line_pos] = '\0';
}
return 0;
}
46 changes: 46 additions & 0 deletions NanodeRF_singleCT_RTCrelay_GLCDtemp/dhcp_dns.ino
@@ -0,0 +1,46 @@
int dhcp_status = 0;
int dns_status = 0;

void dhcp_dns()
{
//-----------------------------------------------------------------------------------
// Get DHCP address
// Putting DHCP setup and DNS lookup in the main loop allows for:
// powering nanode before ethernet is connected
//-----------------------------------------------------------------------------------
if (ether.dhcpExpired()) dhcp_status = 0; // if dhcp expired start request for new lease by changing status

if (!dhcp_status){
wdt_disable();
dhcp_status = ether.dhcpSetup(); // DHCP setup
wdt_enable(WDTO_8S);
Serial.print("DHCP status: "); // print
Serial.println(dhcp_status); // dhcp status

if (dhcp_status){ // on success print out ip's
ether.printIp("IP: ", ether.myip);
ether.printIp("GW: ", ether.gwip);

static byte dnsip[] = {8,8,8,8};
ether.copyIp(ether.dnsip, dnsip);
ether.printIp("DNS: ", ether.dnsip);
//ether.copyIp(ether.hisip, hisip); // un-comment for posting to static IP server (no domain name)
//dns_status = 1; // un-comment for posting to static IP server (no domain name)
} else { ethernet_error = 1; }
}

//-----------------------------------------------------------------------------------
// Get server address via DNS
//-----------------------------------------------------------------------------------
if (dhcp_status && !dns_status){
wdt_disable();
dns_status = ether.dnsLookup(website); // Attempt DNS lookup
wdt_enable(WDTO_8S);
Serial.print("DNS status: "); // print
Serial.println(dns_status); // dns status
if (dns_status){
ether.printIp("SRV: ", ether.hisip); // server ip
} else { ethernet_error = 1; }
}

}

0 comments on commit 444315d

Please sign in to comment.