Make Your Own GPS Transmitter with the HC-12 Transceiver

The initial part of this series, titled Understanding and Implementing the HC-12 Wireless Transceiver Module, demonstrates using HC-12 for long-range data exchange between two Arduino Unos. This article utilizes two HC-12 transceivers, a GPS module, an Arduino, and Google Maps in order to develop a basic tracking device.

GPS Transmitter with the HC-12 Transceiver

Item Cost More Information
HC-12 transceiver (x2) $4 Datasheet
GPS Receiver $16 Datasheet
or Adafruit GPS Logger Shield $45 Project Guide
Arduino Uno R3 (or compatible) $45 Reference

Part one of this two-part series discussed the HC-12 transceiver module and described how to hook it up to an Arduino and a power source.

In this article, we will create a remote GPS receiver that can be used to track nearby items without using a cellular network.

For further information on the transceiver module, please see the HC-12 datasheet (PDF).

Adding and Transmitting GPS

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

The HC-12s can transmit the information from a GPS receiver with no additional programming or circuitry.

You can transmit GPS coordinates to remote locations with as little as a GPS receiver, an HC-12 transceiver, and a battery. Remotely transmitted coordinates would have to be received by another HC-12 transceiver and then processed with a microcontroller or computer.

GPS Transmitter with the HC-12 Transceiver - diagram

Setting up a basic system like the one described can enable you to design a compact remote object tracker; once the object goes beyond a set area, you will receive a notification, giving you a window to locate it before losing signal. This could be utilized with a car, a pet, or even – if you’re worried about theft – the large pumpkin you’re cultivating for the state fair.

If you are in an area with clear line of sight, the transmitters will broadcast up to one kilometer, which is a 15-minute walk (or 5-minute run). The maximum range in urban areas will decrease but should remain adequate to alert you if your luggage is leaving the train station without you, or let you know where your dog ventures when he escapes from your yard.

Instead of sending GPS data directly to the HC-12, we can improve the project by transmitting it to an Arduino first. Afterward, only certain strings are able to be sent to the HC-12. Decreasing the over-the-air baud rate can be helpful in extending the range of HC-12 communication.

The SparkFun GPS module priced at $16 comes with a default transmission rate of 9600 baud, which is equivalent to an over-the-air rate of 15000 baud. Transferring GPS data at 9600 baud to Arduino, then sending specific data at 2400 baud to HC-12 will lower air rate to 5000 baud. Per the datasheet, this enhances receiver sensitivity by a maximum of 5 dB, resulting in a slight boost in range.

The SparkFun GPS receiver provides six sentences at 9600 baud: GPRMCGPVTGGPGGAGPGSAGPGSV, and GPGLL.

The GPS shield from Adafruit, which is significantly more expensive, can be programmed to transmit select sentences at all standard baud rates.

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

$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

Arduino libraries exist that enable decoding of NMEA sentences into the latitude & longitude pairs below:

 

Type UTC Time Position Speed Altitude HDOP, VDOP, PDOP Satellites
RMC 2016-09-20T21:01:54Z 33°58’53.38”N, 117°56’20.03”W 0.824 knots
VTG 0.824 knots
GGA 2016-09-20T21:01:54Z 33°58’53.38”N, 117°56’20.03”W 254.3 5
GSA 1.67, 2.66, 3.15 5
GSV 11
GSV 11
GSV 11
GLL 2016-09-20T21:01:54Z 33°58’53.38”N, 117°56’20.03”W

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 greater precision).

The following program is backward-compatible with the two programs presented in part one. It reads the NMEA sentences sent by the GPS to the Arduino, discards all but the selected sentence, and transmits the selected sentence to a remote Arduino when requested. It works with either the SparkFun GPS receiver or the Adafruit GPS logger shield, as shown below. This program allows users to remotely “ping” distant transceivers to determine their location.

GPS data from a remote transmitter received, via a pair of HC-12 transceivers, by a local Arduino
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 shield
HC-12 transceiver paired with an Adafruit GPS shield
HC-12 transceiver paired with a SparkFun GPS module
HC-12 transceiver paired with a SparkFun GPS module

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

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

One potential problem with using the Arduino UNO for this program is that the SoftwareSerial library can only “listen” to one serial port at a time—data sent to a serial port when the software isn’t “listening” on that port will be discarded. The program functions as intended during testing, but I would not consider it a robust solution. If you need multiple serial communication ports in your project, consider the Arduino Mega, or a separate chip such as the ATSAMD21.

If you are tracking a single object near your house, it is sufficient to set upper and lower limits for expected latitude and longitude values. If you are trying to determine the distance between two GPS units, you might consider implementing Vincenty’s formula on a 16-bit or 32-bit microcontroller.


/*  HC12 Send/Receive Example Program 3
    By Mark J. Hughes
    for AllAboutCircuits.com

    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.

*/

#include 

//--- 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
  }
}

Data that you collect can be converted into KML files for use in Google Maps using one of many free online converters.

KML files for use in Google Map

The above image shows variations in the logged GPS coordinates of a stationary object. The error is rather large, even for the low-cost setup used in this project—the GPS receiver sold by SparkFun, for example, claims to provide positional accuracy of 2.5 m CEP. It is likely that multipath interference made a significant contribution to the additional error.

Real-Time GPS Tracking in Google Earth

Now we will create a GPS tracker with the HC-12 and Google Earth Pro. Through experimentation, I found that tracking worked if at least the $GPGGA, $GPGSA, and $GPGLL strings were passed along to Google Earth.

The program sends GPS data to a distant receiver to track objects from a distance. It gets all sentences at 9600 baud from the computer and sends only the $GPRMC, $GPGGA, and $GPGGL sentences at 4800 baud through the HC-12. To receive and transmit the information to the computer, a different HC-12/Arduino set-up would be required.


/*  HC12 Send/Receive Example Program 4
    By Mark J. Hughes
    for AllAboutCircuits.com

    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.

*/

#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
  }
}

Here are the steps you’ll need to follow:

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

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

Note: An Arduino (or any similar microcontroller) is not necessarily needed for this to function. Connecting an HC-12 directly to a GPS module, like the Adafruit logger shield, allows for programming to transmit specific sentences at 4800 baud. Information can be transmitted to Google Earth by connecting a separate HC-12 to a computer’s serial port through a logic-level-to-RS232 converter.

Conclusion

This two-article series demonstrates that the HC-12 is a versatile and easy-to-use RF transceiver module. It is similar to the nRF24L01, but it offers the important advantage of longer range. The straightforward UART interface facilitates integration into a wide variety of systems—microcontrollers and PCs can directly communicate with the HC-12.

As this article has shown, the HC-12 is a simple solution for logging GPS data and for real-time GPS tracking.

Source: GPS Transmitter with the HC-12 Transceiver


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

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

Scroll to Top