Autres articles / Other articles

Décodage de la transmission LoRa avec ESP32Forth

publication: 22 janvier 2022 / mis à jour 31 janvier 2022

Read this page in english

 

Appel à collaboration

Vous développez des montages, simples ou complexes avec ESP32 et ESP32forth.

Partagez-les ici sur ce site.

ESP32forth ne pourra se développer qu'avec la collaboration active de toutes les bonnes volontés.

Vos montages peuvent aider d'autres développeurs.

Les montages des autres développeurs peuvent vous aider.

Pour proposer un article ou un montage, cliquez ici


 

Listing complet: Decoding LoRa transmission with ESP32Forth


Environnement de développement requis

Pour décoder la chaîne reçue par le transmetteur LoRa REYAX RYLR890, il faut:

On reconstitue la variable alphanumérique LoRaRX et on lui affectue un contenu identique à celui reçu par le transmetteur LoRa SLAV2 (voir précédent article):

256 string LoRaRX 
 
s"+RCV=55,27,this is a transmission test,-36,40 " LoRaRX $! 
$0d LoRaRX c+$! 
$0a LoRaRX c+$! 

Décodage de la transmission

Ici, le dump mémoire de LoRaRX strictement identique à celui reçu par le transmetteur LoRa:

--addr---  00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  ------chars-----
3FFF-84D0  E0 BB FF 3F 00 01 00 00 30 00 00 00 2B 52 43 56  ...?....0...+RCV
3FFF-84E0  3D 35 35 2C 32 37 2C 74 68 69 73 20 69 73 20 61  =55,27,this is a
3FFF-84F0  20 74 72 61 6E 73 6D 69 73 73 69 6F 6E 20 74 65   transmission te
3FFF-8500  73 74 2C 2D 33 36 2C 34 30 20 0D 0A FB 9B 52 60  st,-36,40 ....R`

On va se débarrasser des deux octets 0D 0A en créant le mot normalize$:

2 string $crlf 
  $0d $crlf c+$! 
  $0a $crlf c+$! 
 
\ delete crlf at end of string 
: normalize$ ( addr len -- ) 
    2dup + 2 - 2 
    $crlf $= if         \ if end string = crlf 
        2 - swap 
        cell - !        \ substract 2 at length of string 
    else 
        2drop 
    then 
  ; 

Le mot normalize$ teste si la chaîne qui lui est présentée se termine par 0D 0A. Si c'est le cas, la taille de la chaîne est diminuée de deux unités.

Une fois normalisée, la chaîne de caractères du message LoRa peut être analysée:
  +RCV=55,27,this is a transmission test,-36,40

Décomposition du message LoRa

Nous allons décomposer ce message:

Pour mémoriser chacun de ces éléments, donc permettre un traitement avec d'autres mots, on va créer six chaînes alphanumériques:

\ RCV analyse 
12  string RCVhead      \ head transmission 
6   string RCVaddr      \ master LoRa address 
4   string RCVlength    \ length transmitted datas 
256 string RCVdata      \ transmitted datas 
5   string RCVrssi      \ Received Signal Strength Indicator 
5   string RCVsnr       \ Signal-to-noise ratio 

Pour voir le contenu de ces variables alphanumériques, on crée le mot disVar. ce mot n'aura pas d'autre utilité plus tard:

: disVars ( -- ) 
    cr 
    ." RCVhead.....: " RCVhead      type cr 
    ." RCVaddr.....: " RCVaddr      type cr 
    ." RCVlength...: " RCVlength    type cr 
    ." RCVdata.....: " RCVdata      type cr 
    ." RCVrssi.....: " RCVrssi      type cr 
    ." RCVsnr......: " RCVsnr       type cr 
  ; 

Décomposition de la transmission LoRa

Le but de notre code sera de placer dans chaque variable alphanumérique RCVhead RCVaddr RCVlength RCVdata RCVrssi RCVsnr RCVsnr les différents champs du message reçu par le transmetteur LoRa.

On va d'abord créer un mot qui remet à zéro ces variables alphanumériques:

\ set length of all strings to 0 
: initStrings ( -- ) 
    RCVhead   0$! 
    RCVaddr   0$! 
    RCVlength 0$! 
    RCVdata   0$! 
    RCVrssi   0$! 
    RCVsnr    0$! 
  ; 

On définit ensuite RCV? qui détermine si la transmission reçue commence par +RCV=. Ce mot évitera de traiter un message qui n'est pas issu d'une transmission LoRa:

\ test if string begin with "+RCV=" 
: RCV? ( addr len -- fl ) 
    dup 0 > if 
        drop 5 
        s" +RCV=" $= 
    else 
        2drop 0 
    then 
  ; 

On définit ensuite le mot scan$ qui recherche la présence d'un caractère donné. L'analyse de la chaîne s'interrompt si le caractère recherché est trouvé et ajuste la longeur de la chaîne analysée:

: scan$ { char addr len -- addr len' } 
    0                   \ start index 
    begin 
        dup addr + c@ 
        char <> 
        over len < 
        and 
    while 
        1+              \ increment index 
    repeat 
    addr swap 
    dup len = if 
        drop 0 
    then 
  ; 

Le code FORTH qui suit contient beaucoup de petites définitions. C'est volontaire. Cette méthode permet de vérifier chaque définition via le terminal. Il est possible d'optimiser le code, mais le but est en priorité de définir des mots efficaces et fonctionnels et faciles à comprendre.

Le mot calcNewPosition restitue la sous-chaîne issue de LoRaRX en prenant en compte chaque variable alphanumérique extraite:

variable strPos 
: calcNewPosition ( -- addr len ) 
    0 strPos ! 
    RCVhead nip ?dup 
    if  strPos +!    then 
    RCVaddr nip ?dup 
    if  1+ strPos +! then 
    RCVlength nip ?dup 
    if  1+ strPos +! then 
    RCVdata   nip ?dup 
    if  1+ strPos +! then 
    RCVrssi   nip ?dup 
    if  1+ strPos +! then 
    LoRaRX swap strPos @ + 
    swap strPos @ - 
  ; 

On part du groupe de variables alphanumériques RCVhead RCVaddr RCVlength RCVdata RCVrssi RCVsnr RCVsnr, donc toutes avec une taille nulle. On extrait le premier champ de LoRaRX vers RCVhead. Ce n'est pas une vraie extraction, mais une instanciation qui ne s'effectuera que si LoRaRX commence par +RCV=:

\ extract RCVhead 
: getRCVhead ( -- ) 
    s" +RCV=" RCVhead $! 
  ; 

L'exécution de ce mot getRCVhead modifiera le comportement du mot calcNewPosition. Chaque mot qui récupère le contenu d'une variable alphanumérique aura donc une action sur le comportement de calcNewPosition:

\ extract RCVaddr 
: getRCVaddr ( -- ) 
    [char] , calcNewPosition scan$ 
    RCVaddr $! 
  ; 
 
\ extract RCVlength 
: getRCVlength ( -- ) 
    [char] , calcNewPosition scan$ 
    RCVlength $! 
  ; 
 
: eval$ ( addr len -- n ) 
    S>NUMBER? ?dup 
    if drop then 
  ; 
 
\ extract RCVdata 
: getRCVdata ( -- ) 
    calcNewPosition drop 
    RCVlength eval$ 
    RCVdata $! 
  ; 
 
\ extract RCVrssi 
: getRCVrssi ( -- ) 
    [char] , calcNewPosition scan$ 
    RCVrssi $! 
  ; 
 
\ extract RCVrssi 
: getRCVsnr ( -- ) 
    calcNewPosition 
    RCVsnr $! 
  ; 

Il n'était pas possible de découper les champs de LoRaRX comme si c'était un enregistrement dans une base de données. certes, on a comme séparateur le caractère ",", mais si ce caractère se retrouve dans le contenu du champ RCVdata on risque de perturber un décodage s'appuyant sur un scan simple. D'ailleurs, le mot getRCVdata s'appuie sur le contenu du champ RCVlength en évaluant son contenu pour passer au champ suivant.

Et enfin, on arrive au mot RXdecode qui fait l'analyse du contenu de LoRaRX. Cette analyse ne s'effectue que si le contenu de LoraRX commence par +RCV=:

: RXdecode ( -- ) 
    LoRaRX normalize$ 
    \ extract RCVhead 
    LoRaRX RCV? if          \ test if LoRaRX begin with "+RCV=" 
        initStrings         \ empty all strings 
        getRCVhead 
        getRCVaddr 
        getRCVlength 
        getRCVdata 
        getRCVrssi 
        getRCVsnr 
    then 
  ; 

Il suffit d'exécuter RXdecode puis de voir le contenu des variables alphanumériques en tapant disVars:

--> disVars 
RCVhead.....: +RCV= 
RCVaddr.....: 55 
RCVlength...: 27 
RCVdata.....: this is a transmission test 
RCVrssi.....: -36 
RCVsnr......: 40 

On va vous donner une première piste pour l'interface avec d'autres programmes.

Le secret, c'est l'évaluation du contenu de la variable alphanumérique RCVdata. Si on a un relais à activer avec la séquence FORTH relay01 on, il suffit que le transmetteur LoRa envoie comme contenu du message la chaine FORTH relay01 on. Le mot FORTH de ESP32Forth qui effectuera l'interface est evaluate...

Et c'est tout!!!!

C'est ce que nous verrons dans l'article suivant...


Legal: site web personnel sans commerce / personal site without seling