diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.cpp b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.cpp index 7c74428..09a00bc 100644 --- a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.cpp +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaDisplay.cpp @@ -89,6 +89,10 @@ void ESPMegaDisplay::processPageReportPayload() if (rx_buffer_index != 5) return; // The second byte of the payload is the page number + // Check if the page number is different from the current page + // If it is different, we call the page change callbacks + if (rx_buffer[1] == this->currentPage) + return; this->currentPage = rx_buffer[1]; ESP_LOGV("ESPMegaDisplay", "Page change event: page=%d", this->currentPage); for (auto const &callback : page_change_callbacks) @@ -186,12 +190,15 @@ uint32_t ESPMegaDisplay::getNumber(const char *component) // The rx buffer is invalid, reset the rx buffer index rx_buffer_index = 0; continue; - } else { + } + else + { validPayload = true; break; } } - if (!validPayload) { + if (!validPayload) + { ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]); return 0; } @@ -235,7 +242,7 @@ const char *ESPMegaDisplay::getString(const char *component) if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) { ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response"); - return 0; + return nullptr; } if (rx_buffer[0] != 0x70) { @@ -243,14 +250,17 @@ const char *ESPMegaDisplay::getString(const char *component) // The rx buffer is invalid, reset the rx buffer index rx_buffer_index = 0; continue; - } else { + } + else + { validPayload = true; break; } } - if (!validPayload) { + if (!validPayload) + { ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]); - return 0; + return nullptr; } // The 2nd bytes onwards before the stop bytes is the string // The length of the string is the length of the payload minus 4 @@ -267,29 +277,62 @@ const char *ESPMegaDisplay::getString(const char *component) bool ESPMegaDisplay::getStringToBuffer(const char *component, char *buffer, uint8_t buffer_size) { + // We might be in the middle of a serial command + // We reset the rx buffer index to 0 to ensure that we don't read the wrong data + this->rx_buffer_index = 0; uint32_t start = millis(); // Send the get command this->displayAdapter->print("get "); this->displayAdapter->print(component); sendStopBytes(); // Wait for the response - if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) - return false; - // The rx buffer is valid - // The expected payload is type 0x70 - if (rx_buffer[0] != 0x70) - return false; + // Try to get a valid payload DISPLAY_FETCH_RETRY_COUNT times + // Wait for the response + bool validPayload = false; + for (int i = 0; i < DISPLAY_FETCH_RETRY_COUNT; i++) + { + if (!waitForValidPayload(DISPLAY_FETCH_TIMEOUT)) + { + ESP_LOGE("ESPMegaDisplay", "Timeout while waiting for display response"); + this->sendStopBytes(); + return 0; + } + if (rx_buffer[0] != 0x70) + { + ESP_LOGI("ESPMegaDisplay", "Invalid payload type: %d, retrying.", rx_buffer[0]); + this->sendStopBytes(); + // The rx buffer is invalid, reset the rx buffer index + rx_buffer_index = 0; + continue; + } + else + { + validPayload = true; + break; + } + } + if (!validPayload) + { + ESP_LOGE("ESPMegaDisplay", "Invalid payload type: %d, max retry excceded.", rx_buffer[0]); + this->sendStopBytes(); + return 0; + } + // The 2nd bytes onwards before the stop bytes is the string // The length of the string is the length of the payload minus 4 uint8_t length = rx_buffer_index - 4; // Check if the buffer is large enough to hold the string if (length > buffer_size) + { + ESP_LOGE("ESPMegaDisplay", "Buffer size too small"); + this->sendStopBytes(); return false; + } // Copy the string from the rx buffer to the char array memcpy(buffer, rx_buffer + 1, length); // Add the null terminator buffer[length] = '\0'; - return true; + return 1; } /** diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp index e75f2eb..24201e1 100644 --- a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #define FRAM_ADDRESS 0x56 #define INPUT_BANK_A_ADDRESS 0x21 diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaTCP.hpp b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaTCP.hpp new file mode 100644 index 0000000..5f59860 --- /dev/null +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaTCP.hpp @@ -0,0 +1,9 @@ +// ESPMega TCP API +#include + +class ESPMegaTCP +{ + public: + void begin(); + void loop(); +}; \ No newline at end of file diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaWebServer.hpp.disabled b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaWebServer.hpp similarity index 89% rename from ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaWebServer.hpp.disabled rename to ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaWebServer.hpp index 708ea86..639085f 100644 --- a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaWebServer.hpp.disabled +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaWebServer.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -21,4 +22,5 @@ class ESPMegaWebServer void configHandler(AsyncWebServerRequest *request); void saveConfigHandler(AsyncWebServerRequest *request); void otaHandler(AsyncWebServerRequest *request); + void restAPIHandler(AsyncWebServerRequest *request); }; \ No newline at end of file diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.cpp b/ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.cpp index 50df5c5..be1bb1f 100644 --- a/ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.cpp +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/InternalDisplay.cpp @@ -75,12 +75,117 @@ void InternalDisplay::handlePageChange(uint8_t page) void InternalDisplay::saveNetworkConfig() { - // TODO: implementation + // The network config page have the following components: + // ip_set -> a text input to set the ip address + // netmask_set -> a text input to set the netmask + // gateway_set -> a text input to set the gateway + // dns_set -> a text input to set the dns + // host_set -> a text input to set the hostname + + Serial.println("Saving network config"); + this->sendStopBytes(); + + // Save the ip address + IPAddress ip; + // 000.000.000.000, 16 characters, 3 dots, 3 characters per octet, 1 null terminator + char ip_buffer[30]; + Serial.println("Getting ip_set.txt"); + this->sendStopBytes(); + if(!this->getStringToBuffer("ip_set.txt", ip_buffer, 30)) + { + Serial.println("Failed to get ip_set.txt"); + this->sendStopBytes(); + return; + } + Serial.println("Got ip_set.txt"); + this->sendStopBytes(); + // Validate the ip address + if (!ip.fromString(ip_buffer)) { + Serial.println("Invalid ip address"); + Serial.print("The recieved IP is: "); + Serial.println(ip_buffer); + this->sendStopBytes(); + return; + } + + // Save the netmask + IPAddress netmask; + if (!this->getStringToBuffer("netmask_set.txt", ip_buffer, 30)) { + return; + } + // Validate the netmask + if (!netmask.fromString(ip_buffer)) { + return; + } + + // Save the gateway + IPAddress gateway; + if (!this->getStringToBuffer("gateway_set.txt", ip_buffer, 30)) { + return; + } + // Validate the gateway + if (!gateway.fromString(ip_buffer)) { + return; + } + + // Save the dns + IPAddress dns; + if(!this->getStringToBuffer("dns_set.txt", ip_buffer, 30)) + return; + // Validate the dns + if (!dns.fromString(ip_buffer)) + return; + + // Save the hostname + if(!this->getStringToBuffer("host_set.txt", this->networkConfig->hostname, 32)) + return; + + // Write the ip address, netmask and gateway to the network config + this->networkConfig->ip = ip; + this->networkConfig->subnet = netmask; + this->networkConfig->gateway = gateway; + this->networkConfig->dns1 = dns; + this->networkConfig->dns2 = dns; + this->networkConfig->useStaticIp = true; + this->networkConfig->useWifi = false; + this->networkConfig->wifiUseAuth = false; + this->iot->saveNetworkConfig(); + ESP.restart(); } void InternalDisplay::saveMQTTConfig() { - // TODO: implementation + // The MQTT config page have the following components: + // mqttsv_set -> a text input to set the mqtt server + // port_set -> a text input to set the mqtt port + // use_auth -> a checkbox to enable/disable mqtt authentication + // user_set -> a text input to set the mqtt username + // password_set -> a text input to set the mqtt password + // topic_set -> a text input to set the mqtt base topic + + // Save the mqtt server + if(!this->getStringToBuffer("mqttsv_set.txt", this->mqttConfig->mqtt_server, 16)) + return; + + // Save the mqtt port + this->mqttConfig->mqtt_port = this->getNumber("port_set.val"); + + // Save the mqtt username + if(!this->getStringToBuffer("user_set.txt", this->mqttConfig->mqtt_user, 16)) + return; + + // Save the mqtt password + if(!this->getStringToBuffer("password_set.txt", this->mqttConfig->mqtt_password, 16)) + return; + + // Save the mqtt base topic + if(!this->getStringToBuffer("topic_set.txt", this->mqttConfig->base_topic, 16)) + return; + + // Save the mqtt use auth + this->mqttConfig->mqtt_useauth = this->getNumber("use_auth.val") == 1 ? true : false; + this->iot->saveMqttConfig(); + ESP.restart(); } void InternalDisplay::updateStatusIcons(bool networkStatus, bool mqttStatus) @@ -109,9 +214,19 @@ void InternalDisplay::refreshPage(uint8_t page) this->refreshDashboard(); break; case INTERNAL_DISPLAY_INPUT_PAGE: + if (this->inputCard == nullptr) + { + this->jumpToPage(INTERNAL_DISPLAY_INPUT_NULL_PTR_PAGE); + break; + } this->refreshInput(); break; case INTERNAL_DISPLAY_OUTPUT_PAGE: + if (this->outputCard == nullptr) + { + this->jumpToPage(INTERNAL_DISPLAY_OUTPUT_NULL_PTR_PAGE); + break; + } this->refreshOutput(); break; case INTERNAL_DISPLAY_AC_PAGE: @@ -382,6 +497,14 @@ void InternalDisplay::handleTouch(uint8_t page, uint8_t component, uint8_t type) case INTERNAL_DISPLAY_PWM_ADJUSTMENT_PAGE: this->handlePWMAdjustmentTouch(type, component); break; + case INTERNAL_DISPLAY_NETWORK_CONFIG_PAGE: + if (type == TOUCH_TYPE_RELEASE && component == 7) + this->saveNetworkConfig(); + break; + case INTERNAL_DISPLAY_MQTT_CONFIG_PAGE: + if (type == TOUCH_TYPE_RELEASE && component == 2) + this->saveMQTTConfig(); + break; default: break; } @@ -495,7 +618,7 @@ void InternalDisplay::refreshNetworkConfig() // netmask_set -> a text input to set the netmask // gateway_set -> a text input to set the gateway // dns_set -> a text input to set the dns - // hostname_set -> a text input to set the hostnam + // host_set -> a text input to set the hostname // Refresh the ip address this->displayAdapter->print("ip_set.txt=\""); @@ -518,7 +641,7 @@ void InternalDisplay::refreshNetworkConfig() this->displayAdapter->print("\""); this->sendStopBytes(); // Refresh the hostname - this->displayAdapter->print("hostname_set.txt=\""); + this->displayAdapter->print("host_set.txt=\""); this->displayAdapter->print(this->networkConfig->hostname); this->displayAdapter->print("\""); this->sendStopBytes(); diff --git a/ESPMegaPRO-firmware/platformio.ini b/ESPMegaPRO-firmware/platformio.ini index ffb1631..ceed429 100644 --- a/ESPMegaPRO-firmware/platformio.ini +++ b/ESPMegaPRO-firmware/platformio.ini @@ -28,6 +28,7 @@ lib_deps = adafruit/Adafruit PWM Servo Driver Library@^2.4.1 bblanchon/ArduinoJson@^6.21.4 robtillaart/DS18B20@^0.2.1 robtillaart/DHTNEW@^0.4.18 + https://github.com/me-no-dev/ESPAsyncWebServer.git #esphome/ESPAsyncWebServer-esphome@^3.1.0 monitor_speed = 115200 -build_flags = -DCORE_DEBUG_LEVEL=1 \ No newline at end of file +build_flags = -DCORE_DEBUG_LEVEL=3 \ No newline at end of file diff --git a/ESPMegaPRO-firmware/src/main.cpp b/ESPMegaPRO-firmware/src/main.cpp index c67dfdc..a5cb2ff 100644 --- a/ESPMegaPRO-firmware/src/main.cpp +++ b/ESPMegaPRO-firmware/src/main.cpp @@ -17,6 +17,7 @@ size_t getInfraredCode(uint8_t mode, uint8_t fan_speed, uint8_t temperature, con *codePtr = &(irCode[mode][fan_speed][temperature][0]); return sizeof(irCode[mode][fan_speed][temperature]) / sizeof(uint16_t); } + AirConditioner ac = { .max_temperature = 30, .min_temperature = 16, @@ -26,6 +27,7 @@ AirConditioner ac = { .fan_speed_names = fan_speed_names, .getInfraredCode = &getInfraredCode }; + ClimateCard climateCard = ClimateCard(14, ac); void input_change_callback(uint8_t pin, uint8_t value) { @@ -35,76 +37,55 @@ void input_change_callback(uint8_t pin, uint8_t value) { Serial.println(value); } +void setNetworkConfig() { + NetworkConfig config = { + .ip = {192, 168, 0, 11}, + .gateway = {192, 168, 0, 1}, + .subnet = {255, 255, 255, 0}, + .dns1 = {192, 168, 0, 1}, + .dns2 = {192, 168, 0, 1}, + .useStaticIp = true, + .useWifi = false, + .wifiUseAuth = false, + }; + strcpy(config.ssid, "ssid"); + strcpy(config.password, "password"); + strcpy(config.hostname, "espmega"); + Serial.println("Setting network config"); + espmega.iot->setNetworkConfig(config); + espmega.iot->saveNetworkConfig(); + +} + +void setMqttConfig() { + MqttConfig config = { + .mqtt_port = 1883, + .mqtt_useauth = false + }; + strcpy(config.mqtt_server, "192.168.0.26"); + strcpy(config.base_topic, "/espmegaoop"); + espmega.iot->setMqttConfig(config); + espmega.iot->saveMqttConfig(); +} + void setup() { - // Set each class's log level - esp_log_level_set("ESPMegaPRO", ESP_LOG_VERBOSE); - esp_log_level_set("DigitalOutputCard", ESP_LOG_VERBOSE); - esp_log_level_set("DigitalOutputIoT", ESP_LOG_VERBOSE); - esp_log_level_set("DigitalInputCard", ESP_LOG_VERBOSE); - esp_log_level_set("DigitalInputIoT", ESP_LOG_VERBOSE); - esp_log_level_set("AnalogCard", ESP_LOG_VERBOSE); - esp_log_level_set("AnalogIoT", ESP_LOG_VERBOSE); - esp_log_level_set("ClimateCard", ESP_LOG_VERBOSE); - esp_log_level_set("ClimateIoT", ESP_LOG_VERBOSE); - esp_log_level_set("InternalDisplay", ESP_LOG_VERBOSE); - esp_log_level_set("InternalDisplayIoT", ESP_LOG_VERBOSE); espmega.begin(); espmega.enableIotModule(); ETH.begin(); espmega.iot->bindEthernetInterface(Ð); - // NetworkConfig config = { - // .ip = {192, 168, 0, 11}, - // .gateway = {192, 168, 0, 1}, - // .subnet = {255, 255, 255, 0}, - // .dns1 = {192, 168, 0, 1}, - // .dns2 = {192, 168, 0, 1}, - // .useStaticIp = true, - // .useWifi = false, - // .wifiUseAuth = false, - // }; - // strcpy(config.ssid, "ssid"); - // strcpy(config.password, "password"); - // strcpy(config.hostname, "espmega"); - // Serial.println("Setting network config"); - // espmega.iot->setNetworkConfig(config); - // espmega.iot->saveNetworkConfig(); espmega.iot->loadNetworkConfig(); - Serial.println("Connecting to network"); espmega.iot->connectNetwork(); - Serial.println("Begin MQTT Modules"); - // MqttConfig mqtt_config = { - // .mqtt_port = 1883, - // .mqtt_useauth = false - // }; - // Serial.println("Setting MQTT Server"); - // strcpy(mqtt_config.mqtt_server, "192.168.0.26"); - // strcpy(mqtt_config.base_topic, "/espmegaoop"); - // Serial.println("Loading MQTT Config Struct to IoT Module"); - // espmega.iot->setMqttConfig(mqtt_config); - // espmega.iot->saveMqttConfig(); espmega.iot->loadMqttConfig(); - Serial.println("Connecting to MQTT"); espmega.iot->connectToMqtt(); - Serial.println("Registering Output Card"); espmega.iot->registerCard(0); - Serial.println("Registering Input Card"); espmega.iot->registerCard(1); - Serial.println("Registering Input Change Callback"); espmega.inputs.registerCallback(input_change_callback); - Serial.println("Installing Climate Card"); espmega.installCard(2, &climateCard); climateCard.bindFRAM(&espmega.fram, 301); climateCard.loadStateFromFRAM(); climateCard.setFRAMAutoSave(true); - Serial.println("Enabling Internal Display"); espmega.enableInternalDisplay(&Serial); espmega.display->bindClimateCard(&climateCard); - Serial.println("Initialization Routine Complete"); - Serial.write(0xFF); - Serial.write(0xFF); - Serial.write(0xFF); - Serial.println(sizeof(MqttConfig)); - Serial.println(sizeof(NetworkConfig)); } @@ -112,13 +93,14 @@ void setup() { // Every 20 seconds, dump FRAM 0-500 to serial void loop() { espmega.loop(); - // static uint32_t last_fram_dump = 0; - // if (millis() - last_fram_dump >= 20000) { - // last_fram_dump = millis(); - // Serial.println("Dumping FRAM"); - // espmega.dumpFRAMtoSerial(0, 500); - // Serial.println("Dumping FRAM ASCII"); - // espmega.dumpFRAMtoSerialASCII(0, 500); - // } - + #ifdef FRAM_DEBUG + static uint32_t last_fram_dump = 0; + if (millis() - last_fram_dump >= 20000) { + last_fram_dump = millis(); + Serial.println("Dumping FRAM"); + espmega.dumpFRAMtoSerial(0, 500); + Serial.println("Dumping FRAM ASCII"); + espmega.dumpFRAMtoSerialASCII(0, 500); + } + #endif } \ No newline at end of file