DeltaLab est une association ‘loi 1901’ d’intérêt général, dont l’objectif est la création d’un espace dédié à l’innovation, à la production numérique au prototypage et à l’«expression artistique».
Le principe fondateur de l’association est d’apprendre à faire soi-même, pour passer de l’idée à l’objet.
Deltalab se spécialise en Objets Connectés, et est en train de créer un vaste «écosystème numérique» entre Drôme et Vaucluse, pour répondre à des besoins non couverts, mettre à disposition ressources et équipements pour usage professionnels et instaurer des partenariats avec les autres structures et initiatives numériques existantes.
Deltalab est aussi un FabLab (Fabrication Laboratory / Laboratoire de Fabrication ), un tiers-lieu de type makerspace où se trouve un atelier qui dispose de machines de fabrication comme des Imprimantes 3D ou des découpeuses Laser.
Deltalab se veut ouvert à tous publics : étudiants, professionnels, associations, inventeurs, designers, artistes, etc.
Un des projets à moyen termes de DeltaLab est l’installation d’un réseau LoRaWAN permettant la collecte et le traitement d’un grand nombre de données, et ce sur une grande zone s’étendant sur l’enclave des Papes et la Drôme Provençale.
Ce projet présente l'utilisation d'une sonde d'humidité du sol HD-38 et l'envoi des données de cette sonde en LoRaWAN.
À DeltaLab, un projet d'Aquaponie est en cours, et ce capteur sera évidemment utilisé dès le début.
Ce projet a pour but de présenter l'utilisation de capteur d'humidité du sol, dans le but de les intégrer à un système plus grand.
Le projet se décompose en 2 parties distinctes : le capteur et la carte arduino associée, et le serveur Node-RED.
Les fonctionnalités proposées par ce projet sont les suivantes :
Récupération des données des capteurs
Affichage dans Node-RED
Arduino IDE : téléchargeable à www.arduino.cc/en/main/software
Node-RED : on assume que vous en avez un, sinon installez-le avec ceci.
Arduino UNO : Outils > Type de carte > Arduino AVR Boards. Choisissez ensuite Arduino Uno comme type de carte.
Librairies :
MCCI_LoRaWAN_LMIC : pour LoRaWAN
dans Arduino : Outils > gérer les bibliothèques
entrez LMIC et installez la librairie MCCI_LoRaWAN_LMIC_Library
important : allez dans votre dossier arduino (défaut : Documents\Arduino) et allez dans la configuration de la librairie pour activer la bonne fréquence ( libraries > MCCI_LoRaWAN_LMIC_Library > project_config > lmic_project_config.h ).
Dans ce fichier, décommentez (enlevez le ' // ') la ligne #define CFG_eu868 1 (fréquence européenne) et commentez (mettez un ' // ') la ligne qui était décommentée (par défaut #define CFG_us915 1, fréquence américaine)
Si vous avez un problème de définition pour hal_init ajoutez #define hal_init LMICHAL_init dans libraries > MCCI_LoRaWAN_LMIC_Library > project_config > lmic_project_config.h.
Une fois que vous avez votre Arduino Uno et votre LoRa shield, branchez le shield sur la Arduino.
Vous recevrez un kit comme celui-ci si vous commander sur le site ci-dessus.
Il vous suffit de brancher la sonde au récepteur.
Et souder les pins métalliques au récepteur.
Peu importe le sens, il faut pouvoir brancher le récepteur à la Arduino Uno.
Ensuite vous devez brancher le récepteur au shield.
AO --> A0
- --> G
+ --> 5V
MCCI_LoRaWAN_LMIC (librairie à télécharger avec le gestionnaire de bibliothèques)
Vérifiez le Port et la vitesse d’écriture (mettez la à 115200) dans Outils.
Pour téléverser le code sur la carte, allez dans croquis > Téléverser, ou cliquez sur la flèche en haut à gauche.
Copiez-collez le code dans un nouveau fichier Arduino
/*Librairies*/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
/*Constantes*/
#define SCK 5 // GPIO5 -- SX1278's SCK
#define MISO 19 // GPIO19 -- SX1278's MISnO
#define MOSI 27 // GPIO27 -- SX1278's MOSI
#define SS 18 // GPIO18 -- SX1278's CS
#define RST 14 // GPIO14 -- SX1278's RESET
#define DI0 26 // GPIO26 -- SX1278's IRQ(Interrupt Request)
/*Variables*/
double valA;
double Hum;
osjob_t sendjob;
const unsigned TX_INTERVAL = 2;
// Données à envoyer
typedef union {
float f[2];
unsigned char bytes[8];
} donnees; donnees datas;
// Application EUI : en 'lsb' (little endian format)
static const u1_t PROGMEM APPEUI[8]={ /*disponible lors de la création de l'objet sur TheThingsNetwork */}};
void os_getArtEui(u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
// Device EUI : même chose qu'au dessus
static const u1_t PROGMEM DEVEUI[8]={ /*disponible lors de la création de l'objet sur TheThingsNetwork */}};
void os_getDevEui(u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
// Application Key : vous pouvez la copier telle qu'elle depuis TTN
static const u1_t PROGMEM APPKEY[16] = { /*disponible lors de la création de l'objet sur TheThingsNetwork */} };
void os_getDevKey(u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
//Pin Mapping
const lmic_pinmap lmic_pins = {
.nss = 10,
.rxtx = LMIC_UNUSED_PIN,
.rst = 9,
.dio = {2, 6, 7},
};
//Affiche une variable au format hexadécimal
void printHex2( unsigned v) {
v &= 0xff;
if (v < 16)
Serial.print (’0’);
Serial.print(v, HEX);
}
// Gestionnaire des évenements de TTN
void onEvent (ev_t ev) {
Serial.print(": ");
switch(ev) {
case EV_SCAN_TIMEOUT :
Serial.println(F(" EV_SCAN_TIMEOUT "));
break;
case EV_BEACON_FOUND :
Serial.println(F(" EV_BEACON_FOUND "));
break;
case EV_BEACON_MISSED :
Serial.println(F(" EV_BEACON_MISSED "));
break;
case EV_BEACON_TRACKED :
Serial.println(F(" EV_BEACON_TRACKED "));
break;
case EV_JOINING :
Serial.println(F(" EV_JOINING "));
break;
case EV_JOINED :
Serial.println(F(" EV_JOINED "));
{
u4_t netid = 0;
devaddr_t devaddr = 0;
u1_t nwkKey [16];
u1_t artKey [16];
LMIC_getSessionKeys(& netid , & devaddr , nwkKey , artKey );
Serial.print(" netid : ");
Serial.println(netid , DEC );
Serial.print(" devaddr : ");
Serial.println( devaddr , HEX );
Serial.print(" AppSKey : ");
for ( size_t i=0; i< sizeof ( artKey ); ++i) {
if (i != 0)
Serial.print("-");
printHex2( artKey [i]);
}
Serial.println("");
Serial.print(" NwkSKey : ");
for ( size_t i=0; i< sizeof ( nwkKey ); ++i) {
if (i != 0)
Serial.print("-");
printHex2( nwkKey [i]);
}
Serial.println();
}
// Disable link check validation ( automatically enabled
// during join , but because slow data rates change max TX
// size , we don ’t use it in this example .
LMIC_setLinkCheckMode (0) ;
break;
case EV_JOIN_FAILED :
Serial.println(F(" EV_JOIN_FAILED "));
break;
case EV_REJOIN_FAILED :
Serial.println(F(" EV_REJOIN_FAILED "));
break;
case EV_TXCOMPLETE :
Serial.println(F(" EV_TXCOMPLETE ( includes waiting for RX windows )"));
if ( LMIC.txrxFlags & TXRX_ACK )
Serial.println(F(" Received ack "));
if ( LMIC.dataLen ) {
Serial.print(F(" Received "));
Serial.print( LMIC . dataLen );
Serial.println(F(" bytes of payload "));
}
Serial.println();
// -----------------------------
// Schedule next transmission
os_setTimedCallback(&sendjob , os_getTime()+ sec2osticks(TX_INTERVAL), do_send);
break ;
case EV_LOST_TSYNC :
Serial.println(F(" EV_LOST_TSYNC "));
break;
case EV_RESET :
Serial.println(F(" EV_RESET "));
break;
case EV_RXCOMPLETE :
// data received in ping slot
Serial.println(F(" EV_RXCOMPLETE "));
break;
case EV_LINK_DEAD :
Serial.println(F(" EV_LINK_DEAD "));
break ;
case EV_LINK_ALIVE :
Serial . println (F(" EV_LINK_ALIVE "));
break;
case EV_TXSTART :
Serial.println(F(" EV_TXSTART "));
break;
case EV_TXCANCELED :
Serial.println(F(" EV_TXCANCELED "));
break;
case EV_RXSTART :
/* do not print anything -- it wrecks timing */
break;
case EV_JOIN_TXCOMPLETE :
Serial.println(F(" EV_JOIN_TXCOMPLETE : no JoinAccept "));
break;
default :
Serial.print(F(" Unknown event : "));
Serial.println(( unsigned ) ev);
break;
}
}
// Récupération des données du capteur
void getData(){
valA = analogRead(A0);
Hum = map(valA, 4095, 1000, 0, 100);
Serial print("; valA : ");
Serial.print(valA);
Serial.print("; hum: ");
Serial.print(Hum);
Serial.println(" %;");
}
// Envoi des données à TTN
void Send(osjob_t* j){
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
getData();
//Stockage des données dans le tableau de donnée qui sera envoyé
datas.f[0] = valA; //valeur analogique
datas.f[1] = Hum; //humidité
LMIC_setTxData2(1, datas.bytes, 8, 0);
Serial.println(F("Packet queued"));
}
}
// SetUp : démarrage du capteur, initialisation de LMIC et de la boucle d'envoi
void setup() {
Serial.begin(115200); //Démarrage du moniteur série
Serial.println(F("Starting"));
# ifdef VCC_ENABLE
// For Pinoccio Scout boards
pinMode( VCC_ENABLE , OUTPUT );
digitalWrite( VCC_ENABLE , HIGH );
delay(1000);
# endif
os_init();
LMIC_reset();
LMIC_setClockError(MAX_CLOCK_ERROR * 10/100);
Send(&sendjob);
}
// Boucle automatique
void loop() {
os_runloop_once();
}
Avant de lancer le programme, il faut créer la réception des données sinon le code tournera en boucle sans rien faire.
Sur The Things Network, il est possible d'enregistrer son objet pour voir le flux de données.
Avant cela, si vous n'avez pas d'antenne LoRaWAN pour recevoir les données allez ici.
Sur TTN (www.thethingsnetwork.org/), connectez-vous (ou créez un compte), allez dans la console en cliquant sur votre profil, allez dans Applications et Ajoutez une nouvelle appli. Renseignez un ID, un nom et une description (facultatif).
Dans cette application, ajoutez un nouveau End Device. Renseignez les informations de votre objet connecté dans la section Manually.
LoRaWAN version : MAC V1.0
Sélectionnez la fréquence dans Frequency plan dont l'endroit correspond à la région où sera placé votre objet. En Europe, on prendra la fréquence Europe 863-870 MHz(SF9 for RX2-recommended).
L’AppEUI devrait être renseigné par le fabricant ou remplissez avec des zéros.
Le DevEUI est soit renseigné par le fabricant ou imprimé sur le paquet ou générer par le bouton juste à droite.
L’Appkey est soit renseigné par le fabricant ou générer par le bouton juste à droite.
Dans l'overview de votre End Device , vous avez accès aux EUIs et à l’AppKey nécessaires dans le programme. Pour les EUIs, cliquez sur les chevrons à droite de chacun d'eux, ils seront au format msb.
Et ça tombe bien, sur Arduino les EUIs sont au format msb. Vous pouvez les copiez et les collez dans le code.
Pour l' AppKey, il faudra la rendre visible en cliquant sur l'œil et dans le code remplacez seulement les chiffres après les 0x.
On utilise un décodeur sur TTN car les données qui sont créées par les objets sont au format binaire donc illisible pour l’homme.
On utilisera le langage Javascript pour créer notre propre décodeur.
Dans Application > End devices > votre device > Payload Formatters et dans Uplink sélectionnez Javascript dans le menu déroulant. Copiez-collez le code ci-dessous dans la zone 'Formatter parameter'. Et cliquez sur Save changes pour sauvegarder. Et vérifiez l'indentation sinon cela ne fonctionnera pas.
function B2F32(bytes) { // Convertit les données binaires en nombres à virgules
var sign = (bytes & 0x80000000) ? -1 : 1;
var exponent = ((bytes >> 23) & 0xFF) - 127;
var significand = (bytes & ~(-1 << 23));
if (exponent == 128)
return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);
if (exponent == -127) {
if (significand === 0) return sign * 0.0;
exponent = -126;
significand /= (1 << 22);
} else significand = (significand | (1 << 23)) / (1 << 23);
return sign * significand * Math.pow(2, exponent);
}
function Decoder(bytes, port) { // Envoi du message de donnée au serveur MQTT
var valA= bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
var hum= bytes[7] << 24 | bytes[6] << 16 | bytes[5] << 8 | bytes[4];
return{ valA: B2F32(valA), hum: B2F32(hum)
}; }
Maintenant vous pouvez upload votre programme, si vous voulez suivre ce que le programme fait allez dans Outils et cliquez sur Moniteur série.
Pour que l’on puisse récupérer les données avec Node-RED, nous allons créer un serveur MQTT.
Toujours dans TheThingsNetwork dans votre Application, allez dans Integrations > MQTT. Cliquez sur Generate new API key et copiez-la car elle ne sera plus visible si vous quittez la page. L' API key servira de mot de passe.
Ça y est, on peut maintenant traiter les données avec Node-RED.
Le capteur envoie ses données et TTN les récupère. Node-RED les lit ensuite grâce à la node mqtt-in.
Les données sont ensuite récupérées par les nodes de dashboard qui permettent de les afficher : des jauges pour la valeur analogique, l'humidité.
Chaque fois que vous finissez de configurer un node, cliquez sur Done.
ATTENTION : pour enregistrer vos modifications, cliquez sur Deploy.
Pour commencer, on va vouloir récupérer les données reçu par l’antenne LoRaWAN via TTN. Prenez ce node, double-cliquez dessus.
Dans le menu de sélection Server, prenez Add new mqtt-broker et cliquez sur le bouton à côté.
Dans la partie Security, entrez les identifiants en accord avec ceux lors de la création du serveur MQTT.
Dans la partie Connection, précisez l’adresse du serveur MQTT de la Console The Things Stack. Et dans Protocol, sélectionnez MQTT V3.1.1.
Revenez dans les propriétés. Le topic sert à préciser de quel objet on veut traiter les données. Voici le format du topic : v3/username/devices/device_id/up. Faites attention à ce qu’il n’y est pas d’espace surtout au début et à la fin.
Sélectionnez le QoS que vous voulez (privilégiez le 2).
Et dans Output, prenez a parsed JSON object.
Pour finir, branchez une node de debug pour voir si les données de l’objet choisi arrive bien.
N'hésitez pas à connectez des nodes de debug, cela permet de savoir si vos données arrive bien et au bon format.
Pour voir les données là où vous avez connecté des debug, cliquez sur le petit insecte en haut à droite de l'écran, puis all nodes puis current flow pour voir le flux du flow sur lequel vous travaillez.
Pour l'humidité, cliquez sur add en bas à gauche de Rules.
Mettez la première règle sur Set. Entrez topic, puis state en-dessous.
Ajoutez une deuxième règle et mettez-la sur Move, entrez payload.uplink_message.decoded_payload.state dans le premier champ et payload dans le deuxième.
Faites la même chose pour valA, changez juste les valeurs de state, et n’oubliez pas de faire attention à ne pas laisser d’espace.
Pour les jauges, prenez trois de ce node et connectez-les au mqtt-in et configurez-les de cette manière :
Group: créez un nouveau groupe
ui_tab : créez une nouvelle
width(largeur) : à votre guise
name : celui que vous voulez
Type: gauge
Label: valA, humidité
Value format:
valA: {{msg.payload}}
humidité: {{msg.payload}}
Echelle:
valA: 4095 à 0, unité :
humidité: 0 à 100, unité : %
L'intérêt de tout ceci est pour la visualisation des données. Node-RED permet ceci avec une interface utilisateur.
Pour y accéder, avec le moteur de recherche que vous voulez, tapez l'ip du serveur Node-RED, puis deux points, puis le numéro port puis /ui (XXX.XXX.XXX.XXX:YYYY/ui).
Pour mettre en forme l'interface utilisateur, retournez dans Node-RED
Cliquez sur les trois barres en haut à droite, allez dans View puis cliquez sur Dashboard.
En mettant le curseur sur le nom du groupe des jauges et du graphe, cliquez sur layout.
Sur la droite réglez la largeur de l'interface sur Width. Ensuite cliquez sur Done, puis sur Deploy et revenez dans le layout pour faire la mise en forme.
Pour modifier la taille des composants, cliquez sur le cadenas en haut à droite du composant et une double flèche apparait en bas à droite de ce dernier. Maintenez le clique dessus pour modifier la taille.