From 6071534a72dd12abbcd94d327d54f969d02ad193 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Feb 2018 19:31:54 -0700 Subject: [PATCH] Start firmware --- firmware/firmware.ino | 282 +++++++++++++++++++++++++++++++----------- webclient/src/Tool.js | 4 + webserver/server.js | 27 ++-- 3 files changed, 230 insertions(+), 83 deletions(-) diff --git a/firmware/firmware.ino b/firmware/firmware.ino index ef6535b..c1484f6 100644 --- a/firmware/firmware.ino +++ b/firmware/firmware.ino @@ -1,90 +1,232 @@ #include #include - #include -#include - #include -#define USE_SERIAL Serial +const char* WIFI_SSID = "Protospace"; +const char* WIFI_PASS = "yycmakers"; +char wifiMACAddr[18]; +const String API_ROUTE = String("http://tools.protospace.ca:8080/api/lockout/"); -ESP8266WiFiMulti WiFiMulti; -char macAddr[18]; +#define RELAY_PIN D1 +#define ON_BUTTON_PIN D3 +#define OFF_BUTTON_PIN D4 +#define ARMED_LED_PIN D5 +#define ON_LED_PIN D6 +#define OFF_LED_PIN D7 + +#define RELAY_CLOSED LOW +#define RELAY_OPEN !RELAY_CLOSED +#define BUTTON_CLOSED LOW +#define BUTTON_OPEN !BUTTON_CLOSED +#define LED_ON HIGH +#define LED_OFF !LED_ON + +#define LOGGING true + +#define DELAY_TIME 10 +#define COMM_IDLE_TIME 500 / DELAY_TIME // Generated with: https://arduinojson.org/assistant/ const size_t bufferSize = JSON_OBJECT_SIZE(3) + 50; DynamicJsonBuffer jsonBuffer(bufferSize); -void setup() { +enum wifiStates +{ + WIFI_DISCONNECTED, + WIFI_CONNECTING, + WIFI_CONNECTED, +} wifiState = WIFI_DISCONNECTED; - pinMode(D2, OUTPUT); - digitalWrite(D2, HIGH); +enum lockStates +{ + LOCK_OFF, + LOCK_ARMED, + LOCK_ON_PRESSED, + LOCK_ON, + LOCK_OFF_PRESSED, +} lockState = LOCK_OFF; - USE_SERIAL.begin(115200); - // USE_SERIAL.setDebugOutput(true); +enum commStates +{ + COMM_INIT, + COMM_IDLE, + COMM_SEND, +} commState = COMM_INIT; - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); - - for(uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); - delay(1000); - } - - WiFi.mode(WIFI_STA); - WiFiMulti.addAP("Protospace", "yycmakers"); - - byte ar[6]; - WiFi.macAddress(ar); - sprintf(macAddr, "%02X%02X%02X%02X%02X%02X", ar[0], ar[1], ar[2], ar[3], ar[4], ar[5]); - USE_SERIAL.println(macAddr); +void setup() +{ + if (LOGGING) Serial.begin(115200); + if (LOGGING) Serial.println("[INFO] Serial started."); + pinMode(RELAY_PIN, OUTPUT); + pinMode(ON_BUTTON_PIN, INPUT_PULLUP); + pinMode(OFF_BUTTON_PIN, INPUT_PULLUP); + pinMode(ARMED_LED_PIN, OUTPUT); + pinMode(ON_LED_PIN, OUTPUT); + pinMode(OFF_LED_PIN, OUTPUT); } -void loop() { - // wait for WiFi connection - if((WiFiMulti.run() == WL_CONNECTED)) { +void loop() +{ + processWifiState(); + processLockState(); + processCommState(); - HTTPClient http; - - USE_SERIAL.print("[HTTP] begin...\n"); - // configure traged server and url - //http.begin("https://192.168.1.12/test.html", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS - http.begin(String("http://172.17.32.164:3000/api/tool/") + macAddr); //HTTP - - USE_SERIAL.print("[HTTP] GET...\n"); - // start connection and send HTTP header - int httpCode = http.GET(); - - // httpCode will be negative on error - if(httpCode > 0) { - // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); - - // file found at server - if(httpCode == HTTP_CODE_OK) { - String payload = http.getString(); - USE_SERIAL.println(payload); - JsonObject& root = jsonBuffer.parseObject(payload); - - bool relayOn = root["relayOn"]; // true - bool ledOn = root["ledOn"]; // true - const char* date = root["date"]; // "2018-02-01" - - USE_SERIAL.println(String("relayOn: ") + relayOn + String(" ledOn: ") + ledOn + String(" Date: ") + date); - - digitalWrite(D2, !relayOn); - - } - } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } - - delay(10000); + delay(DELAY_TIME); } +void processWifiState() +{ + switch(wifiState) { + case WIFI_DISCONNECTED: + lockState = LOCK_OFF; + commState = COMM_INIT; + + if (LOGGING) Serial.println("[INFO] Wifi is disconnected. Attempting to connect..."); + WiFi.begin(WIFI_SSID, WIFI_PASS); + + wifiState = WIFI_CONNECTING; + break; + case WIFI_CONNECTING: + lockState = LOCK_OFF; + commState = COMM_INIT; + + if (WiFi.status() == WL_CONNECTED) { + if (LOGGING) Serial.println("[INFO] Wifi is connected."); + + if (LOGGING) Serial.print("[INFO] Wifi IP Address:"); + if (LOGGING) Serial.println(WiFi.localIP()); + byte ar[6]; + WiFi.macAddress(ar); + sprintf(wifiMACAddr, "%02X%02X%02X%02X%02X%02X", ar[0], ar[1], ar[2], ar[3], ar[4], ar[5]); + if (LOGGING) Serial.print("[INFO] Wifi MAC Address:"); + if (LOGGING) Serial.println(wifiMACAddr); + + wifiState = WIFI_CONNECTED; + } + break; + case WIFI_CONNECTED: + if (WiFi.status() != WL_CONNECTED) { + wifiState = WIFI_DISCONNECTED; + } + break; + default: + if (LOGGING) Serial.println("[ERROR] Invalid wifi state."); + wifiState = WIFI_DISCONNECTED; + break; + } +} + +void processLockState() +{ + switch (lockState) { + case LOCK_OFF: + digitalWrite(ARMED_LED_PIN, LED_OFF); + digitalWrite(ON_LED_PIN, LED_OFF); + digitalWrite(OFF_LED_PIN, LED_ON); + + digitalWrite(RELAY_PIN, RELAY_OPEN); + break; + case LOCK_ARMED: + digitalWrite(ARMED_LED_PIN, LED_ON); + digitalWrite(ON_LED_PIN, LED_OFF); + digitalWrite(OFF_LED_PIN, LED_ON); + + digitalWrite(RELAY_PIN, RELAY_OPEN); + + if (digitalRead(OFF_BUTTON_PIN) == BUTTON_CLOSED) { + if (LOGGING) Serial.println("[INFO] Unarming interlock."); + lockState = LOCK_OFF_PRESSED; + } else if (digitalRead(ON_BUTTON_PIN) == BUTTON_CLOSED) { + if (LOGGING) Serial.println("[INFO] On button pressed."); + lockState = LOCK_ON_PRESSED; + } + break; + case LOCK_ON_PRESSED: + if (digitalRead(OFF_BUTTON_PIN) == BUTTON_CLOSED) { + if (LOGGING) Serial.println("[ERROR] Both buttons pressed, aborting."); + lockState = LOCK_OFF_PRESSED; + } else if (digitalRead(ON_BUTTON_PIN) == BUTTON_OPEN) { + if (LOGGING) Serial.println("[INFO] Turning machine on."); + lockState = LOCK_ON; + } + break; + case LOCK_ON: + digitalWrite(ARMED_LED_PIN, LED_ON); + digitalWrite(ON_LED_PIN, LED_ON); + digitalWrite(OFF_LED_PIN, LED_OFF); + + digitalWrite(RELAY_PIN, RELAY_CLOSED); + + if (digitalRead(OFF_BUTTON_PIN) == BUTTON_CLOSED) { + if (LOGGING) Serial.println("[INFO] Off button pressed."); + lockState = LOCK_OFF_PRESSED; + } + break; + case LOCK_OFF_PRESSED: + if (digitalRead(OFF_BUTTON_PIN) == BUTTON_OPEN) { + if (LOGGING) Serial.println("[INFO] Turning machine off."); + lockState = LOCK_OFF; + } + break; + default: + if (LOGGING) Serial.println("[ERROR] Invalid lock state."); + lockState = LOCK_OFF; + break; + } +} + +void processCommState() +{ + static int commIdleCount = 0; + + switch (commState) { + case COMM_INIT: + commIdleCount = 0; + commState = COMM_IDLE; + break; + case COMM_IDLE: + commIdleCount++; + if (commIdleCount >= COMM_IDLE_TIME) { + commState = COMM_SEND; + } + break; + case COMM_SEND: + if (LOGGING) Serial.println("[INFO] HTTP begin."); + HTTPClient http; + //http.begin("https://url", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS + http.begin(API_ROUTE + wifiMACAddr); + http.addHeader("Content-Type", "application/json"); + + if (LOGGING) Serial.print("[INFO] HTTP POST: "); + JsonObject& rootSerializer = jsonBuffer.createObject(); + rootSerializer["lockState"] = lockState; + String postData = String(); + rootSerializer.printTo(postData); + if (LOGGING) Serial.println(postData); + int httpCode = http.POST(postData); + + if (httpCode > 0) { + if (LOGGING) Serial.printf("[INFO] POST success, code: %d\n", httpCode); + + if (httpCode == HTTP_CODE_OK) { + if (LOGGING) Serial.print("[INFO] Resource found, parsing response: "); + String payload = http.getString(); + if (LOGGING) Serial.println(payload); + JsonObject& rootDeserializer = jsonBuffer.parseObject(payload); + + bool armable = rootDeserializer["armable"]; + + if (LOGGING) Serial.println("[INFO] Armable: " + String(armable)); + } else { + if (LOGGING) Serial.println("[ERROR] Resource not found."); + } + } else { + if (LOGGING) Serial.printf("[ERROR] POST failed, error: %s\n", http.errorToString(httpCode).c_str()); + } + + commState = COMM_INIT; + break; + } +} diff --git a/webclient/src/Tool.js b/webclient/src/Tool.js index a23c4f4..2047b70 100644 --- a/webclient/src/Tool.js +++ b/webclient/src/Tool.js @@ -6,6 +6,10 @@ class Tool extends Component { decodeStatus = status => { if (status === null) { return { msg: 'Unknown! Connection error?', canArm: false, canDisarm: false, }; + } else if (status.armable && !status.armed) { + return { msg: 'Arming...', canArm: false, canDisarm: true, }; + } else if (!status.armable && status.armed) { + return { msg: 'Disarming...', canArm: true, canDisarm: false, }; } else if (!status.armed && !status.on) { return { msg: 'Off', canArm: true, canDisarm: false, }; } else if (status.armed && !status.on) { diff --git a/webserver/server.js b/webserver/server.js index 3a50cb3..37a4384 100644 --- a/webserver/server.js +++ b/webserver/server.js @@ -87,14 +87,14 @@ let toolStatus = lockoutData.lockouts.map(x => ( { id: x.id, on: false, + armable: false, armed: false, - user: null, } )); console.log(toolStatus); -const server = app.listen(8080, function () { +const server = app.listen(8080, () => { console.log('Example app listening on port 8080!'); }); const io = require('socket.io')(server); @@ -102,7 +102,7 @@ const io = require('socket.io')(server); // Express http server stuff: // TODO : remove on prod -app.use(function(req, res, next) { +app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); @@ -110,32 +110,34 @@ app.use(function(req, res, next) { app.use('/', express.static('dist')); -app.get('/api/tooldata', function (req, res) { +app.get('/api/tooldata', (req, res) => { console.log('Request for tool data'); res.setHeader('Content-Type', 'application/json'); res.send(toolData); }); -app.get('/api/user', function (req, res) { +app.get('/api/user', (req, res) => { console.log('Request for user data'); res.setHeader('Content-Type', 'application/json'); res.send(users[0]); }); -app.get('/api/lockout/:mac', function (req, res) { +app.post('/api/lockout/:mac', (req, res) => { const mac = req.params.mac; - const data = lockoutData.lockouts.find(x => x.mac === mac); + const lockout = lockoutData.lockouts.find(x => x.mac === mac); if (!data) { res.send(404); } + const tool = toolStatus.find(x => x.id === lockout.id); + console.log('Request from MAC: ' + mac); res.setHeader('Content-Type', 'application/json'); - res.send(JSON.stringify(data)); + res.send(JSON.stringify({ armable: tool.armable })); }); // Socket.io websocket stuff: @@ -159,11 +161,10 @@ io.on('connection', socket => { const toolIndex = toolStatus.findIndex(x => x.id === toolId); let tool = toolStatus[toolIndex]; - if (action === 'arm' && !tool.armed && !tool.on) { - tool.armed = true; - } else if (action === 'disarm' && tool.armed) { - tool.armed = false; - tool.on = false; + if (action === 'arm') { + tool.armable = true; + } else if (action === 'disarm') { + tool.armable = false; } toolStatus[toolIndex] = tool;