diff --git a/README.md b/README.md new file mode 100644 index 0000000..b38f95e --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Terrarium Controller + +It consists of 2 parts, IoT esp32 based controller and server that works as a proxy for other data storages. + diff --git a/esp32/platformio.ini b/esp32/platformio.ini index c28c542..a29bfb0 100644 --- a/esp32/platformio.ini +++ b/esp32/platformio.ini @@ -12,7 +12,11 @@ platform = espressif32 board = nodemcu-32s framework = arduino +monitor_speed = 115200 lib_deps = adafruit/DHT sensor library @ ^1.4.2 adafruit/Adafruit NeoPixel @ ^1.8.0 bblanchon/ArduinoJson@^6.18.0 + marcoschwartz/LiquidCrystal_I2C @ ^1.1.4 + adafruit/RTClib @ ^1.14.1 + alexgyver/EncButton @ ^1.7 \ No newline at end of file diff --git a/esp32/src/climate.cpp b/esp32/src/climate.cpp index c6b9b31..7bac156 100644 --- a/esp32/src/climate.cpp +++ b/esp32/src/climate.cpp @@ -5,27 +5,28 @@ namespace Climate /* schedule -08 - 20 hot max 29.5 -20 - 8 hot max 25 +TODO: add 2 hour difference for UTC timezone -// temp tolerance ~ 1.5C - to be measured and tuned - +time in UTC + +06 - 18 hot max 29.5 +18 - 6 hot max 25 */ -#define DAY_START_HOUR 8 +#define DAY_START_HOUR 6 #define DAY_START_MINUTE 0 -#define NIGHT_START_HOUR 20 +#define NIGHT_START_HOUR 18 #define NIGHT_START_MINUTE 0 #define DHTTYPE DHT22 #define HEATER_RELAY_PIN 4 -#define DHT_HOT_SIDE_PIN 16 // #1 -#define DHT_HOT_CENTER_PIN 17 // #2 -#define DHT_COLD_CENTER_PIN 5 // #3 -#define DHT_COLD_SIDE_PIN 18 // #4 +#define DHT_HOT_SIDE_PIN 16 // #1 +#define DHT_HOT_CENTER_PIN 17 // #2 +#define DHT_COLD_CENTER_PIN 18 // #3 +#define DHT_COLD_SIDE_PIN 19 // #4 #define DAY_MAX_TEMP 28.5 #define DAY_TEMP_TOLERANCE_WARM 0.5 @@ -34,6 +35,10 @@ namespace Climate #define NIGHT_TEMP_TOLERANCE_WARM 0.5 #define NIGHT_TEMP_TOLERANCE_COLD 0.6 +#define MAX_NULL_READINGS_SEC 30 + + int lastNotNullReadings = 0; + DHT_Unified dhtHotSide(DHT_HOT_SIDE_PIN, DHTTYPE); DHT_Unified dhtHotCenter(DHT_HOT_CENTER_PIN, DHTTYPE); DHT_Unified dhtColdCenter(DHT_COLD_CENTER_PIN, DHTTYPE); @@ -41,7 +46,6 @@ namespace Climate volatile byte relayState = LOW; - HeaterPhase heaterPhase; ClimateData readTempHumid(DHT_Unified dht) @@ -80,18 +84,18 @@ namespace Climate void turnRelayOn() { - relayState = LOW; + relayState = HIGH; heaterPhase = heating; digitalWrite(HEATER_RELAY_PIN, relayState); - Serial.println("turn relay on"); + //Serial.println("turn relay on"); } void turnRelayOff() { - relayState = HIGH; + relayState = LOW; heaterPhase = cooling; digitalWrite(HEATER_RELAY_PIN, relayState); - Serial.println("turn relay off"); + //Serial.println("turn relay off"); } void climateSetup() @@ -104,7 +108,7 @@ namespace Climate dhtColdSide.begin(); } - Telemetry::TelemteryData climateControl(int hour, int minute) + Telemetry::TelemteryData climateControl(int hour, int minute, uint32_t now) { bool isDay = hour >= DAY_START_HOUR && hour < NIGHT_START_HOUR && minute >= DAY_START_MINUTE; @@ -121,6 +125,16 @@ namespace Climate // Serial.println("4: cold side"); ClimateData coldSide = readTempHumid(dhtColdSide); + if (hotSide.t > 0 || hotCenter.t > 0) + { + lastNotNullReadings = now; + } + + if (lastNotNullReadings != 0 && now - lastNotNullReadings > MAX_NULL_READINGS_SEC) + { + ESP.restart(); + } + Telemetry::TelemteryData telemetryData = Telemetry::TelemteryData(); float maxTemp = DAY_MAX_TEMP - DAY_TEMP_TOLERANCE_WARM; @@ -160,8 +174,18 @@ namespace Climate } telemetryData.hotSide = hotSide; - telemetryData.hotCenter = hotCenter; - telemetryData.coldCenter = coldCenter; + + if (SENSORS_COUNT == 2) + { + telemetryData.hotCenter = hotSide; + telemetryData.coldCenter = coldSide; + } + else if (SENSORS_COUNT == 4) + { + telemetryData.hotCenter = hotCenter; + telemetryData.coldCenter = coldCenter; + } + telemetryData.coldSide = coldSide; telemetryData.heaterPhase = heaterPhase; diff --git a/esp32/src/climate.h b/esp32/src/climate.h index c256fbc..2a8c622 100644 --- a/esp32/src/climate.h +++ b/esp32/src/climate.h @@ -6,6 +6,7 @@ #include #include "telemetry.h" #include "climate_data.h" +#include "config.h" namespace Climate { @@ -14,7 +15,7 @@ namespace Climate ClimateData readTempHumid(DHT_Unified dht); void turnRelayOn(); void turnRelayOff(); - Telemetry::TelemteryData climateControl(int hour, int minute); + Telemetry::TelemteryData climateControl(int hour, int minute, uint32_t now); } #endif \ No newline at end of file diff --git a/esp32/src/config.h b/esp32/src/config.h new file mode 100644 index 0000000..3fed960 --- /dev/null +++ b/esp32/src/config.h @@ -0,0 +1,7 @@ +#ifndef CONFIG +#define CONFIG + +#define SENSORS_COUNT 2 +#define TERRARIUM_ID 3 + +#endif \ No newline at end of file diff --git a/esp32/src/display.cpp b/esp32/src/display.cpp new file mode 100644 index 0000000..053308e --- /dev/null +++ b/esp32/src/display.cpp @@ -0,0 +1,198 @@ +#include "display.h" + +namespace Display +{ + + int lcdColumns = 20; + int lcdRows = 4; + + LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows); + + void render(DisplayData displayData) + { + // just do clear? + // clearRow(0); + // clearRow(1); + // clearRow(2); + // clearRow(3); + + renderHarvestInfo(displayData.nextHarvestInSec); + renderClimate(displayData); + renderTime(displayData.hour, displayData.minute, displayData.second); + renderInfo(displayData.terrId); + renderSubmissionInfo(displayData.submission); + } + +void renderSubmissionInfo(bool submission) +{ + lcd.setCursor(9, 2); + lcd.print(" "); + lcd.print(" "); + if (submission){ + lcd.setCursor(9, 2); + lcd.print("S"); + } +} + + void renderHarvestInfo(int secondsToNextHarvest) + { + lcd.setCursor(9, 3); + lcd.print(" "); + lcd.print(" "); + lcd.print(" "); + lcd.setCursor(9, 3); + lcd.print(secondsToNextHarvest); + } + + void renderInfo(int id) + { + lcd.setCursor(4, 3); + lcd.print(" "); + lcd.print(" "); + lcd.print(" "); + lcd.print(" "); + lcd.setCursor(4, 3); + lcd.print("ID"); + lcd.setCursor(6, 3); + lcd.print(id); + } + + void clearRow(int row) + { + lcd.setCursor(0, row); + for (int i = 0; i < 20; i++) + { + lcd.print(" "); + } + } + + void renderClimate(DisplayData displayData) + { + clearRow(0); + clearRow(1); + lcd.setCursor(0, 0); + lcd.print(floatToString(displayData.hotSide.t)); + + if (SENSORS_COUNT == 4) + { + lcd.print("|"); + lcd.setCursor(5, 0); + lcd.print(floatToString(displayData.hotCenter.t)); + lcd.setCursor(11, 0); + lcd.print(floatToString(displayData.coldCenter.t)); + lcd.print("|"); + } + + lcd.setCursor(16, 0); + lcd.print(floatToString(displayData.coldSide.t)); + + lcd.setCursor(0, 1); + lcd.print(floatToString(displayData.hotSide.h)); + + if (SENSORS_COUNT == 4) + { + lcd.print("|"); + lcd.setCursor(5, 1); + lcd.print(floatToString(displayData.hotCenter.h)); + lcd.setCursor(11, 1); + lcd.print(floatToString(displayData.coldCenter.h)); + lcd.print("|"); + } + + lcd.setCursor(16, 1); + lcd.print(floatToString(displayData.coldSide.h)); + + lcd.setCursor(0, 2); + lcd.print("heater"); + lcd.setCursor(0, 3); + lcd.print(" "); + lcd.print(" "); + lcd.print(" "); + lcd.setCursor(0, 3); + if (displayData.heater) + { + lcd.print("ON"); + } + else + { + lcd.print("OFF"); + } + lcd.setCursor(13, 2); + if (displayData.heaterPhase == Climate::HeaterPhase::cooling) + { + lcd.print("cooling"); + } + else + { + lcd.print("heating"); + } + } + + char *floatToString(double value) + { + static char buffer[5]; + sprintf(buffer, "%.1f", value); + //Serial.println(buffer); + return buffer; + } + + void renderTime(int hour, int minute, int second) + { + char buffer[20]; + sprintf(buffer, "%02d:%02d:%02d", hour, minute, second); + + lcd.setCursor(12, 3); + lcd.print(buffer); + } + + void renderConnectingToWifi(char *ssid, int attempts) + { + lcd.clear(); + // set cursor to first column, first row + lcd.setCursor(0, 0); + lcd.print("Connecting to WiFi"); + + lcd.setCursor(0, 1); + lcd.print(ssid); + + lcd.setCursor(0, 2); + for (int i = 0; i < attempts; i++) + { + lcd.print("*"); + } + } + + void renderNtp(int attempts) + { + lcd.clear(); + // set cursor to first column, first row + lcd.setCursor(0, 0); + lcd.print("Fetching time from NTP"); + + lcd.setCursor(0, 1); + for (int i = 0; i < attempts; i++) + { + lcd.print("*"); + } + } + + void renderConnectedToWifi(char *ssid) + { + lcd.clear(); + // set cursor to first column, first row + lcd.setCursor(0, 0); + lcd.print("Connected to WiFi"); + + lcd.setCursor(0, 1); + lcd.print(ssid); + } + + void displaySetup() + { + // initialize LCD + lcd.init(); + // turn on LCD backlight + lcd.backlight(); + Serial.println("displaySetup: ok"); + } +} \ No newline at end of file diff --git a/esp32/src/display.h b/esp32/src/display.h new file mode 100644 index 0000000..3d9557c --- /dev/null +++ b/esp32/src/display.h @@ -0,0 +1,43 @@ +#ifndef TERRARIUM_DISPLAY +#define TERRARIUM_DISPLAY + +#include +#include +#include "telemetry.h" +#include "config.h" + +namespace Display +{ + + class DisplayData + { + public: + Climate::ClimateData hotSide; + Climate::ClimateData hotCenter; + Climate::ClimateData coldCenter; + Climate::ClimateData coldSide; + bool heater; + Climate::HeaterPhase heaterPhase; + int hour; + int minute; + int second; + int nextHarvestInSec; + int terrId; + bool submission; + }; + + void displaySetup(); + void renderClimate(DisplayData displayData); + void renderConnectingToWifi(char *ssid, int attempts); + void renderNtp(int attempts); + void renderInfo(int id); + void renderConnectedToWifi(char *ssid); + void renderTime(int hour, int minute, int second); + void renderHarvestInfo(int secondsToNextHarvest); + char *floatToString(double value); + void clearRow(int row); + void render(DisplayData displayData); + void renderSubmissionInfo(bool submission); + +} +#endif \ No newline at end of file diff --git a/esp32/src/encoder.cpp b/esp32/src/encoder.cpp new file mode 100644 index 0000000..0be36ed --- /dev/null +++ b/esp32/src/encoder.cpp @@ -0,0 +1,38 @@ +#include "encoder.h" + +namespace Encoder +{ + +#define CLK 34 +#define DT 32 +#define SW 33 + + EncButton enc; + + void setup() + { + enc.counter = 0; + } + + void tick() + { + enc.tick(); + } + + bool isLeft() + { + return enc.isLeft(); + } + + bool isRight() + { + return enc.isLeft(); + } + + bool isTurn() + { + //Serial.print("turn "); + //Serial.println(enc.counter); + return enc.isTurn(); + } +} \ No newline at end of file diff --git a/esp32/src/encoder.h b/esp32/src/encoder.h new file mode 100644 index 0000000..7902890 --- /dev/null +++ b/esp32/src/encoder.h @@ -0,0 +1,15 @@ +#ifndef TERRARIUM_ENCODER +#define TERRARIUM_ENCODER + +#include + +namespace Encoder +{ + void setup(); + void tick(); + bool isLeft(); + bool isRight(); + bool isTurn(); +} + +#endif \ No newline at end of file diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp index 4d537b2..36ad7cf 100644 --- a/esp32/src/main.cpp +++ b/esp32/src/main.cpp @@ -4,31 +4,106 @@ using namespace Climate; using namespace Lighting; using namespace Security; using namespace Net; -using namespace RealTime; -using namespace Telemetry; -uint32_t delayMS = 1000; +uint32_t delayMS = 100; +uint32_t lastSensorFetch = 0; +uint32_t lastTimeRender = 0; +uint32_t lastTimeReinit = 0; +uint32_t lastSendingTime = 0; +Display::DisplayData displayData = Display::DisplayData(); +Telemetry::TelemteryData gTelemteryData = Telemetry::TelemteryData(); + +#define HARVESTING_INTERVAL_SEC 5 +#define SUBMISSION_INTERVAL_SEC 30 +#define DISPLAY_REFRESH_INTERVAL 1 +#define DISPLAY_REINIT_INTERVAL 60 * 3 + +void submitTelemetry(void * parameter){ + for(;;){ // infinite loop + + Serial.println("submission started"); + displayData.submission = true; + Telemetry::send(gTelemteryData); + displayData.submission = false; + Serial.println("submission finished"); + + + // Pause the task again for 500ms + vTaskDelay(1000 * SUBMISSION_INTERVAL_SEC / portTICK_PERIOD_MS); + } +} void setup() { - Serial.begin(9600); + Serial.begin(115200); + Display::displaySetup(); - connect(); - syncTime(); - printLocalTime(); + //connect(); + RealTime::setupRtcModule(); - // securitySetup(); + //securitySetup(); climateSetup(); - // ledSetup(); + //ledSetup(); + + //Encoder::setup(); + + xTaskCreate( + submitTelemetry, // Function that should be called + "submitTelemetry", // Name of the task (for debugging) + 1024 * 10 , // Stack size (bytes) + NULL, // Parameter to pass + 1, // Task priority + NULL // Task handle + ); + } void loop() { + int now = RealTime::getTimestamp(); + + //Encoder::tick(); + //Encoder::isTurn(); // turnLedOn(255,0,0); - TelemteryData telemteryData = climateControl(getHour(), getMinute()); - // securityCheck(); - send(telemteryData); + if (now - lastTimeReinit >= DISPLAY_REINIT_INTERVAL) + { + Display::displaySetup(); + lastTimeReinit = now; + } + + if (now - lastSensorFetch >= HARVESTING_INTERVAL_SEC) + { + Telemetry::TelemteryData telemteryData = climateControl(RealTime::getHour(), RealTime::getMinute(), now); + gTelemteryData = telemteryData; + lastSensorFetch = now; + + displayData.hotSide = telemteryData.hotSide; + displayData.hotCenter = telemteryData.hotCenter; + displayData.coldCenter = telemteryData.coldCenter; + displayData.coldSide = telemteryData.coldSide; + displayData.heater = telemteryData.heater; + displayData.heaterPhase = telemteryData.heaterPhase; - delay(delayMS); + /*if (now - lastSendingTime >= SENDING_INTERVAL_SEC) + { + renderSendTelemetry(); + Telemetry::send(telemteryData); + lastSendingTime = now; + }*/ + } + + displayData.nextHarvestInSec = HARVESTING_INTERVAL_SEC - (now - lastSensorFetch); + + displayData.terrId = TERRARIUM_ID; + displayData.hour = RealTime::getHour(); + displayData.minute = RealTime::getMinute(); + displayData.second = RealTime::getSecond(); + + if (now - lastTimeRender >= DISPLAY_REFRESH_INTERVAL) + { + Display::render(displayData); + lastTimeRender = now; + } } + diff --git a/esp32/src/main.h b/esp32/src/main.h index 45acef6..b361f09 100644 --- a/esp32/src/main.h +++ b/esp32/src/main.h @@ -7,5 +7,7 @@ #include "real_time.h" #include "net.h" #include "telemetry.h" +#include "display.h" +#include "encoder.h" #endif \ No newline at end of file diff --git a/esp32/src/net.cpp b/esp32/src/net.cpp index ef2f1ee..d7301cf 100644 --- a/esp32/src/net.cpp +++ b/esp32/src/net.cpp @@ -4,28 +4,49 @@ namespace Net { - void connect(){ + void connect(bool interactive) + { + + if (WiFi.isConnected()){ + return; + } WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100); - WiFi.setHostname("Terr Controller"); + + + char buffer[100]; + sprintf(buffer, "%s#%d", "Terrarium controller ID", TERRARIUM_ID); + + WiFi.setHostname(buffer); - int status = WL_IDLE_STATUS; // the Wifi radio's status + int attempts = 0; - // attempt to connect to Wifi network: - while (status != WL_CONNECTED) { - Serial.print("Attempting to connect SSID: "); - Serial.println(WIFI_SSID); - status = WiFi.begin(WIFI_SSID, WIFI_PASS); + //WiFi.config(IPAddress(167772400), IPAddress(167772161), IPAddress(167772160), IPAddress(167772161),IPAddress(167772161)); - // wait 10 seconds for connection: - delay(10000); + WiFi.begin(WIFI_SSID, WIFI_PASS); + + while (!WiFi.isConnected()) + { + attempts++; + if (interactive){ + Display::renderConnectingToWifi(WIFI_SSID, attempts); + } + delay(1 * 1000); + Serial.println(WiFi.status()); + if (attempts >= 20) + { + attempts = 0; + WiFi.begin(WIFI_SSID, WIFI_PASS); + } } // you're connected now, so print out the data: Serial.print("You're connected to the network"); - + if (interactive){ + Display::renderConnectedToWifi(WIFI_SSID); + } } } \ No newline at end of file diff --git a/esp32/src/net.h b/esp32/src/net.h index bebb886..96aca48 100644 --- a/esp32/src/net.h +++ b/esp32/src/net.h @@ -1,8 +1,13 @@ +#ifndef TERRARIUM_NET +#define TERRARIUM_NET + #include #include +#include "display.h" namespace Net { - void connect(); + void connect(bool interactive); +} -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/esp32/src/real_time.cpp b/esp32/src/real_time.cpp index 39aecad..b9fa003 100644 --- a/esp32/src/real_time.cpp +++ b/esp32/src/real_time.cpp @@ -1,58 +1,86 @@ #include "real_time.h" -namespace RealTime{ - -const char* ntpServer = "pool.ntp.org"; -const long gmtOffset_sec = 0; -const int daylightOffset_sec = 3600 * 2; - - void syncTime(){ - - configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); - printLocalTime(); - - Serial.print("time as int: "); - Serial.print(getHour()); - Serial.print(":"); - Serial.println(getMinute()); - } - - int getHour(){ - - struct tm timeinfo; - if(!getLocalTime(&timeinfo)){ - Serial.println("Failed to obtain time"); - return 0; +namespace RealTime +{ + + const char *ntpServer1 = "pool.ntp.org"; + const char *ntpServer2 = "1.europe.pool.ntp.org"; + const char *ntpServer3 = "2.europe.pool.ntp.org"; + const long gmtOffset_sec = 0; // todo fix this to be +1 + const int daylightOffset_sec = 0; // fix this to accept DST + + RTC_DS3231 rtc; + + void setupRtcModule() + { + if (!rtc.begin()) + { + Serial.println("Couldn't find RTC!"); + Serial.flush(); + abort(); } - char timeHour[3]; - strftime(timeHour,3, "%H", &timeinfo); + if (rtc.lostPower()) + { + + Display::renderNtp(0); + + Serial.println("RTC: lost power"); + syncTime(); + + struct tm timeinfo; + int attempts = 0; + while (!getLocalTime(&timeinfo)) + { + attempts++; + Serial.println("Failed to obtain time, retry"); + syncTime(); + Display::renderNtp(attempts); + if (attempts >= 20) + { + attempts = 0; + } + } + + rtc.adjust(mktime(&timeinfo)); + } - // Serial.print("Hour: "); - // Serial.println(timeHour); + //printLocalTime(); + //Serial.println("RTC: now"); + //Serial.println(rtc.now().hour()); + } - return atoi(timeHour); + void syncTime() + { + Net::connect(true); + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2, ntpServer3); + } + int getHour() + { + return int(rtc.now().hour()); } - int getMinute(){ - struct tm timeinfo; - if(!getLocalTime(&timeinfo)){ - Serial.println("Failed to obtain time"); - return 0; - } - char timeMinute[3]; - strftime(timeMinute,3, "%M", &timeinfo); + int getMinute() + { + return int(rtc.now().minute()); + } - // Serial.print("Minute: "); - // Serial.println(timeMinute); + int getSecond() + { + return int(rtc.now().second()); + } - return atoi(timeMinute); + uint32_t getTimestamp() + { + return rtc.now().secondstime(); } - void printLocalTime(){ + void printLocalTime() + { struct tm timeinfo; - if(!getLocalTime(&timeinfo)){ + if (!getLocalTime(&timeinfo)) + { Serial.println("Failed to obtain time"); return; } @@ -76,14 +104,11 @@ const int daylightOffset_sec = 3600 * 2; Serial.println("Time variables"); char timeHour[3]; - strftime(timeHour,3, "%H", &timeinfo); + strftime(timeHour, 3, "%H", &timeinfo); Serial.println(timeHour); char timeWeekDay[10]; - strftime(timeWeekDay,10, "%A", &timeinfo); + strftime(timeWeekDay, 10, "%A", &timeinfo); Serial.println(timeWeekDay); Serial.println(); } - } -// sync time via http -// support for time module later \ No newline at end of file diff --git a/esp32/src/real_time.h b/esp32/src/real_time.h index 09f9f24..b971be1 100644 --- a/esp32/src/real_time.h +++ b/esp32/src/real_time.h @@ -1,9 +1,20 @@ +#ifndef TERRARIUM_RTC +#define TERRARIUM_RTC + #include "time.h" #include "Arduino.h" +#include +#include "display.h" +#include "net.h" namespace RealTime{ void syncTime(); - void printLocalTime(); + void setupRtcModule(); int getHour(); int getMinute(); -} \ No newline at end of file + int getSecond(); + void printLocalTime(); + uint32_t getTimestamp(); +} + +#endif \ No newline at end of file diff --git a/esp32/src/telemetry.cpp b/esp32/src/telemetry.cpp index 698a1cd..56ef117 100644 --- a/esp32/src/telemetry.cpp +++ b/esp32/src/telemetry.cpp @@ -8,12 +8,17 @@ namespace Telemetry void send(TelemteryData telemteryData) { - const char* TELEMETRY_ENDPOINT = "http://terrarium.lab/api/v1/telemetry"; + Net::connect(false); + char telemetryEndpoint [200]; + sprintf (telemetryEndpoint, "http://terrarium.lab/api/v2/telemetry/%d", TERRARIUM_ID); + Serial.print(telemetryEndpoint); + + //const char* TELEMETRY_ENDPOINT = "http://terrarium.lab/api/v1/telemetry"; HTTPClient http; - http.begin(TELEMETRY_ENDPOINT); + http.begin(telemetryEndpoint); http.addHeader("Content-Type", "application/json"); StaticJsonDocument<400> doc; @@ -24,8 +29,6 @@ namespace Telemetry }else{ doc["heater_phase"] = "heating"; } - - doc.createNestedObject("hot_side"); doc["hot_side"]["H"] = telemteryData.hotSide.h; @@ -59,7 +62,7 @@ namespace Telemetry String requestBody; serializeJson(doc, requestBody); - Serial.println(requestBody); + //Serial.println(requestBody); int httpResponseCode = http.POST(requestBody); @@ -70,11 +73,13 @@ namespace Telemetry // Serial.println(httpResponseCode); // Serial.println(response); + return; } else { Serial.printf("Error occurred while sending HTTP POST"); + return; } } } \ No newline at end of file diff --git a/esp32/src/telemetry.h b/esp32/src/telemetry.h index 1c8410c..ce6c28a 100644 --- a/esp32/src/telemetry.h +++ b/esp32/src/telemetry.h @@ -2,6 +2,8 @@ #define TERRARIUM_TELEMETRY #include "climate_data.h" +#include "config.h" +#include "net.h" namespace Telemetry { diff --git a/server/server.go b/server/server.go index cca9b31..6205f51 100644 --- a/server/server.go +++ b/server/server.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "log" "net/http" + "strconv" "time" ) @@ -17,8 +18,8 @@ type ClimateData struct { } type ClimateConfig struct { - DayMaxTemp float64 `json:"day_max_temp"` - NightMaxTemp float64 `json:"night_max_temp"` + DayMaxTemp float64 `json:"day_max_temp"` + NightMaxTemp float64 `json:"night_max_temp"` DayTempToleranceWarm float64 `json:"day_temp_tolerance_warm"` NightTempToleranceWarm float64 `json:"night_temp_tolerance_warm"` DayTempToleranceCold float64 `json:"day_temp_tolerance_cold"` @@ -40,16 +41,30 @@ var errResponse struct { } type Storage struct { - Data Data `json:"data"` - Timestamp int64 `json:"timestamp"` + Data Data `json:"data"` + Timestamp int64 `json:"timestamp"` } -var storage Storage +var storage map[int]Storage var debug = true func recordTelemetry(w http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + listIdString := vars["id"] + + if listIdString == "" { + listIdString = "0" + } + + id, err := strconv.Atoi(listIdString) + + if err != nil { + fmt.Println("error", err) + return + } + if debug { buf, bodyErr := ioutil.ReadAll(req.Body) @@ -66,7 +81,7 @@ func recordTelemetry(w http.ResponseWriter, req *http.Request) { } var telemetry Data - err := json.NewDecoder(req.Body).Decode(&telemetry) + err = json.NewDecoder(req.Body).Decode(&telemetry) if err != nil { errResponse.Message = fmt.Sprintf("error on parsing payload, %v", err) @@ -74,22 +89,34 @@ func recordTelemetry(w http.ResponseWriter, req *http.Request) { return } - storage.Data = telemetry - storage.Timestamp = time.Now().Unix() + storage[id] = Storage{ + Data: telemetry, + Timestamp: time.Now().Unix(), + } fmt.Fprintf(w, "ok\n") } func printTelemetry(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(storage) } func main() { + storage = make(map[int]Storage) + muxRouter := mux.NewRouter() muxRouter.HandleFunc("/api/v1/telemetry", recordTelemetry).Methods("POST") muxRouter.HandleFunc("/api/v1/telemetry", printTelemetry).Methods("GET") - http.ListenAndServe(":80", muxRouter) + muxRouter.HandleFunc("/api/v2/telemetry/{id}", recordTelemetry).Methods("POST") + muxRouter.HandleFunc("/api/v2/telemetry", printTelemetry).Methods("GET") + + err := http.ListenAndServe(":80", muxRouter) + + if err != nil{ + panic(err) + } } diff --git a/server/test_requests.http b/server/test_requests.http index ebaea4e..4206e3f 100644 --- a/server/test_requests.http +++ b/server/test_requests.http @@ -2,10 +2,10 @@ # --request POST # --data '{"p1_consumed":12345,"p2_consumed":1234}' # http://127.0.0.1:8001/api/v1/items -POST http://localhost:8090/api/v1/telemetry +POST http://localhost:8060/api/v1/telemetry Content-Type: application/json {"cold_side":{"T": 26.6, "H": 65.5}} ### -GET http://localhost:8090/api/v1/telemetry +GET http://localhost:8060/api/v1/telemetry