Reading HC-12 is blocking code

Hi all,

I have a question about why reading the HC-12 is blocking the code.
I try to count pulses by pull down D12 while receiving data at the same time from the HC-12 module.

The pulse counter and HC-12 module are working well separately. But when combined and the HC-12 module is receiving data the code is blocking and not count all pulses. When the HC-12 module is not receiving data the code doesn't block.

The HC-12 module is receiving data every to seconds.

The code:

////////////Transmitter////////////
#include <SoftwareSerial.h>
SoftwareSerial HC12(9, 10); // HC-12 TX Pin, HC-12 RX Pin

////////////////////// HC12 incoming/////////////////////////
String payload;
char attributes[6];

//////////////////////Pulse Counter///////////////////////////////////////////////////////
byte pulseDown =0;
byte pulseCounter =0;
int pulseMessage;
int oldPulseMessage;
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 10;    // the debounce time; increase if the output flickers

void setup() {
  Serial.begin(9600);           //  setup serial
  HC12.begin(9600);               // Serial port to HC12 
  pinMode(12, INPUT_PULLUP);    // Pull-up resistor for pulsemeter
}

void loop() {
if (HC12.available()) {             // If HC-12 has data
      payload = HC12.readString();
      payload.toCharArray(attributes, 6);
      Serial.println(attributes);
  }
    pulse();    //checks pulse via D12 and count them
}

void pulse() {
  
  if (digitalRead(12)) //button up
  {
    pulseDown=0;
    pulseMessage = 0;
    lastDebounceTime = millis(); //set the current time
  }
  
  else  //button down
  {  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (!pulseDown) //button was previously up
    {
      pulseCounter++;
      pulseDown=1;
      pulseMessage = 1;
      lastDebounceTime = millis();
      Serial.println(pulseCounter);
    }
  }
    if (pulseMessage != oldPulseMessage){
    oldPulseMessage = pulseMessage;
  }
  
/*  if (pulseCounter >= 20){
pulseCounter = 0;
Serial.println("PulseCounter reset");
  }
}*/
}
}

If anyone has a solution, I'd love to hear it.
Thank you in advance.

Duuk

If this if block executes, it takes several milliseconds (1 millisecond per character) to collect and print the result, at the glacial rate of 9600 Baud. Then it checks for a pulse.

if (HC12.available()) {             // If HC-12 has data
      payload = HC12.readString();
      payload.toCharArray(attributes, 6);
      Serial.println(attributes);
  }

I suggest that you up the output serial Baud rate to something more reasonable, like 115200. Also, you should avoid using Strings. On AVR based Arduinos, Strings cause memory corruption and program crashes.

See the Serial Input Basics tutorial on this forum.

Unlikely with SoftwareSerial even if the module supported it.

This is the "output serial" to which I refer. It is not SoftwareSerial.

Serial.begin(9600); // setup serial

I recommend to restructure that code, getting characters from the HC-12 one by one, checking for pulses in the meantime, and printing only when a complete line has been input.

depending on the microcontroller you are using softwareserial can receive reliably only up to 19200 baud.

So you should post what exact type of microcontroller you are using.

For counting the pulses it might be an advantage use an interrupt for that.
receiving serial input uses an interrupt too.

Though I don't know which interrupt has a higher priority.
Also depends on the microcontroller

the standard readString()-function is indeed blocking and even has a timeout
The SafeString-library which can be installed with library-manager has non-blocking receive-functions even for strings

What is the maximum frequency of the pulses you want to detect?

best regards Stefan

Instead of waiting for the entire string just accumulate it one character at a time if HC12.available() is true. Blocking problem solved.

I changed the baudrade to 115200 and it's faster but still blocking. I didn't know that strings causing memory corruption on AVR based Arduinos.

The Serial input basics is very helpfull especially the non blocking examples.

Don't confuse strings and Strings.

Hi Stefan

I'm using an Arduino Nano clone. I never used interrupts before.
As @jremington said is that stings can cause memory corruption so I maybe get rid of the Strings.
Or is the SafeString-libary really safe? :slight_smile:

The frequency is not really high with 1 pulse in +-4 seconds.

What's the difference?

Yes SafeString is really safe to use. I have tested it explicitly trying to store too much characters in a SafeString. SafeString just drops the too much characters
SafeString also offers non-blocking receiving of serial characters

If the shortest time between two pulses is 3 seconds then interrupts are not needed.
This can easily be handled by polling.
Polling: code "asks" for input with each iteration of void loop()

Interrupts: does what the name says interrupt normal code-execution what ever the code is doing atthe moment. Serve interrupt go on with normal code-execution.

What is the minimum pulse-length?

Great! I will check out the libary.

10ms

Simply, a String is an instance of the String class. A lot of stuff goes on automatically and invisibly.
A string is simply a char array, with a zero char as a terminator.

Both have pitfalls.

and almost all of the pitfalls do not occur when using SafeString.
But of course like with everything the "pitfall" of SafeString is it needs some additional memory

With polling for pulses:
If the pulse is 10 milliseconds short this means your loop has to run though all the code inside loop once every 5 milliseconds to discover the pulse .
.
using interrupts:
Interrupts are used for such short pulses. And in your case processing the pulse is very easy.
You just have to increase a variable by 1 and set a flag-variable "newPulseDetected".

If the shortest time between two pulses is 3 seconds long. Your main-code has 3 seconds time to check "newPulseDetected" ? and if yes reset flag-variable "newPulseDetected" and do what is needed

best regards Stefan

And they're widely-known and taught, and well-supported?

Whenever I had a question the author of the library answered me
and in addition what I have not experienced with string (=arrays of char) and Strings is
that 90% of the users would have explained the pitfalls for beginners.
90% of the users here post code with even small comments

So the "support" for string/Strinsg is if a bug has occured and the new user is asking why
only then - in most cases - the pitfalls are explained

A simple "no" would have sufficed

Hello duukharteveld

How does the data set look like?
Do you have a data logging available?

@duukharteveld,
There is no need for SafeString, String, or Interrupts in this project. The problem is that your handling of the HC-12's data stream is poorly structured. You have already been advised what's necessary to fix it. You must read characters coming from the HC-12 one at a time as they available and store them in an array of char. This is nonblocking. Only attempt to process the data when an entire message becomes available. @jremington advised you to study the Serial Input Basics tutorial. That's a great place to start.

Finally, if this were my project, I'd implement it on hardware that didn't require use of SoftwareSerail. There are plenty of boards in the Arduino Ecosystem that have multiple hardware UARTS just for this purpose. Move to one of them.

Hi gfvalvo,

I studied the Serial Input Basics and found a non blocking example. It's working like a charm.
Thank you all!

The nano has UART and will use it. The first code I found for the HC-12 module used the softwareSerial so I did also.