From 264dae9ae605694d919205778e510167ce5de80c Mon Sep 17 00:00:00 2001 From: Jan Michel Date: Thu, 1 Jan 2026 17:12:40 +0100 Subject: [PATCH] add ESP32 design for modbus + GPIO --- esp32/EthernetUART/ModBus/ModBus.ino | 359 +++++++++++++++++++++++++++ esp32/EthernetUART/ModBus/README.txt | 21 ++ 2 files changed, 380 insertions(+) create mode 100644 esp32/EthernetUART/ModBus/ModBus.ino create mode 100644 esp32/EthernetUART/ModBus/README.txt diff --git a/esp32/EthernetUART/ModBus/ModBus.ino b/esp32/EthernetUART/ModBus/ModBus.ino new file mode 100644 index 0000000..b6f5bc4 --- /dev/null +++ b/esp32/EthernetUART/ModBus/ModBus.ino @@ -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 //for external ADC +#include //for Telnet Server Connection +#include //for I2C Connection +#include //for display control +#include //to save settings +#include + +#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 index 0000000..8c25752 --- /dev/null +++ b/esp32/EthernetUART/ModBus/README.txt @@ -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 -- 2.51.0