[Tuto] Utiliser influxDB avec un Arduino Nano33 IOT
Dans ce tutoriel nous verrons comment créer une base de données InfluxDB et puis uploader des informations via un Arduino Nano33 IOT.
Créer la base de données
1.1) Inscription sur influxData avec le choix influxDb Cloud
Dans cette première partie de ce tutoriel nous allons tout d’abord nous inscrire et créer la base de données sur le site InfluxData.
Inscrivez- vous en choisissant la méthode d’inscription de votre choix (Google, Microsoft, mail) ou connectez en renseignant vos identifants si vous avez déjà un compte.
1.2) Création du bucket
Afin de créer le bucket il faut cliquer sur la flèche vers le haut puis choisir Buckets.
Une fenêtre de dialogue « Create Bucket » va s'ouvrir. Pour terminer la création donnez un nom au bucket et cliquez sur le bouton « Create ».
1.3) Création de la clef d’API
Nous allons maintenant créer une clé d’API (token d’authentification). Ce token permet à l'Arduino Nano33 de communiquer avec le serveur. Sans cette clé le serveur refuserait toutes interactions.
Démarche à suivre pour créer le token :
- Rendez vous dans l'onglet “API TOKENS”.
- Ouvrez le menu déroulant “Generate API Token” puis cliquez sur “All access API Token”.
- Dans la fenêtre suivante donnez un nom pour la nouvelle clef.
- Enregistrez la nouvelle clef en cliquant sur le bouton "Save”
- La clef générée est alors affichée dans la fenêtre suivante.
- Sauvegardez-la ! Elle sera impérative dans les softs accédants à influxDB
1.4) Test de la configuration à l’aide de Curl en ligne de commande
Après avoir configuré la base de données nous allons communiquer avec celle-ci afin de s’assurer que la configuration est bonne. Voici la ligne de commande à rentrer dans un terminal Windows.
curl -i --request POST "https://europe-west1-1.gcp.cloud2.influxdata.com/api/v2/write?org=tutoriel@letmeknow.fr&bucket=NomDuBucket&precision=ns" --header "Authorization: Token xxx...xxx" --header "Content-Type: text/plain; charset=utf-8" --header "Accept: application/json" --data--binary "PZEM,sensor_id=idPZEM voltage=230,courant=1,power=230,energy=1,frequence=50,pf=1"
Copiez la partie de code précédente et modifiez les informations suivantes.
- Le serveur (voir la documentation)
- L’organisation.
Dans notre cas se trouve le champ "org=tutoriel@letmeknow.fr". Il s’agit ici de remplacer l’adresse mail par la vôtre (celle servant au login sur influxData).
- Le bucket auquel ajouter des données
Dans notre cas on retrouve “bucket=NomDuBucket”. Il faut remplacer le nom par celui que vous avez donné au bucket lors de sa création.
- Le token généré précédemment.
Il faut ici remplacer dans le champ “Authorization : Token xxx…xxx” la partie “xxx...xxx” par la clef d’API générée précédemment.
- Les data à transmettre
Exemple : PZEM,sensor_id=idPZEM voltage=230,courant=1,power=230,energy=1,frequence=50,pf=1
- PZEM ⇒ un nom arbitraire d’identification du type de mesure.
- sensor_id ⇒ un nom arbitraire permettant d’identifier le groupe de mesure suivant.
- “voltage=230”, “courant=1”, “power=230” sont les paires “clé=valeur” qui seront remontées et stockées dans la base de données.
Après l’envoi de cette commande, si tout est bien configuré et que les commandes sont complètes alors le serveur enverra un code 204 sans erreur. Sinon il transmettra un message d ‘erreur.
Requête OK
Erreur d'authentification – clef d’API HS
1.5) visualisation des datas
- Ouvrez les buckets disponible et en particulier celui-ci sur lequel vous faites remonter les données.
- Choisissez le bucket utilisé (défini par son nom).
- Choisissez le type de mesure (measurement).
- Sélectionnez une ou plusieurs valeurs (voltage, courant, power…)
- Sélectionnez l’id, ici “sensor_id”.
- Enfin appuyez sur RUN.
Si tout fonctionne correctement les valeurs remontées sont affichées dans la table.
2) Communication entre l’arduino et influxDB
Dans cette deuxième partie nous allons voir comment communiquer avec l’Arduino Nano 33 IOT.
2.1) Initialisation
Dans cette première partie nous allons inclure les librairies et initialiser des valeurs qui serviront à interagir avec la base de données :
- Le nom du wifi (wifiSsid).
- Le mot de passe du wifi (wifiPass).
- Le token (token).
- Le nom du bucket (bucket).
- L’organisation (org).
Il faut donc remplacer les valeurs par défaut par vos valeurs à vous.
#include "Arduino.h"
#include "SPI.h"
#include "WiFiNINA.h"
#include "WiFiUdp.h"
#include "wiring_private.h"
#include "PZEM004Tv30.h"
#define DEBUG
//#define USENTP //recup heure par NTP
//wifi
const char wifiSsid[] = "NomDuWifi";
const char wifiPass[] = "MotDePasseDuWifi";
String macAdresse = "";
//ntp udp
const unsigned int localPort = 2390;
IPAddress ntpServer(37,187,104,44); // serveur ntp fr
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer ntp
WiFiUDP Udp;
//influx data
const char influxServer[]="europe-west1-1.gcp.cloud2.influxdata.com";
const String influxPath="/api/v2/write";
const String influxBucket="nomDuBucket";
const String influxOrg="tutoriel@letmeknow.fr";
const String influxPrecision="ns";
const String influxToken="xxx...xxx";
const String sensorName="PZEM";
const String sensorId="idPZEM";
//PZEM
Uart mySerial (&sercom0, 5, 6, SERCOM_RX_PAD_1, UART_TX_PAD_0);
boolean firstLoop = true;
const uint32_t delayMesure = 10000;
uint32_t lastMesure = 0;
boolean send_NAN=true; // envoi sur influx même si mesures invalides
WiFiSSLClient client;
void setup() {
#ifdef DEBUG
Serial.begin(115200);
#endif
pinPeripheral(5, PIO_SERCOM_ALT);
pinPeripheral(6, PIO_SERCOM_ALT);
initWifi();
Udp.begin(localPort);
}
2.2) Uploader des valeurs
Pour gérer les valeurs et uploader sur le serveur il est nécessaire de copier à la suite le code suivant. Pour les ’envoyer au bon format il est nécessaire de suivre ces étapes :
- Récupérer les données.
- Formater les données.
- Envoyer sur le serveur les données en wifi.
Les valeurs récupérées dans notre cas sont des valeurs fournies par l’objet “pzem”.
Afin de les encoder nous devons les sauvegarder dans une chaîne de caractères vierge.
Attention : Une fois la base de données créée nous n’avons plus besoin de communiquer avec le format “clef=valeur”. Il suffit d’envoyer les valeurs en les séparant d’une virgule. Cependant il faut bien respecter l’ordre sinon les données seront incohérentes.
Pour ce faire nous utilisons la fonctions suivante
String();
Si aucune donnée n’est disponible alors nous donnerons la valeur par défaut “NAN”.
Nous pouvons ajouter plusieurs valeurs à la suite dans la chaîne de caractères (stringValue) en les séparant d’une virgule.
void loop() {
if(firstLoop) {
PZEM004Tv30 pzem(&mySerial);
#ifdef DEBUG
Serial.print("Current address:");
Serial.println(pzem.getAddress());
Serial.println();
#endif
firstLoop = false;
}else{
if (millis()-lastMesure>delayMesure){
String vPZEM=getPzemValue();
lastMesure=millis();
if (vPZEM.length()>0){
sendInfluxData(vPZEM);
}
}
}
}
//###########################################################
// WIFI
//###########################################################
void initWifi(){
if (WiFi.status() == WL_NO_MODULE) {
#ifdef DEBUG
Serial.println("Module wifi KO !!!");
#endif
return;
}else{
#ifdef DEBUG
Serial.println("Connexion au module wifi OK");
#endif
}
String fv = WiFi.firmwareVersion();
#ifdef DEBUG
Serial.println("Version wifi: "+fv+" dernière version: "+WIFI_FIRMWARE_LATEST_VERSION);
#endif
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
#ifdef DEBUG
Serial.println("Mettre à jour le firmware Wifi !!!");
#endif
}
if (!connectWifi()){
#ifdef DEBUG
Serial.println("Connexion au Wifi impossible !!!");
#endif
}else{
#ifdef DEBUG
Serial.println("Connexion au wifi OK");
#endif
IPAddress IP = WiFi.localIP();
#ifdef DEBUG
Serial.print("IP=");
Serial.println(IP);
#endif
}
}
boolean connectWifi(){
uint8_t nb=0;
uint8_t status;
#ifdef DEBUG
Serial.println("Connexion au wifi");
#endif
while (status != WL_CONNECTED) {
delay(200);
#ifdef DEBUG
Serial.print(".");
#endif
status = WiFi.begin(wifiSsid, wifiPass);
nb++;
if (nb>29){
return false;
break;
}
}
#ifdef DEBUG
Serial.println("");
#endif
byte mac[6];
WiFi.macAddress(mac);
char buf[1];
for (int i=0;i<6;i++){
itoa(mac[i],buf,16);
macAdresse=macAdresse+String(buf);
if (i<5) macAdresse=macAdresse+":";
}
macAdresse.toUpperCase();
#ifdef DEBUG
Serial.println(macAdresse);
#endif
return true;
}
//#################################################################
// NTP
//#################################################################
void sendNTPpacket(IPAddress& address) {
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011;
packetBuffer[1] = 0;
packetBuffer[2] = 6;
packetBuffer[3] = 0xEC;
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Udp.beginPacket(address, 123); //port ntp udp = 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
unsigned long decodeNtp(){
Udp.read(packetBuffer, NTP_PACKET_SIZE);
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
//timestamp depuis 1900
unsigned long secsSince1900 = highWord << 16 | lowWord;
//timestamp Unix depuis 1970
const unsigned long since70 = 2208988800UL;
unsigned long epoch = secsSince1900 - since70;
return epoch;
}
String getUnixTime(){
sendNTPpacket(ntpServer);
delay(1000);
String ut="";
if (Udp.parsePacket()){
ut=String(decodeNtp());
}else{
ut="0";
}
return ut;
}
//##################################################################
// INFLUXDB
//##################################################################
void sendInfluxData(String pzemValues){
#ifdef DEBUG
Serial.println("sendInfluxData");
#endif
//commande curl
//curl -i --request POST "https://europe-west1-1.gcp.cloud2.influxdata.com/api/v2/write?org=tutoriel@letmeknow.fr&bucket=NomDuBucket&precision=ns" --header "Authorization: Token xxx...xxx" --header "Content-Type: text/plain; charset=utf-8" --header "Accept: application/json" --data-binary "PZEM,sensor_id=idPZEM voltage=230,courant=1,power=230,energy=1,frequence=50,pf=1 1656486702000000000"
if (client.connectSSL(influxServer, 443)) {
#ifdef DEBUG
Serial.println("connexion ok");
#endif
const String cmdPost="POST "+influxPath+"?org="+influxOrg+"&bucket="+influxBucket+"&precision="+influxPrecision+" HTTP/1.1";
client.println(cmdPost);
client.println("Host: "+String(influxServer));
client.println("Authorization: Token "+influxToken);
String ts="";
#ifdef USENTP
ts=" "+String(getUnixTime())+"000000000";
#ifdef DEBUG
Serial.println(ts);
#endif
#endif
//valeurs pour test
//String datas="PZEM,sensor_id=idPZEM
String datas=sensorName+",sensor_id="+sensorId+" "+pzemValues+ts;
int cl = datas.length();
#ifdef DEBUG
Serial.println(datas);
Serial.println("cl="+String(cl));
#endif
client.println("Connection: close");
client.println("Content-Type: text/plain; charset=utf-8");
client.println("Accept: application/json");
client.println("Content-Length: "+String(cl));
client.println();
client.println(datas);
getInfluxResponse();
client.stop();
}else{
#ifdef DEBUG
Serial.println("connexion ko");
#endif
}
}
void getInfluxResponse(){
int secu=0;
#ifdef DEBUG
Serial.println("attente reponse");
#endif
while (!client.available()){
#ifdef DEBUG
Serial.print(".");
#endif
secu++;
if (secu>100){
#ifdef DEBUG
Serial.println();
Serial.println("pas de reponse du serveur");
#endif
return;
}
delay(100);
}
#ifdef DEBUG
Serial.println();
#endif
while (client.available()) {
char c = client.read();
#ifdef DEBUG
Serial.write(c);
#endif
}
}
//##########################################################
//PZEM
//##########################################################
void SERCOM0_Handler()
{
mySerial.IrqHandler();
}
String getPzemValue(){
String V="";
PZEM004Tv30 pzem(&mySerial);
//voltage
float voltage = pzem.voltage();
if(!isnan(voltage)){
#ifdef DEBUG
Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
#endif
V="voltage="+String(voltage)+",";
}else{
#ifdef DEBUG
Serial.println("Error reading voltage");
#endif
V="voltage=-1,";
}
//courant
float current = pzem.current();
if(!isnan(current)){
#ifdef DEBUG
Serial.print("Current: "); Serial.print(current); Serial.println("A");
#endif
V=V+"courant="+String(current)+",";
}else{
#ifdef DEBUG
Serial.println("Error reading current");
#endif
V=V+"courant=-1,";
}
//puissance
float power = pzem.power();
if(!isnan(power)){
#ifdef DEBUG
Serial.print("Power: "); Serial.print(power); Serial.println("W");
#endif
V=V+"power="+String(power)+",";
}else{
#ifdef DEBUG
Serial.println("Error reading power");
#endif
V=V+"power=-1,";
}
//energy
float energy = pzem.energy();
if(!isnan(energy)){
#ifdef DEBUG
Serial.print("Energy: "); Serial.print(energy,3); Serial.println("kWh");
#endif
V=V+"energy="+String(energy)+",";
}else{
#ifdef DEBUG
Serial.println("Error reading energy");
#endif
V=V+"energy=-1,";
}
//frequence
float frequency = pzem.frequency();
if(!isnan(frequency)){
#ifdef DEBUG
Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz");
#endif
V=V+"frequence="+String(frequency)+",";
}else{
#ifdef DEBUG
Serial.println("Error reading frequency");
#endif
V=V+"frequence=-1,";
}
//mystere !
float pf = pzem.pf();
if(!isnan(pf)){
#ifdef DEBUG
Serial.print("PF: "); Serial.println(pf);
#endif
V=V+"pf="+String(pf);
}else{
#ifdef DEBUG
Serial.println("Error reading power factor");
#endif
V=V+"pf=-1";
}
if (send_NAN){
return V;
}else{
if (V.indexOf("=-1")>-1){
return "";
}else{
return V;
}
} }
Connectez-vous pour commenter
Se connecter