Transferring FLOAT data via HC12

You seem to be using the size of the float datatype (aka 4) as some sort synchronisation marker.
Unfortunately, the value 4 can also appear as part of the representation of the floating point numbers you're trying to send.
That could go wrong for you.

The solution clue is in the above quoted text.

We need a frame synchronizing marker, and it must be beyond the ASCII set (at least beyond of 0 - 9, A - F, and a - f) of characters/digits. That means, we have to send the data (of any nature) from TX-side to RX-side frame-by-frame (Intel-Hex Frame) using asynchronous protocol. I have used the character : (0x3A) as an indicator for the beginning of a transmission frame.

The following setup and codes are sending any number of data bytes (I have tested by sending 5-byte floating numbers) from TX to RX using HC-12 Long Range Wireless Radio Communication Module.

Features:
1. : is the frame synchronizer
2. TX-side HC-12 is soft UART driven device.
3. RX-side HC-12 is hard UART driven device
4. CHKSUM is calculated and send from TX-side; but, it has not been recomputed and validadted at the RX-side.
5. A dummy location (0x0010) has been keyed in the Frame as a buffer loaction for the storgae of received data.
2xHC-12 Setup:

Data Frames Sent:

Received Data Frames and Reconstructed Original Data:

TX Code:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // HC-12 TX Pin, HC-12 RX Pin
float x = 121.50;      //base number for transmission
float deltaX = 1.50;  //base number will be increased by 1.50

byte frameArray[10];  //Frame length is 10-byte including 4-byte for a floating point number
byte dataArray[4];      //space to hold 4-byte codes of the floating point nuber

void setup() 
{
  Serial.begin(9600);             // Hard Serial port to computer
  mySerial.begin(9600);        // Soft Serial port to HC12

}

void loop() 
{  
  for (int n=0; n<5; n++)     //send only 5 floating point numbers: 121.50, 123.00, 124.50, ...
  {
    buildFrame();                  //construct 10-byte wide Intel-Hex Frame: : 04 1000 00 nnnnnnnn XX
    sendFrame();                  //send ASCII for each digit of Intel-hex frame except : which goes as 3A
    x += deltaX;                  //increase the base number by 1.50
  }
  HERE: goto HERE;             //after transmission stay HERE for ever
}

//------------------------------------------------------------------------------------------------------
void sendFrame()                //conver digit to ASCII and write to HC-12 for transmission
{
   mySerial.write(frameArray[0]);        //always send the synchronizer marker (:) as binary code
   Serial.print((char)frameArray[0]);    //show the character on Hard Serial Monitor
  
  for(int i = 1; i<10; i++)
  {
    byte x = frameArray[i];
//    Serial.print(frameArray[i]);
    byte x1 = x;
    x1 = x1>>4;
    if (x1 <=9)
    {
      x1 = x1+0x30;             
      mySerial.write(x1);      //transmit: 0x30 - 0x39 for 0 - 9 for the 1st digit of a byte
       Serial.write(x1);
       //HERE: goto HERE;
    }
    else
    {
      mySerial.write(x1+0x37);  //transmit: 0x41 - 0x46 for A - F for the 1st digit of a byte
       Serial.write(x1+0x37);
    }
  //--------------------------------
    x = frameArray[i];
    x = x & 0x0F;
    if (x <=9)
    {
      x = x+0x30;
      mySerial.write(x);      //transmit: 0x30 - 0x39 for 0 - 9 for the 2nd digit of a byte
       Serial.write(x);
    }
    else
    {
      mySerial.write(x+0x37);    //transmit: 0x41 - 0x46 for 0=A - F for the 2nd digit of a byte
       Serial.write(x+0x37);
    }
    
  } 
  Serial.println();    //enter new line 
}

//-----------------------------------------------------------------------------------------------------------------
void buildFrame()
{
  byte *ptr = (byte*) &x;    //declaration of pointer variable for the binary32 formatted value of a flp number

  frameArray[0] = 0x3A;   //: statrt of a frame
  
  frameArray[1] = 04;       //number of information byte = number of bytes for a floating point number
  
  frameArray[2] = 0x10;   //lower byte of the buffer address: 0x0010
  frameArray[3] = 0x00;   //upper byte of the buffer address: 0x0010
  
  frameArray[4] = 0x00;   //code to mark end-of-file (0x01 indicates EOF) when sending data from file
  //-------------------
  
  for (int i=5, j=0; i<9, j<4; i++, j++)  //saving 4-byte binary32 vale for a flp number
  {
    dataArray[j] = *ptr;
    frameArray[i] =  dataArray[j];
    ptr++;
  }

  frameArray[9] = chkSum();         //creating normal sum based CHKSUM for error control
 
}

byte chkSum()  //CHKSUM algorithm: all all bytes of a frame except :; discard carry; take 2's Complement
{
  byte sum=0;
  for (int k = 0; k<9 ; k++)
  {
    sum += frameArray[k]; 
  }
  sum =~sum;
  sum++;
  return sum;
}

RX Codes:

//#include <SoftwareSerial.h>      //Hard UART Module

bool flag = false;          //Frame synchronizer has not come
byte frameArray[19];
byte x, y, y1;
int i=0;
int j =0;

union
{
  byte myData[4];  //lower-most array elemnet contains lower-most byte
  float z;
  unsigned long p;
} floatNumber;

void setup() 
{
  Serial.begin(9600);             // Serial port to computer
}

void loop() 
{
  if (i == 19) //complete frame is received
  {
    buildAndShowFloatNumber();  //nuumber in: z, p, and myData[] of union{}
    i=0;
    flag = false;
    i=0;
    j=0;
  }

}

void buildAndShowFloatNumber()
{
    //cli();
   Serial.println();//write(frameArray[18]);
   // HERE: goto HERE;

   for(i=9, j=0; i<17, j<4; i++, j++)
   {
      y = frameArray[i];
      if (y < 0x41)
      {
        y = y & 0x0F;
        y = y << 4;
      }
      else
      {
        y = y - 0x37;
        y = y << 4;
      }
     // Serial.println(y, HEX);
      //-------------------
      i = i+1;//i++;
      y1 = frameArray[i];
      if (y1 <0x41)
      {
        y1 = y1 & 0x0F;
        
      }
      else
      {
        y1 = y1 - 0x37;
       
      }
   //   Serial.println(y1, HEX);
      //------------------------
      y = y|y1;
    //  Serial.println(y, HEX);
      floatNumber.myData[j] = y;
   }

 //  Serial.print(floatNumber.myData[3], HEX);
 Serial.print(floatNumber.z, 2);
 Serial.println();
}


void serialEvent()
{
   if (flag == true)
   {
    while (Serial.available()) 
      {        // If HC-12 has data
        x = Serial.read();
        Serial.write(x);     // Send the data to Serial monitor
        frameArray[i] = x;    //save in array
        i++; 
      }
   }

   else
   {
    byte s = Serial.read();
    if ( s != 0x3A)
    {
      flag = false;
    }
    else
    {
      if( s == 0x3A)
      {
        Serial.write(s);
        flag = true;
        i++;
      }
    }
    
  }
}

oe8pck:
Just tried the new code for RX and this is the output. The Transmit just takes the last variable and adds 1.5.

I finally got round evaluating my code out and with the bug fix I mentioned on post #30 and I am receiving and decoding all the float TX bytes correctly... not sure what is going wrong on your side oe8pck

//TRANSMITTER CODE
#include <SoftwareSerial.h>
//using 'union'to store a float and a uint8_t array of the same size as a float in the same memory location
union{
  float num;
  uint8_t bytes[sizeof(float)];
} f_tx;
//

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

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  mySerial.begin (9600);
  f_tx.num=0.0 //initialiase float variable
}
void loop() {
   
  //increment float by 1.2 with every loop
  f_tx.num = f_tx.num + 1.2;
  
  //using size of float as start byte
  mySerial.write(sizeof(float));
  
  //set float bytes one by one using a 'for' loop. delay was commented out as optional
  for(uint8_t i=0; i<sizeof(float);++i){
	mySerial.write(f_tx.bytes[i]);
	//delay(100);
  }
 
  //Serial.print("f_tx.num =      ");
  //Serial.println(f_tx.num,DEC);
  delay(100);
}

//RECEIVER CODE
#include <SoftwareSerial.h>
//using 'union'to store a float and a uint8_t array of the same size as a float in the same memory location
union {
  float num;
  uint8_t bytes[sizeof(float)];
} f_rx;
//

SoftwareSerial mySerial(10, 11); // RX, TX
uint8_t i, r_float;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  mySerial.begin (9600);
}

void loop() {
byte data;
  
  //checking is the serial RX buffer contains unread data
  if (mySerial.available()) {
	//read data from RX buffer
    data = mySerial.read();
	
	//check of data if data is a float start byte (determined be flag r_float AND data = sizeof(float))
    if (data == sizeof(float) && r_float == 0) {
      f_rx.num = 0.0; //reset rx float variable
      r_float = 1; //set flag
      i = 0; //reset byte counter
    }
	//flag was set ie receiving data is part of a float
    else if (r_float == 1) { 
      //if byte counter is less than sizeof(float), add data to array
	  if (i < sizeof(float)) {
        f_rx.bytes[i] = data;
        ++i;
      }
	  //all float bytes received print out
      if (i == sizeof(float)) {
        Serial.print("f_rx.num =      ");
        Serial.println(f_rx.num, 2);
        r_float = 0;
      }
    }
	//float start byte not received. therefore just print out received data 
	else {
      Serial.println(data, DEC);
    }
  }
}

@GolamMostafa "You seem to be using the size of the float datatype (aka 4) as some sort synchronisation marker.Unfortunately, the value 4 can also appear as part of the representation of the floating point numbers you're trying to send.
That could go wrong for you."

irrelevant IF your receiver code if written correctly... once the first '4' is received, if another '4' is received while receiving the next four bytes, it will be interpreted as part of the float variable. PROOF being my code which I tested and confirmed as working correctly

Hi GolamMostafa, This is all very overwhelming at the moment. It looks as though your solution is working but I still want to try Robin2 solution. I will also be trying yours of course, I just need time to gather my thoughts and understand what you wrote because the first look was very complicated for me. Thank you very much and I will get back to you.

oe8pck:
Hi GolamMostafa, This is all very overwhelming at the moment. It looks as though your solution is working but I still want to try Robin2 solution. I will also be trying yours of course, I just need time to gather my thoughts and understand what you wrote because the first look was very complicated for me. Thank you very much and I will get back to you.

Just from the sidelines: I used Robin2's solution for the same issue you have: transmit float, fixed and ascii over HC12. Using his solution is not only sensible, it is very usefull when debugging and writing code in general. I used his algorithms for wome fairly complex programs and never looked back.
Thanks to Robin2!

Robin2:
Start with the code in my example just as it is and see what happens.

...R

Hi Robin2,
I have written the TX code but have a compiling error at "mySerial.write(num,2);" I have defined num, can it be that softwareSerial does not like mySerial after another?

//TRANSMITTER CODE
#include <SoftwareSerial.h>
float num;
SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
mySerial.begin (9600);
num = 0.0; //initialiase float variable
}
void loop() {

//increment float by 1.2 with every loop
num = num + 1.5;
mySerial.write('<');
mySerial.write(num, 2);
mySerial.write('>');
Serial.print("num = ");
Serial.println(num, 2);
delay(1000);
}

mySerial.write(&num, 4);...is what you meant to write. :wink:
(Or perhaps "mySerial.print(num, 2);")

oe8pck:
Hi Robin2,
I have written the TX code but have a compiling error at "mySerial.write(num,2);"

I'm not surprised.

I told you to use mySerial.print()

...R

oe8pck:
Hi Robin2,
I have written the TX code but have a compiling error at "mySerial.write(num,2);" I have defined num, can it be that softwareSerial does not like mySerial after another?

For future reference, in such cases, it is more useful and informative to post all of the error message and all of the code, in code tags.

brice3010:
Just from the sidelines: I used Robin2's solution for the same issue you have: transmit float, fixed and ascii over HC12. Using his solution is not only sensible, it is very usefull when debugging and writing code in general. I used his algorithms for wome fairly complex programs and never looked back.
Thanks to Robin2!

Thanks for your interesting comments.

Robin2:
I'm not surprised.

I told you to use mySerial.print()

...R

Embarrassing sorry :frowning:

@GolamMostafa "You seem to be using the size of the float datatype (aka 4) as some sort synchronisation marker.Unfortunately, the value 4 can also appear as part of the representation of the floating point numbers you're trying to send.
That could go wrong for you."

irrelevant IF your receiver code if written correctly... once the first '4' is received, if another '4' is received while receiving the next four bytes, it will be interpreted as part of the float variable. PROOF being my code which I tested and confirmed as working correctly

Expected results could be achieved in many possible ways, and all these ways are perfectly alright if they can be validated. However, we can always come up with a robust, reliable, and stable solution as long as we remain footed on an established standard. The HC-12 modules are clearly UART Protocol based products; therefore, the natural choice would be to adopt the asynchronous protocol which handles very well the Intel-Hex Formatted data frames.

My codes have been made intentionally elaborated in respect to OP's request. Comments are being slowly added to comprehend the meanings of the instructions concerned. Addition of a hand-shaking scheme (under process) would make the program a really solid one!!

Hi Robin2,
IT WORKS!!!!! and I even understand most of the RX side TX side is OK and Clear.
THANK YOU VERY MUCH!!!!!
This is the best solution at the moment, now the hard work starts getting Altitude data and then working with it on the RX side this is not a problem. I am going to have to use Interrupts, so I hope this does not damage the RX data. Anyway you have ALL helped me very much. Thank you for your patience, I must admit I was frightened of forums because of reading some comments but I think this has gone very well and I hope I have not snubbed anyone.

MANY THANKS TO ALL but especially Robin2.

//TRANSMITTER CODE
#include <SoftwareSerial.h>
float num;
SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  mySerial.begin (9600);
  num = 0.0; //initialiase float variable
}
void loop() {

  //increment float by 1.2 with every loop
  num = num + 1.5;
  mySerial.print('<');
  mySerial.print(num, 2);
  mySerial.print('>');
  Serial.print("num =      ");
  Serial.println(num, 2);
  delay(100);
}
// Example 3 - Receive with start- and end-markers

const byte numChars = 32;
char receivedChars[numChars];

boolean newData = false;
#include <SoftwareSerial.h>;
SoftwareSerial mySerial(10, 11);
void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
  Serial.println("<Arduino is ready>");
}

void loop() {
  recvWithStartEndMarkers();
  showNewData();
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (mySerial.available() > 0 && newData == false) {
    rc = mySerial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

void showNewData() {
  if (newData == true) {
    Serial.print("This just in ... ");
    Serial.println(receivedChars);
    newData = false;
  }
}

GolamMostafa:
Expected results could be achieved in many possible ways, and all these ways are perfectly alright if they can be validated. However, we can always come up with a robust, reliable, and stable solution as long as we remain footed on an established standard. The HC-12 modules are clearly UART Protocol based products; therefore, the natural choice would be to adopt the asynchronous protocol which handles very well the Intel-Hex Formatted data frames.

My codes have been made intentionally elaborated in respect to OP's request. Comments are being slowly added to comprehend the meanings of the instructions concerned. Addition of a hand-shaking scheme (under process) would make the program a really solid one!!

Yes a handshake stem would be the icing on the cake but after nearly 8 months of sweating I am VERY HAPPY with the results now. Thank you for your kind help.

Data communication is just not an event of 'giving' and 'taking'; it must posses the power of error checking and possible way of re-transmission! Let us look at the Arduino IDE -- so short distance between the PC and the UNO, and still they have used asynchronous protocol to handle data in Intel-Hex format. We need to think why they have done so? They could simply do binary byte transmission with some control characters at the beginning and at the end?

sherzaad:
I finally got round evaluating my code out and with the bug fix I mentioned on post #30 and I am receiving and decoding all the float TX bytes correctly... not sure what is going wrong on your side oe8pck

Hi sherzaad, Many thanks your code also works perfectly. Don't know what went wrong. I just overwhelmed by the help from all and would like to thank you very much especially for your kind patience with me .

GolamMostafa:
Data communication is just not an event of 'giving' and 'taking'; it must posses the power of error checking and possible way of re-transmission! Let us look at the Arduino IDE -- so short distance between the PC and the UNO, and still they have used asynchronous protocol to handle data in Intel-Hex format. We need to think why they have done so? They could simply do binary byte transmission with some control characters at the beginning and at the end?

I take it you mean I should write a handshake code, you are quite right but I also have a room problem when I use an OLED screen in the RX side. The library takes up alot of space. I will try this when everything else is working. :slight_smile:

oe8pck:
I take it you mean I should write a handshake code,

I don't think you should.

You were kind enough to say in Reply #51 that things are now working with simple code. Stay with that.

...R

@oe8pck
Just to satisfy my curiosities, I have few questionnaires referring to your statement that this floating point data transmission is related with the control of heights of some kinds of gliders and the like.

1. What is the source of this floating point number?
(a) Are you entering this number from a keyboard.
(b) Is this number coming in the form of a voltage from a pot via the analog channel of the Arduino?

**2.**I have executed the TX/RX codes of your Post#52. They are being executed. The TX is sending some kind of fractional decimal numbers; in response, the RX is printing some kind of 8-bit value. There is no one-to-one correspondence as to what we send and we see?

3. You made humble request to the Forum members to insert comments against every program lines so that you will be able to understand the intention of the program codes, and this way you would be able to improve your programming skills. But, You are not asking simple question like: In the codes of Post#52, the MCU of RX Section is not using the serialEvenet() ISR (and of course not mandatory), which is automatically called upon when a valid character is present in the Receiver-Section of the RX.

4. Referring to Codes of Post#52, The TX is sending '<' and '>' characters which are not being parsed 'explicitly' in the Receiver- Section.

5. In data communication (it is not broadcast like Radio Program), the hand-shaking is always maintained to be sure that data is not lost. The TX asks the RX if the RX is ready to accept data. The RX gives the consent and then the TX proceeds with data transmission.

Thanks,

MCU of RX Section is not using the serialEvenet() ISR

I do not believe that serialEvent is an ISR.