Sending and recieving data via UART

Hi,

I'm using a TF40 UART sensor with an arduino UNO. As the name suggest it uses UART as communication protocol. But i can't figure out how to use it. I would like to know how to send data and recieve data in a more general way.

The program should work as followed:

  1. Define commando's
  2. Set baudrate
  3. Give a test reading using Trig_single during the void setup.
  4. Use Trig_successive to write 5 measurements each second to the serial monitor.

This is what i have right now:

#include <SoftwareSerial.h>

// Define TF40 UART pins
#define TF40_RX_PIN 0 // Connect TF40 UART RX pin to Arduino TX pin
#define TF40_TX_PIN 1 // Connect TF40 UART TX pin to Arduino RX pin

// Create a SoftwareSerial object for TF40 UART communication
SoftwareSerial tf40Serial(TF40_RX_PIN, TF40_TX_PIN);

// Command frame format:
// 1byte: Adress code; 1byte: Function code; 2Bytes: Starting address; 
// 2Bytes: Number of registers (N); 2Bytes: CRC

// Data frame format:
// 1byte: Adress code; 1byte: Function code; 1Byte: Byte count; 
// 2Bytes: Register value; 2Bytes: CRC

byte Baud_rate[13] = {0x01, 0x10, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x01, 0xC2, 0x00, 0xF3, 0x0F};// Controle baud rate
byte Open_laser[11] = {0x01, 0x10, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x01, 0x67, 0xA3}; // Open Beam
byte Close_laser[11] = {0x01, 0x10, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x00, 0xA6, 0x63}; // Close Laser
byte Trig_single[8] = {0x01, 0x03, 0x00, 0x0F, 0x00, 0x02, 0xF4, 0x08}; // Measure once
byte Trig_successive[8] = {0x01, 0x03, 0x00, 0x01, 0x00, 0x02, 0x95, 0xCB}; // Successive measurment (5Hz)
byte read_measure_data[8] = {0x01, 0x03, 0x00, 0x0F, 0x00, 0x02, 0xF4, 0x08}; //Read measuring distance (don't know what to do with this)

void setup() {

  // Baudrate setup
  // Serial -> Communicating to PC Serial Monitor
  Serial.begin(115200); // Tells Arduino the baudrate
  tf40Serial.write(Baud_rate,13); // Sends baudrate to TF40 UART
  delay(1000);
  Serial.println("==========");
  Serial.println("Setup: Baudrate send.");

  // Controle measurment
  Serial.println("Setup: Controle measurement...");
  tf40Serial.write(Close_laser,11);
  delay(200);
  tf40Serial.write(Trig_single,8);
  readMeasurement();
  Serial.println("Setup: Controle measurement completed");
  delay(2000);
  Serial.println("Setup: Successive measurement started...");

}

void loop(){
  //Serial.write(Open_laser,11);
  //Serial.println("Laser opened");
  
  for (int i = 0; i < 5; i++) {
    tf40Serial.write(Trig_successive,8);
    delay(200); // Wait for measurement interval (5Hz) (Is this needed? since the device knows it has to do 5 reading in 1 second)
    readMeasurement();
  }

}

// This part i got from ChatGPT ¯\_(ツ)_/¯
void readMeasurement() {
  byte buffer[4];
  tf40Serial.readBytes(buffer, sizeof(buffer));

  // Convert received data to millimeters
  uint32_t distance = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];

  // Print measured distance
  Serial.print("Measured distance: ");
  Serial.print(distance);
  Serial.println(" mm");
}

At the moment this code gives an error that i use to many bytes and leave to few bytes for local variables, how do i reduce this? There are more functions given by the manufactor if i add these will it not decrease the amount of bytes for local variables even more? I have no idea if the decoding part is done right since i got that from chatGPT.

I hope this all made sense, i'm new to arduino so this sketch is probably full of inefficiency and errors. All help is appreciated, thank you in advance.

Datasheet TF40 UART

Welcome to the forum

// Define TF40 UART pins
#define TF40_RX_PIN 0 // Connect TF40 UART RX pin to Arduino TX pin
#define TF40_TX_PIN 1 // Connect TF40 UART TX pin to Arduino RX pin

// Create a SoftwareSerial object for TF40 UART communication
SoftwareSerial tf40Serial(TF40_RX_PIN, TF40_TX_PIN);

Why are you using the Hardware Serial pins with SoftwareSerial ?

It compiles for me

Because i thought i had to create something to send the intructions to. I have seen other people do kind of the samething but not defining pins (also not using SoftwareSerial), but i dont know how this would work, how does the arduino know which pin to send and recieve from?

If i verify the code it doesnt give an error but when i upload it does give the error.

The datasheet says that the sensor has a baud rate of 38400. I'm pretty sure that's beyond the capabilities of a software serial port.

The datasheet suggests that the baud rate can be changed. However, you would need a device capable of communicating with it at the default baud rate in order to change it.

If you can reduce the baud rate, then you can likely talk to it using an UNO.

Your device appears to be using messages that would be seen when using the Modbus message format but using TTL serial instead of RS485.

You might be able to use the ModbusMaster library to handle the comms - just ignore the mention of the RE and DE signals and the preTransmission() and postTransmission() functions.

Looks like ChatGPT didn't read the user manual. There ate more than 4 bytes in the response to a read distance command.

If you use the Serial interface then the Uno uses pins 0 and 1 and you can't change that. If you use SoftwareSerial to create a second serial interface then you specify the pins that it should use but they must not be pins 0 and 1.

As you will have seen from other replies there is also a limit to how fast a SoftwareSerial connection can be used at

It does say in the user manual:

Note: Measuring distance is four bytes long,
0x00 0x00 0xE0 0xA1 -> 0x0000E0A1 -> 57505mm

but i guess more info get send every time. Thanks for the tips i will look into ModbusMaster

So there is no need to define pins instead just use serial.write and serial.read

Yes, it's a bit misleading. The complete message returned has device address, function code, byte count, 4 bytes of actual measurement data and a 2 byte CRC16 checksum.

You want to avoid using the hardware serial port on an UNO for communicating with sensors. The hardware serial port is hard wired to the USB-serial interface that is used to upload code and allows you to debug your code with suitably placed print statements.

Pins 0 and 1 are used by the Uno to communicate with the Serial monitor and for uploading code.

If you attach anything else to pins 0 and 1 then you will not be able to print or write to the Serial monitor or read from it, and it is very likely that you will also not be able to upload code unless you unplug whatever is connected to pins 0 and 1

If these problems dont affect your project then you can use the Serial interface on pins 0 and 1 to communicate with an external device. Note, however, that you will lose the ability to use the Serial monitor for debugging or to report on what is happening within the sketch when running normally

I use an Arduino Micro (Mega 32U4 processor) for a similar project, uses builtin USB for PC comms and leaves Serial1 free for UART comms.

Turns out SerialSoftware can do the job. The code which works for me is:

#include <SoftwareSerial.h>

// Define TF40 address
#define TF40_ADDRESS 0x01

// Define Modbus function codes
#define READ_HOLDING_REGISTERS 0x03

// Define SoftwareSerial object for communication with sensor
SoftwareSerial mySerial(10, 11); // RX, TX

// Define command arrays
byte read_measure_data[8] = {0x01, 0x03, 0x00, 0x0F, 0x00, 0x02, 0xF4, 0x08};    // Read measuring distance
byte Trig_single[8] = {0x01, 0x03, 0x00, 0x0F, 0x00, 0x02, 0xF4, 0x08};          // Measure once


void setup() {
  // Initialize serial communication for debugging
  Serial.begin(38400);
  
  // Initialize SoftwareSerial for Modbus communication
  mySerial.begin(38400);
}

void loop() {
  // Request measuring distance
  mySerial.write(Trig_single,sizeof(Trig_single));
  uint32_t distance = readMeasuringDistance();
  
  // Print measured distance
  Serial.print("Measured distance: ");
  Serial.print(distance);
  Serial.println(" mm");

  // Delay before next reading
  delay(1000); // Adjust as needed
}

uint32_t readMeasuringDistance() {
  uint32_t measuredDistance = 0;
  
  // Send request to sensor
  mySerial.write(read_measure_data,sizeof(read_measure_data));
  delay(10); // Wait for the sensor to respond
  
  // Read response from sensor
  while (mySerial.available() < 9); // Wait until all bytes are received
  byte slaveAddress = mySerial.read(); // Read slave address
  byte functionCode = mySerial.read(); // Read function code
  byte byteCount = mySerial.read(); // Read byte count
  byte highByte1 = mySerial.read(); // Read high byte of register 1
  byte lowByte1 = mySerial.read(); // Read low byte of register 1
  byte highByte2 = mySerial.read(); // Read high byte of register 2
  byte lowByte2 = mySerial.read(); // Read low byte of register 2
  byte crcHigh = mySerial.read(); // Read CRC high byte
  byte crcLow = mySerial.read(); // Read CRC low byte
  
  // Verify CRC (not implemented in this example)
  
  // Combine received bytes into 32-bit value (big-endian)
  measuredDistance |= (uint32_t)highByte1 << 24;
  measuredDistance |= (uint32_t)lowByte1 << 16;
  measuredDistance |= (uint32_t)highByte2 << 8;
  measuredDistance |= (uint32_t)lowByte2;

  return measuredDistance;
}

Thanks for the help.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.