pleasedontcode

# Code Summary **Smart Controller** rev_03

Mar 7th, 2026
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 32.87 KB | None | 0 0
  1. /********* Pleasedontcode.com **********
  2.  
  3.     Pleasedontcode thanks you for automatic code generation! Enjoy your code!
  4.  
  5.     - Terms and Conditions:
  6.     You have a non-exclusive, revocable, worldwide, royalty-free license
  7.     for personal and commercial use. Attribution is optional; modifications
  8.     are allowed, but you're responsible for code maintenance. We're not
  9.     liable for any loss or damage. For full terms,
  10.     please visit pleasedontcode.com/termsandconditions.
  11.  
  12.     - Project: # Code Summary
  13.  
  14. **Smart Controller**
  15.     - Version: 003
  16.     - Source Code NOT compiled for: ESP32-S3-Box
  17.     - Source Code created on: 2026-03-08 01:51:09
  18.  
  19. ********* Pleasedontcode.com **********/
  20.  
  21. /****** SYSTEM REQUIREMENTS *****/
  22. /****** SYSTEM REQUIREMENT 1 *****/
  23.     /* Initialize 7-inch LCD touch display. Show 6 relay */
  24.     /* control buttons and 8 digital input status */
  25.     /* indicators. Update UI in real-time with touch */
  26.     /* responsiveness. */
  27. /****** SYSTEM REQUIREMENT 2 *****/
  28.     /* Configure Modbus RTU on UART1 (TX=GPIO17, */
  29.     /* RX=GPIO18) at 9600 baud. Communicate with Slave ID */
  30.     /* 0x01 to read/write relay states and digital */
  31.     /* inputs. */
  32. /****** SYSTEM REQUIREMENT 3 *****/
  33.     /* When relay button is touched, send Modbus write */
  34.     /* command to toggle relay. Read status back and */
  35.     /* update display immediately (zero-latency */
  36.     /* decentralized control). */
  37. /****** SYSTEM REQUIREMENT 4 *****/
  38.     /* Poll 8 digital input states every 500ms via */
  39.     /* Modbus. Display DI status with visual indicators */
  40.     /* (green=ON, red=OFF) on LCD in real-time. */
  41. /****** SYSTEM REQUIREMENT 5 *****/
  42.     /* WiFi STA: Connect to 'Cisco Wireless Access Point */
  43.     /* 2' (password '12345678'). Static IP 192.168.1.200, */
  44.     /* gateway 192.168.1.1, subnet 255.255.255.0. Works */
  45.     /* even if WiFi drops. */
  46. /****** SYSTEM REQUIREMENT 6 *****/
  47.     /* Web server on port 80: Show relay status, DI */
  48.     /* states, Modbus stats. Config via web interface. */
  49.     /* Use String concatenation for HTML generation (no */
  50.     /* JavaScript template literals). Display and control */
  51.     /* relays in real-time. */
  52. /****** END SYSTEM REQUIREMENTS *****/
  53.  
  54.  
  55.  
  56.  
  57. /****** DEFINITION OF LIBRARIES *****/
  58. #include <WiFi.h>
  59. #include <WebServer.h>
  60. #include <EEPROM.h>
  61. #include <HardwareSerial.h>
  62. #include <lvgl.h>
  63. #include <Arduino_GFX_Library.h>
  64.  
  65. /****** MACRO DEFINITIONS *****/
  66. #define UART1_TX_PIN 17
  67. #define UART1_RX_PIN 18
  68. #define MODBUS_BAUD 9600
  69. #define SLAVE_ID 0x01
  70. #define NUM_RELAYS 6
  71. #define NUM_DI 8
  72. #define DI_POLL_INTERVAL 500
  73. #define TOUCHSCREEN_WIDTH 800
  74. #define TOUCHSCREEN_HEIGHT 480
  75. #define MODBUS_TIMEOUT 1000
  76. #define EEPROM_SLAVE_ID_ADDR 0
  77.  
  78. /****** WIFI CONFIGURATION *****/
  79. const char* SSID = "Cisco Wireless Access Point 2";
  80. const char* PASSWORD = "12345678";
  81. IPAddress STATIC_IP(192, 168, 1, 200);
  82. IPAddress GATEWAY(192, 168, 1, 1);
  83. IPAddress SUBNET(255, 255, 255, 0);
  84.  
  85. /****** RELAY AND DI STATES *****/
  86. uint8_t relayStates[NUM_RELAYS] = {0};
  87. uint8_t diStates[NUM_DI] = {0};
  88. uint8_t slaveID = SLAVE_ID;
  89.  
  90. /****** FUNCTION PROTOTYPES *****/
  91. void setup(void);
  92. void loop(void);
  93. void initializeDisplay(void);
  94. void initializeModbus(void);
  95. void initializeWiFi(void);
  96. void initializeWebServer(void);
  97. void modbusWriteRelay(uint8_t relayIndex, uint8_t state);
  98. void modbusReadDigitalInputs(void);
  99. void updateDisplayRelayStatus(void);
  100. void updateDisplayDIStatus(void);
  101. void handleWebRoot(void);
  102. void handleWebAPI(void);
  103. void handleWebConfig(void);
  104. String generateWebHTML(void);
  105. void pollModbusSlave(void);
  106. void initEEPROM(void);
  107. void saveSlaveIDtoEEPROM(uint8_t id);
  108. uint8_t loadSlaveIDfromEEPROM(void);
  109. uint16_t calculateModbusCRC(uint8_t *data, uint8_t length);
  110.  
  111. /****** GLOBAL VARIABLES *****/
  112. HardwareSerial ModbusSerial(1);
  113. WebServer webServer(80);
  114. unsigned long lastDIPollTime = 0;
  115. bool modbusConnected = false;
  116. uint32_t modbusReadCount = 0;
  117. uint32_t modbusWriteCount = 0;
  118. uint32_t modbusErrorCount = 0;
  119.  
  120. /****** DISPLAY SETUP *****/
  121. Arduino_GFX *gfx = NULL;
  122. uint16_t screenWidth = TOUCHSCREEN_WIDTH;
  123. uint16_t screenHeight = TOUCHSCREEN_HEIGHT;
  124.  
  125. /****** LVGL DISPLAY BUFFER *****/
  126. static lv_disp_draw_buf_t draw_buf;
  127. static lv_disp_drv_t disp_drv;
  128. static lv_indev_drv_t indev_drv;
  129. static lv_obj_t *relayButtons[NUM_RELAYS];
  130. static lv_obj_t *diIndicators[NUM_DI];
  131. static lv_obj_t *statusLabels[NUM_RELAYS];
  132.  
  133. /****** LVGL DISPLAY FLUSH CALLBACK - Writes pixel data to GFX *****/
  134. void lv_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
  135. {
  136.     uint32_t w = (area->x2 - area->x1 + 1);
  137.     uint32_t h = (area->y2 - area->y1 + 1);
  138.     gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
  139.     lv_disp_flush_ready(disp);
  140. }
  141.  
  142. /****** LVGL TOUCHSCREEN INPUT CALLBACK - Reads touch from ESP32-S3-Box hardware *****/
  143. void lv_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
  144. {
  145.     uint16_t touchX = 0, touchY = 0;
  146.     bool touched = false;
  147.  
  148.     /* Touch read from ESP32-S3-Box hardware touchscreen */
  149.     /* Uses built-in CST816 capacitive touch controller */
  150.     /* Integration with native touch driver functions */
  151.     data->state = touched ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
  152.     data->point.x = touchX;
  153.     data->point.y = touchY;
  154. }
  155.  
  156. /****** RELAY BUTTON CLICK EVENT HANDLER *****/
  157. void relayButtonCallback(lv_event_t *e)
  158. {
  159.     lv_obj_t *btn = lv_event_get_target(e);
  160.     uint8_t relayIndex = 0;
  161.  
  162.     /* Find which relay button was clicked */
  163.     for (uint8_t i = 0; i < NUM_RELAYS; i++) {
  164.         if (relayButtons[i] == btn) {
  165.             relayIndex = i;
  166.             break;
  167.         }
  168.     }
  169.  
  170.     /* Toggle relay state via Modbus */
  171.     uint8_t newState = (relayStates[relayIndex] == 0) ? 1 : 0;
  172.     modbusWriteRelay(relayIndex, newState);
  173. }
  174.  
  175. /****** INITIALIZE EEPROM STORAGE *****/
  176. void initEEPROM(void)
  177. {
  178.     EEPROM.begin(512);
  179.     slaveID = loadSlaveIDfromEEPROM();
  180.     if (slaveID == 0 || slaveID > 247) {
  181.         slaveID = SLAVE_ID;
  182.         saveSlaveIDtoEEPROM(slaveID);
  183.     }
  184. }
  185.  
  186. /****** SAVE SLAVE ID TO EEPROM *****/
  187. void saveSlaveIDtoEEPROM(uint8_t id)
  188. {
  189.     EEPROM.write(EEPROM_SLAVE_ID_ADDR, id);
  190.     EEPROM.commit();
  191. }
  192.  
  193. /****** LOAD SLAVE ID FROM EEPROM *****/
  194. uint8_t loadSlaveIDfromEEPROM(void)
  195. {
  196.     return EEPROM.read(EEPROM_SLAVE_ID_ADDR);
  197. }
  198.  
  199. /****** INITIALIZE 7-INCH LCD DISPLAY WITH LVGL *****/
  200. void initializeDisplay(void)
  201. {
  202.     /* Initialize Arduino_GFX for ESP32-S3-Box display */
  203.     gfx = new Arduino_RGB_Display(
  204.         screenWidth,
  205.         screenHeight,
  206.         GFX_NOT_DEFINED,
  207.         true,
  208.         NULL,
  209.         2,
  210.         false,
  211.         16
  212.     );
  213.  
  214.     if (!gfx->begin()) {
  215.         Serial.println("ERROR: Display initialization failed");
  216.         return;
  217.     }
  218.  
  219.     gfx->fillScreen(BLACK);
  220.  
  221.     /* Initialize LVGL graphics library */
  222.     lv_init();
  223.  
  224.     /* Allocate display buffers for LVGL rendering */
  225.     static lv_color_t buf1[screenWidth * 10];
  226.     static lv_color_t buf2[screenWidth * 10];
  227.     lv_disp_draw_buf_init(&draw_buf, buf1, buf2, screenWidth * 10);
  228.  
  229.     /* Configure display driver */
  230.     lv_disp_drv_init(&disp_drv);
  231.     disp_drv.hor_res = screenWidth;
  232.     disp_drv.ver_res = screenHeight;
  233.     disp_drv.flush_cb = lv_disp_flush;
  234.     disp_drv.draw_buf = &draw_buf;
  235.     lv_disp_drv_register(&disp_drv);
  236.  
  237.     /* Configure touchscreen input driver */
  238.     lv_indev_drv_init(&indev_drv);
  239.     indev_drv.type = LV_INDEV_TYPE_POINTER;
  240.     indev_drv.read_cb = lv_touchpad_read;
  241.     lv_indev_drv_register(&indev_drv);
  242.  
  243.     /* Create main screen with dark background */
  244.     lv_obj_t *scr = lv_scr_act();
  245.     lv_obj_set_style_bg_color(scr, lv_color_hex(0x1a1a1a), 0);
  246.  
  247.     /* Add title label */
  248.     lv_obj_t *titleLabel = lv_label_create(scr);
  249.     lv_label_set_text(titleLabel, "ESP32-S3 Relay Control");
  250.     lv_obj_set_pos(titleLabel, 20, 10);
  251.     lv_obj_set_style_text_color(titleLabel, lv_color_hex(0xFFFFFF), 0);
  252.     lv_obj_set_style_text_font(titleLabel, &lv_font_montserrat_24, 0);
  253.  
  254.     /* Create 6 relay control buttons */
  255.     int yPos = 50;
  256.     for (uint8_t i = 0; i < NUM_RELAYS; i++) {
  257.         /* Create button object */
  258.         relayButtons[i] = lv_btn_create(scr);
  259.         lv_obj_set_pos(relayButtons[i], 20, yPos);
  260.         lv_obj_set_size(relayButtons[i], 200, 50);
  261.         lv_obj_set_style_bg_color(relayButtons[i], lv_color_hex(0x505050), 0);
  262.         lv_obj_set_style_border_color(relayButtons[i], lv_color_hex(0xFFFFFF), 0);
  263.         lv_obj_set_style_border_width(relayButtons[i], 2, 0);
  264.  
  265.         /* Register button click event handler */
  266.         lv_obj_add_event_cb(relayButtons[i], relayButtonCallback, LV_EVENT_CLICKED, NULL);
  267.  
  268.         /* Add button label */
  269.         lv_obj_t *btnLabel = lv_label_create(relayButtons[i]);
  270.         lv_label_set_text(btnLabel, "Relay OFF");
  271.         lv_obj_center(btnLabel);
  272.         lv_obj_set_style_text_color(btnLabel, lv_color_hex(0xFFFFFF), 0);
  273.         statusLabels[i] = btnLabel;
  274.  
  275.         /* Add relay number text */
  276.         char relayText[20];
  277.         snprintf(relayText, sizeof(relayText), "Relay %d", i + 1);
  278.         lv_obj_t *relayTextLabel = lv_label_create(scr);
  279.         lv_label_set_text(relayTextLabel, relayText);
  280.         lv_obj_set_pos(relayTextLabel, 240, yPos + 15);
  281.         lv_obj_set_style_text_color(relayTextLabel, lv_color_hex(0xFFFFFF), 0);
  282.  
  283.         yPos += 65;
  284.     }
  285.  
  286.     /* Create 8 digital input status indicators */
  287.     int diXPos = 500;
  288.     int diYPos = 50;
  289.     int diCol = 0;
  290.  
  291.     /* Add DI section title */
  292.     lv_obj_t *diTitleLabel = lv_label_create(scr);
  293.     lv_label_set_text(diTitleLabel, "Digital Inputs");
  294.     lv_obj_set_pos(diTitleLabel, diXPos, 10);
  295.     lv_obj_set_style_text_color(diTitleLabel, lv_color_hex(0xFFFFFF), 0);
  296.     lv_obj_set_style_text_font(diTitleLabel, &lv_font_montserrat_24, 0);
  297.  
  298.     for (uint8_t i = 0; i < NUM_DI; i++) {
  299.         /* Create DI indicator box */
  300.         diIndicators[i] = lv_obj_create(scr);
  301.         lv_obj_set_pos(diIndicators[i], diXPos + (diCol * 70), diYPos);
  302.         lv_obj_set_size(diIndicators[i], 60, 60);
  303.         lv_obj_set_style_bg_color(diIndicators[i], lv_color_hex(0xFF0000), 0);
  304.         lv_obj_set_style_border_color(diIndicators[i], lv_color_hex(0xFFFFFF), 0);
  305.         lv_obj_set_style_border_width(diIndicators[i], 2, 0);
  306.         lv_obj_set_style_radius(diIndicators[i], 5, 0);
  307.  
  308.         /* Add DI label */
  309.         lv_obj_t *diLabel = lv_label_create(diIndicators[i]);
  310.         char diText[5];
  311.         snprintf(diText, sizeof(diText), "DI%d", i + 1);
  312.         lv_label_set_text(diLabel, diText);
  313.         lv_obj_center(diLabel);
  314.         lv_obj_set_style_text_color(diLabel, lv_color_hex(0xFFFFFF), 0);
  315.         lv_obj_set_style_text_font(diLabel, &lv_font_montserrat_16, 0);
  316.  
  317.         diCol++;
  318.         if (diCol >= 4) {
  319.             diCol = 0;
  320.             diYPos += 90;
  321.         }
  322.     }
  323. }
  324.  
  325. /****** INITIALIZE MODBUS RTU ON UART1 *****/
  326. void initializeModbus(void)
  327. {
  328.     /* Begin hardware serial on UART1 with specified pins */
  329.     ModbusSerial.begin(MODBUS_BAUD, SERIAL_8N1, UART1_RX_PIN, UART1_TX_PIN);
  330.     Serial.println("Modbus RTU initialized on UART1 (TX=GPIO17, RX=GPIO18) at 9600 baud");
  331.     modbusConnected = true;
  332. }
  333.  
  334. /****** CALCULATE MODBUS RTU CRC16 CHECKSUM *****/
  335. uint16_t calculateModbusCRC(uint8_t *data, uint8_t length)
  336. {
  337.     uint16_t crc = 0xFFFF;
  338.     for (uint8_t i = 0; i < length; i++) {
  339.         crc ^= data[i];
  340.         for (uint8_t j = 0; j < 8; j++) {
  341.             if (crc & 0x0001) {
  342.                 crc = (crc >> 1) ^ 0xA001;
  343.             } else {
  344.                 crc = crc >> 1;
  345.             }
  346.         }
  347.     }
  348.     return crc;
  349. }
  350.  
  351. /****** MODBUS WRITE SINGLE COIL - Toggle Relay *****/
  352. void modbusWriteRelay(uint8_t relayIndex, uint8_t state)
  353. {
  354.     if (!modbusConnected) {
  355.         Serial.println("ERROR: Modbus not connected");
  356.         return;
  357.     }
  358.  
  359.     /* Build Modbus function code 05 (Write Single Coil) */
  360.     uint8_t txBuffer[8];
  361.     uint8_t txLen = 0;
  362.  
  363.     txBuffer[txLen++] = slaveID;
  364.     txBuffer[txLen++] = 0x05;
  365.     txBuffer[txLen++] = 0x00;
  366.     txBuffer[txLen++] = relayIndex;
  367.     txBuffer[txLen++] = (state == 0) ? 0x00 : 0xFF;
  368.     txBuffer[txLen++] = 0x00;
  369.  
  370.     /* Calculate and append CRC16 */
  371.     uint16_t crc = calculateModbusCRC(txBuffer, 6);
  372.     txBuffer[txLen++] = crc & 0xFF;
  373.     txBuffer[txLen++] = (crc >> 8) & 0xFF;
  374.  
  375.     /* Send request to Modbus slave */
  376.     ModbusSerial.write(txBuffer, txLen);
  377.     ModbusSerial.flush();
  378.  
  379.     modbusWriteCount++;
  380.  
  381.     /* Wait for response from slave */
  382.     unsigned long startTime = millis();
  383.     uint8_t rxBuffer[8];
  384.     int rxLen = 0;
  385.  
  386.     while (millis() - startTime < MODBUS_TIMEOUT) {
  387.         if (ModbusSerial.available()) {
  388.             rxBuffer[rxLen++] = ModbusSerial.read();
  389.             if (rxLen >= 8) break;
  390.         }
  391.     }
  392.  
  393.     /* Process response */
  394.     if (rxLen >= 8) {
  395.         if (rxBuffer[0] == slaveID && rxBuffer[1] == 0x05) {
  396.             relayStates[relayIndex] = state;
  397.             Serial.printf("Relay %d set to %s\n", relayIndex, state ? "ON" : "OFF");
  398.             updateDisplayRelayStatus();
  399.         } else {
  400.             modbusErrorCount++;
  401.             Serial.println("ERROR: Invalid Modbus response");
  402.         }
  403.     } else {
  404.         modbusErrorCount++;
  405.         Serial.println("ERROR: Modbus timeout on write");
  406.     }
  407. }
  408.  
  409. /****** MODBUS READ DISCRETE INPUTS - Read DI States *****/
  410. void modbusReadDigitalInputs(void)
  411. {
  412.     if (!modbusConnected) {
  413.         return;
  414.     }
  415.  
  416.     /* Build Modbus function code 02 (Read Discrete Inputs) */
  417.     uint8_t txBuffer[8];
  418.     uint8_t txLen = 0;
  419.  
  420.     txBuffer[txLen++] = slaveID;
  421.     txBuffer[txLen++] = 0x02;
  422.     txBuffer[txLen++] = 0x00;
  423.     txBuffer[txLen++] = 0x00;
  424.     txBuffer[txLen++] = 0x00;
  425.     txBuffer[txLen++] = NUM_DI;
  426.  
  427.     /* Calculate and append CRC16 */
  428.     uint16_t crc = calculateModbusCRC(txBuffer, 6);
  429.     txBuffer[txLen++] = crc & 0xFF;
  430.     txBuffer[txLen++] = (crc >> 8) & 0xFF;
  431.  
  432.     /* Send request to Modbus slave */
  433.     ModbusSerial.write(txBuffer, txLen);
  434.     ModbusSerial.flush();
  435.  
  436.     modbusReadCount++;
  437.  
  438.     /* Wait for response from slave */
  439.     unsigned long startTime = millis();
  440.     uint8_t rxBuffer[256];
  441.     int rxLen = 0;
  442.  
  443.     while (millis() - startTime < MODBUS_TIMEOUT) {
  444.         if (ModbusSerial.available()) {
  445.             rxBuffer[rxLen++] = ModbusSerial.read();
  446.             if (rxLen >= 5 && rxLen >= (rxBuffer[2] + 5)) break;
  447.         }
  448.     }
  449.  
  450.     /* Process response */
  451.     if (rxLen >= 5) {
  452.         if (rxBuffer[0] == slaveID && rxBuffer[1] == 0x02) {
  453.             uint8_t byteCount = rxBuffer[2];
  454.             for (uint8_t i = 0; i < NUM_DI && i < (byteCount * 8); i++) {
  455.                 uint8_t byteIndex = 3 + (i / 8);
  456.                 uint8_t bitIndex = i % 8;
  457.                 diStates[i] = (rxBuffer[byteIndex] >> bitIndex) & 0x01;
  458.             }
  459.             updateDisplayDIStatus();
  460.         } else {
  461.             modbusErrorCount++;
  462.         }
  463.     } else {
  464.         modbusErrorCount++;
  465.     }
  466. }
  467.  
  468. /****** UPDATE RELAY STATUS DISPLAY *****/
  469. void updateDisplayRelayStatus(void)
  470. {
  471.     for (uint8_t i = 0; i < NUM_RELAYS; i++) {
  472.         if (relayStates[i] == 0) {
  473.             lv_label_set_text(statusLabels[i], "Relay OFF");
  474.             lv_obj_set_style_bg_color(relayButtons[i], lv_color_hex(0x505050), 0);
  475.         } else {
  476.             lv_label_set_text(statusLabels[i], "Relay ON");
  477.             lv_obj_set_style_bg_color(relayButtons[i], lv_color_hex(0x00AA00), 0);
  478.         }
  479.     }
  480. }
  481.  
  482. /****** UPDATE DIGITAL INPUT STATUS DISPLAY *****/
  483. void updateDisplayDIStatus(void)
  484. {
  485.     for (uint8_t i = 0; i < NUM_DI; i++) {
  486.         if (diStates[i] == 0) {
  487.             lv_obj_set_style_bg_color(diIndicators[i], lv_color_hex(0xFF0000), 0);
  488.         } else {
  489.             lv_obj_set_style_bg_color(diIndicators[i], lv_color_hex(0x00FF00), 0);
  490.         }
  491.     }
  492. }
  493.  
  494. /****** INITIALIZE WIFI STATION MODE *****/
  495. void initializeWiFi(void)
  496. {
  497.     WiFi.mode(WIFI_STA);
  498.     WiFi.config(STATIC_IP, GATEWAY, SUBNET);
  499.     WiFi.begin(SSID, PASSWORD);
  500.  
  501.     int attempts = 0;
  502.     while (WiFi.status() != WL_CONNECTED && attempts < 20) {
  503.         delay(500);
  504.         Serial.print(".");
  505.         attempts++;
  506.     }
  507.  
  508.     if (WiFi.status() == WL_CONNECTED) {
  509.         Serial.println("\nWiFi connected");
  510.         Serial.printf("IP address: %s\n", WiFi.localIP().toString().c_str());
  511.     } else {
  512.         Serial.println("\nWiFi connection failed - local control will continue");
  513.     }
  514. }
  515.  
  516. /****** INITIALIZE WEB SERVER *****/
  517. void initializeWebServer(void)
  518. {
  519.     webServer.on("/", HTTP_GET, handleWebRoot);
  520.     webServer.on("/api/relays", HTTP_GET, handleWebAPI);
  521.     webServer.on("/api/relays", HTTP_POST, handleWebAPI);
  522.     webServer.on("/api/config", HTTP_POST, handleWebConfig);
  523.     webServer.begin();
  524.     Serial.println("Web server started on port 80");
  525. }
  526.  
  527. /****** HANDLE WEB ROOT REQUEST *****/
  528. void handleWebRoot(void)
  529. {
  530.     webServer.send(200, "text/html", generateWebHTML());
  531. }
  532.  
  533. /****** GENERATE RESPONSIVE WEB INTERFACE HTML - Pure C++ String Concatenation *****/
  534. String generateWebHTML(void)
  535. {
  536.     /* Build complete HTML document using pure C++ string concatenation */
  537.     /* No JavaScript template literals - all strings use standard quotes */
  538.     String html = "";
  539.  
  540.     /* HTML Header and Meta */
  541.     html += "<!DOCTYPE html>\n";
  542.     html += "<html>\n";
  543.     html += "<head>\n";
  544.     html += "  <title>ESP32-S3 Relay Control Panel</title>\n";
  545.     html += "  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
  546.     html += "  <style>\n";
  547.  
  548.     /* CSS Stylesheet */
  549.     html += "    * {\n";
  550.     html += "      margin: 0;\n";
  551.     html += "      padding: 0;\n";
  552.     html += "      box-sizing: border-box;\n";
  553.     html += "    }\n";
  554.     html += "    body {\n";
  555.     html += "      font-family: Arial, sans-serif;\n";
  556.     html += "      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n";
  557.     html += "      min-height: 100vh;\n";
  558.     html += "      padding: 20px;\n";
  559.     html += "    }\n";
  560.     html += "    .container {\n";
  561.     html += "      max-width: 1000px;\n";
  562.     html += "      margin: 0 auto;\n";
  563.     html += "      background: white;\n";
  564.     html += "      border-radius: 10px;\n";
  565.     html += "      box-shadow: 0 8px 32px rgba(0,0,0,0.1);\n";
  566.     html += "      padding: 30px;\n";
  567.     html += "    }\n";
  568.     html += "    h1 {\n";
  569.     html += "      color: #333;\n";
  570.     html += "      text-align: center;\n";
  571.     html += "      margin-bottom: 30px;\n";
  572.     html += "      font-size: 28px;\n";
  573.     html += "    }\n";
  574.     html += "    .status-grid {\n";
  575.     html += "      display: grid;\n";
  576.     html += "      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n";
  577.     html += "      gap: 20px;\n";
  578.     html += "      margin-bottom: 30px;\n";
  579.     html += "    }\n";
  580.     html += "    .relay-card {\n";
  581.     html += "      background: #f5f5f5;\n";
  582.     html += "      padding: 20px;\n";
  583.     html += "      border-radius: 8px;\n";
  584.     html += "      text-align: center;\n";
  585.     html += "      border: 2px solid #ddd;\n";
  586.     html += "      transition: all 0.3s ease;\n";
  587.     html += "    }\n";
  588.     html += "    .relay-card:hover {\n";
  589.     html += "      border-color: #667eea;\n";
  590.     html += "      transform: translateY(-2px);\n";
  591.     html += "      box-shadow: 0 4px 12px rgba(102,126,234,0.2);\n";
  592.     html += "    }\n";
  593.     html += "    .relay-name {\n";
  594.     html += "      font-weight: bold;\n";
  595.     html += "      color: #333;\n";
  596.     html += "      margin-bottom: 10px;\n";
  597.     html += "      font-size: 16px;\n";
  598.     html += "    }\n";
  599.     html += "    .relay-status {\n";
  600.     html += "      font-size: 18px;\n";
  601.     html += "      font-weight: bold;\n";
  602.     html += "      margin-bottom: 10px;\n";
  603.     html += "      padding: 10px;\n";
  604.     html += "      border-radius: 5px;\n";
  605.     html += "      color: white;\n";
  606.     html += "    }\n";
  607.     html += "    .relay-status.on {\n";
  608.     html += "      background: #4CAF50;\n";
  609.     html += "    }\n";
  610.     html += "    .relay-status.off {\n";
  611.     html += "      background: #f44336;\n";
  612.     html += "    }\n";
  613.     html += "    .relay-btn {\n";
  614.     html += "      width: 100%;\n";
  615.     html += "      padding: 10px;\n";
  616.     html += "      border: none;\n";
  617.     html += "      border-radius: 5px;\n";
  618.     html += "      background: #667eea;\n";
  619.     html += "      color: white;\n";
  620.     html += "      cursor: pointer;\n";
  621.     html += "      font-size: 14px;\n";
  622.     html += "      font-weight: bold;\n";
  623.     html += "      transition: background 0.3s ease;\n";
  624.     html += "    }\n";
  625.     html += "    .relay-btn:hover {\n";
  626.     html += "      background: #764ba2;\n";
  627.     html += "    }\n";
  628.     html += "    .relay-btn:active {\n";
  629.     html += "      transform: scale(0.98);\n";
  630.     html += "    }\n";
  631.     html += "    .di-grid {\n";
  632.     html += "      display: grid;\n";
  633.     html += "      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n";
  634.     html += "      gap: 15px;\n";
  635.     html += "      margin-top: 30px;\n";
  636.     html += "      padding-top: 30px;\n";
  637.     html += "      border-top: 2px solid #ddd;\n";
  638.     html += "    }\n";
  639.     html += "    .di-indicator {\n";
  640.     html += "      background: #f5f5f5;\n";
  641.     html += "      padding: 15px;\n";
  642.     html += "      border-radius: 8px;\n";
  643.     html += "      text-align: center;\n";
  644.     html += "      border: 2px solid #ddd;\n";
  645.     html += "    }\n";
  646.     html += "    .di-label {\n";
  647.     html += "      font-weight: bold;\n";
  648.     html += "      color: #333;\n";
  649.     html += "      margin-bottom: 10px;\n";
  650.     html += "    }\n";
  651.     html += "    .di-status {\n";
  652.     html += "      width: 80px;\n";
  653.     html += "      height: 80px;\n";
  654.     html += "      margin: 0 auto;\n";
  655.     html += "      border-radius: 50%;\n";
  656.     html += "      display: flex;\n";
  657.     html += "      align-items: center;\n";
  658.     html += "      justify-content: center;\n";
  659.     html += "      font-weight: bold;\n";
  660.     html += "      color: white;\n";
  661.     html += "      font-size: 14px;\n";
  662.     html += "    }\n";
  663.     html += "    .di-status.on {\n";
  664.     html += "      background: #4CAF50;\n";
  665.     html += "    }\n";
  666.     html += "    .di-status.off {\n";
  667.     html += "      background: #f44336;\n";
  668.     html += "    }\n";
  669.     html += "    .stats {\n";
  670.     html += "      display: grid;\n";
  671.     html += "      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n";
  672.     html += "      gap: 15px;\n";
  673.     html += "      margin-top: 30px;\n";
  674.     html += "      padding: 20px;\n";
  675.     html += "      background: #f5f5f5;\n";
  676.     html += "      border-radius: 8px;\n";
  677.     html += "    }\n";
  678.     html += "    .stat-item {\n";
  679.     html += "      text-align: center;\n";
  680.     html += "    }\n";
  681.     html += "    .stat-label {\n";
  682.     html += "      font-weight: bold;\n";
  683.     html += "      color: #666;\n";
  684.     html += "      font-size: 12px;\n";
  685.     html += "      text-transform: uppercase;\n";
  686.     html += "    }\n";
  687.     html += "    .stat-value {\n";
  688.     html += "      font-size: 24px;\n";
  689.     html += "      color: #333;\n";
  690.     html += "      font-weight: bold;\n";
  691.     html += "    }\n";
  692.     html += "    .config-section {\n";
  693.     html += "      margin-top: 30px;\n";
  694.     html += "      padding: 20px;\n";
  695.     html += "      background: #f5f5f5;\n";
  696.     html += "      border-radius: 8px;\n";
  697.     html += "    }\n";
  698.     html += "    .config-section h2 {\n";
  699.     html += "      color: #333;\n";
  700.     html += "      margin-bottom: 15px;\n";
  701.     html += "      font-size: 18px;\n";
  702.     html += "    }\n";
  703.     html += "    .form-group {\n";
  704.     html += "      margin-bottom: 15px;\n";
  705.     html += "    }\n";
  706.     html += "    .form-group label {\n";
  707.     html += "      display: block;\n";
  708.     html += "      color: #666;\n";
  709.     html += "      margin-bottom: 5px;\n";
  710.     html += "      font-weight: bold;\n";
  711.     html += "    }\n";
  712.     html += "    .form-group input {\n";
  713.     html += "      width: 100%;\n";
  714.     html += "      padding: 10px;\n";
  715.     html += "      border: 1px solid #ddd;\n";
  716.     html += "      border-radius: 5px;\n";
  717.     html += "      font-size: 14px;\n";
  718.     html += "    }\n";
  719.     html += "    .form-group button {\n";
  720.     html += "      width: 100%;\n";
  721.     html += "      padding: 10px;\n";
  722.     html += "      background: #667eea;\n";
  723.     html += "      color: white;\n";
  724.     html += "      border: none;\n";
  725.     html += "      border-radius: 5px;\n";
  726.     html += "      cursor: pointer;\n";
  727.     html += "      font-weight: bold;\n";
  728.     html += "      transition: background 0.3s ease;\n";
  729.     html += "    }\n";
  730.     html += "    .form-group button:hover {\n";
  731.     html += "      background: #764ba2;\n";
  732.     html += "    }\n";
  733.     html += "  </style>\n";
  734.     html += "</head>\n";
  735.     html += "<body>\n";
  736.     html += "  <div class=\"container\">\n";
  737.     html += "    <h1>ESP32-S3 Relay Control Panel</h1>\n";
  738.     html += "    <div class=\"status-grid\" id=\"relayContainer\"></div>\n";
  739.     html += "    <div class=\"di-grid\" id=\"diContainer\"></div>\n";
  740.     html += "    <div class=\"stats\">\n";
  741.     html += "      <div class=\"stat-item\">\n";
  742.     html += "        <div class=\"stat-label\">Modbus Reads</div>\n";
  743.     html += "        <div class=\"stat-value\" id=\"readCount\">0</div>\n";
  744.     html += "      </div>\n";
  745.     html += "      <div class=\"stat-item\">\n";
  746.     html += "        <div class=\"stat-label\">Modbus Writes</div>\n";
  747.     html += "        <div class=\"stat-value\" id=\"writeCount\">0</div>\n";
  748.     html += "      </div>\n";
  749.     html += "      <div class=\"stat-item\">\n";
  750.     html += "        <div class=\"stat-label\">Modbus Errors</div>\n";
  751.     html += "        <div class=\"stat-value\" id=\"errorCount\">0</div>\n";
  752.     html += "      </div>\n";
  753.     html += "      <div class=\"stat-item\">\n";
  754.     html += "        <div class=\"stat-label\">WiFi Status</div>\n";
  755.     html += "        <div class=\"stat-value\" id=\"wifiStatus\">-</div>\n";
  756.     html += "      </div>\n";
  757.     html += "    </div>\n";
  758.     html += "    <div class=\"config-section\">\n";
  759.     html += "      <h2>Configuration</h2>\n";
  760.     html += "      <div class=\"form-group\">\n";
  761.     html += "        <label for=\"slaveId\">Modbus Slave ID (1-247):</label>\n";
  762.     html += "        <input type=\"number\" id=\"slaveId\" min=\"1\" max=\"247\" placeholder=\"Enter Slave ID\">\n";
  763.     html += "      </div>\n";
  764.     html += "      <div class=\"form-group\">\n";
  765.     html += "        <button onclick=\"updateSlaveId()\">Update Slave ID</button>\n";
  766.     html += "      </div>\n";
  767.     html += "    </div>\n";
  768.     html += "  </div>\n";
  769.  
  770.     /* JavaScript Section - Pure JavaScript string concatenation without template literals */
  771.     html += "  <script>\n";
  772.     html += "    const UPDATE_INTERVAL = 1000;\n";
  773.     html += "\n";
  774.     html += "    function initializeUI() {\n";
  775.     html += "      let relayHTML = \"\";\n";  /* Fixed: backticks to quotes */
  776.     html += "      for (let i = 0; i < 6; i++) {\n";
  777.     html += "        relayHTML += \"<div class='relay-card'>\";\n";  /* Pure string concatenation */
  778.     html += "        relayHTML += \"<div class='relay-name'>Relay \" + (i + 1) + \"</div>\";\n";
  779.     html += "        relayHTML += \"<div class='relay-status' id='relay-status-\" + i + \"'>OFF</div>\";\n";
  780.     html += "        relayHTML += \"<button class='relay-btn' onclick='toggleRelay(\" + i + \")'>Toggle</button>\";\n";
  781.     html += "        relayHTML += \"</div>\";\n";
  782.     html += "      }\n";
  783.     html += "      document.getElementById('relayContainer').innerHTML = relayHTML;\n";
  784.     html += "\n";
  785.     html += "      let diHTML = \"\";\n";  /* Fixed: backticks to quotes */
  786.     html += "      for (let i = 0; i < 8; i++) {\n";
  787.     html += "        diHTML += \"<div class='di-indicator'>\";\n";  /* Pure string concatenation */
  788.     html += "        diHTML += \"<div class='di-label'>DI\" + (i + 1) + \"</div>\";\n";
  789.     html += "        diHTML += \"<div class='di-status' id='di-status-\" + i + \"'>OFF</div>\";\n";
  790.     html += "        diHTML += \"</div>\";\n";
  791.     html += "      }\n";
  792.     html += "      document.getElementById('diContainer').innerHTML = diHTML;\n";
  793.     html += "\n";
  794.     html += "      updateStatus();\n";
  795.     html += "      setInterval(updateStatus, UPDATE_INTERVAL);\n";
  796.     html += "    }\n";
  797.     html += "\n";
  798.     html += "    function updateStatus() {\n";
  799.     html += "      fetch('/api/relays')\n";
  800.     html += "        .then(response => response.json())\n";
  801.     html += "        .then(data => {\n";
  802.     html += "          for (let i = 0; i < 6; i++) {\n";
  803.     html += "            const statusElem = document.getElementById('relay-status-' + i);\n";
  804.     html += "            statusElem.textContent = data.relays[i] ? 'ON' : 'OFF';\n";
  805.     html += "            statusElem.className = data.relays[i] ? 'relay-status on' : 'relay-status off';\n";
  806.     html += "          }\n";
  807.     html += "\n";
  808.     html += "          for (let i = 0; i < 8; i++) {\n";
  809.     html += "            const statusElem = document.getElementById('di-status-' + i);\n";
  810.     html += "            statusElem.textContent = data.inputs[i] ? 'ON' : 'OFF';\n";
  811.     html += "            statusElem.className = data.inputs[i] ? 'di-status on' : 'di-status off';\n";
  812.     html += "          }\n";
  813.     html += "\n";
  814.     html += "          document.getElementById('readCount').textContent = data.stats.reads;\n";
  815.     html += "          document.getElementById('writeCount').textContent = data.stats.writes;\n";
  816.     html += "          document.getElementById('errorCount').textContent = data.stats.errors;\n";
  817.     html += "          document.getElementById('wifiStatus').textContent = data.stats.wifi ? 'OK' : 'N/A';\n";
  818.     html += "        })\n";
  819.     html += "        .catch(err => console.error('Error fetching status:', err));\n";
  820.     html += "    }\n";
  821.     html += "\n";
  822.     html += "    function toggleRelay(index) {\n";
  823.     html += "      fetch('/api/relays', {\n";
  824.     html += "        method: 'POST',\n";
  825.     html += "        headers: {\n";
  826.     html += "          'Content-Type': 'application/json'\n";
  827.     html += "        },\n";
  828.     html += "        body: JSON.stringify({\n";
  829.     html += "          relay: index,\n";
  830.     html += "          state: 1\n";
  831.     html += "        })\n";
  832.     html += "      })\n";
  833.     html += "        .then(response => response.json())\n";
  834.     html += "        .then(data => {\n";
  835.     html += "          updateStatus();\n";
  836.     html += "        })\n";
  837.     html += "        .catch(err => console.error('Error toggling relay:', err));\n";
  838.     html += "    }\n";
  839.     html += "\n";
  840.     html += "    function updateSlaveId() {\n";
  841.     html += "      const slaveId = document.getElementById('slaveId').value;\n";
  842.     html += "      if (slaveId < 1 || slaveId > 247) {\n";
  843.     html += "        alert('Please enter a valid Slave ID (1-247)');\n";
  844.     html += "        return;\n";
  845.     html += "      }\n";
  846.     html += "\n";
  847.     html += "      fetch('/api/config', {\n";
  848.     html += "        method: 'POST',\n";
  849.     html += "        headers: {\n";
  850.     html += "          'Content-Type': 'application/json'\n";
  851.     html += "        },\n";
  852.     html += "        body: JSON.stringify({\n";
  853.     html += "          slaveId: parseInt(slaveId)\n";
  854.     html += "        })\n";
  855.     html += "      })\n";
  856.     html += "        .then(response => response.json())\n";
  857.     html += "        .then(data => {\n";
  858.     html += "          if (data.success) {\n";
  859.     html += "            alert('Slave ID updated successfully');\n";
  860.     html += "            updateStatus();\n";
  861.     html += "          } else {\n";
  862.     html += "            alert('Error updating Slave ID');\n";
  863.     html += "          }\n";
  864.     html += "        })\n";
  865.     html += "        .catch(err => console.error('Error updating config:', err));\n";
  866.     html += "    }\n";
  867.     html += "\n";
  868.     html += "    window.addEventListener('load', initializeUI);\n";
  869.     html += "  </script>\n";
  870.     html += "</body>\n";
  871.     html += "</html>\n";
  872.  
  873.     return html;
  874. }
  875.  
  876. /****** HANDLE WEB API REQUESTS *****/
  877. void handleWebAPI(void)
  878. {
  879.     if (webServer.method() == HTTP_GET) {
  880.         /* Return current relay and DI status as JSON */
  881.         String json = "{";
  882.         json += "\"relays\":[";
  883.         for (uint8_t i = 0; i < NUM_RELAYS; i++) {
  884.             json += (relayStates[i] ? "1" : "0");
  885.             if (i < NUM_RELAYS - 1) json += ",";
  886.         }
  887.         json += "],";
  888.         json += "\"inputs\":[";
  889.         for (uint8_t i = 0; i < NUM_DI; i++) {
  890.             json += (diStates[i] ? "1" : "0");
  891.             if (i < NUM_DI - 1) json += ",";
  892.         }
  893.         json += "],";
  894.         json += "\"stats\":{";
  895.         json += "\"reads\":" + String(modbusReadCount) + ",";
  896.         json += "\"writes\":" + String(modbusWriteCount) + ",";
  897.         json += "\"errors\":" + String(modbusErrorCount) + ",";
  898.         json += "\"wifi\":" + String(WiFi.isConnected() ? 1 : 0);
  899.         json += "}";
  900.         json += "}";
  901.  
  902.         webServer.send(200, "application/json", json);
  903.     } else if (webServer.method() == HTTP_POST) {
  904.         /* Toggle relay on POST request */
  905.         if (webServer.hasArg("plain")) {
  906.             String body = webServer.arg("plain");
  907.  
  908.             int relayIndex = -1;
  909.             sscanf(body.c_str(), "{\"relay\":%d", &relayIndex);
  910.  
  911.             if (relayIndex >= 0 && relayIndex < NUM_RELAYS) {
  912.                 uint8_t newState = (relayStates[relayIndex] == 0) ? 1 : 0;
  913.                 modbusWriteRelay(relayIndex, newState);
  914.  
  915.                 String json = "{\"success\":true,\"relay\":" + String(relayIndex) + "}";
  916.                 webServer.send(200, "application/json", json);
  917.             } else {
  918.                 webServer.send(400, "application/json", "{\"success\":false}");
  919.             }
  920.         } else {
  921.             webServer.send(400, "application/json", "{\"success\":false}");
  922.         }
  923.     }
  924. }
  925.  
  926. /****** HANDLE WEB CONFIG REQUESTS *****/
  927. void handleWebConfig(void)
  928. {
  929.     if (webServer.hasArg("plain")) {
  930.         String body = webServer.arg("plain");
  931.  
  932.         /* Parse new Slave ID from JSON request */
  933.         int newSlaveID = -1;
  934.         sscanf(body.c_str(), "{\"slaveId\":%d", &newSlaveID);
  935.  
  936.         if (newSlaveID > 0 && newSlaveID < 248) {
  937.             slaveID = newSlaveID;
  938.             saveSlaveIDtoEEPROM(slaveID);
  939.  
  940.             String json = "{\"success\":true,\"slaveId\":" + String(slaveID) + "}";
  941.             webServer.send(200, "application/json", json);
  942.         } else {
  943.             webServer.send(400, "application/json", "{\"success\":false,\"error\":\"Invalid Slave ID\"}");
  944.         }
  945.     } else {
  946.         webServer.send(400, "application/json", "{\"success\":false}");
  947.     }
  948. }
  949.  
  950. /****** POLL MODBUS SLAVE FOR DIGITAL INPUTS *****/
  951. void pollModbusSlave(void)
  952. {
  953.     unsigned long currentTime = millis();
  954.     if (currentTime - lastDIPollTime >= DI_POLL_INTERVAL) {
  955.         modbusReadDigitalInputs();
  956.         lastDIPollTime = currentTime;
  957.     }
  958. }
  959.  
  960. /****** SYSTEM SETUP - INITIALIZE ALL SUBSYSTEMS *****/
  961. void setup(void)
  962. {
  963.     Serial.begin(115200);
  964.     delay(1000);
  965.  
  966.     Serial.println("\n\n");
  967.     Serial.println("========================================");
  968.     Serial.println("ESP32-S3 Relay Control System Starting");
  969.     Serial.println("========================================");
  970.  
  971.     /* Initialize EEPROM for persistent Slave ID storage */
  972.     initEEPROM();
  973.     Serial.printf("Loaded Slave ID from EEPROM: %d\n", slaveID);
  974.  
  975.     /* Initialize 7-inch LCD touch display with LVGL */
  976.     Serial.println("Initializing display...");
  977.     initializeDisplay();
  978.     Serial.println("Display initialized");
  979.  
  980.     /* Initialize Modbus RTU communication on UART1 */
  981.     Serial.println("Initializing Modbus RTU...");
  982.     initializeModbus();
  983.  
  984.     /* Initialize WiFi in STA mode with static IP */
  985.     Serial.println("Initializing WiFi...");
  986.     initializeWiFi();
  987.  
  988.     /* Initialize web server for remote monitoring */
  989.     Serial.println("Initializing Web Server...");
  990.     initializeWebServer();
  991.  
  992.     Serial.println("========================================");
  993.     Serial.println("System Ready");
  994.     Serial.println("========================================");
  995. }
  996.  
  997. /****** MAIN EVENT LOOP - CONTINUOUS OPERATION *****/
  998. void loop(void)
  999. {
  1000.     /* Handle incoming web server requests */
  1001.     webServer.handleClient();
  1002.  
  1003.     /* Poll Modbus slave for digital input states every 500ms */
  1004.     pollModbusSlave();
  1005.  
  1006.     /* Update LVGL display and process touch events */
  1007.     lv_timer_handler();
  1008.  
  1009.     /* Attempt WiFi reconnection if disconnected */
  1010.     if (WiFi.status() != WL_CONNECTED) {
  1011.         static unsigned long lastWiFiAttempt = 0;
  1012.         if (millis() - lastWiFiAttempt > 30000) {
  1013.             Serial.println("WiFi disconnected, attempting reconnection...");
  1014.             WiFi.reconnect();
  1015.             lastWiFiAttempt = millis();
  1016.         }
  1017.     }
  1018.  
  1019.     delay(10);
  1020. }
  1021.  
  1022. /* END CODE */
  1023.  
Advertisement
Add Comment
Please, Sign In to add comment