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.
main.cpp@3:5c3ba12d155b, 2016-01-18 (annotated)
- Committer:
- WiredHome
- Date:
- Mon Jan 18 21:50:17 2016 +0000
- Revision:
- 3:5c3ba12d155b
- Parent:
- 2:649d91b93824
- Child:
- 4:0a7567195e4b
Actual update of all libs.
Who changed what in which revision?
User | Revision | Line number | New 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 | 0:a4887b672ac6 | 417 | svr->header(200, "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 | } |