diff --git a/arduino/firmware-encry b/arduino/firmware-encry new file mode 100644 index 0000000..e98936d --- /dev/null +++ b/arduino/firmware-encry @@ -0,0 +1,246 @@ +#include +#include +#include "bsec.h" +#include +#include +#include +#include +#include + +// WLAN-Zugangsdaten +#define SSID "" +#define PASSWORT "" + +// API-Konfiguration +#define API_HOST "" +#define API_PORT 8080 +#define API_ENDPOINT "/sensors/push-data-encrypted" +#define CLIENT_ID "" +#define API_TOKEN "" + +// AES-Schlüssel (128 Bit) +AES128 aes; +byte aes_key[] = { +}; + +// Base64-Zeichen +const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// Funktionen für Base64-Encoding +String base64Encode(byte* data, int len) { + String out = ""; + for (int i = 0; i < len; i += 3) { + int val = (data[i] << 16) + (i + 1 < len ? data[i + 1] << 8 : 0) + (i + 2 < len ? data[i + 2] : 0); + out += base64_chars[(val >> 18) & 0x3F]; + out += base64_chars[(val >> 12) & 0x3F]; + out += (i + 1 < len) ? base64_chars[(val >> 6) & 0x3F] : '='; + out += (i + 2 < len) ? base64_chars[val & 0x3F] : '='; + } + return out; +} + +String padToBlock(String input) { + int pad = 16 - (input.length() % 16); + for (int i = 0; i < pad; i++) input += '\0'; + return input; +} + +String encryptAndBase64(String plainText) { + String padded = padToBlock(plainText); + int len = padded.length(); + byte plain[len]; + byte encrypted[len]; + + padded.getBytes((unsigned char*)plain, len + 1); + + aes.setKey(aes_key, sizeof(aes_key)); + for (int i = 0; i < len; i += 16) { + aes.encryptBlock(encrypted + i, plain + i); + } + + return base64Encode(encrypted, len); +} + +// Sensor & Netzwerk +Bsec iaqSensor; +WiFiClient wifi; +HttpClient client = HttpClient(wifi, API_HOST, API_PORT); + +// NTP-Client +WiFiUDP ntpUDP; +const long utcOffsetInSeconds = 0; +NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds); + +// Sendeintervall +unsigned long sendInterval = 30000; + +void errorBlink(int code) { + while (true) { + for (int i = 0; i < code; i++) { + digitalWrite(LED_BUILTIN, HIGH); + delay(150); + digitalWrite(LED_BUILTIN, LOW); + delay(150); + } + delay(1000); + } +} + +float clampValue(float val) { + if (val >= 1000.0) return 999.999; + if (val <= -1000.0) return -999.999; + return val; +} + +String getTimestamp() { + timeClient.update(); + unsigned long epochTime = timeClient.getEpochTime(); + int year = 1970; + unsigned long seconds = epochTime; + + while (true) { + bool leap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + int daysInYear = leap ? 366 : 365; + if (seconds >= daysInYear * 86400UL) { + seconds -= daysInYear * 86400UL; + year++; + } else break; + } + + int month = 1; + const int daysInMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + while (month <= 12) { + int dim = daysInMonth[month - 1]; + if (month == 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) dim = 29; + if (seconds >= dim * 86400UL) { + seconds -= dim * 86400UL; + month++; + } else break; + } + + int day = seconds / 86400UL + 1; + seconds = seconds % 86400UL; + int hour = seconds / 3600UL; + seconds = seconds % 3600UL; + int minute = seconds / 60UL; + int second = seconds % 60UL; + + char buf[30]; + sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second); + return String(buf); +} + +bool checkApiHealth() { + HttpClient healthClient = HttpClient(wifi, API_HOST, API_PORT); + healthClient.get("/health"); + int statusCode = healthClient.responseStatusCode(); + healthClient.stop(); + return statusCode == 200; +} + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LOW); + + WiFi.begin(SSID, PASSWORT); + unsigned long startAttemptTime = millis(); + while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 20000) { + delay(500); + } + if (WiFi.status() != WL_CONNECTED) { + errorBlink(1); + } + + if (!checkApiHealth()) { + errorBlink(4); + } + + Wire.begin(); + + byte sensorAddress = 0; + byte possibleAddresses[] = {0x76, 0x77}; + bool sensorFound = false; + for (int i = 0; i < 2; i++) { + byte addr = possibleAddresses[i]; + Wire.beginTransmission(addr); + if (Wire.endTransmission() == 0) { + sensorAddress = addr; + sensorFound = true; + break; + } + } + + if (!sensorFound) { + errorBlink(2); + } + + iaqSensor.begin(sensorAddress, Wire); + if (iaqSensor.bsecStatus != BSEC_OK) { + errorBlink(3); + } + + bsec_virtual_sensor_t sensorList[] = { + BSEC_OUTPUT_IAQ, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + BSEC_OUTPUT_RAW_PRESSURE, + BSEC_OUTPUT_RAW_GAS + }; + iaqSensor.updateSubscription(sensorList, 5, BSEC_SAMPLE_RATE_LP); + + timeClient.begin(); + while (!timeClient.update()) { + timeClient.forceUpdate(); + } + + digitalWrite(LED_BUILTIN, HIGH); +} + +void loop() { + if (iaqSensor.run()) { + float temperature = clampValue(iaqSensor.temperature); + float humidity = clampValue(iaqSensor.humidity); + float voc = clampValue(iaqSensor.iaq); + float gas = clampValue(iaqSensor.gasResistance / 1000.0); + float pressure = iaqSensor.pressure / 100.0; + + String timestamp = getTimestamp(); + + String jsonPayload = "{"; + jsonPayload += "\"timestamp\": \"" + timestamp + "\","; + jsonPayload += "\"temperature\": " + String(temperature, 3) + ","; + jsonPayload += "\"humidity\": " + String(humidity, 3) + ","; + jsonPayload += "\"pressure\": " + String(pressure, 3) + ","; + jsonPayload += "\"voc\": " + String(voc, 3) + ","; + jsonPayload += "\"gas\": " + String(gas, 3); + jsonPayload += "}"; + + String encryptedPayload = encryptAndBase64(jsonPayload); + + String fullPath = String(API_ENDPOINT) + "?client=" + CLIENT_ID; + + client.beginRequest(); + client.post(fullPath); + client.sendHeader("Content-Type", "text/plain"); + client.sendHeader("token", API_TOKEN); + client.sendHeader("Content-Length", encryptedPayload.length()); + client.beginBody(); + client.print(encryptedPayload); + client.endRequest(); + + int statusCode = client.responseStatusCode(); + client.responseBody(); + + if (statusCode == 200) { + digitalWrite(LED_BUILTIN, LOW); + delay(100); + digitalWrite(LED_BUILTIN, HIGH); + } + + if (statusCode < 200 || statusCode >= 300) { + digitalWrite(LED_BUILTIN, LOW); + } + } + + delay(sendInterval); +}