Dividing For loops with payloads

Hello guys,

Firstly thank you for taking time in reading this post!

I want to create a modular network of arduino’s with sensors based on the I2C principle.
I’ve connected multiple arduino’s which communicate with each other via the I2C wires.

“Slave” arduino’s gather the sensor information which will be put on the I2C line to the “Master” arduino.
The problem is the serial output from the “Master” which i will process in Labview. Labview only sees the last info on the serial line.

Therefore i want the code to be put on one single line. this is easy but cant be handeled easy by labview because the data isnt divided.

I want the code to divide the payload with a divider something like “;”
A start and stop bit added “<” cq “>”
It would also be a big help if the payload is marked somehow. the first digit being slave, data1 and data2.

So the master node would be spitting on the serial line something like: <SL2,HUM57,TMP19>

If this can be done, I can connect more arduino’s spitting more data throug the master to Labview :slight_smile:

Can you please help me? Thanks in advance!

//************************************************************************************
SERIAL OUTPUT from master: 

MASTER READER NODE
Maximum Slave Nodes: 6
Payload size: 3
***********************
2
57
19
2
57
19
etc..
//************************************************************************************
//MASTER READER NODE

#include <Wire.h>
#define PAYLOAD_SIZE 2 // how many bytes to expect from each I2C slave node
#define NODE_MAX 6 // maximum number of slave nodes (I2C addresses) to probe
#define START_NODE 2 // The starting I2C address of slave nodes
#define NODE_READ_DELAY 1000 // Some delay between I2C node reads

int nodePayload[PAYLOAD_SIZE];

void setup()
{
  Serial.begin(9600);  
  Serial.println("MASTER READER NODE");
  Serial.print("Maximum Slave Nodes: ");
  Serial.println(NODE_MAX);
  Serial.print("Payload size: ");
  Serial.println(PAYLOAD_SIZE);
  Serial.println("***********************");
  
  Wire.begin();        // Activate I2C link
}

void loop()
{
  for (int nodeAddress = START_NODE; nodeAddress <= NODE_MAX; nodeAddress++) { // we are starting from Node address 2
    Wire.requestFrom(nodeAddress, PAYLOAD_SIZE);    // request data from node#
    if(Wire.available() == PAYLOAD_SIZE) {  // if data size is avaliable from nodes
      for (int i = 0; i < PAYLOAD_SIZE; i++) nodePayload[i] = Wire.read();  // get nodes data
      for (int j = 0; j < PAYLOAD_SIZE; j++) Serial.println(nodePayload[j]);   // print nodes data   
      Serial.println("*************************");      
      }
    }
    delay(NODE_READ_DELAY);
}
//SLAVE SENDER NODE


#include <Wire.h>
#include "DHT.h"
#define DHTPIN 2     // what pin we're connected to
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);
#define NODE_ADDRESS 2  // Change this unique address for each I2C slave node
#define PAYLOAD_SIZE 3 // Number of bytes  expected to be received by the master I2C node

byte nodePayload[PAYLOAD_SIZE];

void setup()
{

  Serial.begin(9600);  
  Serial.println("SLAVE SENDER NODE");
  Serial.print("Node address: ");
  Serial.println(NODE_ADDRESS);
  Serial.print("Payload size: ");
  Serial.println(PAYLOAD_SIZE);
  Serial.println("***********************");

  Wire.begin(NODE_ADDRESS);  // Activate I2C network
  Wire.onRequest(requestEvent); // Request attention of master node
  
  dht.begin();
}

void loop()
{ 
  delay(100);
  int h = dht.readHumidity();
  int t = dht.readTemperature();
  
  nodePayload[0] = NODE_ADDRESS; // I am sending Node address back.  Replace with any other data 
  nodePayload[1] = h; // Read A0 and fit into 1 byte. Replace this line with your sensor value
  nodePayload[2] = t;

}

void requestEvent()
{
  Wire.write(nodePayload,PAYLOAD_SIZE);  
  Serial.print("Hum:  ");  // for debugging purposes. 
  Serial.println(nodePayload[1]); // for debugging purposes. 
  Serial.print("Temp: "); 
  Serial.println(nodePayload[2]); // for debugging purposes.
  Serial.println(""); 
}

So the master node would be spitting on the serial line something like: <SL2,HUM57,TMP19>

You have complete control of the timing, layout and contents of the serial output from the Arduino so what is the problem ? Collect the data from the slaves and at the appropriate time output the formatted serial message to Labview.

Stop spitting out your serial! Bad boy!

UKHeliBob:
You have complete control of the timing, layout and contents of the serial output from the Arduino so what is the problem ? Collect the data from the slaves and at the appropriate time output the formatted serial message to Labview.

The problem is when the for loop runs, it will give all data at once.

As the program runs now, i put in a serial.print(",") after or between the loop it will give all payload data and then a divider.

I cant figure out how or where to intterupt the run and put in the divider.

And aarg, i think you mean the breakfast thing :wink:

The problem is when the for loop runs, it will give all data at once.

Only if you let it. Can you describe in more detail what you want the program to do ?
Your example was <SL2,HUM57,TMP19> which I interpret as slave 1, humidity 57, temperature 19. What if it output <SL1,HUM52,TMP18&SL2,HUM57,TMP19&SL3,HUM58,TMP22&SL4,HUM54,TMP16> for instance. Could that be parsed in Labview ?

Hi,

I did a similar thing with 13 Dallas thermometers on one board (as well as some other functions), and I2C the results to the other board. The Thermometers are read continuously, so a wagonload of data on the sender side.

Created a simplified protocol with 1 byte of data each, gave the thermometers an id bigger than the expected value of the thermometers (id’s 111 - 123). I split the float result of the therm onto two bytes (before and after the decimal comma), and send all of this in a train of 3 bytes. This train goes on forever for all thermometers, with a slight delay to make sure I don’t overflow the serial buffer. So the receiver side reads faster than the sender side sends…

On the reader side check for these to come in, which triggers the beginning of the data train for (say) Therm nr 111 with temp 20 45 (which translates into 20,45 celcius). so the total data train looks like 111 20 45 112 30 35 113 29 11 etc.

If there’s a send or read error on the Therm nr then the reader flushes and waits for a byte 111 <= x <= 123 waits for the next pass of the same thermometer, which takes a couple of seconds. Same with abnormal fluctuations in the temperature.

Hope this helps,
Ton
/* would have added the code if I knew how to do it in a neat way */

UKHeliBob:
Only if you let it. Can you describe in more detail what you want the program to do ?
Your example was <SL2,HUM57,TMP19> which I interpret as slave 1, humidity 57, temperature 19. What if it output <SL1,HUM52,TMP18&SL2,HUM57,TMP19&SL3,HUM58,TMP22&SL4,HUM54,TMP16> for instance. Could that be parsed in Labview ?

Due to the for loop it will read slave 1, put all his data at once on the line and give a request to the next slave and do the same.

That string is exactly what i was aiming for.

I think it would be better if all slave senders were given a single string. something like; <SL1,HUM52,TMP1> <SL2,HUM57,TMP19> <SL3,HUM58,TMP22> <SL4,HUM54,TMP16>
But i think i can manage to make Labview working with your output. How can i achieve that output?

TonVeenhof:
Hi,

I did a similar thing with 13 Dallas thermometers on one board (as well as some other functions), and I2C the results to the other board. The Thermometers are read continuously, so a wagonload of data on the sender side.

Created a simplified protocol with 1 byte of data each, gave the thermometers an id bigger than the expected value of the thermometers (id’s 111 - 123). I split the float result of the therm onto two bytes (before and after the decimal comma), and send all of this in a train of 3 bytes. This train goes on forever for all thermometers, with a slight delay to make sure I don’t overflow the serial buffer. So the receiver side reads faster than the sender side sends…

On the reader side check for these to come in, which triggers the beginning of the data train for (say) Therm nr 111 with temp 20 45 (which translates into 20,45 celcius). so the total data train looks like 111 20 45 112 30 35 113 29 11 etc.

If there’s a send or read error on the Therm nr then the reader flushes and waits for a byte 111 <= x <= 123 waits for the next pass of the same thermometer, which takes a couple of seconds. Same with abnormal fluctuations in the temperature.

Hope this helps,
Ton
/* would have added the code if I knew how to do it in a neat way */

Ton, Thats a very good idea. Make a “date” on all the data the i will give to the master.
Got only one question, how to deal with when a ID gets the number “999”?

Than the output will be something like this;

<SL1,ID111,HUM52,TMP1> <SL2,ID111,HUM57,TMP19> <SL3,ID111,HUM58,TMP22>
<SL1,ID112,HUM51,TMP1> <SL2,ID112,HUM56,TMP20> <SL3,ID112,HUM57,TMP21>
2 outputs from 3 slaves on different times.

@Ton, for posting code, use this: [code@][/code@] (without the “@”)

From your question I take that the number of slaves and sensors is not fixed, and that it may be larger than 999? In my case the numbers are fixed (1 slave, 13 sensors), so I have no such problems.

If the number is not fixed then I think you need to extend the protocol to be bidirectional and let the master pass ID's to slaves. In that way the master defines which sensor on which slave gets which ID, and both master and slaves are in sync.

Ton

In my case I want to create a modular system. Lets say, 1 master, 4 slave + sensors. But if i’m to upgrade, i only want to plug in an extra slave board + sensors on the IC2 wire.

So number of slaves is not fixed. The actual sensors fitted on the slave board is fixed. Maybe an extend to the program to let the master broadcast a signal over the wire, waiting for answers from slaves and give them an “IP/ID adress”. I think this is something out of my league for now.

I created the so called serial data train. I interpeted your ID as timestamps. As for my data train all slaves are identified with a number. After that a timestamp e.g. 111/112/113. Then the sensor data.
I cant figure out how to place the sensor ID e.g. SL/HUM/TMP before the actual data.
I believe it is critical to let the slave place this info on the wire, because multiple slaves give multiple data with different sensors. Lets say slave 2 Always gives a humidity and a temperature and slave 3 gives position data, where slave 4 gives tank volume or used current.

Much info and ideas :slight_smile: The question here is: How do i let the slave place the sensor name in front of the actual sensor data?

The code output I now managed to write:

MASTER READER NODE
Maximum Slave Nodes: 6
Payload size: 4
***********************
<2,7,62,18,><2,8,62,18,><2,9,62,18,><2,10,62,18,>

<   :start
2   :slave no
7   :timestamp
62  :humidity
18  :degrees celcius
>   :stop

How do i let the slave place the sensor name in front of the actual sensor data?

That seems trivial. What have you tried, and what was the result?

Google 'LALR(1) parser'. The thing I see a lot with people attempting to read serial input is they cant cope with the data coming in in chunks. It's all

loop() {
  while(available)  {
    read a byte
  }
  deal with the message
}

which doesn't work when a message comes across in bits, or when a single bit has two messages.

or when a single bit has two messages.

That would be a real challenge, wouldn't it. How do you do that?