This is my final project for UCSC Extension - USB Programming. It is a home monitoring program for motion and temperature and requires host-side code.
Dependencies: C12832_lcd HomeMon LM75B MMA7660 USBDevice mbed
main.cpp@0:977f7cf68014, 2013-09-01 (annotated)
- Committer:
- groletter
- Date:
- Sun Sep 01 23:35:50 2013 +0000
- Revision:
- 0:977f7cf68014
- Child:
- 2:f6a1f96dc3cd
This is my first draft of the UCSC Extension USB Programming final project.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
groletter | 0:977f7cf68014 | 1 | // |
groletter | 0:977f7cf68014 | 2 | // Jerry Roletter - 9/1/13 |
groletter | 0:977f7cf68014 | 3 | // UCSC Extension - USB Programming Class (Summer 2013) |
groletter | 0:977f7cf68014 | 4 | // |
groletter | 0:977f7cf68014 | 5 | // This code is for a simple home monitoring system that |
groletter | 0:977f7cf68014 | 6 | // detects temperature out of a programmed range, and any |
groletter | 0:977f7cf68014 | 7 | // motion of the mbed application board. If motion or out |
groletter | 0:977f7cf68014 | 8 | // of range temperature is detected it sends alerts to the |
groletter | 0:977f7cf68014 | 9 | // host if one is connected via a USB Serial device. Otherwise |
groletter | 0:977f7cf68014 | 10 | // it stores them until a host is connected. |
groletter | 0:977f7cf68014 | 11 | // |
groletter | 0:977f7cf68014 | 12 | // It keeps track of historic temperature and movement data. |
groletter | 0:977f7cf68014 | 13 | // The host can request those buffers for analysis. |
groletter | 0:977f7cf68014 | 14 | // |
groletter | 0:977f7cf68014 | 15 | // I build a protocol on top of the serial port. This allows |
groletter | 0:977f7cf68014 | 16 | // the host and device to communicate. As of 9/1/13 there still |
groletter | 0:977f7cf68014 | 17 | // seem to be some issues around the communication protocol |
groletter | 0:977f7cf68014 | 18 | // due to lost acks or improper error handling. Running out |
groletter | 0:977f7cf68014 | 19 | // of time to fully debug. |
groletter | 0:977f7cf68014 | 20 | // |
groletter | 0:977f7cf68014 | 21 | |
groletter | 0:977f7cf68014 | 22 | #include "mbed.h" |
groletter | 0:977f7cf68014 | 23 | #include "MMA7660.h" |
groletter | 0:977f7cf68014 | 24 | #include "C12832_lcd.h" |
groletter | 0:977f7cf68014 | 25 | #include "LM75B.h" |
groletter | 0:977f7cf68014 | 26 | #include "USBSerial.h" |
groletter | 0:977f7cf68014 | 27 | #include <ctype.h> |
groletter | 0:977f7cf68014 | 28 | |
groletter | 0:977f7cf68014 | 29 | // My specific classes for this assignment |
groletter | 0:977f7cf68014 | 30 | |
groletter | 0:977f7cf68014 | 31 | #include "USBHomeMon.h" |
groletter | 0:977f7cf68014 | 32 | #include "Motion.h" |
groletter | 0:977f7cf68014 | 33 | #include "Temperature.h" |
groletter | 0:977f7cf68014 | 34 | #include "Communication.h" |
groletter | 0:977f7cf68014 | 35 | |
groletter | 0:977f7cf68014 | 36 | // TODO: |
groletter | 0:977f7cf68014 | 37 | // - Add a command to allow host to disconnect |
groletter | 0:977f7cf68014 | 38 | // - Add a command to allow the host to ignore Alert messages |
groletter | 0:977f7cf68014 | 39 | // - Longer term allow host to change alert window period (collapse alerts) |
groletter | 0:977f7cf68014 | 40 | // - Clean up FIXME, printf(), etc. |
groletter | 0:977f7cf68014 | 41 | // - Add time to temperature information |
groletter | 0:977f7cf68014 | 42 | // |
groletter | 0:977f7cf68014 | 43 | |
groletter | 0:977f7cf68014 | 44 | // LCD for debug |
groletter | 0:977f7cf68014 | 45 | C12832_LCD lcd; |
groletter | 0:977f7cf68014 | 46 | // Temperature Sensor |
groletter | 0:977f7cf68014 | 47 | LM75B tmp(p28,p27); |
groletter | 0:977f7cf68014 | 48 | // Accelerometer |
groletter | 0:977f7cf68014 | 49 | MMA7660 MMA(p28, p27); |
groletter | 0:977f7cf68014 | 50 | // USB Device |
groletter | 0:977f7cf68014 | 51 | USBSerial serial; |
groletter | 0:977f7cf68014 | 52 | |
groletter | 0:977f7cf68014 | 53 | Timer sysMonTime; |
groletter | 0:977f7cf68014 | 54 | |
groletter | 0:977f7cf68014 | 55 | DigitalOut connectionLED(LED1); |
groletter | 0:977f7cf68014 | 56 | DigitalOut motionLED(LED2); |
groletter | 0:977f7cf68014 | 57 | DigitalOut tempHiLED(LED3); |
groletter | 0:977f7cf68014 | 58 | DigitalOut tempLoLED(LED3); |
groletter | 0:977f7cf68014 | 59 | |
groletter | 0:977f7cf68014 | 60 | // Global State |
groletter | 0:977f7cf68014 | 61 | int state; |
groletter | 0:977f7cf68014 | 62 | alert_state alertState; |
groletter | 0:977f7cf68014 | 63 | |
groletter | 0:977f7cf68014 | 64 | double get_temp() { |
groletter | 0:977f7cf68014 | 65 | // Convert temperature to farenheit |
groletter | 0:977f7cf68014 | 66 | return tmp.read() * 9.0 / 5.0 + 32.0; |
groletter | 0:977f7cf68014 | 67 | } |
groletter | 0:977f7cf68014 | 68 | |
groletter | 0:977f7cf68014 | 69 | bool detect_motion(motion_vec current_motion, motion_vec last_motion, Motion &myMotion) { |
groletter | 0:977f7cf68014 | 70 | motion_vec thresh = myMotion.get_motion_thresh(); |
groletter | 0:977f7cf68014 | 71 | motion_vec delta; |
groletter | 0:977f7cf68014 | 72 | delta.x = current_motion.x - last_motion.x; |
groletter | 0:977f7cf68014 | 73 | delta.y = current_motion.y - last_motion.y; |
groletter | 0:977f7cf68014 | 74 | delta.z = current_motion.z - last_motion.z; |
groletter | 0:977f7cf68014 | 75 | |
groletter | 0:977f7cf68014 | 76 | if (last_motion.x > (current_motion.x+thresh.x) || last_motion.x < (current_motion.x-thresh.x) || |
groletter | 0:977f7cf68014 | 77 | last_motion.y > (current_motion.y+thresh.y) || last_motion.y < (current_motion.y-thresh.y) || |
groletter | 0:977f7cf68014 | 78 | last_motion.z > (current_motion.z+thresh.z) || last_motion.z < (current_motion.z-thresh.z)) { |
groletter | 0:977f7cf68014 | 79 | // FIXME - for debug |
groletter | 0:977f7cf68014 | 80 | lcd.cls(); |
groletter | 0:977f7cf68014 | 81 | lcd.locate(0,3); |
groletter | 0:977f7cf68014 | 82 | lcd.printf("x = %.2f y = %.2f z = %.2f\n", delta.x, delta.y, delta.z); |
groletter | 0:977f7cf68014 | 83 | return true; |
groletter | 0:977f7cf68014 | 84 | |
groletter | 0:977f7cf68014 | 85 | } |
groletter | 0:977f7cf68014 | 86 | else { |
groletter | 0:977f7cf68014 | 87 | return false; |
groletter | 0:977f7cf68014 | 88 | } |
groletter | 0:977f7cf68014 | 89 | } |
groletter | 0:977f7cf68014 | 90 | |
groletter | 0:977f7cf68014 | 91 | void store_alert(alert_type alert) { |
groletter | 0:977f7cf68014 | 92 | switch (alert) { |
groletter | 0:977f7cf68014 | 93 | case MOTION: |
groletter | 0:977f7cf68014 | 94 | alertState.pendMotion = true; |
groletter | 0:977f7cf68014 | 95 | break; |
groletter | 0:977f7cf68014 | 96 | case TEMP_LOW: |
groletter | 0:977f7cf68014 | 97 | alertState.pendTempLo = true; |
groletter | 0:977f7cf68014 | 98 | break; |
groletter | 0:977f7cf68014 | 99 | case TEMP_HI: |
groletter | 0:977f7cf68014 | 100 | alertState.pendTempHi = true; |
groletter | 0:977f7cf68014 | 101 | break; |
groletter | 0:977f7cf68014 | 102 | } |
groletter | 0:977f7cf68014 | 103 | } |
groletter | 0:977f7cf68014 | 104 | |
groletter | 0:977f7cf68014 | 105 | #define MS_IN_SEC 1000.0 |
groletter | 0:977f7cf68014 | 106 | |
groletter | 0:977f7cf68014 | 107 | int main() { |
groletter | 0:977f7cf68014 | 108 | |
groletter | 0:977f7cf68014 | 109 | alertState.pendMotion = false; |
groletter | 0:977f7cf68014 | 110 | alertState.pendTempHi = false; |
groletter | 0:977f7cf68014 | 111 | alertState.pendTempLo = false; |
groletter | 0:977f7cf68014 | 112 | |
groletter | 0:977f7cf68014 | 113 | Motion myMotion; |
groletter | 0:977f7cf68014 | 114 | Temperature myTemp; |
groletter | 0:977f7cf68014 | 115 | double theTemp; |
groletter | 0:977f7cf68014 | 116 | double lastTimeMS, curTimeMS; |
groletter | 0:977f7cf68014 | 117 | double lastTempAlertSecs, lastMotionAlertSecs; |
groletter | 0:977f7cf68014 | 118 | double MotionAlertWaitSecs, TempAlertWaitSecs; |
groletter | 0:977f7cf68014 | 119 | |
groletter | 0:977f7cf68014 | 120 | // Don't send too many alerts too quickly |
groletter | 0:977f7cf68014 | 121 | // These are just debug settings. A real system would |
groletter | 0:977f7cf68014 | 122 | // have much longer temperature settings |
groletter | 0:977f7cf68014 | 123 | |
groletter | 0:977f7cf68014 | 124 | MotionAlertWaitSecs = 5.0; // Don't allow multiple alerts within 5.0 seconds |
groletter | 0:977f7cf68014 | 125 | TempAlertWaitSecs = 60.0; // Wait at least a minute between temp alerts |
groletter | 0:977f7cf68014 | 126 | |
groletter | 0:977f7cf68014 | 127 | char host_cmd; |
groletter | 0:977f7cf68014 | 128 | msg_type msg; |
groletter | 0:977f7cf68014 | 129 | |
groletter | 0:977f7cf68014 | 130 | motion_vec current_motion; |
groletter | 0:977f7cf68014 | 131 | motion_vec last_motion; |
groletter | 0:977f7cf68014 | 132 | |
groletter | 0:977f7cf68014 | 133 | last_motion.x = MMA.x(); |
groletter | 0:977f7cf68014 | 134 | last_motion.y = MMA.y(); |
groletter | 0:977f7cf68014 | 135 | last_motion.z = MMA.z(); |
groletter | 0:977f7cf68014 | 136 | |
groletter | 0:977f7cf68014 | 137 | sysMonTime.start(); |
groletter | 0:977f7cf68014 | 138 | lastTimeMS = sysMonTime.read_ms()/MS_IN_SEC; |
groletter | 0:977f7cf68014 | 139 | lastTempAlertSecs = sysMonTime.read(); |
groletter | 0:977f7cf68014 | 140 | lastMotionAlertSecs = lastTempAlertSecs; |
groletter | 0:977f7cf68014 | 141 | |
groletter | 0:977f7cf68014 | 142 | // This method of setting the temperature is just for demo |
groletter | 0:977f7cf68014 | 143 | // purposes. I use the current temp to initialize the ranges. |
groletter | 0:977f7cf68014 | 144 | |
groletter | 0:977f7cf68014 | 145 | theTemp = get_temp(); |
groletter | 0:977f7cf68014 | 146 | myTemp.set_min(theTemp - 1.0); |
groletter | 0:977f7cf68014 | 147 | myTemp.set_max(theTemp + 1.0); |
groletter | 0:977f7cf68014 | 148 | |
groletter | 0:977f7cf68014 | 149 | state = monDISCONNECTED; |
groletter | 0:977f7cf68014 | 150 | |
groletter | 0:977f7cf68014 | 151 | while(1) { |
groletter | 0:977f7cf68014 | 152 | if (state == monDISCONNECTED) { |
groletter | 0:977f7cf68014 | 153 | if (check_connection()) { |
groletter | 0:977f7cf68014 | 154 | connectionLED = 1; |
groletter | 0:977f7cf68014 | 155 | state = monCONNECTED; |
groletter | 0:977f7cf68014 | 156 | if (alertState.pendMotion) { |
groletter | 0:977f7cf68014 | 157 | alertState.pendMotion = false; |
groletter | 0:977f7cf68014 | 158 | send_alert(MOTION); |
groletter | 0:977f7cf68014 | 159 | } |
groletter | 0:977f7cf68014 | 160 | else if (alertState.pendTempHi) { |
groletter | 0:977f7cf68014 | 161 | alertState.pendTempHi = false; |
groletter | 0:977f7cf68014 | 162 | send_alert(MOTION); |
groletter | 0:977f7cf68014 | 163 | } |
groletter | 0:977f7cf68014 | 164 | else if (alertState.pendTempLo) { |
groletter | 0:977f7cf68014 | 165 | alertState.pendTempLo = false; |
groletter | 0:977f7cf68014 | 166 | send_alert(MOTION); |
groletter | 0:977f7cf68014 | 167 | } |
groletter | 0:977f7cf68014 | 168 | } |
groletter | 0:977f7cf68014 | 169 | else { |
groletter | 0:977f7cf68014 | 170 | connectionLED = 0; |
groletter | 0:977f7cf68014 | 171 | } |
groletter | 0:977f7cf68014 | 172 | } |
groletter | 0:977f7cf68014 | 173 | // We are connected so listen for commands from |
groletter | 0:977f7cf68014 | 174 | // host. |
groletter | 0:977f7cf68014 | 175 | else { |
groletter | 0:977f7cf68014 | 176 | if (serial.available()) { |
groletter | 0:977f7cf68014 | 177 | host_cmd = rec_command(); |
groletter | 0:977f7cf68014 | 178 | msg = parse_msg(host_cmd); |
groletter | 0:977f7cf68014 | 179 | host_command_resp(msg, myTemp, myMotion); |
groletter | 0:977f7cf68014 | 180 | // FIXME - debug |
groletter | 0:977f7cf68014 | 181 | lcd.cls(); |
groletter | 0:977f7cf68014 | 182 | lcd.locate(0,3); |
groletter | 0:977f7cf68014 | 183 | lcd.printf("Received command %c\n", host_cmd); |
groletter | 0:977f7cf68014 | 184 | } |
groletter | 0:977f7cf68014 | 185 | } |
groletter | 0:977f7cf68014 | 186 | |
groletter | 0:977f7cf68014 | 187 | // Detect if motion has occurred and handle it |
groletter | 0:977f7cf68014 | 188 | current_motion.x = MMA.x(); |
groletter | 0:977f7cf68014 | 189 | current_motion.y = MMA.y(); |
groletter | 0:977f7cf68014 | 190 | current_motion.z = MMA.z(); |
groletter | 0:977f7cf68014 | 191 | if(detect_motion(current_motion, last_motion, myMotion)) { |
groletter | 0:977f7cf68014 | 192 | // Only alert if last alert was outside window of alert time |
groletter | 0:977f7cf68014 | 193 | if (sysMonTime.read() > (lastMotionAlertSecs + MotionAlertWaitSecs)) { |
groletter | 0:977f7cf68014 | 194 | // moved |
groletter | 0:977f7cf68014 | 195 | motionLED = 1; |
groletter | 0:977f7cf68014 | 196 | // Adds the time of motion to the database of motion |
groletter | 0:977f7cf68014 | 197 | myMotion.add_sample(sysMonTime.read()); |
groletter | 0:977f7cf68014 | 198 | // Send it over serial port |
groletter | 0:977f7cf68014 | 199 | if (state == monCONNECTED) { |
groletter | 0:977f7cf68014 | 200 | send_alert(MOTION); |
groletter | 0:977f7cf68014 | 201 | // clear LED since host is connected |
groletter | 0:977f7cf68014 | 202 | motionLED = 0; |
groletter | 0:977f7cf68014 | 203 | } |
groletter | 0:977f7cf68014 | 204 | else { |
groletter | 0:977f7cf68014 | 205 | store_alert(MOTION); |
groletter | 0:977f7cf68014 | 206 | } |
groletter | 0:977f7cf68014 | 207 | lastMotionAlertSecs = sysMonTime.read(); |
groletter | 0:977f7cf68014 | 208 | } |
groletter | 0:977f7cf68014 | 209 | } |
groletter | 0:977f7cf68014 | 210 | |
groletter | 0:977f7cf68014 | 211 | // Now check temperature and generate an alert |
groletter | 0:977f7cf68014 | 212 | theTemp = get_temp(); |
groletter | 0:977f7cf68014 | 213 | if (theTemp > myTemp.get_max()) { |
groletter | 0:977f7cf68014 | 214 | if (sysMonTime.read() > (lastTempAlertSecs + TempAlertWaitSecs)) { |
groletter | 0:977f7cf68014 | 215 | tempHiLED = 1; |
groletter | 0:977f7cf68014 | 216 | if (state == monCONNECTED) { |
groletter | 0:977f7cf68014 | 217 | send_alert(TEMP_HI); |
groletter | 0:977f7cf68014 | 218 | // clear if connected |
groletter | 0:977f7cf68014 | 219 | tempHiLED = 0; |
groletter | 0:977f7cf68014 | 220 | } |
groletter | 0:977f7cf68014 | 221 | else { |
groletter | 0:977f7cf68014 | 222 | store_alert(TEMP_HI); |
groletter | 0:977f7cf68014 | 223 | } |
groletter | 0:977f7cf68014 | 224 | lastTempAlertSecs = sysMonTime.read(); |
groletter | 0:977f7cf68014 | 225 | } |
groletter | 0:977f7cf68014 | 226 | } |
groletter | 0:977f7cf68014 | 227 | else if (theTemp < myTemp.get_min()) { |
groletter | 0:977f7cf68014 | 228 | if (sysMonTime.read() > (lastTempAlertSecs + TempAlertWaitSecs)) { |
groletter | 0:977f7cf68014 | 229 | tempLoLED = 1; |
groletter | 0:977f7cf68014 | 230 | if (state == monCONNECTED) { |
groletter | 0:977f7cf68014 | 231 | send_alert(TEMP_LOW); |
groletter | 0:977f7cf68014 | 232 | // clear if connected |
groletter | 0:977f7cf68014 | 233 | tempLoLED = 0; |
groletter | 0:977f7cf68014 | 234 | } |
groletter | 0:977f7cf68014 | 235 | else { |
groletter | 0:977f7cf68014 | 236 | store_alert(TEMP_LOW); |
groletter | 0:977f7cf68014 | 237 | } |
groletter | 0:977f7cf68014 | 238 | lastTempAlertSecs = sysMonTime.read(); |
groletter | 0:977f7cf68014 | 239 | } |
groletter | 0:977f7cf68014 | 240 | } |
groletter | 0:977f7cf68014 | 241 | |
groletter | 0:977f7cf68014 | 242 | // See if it is time to store the temperature samples |
groletter | 0:977f7cf68014 | 243 | if (sysMonTime.read_ms()/MS_IN_SEC > (lastTimeMS + myTemp.get_period())) { |
groletter | 0:977f7cf68014 | 244 | lcd.printf("time to send temperature\n"); |
groletter | 0:977f7cf68014 | 245 | lastTimeMS = sysMonTime.read_ms()/MS_IN_SEC; |
groletter | 0:977f7cf68014 | 246 | myTemp.add_sample(get_temp()); |
groletter | 0:977f7cf68014 | 247 | } |
groletter | 0:977f7cf68014 | 248 | |
groletter | 0:977f7cf68014 | 249 | //lcd.cls(); |
groletter | 0:977f7cf68014 | 250 | //lcd.locate(0,3); |
groletter | 0:977f7cf68014 | 251 | //lcd.printf("temp = %.2f\n", theTemp); |
groletter | 0:977f7cf68014 | 252 | |
groletter | 0:977f7cf68014 | 253 | last_motion.x = current_motion.x; |
groletter | 0:977f7cf68014 | 254 | last_motion.y = current_motion.y; |
groletter | 0:977f7cf68014 | 255 | last_motion.z = current_motion.z; |
groletter | 0:977f7cf68014 | 256 | wait(0.3); |
groletter | 0:977f7cf68014 | 257 | } |
groletter | 0:977f7cf68014 | 258 | |
groletter | 0:977f7cf68014 | 259 | } |