Documentation

HC-12 433MHz Long-Range Wireless Serial Transceiver Module (SI4438) for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual
Documentation / HC-12 433MHz Long-Range Wireless Serial Transceiver Module (SI4438) for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual

HC-12 433MHz Long-Range Wireless Serial Transceiver Module (SI4438) for Arduino, Raspberry Pi & ESP32 | ShillehTek Product Manual

manualshillehtek

Overview

The HC-12 is the easiest long-range radio you'll ever use, because it doesn't feel like a radio at all — it feels like a very long serial cable. Wire it to a UART, and whatever bytes go into one module's RXD come out the far module's TXD, transparently, at ranges up to a kilometer-plus line-of-sight. No packet framing, no addressing, no radio registers: Serial.print() on one end, Serial.read() on the other.

Under the hood a Si44xx-family sub-GHz transceiver and an onboard MCU handle everything: 100 selectable channels across 433.4-473.0 MHz, up to 100 mW transmit power, and several FU transmission modes trading speed against range (FU3 default balances both; FU4 stretches maximum distance at low baud). Configuration happens over the same UART — pull the SET pin low and the module answers AT commands for channel, power, baud, and mode; release SET and it's transparent again.

Pair two of them for remote sensors, wireless PC links, robot telemetry and control, or a whole party-line network (every module on a channel hears every transmission — add simple message prefixes to address nodes). It runs from 3.2-5.5V, plays nicely with 3.3V and 5V logic, and asks only two things: solder on the included spring antenna (never transmit without one) and give it a solid supply, since 100 mW transmit bursts punish flimsy wiring.

At a Glance

Link Type
Transparent UART bridge
Band
433.4 - 473.0 MHz
Channels
100 (AT+Cxxx)
Range
Up to ~1 km+ LOS
TX Power
Up to 100 mW (20 dBm)
Supply Voltage
3.2 - 5.5V

Specifications

Parameter Value
Module HC-12 wireless serial (Si44xx RF + STM8 controller)
Frequency 433.4 - 473.0 MHz, 100 channels at 400 kHz spacing
TX Power 8 steps, -1 to +20 dBm (AT+P1..P8)
Sensitivity Down to -117 dBm (low-baud modes)
Serial Baud 1200 - 115200 (default 9600 8N1)
Modes FU1 power-save, FU2 ultra-low-power polling, FU3 default balanced, FU4 max-range (1200 baud)
Supply 3.2 - 5.5V; ≥100 mA capable supply recommended for TX bursts
Idle / TX Current ~16 mA idle (FU3); ~100+ mA peaks at full TX power
Config AT commands with SET pin held LOW
Antenna Spring antenna on ANT1 solder pad or U.FL socket — required
Pins VCC, GND, RXD, TXD, SET
Size 27.8 x 14.4 mm

Pinout Diagram

Bottom edge: VCC, GND, RXD, TXD, SET — the whole working interface. Top edge: the antenna options (U.FL connector at ANT1, solder pad at ANT2 for the spring antenna) plus their grounds. SET floats high internally; ground it (directly or via a GPIO) to enter AT command mode.

HC-12 433MHz wireless serial transceiver pinout diagram showing VCC GND RXD TXD SET pins and antenna pads

Wiring Guide

Cross the UART (module TXD to your RX, RXD to your TX), power it, antenna on. SET can stay unconnected for normal use — wire it to a GPIO if you want to change channels/power from code.

Arduino Wiring (SoftwareSerial)

HC-12 Pin Arduino Pin Details
VCC 5V Solid supply; add 100 uF nearby
GND GND
TXD D10 SoftwareSerial RX
RXD D11 SoftwareSerial TX
SET D7 (optional) LOW = AT mode

ESP32 Wiring (UART2)

HC-12 Pin ESP32 Pin Details
VCC VIN (5V) or 3V3 5V gives the strongest TX
GND GND
TXD GPIO 16 (RX2) HC-12 TX is 3.3V-level — safe
RXD GPIO 17 (TX2)
SET GPIO 4 (optional)

Raspberry Pi Wiring (UART0)

HC-12 Pin Pi Pin Details
VCC Pin 2 (5V)
GND Pin 6 (GND)
TXD Pin 10 (GPIO 15, RXD) 3.3V logic from module — safe
RXD Pin 8 (GPIO 14, TXD)
SET Pin 11 (GPIO 17, optional)
Tip: Enable the UART in raspi-config (shell OFF, hardware ON) so /dev/serial0 is free for the radio.

Raspberry Pi Pico Wiring (UART0)

HC-12 Pin Pico Pin Details
VCC VBUS (5V) or 3V3(OUT)
GND GND (pin 38)
TXD GP1 (UART0 RX)
RXD GP0 (UART0 TX)
SET GP2 (optional)

Code Examples

The Arduino example is a serial-monitor chat bridge (flash two boards, type on one, read on the other). The ESP32 example sends sensor-style telemetry. The Pi listens/logs, and the Pico example includes AT configuration via the SET pin.

Arduino — wireless chat

hc12_arduino.ino
// HC-12 - Arduino transparent link (chat bridge)
// TXD->D10, RXD->D11, VCC->5V | flash the same sketch on both boards

#include <SoftwareSerial.h>

SoftwareSerial hc12(10, 11);   // RX, TX

void setup() {
  Serial.begin(9600);
  hc12.begin(9600);            // HC-12 default baud
  Serial.println("HC-12 chat ready - type and press enter");
}

void loop() {
  // radio -> serial monitor
  while (hc12.available()) {
    Serial.write(hc12.read());
  }
  // serial monitor -> radio
  while (Serial.available()) {
    hc12.write(Serial.read());
  }
}

ESP32 (Arduino IDE) — telemetry sender

hc12_esp32.ino
// HC-12 - ESP32 telemetry sender (UART2)
// TXD->GPIO16, RXD->GPIO17

unsigned long counter = 0;

void setup() {
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);   // RX=16, TX=17
  Serial.println("HC-12 telemetry sender");
}

void loop() {
  // Send a labeled reading once a second (replace with real sensor data)
  float fakeTemp = 20.0 + (millis() % 10000) / 1000.0;
  Serial2.printf("NODE1,%lu,%.2f\n", counter++, fakeTemp);
  Serial.printf("sent NODE1,%lu,%.2f\n", counter - 1, fakeTemp);

  // print anything that comes back
  while (Serial2.available()) Serial.write(Serial2.read());
  delay(1000);
}

Raspberry Pi (Python) — receiver/logger

hc12_rpi.py
#!/usr/bin/env python3
# HC-12 - Raspberry Pi receiver/logger
# TXD->GPIO15, RXD->GPIO14 | Install: pip3 install pyserial

import serial, time

port = serial.Serial("/dev/serial0", 9600, timeout=1)
print("Listening for HC-12 packets... Ctrl+C to stop")

try:
    with open("hc12_log.csv", "a") as log:
        while True:
            line = port.readline().decode(errors="ignore").strip()
            if line:
                stamp = time.strftime("%Y-%m-%d %H:%M:%S")
                print(f"[{stamp}] {line}")
                log.write(f"{stamp},{line}\n")
                log.flush()
except KeyboardInterrupt:
    print("Stopped by user")

Raspberry Pi Pico (MicroPython) — with AT configuration

hc12_pico.py
# HC-12 - Pico MicroPython Example with AT config
# TXD->GP1, RXD->GP0, SET->GP2

from machine import UART, Pin
import time

uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1), timeout=300)
set_pin = Pin(2, Pin.OUT, value=1)      # HIGH = transparent mode

def at(cmd):
    """Send one AT command with SET low, return the reply."""
    set_pin.value(0)
    time.sleep_ms(50)
    uart.write(cmd + "\r\n")
    time.sleep_ms(150)
    reply = uart.read()
    set_pin.value(1)
    time.sleep_ms(80)
    return reply.decode(errors="ignore").strip() if reply else "(no reply)"

# One-time setup: channel 5, full power, confirm firmware
print("AT      :", at("AT"))          # expect OK
print("Channel :", at("AT+C005"))    # both ends must match!
print("Power   :", at("AT+P8"))      # +20 dBm
print("Version :", at("AT+V"))

counter = 0
while True:
    uart.write("PICO,{}\n".format(counter))
    print("sent PICO,{}".format(counter))
    counter += 1

    data = uart.read()
    if data:
        print("recv:", data.decode(errors="ignore").strip())
    time.sleep(1)

Frequently Asked Questions

Two modules, nothing received. Checklist?
In order: UART crossed (TXD→RX, RXD→TX) on BOTH ends; same baud on module and code (factory 9600); same channel and mode on both modules (a factory pair matches — mismatches happen after AT experiments; AT+RX prints current settings); antennas fitted; and adequate supply — a starving module transmits garbage or resets. Test the pair a few meters apart first, not across the house.
AT commands just echo back or return nothing.
SET must be held LOW the whole time you're commanding (the Pico helper does this), and AT mode always listens at 9600 regardless of the configured transparent baud. Give it ~50 ms after pulling SET low, terminate with CR/LF, and release SET afterward. If you've forgotten a module's settings entirely, AT+DEFAULT restores factory state.
How do I get maximum range?
AT+P8 (full power) and AT+FU4 (max-range mode, forces 1200 baud) on both ends, antennas vertical and matched in orientation, and — the big one — height and line of sight: a module a few meters up beats any software setting. The spring antenna is decent; a quarter-wave wire (17.3 cm) or a proper 433 MHz whip on the U.FL noticeably outperforms it.
Can more than two modules talk together?
Yes — all modules on one channel share a party line: every transmission reaches every listener. That's ideal for one-to-many broadcast; for many-to-many, prefix messages with node IDs (as the examples do) and take turns — two simultaneous transmitters garble each other. For isolated pairs, put each pair on its own channel with 5+ channels of spacing.
HC-12 vs LoRa (Ra-02) — which should I use?
HC-12 wins on simplicity: transparent serial, no libraries, five minutes to a working link — with solid ~1 km best-case range. LoRa wins on raw range and noise immunity (several km) and structured networking, at the cost of SPI, libraries, and packet management. Telemetry between two known points: HC-12. Long-haul, battery-per-year sensor networks: LoRa.
Is it legal to run at full power?
433 MHz is ISM/SRD in ITU Region 1 (Europe/Africa/Russia and many other countries) with power and duty-cycle limits that vary — the EU commonly allows 10 mW ERP unrestricted, more under specific sub-bands/duty cycles. In the US, 433 MHz sits in an amateur band. Practical guidance: keep transmissions short and infrequent, use the power you need and no more, and check your local rules for anything permanent or commercial.
Random resets or gibberish when transmitting — why?
Transmit bursts pull ~100 mA spikes; thin jumpers, breadboard rails, or an overloaded 3.3V regulator dip under it and the module (or your MCU) brownouts. Fixes: 100-470 uF capacitor right at the module's VCC/GND, short thick power leads, and 5V supply where available. This one issue explains the majority of "works close, fails far" reports.

Related Tutorials