[Solved] HC-12 433 MHz to receive a text message

Hello everyone,

(originally posted in the wrong topic at: HC-12 433MHz module problems programming - Programming Questions - Arduino Forum)

I have difficulties programming an Arduino and HC-12 433 MHz module to transmit and (more importantly) receive a readable and useable text string. In short, I need to transmit an "ALARM" or whatever string from one HC-12 to the other, and identify it by means of an Arduino to take some action.

The idea is to connect an autonomous alarm system in one building with a GSM alarm system in the other building wirelessly. In my case only one alarm activation will be allowed by the GSM Alarm manufacturer (hence, no re-arming required), but still can't get it working.

So long the Tx code is:

#include <SoftwareSerial.h>
SoftwareSerial hc12(9,11); // RX, TX
int PCNPin = 3; // dry contact of the autonomous alarm system
boolean alarmOn = false;

void setup() {
 pinMode(7,OUTPUT); //  setPin on hc12
 pinMode(PCNPin,INPUT_PULLUP); // pin connected to PCN3 (output) of the alarm system
 hc12.begin(9600); // Start the software serial port
 
 // setup of the hc12 module
 digitalWrite(7,LOW); // enter AT command mode
 hc12.print(F("AT+DEFAULT\r\n")); // 9600, CH1, FU3, (F) to bypass flash memory
 delay(100);
 digitalWrite(7,HIGH); // enter transparent mode

}

void loop() {
 if(digitalRead(PCNPin) == LOW && alarmOn == false) {
 for(int i = 0; i < 5; i++)
 {
 hc12.println("ALARM");
 delay(1000);
 }
 alarmOn = true; // After five tries (to eliminate any interference) Arduino can go to sleep
 hc12.end();
 }
}

And the draft Rx code goes:

#include <SoftwareSerial.h>
SoftwareSerial hc12(9,11); // RX, TX
int relayPin = 3;
boolean alarmOn = false;

void setup() {
 pinMode(7,OUTPUT); //  setPin on hc12
 pinMode(relayPin, OUTPUT);
 Serial.begin(9600); // Start the hardware serial port
 hc12.begin(9600); // Start the software serial port
 
 // setup of the hc12 module
 digitalWrite(7,LOW); // enter AT command mode
 hc12.print(F("AT+DEFAULT\r\n")); // 9600, CH1, FU3, (F) to bypass flash memory
 delay(100);
 digitalWrite(7,HIGH); // enter transparent mode
 
}

void loop() {
 if(hc12.available() > 0 && alarmOn == false) { //TO BE MODIFIED!!!

 Serial.write(hc12.read());
 
 if(Serial.find("ALARM")) {
 
 digitalWrite(relayPin, HIGH); // The control line of the GSM alarm system to be cut only once
 delay(5000);
 digitalWrite(relayPin, LOW);
 alarmOn = true;
 }
}
}

In fact, this is merely my raw draft, Not sure that even "hc12.println()" is the best choice...

I'm a complete beginner and don't know how to correctly receive a String with HC-12 (hope, the Tx is correct) and identify the trigger word "ALARM" in it. To keep it human-friendly I would like to maintain the word "Alarm" or the like. All I have tried so far doesn't work. Any advice would be so much appreciated! Thanks)

A link to the radio you are using would be good. Most 433 MHz radios are NOT serial devices. They are communicated with using VirtualWire (now RadioHead), not hardware or software serial.

The serial communication module is from here:

Set up according to the manual here (which is pretty simple):
http://www.seeedstudio.com/wiki/images/b/b0/HC-12_User_Manual.pdf

The challenge begins in the so-called "transparent mode"...

When I clink on your not-exactly-a-link, I get:

Access Denied
You don't have permission to access "Geekcreit® hc-12 433mhz si4463 wireless serial module wireless transceiver transmission serial communication data board remote 1000m Sale - Banggood.com" on this server.

Reference #18.7795dc17.1445945187.10b0fadb

 if(Serial.find("ALARM")) {

Since the INCOMING data is coming in through the hc12 instance, it makes no sense to search for data in the hardware serial stream/buffer.

Oh my bad, sorry! I's because i was logged in... This link below must work.

http://www.ebay.com/itm/SI4463-HC-12-433Mhz-Wireless-Serial-Port-Module-Replace-Bluetooth-for-Arduino-/131167660092

Serial.find("ALARM")

is not ok, Well. But there is no SoftwareSerial.find function in Arduino((

Maybe I need to print the received line to HardwareSerial, or introduce a "char"? But what is the correct and the simple way to do this?

Thank you for a quck reply!

But there is no SoftwareSerial.find function in Arduino((

Why do you need one? You are sending "ALARMALARMALARMALARMALARM". It is easy to collect the data into a char array, adding a NULL terminator after each character is added, and comparing the array data to "ALARM", using strcmp().

If you find "ALARM", do whatever, and dump the rest of the (uselessly redundant) serial data.

Dear PaulS, what do these letters mean?

As far as I understand, I'm sending merely "ALARM/n"... 5 times.

It is easy to collect the data into a char array, adding a NULL terminator after each character is added, and comparing the array data to "ALARM", using strcmp().

God, how can I do that?))) I've never heard of strcmp(), recall only concat() function - is it somewhere near?

If you find "ALARM"

Supposedly by simple

if(MessageToLookFor == MessageReceived){}

?

and dump the rest of the (uselessly redundant) serial data.

How to dump the useless serial data?

Yes, I've warned you I'm a newbie)

what do these letters mean?

== carriage return
== line feed

As far as I understand, I'm sending merely "ALARM/n"... 5 times.

That's \n, not /n, and you are sending both \n and \r.

God, how can I do that?

Um, maybe using strcmp()?

I've never heard of strcmp(), recall only concat() function - is it somewhere near?

No, but google can find it...

concat() is a method of the String class that you will NOT be using. strcmp() is a function.

Supposedly by simple

No. char arrays do not have operators.

How to dump the useless serial data?

Well, the best way would be to change:

 for(int i = 0; i < 5; i++)
 {
 hc12.println("ALARM");

to

   hc12.print("ALARM");

Not sending useless data means that you don't have to dump useless data.

PaulS:
Not sending useless data means that you don't have to dump useless data.

Thanks a lot, PaulS! I will try all the above soon and post the results here. Wish to believe, i'm not the only one who is stuck with this HC-12 device))

PaulS and everyone,

It's my firs time coding anything else since LED blinking))

Thanks to your advice I got the following code, which still does not work...
Could you guys please correct me or point out some mistakes?

This is the transmitter code:

#include <SoftwareSerial.h>
SoftwareSerial hc12(9, 11); // RX, TX
int PCNPin = 3; // dry contact
boolean alarmOn = false;

void setup() {
  pinMode(7, OUTPUT); //  setPin on hc12
  pinMode(PCNPin, INPUT); // pin connected to PCN3 of the alarm system
  hc12.begin(9600); // Start the software serial port

}

void loop() {
  if (digitalRead(PCNPin) == HIGH && alarmOn == false) {
    hc12.println("alarm");
    alarmOn = true;
    hc12.end();
  }
}

And the receiver code:

#include <SoftwareSerial.h>
SoftwareSerial hc12(9, 11); // RX, TX
int relayPin = 3;
boolean alarmOn = false;
char keyword[6] = "alarm";
char message[6];

void setup() {
  pinMode(7, OUTPUT); //  setPin on hc12
  pinMode(relayPin, OUTPUT);

  hc12.begin(9600); // Start the software serial port
  Serial.begin(9600); // Start the hardware serial port

}

void loop() {
  if (hc12.available() && alarmOn == false) {

    for (int i = 0; i < 6; i++)
    {
      message[i] = hc12.read();
    }
    Serial.print(message); // this is just for me to check the incoming messge
    if (strcmp(message, keyword) == 0) { // check whether they are equal

      digitalWrite(relayPin, HIGH);
      delay(5000);
      digitalWrite(relayPin, LOW);
      alarmOn = true;
      hc12.end();
      Serial.end();
    }
  }
}

Thank you in advance!

You could be using Serial to see what is happening. Does the alarm pin read high when it is supposed to?

  if (hc12.available() && alarmOn == false) {

    for (int i = 0; i < 6; i++)
    {
      message[i] = hc12.read();
    }

If there is at least one value to read, reading all 6 of them is wrong. You are sending 7 value, by the way ('a', 'l', 'a', 'r', 'm', carriage return and line feed).

You should be reading, and storing the data as it arrives.

The test for alarmOn is pointless. The sending device will only send "alarm" once.

Right now, Serial.available() will return 1 as soon as the 'a' arrives. That causes you to read 5 characters, 5 of which haven't arrived. What you get is not "alarm", so you loop again and again until the 'l' arrives...

I have changed the code using a handy manual I have found recently here:
http://forum.arduino.cc/index.php?topic=288234.0

Now I have a new issue...

The transmitter is still sending: hc12.println("ALARM"); five times (just to make sure) - that's simple.

The receiver code is:

#include <SoftwareSerial.h>
SoftwareSerial hc12(9, 11); // RX, TX

int relayPin = 3;
boolean newData = false;
boolean alarmOn = false;

const byte numChars = 10;
char messageReceived[numChars];

void setup() {

  pinMode(7, OUTPUT); //  setPin on hc12
  pinMode(relayPin, OUTPUT);

  hc12.begin(9600); // Start the software serial port

  /*
    // setup of the hc12 module
    digitalWrite(7, LOW); // enter AT command mode
    hc12.print(F("AT+DEFAULT\r\n")); // 9600, CH1, FU3, (F) to bypass flash memory
    delay(100);
    digitalWrite(7, HIGH); // enter transparent mode
  */

}

void loop() {

  static byte ndx = 0;
  char endMarker = '\n';
  char charReceived;

  if (hc12.available() > 0 && alarmOn == false) {
    while (hc12.available() > 0 && newData == false) {
      charReceived = hc12.read();
      if (charReceived != endMarker) {
        messageReceived[ndx] = charReceived;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        messageReceived[ndx] = '\0';
        ndx = 0;
        newData = true;
        if (strcmp(messageReceived, "ALARM") == 0) {
          for (int i = 0; i <= 5; i++) {
            digitalWrite(LED_BUILTIN, HIGH);
            delay(1000);
            digitalWrite (LED_BUILTIN, LOW);
            delay(1000);
            
            digitalWrite(relayPin, HIGH);
            delay(5000);
            digitalWrite(relayPin, LOW);
          }
          alarmOn = true;
        } else {
          newData = false;
        }
      }

    }
  }
}

But it ******* still doesn't work!)

I have found out, if I replace "hc12." everywhere to just "Serial." and thence check for the Serial Monitor, the Program. Might. Work.

It is very "terminator-sensitive" though - if in the Serial Monitor I choose the "newline" option at the bottom of the window (typing in \n or \r is not a good idea - see more at What do ((no line ending, newline, carriage return & both NL + CR)) do?(monitor) - Interfacing w/ Software on the Computer - Arduino Forum), the alarm will be activated as expected by typing "ALARM" in the Serial Monitor. Anything but this will be of course rejected by the "strcmp()" function, e.g. the "both CR and NL" option will do its job and the loop will continue searching for the exact string without that "cr" in the ending. Any data with the selected options of the Serial Monitor "no line ending" or "carriage return" will remain in the Serial buffer until a "newline" arrives, which is rather obvious too. Alrighty!

However, now when I change back to "hc12." I fail to understand anything, what's going on. No alarm activation, no message array forming, even "Serial.print(charReceived);" in the beginning of the loop does nothing!

P.S. Yes, both HC-12 modules work fine. At least with the bare minimum code earlier this morning they both reacted to "if(hc12.available()){do something}". I need your help again!

And as for this line:

PaulS:
The test for alarmOn is pointless. The sending device will only send "alarm" once.

I just want to know that the device has taken action in the event of an alarm and it does not try to decode anything else. Just in case, I want it to nap until manually rearmed)

Have you cross connected the software serial RX and TX lines so the RX from one device goes to the TX of the other, and vice-versa?

cattledog:
Have you cross connected the software serial RX and TX lines so the RX from one device goes to the TX of the other, and vice-versa?

The connection is according to the manual. Both Arduinos are programmed to have SoftwareSerial on pins 9 (RX) and 11 (TX), which are cross-connected to HC-12 TX and RX pins accordingly, like this:

I have soldered the wires and I'm sure, the connections are reliable.

NB: If I open a hardware serial in the "void setup()" with 9600 rate and then leave in the "void loop()" the bare:

if (hc12.available() > 0) {
Serial.write(hc12.read());

without the following character-per-character reading, upon triggering the alarm on the transmitting HC-12 the Serial Monitor of the receiver will output the desired

ALARM
ALARM
ALARM
ALARM
ALARM
ALARM

Hence HC-12 works. What should I try now? How can I collect the data correctly? Thanks!

        messageReceived[ndx] = '\0';

After this, what is in messageReceived? Debugging by guesswork is stupid.

You know, I have found something strange in here.

Let's leave alone my code for a minute, but here's what I get when I send "ALARM1" and (after a short delay(500);) "ALARM2" to HC-12 using different commands, and then send the received data to Serial Monitor of the receiver using different commands:

Transmitter Receiver Result (Serial Minitor)

hc12.print(); Serial.print(hc12.read); 657665827749657665827750
hc12.write(); Serial.print(hc12.read); 657665827749657665827750

hc12.print(); Serial.write(hc12.read); ALARM1ALARM2
hc12.write(); Serial.write(hc12.read); ALARM1ALARM2

I conclude that no matter which of the two options (.print or .write) I choose for sending, the HC-12 sends ALARM1ALARM2 as ASCII (or am I wrong?).

BUT I have always thought, it is Serial.print() that prints ASCII symbols to the serial port, whereas Serial.write() writes binary data. The table above shows quite the opposite results, isn't it?

Is it just my (way) below-zero coding skills or is it really confusing? :confused:

Back to my program, when I utilize the line

char charReceived;
charReceived = hc12.read();

to read the letter A (hc12.print("A")), what is actually being stored there? I mean actually... (A, A\0 (or with some other terminator in the same char), 65, 01000001 etc.). And how it would be Serial.printed out (or Serial.written) to the Serial Monitor, taking into account the same table above?
And hence, which terminating symbol do I need to find: \n, , 10, \0 etc. to complete the string (in my case "messageReceived[]")?

CONTINUED IN THE NEXT MESSAGE

PaulS:
Um, maybe using strcmp()?

Dear PaulS, thanks to your advice, I've tried the strcmp() function in a pilot program, and it works great!

But I include it in my bloody ALARM program, where it is supposed to compare the incoming message and the keyword (which is an "ALARM"), and it fails!

PaulS:

        messageReceived[ndx] = '\0';

After this, what is in messageReceived? Debugging by guesswork is stupid.

I believe there is still something wrong in the "char messageReceived[]", missing something or containing extra information. Here's that piece again:

while (hc12.available() > 0) {
      charReceived = hc12.read();
      if (charReceived != endMarker) {
        messageReceived[ndx] = charReceived;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      } else {
        messageReceived[ndx] = '\0';
        ndx = 0;
        Serial.println(messageReceived);

Sending an ALARM with "hc12.println("ALARM")" will make the Serial Monitor print out ALARM. But as you were saying:

PaulS:
You are sending "ALARMALARMALARMALARMALARM". It is easy to collect the data into a char array, adding a NULL terminator after each character is added, and comparing the array data to "ALARM", using strcmp().

I guess, these invisible or are somehow interfering the string and prevent the strcmp() from working properly... Maybe, the string actually reads "ALARM\0" inside? Then how do I get rid of the (or not send it at all), keeping however the number of chars in messageReceived[12] not less than 12?

Sorry for my English, I'm not a native speaker... But I'd like to finish off this program)

Maybe, the string actually reads "ALARM\0" inside?

That is why I never print anonymous data.

Serial.print("messageReceived = [");
Serial.print(messageReceived);
Serial.println("]");

will show whether there are embedded non-printing characters.

messageReceived = [ALARM]
means that there are not.

messageReceived = [ALARM
]
means that there are.

An alternative test-output

char TestMsg[] = "ALARM\r\n";

void setup() {
  Serial.begin(115200);
  dump("TestMsg", TestMsg);
  Serial.print("TestMsg = [");
  Serial.print(TestMsg);
  Serial.println("]");
}

void loop() {}

void dump(const char* title, char* ptr) {
  Serial.print(title);
  Serial.print(" = \"");
  for (char* p = ptr; *p; p++) {
    Serial.write((*p < 0x20) ? '.' : *p);
  }
  Serial.print("\" [");
  for (byte* p = (byte*)ptr; *p; p++) {
    Serial.write(' ');
    if (*p < 16) {
      Serial.write('0');
    }
    Serial.print(*p, 16);
  }
  Serial.println(" ]");
}
TestMsg = "ALARM.." [ 41 4C 41 52 4D 0D 0A ]
TestMsg = [ALARM
]