Question about the code and how to improve it

Hi,

I have been working on project in which i am using 2 sensors, one gives an analog signal an the other is using Modbus communication protocol. Everything works fine, but i need to have exactly 3ms (0,003s) to 4ms between each reading.
Is there a way to do sth like that?

I am using Arduino Mega for this project.

I tried to include prescalers, but no joy.

#include <ModbusMaster.h>


#define MAX485_DE      3

// instantiate ModbusMaster object
ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_DE, 0);
}

void setup(){

  pinMode(MAX485_DE, OUTPUT);


  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(115200);

  Serial1.begin(115200);

  // Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);

}

void loop()
{
  long result;
    
  result = node.readHoldingRegisters(0x0000, 1);

  {

    if (node.getResponseBuffer(0) < 5000)
    {
      Serial.print(node.getResponseBuffer(0)/1.00);
    }
    else
    {
      Serial.print((node.getResponseBuffer(0)/1.00)-65535);
    }
  }

  Serial.print(",");
  Serial.print((analogRead(A0)*5.00)/1024,3);
  Serial.print(",");
  Serial.print(millis());
  Serial.println();

}

Thanks in advance!

You can use millis. Take a look at the blink without delay example in the IDE to see how.

I went through that example, but i have no idea on how to use that in my example so please help.
Exact time spaces between readings are crucial to me, right now it is jumping from 4msc to 6msc and i need to correct that.
Any help will be greatly appreciated.

Grab the code from loop in the BWOD example.

Paste it into your loop.

Adjust the interval to be 3mS.

Replace the section that works the LED with everything you had in loop.

Thanks a lot!

I did that and it seems that intervals below 6msc aren’t taken into accout, no idea why, but up interval equal to 5 mscs nothing changes.
When i set the interval to 7 or 8 mscs, time spaces do change, but they are not stable. For example if interval is equal to 8mscs, time ranges from 7 mscs to 9mscs.

I tried also with using microseconds but with the same results.

Here is the code that is working.

#include <ModbusMaster.h>

#define MAX485_DE      3

unsigned long previousMillis = 0;
const long interval = 8.000;

// instantiate ModbusMaster object
ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_DE, 0);
}

void setup(){

  pinMode(MAX485_DE, OUTPUT);

  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(115200);

  Serial1.begin(115200);

  // Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);

}

void loop()
{

 unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    
    previousMillis = currentMillis;
  
  long result;
    
  result = node.readHoldingRegisters(0x0000, 1);

  {

    if (node.getResponseBuffer(0) < 5000)
    {
      Serial.print(node.getResponseBuffer(0)/1.00);
    }
    else
    {
      Serial.print((node.getResponseBuffer(0)/1.00)-65535);
    }
  }

  Serial.print(",");
  Serial.print((analogRead(A0)*5.00)/1024,2);
  Serial.print(",");
  Serial.println(millis());
    
 }
}

Unfortunately more help is needed :slight_smile:

Sadly the Modbus stuff is a mystery to me. What's sending it? How frequently?

Well, I am not an expert either.

As far as i know it is sending a frame of bits which contain data. It works like a CAN bus, so there are registers which gather data that can be obtained.
It is based on Master and Slave communication. To get the information from the slave, firstly i need to ask for that and then start listening to the message that is coming back.

Longer time period isn't a huge deal for me as long as it is exact. So it can be 4msc, 5 msc, 6msc etc., but it has to be exactly that number.

I wonder if you need to check on the message arrival. It may be that it takes a little while to arrive. As you have it, you send the request and immediately try to read the result. Maybe that's ok, but I'd have to dig into the library code to see.

I also suspect that if you ask too frequently it may cause a problem, which may be why it didn't work with the shorter intervals.

Apparently the callback postTransmission gets called at the end of the data (hence the name :slight_smile: ). You might try printing millis in there as a test.

Printing millis into new location didn't help as well.

I wonder, if the millis are sort of working with 8msc interval, why this interval is unstable (ranges from 7 msc to 9msc)?
I gave a try with 15msc interval but the same thing happens. Every now and then time period jumps to 13 msc or so and back to 15msc. Why isn't it exactly 15 msc, (within few microseconds)?. Because right now it seems that it is 15msc within couple hundred microseconds,?

Is there a way to make it exactly 8msc or any other value (within few microseconds)?

How long does it take for a message to arrive when you request it? Is it consistent?

I am sorry but i have no idea on how to check that, i am total newbie to arduino and programming ;/.

For timing sensitive project I always use "while loop" inside the "loop" procedure. Too much overhead for the bootloader to to exit and to re-enter the "loop" again.
Such as:

void loop() // 2 instruction clocks to call, and 2 instruction clocks to to return; plus others . . . .
{
while (1)
     {
    // your code . . . . .
    // . . . . . . . . . .
     } // it takes only about 3->4 instruction clocks to loop back.
}

Happy Mosquito Free!
Phi

Print a message with the time in millis before you do the request. Have postTransmission do the same thing (with different text obviously).

Hopefully i did it correctly:

    Serial.print(millis());
    
  result = node.readHoldingRegisters(0x0000, 1);
  
  {

    if (node.getResponseBuffer(0) < 5000)
    {
      Serial.print(node.getResponseBuffer(0)/1.00);
    }
    else
    {
      Serial.print((node.getResponseBuffer(0)/1.00)-65535);
    }
  }

  //Serial.print(",");
  //Serial.print((analogRead(A0)*5.00)/1024,2);
  Serial.print(",");
  Serial.println(millis());

If the time printing is done correctly, time difference between sending the request and getting the data takes 4 to 6msc.

Then it sounds like the source of the variability is at the other device. Since it's a small number of milliseconds difference, maybe do the same test with micros instead.

I think what you will need to do is get the data every 8000 (or whatever) micros. When it comes time to get it again, only then do you print the reading. That should eliminate the variation if indeed it comes from the remote device.

Use delay micros instead of delay millis.

Write code so that there is a measurement taken of how long it takes to process the code. Subtract the time of desired delay 4000uS from time taken to execute the code. Brings you closer to hitting the desired time of code execution.

Thanks a lot for the ideas.!!

I'll try to implement them tomorrow so please visit this post in case i'll need some help.
I'll be grateful for that !

I haven’t done too much improvements on my code, but i know that Serial printing of the whole package takes no more than 1msc, so i wonder if there is a way to make code like this?

At the start of every loop, time is recorded and once the modbus communication is done, wait until difference between start of the loop and millis is equal to 7msc, and then print the Serial.

I am total newbie so please be forgiving :slight_smile:

VOID LOOP ()

START //RECORDS THE TIME AT THE START OF THE LOOP

{
          
  result = node.readHoldingRegisters(0x0000, 1);
  data = node.getResponseBuffer(0);

WAIT UNTIL
{
MILLIS() - START = 7.00MSC

THEN

  if (data< 5000)
    {
      Serial.print(data);
      Serial.print(",");
      Serial.print((analogRead(A0)*5.00)/1024,2);
      Serial.print(",");
      Serial.println(millis());
    }
    else
    {
      Serial.print(data-65535);
      Serial.print(",");
      Serial.print((analogRead(A0)*5.00)/1024,2);
      Serial.print(",");
      Serial.println(millis());
    }

What I would do is write a function that gets the data. Call it from setup. Call it in loop every 8000 micros. When it’s time to call it, send the serial data and then get the data again.

The point is to eliminate the suspected inconsistency in timing when you get data.

I get that but i have no idea on how to make it happen. Could you give me a hand?

I know i am asking a lot, but on my own will be struggling with this for few weeks… ;/

#include <ModbusMaster.h>

#define MAX485_DE      3
long result;
long data;

// instantiate ModbusMaster object
ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_DE, 0);
}

void setup(){

  pinMode(MAX485_DE, OUTPUT);

  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(115200);

  Serial1.begin(115200);

  // Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
 
}

void loop()
{

   result = node.readHoldingRegisters(0x0000, 1);
  data = node.getResponseBuffer(0);

     if (data< 5000)
    {
      Serial.print(data);
      Serial.print(",");
      Serial.print((analogRead(A0)*5.00)/1024,2);
      Serial.print(",");
      Serial.println(millis());
    }
    else
    {
      Serial.print(data-65535);
      Serial.print(",");
      Serial.print((analogRead(A0)*5.00)/1024,2);
      Serial.print(",");
      Serial.println(millis());
    }

    }