RadioTest (Ping de Notification)

Une simple application de test RadioShuttle (« RadioTest ») est destinée aux débutants. Il y a deux appareils qui communiquent entre eux et échangent un simple message. Un appareil est appelé un nœud et le second est une station. Un message est transmis du nœud à la station par simple pression d’un bouton (bouton « A »). Cela fonctionne également dans l’autre sens, la station peut également transmettre un message au nœud, en supposant que le nœud a été configuré comme Nœud en ligne (par défaut est Nœud hors ligne).

Code d’exemple de RadioShuttle RadioTest

Cet exemple est inclus dans le produit RadioShuttle, voici le code simplifié :

/*
 * The file is Licensed under the Apache License, Version 2.0
 * (c) 2017 Helmut Tschemernjak
 * 30826 Garbsen (Hannover) Germany
 */
  
#include "PinMap.h"
#include <arduino-mbed.h>
#include <sx1276-mbed-hal.h>
#include <RadioShuttle.h>
#include <RadioStatus.h>
#include <RadioSecurity.h>
#include <arduino-util.h>
#include <RTCZero.h>
// #define RADIO_SERVER 1
  
#ifdef RADIO_SERVER
bool server = true;
#else
bool server = false;
#endif
  
enum SensorsIDs { // Must be unique world wide.
  myTempSensorApp = 0x0001,
#ifdef RADIO_SERVER
  myDeviceID = 1,
  myCode = 0x20EE91D6, // station board id and code
  remoteDeviceID = 9,
#else
  myDeviceID = 9,
  myCode = 0x20EE91DE, // node board id and code
  remoteDeviceID = 1,
#endif
};

Dans la section ci-dessus, il est déterminé quel ID d’application et quels numéros d’appareil sont utilisés par station et par nœud.

/*
 * For details review: SX1276GenericLib/sx1276/sx1276.h
 * Supported spreading factors SF 7,8, 9, 10, 11, (12 does not work well)
 * Working frequencies using the 125000 bandwidth which leaves
 * sufficient distance to the neighbour channel
 * EU: 868.1, 868.3, 868.5 (Default LoRaWAN EU channels)
 * EU: 865.1, 865.3, 865.5, 865.7, 865.9 (additional channels)
 * EU: 866.1, 866.3, 866.5, 866.7, 866.9 (additional channels)
 * EU: 867.1, 867.3, 867.5, 867.7, 867.9 (additional channels)
 * Utilisation of these channels should not exceed 1% per hour per node
 * Bandwidth changes other than 125k requires different channels distances
 */
const RadioShuttle::RadioProfile myProfile[] = {
/*
 * Our default profile
 * frequency, bandwidth, TX power, spreading factor
 */
 { 868100000, 125000, 14, 7 },
 { 0, 0, 0, 0 },
};

La fréquence, la largeur de bande du canal, la puissance d’émission et le facteur d’étalement de LoRa sont déterminés ici. Ces données doivent être identiques pour tous les participants, donc ne changez rien lorsque vous effectuez un premier test !

DigitalOut led(LED);
InterruptIn intr(SW0);
volatile int pressedCount = 0;
  
void SwitchInput(void) {
 dprintf("SwitchInput");
 led = !led;
 pressedCount++;
}
  
Radio *radio; // the LoRa radio modem
RadioShuttle *rs; // the RadioShuttle protocol
RadioStatusInterface *statusIntf; // the status add-on
RadioSecurityInterface *securityIntf; // the security 

Ici, quelques variables globales pour la LED et le bouton « A » sont définies, ainsi qu’une fonction nommée « SwitchInput » qui est appelée après que le bouton a été pressé. InterruptIn et DigitalOut sont des fonctions compatibles mbed. Bien sûr, les fonctions Arduino peuvent également être utilisées.

void setup() {
  MYSERIAL.begin(230400);
  InitSerial(&amp;amp;MYSERIAL, 3000); // wait 2000ms that the Serial Monitor opens, otherwise turn off USB.
  SPI.begin();
  rtc.begin();
  
  dprintf("USBStatus: %s", SerialUSB_active == true ? "SerialUSB_active" : "SerialUSB_disbaled");
  
  led = 1;
  intr.mode(PullUp);
  intr.fall(callback(&amp;SwitchInput));
  dprintf("MyRadioShuttle");
  
  RSCode err;
  RadioStatusInterface *statusIntf = NULL;
  RadioSecurityInterface *securityIntf = NULL;
  
  // RFM95
  radio = new SX1276Generic(NULL, RFM95_SX1276,
        LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5);
  
  statusIntf = new MyRadioStatus();
  securityIntf = new RadioSecurity();
  rs = new RadioShuttle("MyRadioShuttle");
  err = rs-&gt;AddLicense(myDeviceID, myCode);
  if (err)
    return;
  
  rs->AddRadio(radio, MODEM_LORA, myProfile);
  rs->AddRadioStatus(statusIntf);
  rs->ampAddRadioSecurity(securityIntf);
  rs->RegisterApplication(myTempSensorApp, &amp;amp;TempSensorRecvHandler)
  if (server) {
    err = rs->Startup(RadioShuttle::RS_Station_Basic);
    dprintf("Startup as a Server: Station_Basic ID=%d", myDeviceID);
  } else {
    err = rs->Startup(RadioShuttle::RS_Node_Online);
    dprintf("Startup as a Node: RS_Node_Online ID=%d", myDeviceID);
  }
}

L’initialisation ci-dessus active le module radio (RFM95) et le logiciel de protocole RadioShuttle correspondant avec un indicateur d’état « MyRadioStatus » qui fait clignoter les LED de réception et d’émission en activité. Le nœud et le serveur seront alors démarrés par la fonction « Startup ».

void loop() {
  static int cnt = 0;
  
  if (cnt != pressedCount) {
    int flags = RadioShuttle::MF_NeedsConfirm; // optional
    if (server) {
      static char msg[] = "The server feels very good today";
      rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID);
    } else {
      static char msg[] = "Hello, the temperature is 26 celsius";
      rs->SendMsg(myTempSensorApp, msg, sizeof(msg), flags, remoteDeviceID);
    }
    cnt = pressedCount;
  }
  led = 0;
  sleep() // timer and radio interrupts will wakeup us
  led = 1;
  rs->RunShuttle(); // process all pending events
}

La « loop » principale vérifie si le bouton a été actionnée et envoie un message à la station homologue. La fonction sleep interrompt l’application jusqu’à ce qu’un nouveau message apparaisse, c’est-à-dire jusqu’à ce que le bouton ait été enfoncé ou qu’une autre activité interrompt le sommeil. En cas d’activité, la LED s’allume, en cas d’activité élevée (par ex. une fenêtre « Moniteur sériel » USB ouverte) la LED clignote très rapidement (qui ressemble presque à une lumière permanente). Avec une prise USB débranchée et un redémarrage (Reset), la LED doit être éteinte et simplement clignoter en cas d’activité.

L’exemple de code de RadioShuttle inclut une variante deepsleep(), qui a désactivé l’USB et économise ainsi une quantité substantielle d’énergie.

void TempSensorRecvHandler(int AppID, RadioShuttle::devid_t stationID, int msgID, int status, void *buffer, int length)
{
  switch(status) {
  case RadioShuttle::MS_SentCompleted: // A SendMsg has been sent.
    dprintf("MSG_SentCompleted: id=%d %d bytes", msgID, length);
    break;
  case RadioShuttle::MS_SentCompletedConfirmed:// A SendMsg has been sent and confirmed
    dprintf("MSG_SentCompletedConfirmed: id=%d %d bytes", msgID, length);
    break;
  case RadioShuttle::MS_SentTimeout: // A timeout occurred, number of retires exceeded
    dprintf("MSG_SentTimeout ID: %d", msgID);
   break;
  case RadioShuttle::MS_RecvData: // a simple input message
    dprintf("MSG_RecvData ID: %d, len=%d", msgID, length);
    // dump("MSG_RecvData", buffer, length);
    break;
  case RadioShuttle::MS_RecvDataConfirmed: // received a confirmed message
    dprintf("MSG_RecvDataConfirmed ID: %d, len=%d", msgID, length);
    // dump("MSG_RecvDataConfirmed", buffer, length);
    break;
  case RadioShuttle::MS_NoStationFound:
    dprintf("MSG_NoStationFound");
    break;
  case RadioShuttle::MS_NoStationSupportsApp:
    dprintf("MSG_NoStationSupportsApp");
    break;
  case RadioShuttle::MS_AuthenicationRequired: // the password does not match.
    dprintf("MSG_AuthenicationRequired");
    break;
  case RadioShuttle::MS_StationConnected: // a confirmation that the connection was accepted
    dprintf("MSG_StationConnected");
    break;
  case RadioShuttle::MS_StationDisconnected: // a confirmation that the disconnect was accepted
    dprintf("MSG_StationDisconnected");
    break;
  default:
    break;
  }
}

Le Receive Handler (« gestionnaire de réception ») est appelé chaque fois qu’un message a été envoyé, qu’un nouveau message arrive ou qu’une erreur s’est produite. Selon l’application, plusieurs gestionnaires de réception peuvent être définis, un par ID d’application.

Réception du message

En général, une transmission de message est confirmée si l’indicateur RadioShuttle::MF_NeedsConfirm est activé dans la fonction SendMsg. De cette façon, le nœud qui envoie le message n’est notifié de la transmission terminée que lorsque la station homologue a reçu les données. Cela permet d’éviter que le message ne soit reçu que partiellement ou pas du tout.

Autorisation par mot de passe

Un mot de passe peut être enregistré pour chaque ID d’application RadioShuttle. Ceci empêche la station d’accepter des messages si le mot de passe entre le nœud et la station n’est pas identique. Un mot de passe est enregistré pour l’exemple de code original (veuillez changer votre propre mot de passe).

bool usePassword = true; // password the can be used independently of AES
unsigned char samplePassword[] = { "RadioShuttleFly-PleaseChange" };

Le mot de passe garantit que les périphériques externes ne peuvent pas communiquer avec vos périphériques. Il est stocké sur la base d’un ID par application, ce qui permet d’utiliser une application publiquement alors que d’autres applications peuvent être protégées par mot de passe. Les mots de passe n’ont pas de surcharge de protocole parce qu’ils n’ont besoin d’être vérifiés qu’au contact initial entre le nœud et la station. La documentation de La Stratégie d’Applications contient des informations de base supplémentaires relatives aux différentes applications.

Les mots de passe ne sont pas transmis sur le réseau et ne peuvent donc pas être volés. Au lieu d’un mot de passe, un grand nombre aléatoire, ainsi que le mot de passe, est converti en un code de hachage SHA 256-bit. Seul ce code de hachage est transmis, qui change à chaque connexion, par exemple lors du redémarrage d’un appareil.

Cryptage des messages AES 128 bits

Après l’enregistrement d’un mot de passe, le cryptage AES peut être activé.

bool useAES = true; // AES needs the usePassword option on
int flags = RadioShuttle::MF_Encrypted;

AES crypte toutes les données de l’application à l’aide du mot de passe, de sorte qu’aucun participant sur le réseau qui enregistre la communication ne peut voir le contenu du message. De plus, les faux messages ne fonctionnent pas avec AES et sont rejetés.

Pour les besoins de RadioShuttle, le bit AES128 est plus que suffisant car 340282366920938463463374607431768211456 les tentatives (2128) sont très longues et peu réalistes. Le mot de passe RadioShuttle et le cryptage AES sont inclus dans le module séparé « RadioSecurityInterface » et peuvent être ajustés ou modifiés pour d’autres exigences.

Malgré tous ses avantages, le cryptage AES présente un inconvénient majeur : AES128-bit a une longueur de bloc minimum de 16 octets par message qui doit être transmis. Avec le SF7, cela prend environ 100 millisecondes en plus, avec le SF11, c’est jusqu’à une demi-seconde.

Le cryptage AES peut être défini par message. Les drapeaux RadioShuttle::MF_Encrypted dans le « SendMsg » doivent être réglés pour permettre le cryptage AES du message. L’exemple de code RadioTest est déjà configuré de cette façon.

Enregistrement du paquet réseau RadioShuttle

L’exemple RadioTest active l’enregistrement des transactions réseau et des opérations internes. Cela peut être utile pour vérifier ce qui se passe actuellement. La désactivation de la fonction « EnablePacketTrace » désactive l’enregistrement.

Fonction d’enregistrement par dprintf et dump

La fonction « dprintf » permet une sortie compatible « C printf », avec horodatage par ligne de sortie ainsi que l’ajout automatique des marques de fin de ligne.

« dump » permet l’hexdumping de données incluant le nom, l’adresse et une longueur. Des exemples sont inclus dans l’application RadioTest.

Documentation de l’API RadioShuttle

Le fichier « RadioShuttle.h » contient la documentation complète de l’API du protocole RadioShuttle. De plus, le fichier « RadioShuttleProtocol.rtf » documente les concepts sous-jacents du protocole RadioShuttle.