IoT-Link · LevelStream · Bring-up matériel

Mise à jour OTA bloquée sur les prototypes M10

Analyse du bootloader, de la défaillance qui bloque la mise à jour OTA, de ses causes probables et des pistes de durcissement — appuyée sur le code source et la documentation u-blox.

Modem : u-blox SARA-R422M10S (FW 01.24) Bootloader : v0.1.9 MCU : STM32L412 Mis à jour : 25 juin 2026

En résumé

  • Le bootloader se connecte au webservice de mise à jour à chaque démarrage pour vérifier — et, si besoin, télécharger — une nouvelle version de firmware.
  • Cette connexion échoue par intermittence, toujours au même endroit : juste après le message Starting PDP, au moment d'ouvrir la connexion de données (+CME ERROR: no network service ou operation not allowed).
  • Conséquence : l'OTA peut échouer (de façon intermittente, ~2 fois sur 3) — aussi bien la mise à jour d'une sonde en service (qui tourne alors en boucle, log du 19/06) que le premier chargement du firmware sur une carte neuve. L'enjeu est donc la fiabilité, pas une panne systématique.
  • Cause de fond : le bootloader engage la phase de données avant que la pile réseau du modem ne soit prête, et force AT+CGACT=1,1 — redondant en LTE-M où le contexte s'active à l'attachement.
  • Aggravant : quand la sonde a déjà un firmware valide, un échec de connexion ne déclenche aucun nouvel essai automatique — le bootloader démarre l'application et remet la mise à jour au prochain redémarrage. Sur une sonde montée, la seule relance possible est un reset long à l'aimant : il rejoue bien l'OTA, mais échoue lui-même ~2 fois sur 3 → la mise à jour peut nécessiter plusieurs tentatives, voire rester bloquée.

1 Contexte & portée

La mise à jour à distance (OTA) échoue par intermittence sur les prototypes M10 (modem u-blox SARA-R422M10S) : le bootloader n'arrive pas toujours à joindre le webservice — que ce soit pour mettre à jour une sonde en service ou pour charger le firmware sur une carte neuve.

Ce document le décrit, propose un diagnostic appuyé sur la lecture du code et la documentation u-blox, distingue ce qui est vérifié de ce qui reste hypothèse, et liste les mesures et les tests à mener.

Méthode & honnêteté

L'analyse est statique : lecture du code source du bootloader (branche à jour, v0.1.9) et de la documentation u-blox. Elle n'a pas encore été confrontée à une trace réseau réelle ni à une mesure électrique. Les conclusions sont donc graduées (vérifié / probable / à tester), et la section Tests est conçue précisément pour lever les incertitudes avant de figer un correctif.

2 Le mécanisme OTA, étape par étape

À chaque démarrage, le bootloader exécute toujours la même séquence avant de passer la main à l'application.

  1. Allumage du modem Mise sous tension et séquence de power-on.
  2. Initialisation & attente d'enregistrement réseau Le modem doit être enregistré sur le réseau avant d'aller plus loin.
  3. Lecture de l'IMEI Identifiant unique utilisé pour interroger le serveur.
  4. Ouverture du contexte de données — « Starting PDP » C'est ici qu'échoue la connexion. (La trace « Starting PDP » n'apparaît qu'en build verbeux APP_VERBOSE.)
  5. Connexion au serveur & comparaison de checksum Le bootloader compare le checksum du serveur au firmware flashé et, si une mise à jour est en attente, la télécharge. S'il ne peut pas se connecter (étape 4), la mise à jour échoue.

La séquence est lisible telle quelle dans la boucle principale du bootloader :

application.cApplication_Loop — lignes 416-470 (abrégé)
// 1. Power on SARA_R422
ret = SARA_R422_Power_On(&appdata->SARA_R422);
// 2. Initialize SARA_R422  (inclut l'attente d'enregistrement réseau)
ret = SARA_R422_Init(&appdata->SARA_R422);
// 3. Read and store IMEI
ret = SARA_R422_Get_IMEI(&appdata->SARA_R422, appdata->IMEI, ...);
debug_print("[DEBUG]Starting PDP...\r\n");
// 4. SARA R422 PDP activate  <-- POINT DE PANNE
ret = SARA_R422_PDP_Connect(&appdata->SARA_R422, APN);
// 5. Enable HTTP, puis GET du checksum, comparaison, upgrade...

L'ouverture du contexte (étape 4) repose sur ces deux commandes :

SARA_R422.cSARA_R422_PDP_Connect — lignes 809-842 (abrégé)
// Configure PDP Context
sprintf(buf, "AT+CGDCONT=1,\"IP\",\"%s\"\r\n", Apn);   // APN = "tsiot"
ret = SARA_R422_Execute_Cmd(SARA_R422, buf);
...
// Enable PDP Context
ret = SARA_R422_Execute_Cmd(SARA_R422, "AT+CGACT=1,1\r\n");   // <-- commande qui échoue

3 Le symptôme et les deux opérations touchées

La défaillance est toujours la même — l'ouverture de la connexion de données échoue juste après Starting PDP, et le bootloader ne peut plus joindre le webservice. Mais elle frappe dans deux opérations OTA bien réelles : la mise à jour d'une sonde en service, et le premier chargement du firmware sur une carte neuve.

L'impact réel — la boucle de mise à jour

C'est le cas qui a déclenché l'analyse (log du 19/06). Le firmware reçoit une nouvelle version cible et redémarre dans le bootloader pour la télécharger — mais le bootloader échoue à se connecter (no network service), repart sur l'ancien firmware, que l'application redemande aussitôt de mettre à jour : la mise à jour tourne en boucle.

Log_20260619_206_FWUpdate.logdémarrage déclenché par la demande de mise à jour (extrait)
...rebooting to bootloader to get the new firmware
NVM Firmware checksum                    : c4c2…e2dd0   // firmware actuel
Configuration message Firmware checksum  : 5c9c…91e42   // nouvelle cible reçue par l'app
...
[DEBUG]Starting PDP...
[ERROR]CME ERROR Received : +CME ERROR: no network service   // <-- la connexion échoue
[ERROR]Can not connect to TCP
[DEBUG]Jumping on App...                                // repart sur l'ancien firmware -> boucle

Les deux opérations touchées, avec deux codes d'erreur distincts mais au même endroit du code :

Opération A — mise à jour (sonde en service)

+CME ERROR: no network service CME 30
Déclencheur
Nouvelle version poussée par le serveur (ou redémarrage à l'aimant, qui relance la vérification OTA).
Fréquence
Échoue ~2 fois sur 3 au reboot-aimant ; un power-off complet, lui, réussit.
Contournement
Power-off complet puis redémarrage (impossible sur sonde montée).
Enjeu terrain
Une sonde montée ne peut plus être éteinte ; seul le reset long à l'aimant relance l'OTA — mais ~1 fois sur 3 seulement.

Opération B — premier chargement (carte neuve)

+CME ERROR: operation not allowed CME 3
Déclencheur
Tout premier allumage d'une carte vierge — l'installation initiale du firmware (production).
Fréquence
~2 fois sur 3 ; le BL « ne va pas plus loin ».
Contournement
Power-off + redémarrage aimant fonctionne à tous les coups.

Le cas « operation not allowed » (carte neuve) s'explique très probablement par un provisioning de première fois du modem : au tout premier allumage, le module doit initialiser la SIM/eSIM, faire sa première sélection d'opérateur et son premier attachement réseau. Tant que ce n'est pas terminé, une commande de données est refusée. Le deuxième démarrage réussit parce que le premier a amorcé ce provisioning — pas parce que la coupure d'alimentation aurait « nettoyé » quoi que ce soit. Le manuel AT définit d'ailleurs le code 3 comme « the MT is in a state which does not allow performing the entered command » (p. 48) — soit exactement « pas prêt pour cette commande ». À noter : CME 3 est un code générique d'état non autorisé ; l'interprétation « provisioning » est la plus probable mais reste à démontrer par instrumentation, et ne doit pas être assimilée d'office à un problème réseau.

4 La racine commune

Dans tous les cas, le bootloader engage la phase de données alors que la pile réseau du modem n'est pas encore totalement prête — sans mécanisme robuste d'attente ou de nouvel essai à cet endroit.

Le levier choisi pour ouvrir la connexion est AT+CGACT=1,1, une commande standard (3GPP TS 27.007) d'activation explicite du contexte PDP, héritée du modèle GPRS où le contexte devait être activé à la main. Or :

Documentation u-blox — contexte PDP en LTE-M

Le manuel des commandes AT le pose noir sur blanc : « The initial EPS bearer established during LTE attach procedure is actually a default EPS bearer. » Le contexte de données par défaut (cid 1) est donc monté par l'attachement réseau lui-même. Forcer ensuite AT+CGACT=1,1 est redondant — et c'est la première commande de données émise, celle qui échoue dès que l'attachement n'est pas tout à fait terminé.

Réf. : LEXI-R4 / SARA-R4 series — AT commands manual (UBX-18068860, R31), §13.1.1 « PDP contexts » p. 189-190 ; +CGACT §13.7 p. 201, +CGPADDR §13.9 p. 205.

Autrement dit, le code force une étape que le réseau fait déjà de lui-même, et l'émet au moment le plus fragile. Les deux codes d'erreur (« no network service » et « operation not allowed ») ne sont que deux façons, pour le modem, de dire « je ne suis pas prêt pour ça maintenant ».

Nuance : la localisation de la panne (au CGACT, après « Registered ») est vérifiée code + log. Le mécanisme exact, lui, reste à confirmer par l'instrumentation (§9) : plusieurs hypothèses concurrentes — rail batterie pas vraiment à zéro au reboot-aimant, provisioning eSIM, voire une composante serveur — ne sont pas encore écartées (cf. niveaux de confiance §10).

5 Une fausse piste, écartée en toute transparence

Une première intuition — « le redémarrage aimant rallume le modem trop vite, il faut forcer un cycle d'alimentation plus propre » — ne résiste pas à la lecture du code.

Le bootloader force déjà un power-off complet à chaque échec. En fin de boucle, il coupe proprement le modem puis met la carte en veille profonde 5 minutes, alimentation modem coupée :

main.cboucle principale — lignes 112-123
while (1)
{
  ret = Application_Loop(&appdata);
  Modem_Reset(&appdata);                      // coupe proprement le modem (voir ci-dessous)
  // If an error occured, sleep to retry
  if (ret < 0 && appdata.FwIsConsistent != 1)
      Sleep(SLEEP_DURATION_SEC);             // 300 s = 5 min, alim modem coupée
  else
      Jump_To_App(&appdata);                 // démarre l'application
}
SARA_R422.cSARA_R422_Power_Off — lignes 422-458 (abrégé)
SARA_R422_Execute_Cmd(SARA_R422, "AT+CPWROFF\r\n");      // extinction logicielle
...
ret = SARA_R422_Wait_Powered(SARA_R422_AT_SUPPLY_OFF_DELAY, GPIO_PIN_RESET); // attend que VINT retombe — modem réellement OFF (2000 ms)
...
HAL_GPIO_WritePin(SARA_R422_POWER_SUPPLY, GPIO_PIN_RESET);  // coupe l'alimentation

Le firmware refait donc déjà, tout seul, l'essentiel de ce qu'un power-off manuel accomplit : coupure de l'alimentation modem + rail coupé 5 minutes + redémarrage à froid. « Forcer un cycle propre » est donc largement redondant — ce n'est pas là qu'est la solution. Cette correction est assumée : mieux vaut écarter une fausse piste que de coder un faux remède.

Confirmé par les tests terrain (22/06)

Quatre essais ont montré : power-off complet = OK, mais reboot-aimant = KO ~2/3. Cela infirme l'opposition « démarrage à froid vs reset logiciel » : l'intermittence est liée au cycle d'alimentation, pas au type de reset. Conséquence : inutile de recoder un « forçage de démarrage à froid » — c'est déjà ce que fait le nouvel essai, et ça ne suffit pas toujours.

6 Le vrai trou : un nouvel essai conditionné au mauvais critère

Si le bootloader sait refaire un cycle propre, pourquoi faut-il quand même une intervention humaine ?

Parce que ce nouvel essai automatique ne se déclenche que si le firmware interne est jugé « non cohérent ». La condition exacte (revoir l'extrait section 5) est :

Ce qui ne va pas

if (ret < 0 && FwIsConsistent != 1) → Sleep(300s) ; else → Jump_To_App

Si le firmware en place est cohérent (FwIsConsistent == 1), un échec de connexion ne déclenche aucun nouvel essai : le bootloader démarre l'application et la tentative OTA est silencieusement abandonnée jusqu'au prochain redémarrage. C'est précisément ce qui oblige aujourd'hui à un power-off manuel.

Conséquence terrain — critique

Sur une sonde déjà montée, on ne peut pas faire de power-off ; la seule relance est un reset long à l'aimant, qui rejoue bien l'OTA mais échoue lui-même ~2 fois sur 3 (le modem repart probablement « tiède », rail batterie pas vraiment à zéro). Résultat : sans nouvel essai interne, la mise à jour dépend de la chance — il faut parfois répéter le geste, et elle peut rester bloquée — alors qu'un simple retry logiciel la fiabiliserait.

Le correctif en découle directement : il faut un nouvel essai de la phase de données, indépendant de la cohérence du firmware — c'est l'automatisation du geste manuel « power-off + redémarrage » constaté sur le terrain.

7 Ce que dit la documentation u-blox

Plusieurs recommandations du fabricant convergent vers le même constat : la séquence actuelle est conçue « à l'ancienne » et mérite d'être alignée sur les bonnes pratiques Cat-M1.

Ce que disent les manuelsImplication pour le bootloader
Le bearer EPS par défaut est établi par la procédure d'attachement LTE (manuel AT, p. 190).Forcer AT+CGACT=1,1 est redondant en Cat-M1 — le contexte est déjà monté par l'attachement.
AT+CGPADDR lit l'adresse IP attribuée au contexte (manuel AT, §13.9, p. 205).Vérifier l'obtention d'une IP (lecture non destructive) plutôt qu'activer le contexte de force.
+CEREG et son URC (AT+CEREG=2, avec TAC/cell-id) sont documentés pour le SARA-R422M10S (manuel AT, §13.14, p. 214-216).Suivre l'enregistrement par URC est plus fiable et plus sobre que le polling actuel toutes les 500 ms.
« VCC supply can be removed only after V_INT goes low » — sinon « unrecoverable faulty state » (System Integration Manual, p. 32).Déjà respecté à l'extinction ; en revanche le power-on ne confirme pas l'extinction avant de réalimenter (voir §8).
Sources

Références vérifiées dans les deux documents u-blox de référence : le LEXI-R4 / SARA-R4 series — AT commands manual (UBX-18068860, R31) et le SARA-R4 series — System Integration Manual (UBX-16029218, R32). Les pages sont citées au fil du texte.

8 Points à améliorer (par ordre de priorité)

Ce sont des spécifications. Elles vont du correctif direct du symptôme aux durcissements de fond.

① Remplacer l'activation forcée par une vérification d'IP priorité haute

Au lieu de AT+CGACT=1,1 (la commande qui échoue), interroger AT+CGPADDR en boucle jusqu'à obtenir une adresse IP valide (≠ 0.0.0.0), avec un délai généreux. C'est une lecture non destructive, conforme au LTE-M, et valable quelle que soit la cause. → Le meilleur correctif unique si on ne devait en faire qu'un.

SARA_R422.cSARA_R422_PDP_Connect — proposition (remplace la ligne 831)
// AVANT (à retirer) :
// AT+CGACT=1,1   -> "+CME ERROR: ..." si l'attachement n'est pas prêt

// APRÈS : attendre que le réseau ait attribué une IP (le contexte s'active seul en Cat-M1)
do {
    SARA_R422_Execute_Cmd(SARA_R422, "AT+CGPADDR=1\r\n");
    // lire "+CGPADDR: 1,<ip>"  -> succès si ip != 0.0.0.0
} while (ip_invalide && !timeout);

② Ajouter un nouvel essai de la phase de données, indépendant de FwIsConsistent priorité haute

Autour de l'appel à SARA_R422_PDP_Connect (application.c, lignes 462-470), prévoir 2 à 3 tentatives qui rejouent un cycle radio propre avant d'abandonner : AT+CFUN=0 puis AT+CFUN=1 force un nouvel attachement sans redémarrage complet. C'est l'automatisation du power-off manuel — la mesure la plus importante pour le terrain (sondes montées).

③ Attendre la disponibilité réelle de la donnée avant de l'utiliser durcissement

L'attente actuelle valide l'enregistrement réseau, mais rien ne garantit ensuite que le service paquet est prêt avant d'émettre la première commande de données. Vérifier explicitement l'attachement paquet (AT+CGATT? = 1) — et idéalement passer l'enregistrement en notification URC (AT+CEREG=2) — comble cet écart. ⚠️ Si on passe en URC, il faut tokeniser le champ <stat> : le strncmp actuel sur « +CEREG: 0,1 » casse avec l'URC « +CEREG: 1,1 ».

④ Reprendre la main sur la sélection d'opérateur au démarrage cas carte neuve

Pour le cas de la carte neuve, démarrer en mode « déconnecté » puis déclencher l'enregistrement de façon explicite évite que le bootloader ne tire des commandes de données pendant que le modem fait encore son provisioning de première fois.

⑤ Renforcer le power-on — seulement si les tests le prouvent dernier recours

Au power-on, la décharge avant réalimentation repose sur de simples temporisations aveugles (1 s avant réalimentation, 1 s après), sans jamais vérifier que le modem est réellement éteint :

SARA_R422.cSARA_R422_Power_On — lignes 394-420 (extrait abrégé)
HAL_GPIO_WritePin(SARA_R422_PWR_ON, GPIO_PIN_RESET);        // (L399)
HAL_GPIO_WritePin(SARA_R422_POWER_SUPPLY, GPIO_PIN_RESET);  // coupe l'alim (L400)
// Wait capacitors to discharge
HAL_Delay(SARA_R422_SUPPLY_ON_DELAY);                       // 1000 ms AVEUGLE, pas de contrôle VINT (L403)
HAL_GPIO_WritePin(SARA_R422_POWER_SUPPLY, GPIO_PIN_SET);    // réalimente (L406)
HAL_Delay(SARA_R422_SUPPLY_ON_DELAY);                       // encore 1000 ms aveugle (L407)

On pourrait attendre le passage à l'état bas de VINT (la primitive existe déjà) et allonger la marge de décharge — la règle u-blox « couper VCC seulement après VINT bas » figure au System Integration Manual, p. 32. Mais : comme le bootloader fait déjà un power-off complet au nouvel essai (§5), ce renforcement n'apporterait, au mieux, qu'un gain de temps au premier démarrage — et rien du tout si la cause réelle est ailleurs (radio, couverture, provisioning). À ne coder que si l'instrumentation prouve que le premier démarrage incomplet est bien en cause.

À part — conformité RAT (touche le bootloader)

Sans rapport direct avec ce bug, mais à traiter côté BL : le bootloader force URAT=7,9 (LTE-M + 2G). Cible décidée : URAT=7,8 (LTE-M + NB-IoT, 0 % 2G), avec cohérence bootloader ↔ application — la 2G n'étant pas certifiée (RED). Non appliqué à ce jour.

9 Tests à mener (avant de figer un correctif)

L'analyse est statique. Une instrumentation légère tranchera entre les causes possibles et confirmera (ou non) chaque hypothèse.

Instrumentation à ajouter

Faire enregistrer par le bootloader, juste avant l'ouverture du contexte de données et en numérotant le passage (1er démarrage / nouvel essai n°…) :

À capturerCe que ça révèle
AT+CMEE=2 (absent du code — à activer)Affiche le numéro d'erreur exact (3 vs 30)
Quelle commande échoue : CGDCONT (L814) ou CGACT (L831)Localise précisément le point de rupture
AT+CEREG?Enregistré / en recherche / refusé
AT+CGATT?Service paquet attaché ou non
AT+CSQQualité du signal (99 = pas de signal → piste radio)
AT+CGPADDRUne IP est-elle déjà attribuée ?
Le test décisif

Faire au moins 10 à 15 redémarrages aimant et regarder si l'échec persiste au deuxième passage automatique (après les 5 minutes de veille) :

  • S'il disparaît tout seul au second passage → c'est bien un problème de premier démarrage / décharge.
  • S'il persiste malgré un cycle complet → la cause est ailleurs (radio, couverture, eSIM) — inutile de toucher à l'alimentation.
Test serveur (en parallèle)

La boucle pourrait avoir une composante serveur non encore écartée. Vérifier ce que renvoie le webservice pour l'IMEI concerné : curl …/otafwupgrade/checksum?imei=<IMEI> → renvoie-t-il la nouvelle cible (5C9C…) ou encore l'ancien checksum (C4C2…) ? Tant que ce point n'est pas vérifié, on ne peut pas affirmer que la boucle est un échec purement réseau.

Table de décision

ObservationCause probableCorrectif visé
CSQ bon + CEREG 1/5 mais CGACT KOContexte data / commande inadaptée① CGPADDR
CSQ=99 ou CEREG 0/2 persistantRadio / couverture / eSIM④ COPS, pas l'alim
Échec disparaît au 2e passagePremier démarrage incomplet⑤ power-on (et ② retry)
Erreur dès CGDCONT sur carte neuveProvisioning non terminé② retry + ④ COPS

Checklist

  • Activer AT+CMEE=2 et recompiler en mode verbeux.
  • Instrumenter le log (code CME, commande fautive, CEREG/CGATT/CSQ/CGPADDR, n° de passage).
  • Série de ≥10 redémarrages aimant — noter persistance ou non au 2e passage.
  • Idem sur carte neuve pour confirmer la piste provisioning.
  • Décider, table en main, quels correctifs figer.

10 Niveaux de confiance

AffirmationConfiance
La phase post-« Starting PDP » est fragile, sans nouvel essai indépendant de la cohérence firmwareÉlevée — vérifié dans le code
Le BL fait déjà un power-off complet de 5 min au nouvel essaiÉlevée — vérifié dans le code
AT+CGACT=1,1 est le mauvais levier en Cat-M1Élevée (localisation) — le « redondant » est une interprétation appuyée sur la doc, pas une affirmation du fabricant
Carte neuve = course de provisioning au premier boot (SIM/eSIM)Modérée — cohérent, à confirmer par l'instrumentation
« CGPADDR au lieu de CGACT » comme correctifModérée — la vérif IP est solide, le remplacement reste à valider au banc
Le renforcement du power-on changerait quelque choseÀ tester — dépend du test décisif
Cadrage

Le sujet est le durcissement de la fiabilité OTA — déterminant pour le déploiement terrain, où une sonde montée ne peut être relancée qu'à l'aimant. C'est un travail de robustesse, à confirmer par l'instrumentation avant tout correctif.

11 Carte du code (repères)

ÉlémentFichier — lignes
Boucle principale + logique de nouvel essaimain.c — 112-123
Séquence de la boucle applicative (étapes 1-5)application.c — 416-470
Critère de cohérence firmware (FwIsConsistent)application.c — 388, 400, 530, 615-616
Ouverture du contexte de données (point de panne)SARA_R422.c — 809-859
Attente d'enregistrement réseau (CREG + CEREG)SARA_R422.c — 190-241
Power-on (décharge aveugle, 2 × 1 s)SARA_R422.c — 394-420
Power-off (CPWROFF + attente VINT + coupure alim ; early-return si alim déjà coupée)SARA_R422.c — 422-458
Base de référence

Analyse réalisée sur le bootloader v0.1.9, branche rapatriée à jour. Les extraits ci-dessus sont des spécifications, pas des modifications appliquées.