From bb8a212ccb6af00f088c8b85917c605f6642424a Mon Sep 17 00:00:00 2001 From: Sasa Karanovic Date: Fri, 22 Oct 2021 00:30:08 -0400 Subject: [PATCH] Fixing missing files: Adding adjustable flow and logging. --- .../Firmware/CentralUnit/CentralUnit.ino | 211 ++++++++++++++++-- .../Firmware/CentralUnit/CentralUnit_cfg.h | 55 +++++ .../Firmware/CentralUnit/PlantSystem.h | 14 ++ .../CentralUnit/PlantSystem_Logging.ino | 103 +++++++++ .../CentralUnit/PlantSystem_MainLogic.ino | 105 +++++---- .../CentralUnit/PlantSystem_WebAPI.ino | 21 +- 6 files changed, 435 insertions(+), 74 deletions(-) create mode 100644 Part2_Central_board/Firmware/CentralUnit/PlantSystem_Logging.ino diff --git a/Part2_Central_board/Firmware/CentralUnit/CentralUnit.ino b/Part2_Central_board/Firmware/CentralUnit/CentralUnit.ino index 1e51b29..3f0dac1 100644 --- a/Part2_Central_board/Firmware/CentralUnit/CentralUnit.ino +++ b/Part2_Central_board/Firmware/CentralUnit/CentralUnit.ino @@ -1,15 +1,27 @@ #include +#include +#include +#include +#include +#include #include "WiFi.h" #include "ESPAsyncWebServer.h" -#include #include "CentralUnit_cfg.h" #include "wifi_credentials.h" #include "PlantSystem.h" #include "Wire.h" +#include "time.h" +#include +#include + #define WDT_TIMEOUT 20000 #define EEPROM_SIZE 4 +const char* ntpServer = "pool.ntp.org"; +const long gmtOffset_sec = -18000; +const int daylightOffset_sec = 3600; + AsyncWebServer server(80); int WiFi_status = WL_IDLE_STATUS; volatile uint32_t nFlowSensorCount = 0; @@ -17,12 +29,37 @@ volatile uint32_t nFlowSensorCount_last = 0; char dbgBuff[4024] = {0}; uint32_t dbgBuffPos = 0; -float fImpulsePerML = 0; +float fImpulsePerML = WATERING_FLOW_EDGES_PER_mL; void IRAM_ATTR ISR_flowSensor() { nFlowSensorCount++; } +// Function prototypes +void ps_debug_log_reset_Reason(RESET_REASON reason); +void ps_debug_log_verbose_reset_reason(RESET_REASON reason); +int vprintf_into_spiffs(const char* szFormat, va_list args); +void printLocalTime(); +void LOG_Timestamp(void); +void setupWebServer(void); +void flowInterruptEnabled(bool state); +void LED_Blink(uint8_t nTimes, uint16_t n_delayPeriod); +void PlantSystem_tick(void); +void PlantSystem_ScanBus(void); +void PlantSystem_debugShowDevicesOnBus(void); +void PlantSystem_getDeviceInfo(uint8_t devID); +bool PlantSystem_SetSolenoidState(uint32_t I2CAddress, bool energized); +void PlantSystem_UpdateSensors(void); +void I2C_SendBuffer(uint8_t address, uint8_t *pBuffer, uint8_t nLen); +bool I2C_ReadFrom(uint8_t address, uint8_t nLen, uint8_t *pData, uint8_t nPos, uint8_t nBytes); +bool PlantSystem_WaterPlant(uint8_t solenoidAddress, uint32_t n_volume_in_mL); +void PlantSystem_RequestPrimeSystem(void); +bool PlantSystem_PrimeSystemWithWater(void); +bool PlantSystem_WAPI_GetDevices(char *pBuff, uint32_t nMaxLen); +bool PlantSystem_WAPI_SensorData(char *pBuff, uint32_t nMaxLen); +bool PlantSystem_WAPI_SolenoidList(char *pBuff, uint32_t nMaxLen); + + void setup() { @@ -41,6 +78,35 @@ void setup() Serial.begin(115200); + // Setup Logging + if (!SPIFFS.begin(true)) { + Serial.println("An Error has occurred while mounting SPIFFS"); + while(1) + { + digitalWrite(LED_PIN, HIGH); + delay(500); + digitalWrite(LED_PIN, LOW); + delay(500); + } + } + else + { + Serial.println("SPIFFS mounted"); + + } + + delay(500); + esp_log_set_vprintf(&vprintf_into_spiffs); + esp_log_level_set("*", ESP_LOG_DEBUG); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- SPIFFS Boot up\n"); + + // Log reset reason + Serial.println("Storing reason in log.txt"); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- CPU0 "); + ps_debug_log_verbose_reset_reason(rtc_get_reset_reason(0)); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- CPU1 "); + ps_debug_log_verbose_reset_reason(rtc_get_reset_reason(1)); + Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN); // EEPROM begin @@ -56,6 +122,7 @@ void setup() // Debug EEPROM after read Serial.println("After read: fImpulsePerML"); Serial.println((float)(fImpulsePerML),4); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- fImpulsePerML: %f\n", fImpulsePerML); // Configure WDT @@ -64,25 +131,105 @@ void setup() esp_task_wdt_add(NULL); //add current thread to WDT watch // Connect to WiFi + esp_log_write(ESP_LOG_INFO, "SKWS", "-- Configuring WiFi.\n"); + WiFi.mode(WIFI_STA); + WiFi.setHostname("sk_watering_system"); + esp_log_write(ESP_LOG_INFO, "SKWS", "-- Connecting to WiFi...\n"); while ( WiFi_status != WL_CONNECTED) { Serial.print("Connecting to SSID: "); Serial.println(ssid); WiFi_status = WiFi.begin(ssid, password); - // wait 5 seconds for connection: esp_task_wdt_reset(); - delay(5000); + + digitalWrite(LED_PIN, HIGH); + delay(500); + digitalWrite(LED_PIN, LOW); + delay(500); + digitalWrite(LED_PIN, HIGH); + delay(500); + digitalWrite(LED_PIN, LOW); + delay(500); + digitalWrite(LED_PIN, HIGH); + delay(500); + digitalWrite(LED_PIN, LOW); + delay(500); } + esp_log_write(ESP_LOG_INFO, "SKWS", "-- Connected.\n"); + digitalWrite(LED_PIN, LOW); Serial.print("WiFi IP: "); Serial.println(WiFi.localIP()); + Serial.print("Hostname: "); + Serial.println(WiFi.getHostname()); + + char cIP_buff[50] = {0}; + String sIP = WiFi.localIP().toString(); + sIP.toCharArray(cIP_buff, 50); + + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- WiFi IP: %s\n", cIP_buff); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- WiFi hostname: %s\n",WiFi.getHostname()); + + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- mDNS started\n"); + + // Update time + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + printLocalTime(); + LOG_Timestamp(); + + // Config printout + esp_log_write(ESP_LOG_DEBUG, "SKWS", "\n-- I2C_BUS_RESCAN_ENABLED=%d\n", I2C_BUS_RESCAN_ENABLED); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- PERIOD_RESCAN_BUS=%d\n", PERIOD_RESCAN_BUS); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- PERIOD_RELOAD_SENSORS=%d\n", PERIOD_RELOAD_SENSORS); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- FLOW_RATE_DEBUG_ENABLED=%d\n", FLOW_RATE_DEBUG_ENABLED); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- PERIOD_FLOW_RATE_DEBUG=%d\n\n", PERIOD_FLOW_RATE_DEBUG); digitalWrite(LED_PIN, LOW); setupWebServer(); server.begin(); + esp_log_write(ESP_LOG_INFO, "SKWS", "-- Started web server.\n"); + + MDNS.addService("http", "tcp", 80); + + ArduinoOTA.setHostname("WateringSystem"); + ArduinoOTA.setPassword("Tr6spEh1"); + + ArduinoOTA + .onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) + { + type = "sketch"; + } + else // U_SPIFFS + { + type = "filesystem"; + SPIFFS.end(); + } + + // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() + Serial.println("Start updating " + type); + }) + .onEnd([]() { + Serial.println("\nEnd"); + }) + .onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }) + .onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); + else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); + else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); + else if (error == OTA_END_ERROR) Serial.println("End Failed"); + }); + + ArduinoOTA.begin(); flowInterruptEnabled(true); + esp_log_write(ESP_LOG_INFO, "SKWS", "-- Leaving setup.\n"); } @@ -101,10 +248,8 @@ void flowInterruptEnabled(bool state) void loop() { - while(1) - { - PlantSystem_tick(); - } + ArduinoOTA.handle(); + PlantSystem_tick(); } @@ -126,6 +271,39 @@ void setupWebServer(void) }); + // Read log.txt + server.on("/log.txt", HTTP_GET, [](AsyncWebServerRequest *request){ + if(SPIFFS.exists("/log.txt")) + { + Serial.println("Log.txt request received"); + request->send(SPIFFS, "/log.txt", "text/plain"); + } + else + { + request->send(200, "text/plain", "log.txt not found."); + } + }); + + // Delete /log.txt + server.on("/delete_log", HTTP_GET, [](AsyncWebServerRequest *request){ + Serial.println("Delete Log.txt request received"); + if(SPIFFS.exists("/log.txt")) + { + File writeLog = SPIFFS.open("/log.txt", FILE_WRITE); + if(!writeLog) Serial.println("Couldn't open log.txt"); + delay(50); + LOG_Timestamp(); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- Log wiped.\n"); + writeLog.close(); + request->send(200, "text/plain", "log.txt reset"); + } + else + { + request->send(200, "text/plain", "log.txt not found."); + } + }); + + // Get sensors on the bus server.on("/sensorData", HTTP_GET, [] (AsyncWebServerRequest *request) { Serial.println("Sensor data"); @@ -181,7 +359,7 @@ void setupWebServer(void) } }); - // Get devices on the bus + // Get number of flow sensor ticks for last watering action server.on("/flowSensor", HTTP_GET, [] (AsyncWebServerRequest *request) { Serial.println("Flow sensor data"); @@ -203,7 +381,7 @@ void setupWebServer(void) }); - // Get devices on the bus + // Set solenoid state server.on("/setSolenoid", HTTP_GET, [] (AsyncWebServerRequest *request) { Serial.println("Set solenoid to state"); @@ -261,11 +439,12 @@ void setupWebServer(void) if(PlantSystem_WaterPlant(nAddress, nVolume) == true) { char buff[200] = {0}; - int len; + int len = 0; len = snprintf(buff, 200, "{ \"result\": OK, \"Address\":%d, \"Volume\":%d }", nAddress, nVolume); request->send(200, "text/plain", buff); + UNUSED(len); return; } else @@ -292,10 +471,12 @@ void setupWebServer(void) return; }); - // Calibrate flow sensor + // Calibrate flow sensor or read current value server.on("/calibrateFlowSensor", HTTP_GET, [] (AsyncWebServerRequest *request) { Serial.println("Calibrate edges per mL"); + char buff[200] = {0}; + if ( request->hasParam("edges") ) { String xEdges; @@ -312,12 +493,14 @@ void setupWebServer(void) Serial.print("Changing fImpulsePerML to "); Serial.println((float)(fImpulsePerML),4); - request->send(200, "text/plain", "OK"); + snprintf(buff, 200, "New value set to %f", fEdges); + request->send(200, "text/plain", buff); return; } else { - request->send(404, "text/plain", "MISSING_ARGUMENT"); + snprintf(buff, 200, "Current value is %f\r\nLast impulse count was: %d", fImpulsePerML, nFlowSensorCount_last); + request->send(200, "text/plain", buff); return; } }); diff --git a/Part2_Central_board/Firmware/CentralUnit/CentralUnit_cfg.h b/Part2_Central_board/Firmware/CentralUnit/CentralUnit_cfg.h index b8c22e1..a85dcb6 100644 --- a/Part2_Central_board/Firmware/CentralUnit/CentralUnit_cfg.h +++ b/Part2_Central_board/Firmware/CentralUnit/CentralUnit_cfg.h @@ -10,4 +10,59 @@ #define WATER_PUMP_EN_PIN 12 #define FLOW_SENSOR_PIN 26 +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) +#endif + +// Typedefs to allow us easier tracking on devices on the bus and storing received data +typedef struct I2C_Device_TypeDef +{ + uint8_t i2cAddress; + uint8_t deviceType; +} I2C_Device_TypeDef; + +typedef struct I2C_Sensor_TypeDef +{ + uint8_t i2cAddress; + float temperature; + uint32_t soilMoisture; + uint32_t ambienLight; +} I2C_Sensor_TypeDef; + +typedef struct I2C_Solenoid_TypeDef +{ + uint8_t i2cAddress; + uint8_t lastState; +} I2C_Solenoid_TypeDef; + +// I2C bus config +#define I2C_MAX_SENSORS_ON_BUS 20 +#define I2C_MAX_SOLENOIDS_ON_BUS 20 +#define I2C_MAX_DEVICES_ON_BUS (I2C_MAX_SENSORS_ON_BUS + I2C_MAX_SOLENOIDS_ON_BUS) +#define I2C_ADDRESS_LIMIT_LOWER 0x08 +#define I2C_ADDRESS_LIMIT_UPPER 0x7F + +// Plant watering definitions +#define WATERING_VOLUME_MINIMUM 10 +#define WATERING_VOLUME_MAXIMUM 550 +#define WATERING_FLOW_EDGES_PER_mL 4 + +// Sanity checks +#if (WATERING_VOLUME_MINIMUM <= 0) +#error "WATERING_VOLUME_MINIMUM has to be non-zero positive (unsigned int) value!" +#endif +#if (WATERING_VOLUME_MAXIMUM <= 0) +#error "WATERING_VOLUME_MAXIMUM has to be non-zero positive (unsigned int) value!" +#endif +#if (WATERING_FLOW_EDGES_PER_mL <= 0) +#error "WATERING_FLOW_EDGES_PER_mL has to be non-zero positive (float) value!" +#endif + +// PlantSystem_tick periods and debug +#define I2C_BUS_RESCAN_ENABLED 0 // Define to enable I2C bus periodic rescan +#define PERIOD_RESCAN_BUS 24*3600*1000 // Every PERIOD_RESCAN_BUS miliseconds perform I2C bus rescan +#define PERIOD_RELOAD_SENSORS 15*1000 // Every PERIOD_RELOAD_SENSORS miliseconds poll data from sensors +#define FLOW_RATE_DEBUG_ENABLED 0 // Define to enable periodic flow rate debug serial printouts +#define PERIOD_FLOW_RATE_DEBUG 2*1000 // Every PERIOD_FLOW_RATE_DEBUG print out flow rate values (used for debug) + #endif \ No newline at end of file diff --git a/Part2_Central_board/Firmware/CentralUnit/PlantSystem.h b/Part2_Central_board/Firmware/CentralUnit/PlantSystem.h index 2eba104..12eccf0 100644 --- a/Part2_Central_board/Firmware/CentralUnit/PlantSystem.h +++ b/Part2_Central_board/Firmware/CentralUnit/PlantSystem.h @@ -3,6 +3,20 @@ #include +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/rtc.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "rom/rtc.h" +#endif + // CMD (1byte) + DATATYPE (1byte) + DATALEN (2bytes) #define PLANTSYSTEM_MIN_HEADER_LENGHT 4 diff --git a/Part2_Central_board/Firmware/CentralUnit/PlantSystem_Logging.ino b/Part2_Central_board/Firmware/CentralUnit/PlantSystem_Logging.ino new file mode 100644 index 0000000..2d12ed5 --- /dev/null +++ b/Part2_Central_board/Firmware/CentralUnit/PlantSystem_Logging.ino @@ -0,0 +1,103 @@ + + +static char log_print_buffer[512]; + +int vprintf_into_spiffs(const char* szFormat, va_list args) { + //write evaluated format string into buffer + int ret = vsnprintf (log_print_buffer, sizeof(log_print_buffer), szFormat, args); + + //output is now in buffer. write to file. + if(ret >= 0) { + if(!SPIFFS.exists("/log.txt")) + { + File writeLog = SPIFFS.open("/log.txt", FILE_WRITE); + if(!writeLog) Serial.println("Couldn't open log.txt"); + delay(50); + writeLog.close(); + } + + File spiffsLogFile = SPIFFS.open("/log.txt", FILE_APPEND); + //debug output + //printf("[Writing to SPIFFS] %.*s", ret, log_print_buffer); + spiffsLogFile.write((uint8_t*) log_print_buffer, (size_t) ret); + //to be safe in case of crashes: flush the output + spiffsLogFile.flush(); + spiffsLogFile.close(); + } + return ret; +} + + + +void printLocalTime(){ + struct tm timeinfo; + if(!getLocalTime(&timeinfo)){ + Serial.println("Failed to obtain time"); + return; + } + Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); +} + + +void LOG_Timestamp(void) +{ + time_t now; + char strftime_buf[64]; + struct tm timeinfo; + + time(&now); + // Set timezone to China Standard Time + setenv("TZ", "CST-8", 1); + tzset(); + + localtime_r(&now, &timeinfo); + strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); + esp_log_write(ESP_LOG_INFO, "SKWS", "-- Date/time is: %s", strftime_buf); +} + +void ps_debug_log_reset_Reason(RESET_REASON reason) +{ + switch ( reason) + { + case 1 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: POWERON_RESET\n");break; /**<1, Vbat power on reset*/ + case 3 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: SW_RESET\n");break; /**<3, Software reset digital core*/ + case 4 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: OWDT_RESET\n");break; /**<4, Legacy watch dog reset digital core*/ + case 5 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: DEEPSLEEP_RESET\n");break; /**<5, Deep Sleep reset digital core*/ + case 6 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: SDIO_RESET\n");break; /**<6, Reset by SLC module, reset digital core*/ + case 7 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: TG0WDT_SYS_RESET\n");break; /**<7, Timer Group0 Watch dog reset digital core*/ + case 8 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: TG1WDT_SYS_RESET\n");break; /**<8, Timer Group1 Watch dog reset digital core*/ + case 9 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTCWDT_SYS_RESET\n");break; /**<9, RTC Watch dog Reset digital core*/ + case 10 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: INTRUSION_RESET\n");break; /**<10, Instrusion tested to reset CPU*/ + case 11 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: TGWDT_CPU_RESET\n");break; /**<11, Time Group reset CPU*/ + case 12 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: SW_CPU_RESET\n");break; /**<12, Software reset CPU*/ + case 13 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTCWDT_CPU_RESET\n");break; /**<13, RTC Watch dog Reset CPU*/ + case 14 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: EXT_CPU_RESET\n");break; /**<14, for APP CPU, reseted by PRO CPU*/ + case 15 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTCWDT_BROWN_OUT_RESET\n");break;/**<15, Reset when the vdd voltage is not stable*/ + case 16 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTCWDT_RTC_RESET\n");break; /**<16, RTC Watch dog reset digital core and rtc module*/ + default : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: NO_MEAN\n"); + } +} + +void ps_debug_log_verbose_reset_reason(RESET_REASON reason) +{ + switch ( reason) + { + case 1 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Vbat power on reset\n");break; + case 3 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Software reset digital core\n");break; + case 4 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Legacy watch dog reset digital core\n");break; + case 5 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Deep Sleep reset digital core\n");break; + case 6 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Reset by SLC module, reset digital core\n");break; + case 7 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Timer Group0 Watch dog reset digital core\n");break; + case 8 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Timer Group1 Watch dog reset digital core\n");break; + case 9 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTC Watch dog Reset digital core\n");break; + case 10 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Instrusion tested to reset CPU\n");break; + case 11 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Time Group reset CPU\n");break; + case 12 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Software reset CPU\n");break; + case 13 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTC Watch dog Reset CPU\n");break; + case 14 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: for APP CPU, reseted by PRO CPU\n");break; + case 15 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: Reset when the vdd voltage is not stable\n");break; + case 16 : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: RTC Watch dog reset digital core and rtc module\n");break; + default : esp_log_write(ESP_LOG_INFO, "SKWS", "Reset: NO_MEAN\n"); + } +} + diff --git a/Part2_Central_board/Firmware/CentralUnit/PlantSystem_MainLogic.ino b/Part2_Central_board/Firmware/CentralUnit/PlantSystem_MainLogic.ino index 7c9f2a7..e313f54 100644 --- a/Part2_Central_board/Firmware/CentralUnit/PlantSystem_MainLogic.ino +++ b/Part2_Central_board/Firmware/CentralUnit/PlantSystem_MainLogic.ino @@ -1,54 +1,18 @@ -// Plant system note -// CMD (1byte) + DATATYPE (1byte) + DATALEN (2bytes) +#include "CentralUnit_cfg.h" -// Typedefs to allow us easier tracking on devices on the bus and storing received data -typedef struct I2C_Device_TypeDef -{ - uint8_t i2cAddress; - uint8_t deviceType; -} I2C_Device_TypeDef; - -typedef struct I2C_Sensor_TypeDef -{ - uint8_t i2cAddress; - float temperature; - uint32_t soilMoisture; - uint32_t ambienLight; -} I2C_Sensor_TypeDef; - -typedef struct I2C_Solenoid_TypeDef -{ - uint8_t i2cAddress; - uint8_t lastState; -} I2C_Solenoid_TypeDef; - // Every device that we saw on the bus -I2C_Device_TypeDef gDevicesOnBus[250] = { {.i2cAddress=0, .deviceType=0} }; +I2C_Device_TypeDef gDevicesOnBus[I2C_MAX_DEVICES_ON_BUS] = { {.i2cAddress=0, .deviceType=0} }; uint8_t gDevicesOnBusCount = 0; // All sensors that are registered on the bus -I2C_Sensor_TypeDef gSensorsOnBus[20] = { {.i2cAddress=0, .temperature=0.0, .soilMoisture=0, .ambienLight=0} }; +I2C_Sensor_TypeDef gSensorsOnBus[I2C_MAX_SENSORS_ON_BUS] = { {.i2cAddress=0, .temperature=0.0, .soilMoisture=0, .ambienLight=0} }; uint8_t gnSensorsOnBusCnt = 0; // All solenoids that are on the bus -I2C_Solenoid_TypeDef gSolenoidsOnBus[20] = { {.i2cAddress=0, .lastState=0} }; +I2C_Solenoid_TypeDef gSolenoidsOnBus[I2C_MAX_SOLENOIDS_ON_BUS] = { {.i2cAddress=0, .lastState=0} }; uint8_t gnSolenoidsOnBusCnt = 0; -// I2C bus config -#define I2C_ADDRESS_LIMIT_LOWER 0x08 -#define I2C_ADDRESS_LIMIT_UPPER 0x7F - -// Plant watering definitions -#define WATERING_VOLUME_MINIMUM 10 -#define WATERING_VOLUME_MAXIMUM 550 -#define WATERING_FLOW_EDGES_PER_L 5880 //98 - -// PlantSystem_tick periods -#define PERIOD_RESCAN_BUS 24*3600*1000 // Every 24h -#define PERIOD_RELOAD_SENSORS 15*1000 // Every 5sec -#define PERIOD_FLOW_RATE 2*1000 // Every 2sec - // PlantSystem_tick global variables uint32_t nPeriod_Rescanbus=0; uint32_t nPeriod_ReloadSensors=0; @@ -59,6 +23,7 @@ bool bPrimeSystem_requestPending=false; bool bWatering_requestPending=false; bool bWatering_inProgress=false; bool bCloseValvesOnPowerUp=false; +bool bI2CBusScanned=false; uint32_t nWatering_volume=0; uint8_t nWatering_i2cAddress=0; uint32_t nTimeout = 0; @@ -69,16 +34,25 @@ void PlantSystem_tick(void) bool bWateringError = false; esp_task_wdt_reset(); - // -- Rescan I2C bus - if(nPeriod_Rescanbus <= millis()) + // -- Initial I2C scan + if(bI2CBusScanned == false) + { + esp_task_wdt_reset(); + PlantSystem_ScanBus(); + PlantSystem_debugShowDevicesOnBus(); + bI2CBusScanned = true; + } + + // -- I2C periodic rescan + #if (I2C_BUS_RESCAN_ENABLED != 0) + if(nPeriod_Rescanbus <= millis()) { esp_task_wdt_reset(); PlantSystem_ScanBus(); - #if 1 PlantSystem_debugShowDevicesOnBus(); - #endif nPeriod_Rescanbus = millis() + PERIOD_RESCAN_BUS; } + #endif // -- Reload sensor data if(nPeriod_ReloadSensors <= millis()) @@ -112,13 +86,13 @@ void PlantSystem_tick(void) bCloseValvesOnPowerUp = true; } - #if 0 + #if (FLOW_RATE_DEBUG_ENABLED != 0) // -- Flow sensor debug if(nPeriod_FlowRate <= millis()) { Serial.print("nFlowSensorCount: "); Serial.println(nFlowSensorCount, DEC); - nPeriod_FlowRate = millis() + PERIOD_FLOW_RATE; + nPeriod_FlowRate = millis() + PERIOD_FLOW_RATE_DEBUG; } #endif @@ -150,14 +124,15 @@ void PlantSystem_tick(void) digitalWrite(WATER_PUMP_EN_PIN, LOW); Serial.println("Turning pump OFF"); Serial.println("Failed to open solenoid. Aborting..."); + esp_log_write(ESP_LOG_ERROR, "SKWS", "-- Failed to open solenoid 0x%02X. Aborting...\n", nWatering_i2cAddress); LED_Blink(4, 250); return; } + delay(2000); // - Reset flow counter nFlowSensorCount = 0; - nFlowSensorCount_last = 0; flowInterruptEnabled(true); // - Turn ON pump @@ -166,12 +141,12 @@ void PlantSystem_tick(void) digitalWrite(WATER_PUMP_EN_PIN, HIGH); esp_task_wdt_reset(); - nTimeout = millis() + 10000; // 10sec time-out for watering + nTimeout = millis() + 20000; // 20sec time-out for watering // Calculate water flow uint32_t nWaterFlow_millis_timestamp = millis(); float flowMilliLitres = 0.0; - unsigned long totalMilliLitres = 0; + uint32_t totalMilliLitres = 0; // Wait until target mL or timeout is reached while(millis() < nTimeout) @@ -183,6 +158,12 @@ void PlantSystem_tick(void) flowInterruptEnabled(false); // Update mL + if(fImpulsePerML <= 0) + { + Serial.println("Invalid fImpulsePerML value! Please calibrate flow sensor!"); + esp_log_write(ESP_LOG_ERROR, "SKWS", "-- Invalid fImpulsePerML value (fImpulsePerML=%f)! Please calibrate flow sensor!\n", fImpulsePerML); + fImpulsePerML = 100; + } flowMilliLitres = nFlowSensorCount/fImpulsePerML; totalMilliLitres += flowMilliLitres; @@ -213,7 +194,10 @@ void PlantSystem_tick(void) // Store last flow data and prepare for new request - nFlowSensorCount_last = 0; + + + + nFlowSensorCount_last = nFlowSensorCount; nFlowSensorCount = 0; nWatering_volume = 0; @@ -243,7 +227,18 @@ void PlantSystem_tick(void) void PlantSystem_ScanBus(void) { + // Clear/reset data from previous scan + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- Resetting internal I2C bus state.\n"); + Serial.println ("Resetting internal I2C bus state."); + gDevicesOnBusCount = 0; + gnSensorsOnBusCnt = 0; + gnSolenoidsOnBusCnt = 0; + memset(gDevicesOnBus, 0 , sizeof(gDevicesOnBus)); + memset(gSensorsOnBus, 0 , sizeof(gSensorsOnBus)); + memset(gSolenoidsOnBus, 0 , sizeof(gSolenoidsOnBus)); + Serial.println ("Scanning I2C bus..."); + esp_log_write(ESP_LOG_DEBUG, "SKWS", "-- Resetting internal I2C bus state.\n"); for (uint8_t i=I2C_ADDRESS_LIMIT_LOWER; i0 && currentPos 0 && currentPos0 && currentPos0 && %d<%d)\n", len, currentPos, nMaxLen); return false; } } @@ -116,6 +121,7 @@ bool PlantSystem_WAPI_SensorData(char *pBuff, uint32_t nMaxLen) gnSensorsOnBusCnt); if(len<0) { + esp_log_write(ESP_LOG_ERROR, "SKWS", "-- PlantSystem_WAPI_SensorData: Buffer too short (e:%d)\n", len); Serial.println("Buffer too short"); return false; } @@ -162,10 +168,12 @@ bool PlantSystem_WAPI_SensorData(char *pBuff, uint32_t nMaxLen) Serial.println(len, DEC); Serial.print("nMaxLen: "); - Serial.println(len, DEC); + Serial.println(nMaxLen, DEC); Serial.print("currentPos :"); - Serial.println(len, DEC); + Serial.println(currentPos, DEC); + + esp_log_write(ESP_LOG_ERROR, "SKWS", "-- PlantSystem_WAPI_SensorData: Buffer too short. snprintf ret:%d, nMaxLen:%d currentPos:%d\n", len, nMaxLen, currentPos); return false; } @@ -179,6 +187,7 @@ bool PlantSystem_WAPI_SensorData(char *pBuff, uint32_t nMaxLen) } else { + esp_log_write(ESP_LOG_ERROR, "SKWS", "-- PlantSystem_WAPI_SensorData fail (len:%d)\n", len); return false; } }