Web enabled thermostat demo using the Mission Cognition baseboard.
Dependencies: NetServices mbed AvailableMemory
main.cpp@0:2e82bfc9dc19, 2011-10-03 (annotated)
- Committer:
- jt
- Date:
- Mon Oct 03 21:45:56 2011 +0000
- Revision:
- 0:2e82bfc9dc19
1.0
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
jt | 0:2e82bfc9dc19 | 1 | /* |
jt | 0:2e82bfc9dc19 | 2 | Copyright (c) 2011 Jim Thomas, jt@missioncognition.net |
jt | 0:2e82bfc9dc19 | 3 | |
jt | 0:2e82bfc9dc19 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy |
jt | 0:2e82bfc9dc19 | 5 | of this software and associated documentation files (the "Software"), to deal |
jt | 0:2e82bfc9dc19 | 6 | in the Software without restriction, including without limitation the rights |
jt | 0:2e82bfc9dc19 | 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
jt | 0:2e82bfc9dc19 | 8 | copies of the Software, and to permit persons to whom the Software is |
jt | 0:2e82bfc9dc19 | 9 | furnished to do so, subject to the following conditions: |
jt | 0:2e82bfc9dc19 | 10 | |
jt | 0:2e82bfc9dc19 | 11 | The above copyright notice and this permission notice shall be included in |
jt | 0:2e82bfc9dc19 | 12 | all copies or substantial portions of the Software. |
jt | 0:2e82bfc9dc19 | 13 | |
jt | 0:2e82bfc9dc19 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
jt | 0:2e82bfc9dc19 | 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
jt | 0:2e82bfc9dc19 | 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
jt | 0:2e82bfc9dc19 | 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
jt | 0:2e82bfc9dc19 | 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
jt | 0:2e82bfc9dc19 | 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
jt | 0:2e82bfc9dc19 | 20 | THE SOFTWARE. |
jt | 0:2e82bfc9dc19 | 21 | */ |
jt | 0:2e82bfc9dc19 | 22 | |
jt | 0:2e82bfc9dc19 | 23 | #include "mbed.h" |
jt | 0:2e82bfc9dc19 | 24 | #include "SDHCFileSystem.h" |
jt | 0:2e82bfc9dc19 | 25 | #include "EthernetNetIf.h" |
jt | 0:2e82bfc9dc19 | 26 | #include "HTTPClient.h" |
jt | 0:2e82bfc9dc19 | 27 | #include "NTPClient.h" |
jt | 0:2e82bfc9dc19 | 28 | #include "HTTPServer.h" |
jt | 0:2e82bfc9dc19 | 29 | #include "RPCFunction.h" |
jt | 0:2e82bfc9dc19 | 30 | #include "RPCVariable.h" |
jt | 0:2e82bfc9dc19 | 31 | #include "AvailableMemory.h" |
jt | 0:2e82bfc9dc19 | 32 | |
jt | 0:2e82bfc9dc19 | 33 | |
jt | 0:2e82bfc9dc19 | 34 | #define HOSTNAME "mbedSE" |
jt | 0:2e82bfc9dc19 | 35 | #define PORT 80 |
jt | 0:2e82bfc9dc19 | 36 | |
jt | 0:2e82bfc9dc19 | 37 | // Setup the SD card file system on the /sd path |
jt | 0:2e82bfc9dc19 | 38 | SDFileSystem sd(p5, p6, p7, p8, "sd"); |
jt | 0:2e82bfc9dc19 | 39 | |
jt | 0:2e82bfc9dc19 | 40 | EthernetNetIf eth(HOSTNAME); |
jt | 0:2e82bfc9dc19 | 41 | HTTPClient http; |
jt | 0:2e82bfc9dc19 | 42 | NTPClient ntp; |
jt | 0:2e82bfc9dc19 | 43 | HTTPServer svr; |
jt | 0:2e82bfc9dc19 | 44 | |
jt | 0:2e82bfc9dc19 | 45 | // Setup an analog input |
jt | 0:2e82bfc9dc19 | 46 | AnalogIn rawtemp(p15, "temp"); // "AIN1" |
jt | 0:2e82bfc9dc19 | 47 | |
jt | 0:2e82bfc9dc19 | 48 | // Setup Relay outputs |
jt | 0:2e82bfc9dc19 | 49 | DigitalOut relay1(p26, "relay1"); // "PWM1" |
jt | 0:2e82bfc9dc19 | 50 | DigitalOut relay2(p25, "relay2"); // "PWM2" |
jt | 0:2e82bfc9dc19 | 51 | |
jt | 0:2e82bfc9dc19 | 52 | // Setup some variables for a thermostat (degress F) |
jt | 0:2e82bfc9dc19 | 53 | float stat_heat_setpoint = 68.0; |
jt | 0:2e82bfc9dc19 | 54 | float stat_hysteresis = 1.0; |
jt | 0:2e82bfc9dc19 | 55 | float stat_cool_setpoint = 74.0; |
jt | 0:2e82bfc9dc19 | 56 | float stat_current_temp = 0.0; // Will be updated from the temp sensor reading |
jt | 0:2e82bfc9dc19 | 57 | |
jt | 0:2e82bfc9dc19 | 58 | // Make the variables RPC accessible individually |
jt | 0:2e82bfc9dc19 | 59 | RPCVariable<float> rpc_stat_heat_setpoint(&stat_heat_setpoint, "stat_heat_setpoint"); |
jt | 0:2e82bfc9dc19 | 60 | RPCVariable<float> rpc_stat_hysteresis(&stat_hysteresis, "stat_hysteresis"); |
jt | 0:2e82bfc9dc19 | 61 | RPCVariable<float> rpc_stat_cool_setpoint(&stat_cool_setpoint, "stat_cool_setpoint"); |
jt | 0:2e82bfc9dc19 | 62 | RPCVariable<float> rpc_stat_current_temp(&stat_current_temp, "stat_current_temp"); |
jt | 0:2e82bfc9dc19 | 63 | |
jt | 0:2e82bfc9dc19 | 64 | // Make the whole state readable all at once as a JSON dict |
jt | 0:2e82bfc9dc19 | 65 | void DoGetThermostat(char * input, char* output); |
jt | 0:2e82bfc9dc19 | 66 | RPCFunction GetThermostat(&DoGetThermostat, "GetThermostat"); |
jt | 0:2e82bfc9dc19 | 67 | |
jt | 0:2e82bfc9dc19 | 68 | void DoGetThermostat(char * input, char* output) { |
jt | 0:2e82bfc9dc19 | 69 | // Input is ignored |
jt | 0:2e82bfc9dc19 | 70 | printf("Thermostat: %0.1f %0.1f %0.1f %0.1f\n", stat_heat_setpoint, stat_hysteresis, stat_cool_setpoint, stat_current_temp); |
jt | 0:2e82bfc9dc19 | 71 | printf("Relays: %d %d\n", relay1.read(), relay2.read()); |
jt | 0:2e82bfc9dc19 | 72 | |
jt | 0:2e82bfc9dc19 | 73 | sprintf(output, "{\"hs\":%0.1f,\"hy\":%0.1f,\"cs\":%0.1f,\"ct\":%0.1f,\"rh\":%d,\"rc\":%d}", |
jt | 0:2e82bfc9dc19 | 74 | stat_heat_setpoint, stat_hysteresis, stat_cool_setpoint, stat_current_temp, |
jt | 0:2e82bfc9dc19 | 75 | relay1.read(), relay2.read()); |
jt | 0:2e82bfc9dc19 | 76 | } |
jt | 0:2e82bfc9dc19 | 77 | |
jt | 0:2e82bfc9dc19 | 78 | // Setup the status LED |
jt | 0:2e82bfc9dc19 | 79 | DigitalOut led1(LED1, "led1"); |
jt | 0:2e82bfc9dc19 | 80 | |
jt | 0:2e82bfc9dc19 | 81 | int main() { |
jt | 0:2e82bfc9dc19 | 82 | float aval; |
jt | 0:2e82bfc9dc19 | 83 | float res; |
jt | 0:2e82bfc9dc19 | 84 | int counter = 0; |
jt | 0:2e82bfc9dc19 | 85 | |
jt | 0:2e82bfc9dc19 | 86 | EthernetErr ethErr; |
jt | 0:2e82bfc9dc19 | 87 | int count = 0; |
jt | 0:2e82bfc9dc19 | 88 | do { |
jt | 0:2e82bfc9dc19 | 89 | printf("Setting up %d...\n", ++count); |
jt | 0:2e82bfc9dc19 | 90 | ethErr = eth.setup(); |
jt | 0:2e82bfc9dc19 | 91 | if (ethErr) printf("Timeout\n", ethErr); |
jt | 0:2e82bfc9dc19 | 92 | } while (ethErr != ETH_OK); |
jt | 0:2e82bfc9dc19 | 93 | |
jt | 0:2e82bfc9dc19 | 94 | printf("Connected OK\n"); |
jt | 0:2e82bfc9dc19 | 95 | const char* hwAddr = eth.getHwAddr(); |
jt | 0:2e82bfc9dc19 | 96 | printf("HW address : %02x:%02x:%02x:%02x:%02x:%02x\n", |
jt | 0:2e82bfc9dc19 | 97 | hwAddr[0], hwAddr[1], hwAddr[2], |
jt | 0:2e82bfc9dc19 | 98 | hwAddr[3], hwAddr[4], hwAddr[5]); |
jt | 0:2e82bfc9dc19 | 99 | |
jt | 0:2e82bfc9dc19 | 100 | IpAddr ethIp = eth.getIp(); |
jt | 0:2e82bfc9dc19 | 101 | printf("IP address : %d.%d.%d.%d\n", ethIp[0], ethIp[1], ethIp[2], ethIp[3]); |
jt | 0:2e82bfc9dc19 | 102 | printf("Check router DHCP table for name : %s\n", eth.getHostname()); |
jt | 0:2e82bfc9dc19 | 103 | |
jt | 0:2e82bfc9dc19 | 104 | time_t ctTime; |
jt | 0:2e82bfc9dc19 | 105 | ctTime = time(NULL); |
jt | 0:2e82bfc9dc19 | 106 | printf("\nCurrent time is (UTC): %d %s\n", ctTime, ctime(&ctTime)); |
jt | 0:2e82bfc9dc19 | 107 | printf("NTP setTime...\n"); |
jt | 0:2e82bfc9dc19 | 108 | Host server(IpAddr(), 123, "pool.ntp.org"); |
jt | 0:2e82bfc9dc19 | 109 | printf("Result : %d\n", ntp.setTime(server)); |
jt | 0:2e82bfc9dc19 | 110 | |
jt | 0:2e82bfc9dc19 | 111 | ctTime = time(NULL); |
jt | 0:2e82bfc9dc19 | 112 | printf("\nTime is now (UTC): %d %s\n", ctTime, ctime(&ctTime)); |
jt | 0:2e82bfc9dc19 | 113 | |
jt | 0:2e82bfc9dc19 | 114 | char ip[16]; |
jt | 0:2e82bfc9dc19 | 115 | sprintf(ip, "%d.%d.%d.%d", ethIp[0], ethIp[1], ethIp[2], ethIp[3]); |
jt | 0:2e82bfc9dc19 | 116 | |
jt | 0:2e82bfc9dc19 | 117 | FSHandler::mount("/sd/www", "/"); //Mount uSD www path on web root path |
jt | 0:2e82bfc9dc19 | 118 | |
jt | 0:2e82bfc9dc19 | 119 | svr.addHandler<RPCHandler>("/rpc"); // Enable RPC functionality under the /rpc path |
jt | 0:2e82bfc9dc19 | 120 | svr.addHandler<FSHandler>("/"); // Add the file handler at the root |
jt | 0:2e82bfc9dc19 | 121 | |
jt | 0:2e82bfc9dc19 | 122 | svr.bind(PORT); |
jt | 0:2e82bfc9dc19 | 123 | printf("Server listening (port %d)\n", PORT); |
jt | 0:2e82bfc9dc19 | 124 | printf("- LED1 flashes server heartbeat\n"); |
jt | 0:2e82bfc9dc19 | 125 | printf("- URL for RPC commands: %s/rpc \n", ip); |
jt | 0:2e82bfc9dc19 | 126 | |
jt | 0:2e82bfc9dc19 | 127 | Timer tm; |
jt | 0:2e82bfc9dc19 | 128 | tm.start(); |
jt | 0:2e82bfc9dc19 | 129 | |
jt | 0:2e82bfc9dc19 | 130 | while (true) { |
jt | 0:2e82bfc9dc19 | 131 | Net::poll(); |
jt | 0:2e82bfc9dc19 | 132 | if (tm.read() > 0.5) { |
jt | 0:2e82bfc9dc19 | 133 | tm.start(); |
jt | 0:2e82bfc9dc19 | 134 | |
jt | 0:2e82bfc9dc19 | 135 | if (counter % 128 == 0) printf("Available memory (exact bytes) : %d\n", AvailableMemory(1)); |
jt | 0:2e82bfc9dc19 | 136 | |
jt | 0:2e82bfc9dc19 | 137 | // Keep a loop counter |
jt | 0:2e82bfc9dc19 | 138 | counter++; |
jt | 0:2e82bfc9dc19 | 139 | |
jt | 0:2e82bfc9dc19 | 140 | // Toggle the LED |
jt | 0:2e82bfc9dc19 | 141 | led1 = !led1; |
jt | 0:2e82bfc9dc19 | 142 | |
jt | 0:2e82bfc9dc19 | 143 | // Read our rawtemp sensor and convert to degrees F |
jt | 0:2e82bfc9dc19 | 144 | // ref http://www.seeedstudio.com/wiki/index.php?title=Project_Seven_-_rawtemp |
jt | 0:2e82bfc9dc19 | 145 | // ref http://www.seeedstudio.com/wiki/index.php?title=GROVE_-_Starter_Bundle_V1.0b#rawtemp_Sensor_Twig |
jt | 0:2e82bfc9dc19 | 146 | aval = rawtemp.read(); |
jt | 0:2e82bfc9dc19 | 147 | res = (10000.0 / aval) - 10000.0; // Reference resistor is 10k ohm |
jt | 0:2e82bfc9dc19 | 148 | stat_current_temp = (1.0 / ((log(res / 10000.0) / 3975.0) + 1 / 298.15) - 273.15) * 9.0 / 5.0 + 32.0; |
jt | 0:2e82bfc9dc19 | 149 | |
jt | 0:2e82bfc9dc19 | 150 | // Make sure that the setpoints don't cross |
jt | 0:2e82bfc9dc19 | 151 | if (stat_cool_setpoint < stat_heat_setpoint + 2 * stat_hysteresis) |
jt | 0:2e82bfc9dc19 | 152 | stat_cool_setpoint = stat_heat_setpoint + 2 * stat_hysteresis; |
jt | 0:2e82bfc9dc19 | 153 | |
jt | 0:2e82bfc9dc19 | 154 | // Update relays once every 8 loops (~ 4 seconds) |
jt | 0:2e82bfc9dc19 | 155 | // ref http://www.seeedstudio.com/wiki/index.php?title=Project_Five_%E2%80%93_Relay_Control |
jt | 0:2e82bfc9dc19 | 156 | if (counter % 8 == 0) { |
jt | 0:2e82bfc9dc19 | 157 | printf("Thermostat: %0.1f %0.1f %0.1f %0.1f\n", stat_heat_setpoint, stat_hysteresis, stat_cool_setpoint, stat_current_temp); |
jt | 0:2e82bfc9dc19 | 158 | |
jt | 0:2e82bfc9dc19 | 159 | if ((stat_current_temp < stat_heat_setpoint - stat_hysteresis) && !relay1) { |
jt | 0:2e82bfc9dc19 | 160 | relay1 = 1; |
jt | 0:2e82bfc9dc19 | 161 | printf("Turning Relay 1 On\n"); |
jt | 0:2e82bfc9dc19 | 162 | } |
jt | 0:2e82bfc9dc19 | 163 | if ((stat_current_temp > stat_heat_setpoint + stat_hysteresis) && relay1) { |
jt | 0:2e82bfc9dc19 | 164 | relay1 = 0; |
jt | 0:2e82bfc9dc19 | 165 | printf("Turning Relay 1 Off\n"); |
jt | 0:2e82bfc9dc19 | 166 | } |
jt | 0:2e82bfc9dc19 | 167 | if ((stat_current_temp < stat_cool_setpoint - stat_hysteresis) && relay2) { |
jt | 0:2e82bfc9dc19 | 168 | relay2 = 0; |
jt | 0:2e82bfc9dc19 | 169 | printf("Turning Relay 2 Off\n"); |
jt | 0:2e82bfc9dc19 | 170 | } |
jt | 0:2e82bfc9dc19 | 171 | if ((stat_current_temp > stat_cool_setpoint + stat_hysteresis) && !relay2) { |
jt | 0:2e82bfc9dc19 | 172 | relay2 = 1; |
jt | 0:2e82bfc9dc19 | 173 | printf("Turning Relay 2 On\n"); |
jt | 0:2e82bfc9dc19 | 174 | } |
jt | 0:2e82bfc9dc19 | 175 | } |
jt | 0:2e82bfc9dc19 | 176 | } |
jt | 0:2e82bfc9dc19 | 177 | } |
jt | 0:2e82bfc9dc19 | 178 | } |