# Bibliothèque C embarquée

> Créez un client d'appareil C99 pour les hubs Thalovant avec votre propre transport MQTT ou WebSocket.

Source: https://docs.thalovant.com/fr/developers/sdks/embedded-c/

---
{/* Last Edit: Claude Opus 5.5 - 2026-09-28 - Motive: Canadian French translation of developers/sdks/embedded-c.mdx. */}

Utilisez la bibliothèque C embarquée quand votre client est un microcontrôleur ou un petit appareil, comme un ESP32, un appareil Zephyr, un système sans système d'exploitation (bare-metal) ou un ordinateur monocarte sous Linux.

Il s'agit d'une bibliothèque de protocole, pas d'un SDK complet. Elle est indépendante du transport : vous apportez votre propre client MQTT ou WebSocket et votre propre pile TLS, et la bibliothèque fournit tout ce qui est propre au protocole. Elle est écrite en C99 pur, sans aucune dépendance externe, et ses chemins principaux n'allouent jamais de mémoire; chaque fonction écrit dans des tampons fournis par l'appelant.

Code source et versions : [thalovant-embedded-c sur GitHub](https://github.com/thalovant/thalovant-embedded-c).

La bibliothèque donne à un appareil tout ce qu'il lui faut pour ouvrir une session chiffrée et authentifiée avec un hub; votre application fournit les sockets ou la connexion au courtier et pilote l'échange. Les anciennes fonctions d'enveloppe restent disponibles pour les intégrations existantes, mais elles ne peuvent pas se connecter à un hub actuel.

## Ce que la bibliothèque fournit

  
    Analysez le JSON d'identité émis par l'API Thalovant, avec les mêmes alias de champs que ceux qu'acceptent les SDK Node et Go.
  
  
    Dérivez les sujets (topics) MQTT propres à chaque client, les points de terminaison de connexion, les ports et les identifiants de client, identiques octet pour octet à ceux du SDK Node.
  
  
    Chiffrement authentifié de chaque message, vérifié avec des vecteurs de test indépendants.
  
  
    Construisez et analysez les messages du hub, menez l'échange d'authentification et chiffrez chaque message, avec des fonctions pour les requêtes ask et leurs réponses.
  

## Ce que vous fournissez

| Vous fournissez | La bibliothèque fournit |
| --- | --- |
| Un client MQTT, WebSocket ou les deux | Les sujets, les points de terminaison, les codecs de trames et d'enveloppes |
| Une pile TLS | L'indicateur TLS ainsi que l'analyse du schéma et du port |
| Un générateur aléatoire cryptographiquement sûr | Vous fournissez une clé de client persistante et une nouvelle clé éphémère pour chaque connexion; la bibliothèque gère les compteurs de messages |
| Une boucle d'événements et des minuteries | Le classificateur de trames et la sémantique de la boucle ask |
| Un stockage protégé pour l'identité, la clé du client et l'identité de hub enregistrée | L'analyseur d'identité et un échange d'authentification qui vérifie l'identité de hub enregistrée |

La bibliothèque n'a pas de client pour le plan de contrôle. Créez l'identité de client au moyen du [tableau de bord](/fr/manage/connect-a-client/), de l'API ou d'un autre SDK, puis stockez le JSON d'identité sur l'appareil.

## Obtenir une version

Utilisez un nouvel identifiant de requête Ask et un nouvel identifiant Query pour chaque opération logique, y compris les appels ultérieurs. Gardez les identifiants simultanés uniques dans chaque espace de noms correspondant; les espaces de noms Ask et Query sont indépendants. Cette bibliothèque laisse la gestion des collecteurs à votre transport. Vérifiez chaque résultat négatif d'un constructeur ou d'un codec avant de convertir une longueur ou de publier des octets.

L'analyseur superficiel de clés d'objet valide l'objet direct au complet avant de retourner un champ correspondant, y compris les séparateurs qui suivent ce champ. Les conteneurs imbriqués conservent leurs règles d'analyse superficielle documentées.

Intégrez la bibliothèque à votre dépôt (vendoring) ou récupérez-la par une étiquette de version immuable. L'étiquette actuelle est `v0.8.0`.

```sh
git submodule add https://github.com/thalovant/thalovant-embedded-c.git \
    third_party/thalovant-embedded-c
git -C third_party/thalovant-embedded-c checkout v0.8.0
```

`FetchContent` de CMake, les références de composants ESP-IDF et les manifestes west de Zephyr fonctionnent de la même façon avec l'étiquette `v0.8.0`. Chaque version comprend une archive des sources et un fichier `SHA256SUMS` pour vérifier votre téléchargement.

La compilation n'exige qu'un compilateur C99 :

```sh
make            # build/libthalovant.a
make test       # host-side, offline test suite
```

## Aperçu de l'intégration

Consultez le [guide d'intégration](https://github.com/thalovant/thalovant-embedded-c/blob/main/docs/noise-v3.md) du dépôt pour l'API complète et la séquence de transport :

1. Analysez l'identité et connectez votre transport protégé par TLS. Connectez-vous au point de terminaison avec les identifiants de l'identité.
2. Conservez au complet le message d'accueil et l'offre du serveur, et choisissez les options communes avec `thalovant_noise_select`.
3. Dérivez le secret de session à partir du mot de passe de l'identité et de l'identifiant de nœud du serveur avec `thalovant_noise_psk`. Il faut 64 Mio de mémoire de travail `uint64_t` appartenant à l'appelant, que la fonction efface ensuite. Un appareil plus petit doit recevoir le **même secret** par un provisionnement sécurisé, après une dérivation faite hors de l'appareil; n'affaiblissez jamais la dérivation.
4. Initialisez la session avec `thalovant_noise_init`, votre clé de client protégée, une nouvelle clé éphémère générée de façon cryptographiquement aléatoire et toute identité de hub enregistrée. Échangez les messages d'authentification avec `thalovant_noise_write` et `thalovant_noise_read`.
5. Vérifiez et enregistrez l'identité du hub. Envoyez le message d'accueil chiffré du client avant les messages de l'application.
6. Construisez le JSON d'une requête ask ou d'une intention avec les fonctions de trame, chiffrez-le avec `thalovant_noise_encrypt` et déchiffrez les réponses avec `thalovant_noise_decrypt` avant de les classer. Toute erreur d'authentification ou de tramage ferme la session.

Votre transport doit garder ensemble tous les fragments d'un même message, plafonner les trames chiffrées à 65 535 octets et découper les grosses données utiles en fragments de 65 000 octets. Réassemblez-les dans un tampon borné appartenant à l'appelant, d'au plus 32 Mio; choisissez une limite plus petite si l'appareil l'exige. Une reconnexion exige une nouvelle clé éphémère, tout en conservant la clé du client et l'identité de hub enregistrée.

  Stockez les identifiants du client, les secrets dérivés, les clés de client et les identités de hub enregistrées dans un stockage protégé. Ne les incluez jamais dans des images de micrologiciel publiées. Un changement d'identité d'un hub doit être confirmé par le propriétaire du hub avant que vous l'enregistriez.

## Lister ce qu'on peut demander au hub

Un client connecté peut lister chaque intention à laquelle son hub répond, par langue, avec les phrases qu'une personne prononce pour atteindre chacune, dans sa propre session et sans jeton du plan de contrôle. Consultez [Que peut-on demander à mon hub?](/fr/concepts/hub-intents/) pour savoir ce que le hub retourne, quels types de messages la connexion doit être autorisée à publier, et à quoi ressemblent un refus ou une liste en échec.

Depuis la version `v0.2.0`, la bibliothèque prend en charge le côté protocole de cette fonction, dans le même style que `thalovant_ask_*`. `thalovant_intent_list_build_frame` et `thalovant_intent_describe_build_frame` construisent les trames `ovos.intent.list` et `ovos.intent.describe`, et `thalovant_intent_classify` lit une réponse dans un `thalovant_intent_event`. Votre transport scelle et envoie les trames, lui retransmet les réponses et les met en correspondance grâce à l'identifiant de requête que vous définissez.

```c
struct inventory_query { const char *request_id; bool answered; };

static bool on_row(const thalovant_intent_registration *row, void *user)
{
    printf("%s:%s (%s) %s\n", row->skill_id, row->intent_name, row->lang, row->method);
    return true;                       /* false stops the walk */
}

struct inventory_query query = { "req-2", false };
thalovant_intent_list_request list = { "en-us", "sess-1", identity.site_id,
                                       query.request_id, false };
if (thalovant_intent_list_build_frame(&list, frame, sizeof(frame)) < 0) {
    /* THALOVANT_ERR_NOMEM: frame[] too small; nothing was built to send */
}
/* seal and publish the frame, then classify each reply that arrives */

thalovant_intent_event reply;
thalovant_intent_classify(plaintext, plaintext_len, query.request_id, &reply);
switch (reply.kind) {
case THALOVANT_INTENT_LIST_RESPONSE:
    if (query.answered) break;         /* ignore a duplicate reply */
    query.answered = true;
    thalovant_intent_list_rows(&reply, on_row, NULL);
    break;
case THALOVANT_INTENT_POLICY_DENIED:   /* reply.denied_type names the query */
    break;
default: break;                        /* THALOVANT_INTENT_IGNORE */
}
```

Les fonctions de parcours livrent un élément à la fois au moyen d'une fonction de rappel : un manifeste de n'importe quelle taille se lit donc dans une mémoire bornée. `thalovant_intent_list_rows` parcourt les enregistrements, `thalovant_intent_definitions` une réponse de description, et `thalovant_intent_samples` les phrases derrière une définition. `thalovant_intent_same_language` normalise les étiquettes de langue comme le font les autres SDK : `fr-fr` et `fr_FR` correspondent donc. Le `method` d'une ligne vaut `template` ou `keyword`, que les SDK complets exposent comme les moteurs `padatious` et `adapt`.

Un refus arrive sous la forme `THALOVANT_INTENT_POLICY_DENIED`, avec le type refusé dans `reply.denied_type`. Une liste que le hub a acceptée puis n'a pas réussi à produire est un cas différent : depuis la `v0.3.0`, `thalovant_intent_list_rows` retourne `THALOVANT_ERR_HUB_REFUSED` avec les mots du hub dans `reply.error`, parce qu'une liste en échec n'est pas un hub vide. `thalovant_intent_definitions` retourne zéro définition pour la même réponse à une description, qui est une vraie réponse : le hub ne connaît pas cet enregistrement. La mise en correspondance, les nouvelles tentatives et le modèle de résultat sont à votre charge; consultez [Que peut-on demander à mon hub?](/fr/concepts/hub-intents/) pour la structure que les SDK complets construisent à partir des mêmes réponses.

## Quand utiliser plutôt un SDK complet

Utilisez [Python](/fr/developers/sdks/python/), [Node.js](/fr/developers/sdks/node/), [Go](/fr/developers/sdks/go/) ou [Rust](/fr/developers/sdks/rust/) quand l'appareil peut exécuter un environnement d'exécution complet. Ces SDK gèrent pour vous le transport, la logique de reconnexion et les appels au plan de contrôle. Consultez [MQTT](/fr/developers/mqtt/) pour les détails sur le courtier (broker) et la portée des sujets.

## Problèmes courants

| Symptôme | Vérification |
| --- | --- |
| L'analyse de l'identité échoue | Vérifiez que le JSON stocké est le contenu d'identité intact provenant de l'API ou du téléchargement dans le tableau de bord. |
| Le courtier refuse la connexion | Vérifiez le point de terminaison dérivé, le port et l'indicateur TLS, et utilisez les identifiants de courtier propres au client qui se trouvent dans l'identité. |
| Les réponses ne sont jamais classées | Vérifiez que le sujet d'abonnement est `topics.outbound` et que l'identifiant de requête correspond à celui envoyé dans la trame ask. |
| L'authentification ou le déchiffrement échoue | Vérifiez le mot de passe, l'identifiant de nœud du serveur, l'identité de hub enregistrée et l'ordre des trames. Fermez la session en échec; ne réessayez pas avec les anciennes enveloppes et ne supprimez pas automatiquement l'identité de hub enregistrée. |
## Trames des gestionnaires de repli

`thalovant_fallback_list_build_payload` et `thalovant_fallback_list_build_frame` construisent la liste des gestionnaires de repli. Les deux utilisent `thalovant_intent_list_request` pour le contexte de session et de requête, et envoient `ovos.skills.fallback.list` avec un objet de données vide; les gestionnaires ne dépendent pas de la langue. `thalovant_intent_classify` reconnaît la réponse comme `THALOVANT_FALLBACK_LIST_RESPONSE` et expose le tableau `data.fallbacks` par `items_json`, `items_len` et `count`.

L'application gère la sonde facultative de 1,5 seconde, l'analyse des priorités, le tri et le modèle de résultat. Distinguez une réponse indisponible, en échec ou malformée d'une liste explicitement vide. L'absence de phrases d'intention ne suffit pas à établir qu'un hub ne répondra pas. Consultez [la découverte des gestionnaires de repli](/fr/concepts/hub-intents/#gestionnaires-de-repli-et-disponibilité-des-langues).

La bibliothèque prend en charge GCC, Clang et les cibles 32 bits. Elle utilise C99, des tampons appartenant à l'appelant et le transport que vous fournissez.

## Indications de requête, exemples et audio intégré des compétences

`thalovant_ask_build_payload_with_hints` et `thalovant_ask_build_frame_with_hints`
acceptent `thalovant_ask_hints` : `stt_lang`, un tableau de chaînes JSON `pipeline_json`
et un objet JSON `location_json`. Les espaces autour de la langue et des étapes du pipeline sont retirés;
les étapes vides sont omises. Construisez l'objet d'emplacement avec
`thalovant_build_location`; une ville est obligatoire, et les coordonnées invalides ou nulles sont
omises. Les constructeurs existants conservent leurs octets d'origine sur le réseau.

La trame complète utilise directement le tampon de sortie de l'appelant. Chaque étape de pipeline
décodée doit tenir dans `THALOVANT_ASK_TEXT_MAX`, terminateur compris;
le JSON des indications doit tenir dans `THALOVANT_WIRE_MAX_TOKENS`. Ces limites fixées à la compilation gardent
les tampons de l'analyseur lexical et de la normalisation bornés, sans allocation dynamique.

`thalovant_speakable` rend un motif d'intention dans un tampon appartenant à l'appelant, avec
des remplacements `thalovant_speakable_slot` facultatifs. Classez les exemples rendus selon
la présence d'emplacements dans le motif d'origine, conservez le meilleur rang lors de la déduplication
et appliquez ensuite la limite demandée.

`thalovant_ask_classify` reconnaît `THALOVANT_ASK_AUDIO` sans le traiter comme un
échec. L'audio ne règle ni ne prolonge jamais une requête ask : conservez son ordre à côté de la parole
dans la fenêtre de réponse existante. `thalovant_ask_event_language` retourne la
première langue non vide trouvée dans les données de l'événement, le contexte ou la session.
`thalovant_ask_audio_decode` valide la correspondance et ne décode que l'audio hexadécimal
intégré, dans un stockage appartenant à l'appelant; l'espace de travail contient la chaîne
hexadécimale sans échappement. Elle ne lit jamais de fichier ni d'URL. Vérifiez la longueur ou l'erreur retournée avant
de lire les octets. Les limites des tampons peuvent être inférieures au maximum de 4 Mio par extrait.

Initialisez à zéro `thalovant_audio_budget` pour chaque réponse et appelez
`thalovant_audio_budget_accept` avant de conserver de l'audio encodé. Cette fonction plafonne chaque
extrait à 4 Mio et l'ensemble des extraits d'une réponse à 16 Mio (bornes supérieures encodées); les extraits
refusés incrémentent `dropped` et n'ont pas d'effet sur la collecte de la parole. Dédupliquez
les livraisons du transport avant la collecte. La bibliothèque reste indépendante du transport;
la gestion des compétences et de la configuration par le plan de contrôle relève des clients des SDK gérés.

## Revendication des réponses

`thalovant_ask_event_pipeline_id()` et `thalovant_ask_event_skill_id()` extraient les marques de type chaîne des trames de réponse reconnues et mises en correspondance, dans des tampons fournis par l'appelant. Conservez les identifiants distincts dans l'ordre de première apparition, dans votre propre stockage. `thalovant_reply_claimed()` classe la réponse selon son statut traité/échec et les identifiants de pipeline conservés. Un NUL intégré est refusé; des tampons insuffisants retournent `THALOVANT_ERR_NOMEM`.

Les identifiants de pipeline et de compétence sont des chaînes non vides, dans l'ordre de première apparition, sans doublons. Une réponse réussie dont les identifiants de pipeline contiennent tous le texte `fallback` (sensible à la casse) est signalée comme non revendiquée. Toute étape autre qu'un repli rend une réponse réussie revendiquée. Les réponses réussies de hubs plus anciens, sans marques d'étape, restent revendiquées. Les réponses en échec ou non traitées ne sont jamais revendiquées. Les marques malformées qui ne sont pas des chaînes sont ignorées.

Utilisez cette information comme indice pour décider de poursuivre ou non une conversation. Le texte de repli et le statut de réussite existant sont conservés. Ces marques ne constituent pas une identité de pair vérifiée et ne doivent pas autoriser d'actions.
