summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--config.h.template17
-rw-r--r--flowerpot-device.ino217
3 files changed, 235 insertions, 0 deletions
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 <jesse@jesterpm.net>
+ *
+ * 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 <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+#include <ESP8266HTTPClient.h>
+#include <WiFiClient.h>
+
+#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
+ }
+}