В своем проекте использую HC-12 радио модуль. Давно игрался с ним и когда настало время использовать его столкнулся с тем что я не знаю какие в нем параметры установлены. Через ПК по UART можно подключаиться но это долго и не удобно. Решил написать код, который сканирует скорость, выводит в лог на экран и сбрасывает на дефолтные настройки модуль.
Железо
- Arduino Nano
- LCD 5110Nokia (красная плата)
- HC-12
Питание на модуль и LCD - 5В
Мой LCD включает подсветку если сигнал “light/led” кидать на землю.
Собрано на макетке
Подключение указано в коде
#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <SoftwareSerial.h>
#define HC12_TX_PIN 2 // TX модуля HC-12 -> D2 Nano
#define HC12_RX_PIN 3 // RX модуля HC-12 -> D3 Nano
#define HC12_SET_PIN 4 // SET модуля HC-12 -> D4 Nano
// Инициализация дисплея: (CLK=8, DIN=9, D/C=10, CE=11, RST=12)
Adafruit_PCD8544 display = Adafruit_PCD8544(8, 9, 10, 11, 12);
SoftwareSerial hc12Serial(HC12_TX_PIN, HC12_RX_PIN);
const uint32_t bauds[] = {1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200};
const uint8_t baudsCount = 8;
char respBuf[64]; // Увеличенный буфер для приема длинных ответов (например, AT+RX)
// Функция для быстрой печати 5 строк
void updateScreen(const String& line1, const String& line2 = "", const String& line3 = "", const String& line4 = "", const String& line5 = "") {
display.clearDisplay();
display.setCursor(0, 0);
display.println(line1);
display.println(line2);
display.println(line3);
display.println(line4);
display.println(line5);
display.display();
}
// Функция чистого чтения ответа модуля без символов \r и \n
uint8_t readResponse(uint16_t timeoutMs) {
uint8_t idx = 0;
unsigned long startTime = millis();
while (millis() - startTime < timeoutMs && idx < 63) {
if (hc12Serial.available()) {
char c = hc12Serial.read();
if (c != '\r' && c != '\n') {
respBuf[idx++] = c;
}
}
}
respBuf[idx] = '\0';
return idx;
}
// Функция для красивого парсинга и вывода конфигурации на дисплей
void printFormattedConfig(const String& title, String configStr) {
display.clearDisplay();
display.setCursor(0, 0);
display.println(title); // Печатаем наш кастомный заголовок
// Ищем позиции ключевых меток в строке
int posB = configStr.indexOf("OK+B");
int posRC = configStr.indexOf("OK+RC");
int posRP = configStr.indexOf("OK+RP:");
int posFU = configStr.indexOf("OK+FU");
// 1. Вывод скорости (Baud)
if (posB != -1 && posRC != -1) {
String baud = configStr.substring(posB + 4, posRC);
display.print("Baud: "); display.print(baud); display.println("bps");
}
// 2. Вывод канала (Chan)
if (posRC != -1 && posRP != -1) {
String chan = configStr.substring(posRC + 5, posRP);
display.print("Chan: "); display.println(chan);
}
// 3. Вывод мощности (Pwr) и перевод в мВт (mW)
if (posRP != -1 && posFU != -1) {
String pwr = configStr.substring(posRP + 6, posFU);
pwr.trim();
display.print("Pwr: "); display.println(pwr);
display.print(" ");
if (pwr == "-1dBm" || pwr == "-1 dBm") display.println("(0.8mW)");
else if (pwr == "2dBm" || pwr == "2 dBm" || pwr == "+2dBm" || pwr == "+2 dBm") display.println("(1.6mW)");
else if (pwr == "5dBm" || pwr == "5 dBm" || pwr == "+5dBm" || pwr == "+5 dBm") display.println("(3.2mW)");
else if (pwr == "8dBm" || pwr == "8 dBm" || pwr == "+8dBm" || pwr == "+8 dBm") display.println("(6.3mW)");
else if (pwr == "11dBm" || pwr == "11 dBm" || pwr == "+11dBm" || pwr == "+11 dBm") display.println("(12mW)");
else if (pwr == "14dBm" || pwr == "14 dBm" || pwr == "+14dBm" || pwr == "+14 dBm") display.println("(25mW)");
else if (pwr == "17dBm" || pwr == "17 dBm" || pwr == "+17dBm" || pwr == "+17 dBm") display.println("(50mW)");
else if (pwr == "20dBm" || pwr == "20 dBm" || pwr == "+20dBm" || pwr == "+20 dBm") display.println("(100mW)");
else display.println("(unknown)");
}
// 4. Вывод режима работы (Mode) и тока потребления
if (posFU != -1) {
String mode = configStr.substring(posFU + 5);
mode.trim();
display.print("Mode: FU"); display.print(mode);
if (mode == "1") display.println(" 3.6mA");
else if (mode == "2") display.println(" 80uA");
else if (mode == "3") display.println(" 16mA");
else if (mode == "4") display.println(" 16mA");
else display.println("NaN");
}
// Резервный вывод, если строка пришла в битом формате
if (posB == -1 && posRC == -1) {
display.println(configStr);
}
display.display();
}
void setup() {
// Настройка пина SET (D4) через регистры
DDRD |= (1 << HC12_SET_PIN); // Выход
PORTD |= (1 << HC12_SET_PIN); // Исходно HIGH
// --- АППАРАТНАЯ БЛОКИРОВКА РОМАШКИ ---
DDRB |= (1 << DDB3); // Пин D11 на ВЫХОД
PORTB |= (1 << PORTB3); // Устанавливаем HIGH (запрет приема данных дисплеем)
// Инициализация дисплея
display.begin();
display.clearDisplay();
// Разрешаем дисплею принимать данные (опускаем CE в LOW)
PORTB &= ~(1 << PORTB3);
display.display();
display.setContrast(55);
display.setTextSize(1);
display.setTextColor(BLACK);
Serial.begin(9600);
delay(500);
Serial.println(F("=================================================="));
Serial.println(F(" HC-12 SETTINGS REANIMATOR START "));
Serial.println(F("=================================================="));
Serial.println(F("[SYSTEM] Welcome screen..."));
updateScreen("HC-12", "REANIMATOR", "v2.1", "Initializing..");
delay(2000);
// Перебор скоростей
bool speedFound = false;
uint32_t correctBaud = 0;
String hc12Version = "Ver: Unknown";
Serial.println(F("[SCAN] Starting brute-force scan..."));
for (uint8_t i = 0; i < baudsCount; i++) {
uint32_t testBaud = bauds[i];
Serial.print(F("[SCAN] Testing: "));
Serial.print(testBaud);
Serial.println(F(" baud..."));
updateScreen("Scanning...", "Baudrate:", String(testBaud));
hc12Serial.begin(testBaud);
delay(50);
PORTD &= ~(1 << HC12_SET_PIN);
delay(100);
while (hc12Serial.available()) { hc12Serial.read(); }
hc12Serial.print("AT");
uint8_t len = readResponse(200);
if (len >= 2 && strncmp(respBuf, "OK", 2) == 0) {
Serial.print(F("[SCAN] SUCCESS! Module responded OK at "));
Serial.println(testBaud);
correctBaud = testBaud;
speedFound = true;
while(hc12Serial.available()) hc12Serial.read();
hc12Serial.print("AT+V");
readResponse(1000);
Serial.println(respBuf);
hc12Version = "Ver: " + String(respBuf);
break;
} else {
PORTD |= (1 << HC12_SET_PIN);
delay(50);
hc12Serial.end();
}
}
if (!speedFound) {
Serial.println(F("[CRITICAL ERROR] No connection. Check the wires."));
updateScreen("No connection.", "Check the", "wires.");
while (true);
}
updateScreen("Connected!", "Baud: " + String(correctBaud), hc12Version);
delay(5000);
// Запросим настройки командой AT+RX
Serial.println(F("[SYSTEM] Requesting settings: AT+RX"));
while (hc12Serial.available()) { hc12Serial.read(); }
hc12Serial.print("AT+RX");
uint8_t len = readResponse(1000);
Serial.print(F("[RX]: "));
Serial.println(respBuf);
if (strstr(respBuf, "OK") == NULL) {
Serial.println(F("[ERROR] Command failed. No 'OK' in response."));
updateScreen("ERROR COMMAND", "Sent:", "AT+RX");
while (true);
}
String firstConfig = String(respBuf);
// Сверяемся с дефолтными настройками
if (firstConfig.indexOf("B9600") != -1 && firstConfig.indexOf("C001") != -1 && firstConfig.indexOf("+20dBm") != -1 && firstConfig.indexOf("FU3") != -1) {
Serial.println(F("[STATUS] SETTINGS IS DEFAULT"));
updateScreen("SETTINGS IS", "DEFAULT");
delay(2000);
Serial.println(F("[SYSTEM] Displaying config. Freezing."));
// ВЫЗОВ ФУНКЦИИ ФОРМАТИРОВАНИЯ ДЛЯ ТЕКУЩИХ НАСТРОЕК
printFormattedConfig("CURRENT CONFIG", firstConfig);
while (true);
}
else {
// Если настройки не дефолтные -> отправляем AT+DEFAULT
Serial.println(F("[STATUS] Custom settings found. Resetting..."));
updateScreen("SETTINGS IS", "RESETTING");
while (hc12Serial.available()) { hc12Serial.read(); }
hc12Serial.print("AT+DEFAULT");
len = readResponse(1000);
Serial.print(F("[RX]: "));
Serial.println(respBuf);
if (strstr(respBuf, "OK") == NULL) {
Serial.println(F("[ERROR] Command failed. No 'OK' in response."));
updateScreen("ERROR COMMAND", "Sent:", "AT+DEFAULT");
while (true);
}
if (strcmp(respBuf, "OK+DEFAULT") == 0) {
Serial.println(F("[SYSTEM] RESET SUCCESSFULY"));
updateScreen("RESET", "SUCCESSFULY");
delay(2000);
// После AT+DEFAULT скорость HC-12 переключается на 9600.
// Переинициализируем SoftwareSerial на дефолтную скорость 9600, чтобы успешно считать ответ.
hc12Serial.end();
delay(50);
hc12Serial.begin(9600);
delay(50);
// Запрашиваем настройки заново командой AT+RX
Serial.println(F("[SYSTEM] Requesting final settings..."));
while (hc12Serial.available()) { hc12Serial.read(); }
hc12Serial.print("AT+RX");
len = readResponse(1000);
Serial.print(F("[RX Final]: "));
Serial.println(respBuf);
// ВЫЗОВ ФУНКЦИИ ФОРМАТИРОВАНИЯ ДЛЯ СБРОШЕННЫХ НАСТРОЕК
printFormattedConfig("DEFAULT CONFIG", String(respBuf));
while (true);
}
else {
Serial.println(F("[ERROR] Unexpected response on reset."));
updateScreen("ERROR COMMAND", "Sent:", "AT+DEFAULT");
while (true);
}
}
}
void loop() {}
Если дисплей не подключать то вывод можно увидеть в консоли.
Реаниматор проверен в работе.
Подключение произведено к ArdunioNano (ATmega328P).
На фото HС-12 (а то можно подумать что это “демка”)

