Arduino and HC-12 Long Range Wireless Communication Module

In this Arduino tutorial we will learn how to use the HC-12 wireless serial communication module which is capable of making a long range wireless communication between multiple Arduino boards, with distances up to 1.8km. You can watch the following video or read the written tutorial below for more details.

Overview

For this tutorial I made two basic examples explaining the how to connect the HC-12 module and make a basic communication between two Arduinos and an additional example where using an accelerometer sensor at the first Arduino I wirelessly control the position of the stepper at the second Arduino.

HC-12 Wireless Serial Communication Module

HC-12 Wireless Communication Module

First let’s take a closer look at the HC-12 wireless serial port communication module. Here are some specification:

  • Its wireless working frequency band is from 433.4 MHz to 473.0 MHz
  • It has a total of 100 channels with a stepping of 400 KHz between each channel
  • Transmitting power is from -1dBm (0.79mW) to 20dBm (100mW)
  • Receiving sensitivity is from -117dBm (0.019pW) to -100dBm (10pW).

These values actually depend on the selected Serial and Over-the-Air Baud Rate as seen in the table.

HC-12 Wireless Module Receiving Sensitivity

The HC-12 module has a microcontroller which actually doesn’t have to be programmed by the user. For configuring the module we simply use AT commands, which can be sent from an Arduino, a PC, or any other microcontroller using the serial port. For entering the AT command mode we just have to set the “Set” pin of the module to a low logic level.

Arduino and HC-12

Now let’s connect the HC-12 module to the Arduino and make the first example. Here’s the circuit schematics. The operating voltage of the module is from 3.2 V to 5.5 V and for more stable work it is recommended to use a decoupling capacitor and an external power supply. However, I used the PC USB as power for all three examples in this tutorial and didn’t have any problem with it.

I connected the first module to an Arduino UNO and the second module to an Arduino MEGA, but of course, you can use any board you want.

You can get the components needed for this Arduino Tutorial from the links below:

  • HC-12 Wireless Communication Module ……….. Amazon / Banggood / AliExpress
  • Arduino Board ……………………………………………… Amazon / Banggood / AliExpress
  • Breadboard and Jump Wires ………………………… Amazon / Banggood / AliExpress

Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.

Example 01 – Arduino Code

Here’s the Arduino code for the first example, a basic communication between the two modules using the Serial Monitor.

SoftwareSerial

HC12

(

10

,

11

)

;

void

setup

()

{

Serial

.

begin

(

9600

); HC12.

begin

(

9600

); }

void

loop

()

{

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

while

(

Serial

.

available

()) { HC12.

write

(

Serial

.

read

()); } }

Code language:

Arduino

(

arduino

)

The same code is used for both Arduinos. We can connect the two Arduinos on two separate computers but also we can use a single computer.

HC-12 to PC

In that case, once we connect the first Arduino to the computer, we need to select the model and the COM port and upload the code to the Arduino. Then we connect the second Arduino and we have to start the Arduino IDE again in order to be able to select the other COM port to which our second Arduino is connected, and then upload the same code.

So once we have the two Arduino IDEs running we can start the serial monitors and test whether the communication works properly. Anything we type in the serial monitor will be sent from one to the other Arduino.

HC-12 Arduino Serial Communication Example

How the code works: So once we type something in the serial monitor and click the Send button, at the first Arduino, the while loop with the Serial.available() function will become true and using the HC12.write() function we will send the data from the serial monitor to the HC-12 module. This module will transfer the data wirelessly to the second HC-12 module, so at the second Arduino the while loop with the HC12.available() function will become true and using the Serial.write() function the data will be sent to the serial monitor.

We can use the same code for sending AT Commands and configuring the module parameters. All we have to do is connect the “Set” pin of the module to Ground or any digital pin of the Arduino and set the pin to low logic level.

HC-12 AT Command Set Pin

To test whether we have successfully enter the mode, in the serial monitor we can type “AT” and we should get a response message “OK”. There are total of 12 AT Commands, and they are used for changing various parameters like the baud rate, the channel, the transmitting power etc.  For example, if we type “AT+B38400” the baud rate of the module will be set to 38400.

AT Commands:

1. AT – Test command.

Example: Send “AT” to module, and the module returns “OK”.

2. AT+Bxxxx – Change the serial port baud rate.

Available baud rates: 1200 bps, 2400 bps, 4800 bps, 9600 bps, 19200 bps, 38400 bps, 57600 bps, and 115200 bps. Default: 9600 bps.

Example: Send “AT+B38400” to module, and the module returns “OK+B19200”.

3. AT+Cxxxx – Change wireless communication channel, from 001 to 100.

Default: Channel 001, with working frequency of 433.4MHz. Each next channel is 400KHz higher.

Example: If we want to set the module to channel 006, we need to send “AT+C006” command to the module, and the module will return “OK+C006”. The new working frequency will be 435.4MHz.

Example 02

Now let’s move the second example. Here we will use two push buttons for selecting different communication channels and see a different method of storing the incoming data.

Example 02 - HC-12 channels selecting

Note: The “Set” pins of both HC-12 modules are connected to the pins number 6 of the two Arduinos and the two buttons, at the first Arduino, to the pins 4 and 3.

First Arduino code:

SoftwareSerial

HC12

(

10

,

11

)

;

byte

incomingByte;

String

readBuffer =

""

;

int

button1State =

0

;

int

button1Pressed =

0

;

int

button2State =

0

;

int

button2Pressed =

0

;

void

setup

()

{

Serial

.

begin

(

9600

); HC12.

begin

(

9600

);

pinMode

(setPin,

OUTPUT

);

pinMode

(button1,

INPUT

);

pinMode

(button2,

INPUT

);

digitalWrite

(setPin,

HIGH

); }

void

loop

()

{

while

(HC12.

available

()) { incomingByte = HC12.

read

(); readBuffer +=

char

(incomingByte); }

delay

(

100

);

while

(

Serial

.

available

()) { HC12.

write

(

Serial

.

read

()); } button1State =

digitalRead

(button1);

if

(button1State ==

HIGH

& button1Pressed ==

LOW

) { button1Pressed =

HIGH

;

delay

(

20

); }

if

(button1Pressed ==

HIGH

) { HC12.

print

(

"AT+C001"

);

delay

(

100

);

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(

"AT+C001"

);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Serial

.

println

(

"Channel successfully changed"

);

digitalWrite

(setPin,

HIGH

); button1Pressed =

LOW

; } button2State =

digitalRead

(button2);

if

(button2State ==

HIGH

& button2Pressed ==

LOW

) { button2Pressed =

HIGH

;

delay

(

100

); }

if

(button2Pressed ==

HIGH

) { HC12.

print

(

"AT+C002"

);

delay

(

100

);

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(

"AT+C002"

);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Serial

.

println

(

"Channel successfully changed"

);

digitalWrite

(setPin,

HIGH

); button2Pressed =

LOW

; } checkATCommand(); readBuffer =

""

; }

void

checkATCommand

()

{

if

(readBuffer.startsWith(

"AT"

)) {

digitalWrite

(setPin,

LOW

);

delay

(

200

); HC12.

print

(readBuffer);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

digitalWrite

(setPin,

HIGH

); } }

Code language:

Arduino

(

arduino

)

Second Arduino code:

SoftwareSerial

HC12

(

10

,

11

)

;

byte

incomingByte;

String

readBuffer =

""

;

void

setup

()

{

Serial

.

begin

(

9600

); HC12.

begin

(

9600

);

pinMode

(setPin,

OUTPUT

);

digitalWrite

(setPin,

HIGH

); }

void

loop

()

{

while

(HC12.

available

()) { incomingByte = HC12.

read

(); readBuffer +=

char

(incomingByte); }

delay

(

100

);

while

(

Serial

.

available

()) { HC12.

write

(

Serial

.

read

()); }

if

(readBuffer ==

"AT+C001"

) {

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(readBuffer);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Serial

.

println

(

"Channel successfully changed"

);

digitalWrite

(setPin,

HIGH

); readBuffer =

""

; }

if

(readBuffer ==

"AT+C002"

) {

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(readBuffer);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Serial

.

println

(

"Channel successfully changed"

);

digitalWrite

(setPin,

HIGH

); readBuffer =

""

; } checkATCommand(); readBuffer =

""

; }

void

checkATCommand

()

{

if

(readBuffer.startsWith(

"AT"

)) {

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(readBuffer);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

digitalWrite

(setPin,

HIGH

); } }

Code language:

Arduino

(

arduino

)

Description of the codes:

So, first we need to define the pins and set the “Set” pin to high logic level in order the module to work in normal, transparent mode. With the first while loop we store the incoming data into a String variable, so we can better handle it.

while

(HC12.

available

()) { incomingByte = HC12.

read

(); readBuffer +=

char

(incomingByte); }

Code language:

Arduino

(

arduino

)

The incoming data always comes one byte at a time, so for example if we send the string “Test123” from second Arduino, this while loop will do 7 iterations. Each iteration, using the HC12.read() function we will read each incoming byte or character and add it to the String variable named “readBuffer”.

Next let’s see how we can change the communication channel using the first push button. So if we press the first push button, using the HC12.print() function we will send the string “AT+C001”  to the HC-12 module or to the second Arduino.

if

(button1Pressed ==

HIGH

) { HC12.

print

(

"AT+C001"

);

delay

(

100

);

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(

"AT+C001"

);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Serial

.

println

(

"Channel successfully changed"

);

digitalWrite

(setPin,

HIGH

); button1Pressed =

LOW

; }

Code language:

Arduino

(

arduino

)

When this string will be received at the second Arduino, we will set the HC-12 module into AT command mode, and then write the same string “AT+C001” to it which will set the module to communication channel number one.

if

(readBuffer ==

"AT+C001"

) {

digitalWrite

(setPin,

LOW

);

delay

(

100

); HC12.

print

(readBuffer);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Serial

.

println

(

"Channel successfully changed"

);

digitalWrite

(setPin,

HIGH

); readBuffer =

""

; }

Code language:

Arduino

(

arduino

)

We use the next while loop to print the response message from the HC-12 module whether the channel has been successfully changed.

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

Code language:

Arduino

(

arduino

)

Back at the first Arduino, we do the same procedure of sending the AT command to the first HC-12 module. In the same way we, using the pushing the second button, we set the communication channel number two. So using this method we can select, at any time, with which HC-12 module we will communicate.

At the end, the checkATCommand() custom function, checks whether the received message is an AT command, by checking whether the string starts with “AT”. If so, the module enters the AT command mode and executes the command.

void

checkATCommand

()

{

if

(readBuffer.startsWith(

"AT"

)) {

digitalWrite

(setPin,

LOW

);

delay

(

200

); HC12.

print

(readBuffer);

delay

(

200

);

while

(HC12.

available

()) {

Serial

.

write

(HC12.

read

()); }

digitalWrite

(setPin,

HIGH

); } }

Code language:

Arduino

(

arduino

)

HC-12 Wireless Communication: Stepper Motor Control using an Accelerometer

Now let’s take a look at the third example. Here we control the position of the stepper motor at the second Arduino, using the accelerometer module at the first Arduino.

HC-12 Wireless Communication Stepper Motor Control using an Accelerometer - Example 03

The circuit also contains a microswitch for finding the initial position of the stepper motor at 0 degrees.

You can get the components needed for this example from the links below:

  • HC-12 Wireless Communication Module ………… Amazon / Banggood / AliExpress
  • A4988 Stepper Motor Driver ………………………….. Amazon / Banggood / AliExpress
  • Stepper Motor NEMA 17 ………………………………… Amazon / Banggood / AliExpress
  • Arduino Board ……………………………………………….. Amazon / Banggood / AliExpress
  • Breadboard and Jump Wires ………………………….. Amazon / Banggood / AliExpress
  • GY-80 Board with ADXL345 Accelerometer ……… Amazon / AliExpress

Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.

Note here that I already have detailed tutorials on how to connect and use both the accelerometer and the stepper motor, so for this example I will only explain the HC-12 part of the code.

First Arduino – Transmitter code:

SoftwareSerial

HC12

(

10

,

11

)

;

float

angle;

int

lastAngle =

0

;

int

count =

0

;

int

angleSum =

0

;

int

ADXAddress =

0x53

;

int

X0, X1, X_out;

int

Y0, Y1, Y_out;

int

Z1, Z0, Z_out;

float

Xa, Ya, Za;

void

setup

()

{ HC12.

begin

(

9600

);

Wire

.

begin

();

Serial

.

begin

(

9600

);

delay

(

100

);

Wire

.

beginTransmission

(ADXAddress);

Wire

.

write

(Power_Register);

Wire

.

write

(

8

);

Wire

.

endTransmission

(); }

void

loop

()

{

Wire

.

beginTransmission

(ADXAddress);

Wire

.

write

(X_Axis_Register_DATAX0);

Wire

.

write

(X_Axis_Register_DATAX1);

Wire

.

endTransmission

();

Wire

.

requestFrom

(ADXAddress,

2

);

if

(

Wire

.

available

() <=

2

) { X0 =

Wire

.

read

(); X1 =

Wire

.

read

(); X1 = X1 <<

8

; X_out = X0 + X1; Xa = X_out /

256.0

; }

Wire

.

beginTransmission

(ADXAddress);

Wire

.

write

(Y_Axis_Register_DATAY0);

Wire

.

write

(Y_Axis_Register_DATAY1);

Wire

.

endTransmission

();

Wire

.

requestFrom

(ADXAddress,

2

);

if

(

Wire

.

available

() <=

2

) { Y0 =

Wire

.

read

(); Y1 =

Wire

.

read

(); Y1 = Y1 <<

8

; Y_out = Y0 + Y1; Ya = Y_out /

256.0

; }

if

(Y_out >

0

) { angle =

map

(Y_out,

0

,

256

,

90

,

0

); }

else

if

(Y_out <

0

) { angle =

map

(Y_out,

256

,

0

,

90

,

0

); angle =

90

- angle; }

if

(X_out <

0

& Y_out <

0

) { angle =

180

; }

if

(X_out <

0

& Y_out >

0

) { angle =

0

; }

int

angleInt =

int

(angle); angleSum = angleSum + angleInt; count++;

if

(count >=

100

) { angleInt = angleSum /

100

; angleSum =

0

; count =

0

;

if

(angleInt > lastAngle +

2

|| angleInt < lastAngle -

2

) {

Serial

.

println

(angleInt);

String

angleString =

String

(angleInt); HC12.

print

(

"s"

+ angleString +

"e"

);

delay

(

10

); lastAngle = angleInt; angleSum =

0

; count =

0

; } } }

Code language:

Arduino

(

arduino

)

Second Arduino – Receiver code:

SoftwareSerial

HC12

(

10

,

11

)

;

char

incomingByte;

String

readBuffer =

""

;

const

int

dirPin =

4

;

const

int

stepPin =

3

;

const

int

button =

2

;

int

currentAngle =

0

;

int

lastAngle =

0

;

int

rotate =

0

;

void

setup

()

{

Serial

.

begin

(

9600

); HC12.

begin

(

9600

);

pinMode

(dirPin,

OUTPUT

);

pinMode

(stepPin,

OUTPUT

);

pinMode

(button,

INPUT_PULLUP

);

delay

(

10

);

digitalWrite

(dirPin,

HIGH

);

boolean

startingPosition =

true

;

while

(startingPosition) {

digitalWrite

(stepPin,

HIGH

);

delayMicroseconds

(

200

);

digitalWrite

(stepPin,

LOW

);

delayMicroseconds

(

200

);

if

(

digitalRead

(button) ==

LOW

) { startingPosition =

false

; } }

delay

(

100

); }

void

loop

()

{ readBuffer =

""

;

boolean

start =

false

;

while

(HC12.

available

()) { incomingByte = HC12.

read

();

delay

(

5

);

if

(start ==

true

) {

if

(incomingByte !=

'e'

) { readBuffer +=

char

(incomingByte); }

else

{ start =

false

; } }

else

if

( incomingByte ==

's'

) { start =

true

; } } currentAngle = readBuffer.toInt();

if

(currentAngle >

0

&& currentAngle <

180

) { currentAngle =

map

(currentAngle,

0

,

180

,

0

,

1600

);

digitalWrite

(dirPin,

LOW

);

if

(currentAngle != lastAngle) {

if

(currentAngle > lastAngle) { rotate = currentAngle - lastAngle;

for

(

int

x =

0

; x < rotate; x++) {

digitalWrite

(stepPin,

HIGH

);

delayMicroseconds

(

400

);

digitalWrite

(stepPin,

LOW

);

delayMicroseconds

(

400

); } }

if

(currentAngle < lastAngle) { rotate = lastAngle - currentAngle;

digitalWrite

(dirPin,

HIGH

);

for

(

int

x =

0

; x < rotate; x++) {

digitalWrite

(stepPin,

HIGH

);

delayMicroseconds

(

400

);

digitalWrite

(stepPin,

LOW

);

delayMicroseconds

(

400

); } } } lastAngle = currentAngle; } }

Code language:

Arduino

(

arduino

)

Description of the codes:

So first we defining the pins and initializing the modules in the setup section. Then we read the values of the X and Y axis of the accelerometer and map them to a values from 0 to 180 degrees. The values coming from the accelerometer can sometimes be unstable or shake, so for smoothing the result I used the average value of one hundred readings.

angleSum = angleSum + angleInt; count++;

if

(count >=

100

) { angleInt = angleSum /

100

; angleSum =

0

; count =

0

;

if

(angleInt > lastAngle +

2

|| angleInt < lastAngle -

2

) {

Serial

.

println

(angleInt);

String

angleString =

String

(angleInt); HC12.

print

(

"s"

+ angleString +

"e"

);

delay

(

10

); lastAngle = angleInt; angleSum =

0

; count =

0

; } }

Code language:

Arduino

(

arduino

)

For even further smoothing I will send the new value of the angle only if it differs from the previous by 2.

Note here that when sending the angle to the HC-12 module, I’m also sending the character “s” in front, and the character “e” after, which will help me when receiving the data at the second Arduino.

At the second Arduino we wait until the start marker “s” comes, then we read the value of the angle until the end marker “e” arrive. This way we are sure that we will receive only the value of the angle.

while

(HC12.

available

()) { incomingByte = HC12.

read

();

delay

(

5

);

if

(start ==

true

) {

if

(incomingByte !=

'e'

) { readBuffer +=

char

(incomingByte); }

else

{ start =

false

; } }

else

if

( incomingByte ==

's'

) { start =

true

; } }

Code language:

Arduino

(

arduino

)

Then we convert the value to integer, and map the value from 0 to 1600 steps, which corresponds to the selected sixteenth step resolution at the A4988 stepper driver. Then we rotate the stepper motor to the current angle.

So that would be all for this Arduino tutorial. Feel free to ask any question in the comments section below.