Char array to float/hex

I'm playing around with some code where I read incoming data from an digital level which is all working great and receives all the data.
But What I would like or trying to convert this data if possible to HEX format like this RAW BUFF: [ XX XX XX XX 0C 36 ], Where the last 2 hex values contain say this value 32.70 (without the decimal point), so i could send the data to an M5 Stack unit that running code that receives the data via Bluetooth coming from the OWON B35 meter so I can just use the one unit as a display fro the both. I may be able then to work out send the decimal and +/- sign which is contained in the other bits.

This is my code, I've tried to convert to float/int using atof/atoi but the value remains 0

/*********
   #### TX CODE ####
  Wireless SPI-TRONIC Pro 3600 Digital Protractor
  General Information
  The Pro 3600 has an ASCII-format RS-232 compatible serial port for remote angle readout.
  The T&B Ansley 609-1027 connector on the back of the Pro3600 mates with industry
  standard cables. Angles are calculated and transmitted every 8/15 second (533 msec)
  Angle Output Format:
  The ASCII angle output may be read by a computer, or may directly drive a printer. Measured
  angles cover a full 360° range and the readout is between -180.00° and +180.00°.
  Format:
  <sign> XXX.XX <carriage return><line feed>
  examples:
  + 124.50
  + 32.70
  + 9.38
  - 4.32
  - 179.9
            [
  RAW BUFF: [ XX XX XX XX C0 09 ]
*********/
#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display
//##########################
//# SET UP VARIABLES       #
//##########################
SoftwareSerial ESPserial(4, 3); // RX | TX
const byte numChars = 9; //Number of bits to get
char receivedChars[numChars];//store the value of incoming data
boolean newData = false;
unsigned long NO_Data_in = 0;
#define ECHO_TO_SERIAL 1//change to zero = no serial output
// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)
//##########################
//# SET UP STRUCT FOR DATA #
//##########################
typedef struct Data_struct {
  int PRO3600_Disconnected = 10;
  boolean No_data;// = true;
  char receivedChars[9];
} Data_struct;
Data_struct Data;
//###########################################
//# PRINT DATA TO SERIAL MONITOR IF ENABLED #
//###########################################
void printRecord(Print* pr, char sep = ',') {
  // pr->print("0,1,2,3,4,5,6,7,8,"); // Print btye 0
  //  pr->println();
  pr->print(Data.receivedChars[0]); // Print btye 0
  pr->print(sep);
  pr->print(Data.receivedChars[1]); // Print btye 1
  pr->print(sep);
  pr->print(Data.receivedChars[2] ); // Print btye 2
  pr->print(sep);
  pr->print(Data.receivedChars[3]); // Print btye 3
  pr->print(sep);
  pr->print(Data.receivedChars[4]); // Print btye 4
  pr->print(sep);
  pr->print(Data.receivedChars[5]); // Print btye 5
  pr->print(sep);
  pr->print(Data.receivedChars[6] ); // Print btye 6
  pr->print(sep);
  pr->print(Data.receivedChars[7]); // Print btye 7
  pr->print(sep);
  pr->print(Data.receivedChars[8]); // Print btye 7
  pr->print(sep);
  pr->print(Data.PRO3600_Disconnected);
  pr->print(sep);
  pr->print(Data.No_data); // PData.PRO3600_Disconnected
  pr->println();

}
void setup() {
  lcd.init();
  lcd.backlight();
  Serial.begin(115200);
  ESPserial.begin(9600);
  Serial.println("READY TO GO");
  Serial.println("<Arduino is ready>");

}


void loop() {

  recvWithEndMarker();


  if (newData ) { //new data received from eLevel
    newData = false;
    Data.PRO3600_Disconnected = 10;
    Data.No_data = true;
  }

  unsigned long currentMillis = millis();
  if (currentMillis - NO_Data_in >= interval) { // one second is more than enough
    // save the last time you blinked the LED
    NO_Data_in = currentMillis;
    Data.PRO3600_Disconnected = 20;
    Data.No_data = false;
    Data.receivedChars[0] = '0';
    Data.receivedChars[2] = '0';
    Data.receivedChars[3] = '.';
    Data.receivedChars[4] = '0';
    Data.receivedChars[5] = '0';
    Data.receivedChars[6] = '0';
    Data.receivedChars[7] = ' ';
    Data.receivedChars[8] = ' ';
  }

  float dVal = atof(Data.receivedChars);
  lcd.setCursor(0, 0);
  lcd.print(Data.receivedChars[0]);
  lcd.setCursor(1, 0);
  lcd.print(Data.receivedChars[2]);
  lcd.setCursor(2, 0);
  lcd.print(Data.receivedChars[3]);
  lcd.setCursor(3, 0);
  lcd.print(Data.receivedChars[4]);
  lcd.setCursor(4, 0);
  lcd.print(Data.receivedChars[5]);
  lcd.setCursor(5, 0);
  lcd.print(Data.receivedChars[6]);
  lcd.print("  ");

#if ECHO_TO_SERIAL // Send debug to serial port if enabled
  printRecord(&Serial);
#endif //ECHO_TO_SERIAL
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;
  if (ESPserial.available() > 0 && newData == false) { // Tried this
    //   while (gtSerial.available() > 0 && newData == false) { //tried this
    NO_Data_in = millis();
    rc = ESPserial.read();
    if (rc != endMarker) {
      Data.receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      Data.receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
      //  receiveUntilTimeout = true; //tried here
    }
  }
}

A float value takes up four bytes. This will copy a float value to an array of bytes (binary, of course).


char buf[4];
float x=32.70;
memcpy(buf,&x,4);

If I understand that this will convert a float value ?
The 32.70 is in a char value and I need to convert this to HEX value.

32.70 cant be "a char value", so please explain more about your input and output data

sorry I will try and explain a bit better

This is what is shown and been held in the Data.receivedChars variable.

on the SPI level it diaplys -15.74 and If I move it to the opssipte side it displays +15.74

Data.receivedChars[0] = holds the +/- sign
Data.receivedChars[1] = not needed
Data.receivedChars[2] = 1
Data.receivedChars[2] = 5
Data.receivedChars[3] = .
Data.receivedChars[5] = 7
Data.receivedChars[6] = 4

Hex Format output example is what I'm trying to do.(NC = no change)
0   1  2  3   4  5
NC NC .  NC 15 74 (decimal)
23 F0 04 00 06 26 (output in hex format)
The 04 in the meter code is the decimal point place so this would not need to move.

So when I use a dec to hex calculator 1574 = 0626 and would like to send 0x06 and 0x26 has the receiver then converts this to a value
to display 1574 the decimal does not need to be moved but the receiver places it depending on the value of bit

Do I understand correct - you need first convert char array "15.74" to float and second convert the float to HEX 0x0626 ?
Where did you experienced problems?

Please explain this line. Do you receive 15.74 as char array "15.74" (5chars) or as decimal coded value 15 74 (2 bytes)?

And do you need that in hex format it will be a single value 1574 or two hex bytes 15 and 74 ?

First of all I'd like to say thank you for taking time to reply.
This would be the hex format I'd like to achieve.
The Data.receivedChars is where the incoming value is stored from the Mitutoyo pro3600 digital level. I would like to convert the Char value "-15.74" direct to HEX format like above. Not to worried about the +/- sign at the moment that can come after I've masted sending the hex value out just for the level reading
So this how I would like to send the HEX format 23 F0 04 00 06 26 so that it can be sent out
23 = NC value will not change
F0 = NC value will not change
04 = NC value will not change
00 = NC value will not change
06 = value will change depending on the angle of the digital level
26 = value will change depending on the angle of the digital level

15.74 is just an example of the reading in degrees.

Sorry, but it is not an answer to my last question in the message #7
How your float should be encoded - as a single number 1574 (0x0626 in hex) or as two - separately integer part 15 (0xf) and a fraction 74 (0x4a) ?
Your example presents a second variant.

sorry as two - separately hex bits to add to the other 4 bits of hex like the example.
The ASCII format comes in from the digital level like this Format: XXX.XX So I make that 9 as I use the line feed to detect the end of the incoming data and ready for the next lot coming in.
I have a OWON B35+ meter that I use and use the M5Stack in the link below to make it wireless
https://github.com/reaper7/M5Stack_BLE_client_Owon_B35T

My idea is to use the same M5Stack unit to display my digital level data, So basically I'm trying duplicate the data format that the OWON 35 meter is sending out.
This is the example for the meter displaying 2.496V

[ 23 F0 04 00 C0 09 ] =so the reading wold be 2496

the hexadecimal values in the frame are swapped in the M5Stack and these two bytes do not contain information about the decimal place. the Decimal place is controlled by bit 2 (04) of the 6 bits of the hex data and only bits 4&5 need to change when the level is moved
hopefully I've answered your question ?

yes, definitely :slight_smile:

And where did you stuck?

On how to convert 1574 to the HEX value and put it in the format as I've laid out above, I've seen people say convert to float or int but then not sure how to convert to hex or like the format as above. Once I've done that I can then hopefully work on the part to send via bluetooth from an ESP32 that's reading the Serial data coming in. but I just thought I need to workout and just display the hex values on the LCD to start with

You don't need to convert to hex because the difference between hex, decimal and binary exists only in the sketch, for the compiler they are the same thing.
All you need to do is represent the integer 2496 as two bytes and swap them around:

int x = 2496;
byte b1 = x & 0xff;             // low byte
byte b2 = (x >> 8) & 0xff;  // high byte
Serial.print( b1, HEX);
Serial.print(" ");
Serail.println(b2, HEX);

will print
c0 9

Why? Hex notation (like decimal notation) is just a human-readable representation of binary values.

Post a link to the product page and/or user manual for the thing to which you are sending data.

Your terminology is a bit confusing. The date is made up of 6 bytes of data:
byte 0 = 0x23
byte 1 = 0xF0
byte 2 = 0x04
byte 3 = 0x00
byte 4 = 0xC0
byte 5 = 0x09

From your discription, it seems byte 2 contains the position of the decimal point, in this case after the 4th digit, and bytes 4 & 5 contain the value.
The value is not a float, it is a 2-byte integer, in little-endian format (the lower 8 bits in byte 4, the upper 8 bits in byte 5). Written as hexidecimal, the value is 0x09C0, which is 2496 in decimal.

The data from the digital protractor comes in as ASCII data, which you are converting to a float with the atof() function.

You will need to determine how many decimal places are present in the number from the digital protractor, so you can convert the float to an integer. That is probably the most complex part of the code.

Also, it is not clear whether the integer is signed or unsigned.

Sorry jremington i deleted by mistake.
The OWON B35+ digital meter send the data out in this format there is no manual on the data format other than bits I found . It sends it to an M5Stack which uses the code from the github link I posted above by a clever guy. I want to create the same ouput format as the OWOn B35+ meter.
This link is what I also read up on.
https://github.com/DeanCording/owonb35#protocol

Sorry, but it still not clear for me what are you doing and what is your problem.

Converting "15.74" from ascii string to two bytes is relatively easy task if you correctly described the data format.
Since you even have a ready code from github guy - what prevent you from using it in your sketch?
If you tried to do this, show YOUR code and describe what doesn’t work in it

The very comprehensive user manual is here: https://www.owontechnology.eu/_downloads/4eef40ed15e3357814800a22bde1127c?_locale=en_GB

I've been studying an playing with the M5Stack code and used some false meter test code and worked out the layout of the format the owon b35+ meter sends. sorry I got the wrong byte for the decimal place

byte 0 = 0x22 // This sets the decimal place 00.00
byte 1 = 0xF0 // not sure yet
byte 2 = 0x04//sets the function
byte 3 = 0x00 //not sure yet
byte 4 = 0x26 ;//part of voltage reading
byte 5 = 0x06 ;//part of voltage reading

This is part of the false meter so I can display the values without using a meter for testing.

#ifdef FALSEMETER
static void notifyTest() {
  uint8_t _pData[replySizePlus];
  _pData[0] = 0x22;//0x22 decimal places0x23 = 0.00 0x22 = 00.00 0x21 = 000.0 401Fbe77
  _pData[1] = 0xF0;// not sure yet
  _pData[2] = 0x04;//function
  _pData[3] = 0x00;//not sure yet
  _pData[4] = 0x26;//part of voltage reading
  _pData[5] = 0x06;//part voltage readings,showing .
  //c009 is 2.496v
  //c089 is -2.496v
  voltage = (_pData[4] << 8) + _pData[5];
  voltageFloat = voltage * 0.01;
  if (memcmp(rawvalbuf, _pData, replySizePlus) != 0) {                                // if new data <> old data
    if (newBleData == false) {                                                // and if old data are displayed then copy new data
      memcpy(rawvalbuf, _pData, replySizePlus);


#ifdef MYDEBUG
      DEBUG_MSG("D: RAW BUFF: [ ");
      for (uint8_t i = 0; i < replySizePlus; i++) {
        DEBUG_MSG(" %02X ", rawvalbuf[i]);
      }

      DEBUG_MSG("]\n");
      DEBUG_MSG("Volts:%02d ", voltageFloat);


#endif

      if (meterIsPlus == false) {
        meterIsPlus = true;
      }
    }
    newBleData = true;
  }
  lastBleNotify = millis();

}
#endif```
and this is the output of the raw buffer and if I change the values of the bytes 4&5 I can see the human readable value format. so I think it's a Hex signed 2's complement (4 digits).
The trouble is I don't know or understand how to convert from ASCII (char format) to the raw buffer Hex format

Did you try to write the code? Show your efforts