The best tools to make your project dreams come true



By All About Circuits

Using the HC-12 Transceiver to Create a GPS Transmitter

Courtesy of All About Circuits

In this project, we use a pair of HC-12 transceivers, a GPS module, an Arduino, and Google Maps to create a very simple tracking device.

HC-12 transceiver (x2)

GPS Receiver

Adafruit GPS Logger Shield

Arduino Uno R3 (or compatible)

In this project, we will create a remote GPS receiver you can use to track nearby items without using a cellular network.

If you would like more information about the transceiver module, please see the HC-12 datasheet (PDF).

Adding and Transmitting GPS

The Global Positioning System (GPS) allows a user to determine the location of objects above or on the surface of the Earth. Both GPS receivers listed at the top of this article transmit National Marine Electronics Association (NMEA) sentences, which provide a slew of information including latitude and longitude, time, altitude, speed, bearing, and other variables at 9600 baud.

The HC-12s have the ability to transmit information from a GPS receiver without any additional programming or circuitry.

GPS coordinates can be transmitted to remote locations simply with a GPS receiver, an HC-12 transceiver, and a battery. Remotely transmitted coordinates need to be received by a second HC-12 transceiver, then processed with a computer or microcontroller.

Battery GPS and HC-12

Small remote object tracker

A simple setup like one in the picture above would give you the ability to create a small remote object tracker; it can alert you when an object leaves a predefined area. You will then have a certain amount of time to track said object before the transmitter goes out of range. You could set this up to use with a vehicle or a pet.

If you are in an area with a clear line of sight, the transmitters will broadcast up to one kilometer, which is about a 15-minute walk. If you are in an urban area, the maximum range will decrease. However, it should remain adequate enough to alert you if your luggage is leaving without you, or where your dog goes the next time he escapes from your yard.

You can refine this project by passing all GPS data to an Arduino rather than directly to the HC-12. Then, only selected strings will be transmitted to the HC-12. This is especially useful if you want to increase the HC-12 communication’s range by decreasing the over-the-air baud rate.

The SparkFun GPS module runs at around $16, and has a default transmission rate of 9600 baud. This corresponds to an over-the-air rate of 15000 baud. Sending the 9600 baud GPS data to your Arduino and then transmitting only selected information to your HC-12 at 2400 baud will reduce you over-the-air rate to 5000 baud. The datasheet states that this provides a modest range increase by improving receiver sensitivity by, at most, 5 dB.

The SparkFun GPS receiver provides six sentences at 9600 baud: GPRMC, GPVTG, GPGGA, GPGSA, GPGSV, and GPGLL.

You can program the GPS shield from Adafruit to transmit select sentences at all standard baud rates. However, this is a much more expensive solution.

Below is an example of data transmission from the receiver to an Arduino. You can decode your own strings with this handy online tool.

Copy Code
$GPRMC,210154.00,A,3358.88969,N,11756.33387,W,0.824,,200916,,,A*6F
$GPVTG,,T,,M,0.824,N,1.527,K,A*2C
$GPGGA,210154.00,3358.88969,N,11756.33387,W,1,05,1.67,254.3,M,-32.6,M,,*6E
$GPGSA,A,3,13,05,21,18,29,,,,,,,,3.15,1.67,2.66*01
$GPGSV,3,1,11,05,20,044,23,10,10,223,,13,26,083,22,15,37,120,*70
$GPGSV,3,2,11,16,11,322,,18,45,224,23,20,76,043,,21,55,312,22*76
$GPGSV,3,3,11,25,23,195,17,26,27,298,,29,72,108,17*47
$GPGLL,3358.88969,N,11756.33387,W,210154.00,A,A*74

There are Arduino libraries available that enable the decoding of NMEA sentences into the latitude and longitude pairs below:

Decoding of NMEA chart

RMC (recommended minimum information), GGA (3D location and accuracy), and GLL (latitude and longitude) all include latitude, longitude, and time. GGA provides altitude, and GSA provides the dilution of precision of the reading (lower numbers indicate higher precision).

The following program reads the NMEA sentences sent by the GPS to the Arduino. It can then discard all but the selected sentence and transmit the selected sentence to a remote Arduino when requested. It can work with the SparkFun GPS receiver or the Adafruit GPS logger shield, as shown below. This program allows you to remotely "ping" distant transceivers, determining their location. 

GPS Data from a remote transmitter

GPS data from a remote transmitter received, via a pair of HC-12 transceivers, by a local Arduino

HC-12 transceiver paired with an adafruit gps

HC-12 transceiver paired with an Adafruit GPS shield

HC-12 transceiver paired with a sparkfun GPS

HC-12 transceiver paired with a SparkFun GPS module

Power supply GPS Arduino HC-12

Connect the power supply, GPS, Arduino, and HC-12 as shown in the image above.

The SparkFun GPS receiver only has three wires; the fourth signal (GPS RXD) is not required for basic functionality, so it is not made available to the user. If you use the Adafruit shield, however, the GPS RX pin is enabled, enabling you to change the refresh rate and which sentences the GPS transmits, and eliminating the need for the portion of the Arduino code at the end of the file that deletes all unwanted sentences.

A problem that could arise with using the Arduino UNO for this program is that the UNO’s SoftwareSerial library can only "listen" to one serial port at a time—any data sent to a serial port when the software isn't "listening" on that port will be ignored. The program follows its intended function during testing, but I would not consider it a robust solution. If you require multiple serial communication ports for your project, I recommend you consider the Arduino Mega or a separate chip such as the ATSAMD21.

When tracking a single object near your home, setting upper and lower limits for the expected latitude and longitude values is sufficient. For determining the distance between two GPS units, perhaps consider implementing Vincenty's formula on a 16-bit or 32-bit microcontroller.

This code will automatically detect commands as sentences that begin   with AT and both write them and broadcast them to remote receivers when requested. Changing settings on a local transceiver will also change settings on a remote receiver.

Connect HC12 "RXD" pin to Arduino Digital Pin 4

Connect HC12 "TXD" pin to Arduino Digital Pin 5

Connect HC12 "Set" pin to Arduino Digital Pin 6

Connect GPS GND and 3.3V to Arduino or separate supply

Connect GPS "RX" to Arduino Digital Pin 7 (optional)

Connect GPS "TX" to Arduino Digital Pin 8

Do not power over USB.  Per datasheet, power the HC12    with a supply of at least 100 mA current capability, and   include a 22 uF - 1000 uF reservoir capacitor.

Upload code to two Arduinos connected to two computers.

Transceivers must be at least several meters apart to work in default mode.

Copy Code
*/

#include <SoftwareSerial.h>

//--- Begin Pin Declarations ---//
const byte HC12RxdPin = 4; // "RXD" Pin on HC12
const byte HC12TxdPin = 5; // "TXD" Pin on HC12
const byte HC12SetPin = 6; // "SET" Pin on HC12
const byte GPSRxdPin = 7; // "RXD" on GPS (if available)
const byte GPSTxdPin = 8; // "TXD" on GPS
//--- End Pin Declarations ---//

//--- Begin variable declarations ---//
char byteIn; // Temporary variable
String HC12ReadBuffer = ""; // Read/Write Buffer 1 -- Serial
String SerialReadBuffer = ""; // Read/Write Buffer 2 -- HC12
String GPSReadBuffer = ""; // Read/Write Buffer 3 -- GPS

boolean serialEnd = false; // Flag for End of Serial String
boolean HC12End = false; // Flag for End of HC12 String
boolean GPSEnd = false; // Flag for End of GPS String
boolean commandMode = false; // Send AT commands to remote receivers
boolean GPSLocal = true; // send GPS local or remote flag
//--- End variable declarations ---//

// Create Software Serial Ports for HC12 & GPS
// Software Serial ports Rx and Tx are opposite the HC12 Rxd and Txd
SoftwareSerial HC12(HC12TxdPin, HC12RxdPin);
SoftwareSerial GPS(GPSTxdPin, GPSRxdPin);

void setup() {

HC12ReadBuffer.reserve(82); // Reserve 82 bytes for message
SerialReadBuffer.reserve(82); // Reserve 82 bytes for message
GPSReadBuffer.reserve(82); // Reserve 82 bytes for longest NMEA sentence

pinMode(HC12SetPin, OUTPUT); // Output High for Transparent / Low for Command
digitalWrite(HC12SetPin, HIGH); // Enter Transparent mode
delay(80); // 80 ms delay before operation per datasheet
Serial.begin(9600); // Open serial port to computer at 9600 Baud
HC12.begin(9600); // Open software serial port to HC12 at 9600 Baud
GPS.begin(9600); // Open software serial port to GPS at 9600 Baud
HC12.listen(); // Listen to HC12
}

void loop() {
while (HC12.available()) { // If Arduino's HC12 rx buffer has data
byteIn = HC12.read(); // Store each character in byteIn
HC12ReadBuffer += char(byteIn); // Write each character of byteIn to HC12ReadBuffer
if (byteIn == '\n') { // At the end of the line
HC12End = true; // Set HC12End flag to true.
}
}

while (Serial.available()) { // If Arduino's computer rx buffer has data
byteIn = Serial.read(); // Store each character in byteIn
SerialReadBuffer += char(byteIn); // Write each character of byteIn to SerialReadBuffer
if (byteIn == '\n') { // At the end of the line
serialEnd = true; // Set serialEnd flag to true.
}
}

while (GPS.available()) {
byteIn = GPS.read();
GPSReadBuffer += char(byteIn);
if (byteIn == '\n') {
GPSEnd = true;
}
}

if (serialEnd) { // Check to see if serialEnd flag is true
if (SerialReadBuffer.startsWith("AT")) { // Check to see if a command has been sent
if (SerialReadBuffer.startsWith("AT+B")) { // If it is a baud change command, delete it immediately
SerialReadBuffer = "";
Serial.print("Denied: Changing HC12 Baud does not change Arduino Baudrate");
}
HC12.print(SerialReadBuffer); // Send local command to remote HC12 before changing settings
delay(100); //
digitalWrite(HC12SetPin, LOW); // If true, enter command mode
delay(100); // Delay before writing command
HC12.print(SerialReadBuffer); // Send command to HC12
Serial.print(SerialReadBuffer); // Send command to serial
delay(500); // Wait 0.5s for reply
digitalWrite(HC12SetPin, HIGH); // Exit command / enter transparent mode
delay(100); // Delay before proceeding
}
if (SerialReadBuffer.startsWith("GPS")) {
HC12.print(SerialReadBuffer);
GPS.listen();
GPSLocal = true;
}
HC12.print(SerialReadBuffer); // Send text to HC12 to be broadcast
SerialReadBuffer = ""; // Clear buffer 2
serialEnd = false; // Reset serialEnd flag
}

if (HC12End) { // If HC12End flag is true
if (HC12ReadBuffer.startsWith("AT")) { // Check to see if a command was received
digitalWrite(HC12SetPin, LOW); // If true, enter command mode
delay(40); // Delay before writing command
HC12.print(HC12ReadBuffer); // Send incoming command back to HC12
Serial.println(HC12ReadBuffer); // Send command to serial
delay(1000); // Wait 0.5s for reply
digitalWrite(HC12SetPin, HIGH); // Exit command / enter transparent mode
delay(80); // Delay before proceeding
HC12.println("Remote Command Executed");
}
if (HC12ReadBuffer.startsWith("GPS")) {
GPS.listen();
HC12.print("Remote GPS Command Received");
GPSLocal = false;
}
Serial.print(HC12ReadBuffer); // Send message to screen
HC12ReadBuffer = ""; // Empty Buffer
HC12End = false; // Reset Flag
}

if (GPSEnd) {
// Options include GPRMC, GPGGA, GPGLL, etc...
if (GPSReadBuffer.startsWith("$GPGGA")) { // Look for target GPS sentence
if (GPSLocal) {
Serial.print("Local GPS:"); // Send to local serial port
Serial.print(GPSReadBuffer); // Send local GPS
} else {
HC12.print("Remote GPS:"); // Local Arduino responds to remote request
HC12.print(GPSReadBuffer); // Sends local GPS to remote
}
GPSReadBuffer = ""; // Delete target GPS sentence
HC12.listen(); // Found target GPS sentence, start listening to HC12 again
} else {
GPSReadBuffer = ""; // Delete unwanted strings
}
GPSEnd = false; // Reset GPS
}
}

All data collected can be converted into KML files for use in Google Maps using one of many free online converters.

Logged GPS coordinates of a stationary object

The image above shows variations in the logged GPS coordinates of a stationary object. As you can see, the error is quite large. Too large even for the low-cost setup used in this project. For example, the GPS receiver sold by SparkFun, claims to provide positional accuracy of 2.5 m CEP. Most likely, multipath interference made a big contribution to the additional error.

GPS Tracking with Google Earth in Real-Time

Now we can create a GPS tracker using the HC-12 and Google Earth Pro. My findings showed that tracking worked if at least the $GPGGA, $GPGSA, and $GPGLL strings were passed along to Google Earth.

The example program below transmits GPS data to a remote receiver that tracks remote objects. It receives every sentences at 9600 baud from the computer, then transmits only the $GPRMC, $GPGGA, and $GPGGL sentences at 4800 baud through the HC-12. In order to collect the information and transmit it to the computer, you need a separate HC-12/Arduino pair that could receive the information and transmit it to the computer.

Connect HC12 "RXD" pin to Arduino Digital Pin 4  

Connect HC12 "TXD" pin to Arduino Digital Pin 5

Connect HC12 "Set" pin to Arduino Digital Pin 6  

Connect GPS "TXD" to Arduino Digital Pin 7 (Optional)

Connect GPS "RXD" to Arduino Digital Pin 8  

Do not power over USB.  Per datasheet, power the HC12    with a supply of at least 100 mA current capability, and   include a 22 uF - 1000 uF reservoir capacitor.

Upload code to two Arduinos connected to two computers.

Transceivers must be at least several meters apart to work in default mode.

Copy Code
*/

#include &let;SoftwareSerial.h>

//--- Begin Pin Declarations ---//
const byte HC12RxdPin = 4; // "RXD" Pin on HC12
const byte HC12TxdPin = 5; // "TXD" Pin on HC12
const byte HC12SetPin = 6; // "SET" Pin on HC12
const byte GPSTxdPin = 7; // "TXD" on GPS (if available)
const byte GPSRxdPin = 8; // "RXD" on GPS
//--- End Pin Declarations ---//

//--- Begin variable declarations ---//
char GPSbyteIn; // Temporary variable
String GPSBuffer3 = ""; // Read/Write Buffer 3 -- GPS

boolean debug = false;
boolean HC12End = false; // Flag for End of HC12 String
boolean GPSEnd = false; // Flag for End of GPS String
boolean commandMode = false; // Send AT commands to remote receivers
//--- End variable declarations ---//

// Create Software Serial Ports for HC12 & GPS
// Software Serial ports Rx and Tx are opposite the HC12 Rxd and Txd
SoftwareSerial HC12(HC12TxdPin, HC12RxdPin);
SoftwareSerial GPS(GPSRxdPin, GPSTxdPin);

void setup() {
buffer3.reserve(82); // Reserve 82 bytes for longest NMEA sentence

pinMode(HC12SetPin, OUTPUT); // Output High for Transparent / Low for Command
digitalWrite(HC12SetPin, HIGH); // Enter Transparent mode
delay(80); // 80 ms delay before operation per datasheet
HC12.begin(4800); // Open software serial port to HC12
GPS.begin(9600); // Open software serial port to GPS
GPS.listen();
}

void loop() {
while (GPS.available()) {
byteIn = GPS.read();
buffer3 += char(byteIn);
if (byteIn == '\n') {
GPSEnd = true;
}
}

if (GPSEnd) {
// GPRMC, GPVTG, GPGGA, GPGSA, GPGSV, GPGLL
if (buffer3.startsWith("$GPRMC")||buffer3.startsWith("$GPGGA")||buffer3.startsWith("$GPGLL")) {
HC12.print(buffer3); // Transmit RMC, GGA, and GLL sentences
buffer3 = ""; // Clear buffer
} else {
buffer3 = ""; // Delete GSA, GSV, VTG sentences
}
GPSEnd = false; // Reset GPS flag
}
}

Follow these simple steps:

  1. Open Google Earth
  2. Select Tools -> GPS3
  3. Select the Realtime tab and click Start. Google Earth will automatically cycle through available serial ports looking for NMEA sentences. When it finds NMEA data, it will provide a location on the map.

Three separate screenshots were combined

Three separate screenshots were combined in order to show all the steps simultaneously

Note: An Arduino isn’t required for this project to work. A GPS module (like the Adafruit logger shield) that has the ability to be programmed to transmit your desired sentences at 4800 baud can be connected directly to an HC-12. Then, data can be sent to Google Earth using a second HC-12 connected to a computer's serial port via a logic-level-to-RS232 converter.

Conclusion

This project demonstrated that the HC-12 is an easy-to-use and versatile RF transceiver module. While similar to the nRF24L01, it offers the advantage of a longer range. Its straightforward UART interface allows integration into a wide variety of systems, including microcontrollers and PCs which can communicate directly with the HC-12.

The HC-12 is a simple solution for logging GPS data and for real-time GPS tracking.

 

 

Featured image courtesy of SpaceX.

Key Parts and Components

Add all Digi-Key Parts to Cart
  • 1597-1229-ND
  • 1528-1045-ND
  • 1050-1024-ND
  • 1050-1049-ND
  • 1788-1007-ND