Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First commit for github

  • Loading branch information...
commit fa96c1b02e4e28a758af818a0cf2817a68dc00fc 0 parents
@johnmckerrell authored
188 YAHMS.pde
@@ -0,0 +1,188 @@
+/**
+ * YAHMS - Yet Another Home Management System
+ *
+ * (C) John McKerrell 2011
+ * Probably some open source license when I get around to choosing one
+ */
+
+/**
+Do we have the current time or is it a while since we updated?
+ Update the time - similar choice of sync vs async as below
+
+Do we have any config, or is the config too old?
+ Download a config
+ ?? Does this happen asynchronously or not? First time it might as well be sync but otherwise perhaps not
+ What does config contain?
+ Instructions on which digital pins are: Xbee Serial, Input, Output
+ "On blocks" - blocks of time that a relay should be set to on
+ +1 Hour - could simply be an extra On block
+ Not sure about advance blocks - perhaps also an On block, these and +1 hour could be a special type that you only have one of, so a later +1 or advance discards the previous
+
+Read in all of the local samples
+Check the Xbee for any samples
+
+Iterate over outputs, check whether we’re inside an On block, update pin status accordingly
+
+Check the last time we sent back a sample, if it’s been too long then send the latest values, don’t send a sample that’s too old
+*/
+
+#include <Client.h>
+#include <Dhcp.h>
+#include <Dns.h>
+#include <Ethernet.h>
+#include <IPAddress.h>
+#include <Server.h>
+#include <Udp.h>
+#include <util.h>
+
+#include <NewSoftSerial.h>
+#include <XBee.h>
+
+#include <SPI.h>
+
+#include <Time.h>
+
+#include <b64.h>
+#include <HttpClient.h>
+
+#include <EEPROM.h>
+
+#include <Flash.h>
+
+#include "YAHMS_Defines.h"
+#include "YAHMS_SyncTime.h"
+#include "YAHMS_Config.h"
+#include "YAHMS_Sampler.h"
+#include "YAHMS_Controller.h"
+
+void * operator new(size_t size);
+void operator delete(void * ptr);
+
+void * operator new(size_t size)
+{
+ return malloc(size);
+}
+
+void operator delete(void * ptr)
+{
+ free(ptr);
+}
+
+#ifdef LOGGING
+
+FLASH_STRING(CONFIGURING_ETHERNET,"Configuring ethernet");
+FLASH_STRING(CONFIGURING_ETHERNET_FAILED,"Failed to configure Ethernet using DHCP");
+FLASH_STRING(SYNCING_TIME,"Syncing time");
+FLASH_STRING(SETUP_SAMPLER,"Setup sampler");
+FLASH_STRING(SETUP_CONTROLLER,"Setup controller");
+FLASH_STRING(CHECK_FOR_CONFIG,"Check for config");
+FLASH_STRING(TAKE_SAMPLES,"TakeSamples");
+FLASH_STRING(UPDATE_OUTPUT_PINS,"Update output pins");
+FLASH_STRING(CHECK_AND_SUBMIT_SAMPLES,"CheckAndSubmitSamples");
+#endif
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = YAHMS_LOCAL_MAC;
+
+extern char outputPins[];
+extern char inputPins[];
+extern char analogPins[];
+
+extern char xbeeRX;
+extern char xbeeTX;
+
+XBee xbee = XBee();
+NewSoftSerial *xbeeSerial = NULL;
+
+void setup() {
+ Serial.begin(9600);
+
+ for( int i = 0; i < NUM_OUTPUT_PINS; ++i ) {
+ outputPins[i] = -1;
+ inputPins[i] = -1;
+ if (i<NUM_ANALOG_PINS) {
+ analogPins[i] = -1;
+ }
+ }
+ // start Ethernet and UDP
+ #ifdef LOGGING
+ CONFIGURING_ETHERNET.println(Serial);
+ #endif
+ if (Ethernet.begin(mac) == 0) {
+ #ifdef LOGGING
+ CONFIGURING_ETHERNET_FAILED.println(Serial);
+ #endif
+ // no point in carrying on, so do nothing forevermore:
+ for(;;)
+ ;
+ }
+ #ifdef LOGGING
+ SYNCING_TIME.println(Serial);
+ #endif
+ SyncTime_setup();
+ #ifdef LOGGING
+ SETUP_SAMPLER.println(Serial);
+ #endif
+ SetupSampler();
+ #ifdef LOGGING
+ SETUP_CONTROLLER.println(Serial);
+ #endif
+ SetupController();
+}
+
+void loop() {
+ /**
+ Do we have the current time or is it a while since we updated?
+ Update the time - similar choice of sync vs async as below
+ -- All handled by the synctime stuff which will do the ntp when necessary
+ */
+ /*
+ Do we have any config, or is the config too old?
+ Download a config
+ ?? Does this happen asynchronously or not? First time it might as well be sync but otherwise perhaps not
+ What does config contain?
+ Instructions on which digital pins are: Xbee Serial, Input, Output
+ "On blocks" - blocks of time that a relay should be set to on
+ +1 Hour - could simply be an extra On block
+ Not sure about advance blocks - perhaps also an On block, these and +1 hour could be a special type that you only have one of, so a later +1 or advance discards the previous
+ */
+ #ifdef LOGGING
+ CHECK_FOR_CONFIG.println(Serial);
+ #endif
+ CheckAndUpdateConfig();
+/*
+
+Read in all of the local samples
+Check the Xbee for any samples
+*/
+ #ifdef LOGGING
+ TAKE_SAMPLES.println(Serial);
+ #endif
+ TakeSamples();
+/*
+
+Iterate over outputs, check whether we’re inside an On block, update pin status accordingly
+*/
+ #ifdef LOGGING
+ UPDATE_OUTPUT_PINS.println(Serial);
+ #endif
+ CheckAndUpdateState();
+/*
+Check the last time we sent back a sample, if it’s been too long then send the latest values, don’t send a sample that’s too old
+*/
+ #ifdef LOGGING
+ CHECK_AND_SUBMIT_SAMPLES.println(Serial);
+ #endif
+ CheckAndSubmitSamples();
+ /*
+ int i = 1;
+ byte *a = (byte*)malloc(sizeof(byte));
+ while (a) {
+ Serial.println(i);
+ a = NULL;
+ a = (byte*)malloc(sizeof(byte));
+ ++i;
+ }
+ */
+}
501 YAHMS_Config.cpp
@@ -0,0 +1,501 @@
+/**
+ * YAHMS - Yet Another Home Management System
+ *
+ * (C) John McKerrell 2011
+ * Probably some open source license when I get around to choosing one
+ */
+
+/**
+Do we have the current time or is it a while since we updated?
+ Update the time - similar choice of sync vs async as below
+
+Do we have any config, or is the config too old?
+ Download a config
+ ?? Does this happen asynchronously or not? First time it might as well be sync but otherwise perhaps not
+ What does config contain?
+ Instructions on which digital pins are: Xbee Serial, Input, Output
+ "On blocks" - blocks of time that a relay should be set to on
+ +1 Hour - could simply be an extra On block
+ Not sure about advance blocks - perhaps also an On block, these and +1 hour could be a special type that you only have one of, so a later +1 or advance discards the previous
+
+Read in all of the local samples
+Check the Xbee for any samples
+
+Iterate over outputs, check whether we’re inside an On block, update pin status accordingly
+
+Check the last time we sent back a sample, if it’s been too long then send the latest values, don’t send a sample that’s too old
+*/
+
+#include "YAHMS_Config.h"
+#include "YAHMS_Controller.h"
+
+#include <Flash.h>
+
+#ifdef LOGGING
+FLASH_STRING(BLANK," ");
+FLASH_STRING(COMMA,",");
+FLASH_STRING(PROG_ON,"ON");
+FLASH_STRING(PROG_OFF,"OFF");
+FLASH_STRING(DOWNLOADING_CONFIG_FROM,"Downloading config from http://");
+FLASH_STRING(HTTP_REQUEST_FAILED,"HTTP request failed. Reported:");
+FLASH_STRING(HTTP_NOT_CONNECTED,"HTTP request couldn't connect");
+FLASH_STRING(VALID_EEPROM_CONTENTS,"Valid eeprom contents");
+FLASH_STRING(CONFIG_SIZE_IS,"Config size is ");
+FLASH_STRING(XBEE_PINS_CHANGED_TO,"Xbee pins changed to RX=");
+FLASH_STRING(XBEE_PINS_TX," TX=");
+FLASH_STRING(ANALOG_PINS_ARE,"Analog pins are: ");
+FLASH_STRING(INPUT_PINS_ARE,"Input pins are: ");
+FLASH_STRING(OUTPUT_PINS_ARE,"Output pins are: ");
+FLASH_STRING(CONTROL_BLOCKS,"Control Blocks:");
+#endif
+
+ControlBlock *firstControlBlock = NULL, *lastControlBlock = NULL;
+
+boolean configPresent = false;
+unsigned long int configTime = 0;
+
+char outputPins[NUM_OUTPUT_PINS];
+char inputPins[NUM_INPUT_PINS];
+char analogPins[NUM_ANALOG_PINS];
+
+char xbeeRX = -1, xbeeTX = -1;
+
+byte settings[NUM_SETTINGS];
+
+void LogControlBlock(ControlBlock *currentControlBlock) {
+ #ifdef LOGGING
+ Serial.print(currentControlBlock->minute,DEC);
+ Serial.print(BLANK);
+ Serial.print(currentControlBlock->hour,DEC);
+ Serial.print(BLANK);
+ Serial.print(currentControlBlock->day,DEC);
+ Serial.print(BLANK);
+ Serial.print(currentControlBlock->month,DEC);
+ Serial.print(BLANK);
+ Serial.print(currentControlBlock->weekday,DEC);
+ Serial.print(BLANK);
+ Serial.print(currentControlBlock->len,DEC);
+ Serial.print(BLANK);
+ Serial.print(currentControlBlock->pin,DEC);
+ Serial.print(BLANK);
+ Serial.println(currentControlBlock->state?PROG_ON:PROG_OFF);
+ #endif
+}
+
+void CheckAndUpdateConfig() {
+ /*
+ Do we have any config, or is the config too old?
+ Download a config
+ ?? Does this happen asynchronously or not? First time it might as well be sync but otherwise perhaps not
+ What does config contain?
+ Instructions on which digital pins are: Xbee Serial, Input, Output
+ "On blocks" - blocks of time that a relay should be set to on
+ +1 Hour - could simply be an extra On block
+ Not sure about advance blocks - perhaps also an On block, these and +1 hour could be a special type that you only have one of, so a later +1 or advance discards the previous
+ */
+ if (!configPresent || (now()-configTime) > CONFIG_REQUEST_FREQUENCY) {
+ char eepromHeader[5] = { 'Y', 'A', 'H', 'M', 'S' };
+ boolean eepromValid = true, eepromRecent = true;
+ byte i = 0;
+ while (eepromValid) {
+ if (i < 5 && eepromHeader[i] != EEPROM.read(i)) {
+ eepromValid = false;
+ } else if (i<9) {
+ configTime = (configTime * 256) + EEPROM.read(i);
+ } else {
+ break;
+ }
+ ++i;
+ }
+ // Force a download by uncommenting the following line
+ configTime = 0;
+ if (eepromValid) {
+ // If the config is over 5 mins old, refresh it
+ if ((now()-configTime) > 300) {
+ eepromRecent = false;
+ }
+ } else {
+ eepromRecent = false;
+ }
+ // Download the config file from http://bubblino.com/<MAC Address>
+ Client c;
+ HttpClient http(c);
+ if (!eepromRecent) {
+ char configPath[31] = "/api/c/";
+ //configPath[0] = '/api/c/';
+ const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ // Append the MAC address
+ for (int i =0; i < 6; i++)
+ {
+ configPath[7+(2*i)] = hexDigits[(mac[i] >> 4) & 0x0f];
+ configPath[8+(2*i)] = hexDigits[mac[i] & 0x0f];
+ }
+ // And now the config version number
+ configPath[19] = '/';
+ configPath[20] = hexDigits[YAHMS_API_VERSION];
+ // And finally append the timestamp for when we last updated the config
+ configPath[21] = '/';
+ for (int i =0; i<4; ++i)
+ {
+ unsigned long int timeDigit = configTime >> (8 * (3-i));
+ configPath[22+(2*i)] = hexDigits[(timeDigit >> 4) & 0x0f];
+ configPath[23+(2*i)] = hexDigits[timeDigit & 0x0f];
+ }
+ configPath[30] = '\0';
+
+ #ifdef LOGGING
+ Serial.print(DOWNLOADING_CONFIG_FROM);
+ Serial.print(YAHMS_SERVER);
+ Serial.println(configPath);
+ #endif
+ if (http.get(YAHMS_SERVER, 80, configPath, USERAGENT, NULL) == 0)
+ {
+ // Request sent okay
+ http.finishRequest();
+
+ // Look for the response, and specifically pull out the content-length header
+ unsigned long timeoutStart = millis();
+ char* current;
+
+ int err = http.responseStatusCode();
+ if ((err >= 200) && (err < 300))
+ {
+ // We've got a successful response
+ // And we're not interested in any of the headers
+ err = http.skipResponseHeaders();
+ if (err >= 0)
+ {
+ // We're onto the body now
+
+ // Store the config in EEPROM, in the format:
+ // 00-04: IP address of remote server (e.g. search.twitter.com)
+ // 05 : Length of following remote server name
+ // 06- : remote server name (e.g. string "search.twitter.com")
+ // : Length of following path, etc. in URL
+ // : path, etc. for URL
+ unsigned long int i =0;
+ char c = '\0';
+ while ( (i < http.contentLength()) && (i < 512) && ( (millis() - timeoutStart) < (RESPONSETIMEOUT*1000) ))
+ {
+ if (http.available())
+ {
+ // We've got some data to read
+ c = http.read();
+ if (i < 5) {
+ if (c != eepromHeader[i]) {
+ break;
+ }
+ if (i == 4) {
+ configTime = now();
+ for( int j = 0; j < 5; ++j ) {
+ EEPROM.write(j,eepromHeader[j]);
+ }
+ unsigned long outputTime = configTime;
+ // Write out digits from right to left
+ for (int j =3; j >= 0; --j)
+ {
+ unsigned long int timeDigit = outputTime & 0xFF;
+ outputTime /= 256;
+ EEPROM.write(j+5,timeDigit);
+ }
+
+ }
+ } else {
+ // Store it in the EEPROM
+ // Leaving a gap to store the size
+ // But remember that i will be 5 when we start writing
+ // the EEPROM start position needs to be 5 + 4 + 4
+ EEPROM.write(8 + i, c);
+ // And reset the timeout counter
+ timeoutStart = millis();
+ }
+ ++i;
+ }
+ else
+ {
+ // We haven't got any data, so lets pause to allow some to arrive
+ delay(1000);
+ }
+ }
+ if (i > 0) {
+ // Add a new-line to make sure we parse all the numbers
+ EEPROM.write(13 + i, '\n');
+ ++i;
+ for (int j = 3; j >= 0; --j) {
+ unsigned long int sizeDigit = i & 0xFF;
+ i /= 256;
+ EEPROM.write(j+9,sizeDigit);
+ }
+ }
+ eepromValid = true;
+ }
+ }
+ else
+ {
+ // This isn't a successful response
+ #ifdef LOGGING
+ Serial.println(HTTP_REQUEST_FAILED);
+ Serial.println(err);
+ #endif
+ }
+
+ http.stop();
+ c.stop();
+ }
+ #ifdef LOGGING
+ else
+ {
+ // else hope that the last config was okay
+ Serial.println(HTTP_NOT_CONNECTED);
+ }
+ #endif
+ }
+ if (eepromValid) {
+ configTime = now();
+ #ifdef LOGGING
+ Serial.println(VALID_EEPROM_CONTENTS);
+ #endif
+ configPresent = true;
+ unsigned long int configSize= 0;
+ for (int i = 9; i < 13; ++i) {
+ configSize = (256 * configSize) + EEPROM.read(i);
+ }
+ #ifdef LOGGING
+ Serial.print(CONFIG_SIZE_IS);
+ Serial.println(configSize);
+ #endif
+ ControlBlock *currentControlBlock = NULL;
+ while (firstControlBlock) {
+ currentControlBlock = firstControlBlock;
+ firstControlBlock = firstControlBlock->next;
+ free(currentControlBlock);
+ }
+
+ // Now parse the EEPROM contents line by line
+ int i = 0;
+ boolean xbeeChanged = false;
+ currentControlBlock = NULL;
+ int controlBlockValues[8];
+ boolean startedControlBlock = false;
+ int intVal = -1;
+ int intIndex = 0;
+ byte c = '\0';
+ char lineMode = '\0';
+ while (i < configSize+13) {
+ c = EEPROM.read(i);
+ if (i < 13) {
+ // Ignore it
+ } else if (isdigit(c)) {
+ if (lineMode == 'C' && !startedControlBlock) {
+ memset(controlBlockValues,-1,sizeof(controlBlockValues));
+ startedControlBlock = true;
+ //Serial.println("Initialising controlBlockValues");
+ }
+ if (intVal == -1)
+ intVal = 0;
+ intVal = (10*intVal) + (c - '0');
+ } else if (lineMode == '\0') {
+ //Serial.print("lineMode=");
+ lineMode = c;
+ //Serial.println(lineMode);
+ intVal = -1;
+ intIndex = 0;
+ } else if (c == '*' && lineMode == 'C') {
+ if (intIndex < sizeof(controlBlockValues)) {
+ /*
+ Serial.print("Storing controlBlockValues[");
+ Serial.print(intIndex);
+ Serial.print("]=");
+ Serial.println(-1);
+ */
+ controlBlockValues[intIndex] = -1;
+ }
+ intVal = -1;
+ ++intIndex;
+ } else if (intVal != -1) {
+ switch(lineMode) {
+ case 'S':
+ if (intIndex < NUM_SETTINGS) {
+ settings[intIndex] = intVal;
+ Serial.print("Storing setting ");
+ Serial.print(intIndex);
+ Serial.print(" as ");
+ Serial.println(intVal);
+ }
+ break;
+ case 'A':
+ // Analog pins:
+ if (intIndex < NUM_ANALOG_PINS) {
+ analogPins[intIndex] = intVal;
+ }
+ break;
+ case 'I':
+ // Digital pins to set as input
+ if (intIndex < NUM_INPUT_PINS) {
+ inputPins[intIndex] = intVal;
+ }
+ break;
+ case 'O':
+ // Digital pins set as output
+ if (intIndex < NUM_OUTPUT_PINS) {
+ outputPins[intIndex] = intVal;
+ }
+ break;
+ case 'X':
+ // Pins to use for Xbee
+ if (intIndex == 0 && xbeeRX != intVal) {
+ xbeeChanged = true;
+ xbeeRX = intVal;
+ } else if (intIndex == 1 && xbeeTX != intVal) {
+ xbeeChanged = true;
+ xbeeTX = intVal;
+ }
+ break;
+ case 'C':
+ if (intIndex < sizeof(controlBlockValues)) {
+ /*
+ Serial.print("Storing controlBlockValues[");
+ Serial.print(intIndex);
+ Serial.print("]=");
+ Serial.println(intVal);
+ */
+ controlBlockValues[intIndex] = intVal;
+ }
+ break;
+ }
+ intVal = -1;
+ ++intIndex;
+ }
+ if (c == '\n' || c == '\r' ) {
+ switch(lineMode) {
+ case 'S':
+ for(int i = intIndex; i < NUM_SETTINGS; ++i) {
+ settings[i] = 0;
+ }
+ break;
+ case 'A':
+ for(int i = intIndex; i < NUM_ANALOG_PINS; ++i) {
+ analogPins[i] = -1;
+ }
+ break;
+ case 'I':
+ for(int i = intIndex; i < NUM_INPUT_PINS; ++i) {
+ inputPins[i] = -1;
+ }
+ break;
+ case 'O':
+ for(int i = intIndex; i < NUM_OUTPUT_PINS; ++i) {
+ outputPins[i] = -1;
+ }
+ break;
+ case 'C':
+ // Check that the length of the time block is greater than zero
+ // and that the pin that's being turned on is zero or more
+ if (controlBlockValues[5] > 0 && controlBlockValues[6] >= 0) {
+ currentControlBlock = (ControlBlock*)malloc(sizeof(ControlBlock));
+ // Copy the integer values into a ControlBlock
+ currentControlBlock->minute = controlBlockValues[0];
+ currentControlBlock->hour = controlBlockValues[1];
+ currentControlBlock->day = controlBlockValues[2];
+ currentControlBlock->month = controlBlockValues[3];
+ currentControlBlock->weekday = controlBlockValues[4];
+ currentControlBlock->len = controlBlockValues[5];
+ currentControlBlock->pin = controlBlockValues[6];
+ currentControlBlock->state = controlBlockValues[7];
+ if (lastControlBlock) {
+ lastControlBlock->next = currentControlBlock;
+ }
+ currentControlBlock->next = NULL;
+ lastControlBlock = currentControlBlock;
+ if (!firstControlBlock) {
+ firstControlBlock = currentControlBlock;
+ }
+ } else {
+ /*
+ Serial.println("Not storing controlBlockValues");
+ Serial.print("5=");
+ Serial.println(controlBlockValues[5]);
+ Serial.print("6=");
+ Serial.println(controlBlockValues[6]);
+ */
+ //Serial.println(controlBlockValues);
+ }
+ break;
+ }
+ lineMode = '\0';
+ startedControlBlock = false;
+ }
+ ++i;
+ }
+ // We don't need to check whether we're part-way through a number
+ // here because we add a new-line above
+
+ if (xbeeChanged) {
+ #ifdef LOGGING
+ Serial.print(XBEE_PINS_CHANGED_TO);
+ Serial.print(xbeeRX,DEC);
+ Serial.print(XBEE_PINS_TX);
+ Serial.println(xbeeTX,DEC);
+ #endif
+
+ NewSoftSerial *serial = new NewSoftSerial(xbeeRX,xbeeTX);
+ if (xbeeSerial)
+ delete xbeeSerial;
+ xbeeSerial = serial;
+ xbee.setSerial(serial);
+ xbee.begin(9600);
+ }
+ #ifdef LOGGING
+ Serial.print(ANALOG_PINS_ARE);
+ for(int i = 0; i < NUM_ANALOG_PINS; ++i) {
+ if (i > 0) {
+ Serial.print(COMMA);
+ }
+ Serial.print(analogPins[i],DEC);
+ }
+ Serial.println();
+ Serial.print(INPUT_PINS_ARE);
+ #endif
+ for(int i = 0; i < NUM_INPUT_PINS; ++i) {
+ #ifdef LOGGING
+ if (i > 0) {
+ Serial.print(COMMA);
+ }
+ Serial.print(inputPins[i],DEC);
+ #endif
+ if (inputPins[i]>0) {
+ pinMode(inputPins[i],INPUT);
+ }
+ }
+ #ifdef LOGGING
+ Serial.println();
+ Serial.print(OUTPUT_PINS_ARE);
+ #endif
+ for(int i = 0; i < NUM_OUTPUT_PINS; ++i) {
+ #ifdef LOGGING
+ if (i > 0) {
+ Serial.print(COMMA);
+ }
+ Serial.print(outputPins[i],DEC);
+ #endif
+ if (outputPins[i]>0) {
+ pinMode(outputPins[i],OUTPUT);
+ //currentPinState[i] = -1;
+ }
+ }
+ #ifdef LOGGING
+ Serial.println();
+ #endif
+
+ currentControlBlock = firstControlBlock;
+ #ifdef LOGGING
+ Serial.println(CONTROL_BLOCKS);
+ while (currentControlBlock) {
+ LogControlBlock(currentControlBlock);
+ currentControlBlock = currentControlBlock->next;
+ }
+ #endif
+ }
+ }
+
+}
73 YAHMS_Config.h
@@ -0,0 +1,73 @@
+/**
+ * YAHMS - Yet Another Home Management System
+ *
+ * (C) John McKerrell 2011
+ * Probably some open source license when I get around to choosing one
+ */
+
+/**
+Do we have the current time or is it a while since we updated?
+ Update the time - similar choice of sync vs async as below
+
+Do we have any config, or is the config too old?
+ Download a config
+ ?? Does this happen asynchronously or not? First time it might as well be sync but otherwise perhaps not
+ What does config contain?
+ Instructions on which digital pins are: Xbee Serial, Input, Output
+ "On blocks" - blocks of time that a relay should be set to on
+ +1 Hour - could simply be an extra On block
+ Not sure about advance blocks - perhaps also an On block, these and +1 hour could be a special type that you only have one of, so a later +1 or advance discards the previous
+
+Read in all of the local samples
+Check the Xbee for any samples
+
+Iterate over outputs, check whether we’re inside an On block, update pin status accordingly
+
+Check the last time we sent back a sample, if it’s been too long then send the latest values, don’t send a sample that’s too old
+*/
+
+#include <Client.h>
+#include <Ethernet.h>
+#include <IPAddress.h>
+
+#include <NewSoftSerial.h>
+#include <XBee.h>
+
+#include <Time.h>
+
+#include <HttpClient.h>
+#include <EEPROM.h>
+
+#include <math.h>
+
+#include "YAHMS_Defines.h"
+
+extern byte mac[];
+extern XBee xbee;
+extern NewSoftSerial *xbeeSerial;
+extern char outputPins[];
+extern byte settings[];
+
+#ifndef YAHMS_CONFIG_H
+#define YAHMS_CONFIG_H
+
+struct ControlBlock {
+ char minute;
+ char hour;
+ char day;
+ char month;
+ char weekday;
+ int len;
+ char pin;
+ boolean state;
+ struct ControlBlock *next;
+};
+
+typedef struct ControlBlock ControlBlock;
+
+#endif
+
+extern ControlBlock *firstControlBlock;
+
+void LogControlBlock(ControlBlock *currentControlBlock);
+void CheckAndUpdateConfig();
109 YAHMS_Controller.cpp
@@ -0,0 +1,109 @@
+#include "YAHMS_Defines.h"
+#include "YAHMS_Controller.h"
+#include "YAHMS_Config.h"
+
+#include <Flash.h>
+
+#ifdef LOGGING
+FLASH_STRING(ACTIVE_CONTROL_BLOCK,"Active Control Block");
+FLASH_STRING(SETTING_OUTPUTPINS,"Setting outputPins[");
+FLASH_STRING(OUTPUT_PINS_SEP1,"] (");
+FLASH_STRING(OUTPUT_PINS_SEP2,") to ");
+FLASH_STRING(PROG_HIGH,"HIGH");
+FLASH_STRING(PROG_LOW,"LOW");
+#endif
+
+int currentPinState[NUM_OUTPUT_PINS];
+
+void SetupController() {
+ for (int i = 0; i < NUM_OUTPUT_PINS; ++i) {
+ currentPinState[i] = -1;
+ }
+}
+
+void CheckAndUpdateState() {
+ unsigned long int t = now();
+ // Add timezone/DST
+ if (settings[TIME_OFFSET_MINS_SETTING] > 0) {
+ t += settings[TIME_OFFSET_MINS_SETTING]*60;
+ }
+ int minsSinceMidnight = hour(t)*60+minute(t);
+ ControlBlock *currentControlBlock = NULL;
+ for (int i = 0; i < NUM_OUTPUT_PINS; ++i) {
+ //Serial.print("i=");
+ //Serial.println(i);
+ if (outputPins[i] == -1)
+ continue;
+ int pinState = -1;
+ for (currentControlBlock = firstControlBlock;currentControlBlock;currentControlBlock = currentControlBlock->next) {
+ if (currentControlBlock->pin != outputPins[i])
+ continue;
+
+ if (currentControlBlock->day != -1 && currentControlBlock->day != day(t))
+ continue;
+
+ if (currentControlBlock->month != -1 && currentControlBlock->month != month(t))
+ continue;
+
+ if (currentControlBlock->weekday != -1) {
+ if (currentControlBlock->weekday == 8 && (weekday(t) == 1 || weekday(t) == 7) ) {
+ // dow is valid - weekend
+ } else if (currentControlBlock->weekday == 9 && weekday(t) >= 2 && weekday(t) <= 6) {
+ // dow is valid - weekday
+ } else if (currentControlBlock->weekday != weekday(t)) {
+ // dow not valid, skip this one
+ continue;
+ }
+ }
+ if (currentControlBlock->hour != -1) {
+ if (currentControlBlock->minute != -1) {
+ int mins = (currentControlBlock->hour*60)+currentControlBlock->minute;
+ if (mins <= minsSinceMidnight && (mins+currentControlBlock->len) > minsSinceMidnight) {
+ pinState = currentControlBlock->state ? 1 : 0;
+ #ifdef LOGGING
+ Serial.println(ACTIVE_CONTROL_BLOCK);
+ LogControlBlock(currentControlBlock);
+ #endif
+ }
+ } else if (currentControlBlock->hour == hour(t)) {
+ pinState = currentControlBlock->state ? 1 : 0;
+ #ifdef LOGGING
+ Serial.println(ACTIVE_CONTROL_BLOCK);
+ LogControlBlock(currentControlBlock);
+ #endif
+ }
+ } else if (currentControlBlock->minute != -1) {
+ if (currentControlBlock->minute <= minute(t) && (currentControlBlock->minute + currentControlBlock->len) > minute(t)) {
+ pinState = currentControlBlock->state ? 1 : 0;
+ #ifdef LOGGING
+ Serial.println(ACTIVE_CONTROL_BLOCK);
+ LogControlBlock(currentControlBlock);
+ #endif
+ }
+ } else {
+ //pinState = 0;
+ //Serial.println("Turned off by control block:");
+ //LogControlBlock(currentControlBlock);
+ }
+ if (pinState == 0) {
+ // Any "off" blocks override others
+ //Serial.println("Turned off by control block:");
+ break;
+ }
+ }
+ if (pinState == -1)
+ pinState = 0;
+ if (pinState != currentPinState[i]) {
+ #ifdef LOGGING
+ Serial.print(SETTING_OUTPUTPINS);
+ Serial.print(i);
+ Serial.print(OUTPUT_PINS_SEP1);
+ Serial.print(outputPins[i],DEC);
+ Serial.print(OUTPUT_PINS_SEP2);
+ Serial.println(pinState ? PROG_HIGH : PROG_LOW);
+ #endif
+ digitalWrite(outputPins[i], pinState ? HIGH : LOW);
+ currentPinState[i] = pinState;
+ }
+ }
+}
5 YAHMS_Controller.h
@@ -0,0 +1,5 @@
+extern int currentPinState[];
+
+void SetupController();
+
+void CheckAndUpdateState();
28 YAHMS_Defines.h
@@ -0,0 +1,28 @@
+#define YAHMS_API_VERSION 1
+#define USERAGENT "YAHMS/0.1"
+#define YAHMS_SERVER "yahms.johnmckerrell.com"
+#define RESPONSETIMEOUT 30
+#define LOGGING
+
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+#define NUM_OUTPUT_PINS 60
+#define NUM_INPUT_PINS 60
+#define NUM_ANALOG_PINS 20
+#else
+#define NUM_OUTPUT_PINS 10
+#define NUM_INPUT_PINS 1
+#define NUM_ANALOG_PINS 6
+#endif
+
+#define NUM_SAMPLES 10
+#define LAST_SAMPLE 1
+#define SAMPLE_SUBMIT_FREQUENCY 60
+#define CONFIG_REQUEST_FREQUENCY 60
+
+#define NUM_XBEE_PINS 5
+#define MAX_XBEE_NODES 3
+
+#define NUM_SETTINGS 1
+#define TIME_OFFSET_MINS_SETTING 0
+
+#include "YAHMS_Local.h"
2  YAHMS_Local.h
@@ -0,0 +1,2 @@
+#define YAHMS_LOCAL_MAC { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+
315 YAHMS_Sampler.cpp
@@ -0,0 +1,315 @@
+/**
+ * YAHMS - Yet Another Home Management System
+ *
+ * (C) John McKerrell 2011
+ * Probably some open source license when I get around to choosing one
+ */
+
+ /**
+Do we have the current time or is it a while since we updated?
+ Update the time - similar choice of sync vs async as below
+
+Do we have any config, or is the config too old?
+ Download a config
+ ?? Does this happen asynchronously or not? First time it might as well be sync but otherwise perhaps not
+ What does config contain?
+ Instructions on which digital pins are: Xbee Serial, Input, Output
+ "On blocks" - blocks of time that a relay should be set to on
+ +1 Hour - could simply be an extra On block
+ Not sure about advance blocks - perhaps also an On block, these and +1 hour could be a special type that you only have one of, so a later +1 or advance discards the previous
+
+Read in all of the local samples
+Check the Xbee for any samples
+
+Iterate over outputs, check whether we’re inside an On block, update pin status accordingly
+
+Check the last time we sent back a sample, if it’s been too long then send the latest values, don’t send a sample that’s too old
+*/
+
+#include "YAHMS_Sampler.h"
+#include <Flash.h>
+
+#ifdef LOGGING
+FLASH_STRING(HAVE_XBEE_PACKET_FROM_NODE,"Have xbee packet from node ");
+FLASH_STRING(XBEE_SAMPLES_FOR_NODE,"Xbee Samples for node ");
+FLASH_STRING(PROG_PIN," pin ");
+FLASH_STRING(SMOOTHED_OUT_TO," smoothed out to ");
+FLASH_STRING(NO_XBEE_PACKET,"No xbee packet");
+FLASH_STRING(PROG_SAMPLE,"Sample ");
+FLASH_STRING(PROG_EQUALS," = ");
+FLASH_STRING(SUBMITTING_SAMPLES_TO,"Submitting samples to http://");
+FLASH_STRING(HTTP_SUBMIT_CONNECTION_FAILED,"HTTP Connection Failed");
+FLASH_STRING(HTTP_SUBMIT_REQUEST_FAILED,"HTTP Request Failed with: ");
+#endif
+FLASH_STRING(CONTENT_LENGTH,"Content-Length: ");
+FLASH_STRING(SAMPLE_CONTENT_TYPE,"Content-Type: application/x-www-form-urlencoded");
+
+unsigned long int lastSampleSubmission = 0;
+byte samplesBeforeSubmit = NUM_SAMPLES * 2;
+
+int analogSamples[NUM_ANALOG_PINS][NUM_SAMPLES];
+char inputSamples[NUM_INPUT_PINS][NUM_SAMPLES];
+int xbeeSamples[MAX_XBEE_NODES][NUM_XBEE_PINS];
+
+void SetupSampler() {
+ for (int i = 0; i < NUM_ANALOG_PINS; ++i) {
+ for (int j = 0; j < NUM_SAMPLES; ++j) {
+ analogSamples[i][j] = -1;
+ }
+ }
+ for (int i = 0; i < NUM_INPUT_PINS; ++i) {
+ for (int j = 0; j < NUM_SAMPLES; ++j) {
+ inputSamples[i][j] = -1;
+ }
+ }
+ for (int i = 0; i < MAX_XBEE_NODES; ++i) {
+ for (int j = 0; j < NUM_XBEE_PINS; ++j) {
+ xbeeSamples[i][j] = -1;
+ }
+ }
+}
+
+void TakeSamples() {
+ boolean loggedSample = false;
+ for (int i = 0; i < NUM_ANALOG_PINS; ++i) {
+ loggedSample = false;
+ if (analogPins[i] == -1) {
+ continue;
+ }
+ for (int j = 0; j < NUM_SAMPLES; ++j) {
+ if ((j == LAST_SAMPLE || analogSamples[i][j] == -1) && ! loggedSample) {
+ analogSamples[i][j] = analogRead(analogPins[i]);
+ loggedSample = true;
+ } else if (analogSamples[i][LAST_SAMPLE] != -1 && j < LAST_SAMPLE) {
+ // Shift samples down the array
+ analogSamples[i][j] = analogSamples[i][j+1];
+ }
+ }
+ }
+ for (int i = 0; i < NUM_INPUT_PINS; ++i) {
+ loggedSample = false;
+ for (int j = 0; j < NUM_SAMPLES; ++j) {
+ if ((j == LAST_SAMPLE || inputSamples[i][j] == -1) && ! loggedSample) {
+ inputSamples[i][j] = digitalRead(analogPins[i]);
+ loggedSample = true;
+ } else if (inputSamples[i][LAST_SAMPLE] != -1 && j < LAST_SAMPLE) {
+ // Shift samples down the array
+ inputSamples[i][j] = inputSamples[i][j+1];
+ }
+ }
+ }
+ if (xbee.readPacket(5000)) {
+ XBeeResponse response = xbee.getResponse();
+ if (response.getApiId() == RX_16_IO_RESPONSE) {
+ Rx16IoSampleResponse ioSample;
+ xbee.getResponse().getRx16IoSampleResponse(ioSample);
+
+ byte xbeeNode = ioSample.getRemoteAddress16();
+ #ifdef LOGGING
+ Serial.print(HAVE_XBEE_PACKET_FROM_NODE);
+ Serial.println(xbeeNode,DEC);
+ #endif
+ if (xbeeNode < MAX_XBEE_NODES) {
+ for (int i = 0; i < NUM_XBEE_PINS; ++i ) {
+ int sampleSize = ioSample.getSampleSize();
+ if (ioSample.isAnalogEnabled(i)) {
+ int samples[sampleSize];
+ for (int k = 0; k < sampleSize; ++k) {
+ samples[k] = ioSample.getAnalog(i, k);
+ }
+ xbeeSamples[xbeeNode][i] = smoothSamples(samples,sampleSize);
+ #ifdef LOGGING
+ Serial.print(XBEE_SAMPLES_FOR_NODE);
+ Serial.print(xbeeNode,DEC);
+ Serial.print(PROG_PIN);
+ Serial.print(i);
+ Serial.print(SMOOTHED_OUT_TO);
+ Serial.println(xbeeSamples[xbeeNode][i]);
+ #endif
+ } else if (ioSample.isDigitalEnabled(i)) {
+ if (xbeeSamples[xbeeNode][i] == -1)
+ xbeeSamples[xbeeNode][i] = 0;
+ for (int k = 0; k < sampleSize; ++k) {
+ xbeeSamples[xbeeNode][i] += ioSample.getAnalog(i, k) ? 1 : 0;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ #ifdef LOGGING
+ Serial.println(NO_XBEE_PACKET);
+ #endif
+ }
+}
+
+void CheckAndSubmitSamples() {
+ if (samplesBeforeSubmit > 0) {
+ --samplesBeforeSubmit;
+ return;
+ }
+ if ((now() - lastSampleSubmission) < SAMPLE_SUBMIT_FREQUENCY) {
+ return;
+ }
+
+ int submitSamples[NUM_ANALOG_PINS];
+
+ int contentLength = 0;
+ for (int i = 0; i < NUM_ANALOG_PINS; ++i) {
+ submitSamples[i] = -1;
+ if (analogPins[i] == -1) {
+ continue;
+ }
+ submitSamples[i] = smoothSamples(analogSamples[i], NUM_SAMPLES);
+ #ifdef LOGGING
+ Serial.print(PROG_SAMPLE);
+ Serial.print(i);
+ Serial.print(PROG_EQUALS);
+ Serial.println(submitSamples[i]);
+ #endif
+ if (submitSamples[i] != -1) {
+ contentLength += 7; // A0=FFF& or A10=FFF&
+ if (analogPins[i] >= 10 )
+ ++contentLength;
+ }
+ }
+
+ for (int i = 0; i < MAX_XBEE_NODES; ++i) {
+ for (int j = 0; j < NUM_XBEE_PINS; ++j) {
+ if (xbeeSamples[i][j] != -1) {
+ // X0P1=FFF&
+ contentLength += 9;
+ }
+ }
+ }
+
+
+ Client c;
+ HttpClient http(c);
+
+ char configPath[22] = "/api/s/";
+ const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ const unsigned long int divisors [4] = { 0xFFFFFF, 0xFFFF, 0xFF, 0x01 };
+ // Append the MAC address
+ for (int i =0; i < 6; i++)
+ {
+ configPath[7+(2*i)] = hexDigits[(mac[i] >> 4) & 0x0f];
+ configPath[8+(2*i)] = hexDigits[mac[i] & 0x0f];
+ }
+ // And now the config version number
+ configPath[19] = '/';
+ configPath[20] = hexDigits[YAHMS_API_VERSION];
+ // And finally append the timestamp for when we last updated the config
+ configPath[21] = '\0';
+
+ #ifdef LOGGING
+ Serial.print(SUBMITTING_SAMPLES_TO);
+ Serial.print(YAHMS_SERVER);
+ Serial.println(configPath);
+ #endif
+ if (http.post(YAHMS_SERVER, 80, configPath, USERAGENT, NULL) == 0)
+ {
+ // Request sent okay
+ #ifdef LOGGING
+ Serial.print(CONTENT_LENGTH);
+ Serial.println(contentLength);
+ #endif
+ http.print(CONTENT_LENGTH);
+ http.println(contentLength);
+ http.println(SAMPLE_CONTENT_TYPE);
+ http.finishRequest();
+
+ for (int i = 0; i < NUM_ANALOG_PINS; ++i) {
+ if (submitSamples[i] != -1) {
+ http.print("A");
+ http.print(analogPins[i],DEC);
+ http.print("=");
+ http.print(hexDigits[submitSamples[i]>>8 & 0x0F]);
+ http.print(hexDigits[submitSamples[i]>>4 & 0x0F]);
+ http.print(hexDigits[submitSamples[i] & 0x0F]);
+ http.print("&");
+ }
+ }
+
+ for (int i = 0; i < MAX_XBEE_NODES; ++i) {
+ for (int j = 0; j < NUM_XBEE_PINS; ++j) {
+ if (xbeeSamples[i][j] != -1) {
+ // X0P1=FFF&
+ http.print("X");
+ http.print(i);
+ http.print("P");
+ http.print(j);
+ http.print("=");
+ http.print(hexDigits[xbeeSamples[i][j]>>8 & 0x0F]);
+ http.print(hexDigits[xbeeSamples[i][j]>>4 & 0x0F]);
+ http.print(hexDigits[xbeeSamples[i][j] & 0x0F]);
+ http.print("&");
+ xbeeSamples[i][j] = -1;
+ }
+ }
+ }
+
+ int err = http.responseStatusCode();
+ if ((err >= 200) && (err < 300))
+ {
+ // We've got a successful response
+ // And we're not interested in any of the headers
+ err = http.skipResponseHeaders();
+ if (err >= 0)
+ {
+ }
+ } else {
+ #ifdef LOGGING
+ Serial.print(HTTP_SUBMIT_REQUEST_FAILED);
+ Serial.println(err);
+ #endif
+
+ }
+ http.stop();
+ c.stop();
+ } else {
+ #ifdef LOGGING
+ Serial.println(HTTP_SUBMIT_CONNECTION_FAILED);
+ #endif
+ }
+
+ // Have to assume it worked really
+ lastSampleSubmission = now();
+
+}
+
+int smoothSamples( int samples[], int numSamples) {
+
+ int diff = 0, total = 0, average = 0, number = 0, deviation = 0;
+
+ for (int j = 0; j < numSamples; ++j) {
+ if (samples[j] != -1) {
+ total += samples[j];
+ ++number;
+ }
+ }
+ if (number > 0) {
+ average = total / number;
+ for (int j = 0; j < numSamples; ++j) {
+ if (samples[j] != -1) {
+ deviation += abs(samples[j] - average);
+ }
+ }
+ deviation = deviation / number;
+ number = 0;
+ total = 0;
+ for (int j = 0; j < numSamples; ++j) {
+ if (samples[j] != -1) {
+ diff = abs(samples[j] - average);
+ if ( deviation == 0 || diff <= deviation) {
+ total += samples[j];
+ ++number;
+ }
+ }
+ }
+ average = total / number;
+ return average;
+ } else {
+ return -1;
+ }
+}
18 YAHMS_Sampler.h
@@ -0,0 +1,18 @@
+
+#include "YAHMS_Defines.h"
+#include "YAHMS_Config.h"
+
+extern char outputPins[];
+extern char inputPins[];
+extern char analogPins[];
+
+extern char xbeeRX;
+extern char xbeeTX;
+
+void SetupSampler();
+
+void TakeSamples();
+
+void CheckAndSubmitSamples();
+
+int smoothSamples( int samples[], int numSamples);
122 YAHMS_SyncTime.cpp
@@ -0,0 +1,122 @@
+/*
+
+ Udp NTP Client
+
+ Get the time from a Network Time Protocol (NTP) time server
+ Demonstrates use of UDP sendPacket and ReceivePacket
+ For more on NTP time servers and the messages needed to communicate with them,
+ see http://en.wikipedia.org/wiki/Network_Time_Protocol
+
+ created 4 Sep 2010
+ by Michael Margolis
+ modified 17 Sep 2010
+ by Tom Igoe
+
+ This code is in the public domain.
+
+ */
+
+#include "YAHMS_SyncTime.h"
+
+#include <Flash.h>
+
+#ifdef LOGGING
+FLASH_STRING(SECONDS_SINCE_1900,"Seconds since Jan 1 1900 = ");
+FLASH_STRING(UNIX_TIME,"Unix time = ");
+#endif
+
+#define LOCALPORT 8888
+//unsigned int localPort = 8888; // local port to listen for UDP packets
+
+IPAddress timeServer(91,189,94,4); // time.ubuntu.com NTP server
+
+#define NTP_PACKET_SIZE 48
+//const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
+
+byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
+
+// A UDP instance to let us send and receive packets over UDP
+UDP Udp;
+
+void SyncTime_setup()
+{
+ Udp.begin(LOCALPORT);
+ setSyncProvider(getNtpTime);
+ while(timeStatus()== timeNotSet)
+ ; // wait until the time is set by the sync provider
+}
+
+
+unsigned long getNtpTime()
+{
+ sendNTPpacket(timeServer); // send an NTP packet to a time server
+
+ // wait to see if a reply is available
+ delay(1000);
+ if ( Udp.parsePacket() ) {
+ // We've received a packet, read the data from it
+ Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
+
+ //the timestamp starts at byte 40 of the received packet and is four bytes,
+ // or two words, long. First, esxtract the two words:
+
+ unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
+ unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
+ // combine the four bytes (two words) into a long integer
+ // this is NTP time (seconds since Jan 1 1900):
+ unsigned long secsSince1900 = highWord << 16 | lowWord;
+ #ifdef LOGGING
+ Serial.print("Seconds since Jan 1 1900 = " );
+ Serial.println(secsSince1900);
+ #endif
+
+ // now convert NTP time into everyday time:
+ // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
+ const unsigned long seventyYears = 2208988800UL;
+ // subtract seventy years:
+ unsigned long epoch = secsSince1900 - seventyYears;
+ // print Unix time:
+ #ifdef LOGGING
+ Serial.print(UNIX_TIME);
+ Serial.println(epoch);
+ #endif
+ return epoch;
+ }
+
+ return 0; // return 0 if unable to get the time
+}
+
+// send an NTP request to the time server at the given address
+unsigned long sendNTPpacket(IPAddress& address)
+{
+ // set all bytes in the buffer to 0
+ memset(packetBuffer, 0, NTP_PACKET_SIZE);
+ // Initialize values needed to form NTP request
+ // (see URL above for details on the packets)
+ packetBuffer[0] = 0b11100011; // LI, Version, Mode
+ packetBuffer[1] = 0; // Stratum, or type of clock
+ packetBuffer[2] = 6; // Polling Interval
+ packetBuffer[3] = 0xEC; // Peer Clock Precision
+ // 8 bytes of zero for Root Delay & Root Dispersion
+ packetBuffer[12] = 49;
+ packetBuffer[13] = 0x4E;
+ packetBuffer[14] = 49;
+ packetBuffer[15] = 52;
+
+ // all NTP fields have been given values, now
+ // you can send a packet requesting a timestamp:
+ Udp.beginPacket(address, 123); //NTP requests are to port 123
+ Udp.write(packetBuffer,NTP_PACKET_SIZE);
+ Udp.endPacket();
+}
+
+
+
+
+
+
+
+
+
+
+
26 YAHMS_SyncTime.h
@@ -0,0 +1,26 @@
+/*
+
+ Udp NTP Client
+
+ Get the time from a Network Time Protocol (NTP) time server
+ Demonstrates use of UDP sendPacket and ReceivePacket
+ For more on NTP time servers and the messages needed to communicate with them,
+ see http://en.wikipedia.org/wiki/Network_Time_Protocol
+
+ created 4 Sep 2010
+ by Michael Margolis
+ modified 17 Sep 2010
+ by Tom Igoe
+
+ This code is in the public domain.
+
+ */
+
+#include <SPI.h>
+#include <Ethernet.h>
+#include <Udp.h>
+#include <Time.h>
+
+void SyncTime_setup();
+unsigned long getNtpTime();
+unsigned long sendNTPpacket(IPAddress& address);
Please sign in to comment.
Something went wrong with that request. Please try again.