Sending Hex over RS232

Hey All,
I have a bit of a project that I am working on with an ESP32 board. The gist of what I'm trying to achieve is to make a ultrasonic wind gauge bluetooth controllable by a PC running specialist software for field event scoring.

Currently I'm using a 30m cable and people don't know how to roll cables, which is killing me!

I have everything working in pieces but the second I try to put it together I run into issues. The wind gauge itself and the communications protocol are non-negotiable. They are worth $5k and need to be made to international specifications. The wind gauge expects commands in the following format:

<0x01><0x13>CWI<0x02><data><0x04>

Where data is the amount of time for the wind reading. This then gets converted to two individual hex characters to be sent. This is the bit that's causing me issues.

I would really like to using Bluetooth be able to send a decimal number and the wind gauge gets ran for that amount of time. The issue I have got is much Google searching has left me still scratching my head on how to convert these two charcters to hex. I know I'm missing something simple but I just can't seem to get there with it.

So far my (non-working) code is

#include "BluetoothSerial.h"
#include <HardwareSerial.h>

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(9600, SERIAL_7E1);
  SerialBT.begin("ESP32test"); //Bluetooth device name
  
}

void startwg() {
  delay(200);
  byte wgstart[] = {0x01, 0x13, 0x43, 0x57, 0x53, 0x02, 0x30, 0x30, 0x04};
  Serial.write(wgstart, sizeof(wgstart));
}
void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    String data = SerialBT.readStringUntil('\\');
    byte wginit[] = {0x01, 0x13, 0x43, 0x57, 0x49, 0x02};
  Serial.write(wginit, sizeof(wginit));
  //, 0x30, 0x35, 0x04
  char tens = data.charAt(0);
  char ones = data.charAt(1);
  Serial.write(strtoul(tens));
  Serial.write(strtoul(ones));
  Serial.write("0x04");
    startwg();
    
  }
  delay(20);
}

I know this is rough, but I'm just trying to get the basics working and then I'll pretty it up.

Just as a comparison, I can get the wind gauge to work as a hard coded app, but this isn't overly helpful to me:

#include <HardwareSerial.h>
void setup() {
  // put your setup code here, to run once:
Serial.begin(9600, SERIAL_7E1);

}

void loop() {
  // put your main code here, to run repeatedly:
byte wginit[] = {0x01, 0x13, 0x43, 0x57, 0x49, 0x02, 0x30, 0x35, 0x04};
byte wgstart[] = {0x01, 0x13, 0x43, 0x57, 0x53, 0x02, 0x30, 0x30, 0x04};
Serial.write(wginit, sizeof(wginit));
delay(200);
Serial.write(wgstart, sizeof(wgstart));
delay(6000);
byte wgout[] = {0x01, 0x13, 0x43, 0x57, 0x4f, 0x02, 0x30, 0x30, 0x04};
Serial.write(wgout, sizeof(wgout));
delay(10000);

}

Is there a way that I can get what I am trying to achieve done, I have tried a fair bit of stuff I have seen suggested. I thought I was onto a winner with Serial.Print, apparently this isn't included in HardwareSerial, and from everything I read I don't want to use SoftwareSerial, however, I'm considering that at this low baud rate it might be ok.

One other thing I have is this needs to be reliable and work every time without fail.

Thanks in advance.

Your topic title mentions RS232 but you say nothing about a TTL to RS232 converter in your post. Are you actually using RS232 ?

here's a link to code that reads non-ascii values from a geiger counter, Automess.ino.

your data starts with an ASCII start-of-header char and ends with an end-of-text char.

look at loop() in Automess.ino and how it resynchronizes when it receives an unexpected character. it also receives a checksum

Yes, I do have a TTL to RS232 converter, this isn't causing the problem though.

Thanks, I'll look into it now. The data is a copy and paste from the manual for the wind gauge, all I know is it works :slight_smile:

How is your data arriving from bluetooth? as a string like "12234" that you need to convert into a 2 byte hex code? Your code would indicate that the incoming data is just the actual hex values but I'm guessing that might be the issue.

Also, this line

    Serial.write("0x04");

does not write a single byte. You are writing 4 chars...

    Serial.write( (char) 0x04);

will send a single byte

I think you mean "converted to two bytes". Hex is a human readable representation of binary values, as is decimal.

Serial communications are in binary. So the question is, what do those two binary bytes represent?

Please post the documentation for the wind sensor protocol and we can help you figure out how to decode the transmission.

Also, what do you mean by "CWI" below? Three individual binary bytes with ASCII values representing the upper case letters C, W and I?

<0x01><0x13>CWI<0x02><data><0x04>

1. Which DPin of ESP32 you have chosen for BT?
2. Which DPins you have chosen for your Wind Guage?
3. Do you want to type the following image in the InputBox of BT and then click on the Send button?

<0x01><0x13>CWI<0x02><data><0x04>

(1) Which mode (BIN or ASCII) you wish to select in your BT? Select ASCII.
(2) What value you want to put in the data field? (For example: 12 or 0x12 or else.).

4. The possible solution is:
The ESP32 will receive formatted codes from BT for the command of Step-3 and then will assert the same to the SUART (Software UART Port) f Wind Guage.

Thanks for all the replies guys. Rather than replying individually I will combine everything into one.

The data is currently arriving from my phone connecting using a Bluetooth serial program. Eventually this will be changed to a PC based application that will send the decimal digits for the wind gauge time, followed by \r\n. This will only ever be the correct length of 2 digits.

The manual for the wind gauge can be found at http://www.dealer.gillathletics.com/store/documents/gill%20ultrasonic%20wind%20gauge%20user's%20manual.pdf page 7 is the relevant page. I haven't yet got into the return values from the reading, as I figure once I get my head around the starting the retrieving of the readings will be the easy part.

I haven't (don't need to?) specified any pins for BT, and am using the default TXD and RXD pins for the wing gauge. I want to only type in the portion of the command text to start the wind gauge for the specified amount of seconds. The data portion from what I have seen in other projects is two individual digits, so 10 is 1 and 0, not 10.

I hope this answers all the questions above but let me know if I've missed anything and thanks once again for your time. I am a little too rusty in decimal, hex and ascii, which is making this a little more difficult than I care to admit.

@jremington @blh64

It looks like you will need something like this:

#include "BluetoothSerial.h"
#include <HardwareSerial.h>

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(9600, SERIAL_7E1);
  SerialBT.begin("ESP32test"); //Bluetooth device name

  WGReset();
}

void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    String data = SerialBT.readStringUntil('\n');
    if ( data.length() > 1 ) {
      char tens = data.charAt(0);
      char ones = data.charAt(1);
      if ( isDigit(tens) && isDigit(ones) ) {
        WGSetInterval( tens, ones );
      }
      delay(20);
    }
  }
}

void sendWGCommand( char opcode, const char * data ) {
  const byte CMD_LEN = 9;
  const byte OPCODE = 4;  // index into cmd for op code
  const byte DATA = 6;    // index into cmd for data
  byte cmd[CMD_LEN] = {0x01, 0x13, 'C', 'W', 0x00, 0x02, 0x00, 0x00, 0x04};
  cmd[OPCODE] = opcode;
  cmd[DATA] = data[0];
  cmd[DATA + 1] = data[1];

  Serial.write(cmd, CMD_LEN);
  delay(200);
}

void WGReset() {
  sendWGCommand( 'R', "00" );
}

void WGStart() {
  sendWGCommand( 'S', "00" );
}

void WGSetInterval( char tens, char ones) {
  char data[2];
  data[0] = tens;
  data[1] = ones;
  sendWGCommand( 'I', data );
}

void WGOutput() {
  sendWGCommand( 'O', "00" );
}

It also seems like you will want to call WGOutput() to actually retrieve the data from the wind gauge. Ideally, you would ditch the String class and read this: Serial Input Basics - updated which will give you a more robust interface for receiving data

i think

    String data = SerialBT.readStringUntil('\004');

I don't think so. The OP wants to read input from the BT connection which is a string and then send it along to the wind gauge.

sending binary data "to" the wind gauge?

@blh64 is correct in his thinking. The string will come from the BT serial port to be sent to the WG after being "translated".

I'm just testing this code now.

This all works, I made a couple of changes, just for the sake of making the thread complete:

  • Call WGStart after WGSetInterval to start the wind gauge.
  • Call WGOutput after a delay (currently statically set this but will change this to be tens + ones + 00
  • Call WGReset after WG Output

Now I will get the output and do things with it.

Thanks for the help :smiley:

I don't think you will want to reset the wind gauge after every reading. You will also need to read/translate the result that comes back from the output command since it will have the same pre/post amble binary information surrounding the actual data you want

Without sending a reset command it will ignore any further interval commands.... because you know, reasons :roll_eyes:

The manual is poorly written and a bit unclear, but data packets appear to be ASCII characters, including the control characters.

This line agrees with my interpretation of the format for the "Send the most recent reading" command.

 wgout[] = {0x01, 0x13, 0x43, 0x57, 0x4f, 0x02, 0x30, 0x30, 0x04};

Although I would format it this way:

 wgout[] = {0x01, 0x13, 'C' ,'W', 'O', 0x02, '0', '0', 0x04};

Finally, ASCII control characters are named: SOH (start of header) = 0x01, STX (start of text) = 0x02, EOT (end of transmission) = 0x04, CR=0x13, so you could define those symbols and for clarity and ease of typing write

 wgout[] = {SOH, CR, 'C', 'W', 'O', STX, '0', '0', EOT};