HC-12 Radio Module

About this post

In one of my first posts I had already reported on 433 MHz radio modules, but omitted the HC-12 module. I would like to close this gap now, especially since the HC-12 module has some very positive properties. On the one hand, it is characterized by simple operation, and on the other hand, it can be used both as a transmitter and a receiver.

The article is structured as follows:

  • Technical characteristics
  • Quick start
  • How to make settings
  • Range test
  • Transmission of sensor data

Basics / Technical Properties

The HC-12 radio module is available in online shops for 6 to 9 euros. In Chinese shops it is again much cheaper, but you have to accept a few weeks delivery time there. Normally, a spiral antenna is supplied.

The module design varies. Some modules are based on the SI4463 transceiver, others on the SI4438. The former has a slightly longer range (more details here). The connections appear to be the same for all HC-12 modules.

Two HC-12 modules with spiral antenna
HC-12 module back

In advance: not everything the HC-12 module can do in terms of frequencies and ranges is allowed (in Germany – please check for your country). So don’t rejoice too soon. I will come back to that later.

Here is the most important data at a glance:

  • Frequency range: 433.4 – 473.0 MHz
  • Power consumption:
    • non-sending: depending on operating mode 80 µA – 16 mA
    • when sending: ~100 mA
    • Sleep Mode: 23 µA (own measurement)
  • Power supply: 3.2 – 5.5 volts
  • Communication with the module: serial (RX / TX)
    • Baudrate: 1200 – 115200
  • Range: maximum 1800 m
  • maximum transmitting power: 20 dBm / 100 mW
  • Transmission rate (radio): up to 250000 bps
  • Settings via AT commands (SET Pin)

An essential feature of the HC-12 module is that it has its own microcontroller, which takes care of the radio transmission. You only contact the module via the serial connection. As you will see, the handling of the modules is therefore surprisingly easy. You don’t have to deal with any radio library. 

Quick Start

Wiring

Solder the spiral antenna to the module and connect it to the microcontroller or Arduino of your choice. At Arduino Nano it could look like this:

The HC-12 module on the Arduino Nano
The HC-12 module on the Arduino Nano

The HC-12 module is controlled by Software Serial. RX and TX of the module are connected crosswise to the SoftwareSerial pins, i.e. RX to TX and TX to RX. We don’t need the SET Pin in this quick start because we use the default settings.

The set-up is identical for the transmitting and the receiving side (if you can even tell what is the transmitting side and what is the receiving side). Connect both Arduinos to the computer.

The base sketch

As an introduction, we take the modified SoftwareSerial sketch from the examples of the Arduino IDE. We choose 9600 Baud, because this is the default setting of the HC-12.

#include <SoftwareSerial.h>
SoftwareSerial hc12(10,11);

void setup() {
  Serial.begin(9600);
  hc12.begin(9600);
  Serial.println("Let's start!");
}

void loop() { 
  if (hc12.available()) {
    Serial.write(hc12.read());
  }
  if (Serial.available()) {
    hc12.write(Serial.read());
  }
}

 

Upload the sketch on the first Arduino, then you open a second instance of the Arduino IDE (so just open the Arduino IDE again without closing the first one). Select the port of the second Arduino and upload the sketch to the second Arduino as well. Now you can open a serial monitor for each of them. Then write a message in one of the input fields, click on “Send” and the message appears in the serial monitor of the other Arduino.

Output of HC12_SoftwareSerial.ino
Output of HC12_SoftwareSerial.ino

It’s that simple! The only problem is that if you live in Germany, you have just violated the legal regulations for the use of the 433 MHz frequency band. These set 10   mW as the maximum permissible transmitting power. However, the HC-12 module transmits at 100 mW in the default settings. We’ll get to see how you can select the permissible range. If you want to know more about the rules for radio use (in Germany), then I recommend this link to an easy-to-understand summary.

Settings of the HC-12 radio module

The settings are made via AT commands, which you may already know from the Bluetooth modules HC-05 and HC-06. To enter AT mode, all you have to do is connect SET to GND. Alternatively, you connect SET to an I/O pin and set it to OUTPUT and LOW. The SET pin is internally connected with a 10 kOhm pull-up resistor. If you disconnect it from GND again or switch the I/O pin to INPUT, it returns into normal radio mode. It takes about 40 milliseconds to do this.

It makes a difference whether you pull down SET to LOW during operation or already when the module is switched on:

  • Switching during operation: the module remains accessible with the settings selected for the serial connection (baud rate, data bits, parity bit, stop bit).
  • Connecting SET to GND when switching on: The baud rate for communication with the module is set to 9600 baud. Also: 8 data bits, no parity, one stop bit.

The latter method is handy if you forgot which baud rate you set on the module. Otherwise you would simply have to try it out.

Put the HC-12 module into AT mode as a first test. For the Arduino you continue to use the HC12_SoftwareSerial Sketch from above. Type in “AT” (without the quotation marks) into the input field. The module should kindly respond with “OK”.

Setting the baud rate

The following table shows which baud rates you can set for communication with the HC-12 module. The radio baud rate (“Baud Rate in the Air”) is automatically adjusted by the module.

Setting the baud rates of the HC-12 module
Table 1: Setting the baud rate of the HC-12 module

You pay for a high data rate with a low reception sensitivity. With each reduction of 6 dBm, the sensitivity is reduced to half.

The transmitting and the receiving module must have set the same baud rate. The setting is done with:

  • AT+Bx with x = 1200, 2400, 4800, etc.

The setting is always active only when you have left AT mode.

Setting the radio mode

The HC-12 module has the four radio modes: FU1, FU2, FU3 (default) and FU4:

The radio modes of the HC-12 module
Table 2: The radio modes of the HC-12 module

You set the radio mode as follows:

  • AT+FUx with x = 1, 2, 3 or 4

Radio mode FU1

FU1 is a moderate power saving mode. The HC-12 module consumes 3.6 mA (I measured 3.8 mA) as long as it does not send. You can apply all baud rates in this mode, but the radio baud rate (“in the air”) is set to 250000 bps. Thus, you have a high data rate, but only a limited range. I think a range of 100 m as stated in the data sheet is very optimistic. In addition, the range is even shorter if you reduce the transmission power to the level permitted in Germany. 

If you instruct the module to send something, it will do so with some delay. In FU1 mode, the delay is between 15 and 25 ms. 

Radio mode FU2

FU2 mode is an extreme power saving mode. According to the data sheet, the consumption drops to 80 µA. I measured even less, the consumption periodically fluctuated between 23 and 80 µA. Apparently, the module goes intermittent into sleep mode. Only 1200, 2400 or 4800 are allowed baud rates. The radio baud rate is again very high at 250000 bps and the range is accordingly limited. The delay is 500 ms.

If you switch from another mode to FU2 and a higher baud rate was set, then the baud rate is reduced to 4800. If, on the other hand, you set FU2 mode and then select a baud rate greater than 4800, your transmission will no longer work.

Radio mode FU3

FU3 is the default mode. You can select all baud rates. The radio baud rate is set automatically according to Table 1. The power consumption is quite high at 16 mA. According to the data sheet, 1000 m range can be achieved (with illegal 100 mW transmitting power). Depending on the baud rate, the delay is between 4 and 80 ms.

Radio mode FU4

This mode is designed to achieve long ranges of up to 1800 meters. The only allowed baud rate is 1200. The radio baud rate is even reduced to 500 bps. The data packages should not exceed 60 bytes and there should be a pause of 2 seconds between sending two packages.

Setting the transmitting power

Now we come to the setting of the transmitting power. The HC-12 module offers eight power levels:

Transmission power of the HC-12 module

As you can easily see, the legal limit (for Germany) is between Level 4 and Level 5. The setting is done as follows:

  • AT+Px with x = 1, 2, 3, 4, 5, 6, 7, 8

The default is 8.

Setting the channel

A nice feature of the HC-12 module is the possibility to set 100 channels, each with a frequency shifted by 400 kHz. Channel 001 uses the frequency 433.4 MHz, channel 100 uses 473.0 MHz. Unfortunately, there is again a problem here (at least in Germany) because the permissible frequency range is between 433.05 and 434.79. Thus, only channels 1 to 4 are allowed (and again: if you are in Germany).

The channels are set with:

  • AT+Cx with x = 001, 002, 003 …. 099, 100

The default is channel 001.

Setting the data format

In serial data transfer, you can set the number of bits that are transmitted per byte. Anything other than 8 would be exotic. It is followed by a parity bit – or none (non parity). If a parity bit sent, you can choose whether to check for even or odd parity. Finally, either 1, 2 or 1.5 stop bits are transmitted.

The data format is set as follows:

  • AT+Uxyz with:
    • x = number of bits, i.e. normally 8
    • y = O (odd parity check), E (even parity check), N (no parity check)
    • z = 1 (one stop bit), 2 (2 stop bits), 3 (1.5 stop bits)

The default is: 8N1.

Other settings

  • AT+SLEEP puts the module into sleep mode. It will only need 23 µA. The sleep mode starts when you leave the AT mode. The module is awakened by entering AT mode for a short time. An example sketch of this can be found below.
  • AT+DEFAULT resets all settings.
  • AT+UPDATE puts the module in a state that allows uploading a new firmware.

Querying the HC-12 module

You can not only make settings, but also query them:

  • AT+V delivers the firmware version
  • AT+Rx, with
    • x = B, C, F or P for baud rate, channel, radio mode or transmitting power
    • x = X means that all parameters are queried

This is what the answer to “AT+RX” looks like:

Querying the HC-12 module with AT+RX
Querying the HC-12 module with AT+RX

Using the sleep mode

The sleep mode is particularly suitable for modules that are only to send something from time to time, such as the data of a weather station. In principle, I have already explained above how it works. It is important that delays are added in some places.

The sample sketch has the HC-12 module send a friendly “Hello again” every 10 seconds and then sends it to sleep.

#include <SoftwareSerial.h>
SoftwareSerial hc12(10,11);
int SETPin = 5;

void setup() {
  pinMode(SETPin,INPUT);
  Serial.begin(9600);
  hc12.begin(9600);
  Serial.println("Let's start!");
}

void loop() { 
  hc12.print("Hello again");
  delay(100);
    
  pinMode(SETPin, OUTPUT);
  delay(50);
  hc12.print("AT+SLEEP");
  delay(50);
  pinMode(SETPin, INPUT);
  
  delay(10000);
  
  pinMode(SETPin, OUTPUT);
  delay(100);
  pinMode(SETPin, INPUT); 
  delay(100);
}

 

Range test

To test the range of the module, I first used the setup shown above. I operated the two Arduino Nano Boards with 9 volt batteries. The transmitting unit ran with the following simple sketch, which I probably don’t have to explain:

#include <SoftwareSerial.h>
SoftwareSerial hc12(10,11);

void setup() {
  hc12.begin(9600);
}

void loop() { 
  hc12.print("Hi Wolle");
  delay(3000);
}

 

On the receiver side I attached an LED to pin 6 of the Arduino Nano. The recipient sketch checks to see if a message has been sent. If this is the case, it checks to see if the message is correct. If so, the LED lights up for half a second. I moved away from the transmitter with the receiver until the LED stopped blinking or stopped blinking regularly. I did the experiment in an open field with direct visual contact.

SoftwareSerial hc12(10,11);
int ledPin = 6;

void setup() {
  hc12.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() { 
  String message = "";
  if(hc12.available()) {
    message = hc12.readString();
    if(message=="Hi Wolle"){
      digitalWrite(ledPin, HIGH);
      delay(500);
      digitalWrite(ledPin, LOW);
    }
  }
}

 

With the standard settings (FU3 / 9600 Baud / 100 mW transmitting power) and the spiral antenna I could bridge a distance of approx. 120 meters. This is only a fraction of the 600 meters indicated above.

Improving the range

Two hardware changes can increase the range:

  • Attach a big capacitor to the power supply.  The data sheet recommends 1000 µF. I had only 470 F in my stock. The capacitor ensures that the sudden, high power can be supplied when sending.
  • Use a better antenna. From previous experiments I know that a 17.3 cm (= 1/4 of the wavelength) long straight wire already lead to higher ranges than the spiral antennas. 

But now I wanted to go all the way and got two external 433 MHz antennas for about 15 euros each. Since they had SMA connectors, I had to get two SMA-to-IPEX adapters. Here’s what the setup looked like:

Increasing the range of the HC-12 module with external antenna
Increasing the range of the HC-12 module with external antenna

As a schematic:

Fritzing scheme

With this I achieved a range of more than 1000 meters. In the end, the limiting factor was the free view from module to module. So I cannot say with certainty whether 1800 meters are actually reachable.

It was only then that I realized that the transmission power I used was not permitted in Germany. I then did further tests with the allowed powerlevel 4). Using the spiral antenna in FU3 mode, the range dropped less than I feared to 80 meters. Due to the change to FU4 it went up again to 180 meters. And with the good antenna, I could achieve 350 meters. 

I also tested the modules with the SI4438 chip and could not find any difference to the SI4463 based modules. However, I only tried one parameter set.

Here is a summary of all test results:

Results of the HC-12 range tests
Results of the HC-12 range tests

In my house I could easily send messages through two walls and a ceiling using FU3 / 9600 Baud / Spiral Antenna / Powerlevel 4.

Check what you really need and play around a bit. Do not overdo it with the transmitting power. After all, you also wouldn’t want your 433 MHz applications to be disrupted by transmitters of your neighbors!

Transmission of sensor data

Finally, for the less experienced, a guide on how to transfer sensor data. More specifically, it’s about how you build strings from integers and floats, send them, and convert them back on the receiver side.

The transmitter providing the data (server) converts the data using String(Zahl) into strings that are linked together, but separated by non-numbers (asterisks in this example). If the transmitting unit receives a request, the string is sent. The sketch sends fixed values. You would replace that with sensor data or something similar.

#include <SoftwareSerial.h>
SoftwareSerial hc12(10,11);

void setup() {
  Serial.begin(9600);
  Serial.println("Let's start!");
  hc12.begin(9600);
}

void loop() { 
  String requestString = "";
  if (hc12.available()) {
    requestString = hc12.readString();
    Serial.println(requestString);
  }
  if(requestString == "request"){
    String dataString = "";
    createDataString(dataString);
    Serial.println(dataString);
    hc12.print(dataString);
  }
}

void createDataString(String &data){
  int sensor1 = 746;
  int sensor2 = 8295;
  float sensor3 = 41.84;
  data = "*" + String(sensor1) + "*" + String(sensor2) + "*" + String(sensor3); 
}

 

On the receiver side (client) I have installed a button that triggers the request.

After a short time, the client receives the response from the server. To extract the numbers from the incoming string, there are the very useful functions parseInt() and parseFloat(). These functions analyze the incoming characters. They wait until digits occur, collect them until the next non-digit (except for the decimal point at parseFloat()), and convert these numbers into an integer or float value.

#include <SoftwareSerial.h>
SoftwareSerial hc12(10,11);
int requestPin = 7;

void setup() {
  pinMode(requestPin, INPUT);
  Serial.begin(9600);
  hc12.begin(9600);
  Serial.println("Let's start!");
}

void loop() { 
  int sensorData1 = 0;
  int sensorData2 = 0;
  float sensorData3 = 0.0; 
  
  if (hc12.available()){
    sensorData1 = hc12.parseInt();
    sensorData2 = hc12.parseInt();
    sensorData3 = hc12.parseFloat();
    
    Serial.print("Sensor 1: ");
    Serial.println(sensorData1);
    Serial.print("Sensor 2: ");
    Serial.println(sensorData2);
    Serial.print("Sensor 3: ");
    Serial.println(sensorData3);  
  }
  if(digitalRead(requestPin)) {
    delay(300); // debounce
    hc12.print("request"); 
  }
}

 

Here’s what the output looks like on the transmitter and the receiver side:

Output of HC12_Data_Server.ino / HC12_Data_Client.ino

Acknowledgement

The two males on the post picture are from Peggy and Marco Lachmann-Anke. I owe the paragraph symbol to Michael Schwarzenberger. Everything as usual from Pixabay.

The HC-12 module as Fritzing component was created by   Estevéo Trabbold (stvz) on fritzing.org.

Leave a Reply

Your email address will not be published. Required fields are marked *