]> jspc29.x-matter.uni-frankfurt.de Git - avr.git/commitdiff
add ESP32 design for modbus + GPIO master
authorJan Michel <mail@janmichel.eu>
Thu, 1 Jan 2026 16:12:40 +0000 (17:12 +0100)
committerJan Michel <mail@janmichel.eu>
Thu, 1 Jan 2026 16:12:40 +0000 (17:12 +0100)
esp32/EthernetUART/ModBus/ModBus.ino [new file with mode: 0644]
esp32/EthernetUART/ModBus/README.txt [new file with mode: 0644]

diff --git a/esp32/EthernetUART/ModBus/ModBus.ino b/esp32/EthernetUART/ModBus/ModBus.ino
new file mode 100644 (file)
index 0000000..b6f5bc4
--- /dev/null
@@ -0,0 +1,359 @@
+ /*
+
+Telnet Port 2323
+
+Visual Status via Board LEDs:
+ETH Start                 -> Green LED blink once
+Telnet work               -> Green LED on
+ETH Disconnected/stopped  -> Orange LED on
+*/
+
+void getdata(uint8_t buf);
+// load needed Libaries
+#include <Adafruit_ADS1X15.h> //for external ADC
+#include <ETH.h>              //for Telnet Server Connection
+#include <Wire.h>             //for I2C Connection
+#include <SPI.h>              //for display control
+#include <EEPROM.h>           //to save settings          
+#include <ModbusRTUMaster.h>
+
+#define MAX_SRV_CLIENTS 10
+#define FIRMWARE_VERSION 1
+#define EEPROM_SIZE 0x50
+    
+#define SDA 5
+#define SCL 32
+#define LED_GREEN 15
+#define LED_YELLOW 2
+#define CB0 32
+#define CB1 5
+#define CB2 33
+#define CB3 14
+
+
+ModbusRTUMaster modbus(Serial);
+
+WiFiServer server(2323);
+WiFiClient serverClients[MAX_SRV_CLIENTS];
+
+volatile static bool eth_connected = false;
+uint8_t rxcnt = 0, txpoint = 0;
+uint8_t rxbuf[15];
+uint8_t txbuf[13];
+unsigned long lastModBus = 0;
+/*****************************************************************
+ * Ethernet
+ *****************************************************************/
+void WiFiEvent(WiFiEvent_t event) {
+  switch (event){
+    case ARDUINO_EVENT_ETH_START:
+      //set eth hostname here
+      digitalWrite(LED_YELLOW,LOW);
+      digitalWrite(LED_GREEN,HIGH);
+      delay(500);
+      digitalWrite(LED_GREEN, LOW); 
+      ETH.setHostname("esp32-ethernet");
+      break;
+      
+    case ARDUINO_EVENT_ETH_CONNECTED:
+      digitalWrite(LED_YELLOW,LOW);
+      digitalWrite(LED_GREEN,HIGH);
+      break;
+      
+    case ARDUINO_EVENT_ETH_GOT_IP:
+      eth_connected = true;
+      break;
+      
+    case ARDUINO_EVENT_ETH_DISCONNECTED:
+      digitalWrite(LED_GREEN,LOW);
+      digitalWrite(LED_YELLOW,HIGH);
+      eth_connected = false;
+      break;
+      
+    case ARDUINO_EVENT_ETH_STOP:
+      digitalWrite(LED_GREEN,LOW);
+      digitalWrite(LED_YELLOW,HIGH);
+      eth_connected = false;
+      break;
+      
+    default:
+      break;
+  }
+}
+
+
+/*****************************************************************
+ * Command Handling
+ *****************************************************************/
+
+// convert integer or nibbles into hex value
+uint8_t nib_to_hex(uint32_t in, uint8_t nib) {
+  uint8_t t = (in >> (nib * 4)) & 0xF;
+  if (t <= 9) {
+    return t + 0x30;
+    }
+  return t - 10 + 0x61;
+  }
+
+// convert hex value to integer,  assumes valid number
+uint8_t hex_to_int(uint8_t h) {
+  if (h < 0x40) return h - 0x30;
+  if (h < 0x50) return h - 0x41 + 10;
+                return h - 0x61 + 10;
+  }
+
+
+uint32_t hex_to_int32(uint8_t * buf) {
+  uint32_t value = 0;
+  for(uint8_t i = 0;i < 8; i++) {
+    value <<= 4;
+    value += hex_to_int(*buf);
+    buf++;
+    }
+  return value;
+  }
+
+//Send own reply to Ethernet  
+void send_answer_buf(uint8_t *d, uint8_t length) { 
+  for(uint8_t i = 0; i < MAX_SRV_CLIENTS; i++) {
+    if (serverClients[i] && serverClients[i].connected()) {
+      serverClients[i].write(d, length);
+      }
+    }
+  } 
+
+//compose response string
+void send_answer_hex(uint8_t *rxbuf, uint32_t v) {
+  txbuf[0] = 'A';
+  txbuf[1] = rxbuf[1];
+  txbuf[2] = rxbuf[2];
+  txbuf[3] = nib_to_hex(v, 7);
+  txbuf[4] = nib_to_hex(v, 6);
+  txbuf[5] = nib_to_hex(v, 5);
+  txbuf[6] = nib_to_hex(v, 4);
+  txbuf[7] = nib_to_hex(v, 3);
+  txbuf[8] = nib_to_hex(v, 2);
+  txbuf[9] = nib_to_hex(v, 1);
+  txbuf[10] =nib_to_hex(v, 0);
+  txbuf[11] = '\n';
+  txbuf[12] = 0;
+  send_answer_buf(txbuf,12);
+  }
+
+/*****************************************************************
+ * IO Port
+ *****************************************************************/   
+void set_port(uint32_t port) {
+  digitalWrite(CB0,(port >> 0) & 1);
+  digitalWrite(CB1,(port >> 4) & 1);
+  digitalWrite(CB2,(port >> 8) & 1);
+  digitalWrite(CB3,(port >> 12) & 1);
+  }
+
+void set_ddr(uint32_t ddr) {
+  if(ddr & 0x0001) pinMode(CB0, OUTPUT); else pinMode(CB0, INPUT);
+  if(ddr & 0x0010) pinMode(CB1, OUTPUT); else pinMode(CB1, INPUT);
+  if(ddr & 0x0100) pinMode(CB2, OUTPUT); else pinMode(CB2, INPUT);
+  if(ddr & 0x1000) pinMode(CB3, OUTPUT); else pinMode(CB3, INPUT);
+  }
+
+uint32_t get_pin() {
+  uint16_t pin = 0;
+  if(digitalRead(CB0)) pin |= 0x0001;
+  if(digitalRead(CB1)) pin |= 0x0010;
+  if(digitalRead(CB2)) pin |= 0x0100;
+  if(digitalRead(CB3)) pin |= 0x1000;
+  return pin;
+  }
+
+
+/*****************************************************************
+ * EEPROM
+ *****************************************************************/  
+
+/*****************************************************************
+ * Setup
+ *****************************************************************/
+
+void setup() {
+  pinMode(LED_YELLOW, OUTPUT);
+  pinMode(LED_GREEN, OUTPUT); 
+
+  //start the telnet server
+  WiFi.onEvent(WiFiEvent);
+  delay(1000);
+  ETH.begin(1,17,23,18,ETH_PHY_LAN8720,ETH_CLOCK_GPIO0_IN);
+  server.begin();
+  server.setNoDelay(true);
+  
+  while (eth_connected == false);
+  
+  //visual status for telnet work
+  digitalWrite(LED_YELLOW,LOW);
+  digitalWrite(LED_GREEN,HIGH);
+ // Serial.begin(9600, SERIAL_8N2);
+
+  Serial.begin(9600,SERIAL_8N1);
+  modbus.begin(9600,SERIAL_8N1);
+  modbus.setTimeout(1000);
+  }
+
+  
+  
+/*****************************************************************
+ * Main loop
+ *****************************************************************/ 
+void loop() {
+  uint8_t i;
+  if (eth_connected == true) {
+    //check if there are any new clients
+    if (server.hasClient()) {
+      for(i = 0; i < MAX_SRV_CLIENTS; i++) {
+        //find free/disconnected spot
+        if (!serverClients[i] || !serverClients[i].connected()) {
+          if(serverClients[i]) serverClients[i].stop();
+          serverClients[i] = server.available();
+          break;
+          }
+        }
+      if (i >= MAX_SRV_CLIENTS) {
+        //no free/disconnected spot so reject
+        server.available().stop();
+        }
+      }
+    //check clients for data
+    for(i = 0; i < MAX_SRV_CLIENTS; i++) {
+      if (serverClients[i] && serverClients[i].connected()) {
+        if(serverClients[i].available()) {
+          //get data from the telnet client and push it to the UART
+          while(serverClients[i].available()) {
+            int character = serverClients[i].read();
+            if( character >= 0 )
+              getdata(character);
+            }
+          }
+        }
+      else {
+        if (serverClients[i]) {
+          serverClients[i].stop();
+          }
+        }
+      }
+    }
+
+  // unsigned long time = millis();
+  // if(time - lastModBus >= 1000)     {
+  //   lastModBus = time;
+  //   modbustest();
+  //   }
+  }     
+
+// void modbustest(){
+//   uint16_t inputdata[10] = {0xeeee};
+//   for(uint8_t i=2; i<3; i++) {
+//     uint32_t error = modbus.readHoldingRegisters(2,i,inputdata,2);
+//     rxbuf[2] = 'e';
+//     rxbuf[1] = 'e';
+//     send_answer_hex(rxbuf,error);
+//     rxbuf[2] = nib_to_hex(i,0);
+//     rxbuf[1] = nib_to_hex(i,1);
+//     send_answer_hex(rxbuf,((uint32_t)inputdata[0]));
+//     send_answer_hex(rxbuf,((uint32_t)inputdata[1]));
+//     send_answer_hex(rxbuf,((uint32_t)inputdata[2]));
+//     send_answer_hex(rxbuf,((uint32_t)inputdata[3]));
+//
+//     send_answer_hex(rxbuf,(((uint32_t)inputdata[0])<<16) + inputdata[1]);
+//     send_answer_hex(rxbuf,(((uint32_t)inputdata[2])<<16) + inputdata[3]);
+//
+//     send_answer_hex(rxbuf,0x12345678);
+//   }
+// }
+
+uint8_t modbus_busid;
+
+
+/*****************************************************************
+ * Receive & Interpret commands
+ *****************************************************************/
+
+void getdata(uint8_t buf) {
+
+  if (rxcnt != 0 || (buf == 'W' || buf == 'R')) {
+    rxbuf[rxcnt++] = buf;
+    }
+  if (buf == '\n' || buf == '\r') { // End of Command
+    if (rxcnt == 12) {
+      uint8_t regnumber = hex_to_int(rxbuf[2]) + hex_to_int(rxbuf[1])*16;
+
+      if (rxbuf[0] == 'R') {
+        if (regnumber == 1) {        //Register 1: PIN
+          send_answer_hex(&rxbuf[0], get_pin());
+          }         
+
+      } 
+      if (rxbuf[0] == 'W') {
+        uint32_t value = hex_to_int32(&rxbuf[3]);
+        if (regnumber == 0) {        //Register 0: PORT
+          set_port(value);
+          send_answer_hex(&rxbuf[0], value);
+          }
+        if (regnumber == 2) {        //Register 2: DDR
+          set_ddr(value);
+          send_answer_hex(&rxbuf[0], value);
+          }
+        if (regnumber == 0x10) {        //Register 10: MODBUS_SETUP
+          modbus_busid = hex_to_int(rxbuf[4]) + hex_to_int(rxbuf[3])*16;
+          }
+        if (regnumber == 0x11) {        //Register 11: MODBUS_READ_REG
+          uint16_t modbus_address = hex_to_int(rxbuf[6]) + hex_to_int(rxbuf[5])*16 + hex_to_int(rxbuf[4])*16*16 + hex_to_int(rxbuf[3])*16*16*16;
+          uint8_t  modbus_length  = hex_to_int(rxbuf[10]) + hex_to_int(rxbuf[9])*16;
+          uint16_t inputdata[10];
+          uint32_t error = modbus.readHoldingRegisters(modbus_busid,modbus_address,inputdata,modbus_length);
+          if(error) {
+            send_answer_hex(rxbuf,error + 0xeeeeee00);
+            }
+          else {
+            send_answer_hex(rxbuf,(((uint32_t)inputdata[0])<<16) + inputdata[1]);
+            }
+          }
+        if (regnumber == 0x12) {        //Register 10: MODBUS_WRITE_REG
+          uint16_t modbus_address = hex_to_int(rxbuf[6]) + hex_to_int(rxbuf[5])*16 + hex_to_int(rxbuf[4])*16*16 + hex_to_int(rxbuf[3])*16*16*16;
+          uint16_t modbus_data    = hex_to_int(rxbuf[10]) + hex_to_int(rxbuf[9])*16 + hex_to_int(rxbuf[8])*16*16 + hex_to_int(rxbuf[7])*16*16*16;
+          uint32_t error = modbus.writeSingleHoldingRegister(modbus_busid,modbus_address,modbus_data);
+          if(error) {
+            send_answer_hex(rxbuf,error + 0xeeeeee00);
+            }
+          else {
+            send_answer_hex(rxbuf,0);
+            }
+          }
+        }
+      }
+    }
+    
+  if (rxcnt >= 12 || buf == '\n' || buf == '\r') {
+    rxcnt = 0;
+    }    
+  }
+
+
+/*
+ **Data format:**    *XRRvvvvvvvv*
+         X    - command   (write: W, read: R, answer: A)
+         R    - register (Hex value)
+         vvvvvvvv - 32 Bit value
+
+         close with a "\n"
+         e.g. "W0200000000\n"
+
+
+| Registers |  description                                                 |
+|-----------|--------------------------------------------------------------|
+|    0      |  PORT_C                                                 (W)  |  V = xxxx3210 (one nibble per pin, possible values 0/1)
+|    1      |  PIN_C                                                  (R)  |
+|    2      |  DDR_C                                                  (W)  |
+|   10h     |  MODBUS_SETUP     II000000                              (W)  | I: busId
+|   11h     |  MODBUS_READ_REG  AAAA00LL                              (W)  | read LL (1 or 2) words from address A
+|   12h     |  MODBUS_WRITE_REG AAAADDDD                              (W)  | write 16 bit data D to address A
+*/
diff --git a/esp32/EthernetUART/ModBus/README.txt b/esp32/EthernetUART/ModBus/README.txt
new file mode 100644 (file)
index 0000000..8c25752
--- /dev/null
@@ -0,0 +1,21 @@
+4 controllable I/O pins
+Modbus interface on RX/TX - only holding reg read / write implemented
+
+
+ **Data format:**    *XRRvvvvvvvv*
+         X    - command   (write: W, read: R, answer: A)
+         R    - register (Hex value)
+         vvvvvvvv - 32 Bit value
+
+         close with a "\n"
+         e.g. "W0200000000\n"
+
+
+| Registers |  description                                                 |
+|-----------|--------------------------------------------------------------|
+|    0      |  PORT_C                                                 (W)  |  V = xxxx3210 (one nibble per pin, possible values 0/1)
+|    1      |  PIN_C                                                  (R)  |
+|    2      |  DDR_C                                                  (W)  |
+|   10h     |  MODBUS_SETUP     II000000                              (W)  | I: busId
+|   11h     |  MODBUS_READ_REG  AAAA00LL                              (W)  | read LL (1 or 2) words from address A
+|   12h     |  MODBUS_WRITE_REG AAAADDDD                              (W)  | write 16 bit data D to address A