Modbus over tcp/ip with teensy 4.1 (MMXRT1060)

Ett

New member
This is my first posting on this site so please forgive me as I am new to this support process.

I have used teensy for years (I used to coach a high school robotics team). Currently I am an electronics technician attempting to utilize a teensy 4.1 (I bought several of them, along with the magjack ethernet kits) for calibration of a quad encoder with a valve positioner. I need the teensy to feed encoder count values over modbus (ideally through tcp/ip ethernet) to an HMI with an embedded version of windows 7. Currently I pieced together the following code from the examples. It does work to feed encoder values to an internet explorer webpage on the HMI. I am not finding a good example for modbus over tcp/ip for the teensy4.1 that works to get the data I need into the right location.

Here is the code that works as a webserver for the encoder values:

#include "QuadEncoder.h"
#include <SPI.h>
#include <NativeEthernet.h>
byte mac[] = {
0x04, 0xe9, 0xe5, 0x0f, 0xb9, 0xcd
};
IPAddress ip(192, 168, 111, 44);
EthernetServer server(80);

uint32_t mCurPosValue;
uint32_t old_position = 0;
uint32_t mCurPosValue1;
uint32_t old_position1 = 0;
QuadEncoder myEnc1(1, 0, 1, 0);

QuadEncoder myEnc2(2, 2, 3, 0);


void setup()
{
while(!Serial && millis() < 4000);

/* Initialize the ENC module. */
myEnc1.setInitConfig(); //
myEnc1.EncConfig.revolutionCountCondition = ENABLE;
myEnc1.EncConfig.enableModuloCountMode = ENABLE;
myEnc1.EncConfig.positionModulusValue = 20;
// with above settings count rev every 20 ticks
// if myEnc1.EncConfig.revolutionCountCondition = ENABLE;
// is not defined or set to DISABLE, the position is zeroed every
// 20 counts, if enabled revolution counter is incremented when
// phaseA ahead of phaseB, and decrements from 65535 when reversed.
myEnc1.init();

myEnc2.setInitConfig(); //
myEnc2.EncConfig.positionInitialValue = 160;
myEnc2.EncConfig.positionMatchMode = ENABLE;
myEnc2.EncConfig.positionCompareValue = 200;
myEnc2.EncConfig.filterCount = 5;
myEnc2.EncConfig.filterSamplePeriod = 255;
myEnc2.init();
Serial.begin(9600);
Serial.println("Ethernet WebServer Example");
Ethernet.begin(mac, ip);

// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}

// start the server
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());

}

void loop(){
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each analog input pin


client.print(" Encoder count is ");
client.print(mCurPosValue1);
client.println("<br />");

client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disconnected");
}
/* This read operation would capture all the position counter to responding hold registers. */
mCurPosValue = myEnc1.read();

if(mCurPosValue != old_position){
/* Read the position values. */
Serial.printf("Current position value1: %ld\r\n", mCurPosValue);
Serial.printf("Position differential value1: %d\r\n", (int16_t)myEnc1.getHoldDifference());
Serial.printf("Position HOLD revolution value1: %d\r\n", myEnc1.getHoldRevolution());
Serial.println();
}

old_position = mCurPosValue;

mCurPosValue1 = myEnc2.read();

if(myEnc2.compareValueFlag == 1) {
//myEnc2.init();
//resets counter to positionInitialValue so compare
//will hit every 200
myEnc2.write(myEnc2.EncConfig.positionInitialValue);
Serial.print("Compare Value Hit for Encoder 2: ");
Serial.println(myEnc2.compareValueFlag);
Serial.println();
myEnc2.compareValueFlag = 0;
}

if(mCurPosValue1 != old_position1){
/* Read the position values. */
Serial.printf("Current position value2: %ld\r\n", mCurPosValue1);
Serial.printf("Position differential value2: %d\r\n", (int16_t)myEnc2.getHoldDifference());
Serial.printf("Position revolution value2: %d\r\n", myEnc2.getHoldRevolution());
Serial.println();
}

old_position1 = mCurPosValue1;

}





What I need is the Modbus portion instead of the webserver. I also need a way to divine the appropriate memory address for the data tag (encoder count integer value represented by "mCurPosValue1" in the above program) to interface with the HMI visualization program.

Any help would be greatly appreciated!
 
Code:
#include "QuadEncoder.h"
#include <SPI.h>
#include <NativeEthernet.h>
byte mac[] = {
0x04, 0xe9, 0xe5, 0x0f, 0xb9, 0xcd
};
IPAddress ip(192, 168, 111, 44);
EthernetServer server(80);

uint32_t mCurPosValue;
uint32_t old_position = 0;
uint32_t mCurPosValue1;
uint32_t old_position1 = 0;
QuadEncoder myEnc1(1, 0, 1, 0);

QuadEncoder myEnc2(2, 2, 3, 0);


void setup()
{
	while (!Serial && millis() < 4000);

	/* Initialize the ENC module. */
	myEnc1.setInitConfig(); //
	myEnc1.EncConfig.revolutionCountCondition = ENABLE;
	myEnc1.EncConfig.enableModuloCountMode = ENABLE;
	myEnc1.EncConfig.positionModulusValue = 20;
	// with above settings count rev every 20 ticks
	// if myEnc1.EncConfig.revolutionCountCondition = ENABLE;
	// is not defined or set to DISABLE, the position is zeroed every
	// 20 counts, if enabled revolution counter is incremented when
	// phaseA ahead of phaseB, and decrements from 65535 when reversed.
	myEnc1.init();

	myEnc2.setInitConfig(); //
	myEnc2.EncConfig.positionInitialValue = 160;
	myEnc2.EncConfig.positionMatchMode = ENABLE;
	myEnc2.EncConfig.positionCompareValue = 200;
	myEnc2.EncConfig.filterCount = 5;
	myEnc2.EncConfig.filterSamplePeriod = 255;
	myEnc2.init();
	Serial.begin(9600);
	Serial.println("Ethernet WebServer Example");
	Ethernet.begin(mac, ip);

	// Check for Ethernet hardware present
	if (Ethernet.hardwareStatus() == EthernetNoHardware) {
		Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. ");
		while (true) {
			delay(1); // do nothing, no point running without Ethernet hardware
		}
	}
	if (Ethernet.linkStatus() == LinkOFF) {
		Serial.println("Ethernet cable is not connected.");
	}

	// start the server
	server.begin();
	Serial.print("server is at ");
	Serial.println(Ethernet.localIP());

}

void loop() {
	EthernetClient client = server.available();
	if (client) {
		Serial.println("new client");
		// an http request ends with a blank line
		boolean currentLineIsBlank = true;
		while (client.connected()) {
			if (client.available()) {
				char c = client.read();
				Serial.write(c);
				// if you've gotten to the end of the line (received a newline
				// character) and the line is blank, the http request has ended,
				// so you can send a reply
				if (c == '\n' && currentLineIsBlank) {
					// send a standard http response header
					client.println("HTTP/1.1 200 OK");
					client.println("Content-Type: text/html");
					client.println("Connection: close"); // the connection will be closed after completion of the response
					client.println("Refresh: 5"); // refresh the page automatically every 5 sec
					client.println();
					client.println("<!DOCTYPE HTML>");
					client.println("<html>");
					// output the value of each analog input pin


					client.print(" Encoder count is ");
					client.print(mCurPosValue1);
					client.println("<br />");

					client.println("</html>");
					break;
				}
				if (c == '\n') {
					// you're starting a new line
					currentLineIsBlank = true;
				}
				else if (c != '\r') {
					// you've gotten a character on the current line
					currentLineIsBlank = false;
				}
			}
		}
		// give the web browser time to receive the data
		delay(1);
		// close the connection:
		client.stop();
		Serial.println("client disconnected");
	}
	/* This read operation would capture all the position counter to responding hold registers. */
	mCurPosValue = myEnc1.read();

	if (mCurPosValue != old_position) {
		/* Read the position values. */
		Serial.printf("Current position value1: %ld\r\n", mCurPosValue);
		Serial.printf("Position differential value1: %d\r\n", (int16_t)myEnc1.getHoldDifference());
		Serial.printf("Position HOLD revolution value1: %d\r\n", myEnc1.getHoldRevolution());
		Serial.println();
	}

	old_position = mCurPosValue;

	mCurPosValue1 = myEnc2.read();

	if (myEnc2.compareValueFlag == 1) {
		//myEnc2.init();
		//resets counter to positionInitialValue so compare
		//will hit every 200
		myEnc2.write(myEnc2.EncConfig.positionInitialValue);
		Serial.print("Compare Value Hit for Encoder 2: ");
		Serial.println(myEnc2.compareValueFlag);
		Serial.println();
		myEnc2.compareValueFlag = 0;
	}

	if (mCurPosValue1 != old_position1) {
		/* Read the position values. */
		Serial.printf("Current position value2: %ld\r\n", mCurPosValue1);
		Serial.printf("Position differential value2: %d\r\n", (int16_t)myEnc2.getHoldDifference());
		Serial.printf("Position revolution value2: %d\r\n", myEnc2.getHoldRevolution());
		Serial.println();
	}

	old_position1 = mCurPosValue1;

}
Hi, welcome to the world of Teensy.
Could you in future enclose your code between code tags by pressing the # key, as I have done for your code as shown above.
 
I have gotten the code to work successfully for modbus communication over tcp/ip however there is a lot of latency from the request from Qmodbusmaster before I can make another request without throwing an error from the teensy 4.1
The response from the teensy states address 200 (when it is really 1) with erroneous data (25600 coils)
modbuserror.jpg
Code:
//call for ethernet and modbus configuration libraries

#include <NativeEthernet.h>
#include <ArduinoModbus.h>
bool _1s;
unsigned long TimeAct, TimePrev, HoldingResult, InputResult, StartingAddr;
long Cmd;
EthernetServer EthServer(502);
ModbusTCPServer modbusTCPServer;


// call for encoder configuration libraries
#include "QuadEncoder.h"


uint32_t mCurPosValue;
uint32_t old_position = 0;
uint32_t mCurPosValue1;
uint32_t old_position1 = 0;
QuadEncoder myEnc1(1, 0, 1, 0);  // Encoder on channel 1 of 4 available
                                 // Phase A (pin0), PhaseB(pin1), Pullups Req(0)
QuadEncoder myEnc2(2, 2, 3, 0);  // Encoder on channel 2 of 4 available
                                 //Phase A (pin2), PhaseB(pin3), Pullups Req(0)

void setup() {
    while(!Serial && millis() < 4000);

  /* Initialize the ENC module. */
  myEnc1.setInitConfig();  //
  myEnc1.EncConfig.revolutionCountCondition = ENABLE;
  myEnc1.EncConfig.enableModuloCountMode = ENABLE;
  myEnc1.EncConfig.positionModulusValue = 4000; 
  // with above settings count rev every 20 ticks
  // if myEnc1.EncConfig.revolutionCountCondition = ENABLE;
  // is not defined or set to DISABLE, the position is zeroed every
  // 20 counts, if enabled revolution counter is incremented when 
  // phaseA ahead of phaseB, and decrements from 65535 when reversed.
  myEnc1.init();
  
  myEnc2.setInitConfig();  //
  myEnc2.EncConfig.positionInitialValue = 100;
  myEnc2.EncConfig.positionMatchMode = ENABLE;
  myEnc2.EncConfig.positionCompareValue = 200;
  myEnc2.EncConfig.filterCount = 5;
  myEnc2.EncConfig.filterSamplePeriod = 255;
  myEnc2.init();

  
  // call for ip address, mac, subnet mask for comm on ip network
byte mac[] = { 0x04, 0xe9, 0xe5, 0x0f, 0xb9, 0xcd };  // Define MAc address
  byte ip[] = { 192, 168, 111, 44 };                      // Define IP address
  byte subnet[] = { 255, 255, 255, 0 };                 // Define SubNEt mask

  // initialize the ethernet device
  Ethernet.begin(mac, ip, subnet);                      // Assign MAC, IP, and subnet mask
  //Serial.begin(9600);
  EthServer.begin();          // start listening for clients
  modbusTCPServer.begin();    // start listening for clients

  // Define Holding register:
  HoldingResult = modbusTCPServer.configureHoldingRegisters(0, 100);
  InputResult = modbusTCPServer.configureInputRegisters(0, 100);

  //Serial.print("Holding Reg init result =");
  //Serial.print(HoldingResult);
  //Serial.print("\n");

  //Serial.print("Input Reg init result =");
  //Serial.print(InputResult);
  //Serial.print("\n");

  //Serial.print("Modbus server address=");
  //Serial.println(Ethernet.localIP());
  //Serial.print("\n");


}

void loop() {
 EthernetClient client = EthServer.available();

  if (client.connected()) {
    modbusTCPServer.accept(client);
    // poll for Modbus TCP requests, while client connected
    modbusTCPServer.poll();
    // Serial.print("poll");
  }

  //---------------------------------------------------------------------------------
  // TIME  clock 1s
  TimeAct = millis();      // Millis value between  0 and  655535

  _1s = false;
  if (  ( (TimeAct > TimePrev) and (TimeAct - TimePrev) > 1000) or ((TimeAct < TimePrev) and (65535 - TimePrev + TimeAct) > 1000 )  ) {
    _1s = true;
    TimePrev = TimeAct;
  }
    /* This read operation would capture all the position counter to responding hold registers. */
  mCurPosValue = myEnc1.read();

  if(mCurPosValue != old_position){
    /* Read the position values. */
    //Serial.printf("Current position value1: %ld\r\n", mCurPosValue);
    //Serial.printf("Position differential value1: %d\r\n", (int16_t)myEnc1.getHoldDifference());
    //Serial.printf("Position HOLD revolution value1: %d\r\n", myEnc1.getHoldRevolution());
    //Serial.println();
  }

  old_position = mCurPosValue;

  mCurPosValue1 = myEnc2.read();

  if(myEnc2.compareValueFlag == 1) {
    //myEnc2.init();
    //resets counter to positionInitialValue so compare 
    //will hit every 200
    myEnc2.write(myEnc2.EncConfig.positionInitialValue);
    //Serial.print("Compare Value Hit for Encoder 2:  ");
    //Serial.println(myEnc2.compareValueFlag);
    //Serial.println();
    myEnc2.compareValueFlag = 0;
  }

  if(mCurPosValue1 != old_position1){
    /* Read the position values. */
    //Serial.printf("Current position value2: %ld\r\n", mCurPosValue);
    //Serial.printf("Position differential value2: %d\r\n", (int16_t)myEnc2.getHoldDifference());
    //Serial.printf("Position revolution value2: %d\r\n", myEnc2.getHoldRevolution());
    //Serial.println();
  }

  old_position1 = mCurPosValue1;

   // Modbus server  :

  // holding resgiter 40001: heartbeat (FC3)
  modbusTCPServer.holdingRegisterWrite(0x00, mCurPosValue);
    modbusTCPServer.holdingRegisterWrite(0x01, mCurPosValue1);
        modbusTCPServer.holdingRegisterWrite(0x02, 200);
modbusTCPServer.holdingRegisterWrite(0x03, 300);
modbusTCPServer.holdingRegisterWrite(0x04, 400);
modbusTCPServer.holdingRegisterWrite(0x05, 500);
modbusTCPServer.holdingRegisterWrite(0x06, 600);
modbusTCPServer.holdingRegisterWrite(0x07, 700);
modbusTCPServer.holdingRegisterWrite(0x08, 800);
modbusTCPServer.holdingRegisterWrite(0x09, 900);
modbusTCPServer.holdingRegisterWrite(0x0A, 1000);
modbusTCPServer.holdingRegisterWrite(0x0B, 1100);
modbusTCPServer.holdingRegisterWrite(0x0C, 1200);
modbusTCPServer.holdingRegisterWrite(0x0D, 1300);
modbusTCPServer.holdingRegisterWrite(0x0E, 1400);
modbusTCPServer.holdingRegisterWrite(0x0f, 1500);
modbusTCPServer.holdingRegisterWrite(0x010, 1600);
modbusTCPServer.holdingRegisterWrite(0x011, 1700);
modbusTCPServer.holdingRegisterWrite(0x012, 1800);
modbusTCPServer.holdingRegisterWrite(0x013, 1900);
modbusTCPServer.holdingRegisterWrite(0x014, 2000);
modbusTCPServer.holdingRegisterWrite(0x015, 2100);
modbusTCPServer.holdingRegisterWrite(0x016, 2200);
modbusTCPServer.holdingRegisterWrite(0x017, 2300);
modbusTCPServer.holdingRegisterWrite(0x018, 2400);
modbusTCPServer.holdingRegisterWrite(0x019, 2500);
modbusTCPServer.holdingRegisterWrite(0x1A, 2600);
modbusTCPServer.holdingRegisterWrite(0x01B, 2700);
modbusTCPServer.holdingRegisterWrite(0x01C, 2800);
modbusTCPServer.holdingRegisterWrite(0x01D, 2900);
modbusTCPServer.holdingRegisterWrite(0x01E, 3000);
modbusTCPServer.holdingRegisterWrite(0x01F, 3100);
modbusTCPServer.holdingRegisterWrite(0x20, 3200);
modbusTCPServer.holdingRegisterWrite(0x21, 3300);
modbusTCPServer.holdingRegisterWrite(0x22, 3400);
modbusTCPServer.holdingRegisterWrite(0x23, 3500);
modbusTCPServer.holdingRegisterWrite(0x24, 3600);
modbusTCPServer.holdingRegisterWrite(0x25, 3700);
modbusTCPServer.holdingRegisterWrite(0x26, 3800);
modbusTCPServer.holdingRegisterWrite(0x27, 3900);
modbusTCPServer.holdingRegisterWrite(0x28, 4000);
modbusTCPServer.holdingRegisterWrite(0x29, 4100);
modbusTCPServer.holdingRegisterWrite(0x2A, 4200);
modbusTCPServer.holdingRegisterWrite(0x2B, 4300);
modbusTCPServer.holdingRegisterWrite(0x2C, 4400);
modbusTCPServer.holdingRegisterWrite(0x2D, 4500);
modbusTCPServer.holdingRegisterWrite(0x2E, 4600);
modbusTCPServer.holdingRegisterWrite(0x2F, 4700);
modbusTCPServer.holdingRegisterWrite(0x30, 4800);
modbusTCPServer.holdingRegisterWrite(0x31, 4900);
modbusTCPServer.holdingRegisterWrite(0x32, 5000);
modbusTCPServer.holdingRegisterWrite(0x33, 5100);
modbusTCPServer.holdingRegisterWrite(0x34, 5200);
modbusTCPServer.holdingRegisterWrite(0x35, 5300);
modbusTCPServer.holdingRegisterWrite(0x36, 5400);
modbusTCPServer.holdingRegisterWrite(0x37, 5500);
modbusTCPServer.holdingRegisterWrite(0x38, 5600);
modbusTCPServer.holdingRegisterWrite(0x39, 5700);
modbusTCPServer.holdingRegisterWrite(0x3A, 5800);
modbusTCPServer.holdingRegisterWrite(0x3B, 5900);
modbusTCPServer.holdingRegisterWrite(0x3C, 6000);
modbusTCPServer.holdingRegisterWrite(0x3D, 6100);
modbusTCPServer.holdingRegisterWrite(0x3E, 6200);
modbusTCPServer.holdingRegisterWrite(0x3F, 6300);
modbusTCPServer.holdingRegisterWrite(0x40, 6400);
modbusTCPServer.holdingRegisterWrite(0x41, 6500);
modbusTCPServer.holdingRegisterWrite(0x42, 6600);
modbusTCPServer.holdingRegisterWrite(0x43, 6700);
modbusTCPServer.holdingRegisterWrite(0x44, 6800);
modbusTCPServer.holdingRegisterWrite(0x45, 6900);
modbusTCPServer.holdingRegisterWrite(0x46, 7000);
modbusTCPServer.holdingRegisterWrite(0x47, 7100);
modbusTCPServer.holdingRegisterWrite(0x48, 7200);
modbusTCPServer.holdingRegisterWrite(0x49, 7300);
modbusTCPServer.holdingRegisterWrite(0x4A, 7400);
modbusTCPServer.holdingRegisterWrite(0x4B, 7500);
modbusTCPServer.holdingRegisterWrite(0x4C, 7600);
modbusTCPServer.holdingRegisterWrite(0x4D, 7700);
modbusTCPServer.holdingRegisterWrite(0x4E, 7800);
modbusTCPServer.holdingRegisterWrite(0x4F, 7900);
modbusTCPServer.holdingRegisterWrite(0x50, 8000);
modbusTCPServer.holdingRegisterWrite(0x51, 8100);
modbusTCPServer.holdingRegisterWrite(0x52, 8200);
modbusTCPServer.holdingRegisterWrite(0x53, 8300);
modbusTCPServer.holdingRegisterWrite(0x54, 8400);
modbusTCPServer.holdingRegisterWrite(0x55, 8500);
modbusTCPServer.holdingRegisterWrite(0x56, 8600);
modbusTCPServer.holdingRegisterWrite(0x57, 8700);
modbusTCPServer.holdingRegisterWrite(0x58, 8800);
modbusTCPServer.holdingRegisterWrite(0x59, 8900);
modbusTCPServer.holdingRegisterWrite(0x5A, 9000);
modbusTCPServer.holdingRegisterWrite(0x5B, 9100);
modbusTCPServer.holdingRegisterWrite(0x5C, 9200);
modbusTCPServer.holdingRegisterWrite(0x5D, 9300);
modbusTCPServer.holdingRegisterWrite(0x5E, 9400);
modbusTCPServer.holdingRegisterWrite(0x5F, 9500);
modbusTCPServer.holdingRegisterWrite(0x60, 9600);
modbusTCPServer.holdingRegisterWrite(0x61, 9700);
modbusTCPServer.holdingRegisterWrite(0x62, 9800);
modbusTCPServer.holdingRegisterWrite(0x63, 9900);


                  
 
                

  // holding resgiter 40500: Command Word (FC6)
 // Cmd = modbusTCPServer.holdingRegisterRead(90);



}
 
Hello,

Did you find a solution to the modbus delay?

I am facing similar issues with ModbusTCP. It works but after a few requests, it kind of locks until I can make another request.

Thank you.
 
Back
Top