Skip to content

chris2172/OctoDisplay

Repository files navigation

I purchased this display https://www.elecrow.com/esp32-display-4-3-inch-hmi-display-rgb-tft-lcd-touch-screen.html A Crowpanel 4.3" display and was intending to install OpenHasp on it, but unfortunatly it did not work.

So I thought what I could do with it & decided why not get AI to program it for me. Using https://copilot.microsoft.com/ I asked

Question 1: This Project is for a Crowpanel 4.3" Display I want to add functionality that if the following setting values are not available Wifi SSID: Wifi Password: Home Assistant API: Home Assistant IP: MQTT Host: MQTT User: MQTT Password: MQTT Port: Should be pre defined as the default MQTT Port The device publishes its SSID as OctoDisplay with no password Display ui_ScreenSetup and replace "text" on LabelURL with the URL to go to And allow the user to connect to that SSID and go to a web page, and set those settings. By hitting Submit, the device will reboot, log on to the SSID provided and show the Wifi Connection status on the screen ui_ScreenIHD Label6

#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>

#include <Wire.h>
#include <SPI.h>
#include <lvgl.h>
#include "ui.h"
#include "gfx_conf.h"

// Existing display buffers and driver declarations
static lv_disp_draw_buf_t draw_buf;
static lv_color_t disp_draw_buf1[screenWidth * screenHeight / 10];
static lv_color_t disp_draw_buf2[screenWidth * screenHeight / 10];
static lv_disp_drv_t disp_drv;

// For nonvolatile storage (ESP32)
Preferences preferences;

// Default MQTT port (as a string for storage; you could also store it as an int)
const String defaultMQTTPort = "1883";

// Web server running on port 80
WebServer server(80);

// Flag to tell whether we need configuration
bool configMissing = false;

// ---------------------------------------------------------------------------
// 1. Function to load settings from nonvolatile storage.
//    If any required value is missing, we mark configMissing true.
// ---------------------------------------------------------------------------
void loadConfigurations() {
    preferences.begin("config", false);
    String wifiSSID = preferences.getString("wifiSSID", "");
    String wifiPass = preferences.getString("wifiPass", "");
    String haAPI    = preferences.getString("haAPI", "");
    String haIP     = preferences.getString("haIP", "");
    String mqttHost = preferences.getString("mqttHost", "");
    String mqttUser = preferences.getString("mqttUser", "");
    String mqttPass = preferences.getString("mqttPass", "");
    String mqttPort = preferences.getString("mqttPort", "");
    
    // Check if essential values are provided (except mqttPort, which gets a default)
    if (wifiSSID == "" || wifiPass == "" || haAPI == "" || haIP == "" ||
        mqttHost == "" || mqttUser == "" || mqttPass == "") {
        configMissing = true;
    }

    // If MQTT Port is not set, save the default
    if (mqttPort == "") {
        preferences.putString("mqttPort", defaultMQTTPort);
    }
    preferences.end();
}

// ---------------------------------------------------------------------------
// 2. Webserver handlers for the configuration portal
// ---------------------------------------------------------------------------
void handleRoot() {
    // Generate a simple HTML form with fields for each setting.
    String page = "<html><head><title>Device Configuration</title></head><body>";
    page += "<h1>Configure Your Device</h1>";
    page += "<form method='POST' action='/save'>";
    page += "Wifi SSID: <input type='text' name='wifiSSID'><br><br>";
    page += "Wifi Password: <input type='password' name='wifiPass'><br><br>";
    page += "Home Assistant API: <input type='text' name='haAPI'><br><br>";
    page += "Home Assistant IP: <input type='text' name='haIP'><br><br>";
    page += "MQTT Host: <input type='text' name='mqttHost'><br><br>";
    page += "MQTT User: <input type='text' name='mqttUser'><br><br>";
    page += "MQTT Password: <input type='password' name='mqttPass'><br><br>";
    page += "MQTT Port: <input type='text' name='mqttPort' placeholder='1883'><br><br>";
    page += "<input type='submit' value='Submit'>";
    page += "</form></body></html>";
    server.send(200, "text/html", page);
}

void handleSave() {
    if (server.method() == HTTP_POST) {
        preferences.begin("config", false);
        // Save the values from the form submission
        preferences.putString("wifiSSID", server.arg("wifiSSID"));
        preferences.putString("wifiPass", server.arg("wifiPass"));
        preferences.putString("haAPI", server.arg("haAPI"));
        preferences.putString("haIP", server.arg("haIP"));
        preferences.putString("mqttHost", server.arg("mqttHost"));
        preferences.putString("mqttUser", server.arg("mqttUser"));
        preferences.putString("mqttPass", server.arg("mqttPass"));
        
        String port = server.arg("mqttPort");
        if (port == "") {
            port = defaultMQTTPort;
        }
        preferences.putString("mqttPort", port);
        preferences.end();
        
        // Inform the user and then restart
        server.send(200, "text/html", "Settings saved. The device will now restart.");
        delay(2000);
        ESP.restart();
    }
}

// ---------------------------------------------------------------------------
// 3. Function to start the configuration portal: 
//    sets up an access point and the web server.
// ---------------------------------------------------------------------------
void startConfigPortal() {
    // Start Wi-Fi in Access Point mode with no password.
    WiFi.softAP("OctoDisplay");
    IPAddress ip = WiFi.softAPIP();
    Serial.print("AP started. Connect to AP IP: ");
    Serial.println(ip);

    // Update the display's LabelURL on ui_ScreenSetup with the AP IP.
    // (Assuming you have a function or method to update that label.)
    // For example:
    // ui_LabelURL->setText(ip.toString().c_str());

    // Set web server routes.
    server.on("/", HTTP_GET, handleRoot);
    server.on("/save", HTTP_POST, handleSave);
    server.begin();
    Serial.println("Configuration web server started.");
}

// ---------------------------------------------------------------------------
// 4. Display and Touch callbacks (same as your current code)
// ---------------------------------------------------------------------------
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);
    tft.pushImageDMA(area->x1, area->y1, w, h, (lgfx::rgb565_t*)&color_p->full);
    lv_disp_flush_ready(disp);
}

void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
    uint16_t touchX, touchY;
    bool touched = tft.getTouch(&touchX, &touchY);
    if (!touched) {
        data->state = LV_INDEV_STATE_REL;
    } else {
        data->state = LV_INDEV_STATE_PR;
        data->point.x = touchX;
        data->point.y = touchY;
        Serial.print("Data x ");
        Serial.println(touchX);
        Serial.print("Data y ");
        Serial.println(touchY);
    }
}

// ---------------------------------------------------------------------------
// 5. Setup: initialize display, load configuration, and either launch
//    the config portal or proceed to connect to Wi-Fi.
// ---------------------------------------------------------------------------
void setup() {
    Serial.begin(115200);
    Serial.println("Starting CrowPanel ESP32 Display Project");

    // GPIO initialization as per your board (CrowPanel 4.3" example)
    #if defined (CrowPanel_43)
      pinMode(20, OUTPUT);
      digitalWrite(20, LOW);
      pinMode(19, OUTPUT);
      digitalWrite(19, LOW);
      pinMode(35, OUTPUT);
      digitalWrite(35, LOW);
      pinMode(38, OUTPUT);
      digitalWrite(38, LOW);
      pinMode(0, OUTPUT);  // TOUCH-CS
    #endif

    // Display initialization (your existing code)
    tft.begin();
    tft.fillScreen(TFT_BLACK);
    tft.setTextSize(2);
    delay(200);

    lv_init();
    delay(100);
    lv_disp_draw_buf_init(&draw_buf, disp_draw_buf1, disp_draw_buf2, screenWidth * screenHeight / 10);
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.full_refresh = 1;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register(&indev_drv);

    tft.fillScreen(TFT_BLACK);

    // Initialize UI (this sets up your default screen(s))
    ui_init();

    // Load configuration settings from nonvolatile storage.
    loadConfigurations();

    if (configMissing) {
        Serial.println("Configuration missing. Launching config portal mode.");
        // Switch the display to the setup screen.
        // For example, if you have a function to change screens:
        // showScreen(ui_ScreenSetup);
        startConfigPortal();
    } else {
        // If all required settings are present, attempt to connect to Wi-Fi.
        preferences.begin("config", false);
        String ssid = preferences.getString("wifiSSID", "");
        String pass = preferences.getString("wifiPass", "");
        preferences.end();

        Serial.print("Connecting to WiFi: ");
        Serial.println(ssid);
        WiFi.begin(ssid.c_str(), pass.c_str());
    
        int counter = 0;
        while (WiFi.status() != WL_CONNECTED && counter < 40) {  // wait up to 20 seconds
            delay(500);
            Serial.print(".");
            counter++;
        }
        if (WiFi.status() == WL_CONNECTED) {
            Serial.println("\nWiFi Connected!");
            // Switch to the normal operational screen.
            // For example:
            // showScreen(ui_ScreenIHD);
            // And update Label6 with a positive connection status:
            // ui_Label6->setText("Connected.");
        } else {
            Serial.println("\nWiFi connection failed.");
            // Consider reverting to configuration mode or displaying an error.
        }
    }
    Serial.println("Setup done.");
}

// ---------------------------------------------------------------------------
// 6. Loop: handle LVGL tasks and (if in config mode) web server requests.
// ---------------------------------------------------------------------------
void loop() {
    lv_timer_handler();
    // If the device is in configuration mode, keep handling web server requests.
    if (configMissing) {
        server.handleClient();
    }
    delay(5);
}

Question 2: Please change this HTML Layout to look more profesional, remembering that it is on an ESP32-S3-Wroom without any internet access

String page = "<!DOCTYPE html>"
              "<html lang='en'>"
              "<head>"
              "  <meta charset='UTF-8'>"
              "  <meta name='viewport' content='width=device-width, initial-scale=1.0'>"
              "  <title>Device Configuration</title>"
              "  <style>"
              "    body {"
              "      font-family: Arial, sans-serif;"
              "      background-color: #f7f7f7;"
              "      margin: 0;"
              "      padding: 0;"
              "    }"
              "    .container {"
              "      max-width: 600px;"
              "      margin: 50px auto;"
              "      background: #fff;"
              "      padding: 30px;"
              "      border-radius: 8px;"
              "      box-shadow: 0 2px 8px rgba(0,0,0,0.1);"
              "    }"
              "    h1 {"
              "      text-align: center;"
              "      color: #333;"
              "      margin-bottom: 30px;"
              "    }"
              "    form {"
              "      display: flex;"
              "      flex-direction: column;"
              "    }"
              "    label {"
              "      margin: 10px 0 5px;"
              "      font-weight: bold;"
              "    }"
              "    input[type='text'],"
              "    input[type='password'] {"
              "      padding: 10px;"
              "      border: 1px solid #ccc;"
              "      border-radius: 4px;"
              "      font-size: 16px;"
              "    }"
              "    input[type='submit'] {"
              "      margin-top: 20px;"
              "      padding: 10px;"
              "      background-color: #4CAF50;"
              "      border: none;"
              "      border-radius: 4px;"
              "      color: #fff;"
              "      font-size: 16px;"
              "      cursor: pointer;"
              "    }"
              "    input[type='submit']:hover {"
              "      background-color: #45a049;"
              "    }"
              "  </style>"
              "</head>"
              "<body>"
              "  <div class='container'>"
              "    <h1>Configure Your Device</h1>"
              "    <form method='POST' action='/save'>"
              "      <label for='wifiSSID'>Wifi SSID:</label>"
              "      <input type='text' id='wifiSSID' name='wifiSSID'>"
              "      <label for='wifiPass'>Wifi Password:</label>"
              "      <input type='password' id='wifiPass' name='wifiPass'>"
              "      <label for='haAPI'>Home Assistant API:</label>"
              "      <input type='text' id='haAPI' name='haAPI'>"
              "      <label for='haIP'>Home Assistant IP:</label>"
              "      <input type='text' id='haIP' name='haIP'>"
              "      <label for='mqttHost'>MQTT Host:</label>"
              "      <input type='text' id='mqttHost' name='mqttHost'>"
              "      <label for='mqttUser'>MQTT User:</label>"
              "      <input type='text' id='mqttUser' name='mqttUser'>"
              "      <label for='mqttPass'>MQTT Password:</label>"
              "      <input type='password' id='mqttPass' name='mqttPass'>"
              "      <label for='mqttPort'>MQTT Port:</label>"
              "      <input type='text' id='mqttPort' name='mqttPort' placeholder='1883'>"
              "      <input type='submit' value='Submit'>"
              "    </form>"
              "  </div>"
              "</body>"
              "</html>";

Question 3: That worked, but I was thinking how do I re test it. So I was thinking if I hold the boot button for 20 Seconds I want it to clear all the settings then reboot All the files can be found https://github.com/chris2172/OctoDisplay

#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <Wire.h>
#include <SPI.h>
#include <lvgl.h>
#include "ui.h"
#include "gfx_conf.h"

// Existing display buffers and driver declarations
static lv_disp_draw_buf_t draw_buf;
static lv_color_t disp_draw_buf1[screenWidth * screenHeight / 10];
static lv_color_t disp_draw_buf2[screenWidth * screenHeight / 10];
static lv_disp_drv_t disp_drv;

// For nonvolatile storage (ESP32)
Preferences preferences;

// Default MQTT port
const String defaultMQTTPort = "1883";

// Web server running on port 80
WebServer server(80);

// Flag to tell whether configuration is missing
bool configMissing = false;

// ---------------------------------------------------
// Button reset configuration settings variables
// ---------------------------------------------------
const int resetButtonPin = 0;  // Change this if needed.
unsigned long buttonPressStart = 0;
const unsigned long holdDuration = 20000; // 20,000 milliseconds = 20 seconds

// ---------------------------------------------------
// 1. Load configuration and flag missing settings
// ---------------------------------------------------
void loadConfigurations() {
    preferences.begin("config", false);
    String wifiSSID = preferences.getString("wifiSSID", "");
    String wifiPass = preferences.getString("wifiPass", "");
    String haAPI    = preferences.getString("haAPI", "");
    String haIP     = preferences.getString("haIP", "");
    String mqttHost = preferences.getString("mqttHost", "");
    String mqttUser = preferences.getString("mqttUser", "");
    String mqttPass = preferences.getString("mqttPass", "");
    String mqttPort = preferences.getString("mqttPort", "");
    
    // Check essential values (MQTT port gets a default if missing)
    if (wifiSSID == "" || wifiPass == "" || haAPI == "" || haIP == "" ||
        mqttHost == "" || mqttUser == "" || mqttPass == "") {
        configMissing = true;
    }
    
    if (mqttPort == "") {
        preferences.putString("mqttPort", defaultMQTTPort);
    }
    preferences.end();
}

// ---------------------------------------------------
// 2. Webserver handlers for the configuration portal
// ---------------------------------------------------
void handleRoot() {
    String page = "<!DOCTYPE html>"
                  "<html lang='en'>"
                  "<head>"
                  "  <meta charset='UTF-8'>"
                  "  <meta name='viewport' content='width=device-width, initial-scale=1.0'>"
                  "  <title>Device Configuration</title>"
                  "  <style>"
                  "    body { font-family: Arial, sans-serif; background-color: #f7f7f7; margin:0; padding:0; }"
                  "    .container { max-width: 600px; margin: 50px auto; background: #fff; padding: 30px; border-radius: 8px;"
                  "                 box-shadow: 0 2px 8px rgba(0,0,0,0.1); }"
                  "    h1 { text-align: center; color: #333; margin-bottom: 30px; }"
                  "    form { display: flex; flex-direction: column; }"
                  "    label { margin: 10px 0 5px; font-weight: bold; }"
                  "    input[type='text'], input[type='password'] { padding: 10px; border: 1px solid #ccc;"
                  "                 border-radius: 4px; font-size: 16px; }"
                  "    input[type='submit'] { margin-top: 20px; padding: 10px; background-color: #4CAF50; "
                  "                 border: none; border-radius: 4px; color: #fff; font-size: 16px; cursor: pointer; }"
                  "    input[type='submit']:hover { background-color: #45a049; }"
                  "  </style>"
                  "</head>"
                  "<body>"
                  "  <div class='container'>"
                  "    <h1>Configure Your Device</h1>"
                  "    <form method='POST' action='/save'>"
                  "      <label for='wifiSSID'>Wifi SSID:</label>"
                  "      <input type='text' id='wifiSSID' name='wifiSSID'>"
                  "      <label for='wifiPass'>Wifi Password:</label>"
                  "      <input type='password' id='wifiPass' name='wifiPass'>"
                  "      <label for='haAPI'>Home Assistant API:</label>"
                  "      <input type='text' id='haAPI' name='haAPI'>"
                  "      <label for='haIP'>Home Assistant IP:</label>"
                  "      <input type='text' id='haIP' name='haIP'>"
                  "      <label for='mqttHost'>MQTT Host:</label>"
                  "      <input type='text' id='mqttHost' name='mqttHost'>"
                  "      <label for='mqttUser'>MQTT User:</label>"
                  "      <input type='text' id='mqttUser' name='mqttUser'>"
                  "      <label for='mqttPass'>MQTT Password:</label>"
                  "      <input type='password' id='mqttPass' name='mqttPass'>"
                  "      <label for='mqttPort'>MQTT Port:</label>"
                  "      <input type='text' id='mqttPort' name='mqttPort' placeholder='1883'>"
                  "      <input type='submit' value='Submit'>"
                  "    </form>"
                  "  </div>"
                  "</body>"
                  "</html>";
    server.send(200, "text/html", page);
}

void handleSave() {
    if (server.method() == HTTP_POST) {
        preferences.begin("config", false);
        preferences.putString("wifiSSID", server.arg("wifiSSID"));
        preferences.putString("wifiPass", server.arg("wifiPass"));
        preferences.putString("haAPI", server.arg("haAPI"));
        preferences.putString("haIP", server.arg("haIP"));
        preferences.putString("mqttHost", server.arg("mqttHost"));
        preferences.putString("mqttUser", server.arg("mqttUser"));
        preferences.putString("mqttPass", server.arg("mqttPass"));
        
        String port = server.arg("mqttPort");
        if (port == "") {
            port = defaultMQTTPort;
        }
        preferences.putString("mqttPort", port);
        preferences.end();
        
        server.send(200, "text/html", "Settings saved. The device will now restart.");
        delay(2000);
        ESP.restart();
    }
}

void startConfigPortal() {
    WiFi.softAP("OctoDisplay");
    IPAddress ip = WiFi.softAPIP();
    Serial.print("AP started. Connect to AP IP: "); Serial.println(ip);
    
    // Update the setup screen’s LabelURL if needed:
    // For example: ui_LabelURL->setText(ip.toString().c_str());
    
    server.on("/", HTTP_GET, handleRoot);
    server.on("/save", HTTP_POST, handleSave);
    server.begin();
    Serial.println("Configuration web server started.");
}

// ---------------------------------------------------
// 3. Display and touch callback functions (unchanged)
// ---------------------------------------------------
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);
    tft.pushImageDMA(area->x1, area->y1, w, h, (lgfx::rgb565_t*)&color_p->full);
    lv_disp_flush_ready(disp);
}

void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
    uint16_t touchX, touchY;
    bool touched = tft.getTouch(&touchX, &touchY);
    if (!touched) {
        data->state = LV_INDEV_STATE_REL;
    } else {
        data->state = LV_INDEV_STATE_PR;
        data->point.x = touchX;
        data->point.y = touchY;
        Serial.print("Data x ");
        Serial.println(touchX);
        Serial.print("Data y ");
        Serial.println(touchY);
    }
}

// ---------------------------------------------------
// 4. Setup: initialize hardware, display, configuration and WiFi logic
// ---------------------------------------------------
void setup() {
    Serial.begin(115200);
    Serial.println("Starting CrowPanel ESP32 Display Project");
    
    // Configure the reset (boot) button pin.
    // NOTE: Confirm that this pin doesn't conflict with other functions (e.g., touchscreen).
    pinMode(resetButtonPin, INPUT_PULLUP);
    
    // Board-specific GPIO initialization (example for CrowPanel 4.3")
    #if defined (CrowPanel_43)
      pinMode(20, OUTPUT);
      digitalWrite(20, LOW);
      pinMode(19, OUTPUT);
      digitalWrite(19, LOW);
      pinMode(35, OUTPUT);
      digitalWrite(35, LOW);
      pinMode(38, OUTPUT);
      digitalWrite(38, LOW);
      // The following line was originally for TOUCH-CS. Adjust as needed.
      // pinMode(0, OUTPUT); // Already used as resetButtonPin in this example.
    #endif

    tft.begin();
    tft.fillScreen(TFT_BLACK);
    tft.setTextSize(2);
    delay(200);

    lv_init();
    delay(100);
    lv_disp_draw_buf_init(&draw_buf, disp_draw_buf1, disp_draw_buf2, screenWidth * screenHeight / 10);
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.full_refresh = 1;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register(&indev_drv);

    tft.fillScreen(TFT_BLACK);
    ui_init();

    loadConfigurations();

    if (configMissing) {
        Serial.println("Configuration missing. Launching config portal mode.");
        // Optionally switch display to ui_ScreenSetup here.
        startConfigPortal();
    } else {
        // If configuration exists, connect to WiFi.
        preferences.begin("config", false);
        String ssid = preferences.getString("wifiSSID", "");
        String pass = preferences.getString("wifiPass", "");
        preferences.end();
    
        Serial.print("Connecting to WiFi: "); Serial.println(ssid);
        WiFi.begin(ssid.c_str(), pass.c_str());
    
        int counter = 0;
        while (WiFi.status() != WL_CONNECTED && counter < 40) {  // wait up to ~20 seconds
            delay(500);
            Serial.print(".");
            counter++;
        }
        if (WiFi.status() == WL_CONNECTED) {
            Serial.println("\nWiFi Connected!");
            // Optionally switch display to ui_ScreenIHD and update status Label6.
        } else {
            Serial.println("\nWiFi connection failed.");
            // Handle failed connection appropriately.
        }
    }
    Serial.println("Setup done.");
}

// ---------------------------------------------------
// 5. Loop: LVGL tasks, config portal handling and reset check.
// ---------------------------------------------------
void loop() {
    lv_timer_handler();
    
    // Check if the reset button is held for 20 seconds
    if (digitalRead(resetButtonPin) == LOW) {  // Assuming active LOW (pressed)
        if (buttonPressStart == 0) {
            buttonPressStart = millis();  // Start timing the press
        } else {
            // If pressed continuously for 20 seconds, clear settings
            if (millis() - buttonPressStart >= holdDuration) {
                Serial.println("Boot button held for 20 seconds. Clearing configuration...");
                preferences.begin("config", false);
                preferences.clear();
                preferences.end();
                delay(1000);
                ESP.restart();
            }
        }
    } else {
        // Button released; reset the timer
        buttonPressStart = 0;
    }
    
    // If in configuration portal mode, handle web server client requests.
    if (configMissing) {
        server.handleClient();
    }
    
    delay(5);
}

Question 4: Server Provided in the settings.

I need the values from the following MQTT Topics elecrow/grid/power/state elecrow/grid/energy_in/state elecrow/grid/energy_out/state elecrow/grid/price_in/state elecrow/grid/price_out/state

This project will have many more in the following format elecrow/pv elecrow/load elecrow/battery elecrow/inverter elecrow/car

All with sub topics so I need the code to be easily editable to add additional topics

#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <SPI.h>
#include <lvgl.h>
#include "ui.h"
#include "gfx_conf.h"

// -----------------------------
// Display-related declarations
// -----------------------------
static lv_disp_draw_buf_t draw_buf;
static lv_color_t disp_draw_buf1[screenWidth * screenHeight / 10];
static lv_color_t disp_draw_buf2[screenWidth * screenHeight / 10];
static lv_disp_drv_t disp_drv;

// -----------------------------
// Nonvolatile storage and web server
// -----------------------------
Preferences preferences;
const String defaultMQTTPort = "1883";
WebServer server(80);
bool configMissing = false;

// -----------------------------
// Reset button settings
// -----------------------------
const int resetButtonPin = 0;   // Choose appropriate pin (make sure it does not conflict)
unsigned long buttonPressStart = 0;
const unsigned long holdDuration = 20000;  // 20 seconds

// -----------------------------
// MQTT declarations
// -----------------------------
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

// List your MQTT topics here. Additional topics and subtopics can be added to this list.
const char* mqttTopics[] = {
  "elecrow/grid/power/state",
  "elecrow/grid/energy_in/state",
  "elecrow/grid/energy_out/state",
  "elecrow/grid/price_in/state",
  "elecrow/grid/price_out/state",
  // You can easily add more topics under different branches:
  // "elecrow/pv/state", "elecrow/load/state", etc.
};
const int numTopics = sizeof(mqttTopics) / sizeof(mqttTopics[0]);

// MQTT callback: handles inbound messages
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String message;
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.print("Received on topic [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(message);

  // Example: Update display or global variables based on topic
  // if (String(topic) == "elecrow/grid/power/state") {
  //     ui_LabelPower->setText(message.c_str());
  // }
  // Add similar conditional blocks for other topics (or use a lookup table)
}

// Attempt an MQTT connection and subscribe to the topics.
void mqttConnect() {
  // The client ID can be set to any unique string (or consider using a unique value)
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mqttClient.connect("ESP32DisplayClient")) {
      Serial.println(" connected.");
      // Subscribe to every topic in the list.
      for (int i = 0; i < numTopics; i++) {
        if (mqttClient.subscribe(mqttTopics[i])) {
          Serial.print("Subscribed to: ");
          Serial.println(mqttTopics[i]);
        } else {
          Serial.print("Failed subscription: ");
          Serial.println(mqttTopics[i]);
        }
      }
    } else {
      Serial.print("Failed, rc=");
      Serial.print(mqttClient.state());
      Serial.println("; try again in 5 seconds");
      delay(5000);
    }
  }
}

// -----------------------------
// Configuration Portal Web Handlers
// -----------------------------
void handleRoot() {
    String page = "<!DOCTYPE html>"
                  "<html lang='en'>"
                  "<head>"
                  "  <meta charset='UTF-8'>"
                  "  <meta name='viewport' content='width=device-width, initial-scale=1.0'>"
                  "  <title>Device Configuration</title>"
                  "  <style>"
                  "    body { font-family: Arial, sans-serif; background-color: #f7f7f7; margin:0; padding:0; }"
                  "    .container { max-width: 600px; margin: 50px auto; background: #fff; padding: 30px; border-radius: 8px;"
                  "                 box-shadow: 0 2px 8px rgba(0,0,0,0.1); }"
                  "    h1 { text-align: center; color: #333; margin-bottom: 30px; }"
                  "    form { display: flex; flex-direction: column; }"
                  "    label { margin: 10px 0 5px; font-weight: bold; }"
                  "    input[type='text'], input[type='password'] { padding: 10px; border: 1px solid #ccc;"
                  "                 border-radius: 4px; font-size: 16px; }"
                  "    input[type='submit'] { margin-top: 20px; padding: 10px; background-color: #4CAF50; "
                  "                 border: none; border-radius: 4px; color: #fff; font-size: 16px; cursor: pointer; }"
                  "    input[type='submit']:hover { background-color: #45a049; }"
                  "  </style>"
                  "</head>"
                  "<body>"
                  "  <div class='container'>"
                  "    <h1>Configure Your Device</h1>"
                  "    <form method='POST' action='/save'>"
                  "      <label for='wifiSSID'>Wifi SSID:</label>"
                  "      <input type='text' id='wifiSSID' name='wifiSSID'>"
                  "      <label for='wifiPass'>Wifi Password:</label>"
                  "      <input type='password' id='wifiPass' name='wifiPass'>"
                  "      <label for='haAPI'>Home Assistant API:</label>"
                  "      <input type='text' id='haAPI' name='haAPI'>"
                  "      <label for='haIP'>Home Assistant IP:</label>"
                  "      <input type='text' id='haIP' name='haIP'>"
                  "      <label for='mqttHost'>MQTT Host:</label>"
                  "      <input type='text' id='mqttHost' name='mqttHost'>"
                  "      <label for='mqttUser'>MQTT User:</label>"
                  "      <input type='text' id='mqttUser' name='mqttUser'>"
                  "      <label for='mqttPass'>MQTT Password:</label>"
                  "      <input type='password' id='mqttPass' name='mqttPass'>"
                  "      <label for='mqttPort'>MQTT Port:</label>"
                  "      <input type='text' id='mqttPort' name='mqttPort' placeholder='1883'>"
                  "      <input type='submit' value='Submit'>"
                  "    </form>"
                  "  </div>"
                  "</body>"
                  "</html>";
    server.send(200, "text/html", page);
}

void handleSave() {
    if (server.method() == HTTP_POST) {
        preferences.begin("config", false);
        // Save all configuration values
        preferences.putString("wifiSSID", server.arg("wifiSSID"));
        preferences.putString("wifiPass", server.arg("wifiPass"));
        preferences.putString("haAPI", server.arg("haAPI"));
        preferences.putString("haIP", server.arg("haIP"));
        preferences.putString("mqttHost", server.arg("mqttHost"));
        preferences.putString("mqttUser", server.arg("mqttUser"));
        preferences.putString("mqttPass", server.arg("mqttPass"));

        String port = server.arg("mqttPort");
        if (port == "") {
            port = defaultMQTTPort;
        }
        preferences.putString("mqttPort", port);
        preferences.end();

        server.send(200, "text/html", "Settings saved. The device will now restart.");
        delay(2000);
        ESP.restart();
    }
}

void startConfigPortal() {
    WiFi.softAP("OctoDisplay");
    IPAddress ip = WiFi.softAPIP();
    Serial.print("AP started. Connect to AP IP: ");
    Serial.println(ip);

    // Update the display’s setup screen LabelURL if needed:
    // ui_LabelURL->setText(ip.toString().c_str());

    server.on("/", HTTP_GET, handleRoot);
    server.on("/save", HTTP_POST, handleSave);
    server.begin();
    Serial.println("Configuration web server started.");
}

// -----------------------------
// LVGL Display and Touch functions (as before)
// -----------------------------
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);
    tft.pushImageDMA(area->x1, area->y1, w, h, (lgfx::rgb565_t*)&color_p->full);
    lv_disp_flush_ready(disp);
}

void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
    uint16_t touchX, touchY;
    bool touched = tft.getTouch(&touchX, &touchY);
    if (!touched) {
        data->state = LV_INDEV_STATE_REL;
    } else {
        data->state = LV_INDEV_STATE_PR;
        data->point.x = touchX;
        data->point.y = touchY;
        Serial.print("Data x ");
        Serial.println(touchX);
        Serial.print("Data y ");
        Serial.println(touchY);
    }
}

// -----------------------------
// Load configurations and mark as missing if needed.
// -----------------------------
void loadConfigurations() {
    preferences.begin("config", false);
    String wifiSSID = preferences.getString("wifiSSID", "");
    String wifiPass = preferences.getString("wifiPass", "");
    String haAPI    = preferences.getString("haAPI", "");
    String haIP     = preferences.getString("haIP", "");
    String mqttHost = preferences.getString("mqttHost", "");
    String mqttUser = preferences.getString("mqttUser", "");
    String mqttPass = preferences.getString("mqttPass", "");
    String mqttPort = preferences.getString("mqttPort", "");
    
    // Check for essential values
    if (wifiSSID == "" || wifiPass == "" || haAPI == "" || haIP == "" ||
        mqtt

About

Home Assistant Energy In-Home Display

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published