3

I would like to read data from a rs232 device (SL-5868P Sound Level Meter).
protocol and more : https://sigrok.org/wiki/Colead_SL-5868P.

I already have a Python script to read the data with a TTL to USB converter (without Arduino). Now I also want to be able to read the meter via the Arduino.

I've bought a RS232 to TTL Converter (MAX3232). I get either very weird characters (baud rate set correctly), or not logical numbers. With the MAX chip I get no data at all.

  1. To see if there is data ready, 0x10 must be sent by the meter.
  2. To get the data, send 0x20.

Would someone be willing to help me out a bit? That would be really nice. Maybe there is someone who has experience with reading (and writing to) RS232. The only thing that matters is how I can retrieve the data. I know the rest what needs to be done with software (conversion, etc.). I'm not that familiar with Arduino's .

The code below is an example from SoftwareSerial (a little bit modified). I'm using the Arduino Uno.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(2400);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.println("Goodnight moon!");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(2400);
}

void loop() { // run over and over
  if (mySerial.read() == 0x10) {
    //mySerial.write(0x20);
    Serial.println("Data available");
    Serial.println(mySerial.read());
  }

  if (Serial.available()) {
    mySerial.write(Serial.read());
  }
  //delay(300);
}

And the current schematic (sorry for my drawing skills)

enter image description here

1
  • you only read one byte after the 0x10. is it ok? If you send numbers from Serial Monitor they are sent as text. I doubt the meter accepts "10\r\n" (0x30, 0x31, 0x0D and 0x0A) – Juraj May 31 at 18:17
3

You didn't show how you connected the MAX3232, so I cannot say anything about that. But I can write something about your code and how the data gets received on the PC.

  • On the PC: Here it is important how you are reading the serial data on the PC. The Serial Monitor for example expects ASCII encoded text. It cannot correctly display binary data. That is the reason, that you get weird characters with the correct baudrate. The data is correctly received, but it doesn't represent ASCII characters. It is just binary data and interpreting it as ASCII characters will not make any sense.

    Also it just sends ASCII encoded text, no binary data. So when you are sending "10" it will send the bytes 0x31 and 0x30, plus any line ending, that you have selected (\n aka 0x0A if you selected newline as line ending). As your device speaks in binary data you might wanna speak binary to it. So you either need to decode the ASCII data on the Arduino, before sending it to the device, or you need to use a different serial program, which can handle binary data.

  • Your code: First look at this line

      Serial.println(mySerial.read());
    

    Here you are reading exactly one byte from the SoftwareSerial interface and then you are printing it. Serial.read() or SoftwareSerial.read() only read one single byte from the buffer, but the data from your device spans over multiple bytes, as described on the wiki page, that you linked. Look at the table titled "Data structure". The returned data consists of 10 bytes in total (numbered from 0 to 9), where bytes 3 to 7 contain the actual data. The rest gives configuration and status data, a header as packet start and a checksum to check for transmission errors. So reading only one byte isn't enough you need to read all of them.

    Then you are Serial.print()ing the data. The print() function and its siblings are formatting the data human readable. That might help you here, but you need to make sure, that the resulting output is really readable for you, especially when you are reading multiple bytes (as you need to do, as explained above). Here you need to make a decision: Either

    • you could decode and format the binary data from the device on the Arduino and send the human readable format over Serial
    • or you could send the binary data straight to the PC and decode it there with a fitting program (either a Serial program, which can handle binary data, or your own program, for example with python or processing)

    If you go the first way, you should first read the different parts of the message and then print these parts with additional text, that explains, what data means what. Otherwise you would have just a long line of numbers, which are also rather difficult to read. Also you might wanna decide on a number base. I think only decimal or hexadecimal are really relevant here. For that you could use Serial.print(value, DEC) or Serial.print(value, HEX), which tells the Serial library to use the specified number base.

    Now about the actual data format: The data format is described with

    BCD-encoded value, one byte per digit 0x00-0x09. 0x0a means ignored digit. The last digit represents the decimal.

    on the linked wiki site. So while this is still binary data (not ASCII encoded), each byte represents a decimal digit of the actual value. You could decode it accordingly. So I guess a byte sequence of 0x01 0x00 0x02 0x09 0x07 would mean 1029.7. As you wrote, that you already know how to convert the data, I will not go deeper about this here.

    And lastly you need to rethink your program structure. The workflow with your device is the following:

      Arduino waits for device to send data ready byte (0x10)
      device sends 0x10
      Arduino sends 0x20 (command to send data)
      Arduino waits for device to send at least 10 bytes of data
      device sends a complete data packet with 10 bytes
      Arduino interprets the data in the received packet and acts accordingly
      repeat
    

    You need to follow this workflow and also anticipate, that the data transmission takes some time (especially at the really slow 2400 baud). For this a Finite State Machine (FSM) is a good fit (there are many extended descriptions about FSMs on this site, for example my answer to this question or this question, though there are many more, which you just need to search for). We just track at which part of the workflow we are currently and execute the according code. Something like this:

      enum ReceiveState {IDLE, RECEIVING};
      ReceiveState rec_state = IDLE;
    
      void loop(){
          if(mySerial.available()){
              switch(rec_state){
                  case IDLE:
                      if(mySerial.read() == 0x10){
                          // data is ready, command to send data
                          mySerial.write(0x20);
                          // change state to receiving
                          rec_state = RECEIVING;
                      }
                      break;
                  case RECEIVING:
                      if(mySerial.available() >= 10){// only read data, if a full packet was received, at least 10 bytes
                          // Here you need to decode the binary data and handle it accordingly (for example printing in the wanted format)
                          // Reset rec_state afterwards to be ready for the next time, that data is ready to read
                          rec_state = IDLE;
                      }
                      break;
                  }
              }
          }
      }
    

    That might seem like a bit of overkill, but the concept is very important in the Arduino world and it is very powerful. Also it makes it easier to accommodate workflow structures.

1
  • Thanks for the detailed explanation. This is going to help me. But I still have one problem. When I add debug printlines, it won't be printed. So, no data is coming in. I think it has to do with the MAX3232 chip. I've added a simple schematic in the main post. Maybe it isn't correct connected, or the wrong chip? Connect RS232 directly to Arduino is not a option if I understand correctly. – Joost Jun 1 at 7:30
2

Hi and welcome to the wonderful world of Arduino. It appears that you have picked-up Arduino with ease. Your program is almost there. Your wiring connection diagram looks to be correct.

When I am doing a project like this, I won't write my full application first. Instead I write a series of test programs to make sure each chunk is performing exactly as I want it to.

I would start by writing a program that just passes the serial information recieved from the Sound Level Meter and displays it on the serial monitor. Something similar to this:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
  // Open USB serial communications
  Serial.begin( 2400 );
  Serial.println("USB Serial is up and running");

  // set the data rate for the SoftwareSerial port
  mySerial.begin( 2400 );
}

void loop()
{ 
  if ( mySerial.available() )
  {
    // Write the serial from the S.L.M. to the USB (debug) serial port
    Serial.write( mySerial.read() );
  }

  if ( Serial.available() )
  {
    // Write the serial from the USB (debug) serial port to the S.L.M.
    mySerial.write( Serial.read() );
  }
}

This program should be enough to communicate with the Sound Level Meter. But this leads me to my first concern - choice of serial monitor.

While the Arduino built in serial monitor is adequate it is only really good for sending/recieving text. (Plus it sends a CR/LF every time you hit enter.) I can see straight away that you require to send/recieve hex numbers. Hex numbers won't display well on the serial monitor.

Instead I prefer to use a terminal program by "der-hammer" called hterm. (Screenshot here). It lets you view the input in ASCII, hex, bin and decimal. Likewise it lets you send ASCII, hex, bin and decimal or a mixture of each. When sending information, you can configure it so that it finishes the data with either a line feed, a carriage return, both or none.

By the sound of it, you don't want a CR or LF at the end of the data you send. So in the "Input Options" set the "Send on enter" to "None".

For the recieved data, you need to view that in hex so make sure the "hex" box is checked just above the recieved data. (Uncheck "ASCII".)

You could create a loop-back test by removing the Sound Level Meter and instead link the RS232 Rx to the RS232 Tx. This will allow you to see on hterm exactly what the Sound Level Meter sees when you send data.


Issues with your program

  if (mySerial.read() == 0x10) {
    //mySerial.write(0x20);
    Serial.println("Data available");
    Serial.println(mySerial.read());
  }

The section of code will only read one byte of data after it has detected 0x10 (See the Serial.Read() reference page). Anything more than that will get consumed by the next time the if statement checks the serial line for 0x10.

I notice in the line Serial.println(mySerial.read()); that you are reading the data in (which is in hex format) but then trying to write it out in ASCII by using the println() function. This will not give you the output you desire.

Also, after detecting 0x10 it reads the software serial line for one more byte - meaning if there is nothing following the 0x10 then the mySerial.Read() command will hang for 1s until it timeouts.

if (Serial.available()) {
  mySerial.write(Serial.read());
}

As others have pointed out, using the serial monitor will send this as ASCII characters (and include a CR/LF character(s)) instead of the hex value that you want. Kudos is given to you, that you chose mySerial.write() instead of mySerial.println().


My Take on Your Program

The follwoing code is untested.

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(2400);
  // The wait for serial port to connect is not needed for Arduino Uno
  Serial.println("USB Serial is up and running");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(2400);
}

void loop()
{
  if ( mySerial.available() )
  {
    int recievedByte = mySerial.read();
    Serial.write(recievedByte);    // Send to USB serial - assuming using hterm in hex
    if ( recievedByte == 0x10 )
    {
      mySerial.write(0x20);
    } 
  }

  // You could remove the following code - data must be in hex.
  if ( Serial.available() )
  {
    mySerial.write(Serial.read());
  }
}
3
  • Thank you so much for your effort to explain this all. But as said earlier in the other post, there is totally no data coming in. Maybe there is something wrong with the MAX3232. With debugging (add println's in if-statements), there is also no result. Only when I disconnected and connect the GND again I get data from the mySerial.available() if statement. – Joost Jun 2 at 7:28
  • Which GND are you referring to? – sa_leinad Jun 2 at 14:49
  • 1
    It's working for now. I've disconnected the MAX3232 chip and connected the RS232 device directly to the Arduino. I know, it's not a best practice, but it is low voltage. – Joost Jun 3 at 9:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.