Watt Eye has a simple purpose - monitor pulses that comes from the home electric meter, measure the interval between the pulses and compute the real-time energy being consumed, broadcast that onto the network using UDP packets so that CouchCalendar has something to do and display, and publish the data to a web server, where it can be used (graphed or placed into a db).

Dependencies:   IniManager mbed HTTPClient SWUpdate StatisticQueue mbed-rtos NTPClient Watchdog SW_HTTPServer EthernetInterface TimeInterface

Features:

  • Reads the time between pulses (which the home electric meter emits as IR for each Watt consumed).
  • Once every 5 seconds, it broadcasts this via UDP to the network, so other nodes can listen to this real-time data.
  • Once every 5 minutes, it posts statistics to a web server for logging.
  • Once a day, it checks the web server to see if there is a SW update (and if so it downloads, installs, and activates it).
  • It syncs to a configured NTP server, but doesn't actually use this information for anything.
  • It hosts a web server, but this is not being used at this time.

So, this is a rather expensive piece of hardware to monitor a single pulse, and yet it is easy to imagine enhancing this:

  • Read the water meter in a similar manner.
  • Read the gas meter in a similar manner.

And even then, there will be many left-over port pins for other uses.

Committer:
WiredHome
Date:
Sat Mar 02 23:15:49 2019 +0000
Revision:
4:0a7567195e4b
Parent:
3:5c3ba12d155b
Checkpoint publish before changes.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 0:a4887b672ac6 1 #include "mbed.h" // v83, RTOS 38
WiredHome 0:a4887b672ac6 2 #include "RawSerial.h" // ?
WiredHome 0:a4887b672ac6 3
WiredHome 0:a4887b672ac6 4 // My libs
WiredHome 0:a4887b672ac6 5 #include "TimeInterface.h" // ver 3
WiredHome 0:a4887b672ac6 6 #include "HTTPClient.h" // ver 0
WiredHome 0:a4887b672ac6 7 #include "IniManager.h" // ver 9
WiredHome 0:a4887b672ac6 8 #include "SWUpdate.h" // ver 17
WiredHome 0:a4887b672ac6 9 #include "Watchdog.h" // ver 2
WiredHome 0:a4887b672ac6 10 #include "StatisticQueue.h"
WiredHome 0:a4887b672ac6 11
WiredHome 0:a4887b672ac6 12 //#define WIFLY
WiredHome 0:a4887b672ac6 13 #define HW_ADAPTER SMART_BOARD /* Which board are we compiling against? */
WiredHome 0:a4887b672ac6 14
WiredHome 0:a4887b672ac6 15 #ifdef WIFLY
WiredHome 0:a4887b672ac6 16 #include "WiflyInterface.h"
WiredHome 0:a4887b672ac6 17 #else
WiredHome 3:5c3ba12d155b 18 #include "EthernetInterface.h" // ver 51
WiredHome 0:a4887b672ac6 19 #endif
WiredHome 0:a4887b672ac6 20
WiredHome 0:a4887b672ac6 21 #include "SW_HTTPServer.h"
WiredHome 0:a4887b672ac6 22
WiredHome 0:a4887b672ac6 23 extern "C" void mbed_reset();
WiredHome 0:a4887b672ac6 24
WiredHome 0:a4887b672ac6 25 //#define DEBUG "MAIN"
WiredHome 0:a4887b672ac6 26 #include <cstdio>
WiredHome 0:a4887b672ac6 27 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
WiredHome 0:a4887b672ac6 28 #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:a4887b672ac6 29 #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:a4887b672ac6 30 #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:a4887b672ac6 31 #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:a4887b672ac6 32 #else
WiredHome 0:a4887b672ac6 33 #define DBG(x, ...)
WiredHome 0:a4887b672ac6 34 #define WARN(x, ...)
WiredHome 0:a4887b672ac6 35 #define ERR(x, ...)
WiredHome 0:a4887b672ac6 36 #define INFO(x, ...)
WiredHome 0:a4887b672ac6 37 #endif
WiredHome 0:a4887b672ac6 38
WiredHome 0:a4887b672ac6 39 #define TIME_TO_CHECK_SW_UPDATE (60*60) /* once per hour */
WiredHome 0:a4887b672ac6 40
WiredHome 0:a4887b672ac6 41 EthernetInterface eth;
WiredHome 0:a4887b672ac6 42 Mutex eth_mutex;
WiredHome 0:a4887b672ac6 43
WiredHome 0:a4887b672ac6 44 Watchdog wd;
WiredHome 0:a4887b672ac6 45
WiredHome 0:a4887b672ac6 46 RawSerial pc(USBTX, USBRX);
WiredHome 0:a4887b672ac6 47 LocalFileSystem local("local");
WiredHome 0:a4887b672ac6 48 INI ini;
WiredHome 0:a4887b672ac6 49
WiredHome 0:a4887b672ac6 50 DigitalOut linkup(p26);
WiredHome 0:a4887b672ac6 51 DigitalOut linkdata(p25);
WiredHome 0:a4887b672ac6 52
WiredHome 0:a4887b672ac6 53 TimeInterface ntp;
WiredHome 0:a4887b672ac6 54 HTTPClient http;
WiredHome 0:a4887b672ac6 55
WiredHome 0:a4887b672ac6 56 // Keep a sample every 5 s for 5 minutes
WiredHome 0:a4887b672ac6 57 // 12 samples / min * 5 min => 60 samples
WiredHome 0:a4887b672ac6 58 #define SampleInterval_Sec 5
WiredHome 0:a4887b672ac6 59 #define SampleHistory_5m (60)
WiredHome 0:a4887b672ac6 60 StatisticQueue stats5s(SampleHistory_5m);
WiredHome 0:a4887b672ac6 61
WiredHome 0:a4887b672ac6 62 // Keep 5 minute data for 1 day
WiredHome 0:a4887b672ac6 63 // 12 samples / hour * 24 hours => 288
WiredHome 0:a4887b672ac6 64 #define SampleInterval_Min 5
WiredHome 0:a4887b672ac6 65 #define SampleHistory_1d 288
WiredHome 0:a4887b672ac6 66 StatisticQueue stats5m(SampleHistory_1d);
WiredHome 0:a4887b672ac6 67
WiredHome 0:a4887b672ac6 68 const char * PROG_INFO = "Watt Eye: " __DATE__ ", " __TIME__;
WiredHome 0:a4887b672ac6 69 const char * iniFile = "/local/WattEye.ini";
WiredHome 0:a4887b672ac6 70
WiredHome 0:a4887b672ac6 71
WiredHome 0:a4887b672ac6 72 DigitalOut PulseIndicator(LED1);
WiredHome 0:a4887b672ac6 73 DigitalOut UDPSendIndicator(LED2);
WiredHome 0:a4887b672ac6 74 DigitalOut URLSendIndicator(LED3);
WiredHome 0:a4887b672ac6 75 PwmOut signOfLife(LED4);
WiredHome 0:a4887b672ac6 76
WiredHome 0:a4887b672ac6 77 InterruptIn event(p15);
WiredHome 0:a4887b672ac6 78 Timer timer;
WiredHome 0:a4887b672ac6 79 Timeout flash;
WiredHome 0:a4887b672ac6 80
WiredHome 0:a4887b672ac6 81 typedef struct
WiredHome 0:a4887b672ac6 82 {
WiredHome 0:a4887b672ac6 83 time_t todClock;
WiredHome 0:a4887b672ac6 84 uint32_t tLastStart;
WiredHome 0:a4887b672ac6 85 uint32_t tLastRise;
WiredHome 0:a4887b672ac6 86 uint16_t Samples10s[30]; // Every 10s for 5 min
WiredHome 0:a4887b672ac6 87 uint16_t Samples10sIndex;
WiredHome 0:a4887b672ac6 88 uint16_t Samples5m[12*24]; // Every 5m for 1 day
WiredHome 0:a4887b672ac6 89 uint16_t Samples5mIndex;
WiredHome 0:a4887b672ac6 90 uint16_t Samples1d[365]; // Every
WiredHome 0:a4887b672ac6 91 uint16_t Samples1dIndex;
WiredHome 0:a4887b672ac6 92 } WattData;
WiredHome 0:a4887b672ac6 93
WiredHome 0:a4887b672ac6 94 typedef struct
WiredHome 0:a4887b672ac6 95 {
WiredHome 0:a4887b672ac6 96 float instantKW;
WiredHome 0:a4887b672ac6 97 float averageKW;
WiredHome 0:a4887b672ac6 98 uint32_t measuredCycles;
WiredHome 0:a4887b672ac6 99 } Atomic_t;
WiredHome 0:a4887b672ac6 100
WiredHome 0:a4887b672ac6 101 Atomic_t PowerSnapshot;
WiredHome 0:a4887b672ac6 102
WiredHome 0:a4887b672ac6 103 typedef struct
WiredHome 0:a4887b672ac6 104 {
WiredHome 0:a4887b672ac6 105 bool init;
WiredHome 0:a4887b672ac6 106 time_t startTimestamp;
WiredHome 0:a4887b672ac6 107 uint64_t tStart;
WiredHome 0:a4887b672ac6 108 uint64_t tLastRise;
WiredHome 0:a4887b672ac6 109 uint64_t tStartSample;
WiredHome 0:a4887b672ac6 110 uint32_t cycles;
WiredHome 0:a4887b672ac6 111 } RawSample_t;
WiredHome 0:a4887b672ac6 112
WiredHome 0:a4887b672ac6 113 RawSample_t RawPowerSample;
WiredHome 0:a4887b672ac6 114
WiredHome 0:a4887b672ac6 115 //uint64_t tElapsedFive;
WiredHome 0:a4887b672ac6 116 //uint32_t cycleFive;
WiredHome 0:a4887b672ac6 117
WiredHome 0:a4887b672ac6 118
WiredHome 0:a4887b672ac6 119
WiredHome 0:a4887b672ac6 120 void SoftwareUpdateCheck(bool force = false)
WiredHome 0:a4887b672ac6 121 {
WiredHome 0:a4887b672ac6 122 static time_t tLastCheck;
WiredHome 0:a4887b672ac6 123 char url[100], name[10];
WiredHome 0:a4887b672ac6 124 time_t tCheck = ntp.time();
WiredHome 0:a4887b672ac6 125
WiredHome 0:a4887b672ac6 126 if (tCheck < tLastCheck)
WiredHome 0:a4887b672ac6 127 force = true; // guard against bad stuff that would prevent updates
WiredHome 0:a4887b672ac6 128
WiredHome 0:a4887b672ac6 129 if ((tCheck - tLastCheck > TIME_TO_CHECK_SW_UPDATE) || force) {
WiredHome 0:a4887b672ac6 130 tLastCheck = tCheck;
WiredHome 0:a4887b672ac6 131 eth_mutex.lock();
WiredHome 0:a4887b672ac6 132 pc.printf("SoftwareUpdateCheck\r\n");
WiredHome 0:a4887b672ac6 133 if (ini.ReadString("SWUpdate", "url", url, sizeof(url))
WiredHome 0:a4887b672ac6 134 && ini.ReadString("SWUpdate", "name", name, sizeof(name))) {
WiredHome 0:a4887b672ac6 135 //pc.printf("SW Check(%s,%s)\r\n", url, name);
WiredHome 0:a4887b672ac6 136 SWUpdate_T su = SoftwareUpdate(url, name, DEFER_REBOOT);
WiredHome 0:a4887b672ac6 137 if (SWUP_OK == su) {
WiredHome 0:a4887b672ac6 138 eth_mutex.unlock();
WiredHome 0:a4887b672ac6 139 pc.printf(" new software installed, restarting...\r\n");
WiredHome 0:a4887b672ac6 140 Thread::wait(3000);
WiredHome 0:a4887b672ac6 141 mbed_reset();
WiredHome 0:a4887b672ac6 142 } else if (SWUP_SAME_VER == su) {
WiredHome 0:a4887b672ac6 143 pc.printf(" no update available.\r\n");
WiredHome 0:a4887b672ac6 144 } else {
WiredHome 0:a4887b672ac6 145 pc.printf(" update failed %04X, http %d\r\n", su, SoftwareUpdateGetHTTPErrorCode());
WiredHome 0:a4887b672ac6 146 }
WiredHome 0:a4887b672ac6 147 } else {
WiredHome 0:a4887b672ac6 148 pc.printf(" can't get info from ini file.\r\n");
WiredHome 0:a4887b672ac6 149 eth_mutex.unlock();
WiredHome 0:a4887b672ac6 150 }
WiredHome 0:a4887b672ac6 151 }
WiredHome 0:a4887b672ac6 152 }
WiredHome 0:a4887b672ac6 153
WiredHome 0:a4887b672ac6 154 void ShowIPAddress(bool show = true)
WiredHome 0:a4887b672ac6 155 {
WiredHome 0:a4887b672ac6 156 char buf[16];
WiredHome 0:a4887b672ac6 157
WiredHome 0:a4887b672ac6 158 if (show)
WiredHome 0:a4887b672ac6 159 sprintf(buf, "%15s", eth.getIPAddress());
WiredHome 0:a4887b672ac6 160 else
WiredHome 0:a4887b672ac6 161 sprintf(buf, "%15s", "---.---.---.---");
WiredHome 0:a4887b672ac6 162 pc.printf("Ethernet connected as %s\r\n", buf);
WiredHome 0:a4887b672ac6 163 }
WiredHome 0:a4887b672ac6 164
WiredHome 0:a4887b672ac6 165
WiredHome 0:a4887b672ac6 166
WiredHome 0:a4887b672ac6 167 bool SyncToNTPServer(void)
WiredHome 0:a4887b672ac6 168 {
WiredHome 0:a4887b672ac6 169 char url[100];
WiredHome 0:a4887b672ac6 170 char tzone[10];
WiredHome 0:a4887b672ac6 171
WiredHome 0:a4887b672ac6 172 if (ini.ReadString("Clock", "timeserver", url, sizeof(url))) {
WiredHome 0:a4887b672ac6 173 ini.ReadString("Clock", "tzoffsetmin", tzone, sizeof(tzone), "0");
WiredHome 0:a4887b672ac6 174
WiredHome 0:a4887b672ac6 175 time_t tls = ntp.get_timelastset();
WiredHome 0:a4887b672ac6 176 //time_t tnow = ntp.time();
WiredHome 0:a4887b672ac6 177 //int32_t tcr = ntp.get_cal();
WiredHome 0:a4887b672ac6 178 eth_mutex.lock();
WiredHome 0:a4887b672ac6 179 pc.printf("NTP update time from (%s)\r\n", url);
WiredHome 0:a4887b672ac6 180 linkdata = true;
WiredHome 0:a4887b672ac6 181 int32_t tzo_min = atoi(tzone);
WiredHome 0:a4887b672ac6 182 ntp.set_tzo_min(tzo_min);
WiredHome 0:a4887b672ac6 183 int res = ntp.setTime(url);
WiredHome 0:a4887b672ac6 184 eth_mutex.unlock();
WiredHome 0:a4887b672ac6 185 linkdata = false;
WiredHome 0:a4887b672ac6 186 if (res == 0) {
WiredHome 0:a4887b672ac6 187 time_t ctTime;
WiredHome 0:a4887b672ac6 188 ctTime = ntp.timelocal();
WiredHome 0:a4887b672ac6 189 pc.printf(" Time set to (UTC): %s\r\n", ntp.ctime(&ctTime));
WiredHome 0:a4887b672ac6 190 return true;
WiredHome 0:a4887b672ac6 191 } else {
WiredHome 0:a4887b672ac6 192 pc.printf("Error %d\r\n", res);
WiredHome 0:a4887b672ac6 193 }
WiredHome 0:a4887b672ac6 194 } else {
WiredHome 0:a4887b672ac6 195 pc.printf("no time server was set\r\n");
WiredHome 0:a4887b672ac6 196 }
WiredHome 0:a4887b672ac6 197 return false;
WiredHome 0:a4887b672ac6 198 }
WiredHome 0:a4887b672ac6 199
WiredHome 0:a4887b672ac6 200 void TransmitEnergy(bool sendNow, float iKW, float min5s, float avg5s, float max5s, float min5m, float avg5m, float max5m)
WiredHome 0:a4887b672ac6 201 {
WiredHome 0:a4887b672ac6 202 char url[100], dest[20], port[8];
WiredHome 0:a4887b672ac6 203 char data[150];
WiredHome 0:a4887b672ac6 204 char myID[50];
WiredHome 0:a4887b672ac6 205 char fullurl[250];
WiredHome 0:a4887b672ac6 206 bool bEU = ini.ReadString("Energy", "url", url, sizeof(url));
WiredHome 0:a4887b672ac6 207 bool bDS = ini.ReadString("Energy", "dest", dest, sizeof(dest));
WiredHome 0:a4887b672ac6 208 bool bPO = ini.ReadString("Energy", "port", port, sizeof(port));
WiredHome 0:a4887b672ac6 209 bool bID = ini.ReadString("Node", "id", myID, sizeof(myID));
WiredHome 0:a4887b672ac6 210
WiredHome 0:a4887b672ac6 211 if (bEU && bDS && bPO && bID) {
WiredHome 0:a4887b672ac6 212 snprintf(data, 150, "ID=%s&iKW=%5.3f&min5s=%5.3f&avg5s=%5.3f&max5s=%5.3f&min5m=%5.3f&avg5m=%5.3f&max5m=%5.3f",
WiredHome 0:a4887b672ac6 213 myID, iKW, min5s, avg5s, max5s, min5m, avg5m, max5m);
WiredHome 0:a4887b672ac6 214 eth_mutex.lock();
WiredHome 0:a4887b672ac6 215 // Send the UDP Broadcast, picked up by a listener
WiredHome 0:a4887b672ac6 216 UDPSendIndicator = true;
WiredHome 0:a4887b672ac6 217 UDPSocket bcast;
WiredHome 0:a4887b672ac6 218 Endpoint ep;
WiredHome 0:a4887b672ac6 219 int h = ep.set_address(dest, atoi(port));
WiredHome 0:a4887b672ac6 220 int i = bcast.bind(atoi(port));
WiredHome 0:a4887b672ac6 221 int j = bcast.set_broadcasting(true);
WiredHome 0:a4887b672ac6 222 int k = bcast.sendTo(ep, data, strlen(data));
WiredHome 0:a4887b672ac6 223 bcast.close();
WiredHome 0:a4887b672ac6 224 UDPSendIndicator = false;
WiredHome 0:a4887b672ac6 225 // On the 5-minute interval, post the data to a specified web server
WiredHome 0:a4887b672ac6 226 if (sendNow && *url) {
WiredHome 0:a4887b672ac6 227 //HTTPClient http;
WiredHome 0:a4887b672ac6 228 char buf[50];
WiredHome 0:a4887b672ac6 229 URLSendIndicator = true;
WiredHome 0:a4887b672ac6 230 snprintf(fullurl, 250, "%s?%s", url, data);
WiredHome 0:a4887b672ac6 231 pc.printf("Contacting %s\r\n", fullurl);
WiredHome 0:a4887b672ac6 232 http.setMaxRedirections(3);
WiredHome 0:a4887b672ac6 233 int x = http.get(fullurl, buf, sizeof(buf));
WiredHome 0:a4887b672ac6 234 URLSendIndicator = false;
WiredHome 0:a4887b672ac6 235 pc.printf(" return: %d\r\n", x);
WiredHome 0:a4887b672ac6 236 }
WiredHome 0:a4887b672ac6 237 eth_mutex.unlock();
WiredHome 0:a4887b672ac6 238 }
WiredHome 0:a4887b672ac6 239 }
WiredHome 0:a4887b672ac6 240
WiredHome 0:a4887b672ac6 241
WiredHome 0:a4887b672ac6 242 /// ShowSignOfLife
WiredHome 0:a4887b672ac6 243 ///
WiredHome 0:a4887b672ac6 244 /// Pulse an LED to indicate a sign of life of the program.
WiredHome 0:a4887b672ac6 245 /// This also has some moderate entertainment value.
WiredHome 0:a4887b672ac6 246 ///
WiredHome 0:a4887b672ac6 247 void ShowSignOfLife()
WiredHome 0:a4887b672ac6 248 {
WiredHome 0:a4887b672ac6 249 #define PI 3.14159265359
WiredHome 0:a4887b672ac6 250 static Timer activityTimer;
WiredHome 0:a4887b672ac6 251 static unsigned int activityStart;
WiredHome 0:a4887b672ac6 252 static bool init;
WiredHome 0:a4887b672ac6 253 static int degrees = 0;
WiredHome 0:a4887b672ac6 254 float v;
WiredHome 0:a4887b672ac6 255
WiredHome 0:a4887b672ac6 256 if (!init) {
WiredHome 0:a4887b672ac6 257 activityTimer.start();
WiredHome 0:a4887b672ac6 258 activityStart = (unsigned int) activityTimer.read_ms();
WiredHome 0:a4887b672ac6 259 init = true;
WiredHome 0:a4887b672ac6 260 }
WiredHome 0:a4887b672ac6 261 if ((unsigned int)activityTimer.read_ms() - activityStart > 20) {
WiredHome 0:a4887b672ac6 262
WiredHome 0:a4887b672ac6 263 v = sin(degrees * PI / 180);
WiredHome 0:a4887b672ac6 264 if (v < 0)
WiredHome 0:a4887b672ac6 265 v = 0;
WiredHome 0:a4887b672ac6 266 signOfLife = v;
WiredHome 0:a4887b672ac6 267 degrees += 5;
WiredHome 0:a4887b672ac6 268 activityStart = (unsigned int) activityTimer.read_ms();
WiredHome 0:a4887b672ac6 269 }
WiredHome 0:a4887b672ac6 270 }
WiredHome 0:a4887b672ac6 271
WiredHome 0:a4887b672ac6 272 void LedOff(void)
WiredHome 0:a4887b672ac6 273 {
WiredHome 0:a4887b672ac6 274 PulseIndicator = 0;
WiredHome 0:a4887b672ac6 275 }
WiredHome 0:a4887b672ac6 276
WiredHome 0:a4887b672ac6 277 void CheckConsoleInput(void)
WiredHome 0:a4887b672ac6 278 {
WiredHome 0:a4887b672ac6 279 if (pc.readable()) {
WiredHome 0:a4887b672ac6 280 int c = pc.getc();
WiredHome 0:a4887b672ac6 281 switch (c) {
WiredHome 0:a4887b672ac6 282 case 'r':
WiredHome 0:a4887b672ac6 283 mbed_reset();
WiredHome 0:a4887b672ac6 284 break;
WiredHome 0:a4887b672ac6 285 case 's':
WiredHome 0:a4887b672ac6 286 SoftwareUpdateCheck(true);
WiredHome 0:a4887b672ac6 287 break;
WiredHome 0:a4887b672ac6 288 case 't':
WiredHome 0:a4887b672ac6 289 SyncToNTPServer();
WiredHome 0:a4887b672ac6 290 break;
WiredHome 0:a4887b672ac6 291 default:
WiredHome 0:a4887b672ac6 292 pc.printf("unknown command '%c'\r\n", c);
WiredHome 1:04ab0a3d07f1 293 case ' ':
WiredHome 1:04ab0a3d07f1 294 case '\r':
WiredHome 1:04ab0a3d07f1 295 case '\n':
WiredHome 0:a4887b672ac6 296 pc.printf("Commands:\r\n"
WiredHome 0:a4887b672ac6 297 " r = reset\r\n"
WiredHome 0:a4887b672ac6 298 " s = software update check\r\n"
WiredHome 0:a4887b672ac6 299 " t = time sync to NTP server\r\n"
WiredHome 0:a4887b672ac6 300 );
WiredHome 0:a4887b672ac6 301 ShowIPAddress();
WiredHome 0:a4887b672ac6 302
WiredHome 0:a4887b672ac6 303 break;
WiredHome 0:a4887b672ac6 304 }
WiredHome 0:a4887b672ac6 305 }
WiredHome 0:a4887b672ac6 306 }
WiredHome 0:a4887b672ac6 307
WiredHome 0:a4887b672ac6 308 bool NetworkIsConnected(void)
WiredHome 0:a4887b672ac6 309 {
WiredHome 0:a4887b672ac6 310 return eth.is_connected();
WiredHome 0:a4887b672ac6 311 }
WiredHome 0:a4887b672ac6 312
WiredHome 0:a4887b672ac6 313 void PulseRisingISR(void)
WiredHome 0:a4887b672ac6 314 {
WiredHome 0:a4887b672ac6 315 uint64_t tNow = timer.read_us();
WiredHome 0:a4887b672ac6 316
WiredHome 0:a4887b672ac6 317 __disable_irq();
WiredHome 0:a4887b672ac6 318 if (!RawPowerSample.init) {
WiredHome 0:a4887b672ac6 319 RawPowerSample.init = true;
WiredHome 0:a4887b672ac6 320 RawPowerSample.cycles = (uint32_t)-1;
WiredHome 0:a4887b672ac6 321 RawPowerSample.tStart = tNow;
WiredHome 0:a4887b672ac6 322 RawPowerSample.tLastRise = tNow;
WiredHome 0:a4887b672ac6 323 RawPowerSample.startTimestamp = ntp.time();
WiredHome 0:a4887b672ac6 324 }
WiredHome 0:a4887b672ac6 325 RawPowerSample.cycles++;
WiredHome 0:a4887b672ac6 326 RawPowerSample.tStartSample = RawPowerSample.tLastRise;
WiredHome 0:a4887b672ac6 327 RawPowerSample.tLastRise = tNow;
WiredHome 0:a4887b672ac6 328 __enable_irq();
WiredHome 0:a4887b672ac6 329 PulseIndicator = 1;
WiredHome 0:a4887b672ac6 330 flash.attach_us(&LedOff, 25000);
WiredHome 0:a4887b672ac6 331 }
WiredHome 0:a4887b672ac6 332
WiredHome 0:a4887b672ac6 333 void RunPulseTask(void)
WiredHome 0:a4887b672ac6 334 {
WiredHome 0:a4887b672ac6 335 static time_t timeFor5s = 0;
WiredHome 0:a4887b672ac6 336 static time_t timeFor5m = 0;
WiredHome 0:a4887b672ac6 337 static uint32_t lastCount = 0;
WiredHome 0:a4887b672ac6 338 time_t timenow = ntp.time();
WiredHome 0:a4887b672ac6 339 float iKW = 0.0f;
WiredHome 0:a4887b672ac6 340 bool sendToWeb = false;
WiredHome 0:a4887b672ac6 341
WiredHome 0:a4887b672ac6 342 __disable_irq();
WiredHome 0:a4887b672ac6 343 uint32_t elapsed = RawPowerSample.tLastRise - RawPowerSample.tStartSample;
WiredHome 0:a4887b672ac6 344 uint32_t count = RawPowerSample.cycles;
WiredHome 0:a4887b672ac6 345 __enable_irq();
WiredHome 0:a4887b672ac6 346
WiredHome 0:a4887b672ac6 347 if (elapsed) {
WiredHome 0:a4887b672ac6 348 // instantaneous, from this exact sample
WiredHome 0:a4887b672ac6 349 iKW = (float)3600 * 1000 / elapsed;
WiredHome 0:a4887b672ac6 350 }
WiredHome 0:a4887b672ac6 351 if (timeFor5s == 0 || timenow < timeFor5s) // startup or if something goes really bad
WiredHome 0:a4887b672ac6 352 timeFor5s = timenow;
WiredHome 0:a4887b672ac6 353 if (timeFor5m == 0 || timenow < timeFor5m) // startup or if something goes really bad
WiredHome 0:a4887b672ac6 354 timeFor5m = timenow;
WiredHome 0:a4887b672ac6 355
WiredHome 0:a4887b672ac6 356 if ((timenow - timeFor5m) >= 60) { // 300) {
WiredHome 2:649d91b93824 357 //pc.printf(" tnow: %d, t5m: %d\r\n", timenow, timeFor5m);
WiredHome 0:a4887b672ac6 358 sendToWeb = true;
WiredHome 1:04ab0a3d07f1 359 timeFor5s = timeFor5m = timenow;
WiredHome 1:04ab0a3d07f1 360 stats5s.EnterItem(iKW);
WiredHome 0:a4887b672ac6 361 stats5m.EnterItem(stats5s.Average());
WiredHome 1:04ab0a3d07f1 362 TransmitEnergy(true, iKW, stats5s.Min(), stats5s.Average(), stats5s.Max(),
WiredHome 1:04ab0a3d07f1 363 stats5m.Min(), stats5m.Average(), stats5m.Max());
WiredHome 1:04ab0a3d07f1 364 } else if ((timenow - timeFor5s) >= 5) {
WiredHome 2:649d91b93824 365 sendToWeb = true;
WiredHome 0:a4887b672ac6 366 timeFor5s = timenow;
WiredHome 0:a4887b672ac6 367 stats5s.EnterItem(iKW);
WiredHome 1:04ab0a3d07f1 368 TransmitEnergy(false, iKW, stats5s.Min(), stats5s.Average(), stats5s.Max(),
WiredHome 0:a4887b672ac6 369 stats5m.Min(), stats5m.Average(), stats5m.Max());
WiredHome 0:a4887b672ac6 370 }
WiredHome 2:649d91b93824 371 if (sendToWeb) { // count != lastCount) {
WiredHome 0:a4887b672ac6 372 lastCount = count;
WiredHome 0:a4887b672ac6 373 pc.printf("%8.3fs => %4.3f (%4.3f,%4.3f,%4.3f) iKW, (%4.3f,%4.3f,%4.3f) KW 5m\r\n",
WiredHome 0:a4887b672ac6 374 (float)elapsed/1000000,
WiredHome 0:a4887b672ac6 375 iKW,
WiredHome 0:a4887b672ac6 376 stats5s.Min(), stats5s.Average(), stats5s.Max(),
WiredHome 0:a4887b672ac6 377 stats5m.Min(), stats5m.Average(), stats5m.Max());
WiredHome 0:a4887b672ac6 378 }
WiredHome 0:a4887b672ac6 379 }
WiredHome 0:a4887b672ac6 380
WiredHome 0:a4887b672ac6 381 /// SimplyDynamicPage1
WiredHome 0:a4887b672ac6 382 ///
WiredHome 0:a4887b672ac6 383 /// This web page is generated dynamically as a kind of "bare minimum".
WiredHome 0:a4887b672ac6 384 /// It doesn't do much.
WiredHome 0:a4887b672ac6 385 ///
WiredHome 0:a4887b672ac6 386 /// You can see in main how this page was registered.
WiredHome 0:a4887b672ac6 387 ///
WiredHome 0:a4887b672ac6 388 HTTPServer::CallBackResults SuperSimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type,
WiredHome 0:a4887b672ac6 389 const char * path, const HTTPServer::namevalue *params, int paramcount)
WiredHome 0:a4887b672ac6 390 {
WiredHome 0:a4887b672ac6 391 HTTPServer::CallBackResults ret = HTTPServer::ACCEPT_ERROR;
WiredHome 0:a4887b672ac6 392 char contentlen[30];
WiredHome 0:a4887b672ac6 393 char buf[500];
WiredHome 0:a4887b672ac6 394 char linebuf[100];
WiredHome 0:a4887b672ac6 395
WiredHome 0:a4887b672ac6 396 switch (type) {
WiredHome 0:a4887b672ac6 397 case HTTPServer::SEND_PAGE:
WiredHome 0:a4887b672ac6 398 // This sample drops it all into a local buffer, computes the length,
WiredHome 0:a4887b672ac6 399 // and passes that along as well. This can help the other end with efficiency.
WiredHome 0:a4887b672ac6 400 strcpy(buf, "<html><head><title>Smart WattEye/title></head>\r\n");
WiredHome 0:a4887b672ac6 401 strcat(buf, "<body>\r\n");
WiredHome 0:a4887b672ac6 402 strcat(buf, "<h1>Smart WattEye</h1>\r\n");
WiredHome 0:a4887b672ac6 403 strcat(buf, "<table>");
WiredHome 0:a4887b672ac6 404 snprintf(linebuf, sizeof(linebuf), "<tr><td>Instantaneous</td><td align='right'>%5.3f.</td></tr>\r\n",
WiredHome 0:a4887b672ac6 405 PowerSnapshot.instantKW);
WiredHome 0:a4887b672ac6 406 strcat(buf, linebuf);
WiredHome 0:a4887b672ac6 407 snprintf(linebuf, sizeof(linebuf), "<tr><td>Average</td><td align='right'>%5.3f</td></tr>\r\n",
WiredHome 0:a4887b672ac6 408 PowerSnapshot.averageKW);
WiredHome 0:a4887b672ac6 409 strcat(buf, linebuf);
WiredHome 0:a4887b672ac6 410 snprintf(linebuf, sizeof(linebuf), "<tr><td>Total Cycles</td><td align='right'>%10u</td></tr>\r\n",
WiredHome 0:a4887b672ac6 411 PowerSnapshot.measuredCycles);
WiredHome 0:a4887b672ac6 412 strcat(buf, linebuf);
WiredHome 0:a4887b672ac6 413 strcat(buf, "</table>");
WiredHome 0:a4887b672ac6 414 strcat(buf, "<a href='/'>back to main</a></body></html>\r\n");
WiredHome 0:a4887b672ac6 415 sprintf(contentlen, "Content-Length: %d\r\n", strlen(buf));
WiredHome 0:a4887b672ac6 416 // Now the actual header response
WiredHome 4:0a7567195e4b 417 svr->header(HTTPServer::OK, "OK", "Content-Type: text/html\r\n", contentlen);
WiredHome 0:a4887b672ac6 418 // and data are sent
WiredHome 0:a4887b672ac6 419 svr->send(buf);
WiredHome 0:a4887b672ac6 420 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 0:a4887b672ac6 421 break;
WiredHome 0:a4887b672ac6 422 case HTTPServer::CONTENT_LENGTH_REQUEST:
WiredHome 0:a4887b672ac6 423 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 0:a4887b672ac6 424 break;
WiredHome 0:a4887b672ac6 425 case HTTPServer::DATA_TRANSFER:
WiredHome 0:a4887b672ac6 426 ret = HTTPServer::ACCEPT_COMPLETE;
WiredHome 0:a4887b672ac6 427 break;
WiredHome 0:a4887b672ac6 428 default:
WiredHome 0:a4887b672ac6 429 ret = HTTPServer::ACCEPT_ERROR;
WiredHome 0:a4887b672ac6 430 break;
WiredHome 0:a4887b672ac6 431 }
WiredHome 0:a4887b672ac6 432 return ret;
WiredHome 0:a4887b672ac6 433 }
WiredHome 0:a4887b672ac6 434
WiredHome 0:a4887b672ac6 435
WiredHome 0:a4887b672ac6 436 int main()
WiredHome 0:a4887b672ac6 437 {
WiredHome 0:a4887b672ac6 438 bool SensorStarted = false;
WiredHome 0:a4887b672ac6 439 pc.baud(460800);
WiredHome 0:a4887b672ac6 440 pc.printf("\r\n%s\r\n", PROG_INFO);
WiredHome 0:a4887b672ac6 441
WiredHome 0:a4887b672ac6 442 if (wd.WatchdogCausedReset()) {
WiredHome 0:a4887b672ac6 443 pc.printf("**** Watchdog Event caused reset ****\r\n");
WiredHome 0:a4887b672ac6 444 }
WiredHome 0:a4887b672ac6 445 wd.Configure(30.0); // nothing should take more than 30 s we hope.
WiredHome 0:a4887b672ac6 446 ini.SetFile(iniFile);
WiredHome 0:a4887b672ac6 447 // Thread bcThread(Scheduler_thread, NULL, osPriorityHigh);
WiredHome 0:a4887b672ac6 448
WiredHome 0:a4887b672ac6 449 // Now let's instantiate the web server - along with a few settings:
WiredHome 0:a4887b672ac6 450 // the Wifly object, the port of interest (typically 80),
WiredHome 0:a4887b672ac6 451 // file system path to the static pages,
WiredHome 0:a4887b672ac6 452 // the maximum parameters per transaction (in the query string),
WiredHome 0:a4887b672ac6 453 // the maximum number of dynamic pages that can be registered,
WiredHome 0:a4887b672ac6 454 // the serial port back thru USB (for development/logging)
WiredHome 0:a4887b672ac6 455 //HTTPServer svr(NULL, 80, "/Local/", 15, 30, 10, &pc);
WiredHome 0:a4887b672ac6 456
WiredHome 0:a4887b672ac6 457 // But for even more fun, I'm registering a few dynamic pages
WiredHome 0:a4887b672ac6 458 // You see the handlers for in DynamicPages.cpp.
WiredHome 0:a4887b672ac6 459 // Here you can see the path to place on the URL.
WiredHome 0:a4887b672ac6 460 // ex. http://192.168.1.140/dyn
WiredHome 0:a4887b672ac6 461 //svr.RegisterHandler("/dyn", SuperSimpleDynamicPage);
WiredHome 0:a4887b672ac6 462
WiredHome 0:a4887b672ac6 463
WiredHome 0:a4887b672ac6 464 pc.printf("***\r\n");
WiredHome 0:a4887b672ac6 465 pc.printf("Initializing network interface...\r\n");
WiredHome 0:a4887b672ac6 466
WiredHome 0:a4887b672ac6 467 int res;
WiredHome 0:a4887b672ac6 468 char ip[20], mask[20], gw[20];
WiredHome 0:a4887b672ac6 469 bool bIP, bMask, bGW;
WiredHome 0:a4887b672ac6 470 bIP = ini.ReadString("Network", "addr", ip, 20, "");
WiredHome 0:a4887b672ac6 471 bMask = ini.ReadString("Network", "mask", mask, 20, "");
WiredHome 0:a4887b672ac6 472 bGW = ini.ReadString("Network", "gate", gw, 20, "");
WiredHome 0:a4887b672ac6 473
WiredHome 0:a4887b672ac6 474 if (bIP && bMask && bGW) {
WiredHome 0:a4887b672ac6 475 res = eth.init(ip,mask,gw);
WiredHome 0:a4887b672ac6 476 } else {
WiredHome 0:a4887b672ac6 477 res = eth.init();
WiredHome 0:a4887b672ac6 478 }
WiredHome 0:a4887b672ac6 479 if (0 == res) { // Interface set
WiredHome 0:a4887b672ac6 480 do {
WiredHome 0:a4887b672ac6 481 pc.printf("Connecting to network...\r\n");
WiredHome 0:a4887b672ac6 482 if (0 == eth.connect()) {
WiredHome 0:a4887b672ac6 483 linkup = true;
WiredHome 0:a4887b672ac6 484 ShowIPAddress(true);
WiredHome 3:5c3ba12d155b 485 int speed = eth.get_connection_speed();
WiredHome 0:a4887b672ac6 486 pc.printf("Connected at %d Mb/s\r\n", speed);
WiredHome 0:a4887b672ac6 487 SoftwareUpdateCheck(true);
WiredHome 0:a4887b672ac6 488 SyncToNTPServer(); // we hope to have the right time of day now
WiredHome 0:a4887b672ac6 489 wait(5);
WiredHome 0:a4887b672ac6 490 if (!SensorStarted) {
WiredHome 0:a4887b672ac6 491 timer.start();
WiredHome 0:a4887b672ac6 492 timer.reset();
WiredHome 0:a4887b672ac6 493 event.rise(&PulseRisingISR);
WiredHome 0:a4887b672ac6 494 SensorStarted = true;
WiredHome 0:a4887b672ac6 495 }
WiredHome 0:a4887b672ac6 496 while (NetworkIsConnected()) {
WiredHome 0:a4887b672ac6 497 Thread::wait(5);
WiredHome 0:a4887b672ac6 498 linkdata = !linkdata;
WiredHome 0:a4887b672ac6 499 // Here's the real core of the main loop
WiredHome 0:a4887b672ac6 500 RunPulseTask();
WiredHome 0:a4887b672ac6 501 //svr.Poll();
WiredHome 0:a4887b672ac6 502 CheckConsoleInput();
WiredHome 0:a4887b672ac6 503 ShowSignOfLife();
WiredHome 0:a4887b672ac6 504 SoftwareUpdateCheck();
WiredHome 0:a4887b672ac6 505 wd.Service();
WiredHome 0:a4887b672ac6 506 }
WiredHome 0:a4887b672ac6 507 linkup = false;
WiredHome 0:a4887b672ac6 508 pc.printf("lost connection.\r\n");
WiredHome 0:a4887b672ac6 509 ShowIPAddress(false);
WiredHome 0:a4887b672ac6 510 eth.disconnect();
WiredHome 0:a4887b672ac6 511 }
WiredHome 0:a4887b672ac6 512 else {
WiredHome 0:a4887b672ac6 513 pc.printf(" ... failed to connect.\r\n");
WiredHome 0:a4887b672ac6 514 }
WiredHome 0:a4887b672ac6 515 CheckConsoleInput();
WiredHome 0:a4887b672ac6 516 }
WiredHome 0:a4887b672ac6 517 while (1);
WiredHome 0:a4887b672ac6 518 }
WiredHome 0:a4887b672ac6 519 else {
WiredHome 0:a4887b672ac6 520 pc.printf(" ... failed to initialize, rebooting...\r\n");
WiredHome 0:a4887b672ac6 521 mbed_reset();
WiredHome 0:a4887b672ac6 522 }
WiredHome 0:a4887b672ac6 523
WiredHome 0:a4887b672ac6 524 }