HC12 using SoftwareSerial receiving data buffer order incorrect

I have two Arduino Nano micro controllers using HC12 transceivers to transmit data between the two.

The HC12 radio communication works fine, but sometimes I do get radio transmission error, (radio signal lost for a few seconds and then recovers). It is with this loss of signal and the recovery thereafter that I have a problem with the data received.

In the receiving Nano serial buffer, I lose the starting point of the transmission sequence, and then all data received is scrambled.

Example:

This is what I am transmitting from the sending Nano HC12 node

[0]=9 [1]=6F [2]=27 [3]=51 [4]=11 [5]=C6 [6]=1D [7]=7E [8]=0 [9]=FF Loop: 128
[0]=9 [1]=6F [2]=27 [3]=51 [4]=11 [5]=C6 [6]=1D [7]=7E [8]=0 [9]=FF Loop: 127
[0]=9 [1]=6F [2]=27 [3]=51 [4]=11 [5]=C6 [6]=1D [7]=7E [8]=0 [9]=FF Loop: 123

On the receiving Nano HC12 node, this is what I should be receiving ....

[0]=9 [1]=6F [2]=27 [3]=51 [4]=11 [5]=C6 [6]=1D [7]=7E [8]=0 [9]=0 Temp: 2415

But, should I loose radio signal, or the Nano resets and communication resumes,
I get the following...

Reset:
[0]=1D [1]=7E [2]=0 [3]=FF [4]=9 [5]=6F [6]=27 [7]=51 [8]=11 [9]=0 Temp: 7550
Reset again:
[0]=11 [1]=C6 [2]=1D [3]=7E [4]=0 [5]=FF [6]=9 [7]=6F [8]=27 [9]=0 Temp: 4550
Reset again:
[0]=51 [1]=11 [2]=C6 [3]=1D [4]=7E [5]=0 [6]=FF [7]=9 [8]=6F [9]=0 Temp: 20753
Reset again:
[0]=27 [1]=51 [2]=11 [3]=C6 [4]=1D [5]=7E [6]=0 [7]=FF [8]=9 [9]=0 Temp: 10065

You get the picture ....

You will notice that the data sequence stays the same, i.e.
0x09, 0x6F, 0x27, 0x51, 0x11, 0xC6, 0x1D, 0x7E, 0x00, 0x09 always stays the same, but after each reset or radio signal loss, the first transmitted byte shows up in the wrong starting order on the receiving node.

I have had a look on the Arduino forum. I used this OP’s code to help me compile my sketch.

I had a look at Nick Gammon’s site.

I must be missing something? But after battling now for what seems an eternity, I finally submit to asking for help. Google has been my best friend so far, but there’s also only so much that Google can help you with :frowning:

My transmission sketch ...

/* -------------------------------------------- //
 * HC12 Tranceiver Test code.
 * Sends 10 bytes of data to a partner HC12
 * When cycling power on the partner the data receive
 * sequence does not always start with the first byte. WHY???
 * -------------------------------------------	*/

#include <SoftwareSerial.h>		// HC12 communication
SoftwareSerial mySerial(2, 3); 	// RX, TX

#define LG          6			// Green LEX (Tx)
#define LB          7			// Blue LED (Rx)
#define FlashTime   2			// LED flash time in ms


// -------------------------------------------------------- //
//	code snippets from ....
//	http://forum.arduino.cc/index.php?topic=96280.30 Post #35
// -------------------------------------------------------- //

int long inArray[10]; 			// Data to be sent
int myData[5];				// Variables to be transmitted
byte bDataH[5];				// High byte for myData
byte bDataL[5];				// Low byte for myData
int ByteNumber;
const byte numChars = 9;
char messageReceived[numChars]; // allocate some space for the incoming message


static unsigned long NowMillis = 0;		
static unsigned long LastMillis = 0;		
static unsigned long LoopMillis = 0;		


// -------------------------------------------------------- //
void setup() {
	Serial.begin(9600);
	mySerial.begin(4800);				// HC-12 baudrate

	pinMode(LG, OUTPUT);				// Green Tx
	pinMode(LB, OUTPUT);				// Blue Rx

	ByteNumber = 0;

// -------------------------------------------------------- //
// Test data to be sent

myData[0] = 2415;					// 24.15Dec 
myData[1] = 10065;				        // Pressure 1006.5mbar
myData[2] = 4550;					// Relative humidity 45.5
myData[3] = 7550;					// water tank level 75.5%
myData[4] = 255;						// incremental watchdog signal

{
	for (int X = 0; X < 5; X += 1) {
	bDataH[X] = (highByte(myData[X]));
	bDataL[X] = (lowByte(myData[X]));
	}
}

} /// End Setup


// -------------------------------------------------------- // 
void loop() {

char endMarker = '\n'; // newline character will be terminating

inArray[0]=bDataH[0];	// Hex 0x096F = 2415 °C
inArray[1]=bDataL[0];	// I want to send the value of 2415 to other radio
inArray[2]=bDataH[1];	// Hex 0x2751 = 10065
inArray[3]=bDataL[1];	// Pressure signal
inArray[4]=bDataH[2];	// Hex 0x11C6 = 4550
inArray[5]=bDataL[2];	// Relative humidity
inArray[6]=bDataH[3];	// Hex 0x1D7E = 7550
inArray[7]=bDataL[3];	// Water level %
inArray[8]=bDataH[4];	// Hex 0x00FF = 255 
inArray[9]=bDataL[4];	// Watchdog signal

TxData();									// 

delay(50);
mySerial.flush();								// Clear the Tx buffer
RunTime();									// Loop cycle time

}

// -------------------------------------------------------- //
//	Data to be Tx to partner HC12 radio
// -------------------------------------------------------- //
void TxData() {
	for (int k = 0; k < 10; k += 1) {
		mySerial.println(inArray[k]);
	Serial.print(" [");
	Serial.print(k);
	Serial.print("]=");
	Serial.print(inArray[k],HEX);
	FlashG();								// LED indication
  } /// end for k
  
}


// -------------------------------------------------------- //
// Flash Green LED
// -------------------------------------------------------- //
void FlashG (){
		digitalWrite(LG, HIGH);					// Ligjt Led Back on Transmitter
		delay(FlashTime);						
		digitalWrite(LG, LOW);					
}


// -------------------------------------------------------- //
// Flash Blue LED
// -------------------------------------------------------- //
void FlashB (){
		digitalWrite(LB, HIGH);					// Ligjt Led Back on Transmitter
		delay(FlashTime);						
		digitalWrite(LB, LOW);					
}

// -------------------------------------------------------- //
// ----------------- RunTime routine ----------------------
// -------------------------------------------------------- //
void RunTime() {
	LastMillis = NowMillis;
	NowMillis = millis();	
	LoopMillis = NowMillis - LastMillis;

    Serial.print(" Loop: ");
    Serial.println(LoopMillis);
} /// End of RunTime

My Receiver sketch ...

/* -------------------------------------------- //
 * HC12 Tranceiver Test code. (Rx side)
 * Sends 10 bytes of data to a partner HC12
 * When cycling power on the partner the data receive
 * sequence does not always start with the first byte. WHY???
 * -------------------------------------------	*/

#include <SoftwareSerial.h>							
SoftwareSerial mySerial(2, 3); 

static unsigned long NowMillis = 0;	
static unsigned long LastMillis = 0;
static unsigned long LoopMillis = 0;

int outArray[9];
int iTemp;
int ByteNumber;


// -------------------------------------------------------- //
void setup() {
	Serial.begin(9600);	
	mySerial.begin(4800);		// HC-12 baudrate
} /// End Setup


// -------------------------------------------------------- //
 void loop() {	

	if (mySerial.available () > 0)
	{
		outArray[ByteNumber ++] = mySerial.parseInt();
	}
 
 if (ByteNumber > 11){	// process it

	ByteNumber = 0;                                         // ready for next time
	displayData();
	combine();						       // combine bytes into int

    }  // end if full

	RunTime();							// Show cycle time of loop

} // End Loop


// -------------------------------------------------------- //
//display the array in serial monitor
// -------------------------------------------------------- //
void displayData(){
 for (int j = 0; j < 10; j += 1){
    Serial.print(" [");
    Serial.print(j);
    Serial.print("]=");
	Serial.print(outArray[j],HEX);
 }
}

// -------------------------------------------------------- //
// byte sent over. Combine to form int signal
// -------------------------------------------------------- //
void combine(){
 unsigned iTemp = outArray[0] << 8 | outArray[1];
    Serial.print(" Temp: ");   
 Serial.println(iTemp);
}


// -------------------------------------------------------- //
// ----------------- RunTime routine 
// -------------------------------------------------------- //
void RunTime() {
	LastMillis = NowMillis;
	NowMillis = millis();			
	LoopMillis = NowMillis - LastMillis;
//    Serial.print("LoopMillis: ");
//    Serial.println(LoopMillis);
} /// End of RunTime
int long inArray[10]; 			// Data to be sent
byte bDataH[5];

Why do you need a long array (4 bytes per element) to store one byte elements?

		outArray[ByteNumber ++] = mySerial.parseInt();

outArray holds the incoming data, while inData holds the outgoing data? WTF?

Your assumption that serial data is never lost is complete nonsense. You MUST plan for that, and make some effort to re-sync, if data is lost. That is difficult with binary data, but you are not sending binary data.

Sending the data in a more readable fashion, like "<1D, 7E, 0, FF, 9, 6F, 27, 1, 11, 0>" would give you a start of packet marker ('<') to sync on and an end of packet marker ('>') to know when a complete packet is received.

The HC12 transceivers do not provide end to end data integrity checking, so if thats what you want you must include code at both ends that checks the data integrity.
eg send data in small blocks with a CRC checksum and a block number.
If the receiver gets a block with the wrong block number, ie out of sequence, then it requests the transmitter sends the missing block.

In this Arduino.cc forum I found the following “Serial Input Basics – updated” post by Robin2. Very good example of the Start and End marker. With his code example I completed my code.

Transmitter code …

/*
 * HC12 BMP180 test code
 * 2017-02-05 Rev0.0
 * 
 */

#include <Streaming.h>  // http://arduiniana.org/libraries/streaming/
                        // http://arduiniana.org/Streaming/Streaming5.zip
#include <BMP180.h>
#include <SoftwareSerial.h>
SoftwareSerial HC12(2, 3);

BMP180 barometer;
float seaLevelPressure = 102175;  // Kuwait
float fAm, fTC, fPm, fPH, fBv;
long PressureP;

char cTx[7];

long lData;
int iAI6;

static unsigned long NowMillis = 0;
static unsigned long LastMillis = 0;
static unsigned long LoopMillis = 0;

/* -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- */

void setup() {
	Serial.begin(9600);
	HC12.begin(4800);

	// String the I2C on the Arduino for communication
	// with the BMP180 sensor
	Wire.begin();
	// Creating an instance of the BMP180 sensor.
	barometer = BMP180();
  
	// Check to see if the sensor is connected.
	if (barometer.EnsureConnected())
	{
		Serial.println("Connected to BMP180."); 
		// When connected, Reset the device
		// to ensure a clean start.
		barometer.SoftReset();
		// Initialize the sensor.
		barometer.Initialize();
	}
	else
	{
		Serial.println("No sensor found.");
	}

	lData = 0;
}

/* -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP  */
 
void loop() {

	Read_BMP_Data();
	lData ++;
	TxData();  // Transmit on HC12
	delay(25);  // Allow Rx node to process data
	RunTime();  // Loop cycle time
}



/*
 * Transmit data to slave node
 * The master will transmit in the following order ...
 * Temp, Press, Alt, Batt, lData
 * < Temp , Press , Alt , Batt , lData >   
 *
 */
void TxData () {
HC12 << "<" << fTC << "," << fPm << "," << fAm << "," << fBv << "," << lData << ">" << endl;
}


/*
 * RunTime routine
 *
 */

 void RunTime() {
	LastMillis = NowMillis;
	NowMillis = millis();
	LoopMillis = NowMillis - LastMillis;

	Serial.print(" LoopTime: ");
	Serial.println(LoopMillis);
}


/*
 * Read_BMP_Data
 *
 */
void Read_BMP_Data () {

	if(barometer.IsConnected) {
	PressureP = barometer.GetPressure();
	fPm = PressureP/100.0;
	fPH = fPm*0.02953;
 }

	fAm = barometer.GetAltitude(seaLevelPressure);
	fTC = barometer.GetTemperature();

	iAI6 = analogRead(A6);  // Raw AI signal Battery voltage input
	fBv = iAI6 * 25.0 / 1024;  // Scale to max 25V input signal
}

Receiver code …

/* 
 * BMP180 HC12 communication test
 * 2017-02-05 Rev1.0
 *
 */

#include <SoftwareSerial.h>
SoftwareSerial HC12(2, 3);

static unsigned long NowMillis = 0;
static unsigned long LastMillis = 0;
static unsigned long LoopMillis = 0;

const byte numChars = 40;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
                          // variables to hold the parsed data
char messageFromPC[numChars] = {0};
long iData = 0;
long lCnt = 0;

float fTemp, fPress, fAlt, fBatt;
boolean newData = false;

/* -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- */
void setup() {
	Serial.begin(9600);
	HC12.begin(4800);
}


/* -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP  */
void loop() {
	recvWithStartEndMarkers();

	if (newData == true) {
		strcpy(tempChars, receivedChars);  // this temporary copy is necessary to protect the original data
                                           // because strtok() used in parseData() replaces the commas with \0
		parseData();  // Strip required data out of received string
		showParsedData();
		newData = false;
		RunTime();
	}
}


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

	while (HC12.available() > 0 && newData == false) {
		rc = HC12.read();
		if (recvInProgress == true) {
			if (rc != endMarker) {
				receivedChars[ndx] = rc;
				ndx++;
				if (ndx >= numChars) {
					ndx = numChars - 1;
				}
			}
			else {
				receivedChars[ndx] = '\0';
				recvInProgress = false;
				ndx = 0;
				newData = true;
			}
		}
		else if (rc == startMarker) {
			recvInProgress = true;
		}
	}
}


/* 
 * parseData() 
 * Code by Robin2
 * The master will transmit in the following order ...
 * fTemp, fPress, fAlt, fBatt, iData
 *
 */
void parseData() {  // split the data into its parts

	char * strtokIndx;  // this is used by strtok() as an index

	strtokIndx = strtok(tempChars,",");  // Get the first part of the data
	fTemp = atof(strtokIndx);
 
	strtokIndx = strtok(NULL, ",");  // this continues where the previous call left off
	fPress = atof(strtokIndx);

	strtokIndx = strtok(NULL, ",");
	fAlt = atof(strtokIndx);

	strtokIndx = strtok(NULL, ",");
	fBatt = atof(strtokIndx);

	strtokIndx = strtok(NULL, ",");
	iData = atol(strtokIndx);
	}


/*
 * showParsedData()
 * Original Code by Robin2
 *
 */
void showParsedData() {
	Serial.print(" : T ");
	Serial.print(fTemp);
	Serial.print(" : Pr ");
	Serial.print(fPress);
	Serial.print(" : Alt ");
	Serial.print(fAlt);
	Serial.print(" : Batt ");
	Serial.print(fBatt);
	Serial.print(" : iData ");
	Serial.print(iData);
 }


/*
 * RunTime
 *
 */
void RunTime() {
	LastMillis = NowMillis;
	NowMillis = millis();
	LoopMillis = NowMillis - LastMillis;
    Serial.print(" : LoopTime ");
    Serial.println(LoopMillis);
}

Hi

I am trying to do something similar to what is shown in the last post. I am trying to transmit 13 int variable values in a particular sequence and trying to parse and store them in the similar order at the receiving end. I have adopted the code show in the last post from Gryskop. But I have not had any luck yet with getting the two Arduinos to communicate.

I am using two Arduino Pro Mini 168 with each connected to an HC-12 radio. The receiver is also connected to the computer through USB to see the output on the serial monitor.

I am unable to see anything on the receiver side on the Serial monitor. Could someone please help?

Here is my transmitter code:

/*
 * HC12 BMP180 test code
 * 2017-02-05 Rev0.0
 * 
 */

#include <Streaming.h>  // http://arduiniana.org/libraries/streaming/
                        // http://arduiniana.org/Streaming/Streaming5.zip
//#include <BMP180.h>
#include <SoftwareSerial.h>
SoftwareSerial HC12(2, 3);

int val1 = 1;
int val2 = 2;
int val3 = 49;
int val4 = 102;
int val5 = 155;
int val6 = 20;
int val7 = 0;
int val8 = 0;
int val9 = 0;
int val10 = 0;
int val11 = 0;
int val12 = 0;
int val13 = 0;

char cTx[13];

static unsigned long NowMillis = 0;
static unsigned long LastMillis = 0;
static unsigned long LoopMillis = 0;

/* -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- */

void setup() {
  Serial.begin(9600);
  HC12.begin(4800);
}

/* -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP  */
 
void loop() {

//  Read_BMP_Data();
//  lData ++;
  TxData();  // Transmit on HC12
  delay(25);  // Allow Rx node to process data
  RunTime();  // Loop cycle time
}

void TxData () {
HC12 << "<" << val1 << "," << val2 << "," << val3 << "," << val4 << "," << val5 << "," << val6 << "," << val7 << "," << val8 << val9 << val10 << "," << val11 << val12 << val13 << ">" << endl;
}

void RunTime() {
  LastMillis = NowMillis;
  NowMillis = millis();
  LoopMillis = NowMillis - LastMillis;

  Serial.print(" LoopTime: ");
  Serial.println(LoopMillis);
}

The receiver code is:

/* 
 * BMP180 HC12 communication test
 * 2017-02-05 Rev1.0
 *
 */

#include <SoftwareSerial.h>
SoftwareSerial HC12(2, 3);

static unsigned long NowMillis = 0;
static unsigned long LastMillis = 0;
static unsigned long LoopMillis = 0;

const byte numChars = 60;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
                          // variables to hold the parsed data
char messageFromPC[numChars] = {0};

int val1;
int val2;
int val3;
int val4;
int val5;
int val6;
int val7;
int val8;
int val9;
int val10;
int val11;
int val12;
int val13;

boolean newData = false;

/* -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- SETUP -- */
void setup() {
  Serial.begin(9600);
  HC12.begin(4800);
}

/* -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP -- LOOP  */
void loop() {
  recvWithStartEndMarkers();

  if (newData == true) {
    strcpy(tempChars, receivedChars);  // this temporary copy is necessary to protect the original data
                                           // because strtok() used in parseData() replaces the commas with \0
    parseData();  // Strip required data out of received string
    showParsedData();
    newData = false;
    RunTime();
  }

}

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

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

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

void parseData() {  // split the data into its parts

  char * strtokIndx;  // this is used by strtok() as an index

  strtokIndx = strtok(tempChars,",");  // Get the first part of the data
  val1 = atof(strtokIndx);
 
  strtokIndx = strtok(NULL, ",");  // this continues where the previous call left off
  val2 = atof(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val3 = atof(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val4 = atof(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val5 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val6 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val7 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val8 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val9 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val10 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val11 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val12 = atol(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  val13 = atol(strtokIndx);
  }

void showParsedData() {
  Serial.print("Val 1: ");
  Serial.print(val1);
  Serial.print(" Val 2: ");
  Serial.print(val2);
  Serial.print(" Val 3: ");
  Serial.print(val3);
  Serial.print(" Val 4: ");
  Serial.println(val4);
  Serial.print("Val 5: ");
  Serial.print(val5);
  Serial.print(" Val 6: ");
  Serial.print(val6);
  Serial.print(" Val 7: ");
  Serial.println(val7);
  Serial.print("Val 8: ");
  Serial.print(val8);
  Serial.print(val9);
  Serial.print(val10);
  Serial.print(" Val 9: ");
  Serial.print(val11);
  Serial.print(val12);
  Serial.println(val13);
 }

void RunTime() {
  LastMillis = NowMillis;
  NowMillis = millis();
  LoopMillis = NowMillis - LastMillis;
    Serial.print(" : LoopTime ");
    Serial.println(LoopMillis);
}

Have you configured the HC12 to operate at 4800, as they normally run at 9600?

What does this line do?

HC12 << "<" << val1 << "," << val2 << "," << val3 << "," << val4 << "," << val5 << "," << val6 << "," << val7 << "," << val8 << val9 << val10 << "," << val11 << val12 << val13 << ">" << endl;

I suggest you put the output data into a char array and print it from the transmit program so you can see what it contains before you transmit it.

Maybe it contains an unintended end- or start-marker?

...R

mauried:
Have you configured the HC12 to operate at 4800, as they normally run at 9600?

I knew it would be something stupid like this that is tripping me :slight_smile:

It's working perfectly now. Thanks

It’s been a while since last I visited the forum.

I have been spending time refining my HC12 communication sketches. Only to find out halfway down the line that line that the modules I have was indeed HC11 and not HC12 modules. Explained why I could net get the range that the HC12 modules are supposed to get. Luckily I did have some HC12 modules in my magic box and the range achieved with the HC12 modules were much greater than with the HC11 modules.

BUT.... This thread was not about HC11 or HC12 per se, but more about the failure of data I encountered. Thanks to Robin2 and his approach, data received accuracy greatly improved.

However, there was still the odd byte or two that just seemed to “glitch” (for the lack of better description)

I then looked for ways to ensure that the data transmitted and data received was indeed valid (Checksum). Found numerous ways of doing checksum calculations but it final method I used worked the best for me. (Checksum each word, vs checksum the complete package)

I include my Transmit and receive code as well as the circuit diagrams for the two sketches.

I’m now using the same approach on my RS485 network and it works a charm.

Tx07.pdf (52.6 KB)

Tx07.ino (16.1 KB)

Rx07.ino (16.6 KB)

Rx07.pdf (39.7 KB)