Skip to content

Instantly share code, notes, and snippets.

@bungernut
Last active October 18, 2022 16:31
Show Gist options
  • Save bungernut/fbbf6b352b754029f3d69323d267d19f to your computer and use it in GitHub Desktop.
Save bungernut/fbbf6b352b754029f3d69323d267d19f to your computer and use it in GitHub Desktop.
Modbus ESP32 TTGO LCD Demo
/*
The device I'm using is https://myduino.com/product/tgo-007/
Before compiling make a WPA_secrets.h file with the following two lines
const char* SECRET_SSID = "your ssid";
const char* SECRET_WIFI_PASS = "your WPA pass";
Libraries:
modbus: https://github.com/eModbus/eModbus (and it's dependencies)
display: https://github.com/Xinyuan-LilyGO/TTGO-T-Display
Arduino IDE Setup:
This was a good video to follow: https://www.youtube.com/watch?v=UE1mtlsxfKM&t=307s,
A FEW NOTES ON THAT VIDEO:
*) Add this to File>Pref>Board Manager URL: https://dl.espressif.com/dl/package_esp32_index.json
*) I dont think USB serial drivers are necessary in WIN10? Maybe I had them already...
*) Edit the User_Setup_Select.h per the video
*) No need for the Data-Uploader for our projects
Reading the registers in python
```
from pymodbus.client.sync import ModbusTcpClient
client = ModbusTcpClient('192.168.1.115', port=502)
client.connect()
rr = client.read_holding_registers(105,1, unit=1)
assert(rr.function_code < 0x80) # test that we are not an error
print(rr.registers)
#client.close()
```
Writing registers in python
```
client = ModbusTcpClient('192.168.1.115', port=502)
client.connect()
rr = client.write_register(5,220, unit=1)
#assert(rr.function_code < 0x80) # test that we are not an error
print(rr)
client.close()
```
*/
#include "WPA_secrets.h"
#include <Arduino.h>
#include <WiFi.h>
#include <SPI.h>
#include "ModbusServerWiFi.h"
#include <Ethernet.h>
#include <time.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
const char* ssid = SECRET_SSID;
const char* password = SECRET_WIFI_PASS;
IPAddress local_IP(192, 168, 1, 115);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8); //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional
/* Modbus
Input registers are 16-bit registers used for input, and may only be read.
Holding registers are the most universal 16-bit register, may be read or written
API: https://github.com/emelianov/modbus-esp8266/blob/master/API.md
REMEMBER: memo and address are offset by 1, so IREG_SEC is at addr 101
*/
const uint8_t MY_SERVER(1);
const uint8_t nInputRegisters = 128;
uint16_t MBregisters[nInputRegisters]; // Allocate memory for registers
bool MBWriteEnabled[nInputRegisters];
const uint16_t IREG_SEC = 0; //seconds
const uint16_t IREG_MIN = 1; //minutes
const uint16_t IREG_HOUR = 2; //hours
const uint16_t IREG_ADC2 = 3; // reading the ADC on GPIO2
const uint16_t IREG_RAND = 4;
const uint16_t HREG_01 = 5;
const uint16_t HREG_02U = 6;
const uint16_t HREG_02L = 7;
union {
float asFloat;
uint16_t asInt[2];
} flreg;
ModbusServerWiFi MBserver;
// NTP Time keeping
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");
struct tm utc;
/* Display Setup
int16_t h = 135;
int16_t w = 240;
*/
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
// Loop timeing variables
unsigned long currentMillis;
unsigned long ntpUpdateMillis;
unsigned long modbusUpdateMillis;
unsigned long randomUpdateMillis;
unsigned long adcUpdateMillis;
unsigned long printMBMillis;
const int adcPin = 32; // My board GPIO2/12 did not work...
void setup() {
/* Setup Serial */
Serial.begin(115200);
/* Setup WiFi */
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("ESP Mac Address: ");
Serial.println(WiFi.macAddress());
Serial.print("Subnet Mask: ");
Serial.println(WiFi.subnetMask());
Serial.print("Gateway IP: ");
Serial.println(WiFi.gatewayIP());
Serial.print("DNS: ");
Serial.println(WiFi.dnsIP());
timeClient.begin();
Serial.print("NTP Begin: ");
timeClient.update();
Serial.println(timeClient.getFormattedTime());
/* Setup Screen */
tft.init();
tft.setRotation(2);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
delay(500);
// Register the worker function with the Modbus server
MBserver.registerWorker(MY_SERVER, READ_HOLD_REGISTER, &FC03);
// Register the worker function again for another FC
MBserver.registerWorker(MY_SERVER, READ_INPUT_REGISTER, &FC03);
// And write holding registers
MBserver.registerWorker(MY_SERVER, WRITE_HOLD_REGISTER, &FC06);
// Port number 502, maximum of 4 clients in parallel, 10 seconds timeout
MBserver.start(502, 4, 10000);
}
void loop() {
currentMillis = millis();
if (currentMillis - ntpUpdateMillis > 360000) {
// update NTP clock occasionally
timeClient.update();
Serial.println(timeClient.getFormattedTime());
ntpUpdateMillis = currentMillis;
}
if (currentMillis - randomUpdateMillis > 10000){
MBregisters[IREG_RAND] = (uint16_t) random(65000);
randomUpdateMillis = currentMillis;
}
if (currentMillis - adcUpdateMillis > 1000){
MBregisters[IREG_ADC2] = analogRead(adcPin);
//Serial.println(analogRead(adcPin));
adcUpdateMillis = currentMillis;
}
if (currentMillis - modbusUpdateMillis > 1000){
modbusUpdateMillis = currentMillis;
MBregisters[IREG_SEC] = timeClient.getSeconds();
MBregisters[IREG_MIN] = timeClient.getMinutes();
MBregisters[IREG_HOUR]= timeClient.getHours();
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE,TFT_BLACK);
tft.setTextSize(2); //I think each unit is 8 tall?
tft.setCursor(0,0);
tft.print(IREG_SEC); tft.print(" SEC "); tft.println(MBregisters[IREG_SEC]);
tft.print(IREG_MIN); tft.print(" MIN "); tft.println(MBregisters[IREG_MIN]);
tft.print(IREG_HOUR); tft.print(" HR "); tft.println(MBregisters[IREG_HOUR]);
tft.print(IREG_ADC2); tft.print(" ADC "); tft.println(MBregisters[IREG_ADC2]);
tft.print(IREG_RAND); tft.print(" RND "); tft.println(MBregisters[IREG_RAND]);
tft.print(HREG_01); tft.print(" U01 "); tft.println(MBregisters[HREG_01]);
tft.print(HREG_02U); tft.print(" U2U "); tft.println(MBregisters[HREG_02U]);
tft.print(HREG_02L); tft.print(" U2L "); tft.println(MBregisters[HREG_02L]);
tft.print("FLOAT ");
flreg.asInt[1] = MBregisters[HREG_02U];
flreg.asInt[0] = MBregisters[HREG_02L];
tft.println(flreg.asFloat);
}
// We will be idling around here - all is done in subtasks :D
if (currentMillis - printMBMillis > 2000) {
Serial.print(MBserver.activeClients());
Serial.println(" clients running");
printMBMillis = currentMillis;
}
}
ModbusMessage FC06(ModbusMessage request) {
/*
ModbusMessage(uint8_t serverID, uint8_t functionCode, uint16_t p1, uint16_t p2)
This constructor will be one of the most used throughout, as the most relevant Modbus
messages have this signature (taking two 16bit parameters). These are the FCs 0x01, 0x02, 0x03, 0x04, 0x05 and 0x06.
*/
// WRITE_HOLD_REGISTER = 0x06,
// Worker function for serverID=1
Serial.print("FC06: Function Code: ");
Serial.println(request.getFunctionCode());
uint16_t addr = 0; // Start address to read
uint16_t datr = 0; // Data of a write_register()
ModbusMessage response;
// Prints the request message
for (auto& byte : request) {
Serial.printf("%02X ", byte);
}
Serial.println();
// Get addr and words from data array. Values are MSB-first, get() will convert to binary
request.get(2, addr);
request.get(4, datr);
Serial.print("Addr:"); Serial.println(addr);
Serial.print("datr:"); Serial.println(datr);
// address valid?
if (!addr || addr > nInputRegisters) {
// No. Return error response
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
return response;
}
MBregisters[addr] = datr;
// Prepare response
response.setError(request.getServerID(), request.getFunctionCode(), SUCCESS);
// Return the data response
return response;
}
ModbusMessage FC03(ModbusMessage request) {
// READ_HOLD_REGISTER = 0x03,
// READ_INPUT_REGISTER = 0x04,
// Worker function for serverID=1
// holding_memo[16]
uint16_t addr = 0; // Start address to read
uint16_t wrds = 0; // Number of words to read
ModbusMessage response;
// Get addr and words from data array. Values are MSB-first, get() will convert to binary
request.get(2, addr);
request.get(4, wrds);
// address valid?
if (!addr || addr > nInputRegisters) {
// No. Return error response
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
return response;
}
// Number of words valid?
if (!wrds || (addr + wrds) > (nInputRegisters-1)) {
// No. Return error response
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
return response;
}
// Prepare response
response.add(request.getServerID(), request.getFunctionCode(), (uint8_t)(wrds * 2));
// Loop over all words to be sent
for (uint16_t i = 0; i < wrds; i++) {
// Add word MSB-first to response buffer
response.add(MBregisters[addr + i]);
}
// Return the data response
return response;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment