HC-12 RF Transceiver & Arduino Nano

Created:  
Updated:   12Aug2024   21:18:26   UTC 2024-08-12T21:18:26Z
Rating:   (0 reviewsThis article has not been rated yet)

The HC-12 wireless RFRadio Frequency transceiver module can be controlled by an Arduino microcontroller board to establish two-way half-duplex wireless communication. The microcontroller can configure the HC-12 and send/receive messages over 2-Wire (RXReceiveTXTransmit) UARTUniversal Asynchronous Receiver-Transmitter.

In this tutorial, wireless text messages will be sent from a transmitting pair of Arduino Nano and HC-12 modules to a receiving pair using software serial UARTUniversal Asynchronous Receiver-Transmitter on the digital pins (D2, D3) of the Arduino Nano. The transmitting Arduino and HC-12 pair will be a stand-alone device connected to an external 5V power source. The receiving Arduino and HC-12 pair will be connected to a computer over USBUniversal Serial Bus, where a Python program on the computer will be used to read in the messages over the serial port and display them on the console.

HC-12 and Arduino Nano Setup

The contents of this tutorial include the hardware wiring setup, software Python and Pyserial setup, Arduino transmitter code, Arduino receiver code, and Computer Python code.

Setup

HC-12 Pinout

The pinout for the HC-12 module is given in the figure below. The setup in this tutorial uses the factory default firmware settings on the HC-12 module, so only the power and serial pins 1 through 4 will be used and there is no need to hook up the SET pin (connecting the SET pin to ground will put HC-12 in a configuration mode where settings can be changed).

HC-12 Pinout

Wiring

There is only one hardware UARTUniversal Asynchronous Receiver-Transmitter serial port on the Arduino Nano board that has two pins, one for receiving data (RX0) and the other for transmitting data (TX0). However, these UARTUniversal Asynchronous Receiver-Transmitter header pins (RX0, TX1) are physically connected to a USB-to-UART converter circuit for USBUniversal Serial Bus communication, so you cannot use (RX0, TX1) pins if you are already using the USBUniversal Serial Bus to send/receive data to a computer as in this tutorial.

There are other Arduino boards (e.g., Arduino Zero, MKR1000, and 101) that have multiple hardware UARTUniversal Asynchronous Receiver-Transmitter interfaces separate from the USBUniversal Serial Bus port where they can be used at the same time (in the Arduino code, the USBUniversal Serial Bus is accessed using the Serial object, while the UARTUniversal Asynchronous Receiver-Transmitter(s) are accessed with the serial objects Serial1, Serial2, and so on).

For the Arduino Nano used in this tutorial, the digital pins (D2, D3) will be used to transmit and receive data to the HC-12 using the Arduino SoftwareSerial library that provides the capability of serial communication on digital pins. The receiving and transmitting connections between the Arduino Nano and HC-12 need to be crossed as shown in the figure below.

HC-12 and Arduino Nano Wiring

Python & PySerial

Both Python and the PySerial package must first be installed on the computer to read in microcontroller data over USBUniversal Serial Bus. Also, when connecting the microcontroller to the computer with a USBUniversal Serial Bus cable, the USBUniversal Serial Bus device on the computer needs to be identified to establish a connection.

Python can be downloaded from python.org. A detailed tutorial on how to install Python on Windows, Mac, or Linux can be found at realpython.com.

The PySerial package is open source code written and maintained by Chris Liechti with the functionality to access and communicate with the serial port from Python. PySerial automatically selects the appropriate backend whether you're running on Windows, OSX (Mac), Linux, BSDBerkeley Software Distribution (BSD) operating system (possibly any POSIXPortable Operating System Interface (POSIX) is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems compliant system) or IronPython. The PySerial documentation is at pyserial.readthedocs.io and the source code can be found on GitHub.

Instructions for installing the PySerial package can be found at pyserial.readthedocs.io. It is recommended that you create a virtual environment to install PySerial in. A primer on Python virtual environments can be found at realpython.com. The command for creating a virtual environment is using the venv module is given below.

python -m venv venv

The -m flag stands for module-name. The first venv in the command is the venv module name and the second venv argument is the name of the virtual environment and the folder name it creates.

Then activate the virtual environment.

For Windows, the command is:

> .\venv\Scripts\activate

For Linux and macOS, the command is:

$ source venv/bin/activate

You can install PySerial using the following PIPPIP is a package installer for Python that allows users to install packages from the Python Package Index and other indexes. The name is a recursive acronym for "Pip Installs Packages" command.

python -m pip install pyserial

After PySerial is installed, you can verify the PySerial version with the following commands in Python:

>>> import serial
>>> print(serial.VERSION)


The computer device name is needed by PySerial to access the device. When a microcontroller is connected to the USBUniversal Serial Bus of a computer, it shows up as two devices:

HIDUSB Human Interface Device:
Human Interface Device which provides a generic interface that does not require drivers to be installed. It is limited to 64 Bytes/ms (64kB/sKilobytes Per Second) per endpoint for sending 64 byte "reports" back and forth.
CDCUSB Communication Device Class ACMAbstract Control Model:
Communication Device Class of sub-type Abstract Control Model that simulates a serial port and requires drivers (INF file) to be installed.

On a Linux machine, the USBUniversal Serial Bus CDCUSB Communication Device Class ACMAbstract Control Model is used with the device name /dev/ttyACM0. After plugging in the microcontroller into the USBUniversal Serial Bus you can see the USBUniversal Serial Bus devices listed by running the command dmesg. If a USBUniversal Serial Bus adapter is used, then these devices are usually named /dev/ttyUSB0, /dev/ttyUSB1, etc.

On a Windows PC, the Device Manager will show the microcontroller under the Ports (COMCommunication & LPTLine Print Terminal) as a USBUniversal Serial Bus Serial Device.

Arduino Transmitter Code

The Arduino Nano and HC-12 transmitter code starts out in 'standby' mode, where it waits for an incoming message to set it to 'transmit' mode.

When the transmit mode is received, a "Hello" message is transmitted every 3 seconds, with the message count number appended to determine which message was received. The message counter is an unsigned int that will go from 0 to 65536, and when it exceeds 65536 it will roll over back to 0.

The SoftwareSerial library is used to send serial data over pin 3 on the Arduino Nano to the HC-12 at a baud rate of 9600bpsbits per second. The transmission delay time is setup to be non-blocking so the board can receive incoming messages to change the operation mode (from 'transmit' to 'standby') between transmissions.

Transmitter Arduino Code (transmitter.ino)
            

Arduino Receiver Code

The Arduino Nano and HC-12 receiver code acts as a passthrough between the computer and transmitter. It reads operation mode messages from the computer USBUniversal Serial Bus serial buffer to relay to the transmitter and also reads messages from the transmitter to pass over USBUniversal Serial Bus to the computer.

The code utilizes the readBytesUntil() method to read characters from serial into a message buffer where it terminates if the '\n' terminator character is detected, the BUFFER_SIZE length has been read, or it times out with a default of 1 second (the timeout interval can be changed with setTimeout()). This function only reads the characters up to the last character before the '\n' terminator character (i.e., the '\n' terminator character itself is not copied to the buffer).

The receiver code then sends the message over USB to the computer with Serial.println(message_buffer).

Receiver Arduino Code (receiver.ino)
            

Computer Python Code

The computer Python code given below uses the PySerial library to communicate with the receiver over USBUniversal Serial Bus. You can also do this using the Serial Monitor in the Arduino IDEIntegrated Development Environment (IDE) is a software application that helps develop software code efficiently. or other software, but a custom program would offer more flexibility and capability if you want to do something with the incoming messages, such as trigger an alert notification or store the messages in a file or database.

The code below starts by sending an init message to the receiver to change the default operation mode from 'standby' to 'transmit', then runs in an infinite loop to constantly check for incoming messages that were transmitted wirelessly and prints them to the console. This program can be terminated at any time by hitting CTRL+C on the keyboard.

Computer Python Code (computer_hello_msg.py)
            

The Python code starts by importing the following libraries:

platform:
used to determine the operating system to set the serial USBUniversal Serial Bus port name accordingly
os:
used to get the filename of the code (filename = os.path.basename(__file__)) to print to the console.
serial:
the PySerial library used for serial communication between the computer and receiver (pyserial.readthedocs.io)
select:
for polling the serial buffer to detect incoming messages (docs.python.org)
time:
for delays to allow the serial buffer to fill up with the full message (docs.python.org)

The serial connection from the computer to receiver is done with a Serial() object imported from the PySerial library, which implements a standard UARTUniversal Asynchronous Receiver-Transmitter duplex serial communications protocol. The Serial() constructor arguments takes the USBUniversal Serial Bus device (e.g., port="/dev/ttyACM0") on the computer used to communicate with the receiver and the baud rate in bits per second (bpsbits per second) of this connection (e.g., baudrate=9600, which is 9600bpsbits per second, although it could be set higher if needed). This baud rate is for the USBUniversal Serial Bus connection to the receiver and does not need to match the RFRadio Frequency transceiver baud rate because they are on separate serial connections.

The next part of the initial setup is initializing the polling object. Instead of constantly checking the receiver for a response in a loop, it is more efficient to set a polling event interrupt that will hold the execution of the code (blocking) until that event occurs or a specified timeout interval has elapsed (if the timeout argument in milliseconds is provided). This can be accomplished from the select library with the method poll(). More details about the select library and poll can be found in the Python documentation at docs.python.org. A constant POLL_TIMEOUT_ACK parameter is set in milliseconds that specifies how long to wait for an acknowledgment (ACKAcknowledgment) after sending a message. The final part of the initial setup code flushes out the serial buffer with the flush() method, in case there is any remaining data in buffer from a previous run.

The main code starts by sending an init message to the receiver to change its default operation mode from 'standby' to 'transmit'. If the message was received, then the transmitter will start sending 'Hello' messages every 3 seconds to receiver to be relayed to the computer. The computer code waits for a transmitted messages from with a poll() event handler. When a message is starting to be received (i.e., characters are starting to occupy the serial buffer) from the receiver, the code moves onto a 1 second delay. This delay is needed to give enough time for the serial buffer to fill up with the complete message before reading it.

When serial data is transmitted, that information is transferred as raw bytes. A mapping between characters and bytes is needed in order to convert all the characters in a message to bytes (encode) and then convert those bytes back to characters (decode) at the receiving end. ASCIIAmerican Standard Code for Information Interchange is a simple mapping that consist of 128 characters, each uniquely represented by a different byte as integers from 0 - 127. The main limitation with ASCIIAmerican Standard Code for Information Interchange is that it does not have most of the characters in the world, which can not be represented by a single byte so we need a special representation for them. UTFUnicode Transformation Format is a universal standard for mapping Unicode encodings, defining how writing from anywhere in the world, stored in any Unicode encoding format, should be represented on different computer systems.-8 encoding has a lot of characters and this why it is the default used for Python (see docs on encode at docs.python.org). A more in-depth article on character encodings/decodings with Python can be found at realpython.com.

The serial data is read in by Python using the ser.readline() method, which returns a byte object that can be converted to a regular Python string using the .decode('utf-8', 'ignore') method where the 1st argument 'utf-8' specifies the format and the 2nd argument 'ignore' means that any errors in the message will be ignored. Any CRCarriage Return (Carriage Return) or LFLine Feed (Line Feed), \r\n, at the end of the string will be stripped off using Python's .rstrip() method. The Python code then prints the formatted string message to the console for viewing.

This Python script runs in an infinite loop that constantly checks for incoming messages, but the program can be stopped by the user hitting CTRL+C on the keyboard to trigger a keyboard interrupt exception. When this occurs the code sends a message to change the operation mode from from 'transmit' to 'standby' before closing the serial object with ser.close() and exiting the program.

An example of the output on the computer console is shown below. The transmitter sent 5 text messages that were received before the computer program was terminated by hitting CTRL+C on the keyboard.

Computer Console Output
            

Conclusion

There is a lot of room for improvement with the programs used for both the computer and Arduino boards. This tutorial was intended to be a starting point to understand how the computer and modules interact with each other, but there are some other things to consider if you plan on using this kind of setup.

One limitation with the software is no error message handling if the data packets were corrupted or not received. The HC-12 modules do not have error message handling built in like some other transceivers, such as the nRF24L01+ and RFM69, so you need to implement this in software.

The Python program on the computer can be improved by adding an interactive user input with a GUIGraphical User Interface to allow you to change the operation mode (standby/transmit) or any of the HC-12 configuration parameters without terminating the program. Ping tests could also be implemented to see if the transmitter/receiver pair are alive. If the transmitter is making measurements with a sensor, then the ability to control the measurements (sample rate, number of samples, duration, time of day, etc.) would be useful as well as recording the data to a file or database.

Related Content


HC-12 433 MHz Wireless Transceiver Module

Created:   02Jun2022   22:25:06   UTC 2022-06-02T22:25:06Z
Updated:   12Aug2024   05:23:30   UTC 2024-08-12T05:23:30Z
Rating:  (0 reviewsThis article has not been rated yet)

Overview of the HC-12 433MHz wireless transceiver module. Covers the HC-12 specs, board layout, and modes of operation.


Arduino Nano

Created:   20Nov2023   19:40:22   UTC 2023-11-20T19:40:22Z
Updated:   18Aug2024   04:04:37   UTC 2024-08-18T04:04:37Z
Rating:  (0 reviewsThis article has not been rated yet)

The Arduino Nano is a small breadboard-friendly microcontroller board based on the ATmega328 MCU. This is a hardware overview of the board that covers:

  • Specs
  • Board Layout
  • Different ways of powering the board and output power
  • Digital I/O, Analog Inputs, and Communication Interfaces (UART/I2C/SPI)
  • Accessories

HC-12 RF Transceiver and RPi Pico with MicroPython

Created:   28Jun2022   02:03:47   UTC 2022-06-28T02:03:47Z
Updated:   13Aug2024   01:11:20   UTC 2024-08-13T01:11:20Z
Rating:  (0 reviewsThis article has not been rated yet)

Tutorial on how to send wireless messages between two RPi Pico and HC-12 module pairs.

  • Hardware and Software Setup
  • RPi Pico MicroPython Code
  • Computer Python Code with PySerial
  • Computer Console Output

Products

HC-12 RF Module Products

Created:   18Jan2023   08:19:54   UTC 2023-01-18T08:19:54Z
Updated:   09May2024   03:12:24   UTC 2024-05-09T03:12:24Z

  • HC-12 433MHz RF Module
  • Pin Headers
  • 433MHz Antennas

ATmega328P Nano Board Products

Created:   05Jul2023   22:59:08   UTC 2023-07-05T22:59:08Z
Updated:   03Sep2024   23:16:57   UTC 2024-09-03T23:16:57Z

Processor:
8-bit 16MHz AVRAVR microcontrollers derives its name from its developers and stands for, Alf-Egil Bogen and Vegard Wollan RISC microcontroller, and is also known as Advanced Virtual RISC. (max speed 20MHz)
Memory:
32KB Flash, 1KB EEPROMElectrically Erasable Programmable Read-Only Memory, and 2048 bytes SRAMStatic Random Access Memory
Interface:
14x Digital I/OInput/Output with 6x PWMPulse-Width Modulation, 8x 10-bit ADCAnalog-to-Digital Converter (ADC, A/D, or A-to-D), 1x UARTUniversal Asynchronous Receiver-Transmitter,1x I2CInter-Integrated Circuit. Also referred to as IIC or I2C., and 1x SPISerial Peripheral Interface
Boards:
  • Arduino Nano
  • Compatible Boards: MakerFocus, Lafvin, Elegoo, Jameco, DFRobot

Article Rating

Sign in to rate this article

Sign In


(0) Comments

Sign in to leave a comment

Sign In