Lire un capteur depuis Arduino, enregister les valeurs dans une BDD MariaDB sur un Raspberry Pi

Dans le cadre de mon projet de station météo, il s’agit ici de lire, depuis le raspberrry Pi, le port série sur lequel Arduino envoie les données de mes capteurs en utilisant la librarie wiringPi. Ensuite, nous enregistrerons les valeurs dans une Base de Données MySQL-MariaDB. Il est possible de faire tout cela facilement en utilisant Node-red , qui génére un code JS . Je ne sais pas bien si le code de Node-red sera durable dans le temps, alors je préfére me tourner vers du code en C-C++ qui de toute façon me donnera toute la flexibilité dont je pourrais avoir besoin.

J’ai longuement décrit l’installation et la gestion de la BDD MariaDB ici.

Il faudra aussi installer l’environement de dévelopement Arduino.

Installation du pilote du capteur

Du côté Arduino, il suffit d’installer  le pilote du capteur HTU21D.

Ecriture des données sur le port série côté Arduino

Programmer le micrcontroleur à l’aide de la fonction suivante:

/***************************************************
This is an example for the HTU21D-F Humidity & Temp Sensor

Designed specifically to work with the HTU21D-F sensor from Adafruit
—-> https://www.adafruit.com/products/1899

These displays use I2C to communicate, 2 pins are required to
interface
****************************************************/

#include <Wire.h>
#include « Adafruit_HTU21DF.h »

// Connect Vin to 3-5VDC
// Connect GND to ground
// Connect SCL to I2C clock pin (A5 on UNO)
// Connect SDA to I2C data pin (A4 on UNO)

Adafruit_HTU21DF htu = Adafruit_HTU21DF();

void setup() {
Serial.begin(9600);
Serial.println(« HTU21D-F test »);

if (!htu.begin()) {
Serial.println(« Couldn’t find sensor! »);
while (1);
}
}

void loop() {
float temp = htu.readTemperature();
float rel_hum = htu.readHumidity();
Serial.print(« \nT= »); Serial.print(temp);// Serial.print( » C »);
Serial.print(« ; »);
Serial.print(« H= »); Serial.print(rel_hum);// Serial.println( » \% »);
Serial.print(« ; »);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(300); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW

delay(2000);
}

Installation de la librairie wiringPi

Sur le site de Sir Drogon, ou trouvera :

sudo apt-get install git-core
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build

La librarie (shared Object libwiringPi.so) est alors installée ici :

Capture d’écran, le 2018-11-10 à 16.37.52.png

Ce répertoire est bien déclaré dans un fichier libc.conf du répertoire ci-dessous (accessible uniquement depuis le root et aussi l’usager):

ls -al /etc/ld.so.conf.d/

Capture d’écran, le 2018-11-10 à 16.41.11.png

on remarque alors que le répertoireusr/local/lib est bien déclaré.

nano libc.conf

Capture d’écran, le 2018-11-10 à 16.26.16.png

et on peut donc vérifier que la librairie est bien dans le cache:

ldconfig -p | grep *

Capture d’écran, le 2018-11-10 à 16.21.42.png

Au passage, on remarquera que les fichiers .c et .h (qui définissent la libraire) sont présents dans le répertoire ci-dessous. Le fichier

Capture d’écran, le 2018-11-10 à 16.46.01.png

On peut remarquer aussi la présence de la librarie elle-même libwiringPi.so, ce qui peut paraitre surprenant car elle figure deja dans usr/local/lib.

Je sousponne que lors de l’installation, le fichier .so est compilé dans le répertoire wiringPi ci-dessus puis copié dans usr/local/lib. Les 2 libraries .so ont la meme date , meme heure. Je pourrais vérifer que tout fonctionne en effacant la librairie ici dans ce dernier répertoire local (à tester).

J’ai du faire la même chose  pour ma propre librarie.

Note:

La librairie est aussi ici présente dans le répertoire ci-dessous (aussi accessible depuis l’usager) mais je ne sais pas trop pourquoi…

ls -al  //usr/lib

Capture d’écran, le 2018-11-10 à 16.13.14.png

Enfin, on pourra voir les dépendances de la librairie :

ldd /home/cm70s/wiringPi/wiringPi/libwiringPi.so.2.46

Capture d’écran, le 2018-11-10 à 17.20.00.png

Lecture de données sur le port série côté Raspberry Pi

Voici le code en C qui lit les caractères un par un sur le port série de mon raspberry Pi. La fcuntnio serialGetChar() lit un caractère à la fois et

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <wiringSerial.h>

int main (){
int fd ;

if ((fd = serialOpen (« /dev/ttyACM0 », 9600)) < 0){
fprintf (stderr, « Unable to open serial device: %s\n », strerror (errno)) ;return 1 ;
}

// Loop, getting and printing characters
for (;;){
putchar (serialGetchar (fd)) ;
fflush (stdout) ;
}
return 0;
}

Compilation, exécution:

gcc -Wall -Wextra -o fromage moncode.c -lwiringPi

./fromage

Capture d’écran, le 2018-11-10 à 16.00.58.png

Ce programme lit donc les données sur le port série en continue ou chaque ligne est envoyé toutes les 2 secondes environ.

Voici à peu près la meme chose en utlisant un code en C++:

#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <wiringSerial.h>

using namespace std;

int main () {

std::string ArduinoString(«  »);
char CurrentChar = ‘\n’;

int i=1,fd,data_integer;
if ((fd = serialOpen (« /dev/ttyACM0 », 9600)) < 0)
{
std::cout<<« ECHEC: Ouverture du Port. Verifier connection. « <<‘\n’;
return 0;
}
else
std::cout<<« Ouverture du Port reussie »<<‘\n’;

for (;;){
data_integer = serialGetchar (fd);
fflush (stdout);
CurrentChar = static_cast<char>(data_integer);
ArduinoString += CurrentChar;
if (data_integer == 10) //carriage Return{
std::cout <<i++<<« Ligne recue –> »<<ArduinoString;
ArduinoString = » »;
}
}
return 0;
}

compilation

g++ -L/usr/local/lib/olivier  -Wall maroille.cpp -o maroille -lwiringPi  -std=c++11

Capture d’écran, le 2018-11-10 à 18.21.09.png

Ecriture des données dans MariaDB

Et enfin, la totale ici :

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <thread>
#include <wiringSerial.h>
#include <mysql/mysql.h>
#include <sstream>

using namespace std;
const string sDelim1(« = »);
const string sDelim2(« ; »);
MYSQL *conn, mysql;
MYSQL_RES *res;
MYSQL_ROW row;
int query_state;
string query;
string line;

const char *server= »localhost »;
const char *user= »cm70s »;
const char *password= »kfMmIGHa »;
const char *database= »meteobdd3″;

/************************************************************************************************/
int MeasureToDB(std::string sKeyword ,double Value)
{
if (!sKeyword.compare(0,1, »T »))
{
query = »INSERT INTO Mesure_Table (M_Nom,M_Famille,M_Valeur,M_Unite,M_Description,M_Date,M_Capteur,M_Fournisseur,M_Branchement) « ;
query +=  » VALUES (‘temperature’,’chaleur’, »;
query += to_string(Value);
query += « ,’C’,’Bureau de Singapour’,CURRENT_TIME(),’HTU21D’,’Adafruit’,’Pin 12-13 Arduino’ ); « ;
}
if (!sKeyword.compare(0,1, »H »))
{
query = »INSERT INTO Mesure_Table (M_Nom,M_Famille,M_Valeur,M_Unite,M_Description,M_Date,M_Capteur,M_Fournisseur,M_Branchement) « ;
query +=  » VALUES (‘humidite’,’chaleur’, »;
query += to_string(Value);
query += « ,’pc’,’Bureau de Singapour’,CURRENT_TIME(),’HTU21D’,’Adafruit’,’Pin 12-13 Arduino’ ); « ;
}

query_state=mysql_query(conn, query.c_str());
if(query_state!=0){
cout<<mysql_error(conn)<<endl<<endl;
return 1;
}

return 1;
}
/************************************************************************************************/
string::size_type GetSensor(std::string sLine,std::string SensorName,double & Value)
{

std::string sValue(«  »);

string::size_type posBeginIdx = 0,posEndIdx;

posEndIdx = sLine.find_first_of(SensorName);
if (posEndIdx < 0) //couldnt find a string
return posEndIdx;
posBeginIdx = posEndIdx + 1;
posEndIdx = sLine.find_first_of(SensorName);

posBeginIdx = sLine.find(sDelim1,posEndIdx) + 1;
posEndIdx = sLine.find(sDelim2,posBeginIdx);
sValue = sLine.substr(posBeginIdx,posEndIdx-posBeginIdx);
Value = std::stod(sValue.c_str());

return posBeginIdx;
}
/************************************************************************************************/
int main () {

std::string ArduinoString(«  »);
char CurrentChar = ‘\n’;
double SensorValue=0;
// ACCESS MYSQL DB

mysql_init(&mysql);
conn=mysql_real_connect(&mysql, server, user, password, database, 0, 0, 0);
if(conn==NULL){
cout << mysql_error(&mysql) << endl << endl;
return 1;
}

// OPEN SERIAL PORT
int i=0,fd,data_integer;
if ((fd = serialOpen (« /dev/ttyACM0 », 9600)) < 0)
{
std::cout<<« ECHEC: Ouverture du Port. Verifier connection. « <<‘\n’;
return 0;
}

std::cout<<« Ouverture du Port reussie »<<‘\n’;

// READ LINE ON SERIAL PORT
for (;;)
{
data_integer = serialGetchar (fd);
fflush (stdout);
CurrentChar = static_cast<char>(data_integer);
ArduinoString += CurrentChar;
if ((data_integer == 10) && (ArduinoString.length()>2))//carriage Return
{
//cout<<« string: »<<ArduinoString <<endl;
GetSensor(ArduinoString, »T »,SensorValue);
MeasureToDB(« T »,SensorValue);
GetSensor(ArduinoString, »H »,SensorValue);
MeasureToDB(« H »,SensorValue);
ArduinoString= » »;
}
}
return 0;
}

 

Et voila, le résulat après 5 secondes:

Capture d’écran, le 2018-11-12 à 22.44.55.png

Corrolaire 1: Lire une chaine de caractère en C++

Voici un petit bout de code en C++ qui permet de rechercher des delimiteurs dans une chaine de caractères.

#include <iostream>
#include <fstream>
#include <cstring>
#include <cctype>
int main(void)
{
using namespace std;
std::string ArduinoString(« Temperature=23.4;Humidite=45.6231;blablabla »);
const string sDelim1(« = »);
const string sDelim2(« ; »);
string::size_type posBeginIdx = 0,posEndIdx;
string sKeyword,sValue;
std::cout <<endl<< » Example of Arduino String-> »<<ArduinoString<<endl;
// Read First measure (temperature)
    posEndIdx = ArduinoString.find_first_of(sDelim1);
    sKeyword = ArduinoString.substr(posBeginIdx,posEndIdx-posBeginIdx);
    posBeginIdx = posEndIdx+1;
posEndIdx=ArduinoString.find(sDelim2,posBeginIdx);
sValue= ArduinoString.substr(posBeginIdx,posEndIdx-posBeginIdx);
    std::cout <<endl<<« T keyword-> » <<sKeyword <<endl<<« T value-> »<<sValue<<endl;
// Read second measure (humidity)
    posBeginIdx = posEndIdx+1;
posEndIdx=ArduinoString.find(sDelim1,posBeginIdx);
    sKeyword = ArduinoString.substr(posBeginIdx,posEndIdx-posBeginIdx);
    posBeginIdx = posEndIdx+1;
posEndIdx=ArduinoString.find(sDelim2,posBeginIdx);
sValue= ArduinoString.substr(posBeginIdx,posEndIdx-posBeginIdx);
    std::cout <<endl<<« H keyword-> » <<sKeyword <<endl<<« H value-> »<<sValue<<endl;
return 1;
}

compiler:

g++  -Wall monfichier.cpp -o monprogramme

Résultat:

Example of Arduino String->Temperature=23.4;Humidite=45.6231;blablabla

T keyword->TemperatureT value->23.4H keyword->HumiditeH value->45.6231

Corrolaire 2: Compiler C et C++

solution 1
gcc -c -std=c99 -o file1.o file1.c
g++ -c -std=c++0x -o file2.o file2.cpp
g++ -o myapp file1.o file2.o

solution 2
gcc -std=c99 -c -o test.c.o test.c
g++ -std=c++0x -c -o test.cpp.o test.cpp
g++ -o executable test.cpp.o test.c.o

Liens:
Mixer C et C++
https://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html#c_from_cpp