Integration with HC-12 433mhz Transceiver

Has anyone used one of these or is it possible to integrate this transceiver into ESPhome.

A quick search on ESPhome does not show anything.

Thanks

Are you sure this does what you think it does?

They seem to work in Sender/Receiver pairs, with whatever you send on the sender side through the UART interface received on the other side…

It won’t replace a 433MHz remote.

Correct. Or they can also be used in a multicast or mesh configuration. Basically they’re transceiver chips providing only the RF layer and transparent data transport. You will have to write your own protocol on top of that (error correction, retransmit, Node IDs, routing, etc).

They’re excellent devices btw, the range is really good.

1 Like

Yes I know, but i’m just trying to get a simple 2 way 433mhz link for a reed switch to trigger a relay. The reason I chose the HC-12 was its range and performance.

However, it seems like a mission to work with.

Still have not had any luck getting it up and running on my esp8266 D1 mini board :frowning:

You could go with a LoRa transceiver, you would get a very good range, and there are boards combining already an ESP32 and LoRa transceiver.

Thanks, can you please give me some examples of some LoRa board that are compatible with esphome?

Thanks

i am using this, actually quite easy once configured and delimited string that is sent:

example of single message: “n1:n2:n3;”

n1 - device id
n2 - sensor id on this device
n3 - sensor data

message parts delimied with :
end of message announced with ;

these are transceivers, so easy to think of some sort of simple protocol to add a message id and confirming that message was received.

from the device in remote location i am transmitting info like:

  • booting of the device once completed, uptime (to monitor device reboots)
  • periodic update (just to know the device is alive and in range)
  • door sensor state (actually this is my alarm :D)
  • dht11 sensor temp & humidity

transmitting part (called from various places as scripts):

script:

  - id: transmitt_info_boot_complete
    mode: restart
    then:      
      - uart.write: 
          id: uart_bus          
          data: !lambda |-            
            std::string mss = String(  "1:0:1;"  ).c_str();
            return std::vector<unsigned char>(mss.begin(), mss.end());

  - id: transmitt_info_interval_update
    mode: restart
    then:      
      - uart.write: 
          id: uart_bus          
          data: !lambda |-            
            std::string mss = String(  "1:0:2;"  ).c_str();
            return std::vector<unsigned char>(mss.begin(), mss.end());

  - id: transmitt_info_door_sensor_update
    mode: restart
    then:      
      - uart.write: 
          id: uart_bus          
          data: !lambda |-            
            std::string mss = String(  "1:0:3;"  ).c_str();
            return std::vector<unsigned char>(mss.begin(), mss.end());

  - id: transmitt_dht11
    mode: restart
    then:
      # data: "device_id_hc12:sensor_id:sensor_data;"
      # dht11 temperature & humidity sensor
      - uart.write: 
          id: uart_bus          
          data: !lambda |-            
            std::string mss = (  "1:1:" + String(id(dht11_temp).state) + "!" + String(id(dht11_hum).state) + ";"  ).c_str();
            return std::vector<unsigned char>(mss.begin(), mss.end());

  # door sensor
  - id: transmitt_door_state
    mode: restart
    then:      
      - uart.write:
          id: uart_bus
          data: !lambda |-
            std::string mss = String("1:2:1;").c_str();
            if ( id(contracton).state == false ) {
              mss = String("1:2:0;").c_str();
            }            
            return std::vector<unsigned char>(mss.begin(), mss.end());  
      

  - id: transmitt_uptime
    mode: restart
    then:
      - uart.write: 
          id: uart_bus          
          data: !lambda |-            
            std::string mss = (  "1:3:" + String(id(device_uptime).state) + ";"  ).c_str();
            return std::vector<unsigned char>(mss.begin(), mss.end());

as you can see, the door sensor sends state 0 or 1, and is listed as a sensor 2 on a device 1

dht11 it additionally delimit temperature and humity with an !
“1:1:dht11_temp!dht11_hum;”

as you can see i use a lot of datatype converting, careless use of strings etc, but im not an expert on c, so probably better way to do this… it does work however flawlesly for about a year now, so not that heavy for esp8266 i am runnings this on, i suppose…

and receiving part (the hub that receives all my “clients” data - even though technically all HC12 devices are transceivers, as mentioned):

uart:
  id: uart_bus
  rx_pin: GPIO6
  tx_pin: GPIO7
  baud_rate: 9600
  debug:
    direction: BOTH
    dummy_receiver: true
    after:
      delimiter: ";"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          int deviceUID=0;
          int sensorUID=0;
          String sensor_data="";
          
          std::string str(bytes.begin(), bytes.end());

          
          ESP_LOGD("HC12", "received something...");
          if (sscanf(str.c_str(), "%d:%d:%s", &deviceUID, &sensorUID, &sensor_data ) == 3 ) {
            
            if(deviceUID==1) {
              ESP_LOGD("HC12", "received device_1 data");

              // sensorUID=1 is dht11
              if ( sensorUID == 1 ) {
                float sensor_temp=0;
                float sensor_hum=0;
                if (sscanf(sensor_data.c_str(), "%f!%f", &sensor_temp, &sensor_hum ) == 2 )
                {
                  id(device1_temp).publish_state(sensor_temp);
                  id(device1_hum).publish_state(sensor_hum);
                  ESP_LOGD("HC12", "received device1 temperature and humidity");
                }
              }

              // sensorUID=2 is door sensor
              if ( sensorUID == 2 ) {
                int sensor_door=-1;
                if (sscanf(sensor_data.c_str(), "%d", &sensor_door) == 1 )
                {
                  ESP_LOGD("HC12", "received device1 door");
                  if (sensor_door==1) {
                    id(device1_door).publish_state(true);
                    ESP_LOGD("HC12", "received device1 door - ON");
                  }
                  if (sensor_door==0) {
                    id(device1_door).publish_state(false);
                    ESP_LOGD("HC12", "received device1 door - OFF");
                  }                  
                }
              }

              // sensorUID=3 uptime
              if ( sensorUID == 3 ) {
                int sensor_uptime=-1;
                if (sscanf(sensor_data.c_str(), "%d", &sensor_uptime) == 1 )
                {
                  ESP_LOGD("HC12", "received device1 uptime");                  
                  id(device1_uptime).publish_state(sensor_uptime);                                   
                }
              }

              // sensorUID=4 is cpu temp
              if ( sensorUID == 4 ) {
                float sensor_temp=0;
                if (sscanf(sensor_data.c_str(), "%f", &sensor_temp) == 1 )
                {
                  id(device1_cpu_temp).publish_state(sensor_temp);
                  ESP_LOGD("HC12", "received device1 device cpu temperature");
                }
              }



              // sensorUID=0 info
              if ( sensorUID == 0 ) {
                int device_info=-1;
                if (sscanf(sensor_data.c_str(), "%d", &device_info) == 1 )
                {
                  ESP_LOGD("HC12", "received device info");                  
                  if (device_info==1) {
                    id(device1_device_info).publish_state("boot_complete");
                    ESP_LOGD("HC12", "received device1 info - boot complete");
                  }
                  if (device_info==2) {
                    id(device1_device_info).publish_state("interval_update");
                    ESP_LOGD("HC12", "received device1 info - interval update");
                  }
                  if (device_info==3) {
                    id(device1_device_info).publish_state("door_sensor_update");
                    ESP_LOGD("HC12", "received device1 info - door sensor update");
                  }
                }
              }
            }


and on properly recognized string, it publishes data to the local “hub” sensors, from where HA reads it through wifi

i think it would work reliably in my scenario with using strings alltogether, instead of numering for ids of devices or sensors - that would help readability of the code, but would probably be more prone for intereferences and increase the length of the transmission itself

This is exactly what I need but I struggle with yaml files. I cant compile the receiver file get errors in the Lambda section any chance you could post the complete ymal file

Yea, no problem

this is simplified version
so if a recognizable transmission is received - a led will flash, and a HUB will answer with the OTA status for clients (client is waiting for an answer some time, and if receives OTA mode - will fire up WiFi and try to connect

one thing to notice - if the HC21 transmitted string was too long - it was forcing my HUB to reboot with an overflow message, i added the
rx_buffer_size: 1024
however this does not seem to help - so just in case, start with short strings to check how does that work for you


substitutions:
  device_name: hub-hc12-test


esphome:
  name: ${device_name}
  friendly_name: ${device_name}


esp32:
  board: lolin32
  framework:
    type: arduino


logger:


api:
  encryption:
    key: "syk******************G1y0gpf5gi0="


ota:
  password: "52b***********64e7"


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  ap:
    ssid: "*******-Hc12-Test"
    password: "ro********R"


captive_portal:


web_server:
  port: 80



text_sensor:
  - platform: template
    name: "Lily_1 wakeup reason"
    id: lily_1_wakeup_reason



sensor:
  - platform: uptime
    name: "uptime"
    id: "device_uptime"
    update_interval: 1min
    entity_category: "diagnostic"

  - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
    name: "WiFi dB"
    id: wifi_signal_db
    update_interval: 1min
    entity_category: "diagnostic"

  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi %"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"
    device_class: ""

 # HC12 sensors data
  - platform: template
    name: "Lilly_1 Temperature"
    id: "lilly_1_temp"
    unit_of_measurement: "°C" 
    device_class: "temperature" # https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes
    state_class: "measurement"
    accuracy_decimals: 1
  
  - platform: template
    name: "Lilly_1 Humidity"
    id: "lilly_1_hum"
    unit_of_measurement: "%"
    device_class: "humidity"
    state_class: "measurement"
    accuracy_decimals: 0

  - platform: template
    name: "Lilly_1 Pressure"
    id: "lilly_1_press"
    unit_of_measurement: "hPa"
    device_class: "pressure"
    state_class: "measurement"
    accuracy_decimals: 0


light:
  - platform: status_led
    id: light_status_led
    name: "status led"
    pin:
      number: GPIO22
      inverted: true



switch:
  - platform: restart
    name: "Restart"

  - platform: template
    name: "OTA announce"
    id: switch_ota_announce
    optimistic: true
    turn_on_action:
      then:
        - script.execute: transmit_OTA_on
    turn_off_action:
      then:
        - script.execute: transmit_OTA_off



uart:
  id: uart_bus
  rx_pin: GPIO23
  tx_pin: GPIO19
  baud_rate: 9600
  rx_buffer_size: 1024
  debug:
    direction: BOTH
    dummy_receiver: true
    after:
      delimiter: ";"
    sequence:
      - light.turn_on: light_status_led
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          int device_id = 0;
          int wakeup_reason = -1;
          String payload_data="";          
          std::string str(bytes.begin(), bytes.end());

          if (sscanf(str.c_str(), "%d:%d:%s", &device_id, &wakeup_reason, &payload_data ) == 3 ) {
            ESP_LOGD("HC12", str.c_str() );
            String response_payload = String(  "0:" + String(device_id) + "!"  + String(id(switch_ota_announce).state) + ";"  ).c_str(); // respond to the device immidiately
            id(transmit_rf)->execute(response_payload.c_str() );

            if (device_id == 1 ) {
              
              //if (sscanf(payload_data.c_str(), "%d;", &wakeup_reason ) == 1 ) {
              ESP_LOGD("HC12", payload_data.c_str() );              
              if (wakeup_reason==0) { id(lily_1_wakeup_reason).publish_state("RESET"); }
              if (wakeup_reason==0) { id(lily_1_wakeup_reason).publish_state("RESET_POST"); }
              if (wakeup_reason==2) { id(lily_1_wakeup_reason).publish_state("PIR"); }
              if (wakeup_reason==2) { id(lily_1_wakeup_reason).publish_state("PIR_POST"); }
              if (wakeup_reason==4) { id(lily_1_wakeup_reason).publish_state("TIMER"); }
              if (wakeup_reason==4) { id(lily_1_wakeup_reason).publish_state("TIMER_POST"); }
            
              float sensor_temp=0;
              int sensor_hum=0;
              int sensor_press=0;
              if (sscanf(payload_data.c_str(), "%f!%d!%d", &sensor_temp, &sensor_hum, &sensor_press ) == 3 ) {
                  id(lilly_1_temp).publish_state(sensor_temp);
                  id(lilly_1_hum).publish_state(sensor_hum);
                  id(lilly_1_press).publish_state(sensor_press);
              }
            }
          }
      - delay: 100ms
      - light.turn_off: light_status_led



script:
  - id: transmit_rf
    parameters:
      message_payload: std::string
    mode: restart
    then:
      - uart.write: 
          id: uart_bus          
          data: !lambda |-
            ESP_LOGD("HC12", message_payload.c_str() );
            std::string mss = message_payload;
            return std::vector<unsigned char>(mss.begin(), mss.end());

  - id: transmit_OTA_on
    mode: restart
    then:
      - lambda: |-
          String payload = String(  "0:0!1;" ).c_str();
          id(transmit_rf)->execute(payload.c_str() );
      
  - id: transmit_OTA_off
    mode: restart
    then:
      - lambda: |-
          String payload = String(  "0:0!0;" ).c_str();
          id(transmit_rf)->execute(payload.c_str() );

anyway the important part:
the clients transmits a string
device_id:wakeup_reason:payload_data;

paylaod_data is next split into bme_temperature!bme_humidity (you could make the split all at once if you prefer, for me its a bit more readable this way)
the HUB first splits it to the parts:
“%d:%d:%s”, &device_id, &wakeup_reason, &payload_data

this is the test HUB (receiver) history:

besides BME280 sensor i have attached a PIR sensor
i only differentiate 3 wakeup reasons - RESET, TIMER and PIR
after each wake up - a timer deep sleep will schedule an hour sleep - unless PIR sensor wakes it up
as you can also see in the code - i have 2 statuses for each wake up reason - updating second just after the first one - that way i know when exactly and how often a PIR sensor woke up the device for example:

image

as you can see i should sleep a little more… :smiley:

Is it possible to add sensor signal strength?

Yes it is!