ModbusMaster Library issue over E2 error code

Hi all, I am having an issue ModbusMaster.h library trying to read input registers from modbus slave. I am using vs code extension. The code is given below i havent made much changes to the reference such that i dont make any mistakes. plzz find the code below.

`/*********************************************************************
 * Project Modbus_Project
 * Description:  Creating a project that communicates with a
 *               peripherial device, temp sensor, using Modbus RTU
 *               over RS485.
 * Author:       Erik Fasnacht
 * Date:         08/25/22
 *********************************************************************/
#include "Particle.h"

/********************************************************************/
//! /brief library includes
#include <ModbusMaster.h>
#include "JsonParserGeneratorRK.h"

/********************************************************************/
//! /brief  declarations
#define SUCCESS 1 // success code for modbus
#define FAIL -1	  // fail code for modbus
#define BAUD 9600 // baud rate for modbus and UART
#define MODADDR 2 // sets server (slave) address for modbus
// #define SERIAL1 1 // used to select Serial1, RX/TX pins for Modbus

double curHum;	// current humidity (%)
double curTemp; // current temperature (°F)

int result; // modbus error code

int humSet = 25;  // humidity setpoint, preset to 40%
int tempSet = 24; // temperature setpoint, preset to 75°F

int humWindow = 5;	// humidity window, preset to +/- 5%
int tempWindow = 5; // temperature window, preset to +/- 5°F

int senFreq = 60; // sensor publish frequency (in seconds), preset to 60 seconds
int warFreq = 60; // warning publish frequency (in seconds), preset to 60 seconds

/********************************************************************/
//! /brief Other settings

// Name of published event with temperature/humidity information
const char *sensorEventName = "modbus-sensor";

const char *errorEventName = sensorEventName;

// How often to check the sensor. Default: Every 1 second
const std::chrono::milliseconds checkInterval = 1s; // check sensor every 1 second

/********************************************************************/
//! /brief particle platform initialization
SYSTEM_MODE(SEMI_AUTOMATIC); // connect app to cloud automatically
SYSTEM_THREAD(ENABLED);

SerialLogHandler logHandler;

// To enable more debugging, use this version instead of the one above:
// SerialLogHandler logHandler(LOG_LEVEL_TRACE);

/********************************************************************/
//! /brief modbus initialization
ModbusMaster node(Serial1, MODADDR); // selects serial1 port and address id 1

/********************************************************************/
//! /brief forward declarations
int setHumidityWindow(String value);
int setTemperatureWindow(String value);
int temperatureSetpoint(String value);
int humiditySetpoint(String value);
int setPublishFrequency(String value);
int setWarningFrequency(String value);

int getSensorValues();
void publishSensorValues();
void publishWarningMessage(int errorCode = 0);

/********************************************************************/
//! /brief setup loop, only runs once at inital startup
void setup()
{
	//! setup cloud functions, best practice to do first in setup loop
	Particle.function("HumWindow", setHumidityWindow);	   // set humidity window
	Particle.function("TempWindow", setTemperatureWindow); // set temperature window

	Particle.function("HumSetPoint", humiditySetpoint);		// set humidity set point
	Particle.function("TempSetPoint", temperatureSetpoint); // set temperature set point

	Particle.function("SensorPublish", setPublishFrequency);  // set senor publish frequency (seconds)
	Particle.function("WarningPublish", setWarningFrequency); // set warning publish frequency (seconds)

	//! initialize Modbus communication baud rate and TX pin
	node.begin(BAUD);	  // set baud rate for modbus
	// node.enableTXpin(RT); // TX enable pin of RS485 driver
}

/********************************************************************/
//! /brief main code loop, runs continuously
void loop()
{
	static unsigned long lastCheck = 0;
	static unsigned long lastSend = 0;
	static unsigned long lastWarning = 0;

	if (millis() - lastCheck >= checkInterval.count())
	{
		lastCheck = millis();

		//! get current temp and humidity and check for modbus comm error
		if (getSensorValues() == SUCCESS) // get current temp and humidity
		{
			//! check for whether temp and humdity is outside of window
			if ((curTemp < (tempSet - tempWindow)) || (curTemp > (tempSet + tempWindow)) || (curHum < (humSet - humWindow)) || (curHum > (humSet + humWindow)))
			{
				if (millis() - lastWarning >= (unsigned long)(warFreq * 1000))
				{
					lastWarning = millis();

					//! publish warning message with sensor data
					publishWarningMessage(); // send warning message
				}
			}

			//! temp/humidity within window
			else
			{
				//! publish data at set interval
				if (millis() - lastSend >= (unsigned long)(senFreq * 1000))
				{
					lastSend = millis();

					//! publish sensor data
					publishSensorValues(); // send temp and humidity
				}
			}
		}

		//! modbus error occurred
		else
		{
			//! publish data at set interval (same as warning interval)
			if (millis() - lastWarning >= (unsigned long)(warFreq * 1000))
			{
				lastWarning = millis();
				publishWarningMessage(result); // send warning message
			}
		}
	}
}

/********************************************************************/
//! /brief function for reading temp and humidity
int getSensorValues()
{
	//! local variables
	uint16_t data[3]; // create a 2 element array of 16 bit ints
	double tempC;	  // variable for temp in celcius

	// readHoldingRegisters and readInputRegisters take two parameters:
	// - the register to start reading from
	// - the number of registers to read (1, 2, ...)

	// Some sensors have the temperature and humidity in holding register 0 and 1. If so, use this version:
	result = node.readInputRegisters(0x0004,3);

	// Some sensors have the temperature and humidity in input registers 1 and 2. If so, use this version:
	// result = node.readInputRegisters(1, 2);

	// If you get Modbus Read Error 0x02 (ku8MBIllegalDataAddress), you probably have the wrong register
	// or input/holding selection for your sensor.

	//! read was successful
	if (result == node.ku8MBSuccess)
	{
		//! parse response buffer
		for (uint8_t i = 0; i < 3; i++)
		{
			data[i] = node.getResponseBuffer(i);
		}
		curHum = data[0] / 10;		  // humidity received divide by 10
		tempC = data[2] / 10;		  // temp received divide by 10
		curTemp = data[1] / 10;		  // temp received divide by 10

		// curHum = data[0] / 10;		  // humidity received divide by 10
		// tempC = data[1] / 10;		  // temp received divide by 10
		// curTemp = (tempC * 1.8) + 32; // convert celsuis to fahrenheit
    Log.info("value1=%1f (C),testStr=%1f (C)", curHum, curTemp);

		// debug serial messages
		Log.trace("Hum=%.1f (%% RH), Temp=%.1f (C) =%.1f (F)", curHum, tempC, curTemp);

		return SUCCESS; // return success code
	}

	//! communication failure occured
	else
	{
		// debug serial messages
		Log.info("Modbus Read Error 0x%02x", result);

		return FAIL; // return fail code
	}
}

/********************************************************************/
//! /brief function for changing humidity window
//! @param value is a string used for changing the window, value should be a whole integer or null. Example value = 5
int setHumidityWindow(String value)
{
	//! null case, return current value
	if (value == NULL)
	{
		return humWindow; // return current value
	}

	//! update to new value passed and return new value
	else
	{
		humWindow = value.toInt(); // set new volue to global variable
		Log.info("humWindow=%d", humWindow);
		return humWindow; // return new value
	}
}

/********************************************************************/
//! /brief function for changing temperature window
//! @param value is a string used for changing the window, value should be a whole integer or null. Example value = 5
int setTemperatureWindow(String value)
{
	//! null case, return current value
	if (value == NULL)
	{
		return tempWindow; // return current value
	}

	//! update to new value passed and return new value
	else
	{
		tempWindow = value.toInt(); // set new volue to global variable
		Log.info("tempWindow=%d", tempWindow);
		return tempWindow; // return new value
	}
}

/********************************************************************/
//! /brief function for changing temperature set point
//! @param value is a string used for changing the set point, value should be a whole integer or null. Example value = 75
int temperatureSetpoint(String value)
{
	//! null case, return current value
	if (value == NULL)
	{
		return tempSet; // return current value
	}

	//! update to new value passed and return new value
	else
	{
		tempSet = value.toInt(); // set new volue to global variable
		Log.info("tempSet=%d", tempSet);
		return tempSet; // return new value
	}
}

/********************************************************************/
//! /brief function for changing humidity set point
//! @param value is a string used for changing the set point, value should be a whole integer or null. Example value = 45
int humiditySetpoint(String value)
{
	//! null case, return current value
	if (value == NULL)
	{
		return humSet; // return current value
	}

	//! update to new value passed and return new value
	else
	{
		humSet = value.toInt(); // set new volue to global variable
		Log.info("humSet=%d", humSet);
		return humSet; // return new value
	}
}

/********************************************************************/
//! /brief function for changing the sensor publish frequency in seconds
//! @param value is a string used for changing the frequency, value should be a whole integer or null. Example value = 60
int setPublishFrequency(String value)
{
	//! null case, return current value
	if (value == NULL)
	{
		return senFreq; // return current value
	}

	//! update to new value passed and return new value
	else
	{
		senFreq = value.toInt(); // set new volue to global variable
		Log.info("senFreq=%d", senFreq);
		return senFreq; // return new value
	}
}

/********************************************************************/
//! /brief function for changing the warning publish frequency
//! @param value is a string used for changing the frequency, value should be a whole integer or null. Example value = 20
int setWarningFrequency(String value)
{
	//! null case, return current value
	if (value == NULL)
	{
		return warFreq; // return current value
	}

	//! update to new value passed and return new value
	else
	{
		warFreq = value.toInt(); // set new volue to global variable
		Log.info("warFreq=%d", warFreq);
		return warFreq; // return new value
	}
}

/********************************************************************/
//! /brief function to publish the sensor state to cloud in JSON
void publishSensorValues()
{
	//! create JSON buffer and write values to it
	JsonWriterStatic<256> jw; // creates a 256 byte buffer to write JSON to
	{
		JsonWriterAutoObject obj(&jw);									  // creates an object to pass JSON
		jw.insertKeyValue("Temperature", curTemp);						  // set field for temperature
		jw.insertKeyValue("Humidity", curHum);							  // set field for humidity
		jw.insertKeyValue("Time", Time.format(TIME_FORMAT_ISO8601_FULL)); // set field for time stamp
	}

	Log.info("%s %s", sensorEventName, jw.getBuffer());

	//! send publish only if cloud is connected
	if (Particle.connected() == TRUE)
	{
		Particle.publish(sensorEventName, jw.getBuffer());
	}
}

/********************************************************************/
//! /brief function to publish the sensor state to cloud in JSON
void publishWarningMessage(int errorCode)
{
	//! create JSON buffer and write values to it
	JsonWriterStatic<256> jw; // creates a 256 byte buffer to write JSON to
	{
		JsonWriterAutoObject obj(&jw);

		if (errorCode == 0)
		{
			jw.insertKeyValue("Warning", "Out of Range"); // set field for warning message
			jw.insertKeyValue("Temperature", curTemp);	  // set field for temperature
			jw.insertKeyValue("Humidity", curHum);		  // set field for humidity
		}
		else
		{
			jw.insertKeyValue("Warning", "Modbus Read Error"); // set field for warning message
			jw.insertKeyValue("ErrorCode", errorCode);
		}
		jw.insertKeyValue("Time", Time.format(TIME_FORMAT_ISO8601_FULL)); // set field for time stamp
	}

	Log.info("%s %s", errorEventName, jw.getBuffer());

	//! send publish only if cloud is connected
	if (Particle.connected() == TRUE)
	{
		Particle.publish(errorEventName, jw.getBuffer());
	}
}

it is giving the e2 error.

Error 0xE2 is ku8MBResponseTimedOut.

Which RS485 adapter do you have? How is it connected? Does it have auto-direction sensing (the code above assumes auto-sense).

Are you sure the following are correct:

  • Baud rate
  • Settings (usually 8N1, but could be something else)
  • Slave ID (the code above uses 2, but often it's 1)

And is it the same temperature and humidity sensor as the code above? Any other sensor may be using different registers.

yes @rickkas7 thank for the swift response we are using a modbus slave device using rtu communication which has a slave id 2 and baud rate 9600 , we are not using any rs485 adapter but using monitor one. already has a RS485 integrated and also tried another slave device just modbus code given below using modbusmaster particle it is returning like.
no its like same humidity temperature but i changed registers according to that.

/*
 * Project myProject
 * Author: Your Name
 * Date: 
 * For comprehensive documentation and examples, please visit:
 * https://docs.particle.io/firmware/best-practices/firmware-template/
 */

// Include Particle Device OS APIs
#include "Particle.h"

// Test code
SYSTEM_MODE(SEMI_AUTOMATIC);

#include "ModbusMaster-Particle.h"

SerialLogHandler logHandler(9600, LOG_LEVEL_WARN, {
    {"app", LOG_LEVEL_TRACE},
    {"system", LOG_LEVEL_INFO}
});

ModbusMaster slave;

void preTransmission() {
    // set interface to TX
}

void postTransmission() {
    // set interface to RX
}

void idle() {
    delay(100); // in case slave only replies after 10ms
    Particle.process(); // avoids letting the connection close if open
}

void setup() {
    slave.begin(7, Serial1); // slaveID=7, serial=Serial1
    slave.setSpeed(9600, SERIAL_8N1);
    slave.enableDebug(); // to catch the logs
    slave.preTransmission(preTransmission);
    slave.postTransmission(postTransmission);
    slave.idle(idle);
}

void loop() {
    uint8_t result = slave.readInputRegisters(0, 2);

    if (!result) {
        // Read the register values and combine them into a single 32-bit integer
        uint32_t rawValue = (slave.getResponseBuffer(0) << 16) | slave.getResponseBuffer(1);
        
        // Convert to float (assuming it's in IEEE 754 format)
        float voltage = *(float*)&rawValue;

        // Print the float voltage
        Log.info("Received voltage: %f", voltage);
    } else {
        Log.warn("Read error");
    }
    // do nothing :P
}

from com and putty we are reading the data. it is showing in the terminal as the following

It looks like you're not setting the direction pin.

The pre and post transmission should set the pin:

// preTransmission
digitalWrite(MONITOREDGE_IOEX_RS485_DE_PIN, 1);
// postTransmission
digitalWrite(MONITOREDGE_IOEX_RS485_DE_PIN, 0);

In setup you'll also need to set it as an output:

pinMode(MONITOREDGE_IOEX_RS485_DE_PIN, OUTPUT);

You should be basing your software off Monitor Edge, not off an empty application, but if you are using an empty app, MONITOREDGE_IOEX_RS485_DE_PIN is D4.

thanks a lot @rickkas7 for your incredible help. Now its working.

A post was split to a new topic: Modbus with Tracker SoM

A post was merged into an existing topic: Modbus with Tracker SoM

A post was split to a new topic: Monitor One with Wi-Fi