Envoyer et recevoir des SMS depuis HA - ESPHome + ESP32 + SIM7600E-L1C

Salut :wink:
Dans le cadre d’un projet ESPHome, qui comprend l’utilisation d’un module SIM7600E, j’ai explorer l’envoi/réception de SMS.
Ce n’est pas une fonction maîtresse de mon projet, mais dans le paquet de toutes mes lectures sur ce SIM7600E, j’ai relu ce post de @Argonaute (et il y en a quelques autres qui évoquent le sujet)

Et comme j’ai vu que les puces SIM7600 étaient évoquées, mais que personne ne s’était aventuré à les tester, je me suis dit que cela me ferais un bon sujet de départ pour tenter d’apprivoiser la bête.
Pour rappel, à contrario des SIM800L, les SIM7600 ne sont pas (encore) intégrée à ESPHome.
Il faut dés lors jouer avec les commande « AT » en UART.

Le matériel utilisé :
Un ESP32S
Un SIM7600E-L1C avec antenne LTE et antenne GPS
Une carte SIM valide
Une alimentation USB (5V / 2A)

Les connections :

ESP32			   SIM7600

TX GPIO17	--->	R (RX)
RX GPIO16	--->	T (TX)
GND		    --->	G (GND)
5V		    --->	V (VCC)

Il existe une commande « AT » pour communiquer le code PIN de la carte SIM. Mais pour des raisons de simplicité, j’ai d’abord désactivé la demande de code PIN de ma carte SIM.
Une fois le système sous tension (branchement de l’alimentation USB sur l’entrée USB de l’ESP32), la LED rouge de l’ESP32 et la LED rouge du SIM7600E sont allumée fixe. C’est le signe que tout est alimenté correctement.
Ensuite, si le SIM7600E accroche le réseau (4G), sa LED verte clignote.

Pour ce qui est de l’envoi de SMS avec le SIM7600E, c’est assez simple. Il faut connaître les bonnes commandes « AT ».
Moi, comme s’était juste pour jouer, j’ai utilisé un « button » avec l’instruction « uart.write »
Ce « button » remonte dans Home Assistant et peut-être utilisé soit dans une automatisation, soit dans un script, … et ce, soit dans l’ESP ou dans Home Assistant.
Code button :

button:
  # Déclanche l'envoi d'un SMS  
  - platform: template
    name: "Envoyer SMS Test"
    on_press:
      then:
        - if:
            condition:
              lambda: |-
                return id(ha_tel_num_select).has_state() && id(ha_message_select).has_state();
            then:
              - uart.write:
                  id: uart_sim7600
                  data: "AT+CMGF=1\r\n"
              - delay: 1s
              - uart.write: !lambda
                  std::string cmd = (std::string("AT+CMGS=") + id(ha_tel_num_select).state + "\r\n");
                  return std::vector<unsigned char>(cmd.begin(), cmd.end());
              - delay: 1s
              - uart.write: !lambda
                  std::string msg = (id(ha_message_select).state + "\x1A");
                  return std::vector<unsigned char>(msg.begin(), msg.end());
            else:
              - logger.log: "ERREUR: Les valeurs du numéro de téléphone ou du message n'ont pas été définies."

Pour recevoir et et analyser des SMS reçus par le SIM7600E, c’est une autre paire de manches.
Le plus simple est d’utiliser le « debug » de l’UART. Ce n’est sans doute pas ce qu’il y a de plus propre, mais je ne sais pas (encore) faire avec des « external_components » gavé de C++.

Voici le code ESPHome :


esphome:
  name: esphome-web-5165f0
  friendly_name: Bidouille

  on_boot:
    priority: 80
    then:
      # Initialisation du text_sensor via lambda (méthode C++)
      - lambda: |-
          id(sms_status).publish_state("En attente de SMS...");

      # On force le mode texte et la notification SMS au boot
      - uart.write:
          id: uart_sim7600
          data: "AT+CMGF=1\r\n"
      - delay: 500ms
      - uart.write:
          id: uart_sim7600
          data: "AT+CPMS=\"SM\",\"SM\",\"SM\"\r\n"
      - delay: 500ms

      # Demander la livraison directe (+CMT) au terminal pour recevoir le message complet
      - uart.write:
          id: uart_sim7600
          data: "AT+CNMI=2,2,0,0,0\r\n"
      - delay: 500ms


esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  level: VERBOSE #Ca cause un max pour développer, c'est bien

# Enable Home Assistant API
api:
  encryption:
    key: !secret encryption_key

ota:
  - platform: esphome


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 192.168.5.40
    gateway: 192.168.5.1
    subnet: 255.255.255.0
  ap:
    ssid: "bidouille"
    password: !secret bidouille_wifi_ap_password

captive_portal:

web_server:
  port: 80

# Définition de 2 select qui servent de listes de N° de tel et de messages prédéfinis
select:
  - platform: template
    name: "Numero Telephone HA"
    id: ha_tel_num_select
    options:
     - '"+32123456789"'
     - '"+32987654321"'
    initial_option: '"+32123456789"'
    optimistic: true
  - platform: template
    name: "Message HA"
    id: ha_message_select
    options:
     - "Test SMS depuis ESP32/SIM7600"
     - "Hello World"
     - "Ce message est envoye depuis mon module 4G\nC'est un processus automatique\nMerci de ne pas repondre !"
    initial_option: "Test SMS depuis ESP32/SIM7600"
    optimistic: true

text_sensor:
  - platform: template
    name: "Statut Reception SMS"
    id: sms_status

  - platform: template
    name: "SMS Sender"
    id: sms_sender

  - platform: template
    name: "SMS Body"
    id: sms_body

globals:
  - id: wait_for_sms_content
    type: bool
    initial_value: 'false'
  - id: last_sms_sender
    type: std::string
    initial_value: '""'

uart:
  id: uart_sim7600
  tx_pin: GPIO17
  rx_pin: GPIO16
  baud_rate: 115200
  data_bits: 8
  parity: NONE
  stop_bits: 1

  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\n"
    sequence:
      - lambda: |-
          // Convert bytes vector to std::string et enlever '\r'
          std::string data(bytes.begin(), bytes.end());
          data.erase(std::remove(data.begin(), data.end(), '\r'), data.end());

          ESP_LOGD("uart_rx", "Recu: %s", data.c_str());

          // 1) Réception directe (mode CNMI=2,2): en-tête +CMT: suivi de la ligne(s) du message.
          if (data.find("+CMT:") != std::string::npos) {
            // Extraire le numéro d'expéditeur entre guillemets
            size_t q1 = data.find('"');
            size_t q2 = std::string::npos;
            if (q1 != std::string::npos) {
              q2 = data.find('"', q1 + 1);
            }
            std::string sender = "";
            if (q1 != std::string::npos && q2 != std::string::npos && q2 > q1) {
              sender = data.substr(q1 + 1, q2 - q1 - 1);
            }
            id(last_sms_sender) = sender;
            id(wait_for_sms_content) = true;
            id(sms_status).publish_state("=> Nouveau SMS detecte de: " + (sender.empty() ? "inconnu" : sender));
            ESP_LOGI("uart_rx", "ALERTE SMS RECUS de: %s", sender.c_str());
            return;
          }

          // 2) Si on attend le corps du SMS, la prochaine ligne est le message
          if (id(wait_for_sms_content)) {
            // data contient le corps du SMS (une ligne). Publier dans text_sensor(s)
            id(sms_body).publish_state(data);
            id(sms_sender).publish_state(id(last_sms_sender));
            id(sms_status).publish_state("SMS recu de " + id(last_sms_sender));
            ESP_LOGI("uart_rx", "Contenu SMS: %s", data.c_str());
            // Reset du flag d'attente
            id(wait_for_sms_content) = false;
            return;
          }

          // 3) Pour compatibilité, si le modem continue à renvoyer +CMTI (nouveau message stocké),
          //    on peut le détecter et éventuellement appeler AT+CMGR=<index> (non implémenté ici).
          if (data.find("+CMTI:") != std::string::npos) {
            id(sms_status).publish_state("=> Nouveau SMS detecte (stocke): " + data);
            ESP_LOGI("uart_rx", "CMTI detecte: %s", data.c_str());
            // Optionnel: lancer lecture via AT+CMGR si vous préférez cette méthode
          }

button:
  # Déclanche l'envoi d'un SMS  
  - platform: template
    name: "Envoyer SMS Test"
    on_press:
      then:
        - if:
            condition:
              lambda: |-
                return id(ha_tel_num_select).has_state() && id(ha_message_select).has_state();
            then:
              - uart.write:
                  id: uart_sim7600
                  data: "AT+CMGF=1\r\n"
              - delay: 1s
              - uart.write: !lambda
                  std::string cmd = (std::string("AT+CMGS=") + id(ha_tel_num_select).state + "\r\n");
                  return std::vector<unsigned char>(cmd.begin(), cmd.end());
              - delay: 1s
              - uart.write: !lambda
                  std::string msg = (id(ha_message_select).state + "\x1A");
                  return std::vector<unsigned char>(msg.begin(), msg.end());
            else:
              - logger.log: "ERREUR: Les valeurs du numéro de téléphone ou du message n'ont pas été définies."

  # Permet d'effacer tous les SMS de la carte SIM logée dans le module SIM
  - platform: template
    name: "Nettoyer tous les SMS (AT+CMGD)"
    on_press:
      then:
        - uart.write:
            id: uart_sim7600
            data: "AT+CMGD=,4\r\n"
        - logger.log: "Commande de nettoyage de tous les SMS envoyée."
        - delay: 1s
        - lambda: |-
            id(sms_status).publish_state("Nettoyage des SMS effectué. En attente de nouveau SMS...");

Et dans Home Assistant :

Comme, dans mon projet, je n’ai pas besoin d’envoi de SMS, je ne suis pas aller plus loin que de vérifier s’il était possible d’envoyer des SMS avec ce système au départ de Home Assistant.
Maintenant qu’il est confirmé que cela fonctionne, pour les personnes intéressées, il vous reste à développer / adapter le code pour vos propre besoins.
Pour ce code, l’IA m’a beaucoup aidé :sweat_smile:
Si quelqu’un à une solution pour éviter d’utiliser le bloc debug de l’UART, je suis preneur.
Maintenant, je vais continuer sur mon projet … :wink:

@+ Guy

3 « J'aime »