From 379b40f47ff858c56c06048aacaafe163156471e Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Fri, 24 Dec 2021 09:01:33 -0800 Subject: Christmas eve checkpoint I'd like to cleanup the code and make the status light more useful, but it's time to ship! --- .gitignore | 1 + config.h.template | 17 ++++ flowerpot-device.ino | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 .gitignore create mode 100644 config.h.template create mode 100644 flowerpot-device.ino diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e56cf2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.h diff --git a/config.h.template b/config.h.template new file mode 100644 index 0000000..805600e --- /dev/null +++ b/config.h.template @@ -0,0 +1,17 @@ +// Copy this file to config.h and fill in the variables + + +// Define if using https. +#define USE_TLS 1 + +// The http(s) endpoint +const char* ENDPOINT = "https://jesterpm.net/flowerpot/data"; + +// The fingerprint of the SSL certificate for the endpoint. +const char* FINGERPRINT = "0F 7E 78 97 59 32 B9 58 DC DD 9A 97 96 D3 0F 6E B3 4A 5E DE"; + +// The secret key used to sign the messages. +const char SECRET_KEY[] = "secretkey"; + +// Define if device has a battery +// #define HAS_BATTERY 1 diff --git a/flowerpot-device.ino b/flowerpot-device.ino new file mode 100644 index 0000000..e7f6186 --- /dev/null +++ b/flowerpot-device.ino @@ -0,0 +1,217 @@ +/** + * Flowerpot Device Firmware + * + * Copyright © 2021 Jesse Morgan + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "Seeed_mbedtls.h" + +#include "config.h" + + +// IO Pin Configuration +#define BATTERY_SWITCH D6 +#define MEASURE_SWITCH D8 + + +// Wifi +ESP8266WiFiMulti WiFiMulti; + +// HMAC Context +mbedtls_md_context_t ctx; +const mbedtls_md_type_t MD_TYPE = MBEDTLS_MD_SHA256; +#define HMAC_SIZE_BYTES 32 +const char* HEX_BYTE = "0123456789ABCDEF"; + +// The length of the secret key defined in config.h +#define SECRET_KEY_LEN sizeof(SECRET_KEY) - sizeof(SECRET_KEY[0]) + +// Generate an HMAC signature for a payload. +const char* generate_signature(const char* payload, size_t len) { + byte hmacResult[HMAC_SIZE_BYTES]; + static char hexstring[2 * HMAC_SIZE_BYTES + 1]; + + // Generate the HMAC + mbedtls_md_hmac_starts(&ctx, (const unsigned char *) SECRET_KEY, SECRET_KEY_LEN); + mbedtls_md_hmac_update(&ctx, (const unsigned char *) payload, len); + mbedtls_md_hmac_finish(&ctx, hmacResult); + + // Encode as a hex string + for (int i = 0; i < HMAC_SIZE_BYTES; i++) { + hexstring[2 * i] = HEX_BYTE[(hmacResult[i] >> 4) & 0x0F]; + hexstring[2 * i + 1] = HEX_BYTE[hmacResult[i] & 0xF]; + } + hexstring[2 * HMAC_SIZE_BYTES] = 0; + + return hexstring; +} + +// Read the ADC and return a value between 0 and 1. +float read_value(int pin) { + pinMode(A0, INPUT); + + // Connect load to the ADC + digitalWrite(pin, HIGH); + + // Give things a second to normalize + delay(100); + + int raw; + // The ADC will return a value from 0 to 1024, + // corresponding to a voltage range between 0 and 1 volts. + raw = analogRead(A0); + + // Disconnect load from the ADC + digitalWrite(pin, LOW); + + return raw / 1024.0; +} + + +void setup() { + Serial.begin(115200); + Serial.println("\n\n"); + + // Initialize the output variables as outputs + pinMode(LED_BUILTIN, OUTPUT); + #ifdef HAS_BATTERY + pinMode(BATTERY_SWITCH, OUTPUT); + #endif + pinMode(MEASURE_SWITCH, OUTPUT); + + // Set outputs to LOW + digitalWrite(LED_BUILTIN, LOW); + #ifdef HAS_BATTERY + digitalWrite(BATTERY_SWITCH, LOW); + #endif + digitalWrite(MEASURE_SWITCH, LOW); + + for (uint8_t t = 4; t > 0; t--) { + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); + delay(1000); + } + + Serial.printf("Hello, my name is %x, you killed my father...\n", system_get_chip_id()); + + Serial.printf("Key len %d\n", SECRET_KEY_LEN); + + WiFi.mode(WIFI_STA); + WiFiMulti.addAP("your_network", "your_password?"); + + // Prepare the HMAC context + mbedtls_md_init(&ctx); + mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MD_TYPE), 1); +} + +void loop() { + // wait for WiFi connection + if ((WiFiMulti.run() == WL_CONNECTED)) { + + #ifdef USE_TLS + WiFiClientSecure client; + client.setFingerprint(FINGERPRINT); + #else + WiFiClient client; + #endif + + HTTPClient http; + + Serial.print("[HTTP] begin...\n"); + digitalWrite(LED_BUILTIN, LOW); + if (http.begin(client, ENDPOINT)) { // HTTP + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + + Serial.print("[HTTP] PUT...\n"); + // start connection and send HTTP header + + #ifdef HAS_BATTERY + // Get current battery state. + Serial.println("Reading battery..."); + float battery_value = read_value(BATTERY_SWITCH); + #else + float battery_value = 1.0; + #endif + + // Get the current resistance + Serial.println("Reading R..."); + float value = read_value(MEASURE_SWITCH); + Serial.printf("Raw measure value = %f\n", value); + value = value * 1024.0 / 938.0; + Serial.printf("Calibrated measure value = %f\n", value); + float r = 320000.0 / value - 320000.0; + Serial.printf("R = %f\n", r); + + // Payload + const size_t payload_length = 1024; + char payload[payload_length]; + int len = snprintf(payload, payload_length, + "battery_value=%0.2f&device_id=%08x&value=%0.2f&signature=", + battery_value, + system_get_chip_id(), + r); + + if (len + 65 <= payload_length) { + const char* signature = generate_signature(payload, len - 11); + strncpy(payload + len, signature, 65); + } else { + Serial.printf("Overflow! '%s'\n", payload); + } + + Serial.printf("Putting payload '%s'\n", payload); + + int httpCode = http.PUT(payload); + + // httpCode will be negative on error + if (httpCode > 0) { + // HTTP header has been send and Server response header has been handled + Serial.printf("[HTTP] PUT... code: %d\n", httpCode); + + // file found at server + if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { + String payload = http.getString(); + Serial.println(payload); + } + } else { + Serial.printf("[HTTP] PUT... failed, error: %s\n", http.errorToString(httpCode).c_str()); + } + + http.end(); + } else { + Serial.printf("[HTTP} Unable to connect\n"); + } + + digitalWrite(LED_BUILTIN, HIGH); + Serial.printf("Nighty night.\n"); + #ifdef HAS_BATTERY + ESP.deepSleep(3600 * 1000000); + #else + ESP.deepSleep(900 * 1000000); + #endif + } +} -- cgit v1.2.3