Persisting a global string to flash for RFID tags

I am using the rc522_spi to scan RF ID card to unlock a garden gate.
I can hold a list of valid tags in a global string, comma separated.
I also want the ability to add new valid tags…
I’ve decided to do this by having a special tag called “Add”. When this is presented to the reader, it signals that the next tag presented should be added to the valid tag global string.
The ISSUE:
How can I persist the valid tag global string so that should the ESP32 be switched off and on, the new tag is in the valid tag global string?

What I’ve tried:
text_sensor: moving the global string to a text_sensor - not allowed to have restore_mode: true, not allowed an initial value.
sensor: - doesn’t like strings
I even tried ChatGPT, but it gets this completely wrong suggesting restore_mode is available in options that are not possible.

Can anyone help?
Any pointers to web pages or documentation would be greatly appreciated.

Whole code pasted below. Sorry if it’s a bit rough :slight_smile:

In the past, I implemented all of this in Home Assistant. But when that is offline, the gate lock becomes unoperable. I’d like this gate to be usable at all times.

Many thanks in advance

esphome:
  name: rfid32
  comment: RFID Gate Controller
  area: Garden
  friendly_name: rfid32
  on_boot:
    - priority: 600  # Ensures it runs after other boot tasks
      then:
        - delay: 5s  # Wait for 5 seconds
        - lock.lock: lock1

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "ABCDE/0FGHIJKLMNOPQRSTUVWXYZabcdefghijklmno="


substitutions:
  device_name: rfid32

ota:
  - platform: esphome
    password: "password"

preferences:
  flash_write_interval: 1s

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Rfid32 Fallback Hotspot"
    password: "password"

captive_portal:
    
spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

button:
  - platform: safe_mode
    name: Safe Mode
    id: ${device_name}_safe_mode
    icon: mdi:restart-alert
    device_class: restart
    entity_category: diagnostic
    disabled_by_default: true

interval:
  - interval: 60s
    then:
      - if:
          condition:
            wifi.connected:
            #api.connected:
          then:
            - lambda: |-
                ESP_LOGI("watchdog", "wifi.connected");
          else:
            - lambda: |-
                ESP_LOGW("watchdog", "considering safe_mode");
            - delay: 60s
            - if:
                condition:
                  #api.connected:
                  wifi.connected:
                then:
                  - lambda: |-
                      ESP_LOGI("watchdog", "wifi.connected");
                else:
                  - lambda: |-
                      ESP_LOGE("watchdog", "entering safe_mode");
                  - button.press: ${device_name}_safe_mode

rc522_spi:
  cs_pin: GPIO21
  reset_pin: GPIO22
  update_interval: 0.25s
  on_tag:
    then:
      - lambda: |-
          id(rfid_text_sensor).publish_state(x.c_str());
          ESP_LOGI("main", "Value of sensor %s", id(rfid_text_sensor).state.c_str());
          if (id(id_valid_tags).find(id(rfid_text_sensor).state.c_str()) != std::string::npos) {
            ESP_LOGI("main", "Value of valid tag is %s", id(rfid_text_sensor).state.c_str());
            id(valid_card).publish_state(true); //Match found
            id(add_next_card_switch).turn_off();
          } else {
            ESP_LOGI("main", "******INVALID TAG******");
            ESP_LOGI("main", "INVALID: %s", id(rfid_text_sensor).state.c_str());
            ESP_LOGI("main", "CLEAR  : %s", id(id_clear).c_str());
            ESP_LOGI("main", "ADD    : %s", id(id_add).c_str());
            id(valid_card).publish_state(false); //No Match

            ESP_LOGI("main","ADD State: %d", id(add_next_card_switch).state);
            if (id(rfid_text_sensor).state == id(id_clear)) {
              ESP_LOGI("main", "CLEAR MEMORY REQUESTED %s", id(rfid_text_sensor).state.c_str());
              id(id_valid_tags) = "";
              id(add_next_card_switch).turn_off();
            } else {
              if (id(rfid_text_sensor).state == id(id_add)) {
                ESP_LOGI("main", "ADD TAG REQUESTED %s", id(rfid_text_sensor).state.c_str());
                id(add_next_card_switch).publish_state(true);
              } else {
                if (id(add_next_card_switch).state) {
                  ESP_LOGI("main", "ADDING CARD %s", id(rfid_text_sensor).state.c_str());
                  id(id_valid_tags) = id(id_valid_tags) + "!" + id(rfid_text_sensor).state;
                }
                id(add_next_card_switch).turn_off();
              }
            }
          }
          ESP_LOGI("main", "Valid tags from ***STORAGE*** are %s", id(id_valid_tags).c_str());


  on_tag_removed:
    then:
      - lambda: |-
          id(rfid_text_sensor).publish_state("");
          ESP_LOGI("main", "Value of sensor %s", id(rfid_text_sensor).state.c_str());
          id(valid_card).publish_state(false); //Clear Match

text_sensor:
  - platform: template
    name: "Scanning Tag"
    id: rfid_text_sensor
  - platform: wifi_info
    ip_address:
      name: ESP IP Address
      address_0:
        name: ESP IP Address 0
      address_1:
        name: ESP IP Address 1
      address_2:
        name: ESP IP Address 2
      address_3:
        name: ESP IP Address 3
      address_4:
        name: ESP IP Address 4
    ssid:
      name: ESP Connected SSID
    bssid:
      name: ESP Connected BSSID
    mac_address:
      name: ESP Mac Wifi Address
    scan_results:
      name: ESP Latest Scan Results
    dns_address:
      name: ESP DNS Address

binary_sensor:
  - platform: status
    name: "RFID status"

  - platform: template
    name: "Valid card"
    id: valid_card
    device_class: occupancy
    on_press: 
      then:
        - lock.unlock: lock1
        - delay: 5s
        - lock.lock: lock1
  
  - platform: gpio
    pin:
      number: GPIO35
    name: "RFID button"
    on_press:
      then:
        - lock.unlock: lock1
        - delay: 5s
        - lock.lock: lock1

  - platform: rc522
    uid: C7-EB-0F-29
    name: "Clear"

  - platform: rc522
    uid: 44-96-E3-72
    name: "Add"

  - platform: rc522
    uid: 99-84-17-55
    name: "George"

  - platform: rc522
    uid: B9-1C-17-55
    name: "Zippy"

  
sensor:
  - platform: wifi_signal
    name: "RFID522 WiFi Signal"
    update_interval: 60s

switch:
  - platform: restart
    name: "RFID restart"

  - platform: template
    name: "Add next card"
    id: add_next_card_switch
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF


light:
  - platform: binary
    id: blue_light
    name: "Blue LED"
    output: blue_led

output:
  - platform: gpio
    id: relay
    pin:
      number: GPIO0
      inverted: false

  - platform: gpio
    id: blue_led
    pin: GPIO2

lock:
  - platform: output
    name: "Garden Gate Lock"
    id: lock1
    output: relay
    on_lock:
      - logger.log: "Door Locked!"
      - light.turn_off: blue_light
    on_unlock:
      - logger.log: "Door Unlocked!"
      - light.turn_on: blue_light

globals:
  - id: id_clear
    type: std::string
    initial_value: '"C7-EB-0F-29"'

  - id: id_add
    type: std::string
    initial_value: '"44-96-E3-72"'

  - id: id_valid_tags
    type: std::string
    initial_value: '"!99-84-17-55!B9-1C-17-55"'

  - id: id_names
    type: std::string
    initial_value: '"!George     !Zippy      "'

restore_value: yes should do it. Globals are saved to flash.

   # Example for global string variable
   - id: my_global_string
     type: std::string
     restore_value: yes

Thank you Karosm.

I updated the globals:

globals:
  - id: id_clear
    type: std::string
    initial_value: '"C7-EB-0F-29"'
    restore_value: yes

  - id: id_add
    type: std::string
    initial_value: '"44-96-E3-72"'
    restore_value: yes

  - id: id_valid_tags
    type: std::string
    initial_value: '"!99-84-17-55!B9-1C-17-55"'
    restore_value: yes

  - id: id_valid_names
    type: std::string
    initial_value: '"!George     !Mildred    "'
    restore_value: yes

Unfortunatelly the new values does not persist. Restarting the device loses the new card value. I can see in the logs that it is saving 1 preference, but if power is lost, the tag is no longer there.

Are you sure your globals update is working, what are you getting on logs when you update it?

Is this valid code?

Hi Karosm

Thanks for all your help. Yes the code above is valid as the “id_valid_tags” is a global of type std::string and the “rfid_text_sensor” is of type “text_sensor”. The property state, holds the value. All is good.

I don’t know what I changed, but it appears to be working…

esphome:
  name: rfid32
  comment: RFID Gate Controller
  area: Garden
  friendly_name: rfid32
  on_boot:
    - priority: 600  # Ensures it runs after other boot tasks
      then:
        - delay: 5s  # Wait for 5 seconds
        - lock.lock: lock1
        - lambda: |-
            id(accepted_tags).publish_state( id(id_valid_tags) );
            id(accepted_names).publish_state( id(id_valid_names) );
        - delay: 5s
        - lambda: |-
            ESP_LOGI("main", "Valid tags from ***STORAGE*** are %s", id(id_valid_tags).c_str());

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "ABCDE/0FGHIJKLMNOPQRSTUVWXYZabcdefghijklmno="


substitutions:
  device_name: rfid522

ota:
  - platform: esphome
    password: "password"

preferences:
  flash_write_interval: 1s

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Rfid32 Fallback Hotspot"
    password: "password"

captive_portal:
    
spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

button:
  - platform: safe_mode
    name: Safe Mode
    id: ${device_name}_safe_mode
    icon: mdi:restart-alert
    device_class: restart
    entity_category: diagnostic
    disabled_by_default: true

  - platform: template
    name: "Factory reset"
    id: factory_reset_button
    icon: "mdi:cog-counterclockwise"
    on_press:
      - logger.log: "Factory reset"
      - lambda: |-
          id(id_valid_tags) = "!99-84-17-55!B9-1C-17-55";
          id(id_valid_names) = "!George     !Zippy      ";
          id(accepted_tags).publish_state( id(id_valid_tags) );
          id(accepted_names).publish_state( id(id_valid_names) );

interval:
  - interval: 60s
    then:
      - if:
          condition:
            wifi.connected:
            #api.connected:
          then:
            - lambda: |-
                ESP_LOGI("watchdog", "wifi.connected");
          else:
            - lambda: |-
                ESP_LOGW("watchdog", "considering safe_mode");
            - delay: 60s
            - if:
                condition:
                  #api.connected:
                  wifi.connected:
                then:
                  - lambda: |-
                      ESP_LOGI("watchdog", "wifi.connected");
                else:
                  - lambda: |-
                      ESP_LOGE("watchdog", "entering safe_mode");
                  - button.press: ${device_name}_safe_mode

rc522_spi:
  cs_pin: GPIO21
  reset_pin: GPIO22
  update_interval: 0.25s
  on_tag:
    then:
      - lambda: |-
          id(rfid_text_sensor).publish_state(x.c_str());
          ESP_LOGI("main", "Value of sensor %s", id(rfid_text_sensor).state.c_str());
          if (id(id_valid_tags).find(id(rfid_text_sensor).state.c_str()) != std::string::npos) {
            //ESP_LOGI("main", "Value of valid tag is %s", id(rfid_text_sensor).state.c_str());
            id(valid_card).publish_state(true); //Match found
            id(add_next_card_switch).turn_off();
          } else {
            //ESP_LOGI("main", "******INVALID TAG******");
            //ESP_LOGI("main", "INVALID: %s", id(rfid_text_sensor).state.c_str());
            //ESP_LOGI("main", "CLEAR  : %s", id(id_clear).c_str());
            //ESP_LOGI("main", "ADD    : %s", id(id_add).c_str());
            id(valid_card).publish_state(false); //No Match

            //ESP_LOGI("main","ADD State: %d", id(add_next_card_switch).state);
            if (id(rfid_text_sensor).state == id(id_clear)) {
              //ESP_LOGI("main", "CLEAR MEMORY REQUESTED %s", id(rfid_text_sensor).state.c_str());
              id(id_valid_tags) = "";
              id(id_valid_names) = "";
              id(add_next_card_switch).turn_off();
              id(accepted_tags).publish_state( id(id_valid_tags) );
              id(accepted_names).publish_state( id(id_valid_names) );
            } else {
              if (id(rfid_text_sensor).state == id(id_add)) {
                //ESP_LOGI("main", "ADD TAG REQUESTED %s", id(rfid_text_sensor).state.c_str());
                id(add_next_card_switch).publish_state(true);
              } else {
                if (id(add_next_card_switch).state) {
                  //ESP_LOGI("main", "ADDING CARD %s", id(rfid_text_sensor).state.c_str());
                  id(id_valid_tags) = id(id_valid_tags) + "!" + id(rfid_text_sensor).state;
                  id(accepted_tags).publish_state( id(id_valid_tags) );
                }
                id(add_next_card_switch).turn_off();
              }
            }
          }
          //ESP_LOGI("main", "Valid tags from ***STORAGE*** are %s", id(id_valid_tags).c_str());


  on_tag_removed:
    then:
      - lambda: |-
          id(rfid_text_sensor).publish_state("");
          ESP_LOGI("main", "Value of sensor %s", id(rfid_text_sensor).state.c_str());
          id(valid_card).publish_state(false); //Clear Match

text_sensor:
  - platform: template
    name: "Scanning Tag"
    id: rfid_text_sensor
  - platform: wifi_info
    ip_address:
      name: ESP IP Address
      address_0:
        name: ESP IP Address 0
      address_1:
        name: ESP IP Address 1
      address_2:
        name: ESP IP Address 2
      address_3:
        name: ESP IP Address 3
      address_4:
        name: ESP IP Address 4
    ssid:
      name: ESP Connected SSID
    bssid:
      name: ESP Connected BSSID
    mac_address:
      name: ESP Mac Wifi Address
    scan_results:
      name: ESP Latest Scan Results
    dns_address:
      name: ESP DNS Address
  - platform: template
    name: "Accepted tags"
    id: accepted_tags
  - platform: template
    name: "Accepted names"
    id: accepted_names


binary_sensor:
  - platform: status
    name: "RFID status"

  - platform: template
    name: "Valid card"
    id: valid_card
    device_class: occupancy
    on_press: 
      then:
        - lock.unlock: lock1
        - delay: 5s
        - lock.lock: lock1
  
  - platform: gpio
    pin:
      number: GPIO35
    name: "RFID button"
    on_press:
      then:
        - lock.unlock: lock1
        - delay: 5s
        - lock.lock: lock1

  - platform: rc522
    uid: C7-EB-0F-29
    name: "Clear"

  - platform: rc522
    uid: 44-96-E3-72
    name: "Add"

  - platform: rc522
    uid: 99-84-17-55
    name: "George"

  - platform: rc522
    uid: B9-1C-17-55
    name: "Zippy"

  
sensor:
  - platform: wifi_signal
    name: "RFID522 WiFi Signal"
    update_interval: 60s

switch:
  - platform: restart
    name: "RFID restart"

  - platform: template
    name: "Add next card"
    id: add_next_card_switch
    optimistic: true
    restore_mode: RESTORE_DEFAULT_OFF


light:
  - platform: binary
    id: blue_light
    name: "Blue LED"
    output: blue_led

output:
  - platform: gpio
    id: relay
    pin:
      number: GPIO0
      inverted: false

  - platform: gpio
    id: blue_led
    pin: GPIO2

lock:
  - platform: output
    name: "Garden Gate Lock"
    id: lock1
    output: relay
    on_lock:
      - logger.log: "Door Locked!"
      - light.turn_off: blue_light
    on_unlock:
      - logger.log: "Door Unlocked!"
      - light.turn_on: blue_light

globals:
  - id: id_clear
    type: std::string
    initial_value: '"C7-EB-0F-29"'
    restore_value: yes

  - id: id_add
    type: std::string
    initial_value: '"44-96-E3-72"'
    restore_value: yes

  - id: id_valid_tags
    type: std::string
    initial_value: '"!99-84-17-55!B9-1C-17-55"'
    restore_value: yes

  - id: id_valid_names
    type: std::string
    initial_value: '"!George     !Zippy      "'
    restore_value: yes


Nice! Maybe it didn’t upload first time…

1 Like