Skip to content

About

Firmware MeshCore : capteur température + 2 relais pilotables (Heltec V3, extensible) | MeshCore firmware: temperature sensor + 2 relay outputs (Heltec V3, extensible)

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

meshcore-IOT

🇬🇧 English version

Firmware pour un nœud capteur/actionneur compatible MeshCore : mesure de température, pilotage de 2 relais (extensible), 2 entrées bouton poussoir pour un contrôle local des relais. État consultable et relais pilotables à distance via le mesh LoRa — depuis l'app officielle MeshCore, ou en ligne de commande avec meshcore-cfg.

Board actuellement supportée : Heltec WiFi LoRa 32 V3 (ESP32-S3). Voir « Adapter à un autre câblage ou une autre board Heltec » pour porter vers un autre modèle.

Objectif

  • Température : lue localement (capteur BME280), consultable à distance.
  • 2 relais minimum : état lisible et modifiable à distance, pilotables aussi localement via 2 boutons poussoir dédiés (1 bouton = 1 relais).
  • Compatible MeshCore : s'appuie sur le firmware simple_sensor officiel (MeshCore, submodule git), rôle ADV_TYPE_SENSOR — aucune commande custom inventée là où MeshCore a déjà un mécanisme standard (voir « Conformité MeshCore » plus bas).

Installation rapide

git clone --recurse-submodules https://github.com/jmpuch/meshcore-iot.git
cd meshcore-iot

python3 -m venv .venv-platformio && source .venv-platformio/bin/activate
pip install platformio

./scripts/build.sh                          # compile + génère le binaire fusionné
meshcore-cfg --port /dev/ttyUSBx flash .pio/build/Heltec_v3_iot/firmware-merged.bin

(ou directement pio run -e Heltec_v3_iot -t upload --upload-port /dev/ttyUSBx si vous préférez PlatformIO plutôt que meshcore-cfg)

Avant tout usage réel : changez ADMIN_PASSWORD dans platformio.ini (voir plus bas) — le firmware est livré avec le mot de passe par défaut "password".

Conformité MeshCore — mécanismes natifs utilisés

Pas de commande custom inventée là où MeshCore a déjà un mécanisme standard :

  • Relais (lecture/écriture à distance) : mécanisme natif sensor get <clé> / sensor set <clé> <valeur> / sensor list (SensorManager::getSettingByKey/setSettingValue, CommonCLI.cpp:276-311) — un magasin de paramètres nommés générique, déjà utilisé dans le firmware officiel pour des toggles comme gps (EnvironmentSensorManager). Les relais sont exposés ainsi : sensor set relay1 1 / sensor get relay1, plutôt qu'une syntaxe set relay1 on qui entrerait en conflit avec le système set <var> <valeur> des variables de configuration classiques — deux magasins distincts dans le firmware.
  • Température (lecture à distance) : mécanisme de télémétrie natif (CayenneLPP, SensorManager::querySensors()) — addTemperature(canal, valeur), interrogeable à distance via le protocole companion standard, déjà géré automatiquement par le firmware pour un capteur BME280, aucun code à écrire. Relais aussi inclus dans cette même trame (addDigitalOutput, canaux 3/4) — visible à la fois via sensor get relay1/relay2 (CLI/mesh) et dans la télémétrie standard (app officielle), plus sur l'écran OLED du device. A nécessité de forker SensorMesh/UITask dans src/ (ce ne sont pas des fichiers librairie stables, contrairement à vendor/MeshCore/src/ — voir « Architecture » plus bas).

Architecture

vendor/MeshCore/examples/simple_sensor/ (firmware officiel) est un template de départ à forker, pas une librairie stable — contrairement à vendor/MeshCore/src/, jamais modifié. SensorMesh.{h,cpp}/ UITask.{h,cpp}/TimeSeriesData.{h,cpp} sont donc copiés dans notre propre src/ et personnalisés :

  • SensorMesh.h : un accesseur protégé telemetryBuffer() ajouté (seul changement réel à cette classe), pour que main.cpp puisse injecter l'état des relais dans la trame de télémétrie.
  • main.cpp : intercepte sensor get/set relay1|relay2 via handleCustomCommand() (vérifié en tout premier par SensorMesh, avant même sensor get/set standard) — un seul chemin de code pilote un relais, que ce soit depuis une commande CLI/mesh ou l'appui d'un bouton local (setRelay(idx, bool), jamais de logique dupliquée).
  • UITask.{h,cpp} : affiche l'état des relais sur l'écran OLED, sous les infos radio (RELAY1/RELAY2, ON/OFF).
  • Lecture directe du BME280 (beginOwnBme280()) plutôt que via le buffer interne CayenneLPP de SensorMesh::getTelemValue() — ce dernier contient un bug firmware réel (voir « Bugs trouvés » plus bas), contourné plutôt que corrigé dans vendor/.

Décisions de conception

  • Alimentation : secteur permanent (USB/adaptateur) — pas de gestion batterie à prévoir.
  • Capteur température : BME280 (I2C) — supporté nativement (ENV_INCLUDE_BME280, EnvironmentSensorManager.cpp, adresse I2C par défaut 0x76). Bonus : humidité + pression atmosphérique disponibles aussi sans coût supplémentaire (addRelativeHumidity/ addBarometricPressure/addAltitude), le driver les lit de toute façon.
  • Boutons poussoir : appui = bascule l'état du relais correspondant (toggle), pas de logique maintenu/relâché. Anti-rebond logiciel (DEBOUNCE_MS, paramétrable).
  • Sécurité accès distant : modèle natif MeshCore — un seul niveau admin, pas de palier lecture-seule séparé (simple_sensor réserve tout accès CLI aux clients admin, contrairement aux répéteurs qui ont un mode invité).
  • Module relais : logique de déclenchement active-haut par défaut (GPIO HIGH = relais ON), paramétrable (RELAY_ACTIVE_HIGH, voir plus bas) — dépend du module relais utilisé, pas du firmware.
    • Point de vigilance matériel : un GPIO flottant/indéterminé pendant le boot de l'ESP32-S3 (avant l'initialisation du firmware) pourrait activer un relais brièvement s'il flotte à l'état actif — prévoir une résistance de pull externe adaptée à la polarité choisie sur chaque ligne de commande, ou s'assurer que l'état OFF est forcé le plus tôt possible dans setup() (déjà le cas dans main.cpp) avant toute alimentation effective des modules relais.

GPIO (Heltec V3 / ESP32-S3)

Déjà occupées par simple_sensor/Heltec V3 (radio, OLED, GPS réservé même sans module physique) : 0, 1, 8, 9, 10, 11, 13, 14, 17, 18, 26, 35, 36, 37, 47, 48. I2C capteur (BME280) : 41 (SDA) / 42 (SCL) — pins JTAG réutilisées (défaut MeshCore 33/34 non utilisé ici).

Affectation retenue (parmi les 5 GPIO libres identifiées, 1 broche de marge conservée) :

Fonction GPIO Num. broche HV3 Sens Note
RELAY1 4 J3-15 Sortie actif-haut par défaut
RELAY2 5 J3-16 Sortie actif-haut par défaut
BUTTON1 (pilote RELAY1) 6 J3-17 Entrée pull-up interne, bouton vers GND
BUTTON2 (pilote RELAY2) 7 J3-18 Entrée pull-up interne, bouton vers GND
(réserve, extension future) 2 J3-13 — libre

Numérotation des broches selon la doc officielle Heltec (HTIT-WB32LA_V3.2.pdf, §2.2 « Pin description », table 2.2-2) — toutes ces GPIO sont sur le header J3 (côté droit de la carte, écran face à soi, USB-C en haut).

Build

python3 -m venv .venv-platformio && source .venv-platformio/bin/activate   # une fois
pip install platformio                                                     # une fois
git submodule update --init                                                # une fois

pio run -e Heltec_v3_iot                   # compile
pio run -e Heltec_v3_iot -t upload          # flash (device branché en USB)
pio device monitor -b 115200                # console série

Régénérer le binaire fusionné (bootloader + partitions + app, celui que meshcore-cfg flash ou tout flasher externe attend) — pio run seul ne le fait pas, il faut la cible mergebin séparément :

./scripts/build.sh                    # environnement par défaut (Heltec_v3_iot)
./scripts/build.sh MonEnvironnement   # un autre environnement de platformio.ini
# -> .pio/build/<env>/firmware-merged.bin, prêt pour `meshcore-cfg ... flash ...`

Adapter à un autre câblage ou une autre board Heltec

Tout ce qui est spécifique à ce montage (pas au board lui-même) est un simple flag -D dans [env:Heltec_v3_iot] de platformio.ini — pas besoin de toucher au code C++ pour changer ces valeurs :

Define Rôle Défaut actuel
RELAY1_PIN / RELAY2_PIN GPIO de sortie relais 4 / 5
BUTTON1_PIN / BUTTON2_PIN GPIO d'entrée bouton 6 / 7
RELAY_ACTIVE_HIGH 1 = relais ON à l'état haut, 0 = actif-bas 1
DEBOUNCE_MS Anti-rebond bouton (ms) 40
ENV_PIN_SDA / ENV_PIN_SCL I2C du capteur BME280 41 / 42
ADVERT_NAME Nom affiché sur le mesh "meshcore-IOT"
ADVERT_LAT / ADVERT_LON Coordonnées GPS statiques 0.0 / 0.0
ADMIN_PASSWORD Mot de passe admin CLI/app "password" — à changer

Cas 1 — même board (Heltec V3), câblage différent

Il suffit de changer les valeurs ci-dessus. Deux façons :

  • Éditer directement les -D ... dans [env:Heltec_v3_iot].
  • Ou dupliquer la section sous un autre nom ([env:Heltec_v3_iot_maison2]) pour garder plusieurs variantes de câblage compilables séparément — ./scripts/build.sh Heltec_v3_iot_maison2 régénère celle-ci précisément.

Cas 2 — une autre board Heltec (V2, V4, tracker, etc.)

Plus de travail : chaque board a un jeu de GPIO occupées différent (radio, écran, USB natif...), donc les 5 GPIO libres retenues pour la V3 (2, 4, 5, 6, 7) ne le sont pas forcément ailleurs. Exemple réel trouvé en préparant cette section : sur la Heltec V4, les GPIO 2, 5 et 7 sont réservées au contrôle de l'amplificateur radio (P_LORA_GC1109_PA_EN/P_LORA_KCT8103L_PA_CSD/_CTX dans vendor/MeshCore/variants/heltec_v4/platformio.ini) — exactement les broches utilisées ici pour RELAY2/BUTTON2/la réserve sur la V3. Jamais supposer qu'une affectation GPIO se transpose d'une board à l'autre.

Marche à suivre :

  1. Lister les boards Heltec disponibles côté firmware officiel :
    ls vendor/MeshCore/variants/ | grep heltec
  2. Ouvrir vendor/MeshCore/variants/<board>/platformio.ini, repérer la section [Heltec_loraXX_vY] en tête de fichier (pas un [env:...] — c'est la base commune, réutilisée par tous les rôles de cette board : repeater/sensor/companion/etc.). Elle liste tout ce que cette board réserve déjà (radio, USB, écran, bouton utilisateur...).
  3. Croiser cette liste avec la doc GPIO officielle Heltec du board visé — puis vérifier physiquement au voltmètre/fils volants avant tout câblage définitif, jamais une simple lecture de doc en confiance aveugle.
  4. Dans platformio.ini, dupliquer [env:Heltec_v3_iot] sous un nouveau nom (ex. [env:Heltec_v4_iot]), et remplacer :
    • board = ... (valeur du champ board = de la section [Heltec_loraXX_vY] trouvée à l'étape 2)
    • le bloc de -D spécifiques board (radio, GPS, écran, bouton — tout ce qui n'est pas dans le tableau ci-dessus) par celui de cette même section
    • -I vendor/MeshCore/variants/<board> et la ligne équivalente dans build_src_filter
    • les GPIO relais/boutons du tableau ci-dessus, avec les nouvelles valeurs choisies à l'étape 3
  5. ./scripts/build.sh Heltec_v4_iot pour compiler, puis test matériel complet avant tout usage réel — rien de tout ça n'est vérifié tant qu'un vrai device n'a pas confirmé chaque broche physiquement.

Suivre les mises à jour du firmware officiel MeshCore

vendor/MeshCore est un submodule git épinglé à un commit précis — le firmware officiel bouge, cette copie non :

cd vendor/MeshCore
git fetch --tags
git log --oneline HEAD..origin/main | head -20   # voir ce qui a changé
git checkout <nouveau-tag-ou-commit>              # ex. companion-v1.18.0
cd ../..
git add vendor/MeshCore
git commit -m "Met à jour vendor/MeshCore vers <tag>"

Point d'attention : les -D spécifiques à la board dans [env:Heltec_v3_iot] sont une copie manuelle de la section [Heltec_lora32_v3] de vendor/MeshCore/variants/heltec_v3/platformio.ini — une mise à jour du submodule peut faire diverger les deux silencieusement (nouveau champ ajouté en amont, valeur changée...). Après toute mise à jour du submodule, comparer les deux à la main :

sed -n '/^\[Heltec_lora32_v3\]/,/^\[env:/p' vendor/MeshCore/variants/heltec_v3/platformio.ini

... et reporter les écarts pertinents dans platformio.ini, en gardant les ajouts propres à ce projet (RELAY*_PIN, ADVERT_*, etc.) intacts. Recompiler et retester sur matériel réel avant tout déploiement après une telle mise à jour.

Test

Une fois flashé, ce firmware parle le même CommonCLI/série directe que n'importe quel autre device MeshCore — meshcore-cfg fonctionne donc dessus sans aucune modification (get/set/raw/dump, CLI ou interface graphique) :

meshcore-cfg --port /dev/ttyUSBx raw "sensor get relay1"
meshcore-cfg --port /dev/ttyUSBx raw "sensor set relay1 1"
meshcore-cfg --port /dev/ttyUSBx raw "sensor list"

# Diagnostic BME280 (lecture directe, indépendante de la télémétrie CayenneLPP)
meshcore-cfg --port /dev/ttyUSBx raw "sensor get temp"
meshcore-cfg --port /dev/ttyUSBx raw "sensor get humidity"
meshcore-cfg --port /dev/ttyUSBx raw "sensor get pressure"
meshcore-cfg --port /dev/ttyUSBx raw "sensor get altitude"

# Diagnostic anti-rebond (simule un train de rebonds électriques réaliste
# sur l'entrée bouton correspondante, sans câblage physique -- doit
# toujours répondre "1 toggle(s)", jamais plus)
meshcore-cfg --port /dev/ttyUSBx raw "sensor get simulate-button1"
meshcore-cfg --port /dev/ttyUSBx raw "sensor get simulate-button2"

Device injoignable en USB direct ? meshcore-cfg sait aussi le piloter à distance via un companion local relayant sur le mesh LoRa (--transport companion --target <clé-publique> --password <mot-de-passe> en CLI, ou le sélecteur de cible de l'interface graphique) — voir sa propre documentation.

Depuis l'app officielle MeshCore

Même protocole CLI (CommonCLI) qu'en série directe/meshcore-cfg — les commandes envoyées depuis la console/CLI admin de l'app (après connexion avec le mot de passe admin du device) sont identiques, textuellement :

sensor set relay1 1      # active RELAY1
sensor set relay1 0      # désactive RELAY1
sensor get relay1        # lit l'état actuel de RELAY1

sensor set relay2 1      # active RELAY2
sensor set relay2 0      # désactive RELAY2
sensor get relay2        # lit l'état actuel de RELAY2

sensor list               # état des deux relais en un seul appel

État des relais aussi visible passivement, sans commande, dans l'écran de télémétrie standard de l'app (canaux DigitalOutput) et sur l'écran OLED du device.

Bugs trouvés en cours de développement

Trois bugs réels trouvés et corrigés en testant contre du matériel réel — notés ici pour qui reprendrait un projet similaire :

  • Un memcmp comparait la mauvaise longueur dans notre propre interception de commande ("sensor set relay1 " : le code utilisait sizeof(...) — qui inclut le terminateur nul — au lieu de strlen(...), donc le préfixe ne matchait jamais dès qu'une vraie valeur suivait. sensor set relay1 1 retombait silencieusement sur le handler standard ("can't find custom var") au lieu d'être intercepté.
  • Un bug firmware réel dans SensorMesh.cpp (upstream, jamais modifié) : la table interne getDataSize()/getMultiplier() traite LPP_RELATIVE_HUMIDITY comme 2 octets/×10, alors que la vraie spec CayenneLPP (CayenneLPP.h, qui écrit réellement la trame) dit 1 octet/×2 — désaligne la lecture interne de tout ce qui suit l'humidité dans le buffer. N'affecte que la relecture interne du firmware, pas la télémétrie réellement émise sur le mesh (le code d'écriture, CayenneLPP::addRelativeHumidity, respecte bien la vraie spec) — contourné en relisant le BME280 directement plutôt que via ce mécanisme interne.
  • La télémétrie à la demande (depuis l'app) ignorait nos canaux relais : handleRequest() (le vrai chemin de réponse à une requête de télémétrie en direct) fait son propre reset()+querySensors() et n'appelle jamais onSensorDataRead() (qui ne tourne que sur le rafraîchissement périodique interne) — nos canaux relais étaient bien ajoutés en interne puis systématiquement écrasés juste avant l'envoi réel. Corrigé en ajoutant un point d'accroche onTelemetryRequest() dans notre copie de SensorMesh, appelé juste avant l'envoi.

Licence

Usage libre — voir vendor/MeshCore pour la licence du firmware officiel sur lequel ce projet s'appuie.

About

Firmware MeshCore : capteur température + 2 relais pilotables (Heltec V3, extensible) | MeshCore firmware: temperature sensor + 2 relay outputs (Heltec V3, extensible)

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages