La Raspberry picow est une raspberry pico avec une interface Wifi et Bluetooth.
Il existe cependant quelques différences comme le fait que la led de la carte n'est plus sur le GPIO 25 mais sur un GPIO du circuit CYW43439 avec les interfaces Wifi et Bluetooth. Avec L'IDE Arduino, cette LED se nomme toujours LED_BUILTIN.
La méthode de programmation reste identique à celle de la pico.
Il est conseillé de se reporter au chapitre sur la raspberry pico avant d'utiliser la picow.
Notez que je décline toutes responsabilités quant aux conséquences que pourraient avoir l'application des méthodes et conseils suivants ainsi que l'utilisation des programmes présentés. Ceux-ci pourraient être erronés ou obsolètes.
On utilise les fonctions de l'interface Wifi CYW43439 :
cyw43_arch_init()
initialise le circuitcyw43_arch_gpio_put(gpio,valeur)
positionne un GPIO (0 ou 1) avec gpio qui est le numéro du gpio : CYW43_WL_GPIO_LED_PIN pour la led de la carte.cyw43_arch_deinit()
désactive le circuitContenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_BOARD pico_w)
pico_sdk_init()
add_executable(blink_w
blink_w.c
)
pico_enable_stdio_usb(blink_w 1)
pico_enable_stdio_uart(blink_w 1)
pico_add_extra_outputs(blink_w)
target_link_libraries(blink_w pico_stdlib pico_cyw43_arch_none)
Contenu du fichier blink.c
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
int main() {
stdio_init_all();
if (cyw43_arch_init()) {
printf("Wi-Fi init failed");
return -1;
}
while (true) {
printf("on clignote\n");
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
sleep_ms(250);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(250);
}
cyw43_arch_deinit();
}
stdio_init_all
dirige l'entrée et la sortie standard vers la liaison série. Les messages peuvent être visualisés à l'aide d'un logiciel de terminal série.On utilise le capteur de température et d'humidité de la carte Arduino MKR env. N'ayant pas de librairie compatible avec le SDK C, on la crée.
La gestion du bus I2C a déjà été présentée dans le chapitre sur l'embarqué.
Contenu du fichier hts221_defs.h
#ifndef __HTS221_DEFS_H
#define __HTS221_DEFS_H
#define GP_SDA 4
#define GP_SCL 5
#define HTS221_I2CADDR 0x5f
#define HTS221_WHO_AM_I_REG 0x0f
#define HTS221_AV_CONF_REG 0x10
#define HTS221_CTRL_REG1 0x20
#define HTS221_CTRL_REG2 0x21
#define HTS221_CTRL_REG3 0x22
#define HTS221_STATUS_REG 0x27
#define HTS221_HUMIDITY_OUT_L_REG 0x28
#define HTS221_TEMP_OUT_L_REG 0x2a
#define HTS221_H0_rH_x2_REG 0x30
#define HTS221_H1_rH_x2_REG 0x31
#define HTS221_T0_degC_x8_REG 0x32
#define HTS221_T1_degC_x8_REG 0x33
#define HTS221_T1_T0_MSB_REG 0x35
#define HTS221_H0_T0_OUT_REG 0x36
#define HTS221_H1_T0_OUT_REG 0x3a
#define HTS221_T0_OUT_REG 0x3c
#define HTS221_T1_OUT_REG 0x3e
#define HTS221_WHO_AM_I 0xbc
#define HTS221_AVGH_MASK 0x07
#define HTS221_AVGT_MASK 0x38
#define HTS221_AVGT_2 (0x00 << 3)
#define HTS221_AVGT_4 (0x01 << 3)
#define HTS221_AVGT_8 (0x02 << 3)
#define HTS221_AVGT_16 (0x03 << 3)
#define HTS221_AVGT_32 (0x04 << 3)
#define HTS221_AVGT_64 (0x05 << 3)
#define HTS221_AVGT_128 (0x06 << 3)
#define HTS221_AVGT_256 (0x07 << 3)
#define HTS221_AVGH_4 0x00
#define HTS221_AVGH_8 0x01
#define HTS221_AVGH_16 0x02
#define HTS221_AVGH_32 0x03
#define HTS221_AVGH_64 0x04
#define HTS221_AVGH_128 0x05
#define HTS221_AVGH_256 0x06
#define HTS221_AVGH_512 0x07
#define HTS221_REG1_PD 0x80
#define HTS221_REG1_BDU 0x04
#define HTS221_REG1_ORD_ONE_SHOT 0x00
#define HTS221_REG1_ORD_1HZ 0x01
#define HTS221_REG1_ORD_7HZ 0x02
#define HTS221_REG1_ORD_12_5HZ 0x03
#define HTS221_REG1_ORD_MASK 0x03
#define HTS221_REG2_BOOT 0x80
#define HTS221_REG2_HEATER 0x02
#define HTS221_REG2_ONE_SHOT 0x01
#define HTS221_REG3_DRDY_H_L 0x80
#define HTS221_REG3_PP_OD 0x40
#define HTS221_REG3_DRDY_EN 0x02
#define HTS221_STATUS_H_DA 0x02
#define HTS221_STATUS_T_DA 0x01
#define HTS221_T0_MSB_MASK 0x03
#define HTS221_T1_MSB_MASK 0x0c
#define HTS221_T0_MSB_SHIFT 8
#define HTS221_T1_MSB_SHIFT 6
#endif
Contenu du fichier hts221.h
#ifndef __HTS221_H
#define __HTS221_H
#include "hts221_defs.h"
int hts221manutactureId(void);
int hts221Init(int);
void hts221tempCalibration(void);
void hts221humCalibration(void) ;
int hts221Mesure(void);
int hts221Pret(void) ;
int hts221lire(unsigned int );
void hts221close(void);
float hts221lireTemperature();
uint16_t hts221lireHumidite();
#endif
Les fonctions de gestion du bus I2C sont inspirées du chapitre sur l'embarqué.
Le paramètre mode
permet de définir la période d'échantillonnage dans les bits 1 et 0) du registre de contrôle 1 (HTS221_CTRL_REG1) :
HTS221_REG1_ORD_ONE_SHOT
: pas de période, mode One-shot mesure à la demande (bit 0 du registre de contrôle 2), il faut également créer cette période d'échantillonnage par programme (ex sleep).HTS221_REG1_ORD_1HZ
: 1 ,période d'échantillonnage 1Hz (pas de demande manuelle).HTS221_REG1_ORD_7HZ
: 2 ,période d'échantillonnage 7Hz (pas de demande manuelle).HTS221_REG1_ORD_12_5HZ
: 3 , période d'échantillonnage 12.5Hz (pas de demande manuelle).Pour les choix 1 à 3, avec période, il faut que le traitement des données soit inférieures à cette période, sinon il faut mieux utiliser le mode manuel.
Les fonctions de calibration sont inspirées de la documentation TN1218 fournie par le constructeur.
Contenu du fichier hts221.c
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hts221.h"
const uint sda_pin = GP_SDA;
const uint scl_pin = GP_SCL;
i2c_inst_t *i2c = i2c0;
int mode;
int16_t T0_degC;
int16_t T1_degC;
int16_t T0_OUT;
int16_t T1_OUT;
int16_t H0_T0_OUT;
int16_t H1_T0_OUT;
int16_t H0_rH;
int16_t H1_rH;
void hts221EcrireReg(unsigned char reg,unsigned char valeur) {
unsigned char buffer[2];
buffer[0] = reg;
buffer[1] = valeur;
i2c_write_blocking(i2c,HTS221_I2CADDR,buffer,2,false);
}
int hts221LireReg(unsigned char reg) {
int resultat = 0;
unsigned char buffer[2];
buffer[0] = reg;
i2c_write_blocking(i2c,HTS221_I2CADDR,buffer,1,true);
int nread = i2c_read_blocking(i2c,HTS221_I2CADDR,buffer,1,false);
if (nread != 1) {
return -1 ;
}
resultat = (int)buffer[0];
return resultat;
}
int hts221lire(unsigned int LSBReg) {
int valeur = 0;
int lsb,msb;
int resultat =0;
resultat = hts221LireReg(LSBReg);
if (resultat < 0) {
return resultat;
}
lsb = resultat ;
resultat = hts221LireReg(LSBReg + 1);
if (resultat < 0) {
return resultat;
}
msb = resultat;
valeur = ((msb & 0xff) << 8) + (lsb & 0xff) ;
return valeur;
}
int hts221Init(int m) {
int resultat=0;
i2c_init(i2c, 100000UL);
gpio_set_function(sda_pin, GPIO_FUNC_I2C);
gpio_set_function(scl_pin, GPIO_FUNC_I2C);
mode = m ;
if (mode != 0) {
hts221EcrireReg(HTS221_AV_CONF_REG,HTS221_AVGH_256 | HTS221_AVGT_256);
hts221EcrireReg(HTS221_CTRL_REG1,HTS221_REG1_PD | mode);
}
else {
hts221EcrireReg(HTS221_CTRL_REG1,HTS221_REG1_PD | HTS221_REG1_BDU);
}
hts221EcrireReg(HTS221_CTRL_REG2,0);
hts221EcrireReg(HTS221_CTRL_REG3,HTS221_REG3_PP_OD);
return resultat;
}
void hts221tempCalibration() {
uint16_t Tx_degC_x8_msb_lsb ;
uint16_t Tx_degC_x8_lsb;
T0_OUT = hts221lire(HTS221_T0_OUT_REG);
T1_OUT = hts221lire(HTS221_T1_OUT_REG);
Tx_degC_x8_lsb = hts221LireReg(HTS221_T0_degC_x8_REG);
Tx_degC_x8_msb_lsb = hts221LireReg(HTS221_T1_T0_MSB_REG);
T0_degC = (Tx_degC_x8_lsb | ((( Tx_degC_x8_msb_lsb & HTS221_T0_MSB_MASK) << HTS221_T0_MSB_SHIFT))) >> 3;
Tx_degC_x8_lsb = hts221LireReg(HTS221_T1_degC_x8_REG);
T1_degC = (Tx_degC_x8_lsb | ((( Tx_degC_x8_msb_lsb & HTS221_T1_MSB_MASK) << HTS221_T1_MSB_SHIFT))) >> 3;
}
void hts221humCalibration() {
uint8_t HO_rHx2;
uint8_t H1_rHx2;
H0_T0_OUT = hts221lire(HTS221_H0_T0_OUT_REG);
H1_T0_OUT = hts221lire(HTS221_H1_T0_OUT_REG);
HO_rHx2 = hts221lire(HTS221_H0_rH_x2_REG);
H1_rHx2 = hts221lire(HTS221_H1_rH_x2_REG);
H0_rH = HO_rHx2 >> 1;
H1_rH = H1_rHx2 >> 1;
}
int hts221manutactureId() {
int resultat = 0 ;
resultat = hts221LireReg(HTS221_WHO_AM_I_REG);
return resultat;
}
int hts221Mesure() {
int resultat = 0;
hts221EcrireReg(HTS221_CTRL_REG2,HTS221_REG2_ONE_SHOT);
return resultat ;
}
int hts221Pret() {
int resultat =0;
resultat = hts221LireReg(HTS221_STATUS_REG);
if (resultat < 0) {
return resultat;
}
return ((resultat & (HTS221_STATUS_H_DA | HTS221_STATUS_T_DA))==(HTS221_STATUS_H_DA | HTS221_STATUS_T_DA));
}
float hts221lireTemperature() {
int16_t valeur;
uint16_t T_out = hts221lire(HTS221_TEMP_OUT_L_REG);
int32_t numerateur = (( int32_t)(T_out - T0_OUT)) * (( int32_t)(T1_degC - T0_degC)*10);
valeur = numerateur /(T1_OUT - T0_OUT) + T0_degC*10;
return valeur/10.0 ;
}
uint16_t hts221lireHumidite() {
uint16_t valeur;
int16_t H_T_out = hts221lire(HTS221_HUMIDITY_OUT_L_REG);
int32_t numerateur = ((int32_t)(H_T_out - H0_T0_OUT)) * ((int32_t)(H1_rH - H0_rH));
valeur = (uint16_t) (numerateur/(H1_T0_OUT - H0_T0_OUT) + H0_rH);
return valeur ;
}
void hts221close() {
i2c_deinit(i2c);
}
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_BOARD pico_w)
pico_sdk_init()
add_executable(picow_hts221
hts221.c
picow_hts221.c
)
pico_enable_stdio_usb(picow_hts221 1)
pico_enable_stdio_uart(picow_hts221 1)
pico_add_extra_outputs(picow_hts221)
target_link_libraries(picow_hts221 hardware_i2c pico_stdlib)
Contenu du programme de test
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hts221.h"
int main() {
float temperature;
uint16_t humidite;
stdio_init_all();
printf("init\n");
hts221Init(0);
printf("calibration\n");
hts221tempCalibration();
hts221humCalibration();
int id = hts221manutactureId();
printf("id = %d\n",id);
while (true) {
hts221Mesure();
while (!hts221Pret());
temperature = hts221lireTemperature();
humidite = hts221lireHumidite();
printf("temperature %.2f\n",temperature);
printf("humidite %u\n",humidite);
sleep_ms(1000);
}
}
L'utilisation du Wifi utilise également la librairie interface Wifi CYW43439, ici on utilise les fonctions :
cyw43_arch_init_with_country(codepays)
initialise le circuit avec codepays qui peut être ici CYW43_COUNTRY_FRANCE pour la codification correspondant à la France.cyw43_arch_enable_sta_mode()
définit le mode client wifi (STA).cyw43_arch_wifi_connect_blocking(ssid,passphase,authentification)
qui établit la connexion sur le réseau ssid avec le mode de passe passphrase, authentification inique le type d'authentification qui vaut CYW43_AUTH_WPA2_AES_PSK pour un réseau personnel (box par exemple).cyw43_wifi_get_mac(&cyw43_state,type_interface,MACaddr)
retourne l'adresse MAC dans le tableau MACadr, &cyw43_state est une variable prédéfinie qu'il faut utiliser, type_interface définit le type d'interface CYW43_ITF_STA pour un client wifi ou CYW43_ITF_AP pour une borne wifi.cyw43_wifi_get_rssi(&cyw43_state,adresserssi)
avec adresserssi qui est l'adresse d'un entier dans lequel transmis la valeur du RSSI.cyw43_arch_deinit()
désactive le circuitLe projet wifi nécessite des fichiers supplémentaires :
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_BOARD pico_w)
pico_sdk_init()
add_executable(picow_wifi
picow_wifi.c
)
pico_enable_stdio_usb(picow_wifi 1)
pico_enable_stdio_uart(picow_wifi 1)
pico_add_extra_outputs(picow_wifi)
target_include_directories(picow_wifi PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(picow_wifi pico_stdlib pico_cyw43_arch_lwip_threadsafe_background)
pico_cyw43_arch_lwip_threadsafe_background est la librairie utilisée pour la gestion du Wifi.
La fonction Wificonnexion()
définit le mode client puis établit la connexion Wifi en mode WPA2 personnel.
Si la connexion est réussie, le programme affiche l'adresse MAC de la carte ainsi que le niveau RSSI de la liaison.
La boucle infinie fait clignoter la LED de la carte.
Pour l'utilisation de la fonction return, voir l'explication la raspberry pico de l'embarqué.
Contenu du fichier blink.c
#include <pico/stdlib.h>
#include "pico/cyw43_arch.h"
#include "wifi_connexion.h"
int Wificonnexion() {
cyw43_arch_enable_sta_mode();
int conok=cyw43_arch_wifi_connect_blocking(BORNE_SSID,BORNE_PASSPHRASE,CYW43_AUTH_WPA2_AES_PSK);
if (conok != 0) {
printf("connection error\n");
return -1;
}
printf("connecté\n");
return 0;
}
int main() {
char MACaddr[30];
cyw43_t cyw43;
int32_t rssi;
stdio_init_all();
if (cyw43_arch_init_with_country(CYW43_COUNTRY_FRANCE)) {
return -1;
}
int err=Wificonnexion();
if (err) {
return err;
}
err=cyw43_wifi_get_mac(&cyw43_state,CYW43_ITF_STA,MACaddr);
if (err) {
printf("Erreur\n");
}
printf("MAC : ");
for(int i=0;i<5;i+=1) {
printf("%02x:",MACaddr[i]);
}
printf("%02x\n",MACaddr[5]);
err = cyw43_wifi_get_rssi(&cyw43_state, &rssi);
if (err) {
printf("Erreur\n");
}
printf("RSSI : %d dB\n",rssi);
while (true) {
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(250);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
sleep_ms(250);
}
}
On utilise les fonctions de la librairie lwip.
httpc_get_file_dns(HOSTNAME, HTTP_DEFAULT_PORT, uri_donnees, ¶metres, recv_fn, NULL, &connexion)
établit une connexion HTPP pour transmettre les données avec
httpc_connection_t
.
httpc_state_t
qui contient les informations de connexion.Le programme effectue l'envoi de valeurs de température et d'humidité au serveur via le script CGI sauvehts221.cgi qui est une adaptation du script CGI sauvecapteurs.cgi.
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_BOARD pico_w)
pico_sdk_init()
add_executable(picow_hts221_wifi_http
hts221.c
picow_hts221_wifi_http.c
)
pico_enable_stdio_usb(picow_hts221_wifi_http 1)
pico_enable_stdio_uart(picow_hts221_wifi_http 1)
pico_add_extra_outputs(picow_hts221_wifi_http)
target_include_directories(picow_hts221_wifi_http PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(picow_hts221_wifi_http pico_stdlib hardware_i2c pico_cyw43_arch_lwip_threadsafe_background pico_lwip_http)
pico_lwip_http est la librairie de gestion de HTTP.
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID, BORNE_PASSPHRASE et HOSTNAME.
La fonction headers_done_fn ne fait rien.
La fonction result_fn affiche un message en cas d'erreur de code retour HTTP.
La fonction recv_fn affiche et traite les données reçues, qui dans le cas du script CGI utilisé se résument à la chaîne code=retour.
Le programme principal initialise la connexion Wifi, initialise le capteur, calibre le capteur, puis lit les données de température et d'humidité et enfin les envoie au serveur via le script CGI.
Pour l'utilisation de la fonction return, voir l'explication la raspberry pico de l'embarqué.
Contenu du programme d'envoi des données au serveur
#include >pico/stdlib.h<
#include "pico/cyw43_arch.h"
#include "lwip/apps/http_client.h"
#include "hardware/i2c.h"
#include "hts221.h"
#include "wifi_connexion.h"
int Wificonnexion() {
cyw43_arch_enable_sta_mode();
int conok=cyw43_arch_wifi_connect_blocking(BORNE_SSID,BORNE_PASSPHRASE,CYW43_AUTH_WPA2_AES_PSK);
if (conok != 0) {
printf("connection error\n");
return -1;
}
printf("connecté\n");
return 0;
}
static err_t headers_done_fn(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len) {
return ERR_OK;
}
static void result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err) {
if (srv_res != 200) {
printf("Erreur HTTP\n");
}
}
static err_t recv_fn(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
char mescode[20] ;
if (p == NULL) {
printf("Erreur NULL\n");
}
else {
strncpy(mescode,(char *)p->payload,p->len);
mescode[p->len-1] = '\0' ;
char *p=strtok(mescode,"=");
p=strtok(NULL,"=");
printf("%s\n",p);
int code = strtol(p,NULL,0);
if (code == 0) {
printf("Transmission OK\n");
}
else {
printf("Transmission ERREUR\n");
}
}
return ERR_OK;
}
int main() {
float temperature;
uint16_t humidite;
char urlvaleurs[100];
httpc_connection_t settings = {
.use_proxy = 0,
.headers_done_fn = headers_done_fn,
.result_fn = result_fn
};
httpc_state_t *connection = NULL;
stdio_init_all();
printf("init\n");
hts221Init(0);
printf("calibration\n");
hts221tempCalibration();
hts221humCalibration();
int id = hts221manutactureId();
printf("id = %d\n",id);
if (cyw43_arch_init_with_country(CYW43_COUNTRY_FRANCE)) {
return -1;
}
int connexion=Wificonnexion();
if (connexion) {
return connexion;
}
while (true) {
hts221Mesure();
while (!hts221Pret());
temperature = hts221lireTemperature();
humidite = hts221lireHumidite();
printf("temperature %.2f\n",temperature);
printf("humidite %u\n",humidite);
sprintf(urlvaleurs,"/cgi-bin/sauvehts221.cgi?temperature=%.2f&humidite=%d",temperature,humidite);
printf("%s\n",urlvaleurs);
httpc_get_file_dns(HOSTNAME, HTTP_DEFAULT_PORT, urlvaleurs , &settings, recv_fn, NULL, &connection);
sleep_ms(1000);
}
}
On utilise également les fonctions de la librairie lwip.
En premier il faut chercher l'adresse IP du serveur en utilisant une requête DNS
cyw43_arch_lwip_begin()
débute la requêtedns_gethostbyname(HOSTNAME, adresse_ip, dns_found, dns_found_param)
effectue la requête DNS sur HOSTNAME, adresse_ip est la structure de type ip_addr_t
qui contient l'adresse IP, dns_found est l'adresse d'une fonction qui est appelée lorsque la requête est terminée, dns_found_param est l'adresse d'une zone de paramètres transmis à la fonction dns_found.cyw43_arch_lwip_end()
termine la requêteEnsuite on effectue les requête MQTT avec l'adresse IP
mqtt_client_new()
alloue la mémoire et retourne un pointeur vers une structure de type mqtt_client_t
mqtt_client_connect(client, ip, port,mqtt_callback, mqtt_callback_param, clientInfo)
avec
mqtt_client_t
mqtt_client_is_connected(client)
retourne vari si la connexion est effectivemqtt_publish(mqtt_publish(client, topic, message, longueur, qos, retain, mqtt_pub_request_cb, arg)
avec
mqtt_client_t
mqtt_disconnect(mqtt_client)
déconnexion du serveurmqtt_client_free(mqtt_client)
libère la mémoire allouéeExemple d'envoi de publication d'un message auprès d'un serveur MQTT
Contenu du fichier CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(perga_projets C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_BOARD pico_w)
pico_sdk_init()
add_executable(picow_wifi_mqtt
picow_wifi_mqtt.c
)
pico_enable_stdio_usb(picow_wifi_mqtt 1)
pico_enable_stdio_uart(picow_wifi_mqtt 1)
pico_add_extra_outputs(picow_wifi_mqtt)
target_include_directories(picow_wifi_mqtt PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(picow_wifi_mqtt pico_stdlib pico_cyw43_arch_lwip_threadsafe_background pico_lwip_mqtt)
pico_lwip_mqtt est la librairie MQTT
Requête DNS
La fonction run_dns_lookup
effectue la requête DNS afin d'obtenir l'adresse IP du serveur.
A la fin de la requête la foncion callback dns_found
est appelée.
La fonction attend que l'adresse soit trouvée pour se terminer. On a une synchronisation avec la fonction callback.
Connexion MQTT
La fonction MQTTconnexion
effectue la connexion au serveur MQTT tant que la connexion n'a pas réussie. La fonction callback mqtt_connection_cb
est appelée en fin de connexion, elle ne fait rien d'autre qu'afficher le résultat de la connexion.
La fonction MQTTpublish
publie un message.
La fonction callback mqtt_pub_request_cb
ne fait qu'afficher le résultat de la demande de publication.
Le programme principal effectue les étapes suivantes
Pour l'utilisation de la fonction return, voir l'explication la raspberry pico de l'embarqué.
Contenu du fichier de test
#include <pico/stdlib.h>
#include "pico/cyw43_arch.h"
#include "lwip/dns.h"
#include "lwip/apps/mqtt.h"
#include "lwip/apps/mqtt_priv.h"
#include "wifi_connexion.h"
#define TOPIC "/maison/piece/lumiere"
#define MESSAGE "allume"
int Wificonnexion() {
cyw43_arch_enable_sta_mode();
int conok=cyw43_arch_wifi_connect_blocking(BORNE_SSID,BORNE_PASSPHRASE,CYW43_AUTH_WPA2_AES_PSK);
if (conok != 0) {
printf("connection error\n");
return -1;
}
printf("connecté\n");
return 0;
}
void dns_found(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
ip_addr_t *paddr = (ip_addr_t*)callback_arg;
printf("Requête DNS terminée, adresse %s trouvée\n", ip4addr_ntoa(ipaddr));
*paddr = *ipaddr;
}
int run_dns_lookup(ip_addr_t *ip_addr) {
printf("recherche DNS pour %s\n", HOSTNAME);
cyw43_arch_lwip_begin();
err_t err = dns_gethostbyname(HOSTNAME, ip_addr, dns_found, ip_addr);
cyw43_arch_lwip_end();
if (err == ERR_ARG) {
printf("ERREUR reherche DNS\n");
return -1;
}
if (err == ERR_OK) {
printf("DNS Ok sans lookup");
return 0;
}
while (ip_addr->addr == 0) {
sleep_ms(1);
}
return 0;
}
void afficheInfos(struct mqtt_connect_client_info_t *clientInfo) {
printf("ID : %s\n",clientInfo->client_id);
if (clientInfo->client_user != NULL) {
printf("USER/password : %s\n",clientInfo->client_user);
}
else {
printf("no USER, no password\n");
}
printf("keep_alive %u\n",clientInfo->keep_alive);
if (clientInfo->will_topic != NULL) {
printf("will topic : %s\n",clientInfo->will_topic);
}
else {
printf("no will_topic\n");
}
}
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t statut) {
err_t err;
ip_addr_t *paddr = (ip_addr_t *)arg;
if(statut == MQTT_CONNECT_ACCEPTED) {
printf("mqtt_connection_cb, connexion réussie\n");
}
else {
printf("mqtt_connection_cb, ERREUR connexion %d\n", statut);
}
}
void MQTTconnexion(mqtt_client_t *client,ip_addr_t *ip_addr,struct mqtt_connect_client_info_t *clientInfo) {
err_t err;
err = mqtt_client_connect(client, ip_addr, MQTT_PORT, mqtt_connection_cb, ip_addr, clientInfo);
cyw43_arch_poll();
while (err != ERR_OK) {
printf("mqtt_connect return %d, reconnect\n", err);
err = mqtt_client_connect(client, ip_addr, MQTT_PORT, mqtt_connection_cb, ip_addr, clientInfo);
cyw43_arch_poll();
}
afficheInfos(clientInfo);
}
static void mqtt_pub_request_cb(void *arg, err_t result) {
char *message = (char *)arg;
if(result != ERR_OK) {
printf("cb Publish result: %d\n", result);
}
else {
printf("cb publish OK %s\n",message);
}
}
void MQTTpublish(mqtt_client_t *client, char *topic, char *message ,void *arg) {
err_t err;
u8_t qos = 0;
u8_t retain = 0;
int longueur = strlen(message);
printf("%s %s %d\n",topic,message,longueur);
err = mqtt_publish(client, topic, message, longueur, qos, retain, mqtt_pub_request_cb, arg);
if(err != ERR_OK) {
printf("Publish err: %d\n", err);
}
else {
printf("publish OK\n");
}
}
int main() {
ip_addr_t server_addr;
mqtt_client_t *mqtt_client;
struct mqtt_connect_client_info_t clientInfo;
stdio_init_all();
if (cyw43_arch_init_with_country(CYW43_COUNTRY_FRANCE)) {
return -1;
}
int connexion=Wificonnexion();
if (connexion) {
return connexion;
}
server_addr.addr = 0 ;
int err = run_dns_lookup(&server_addr);
if (err) {
return err;
}
printf("DNS terminé adresse %s : %s\n",HOSTNAME,ip4addr_ntoa(&server_addr));
printf("création mémoire mqtt_client\n");
mqtt_client = mqtt_client_new();
if (mqtt_client == NULL) {
printf("allocation client erreur\n");
return -1;
}
memset(&clientInfo,0,sizeof(clientInfo));
clientInfo.client_id = "picow";
printf("IP %s\n",ip4addr_ntoa(&server_addr));
printf("connexion ...\n");
MQTTconnexion(mqtt_client,&server_addr,&clientInfo);
sleep_ms(1000);
int estconnecte = mqtt_client_is_connected(mqtt_client);
printf("connexion %d\n",estconnecte);
sleep_ms(1000);
if (estconnecte) {
printf("publie \n");
MQTTpublish(mqtt_client,TOPIC,MESSAGE,"essai");
sleep_ms(1000);
}
printf("deconnexion serveur\n");
mqtt_disconnect(mqtt_client);
printf("libération mémoire mqtt_client\n");
mqtt_client_free(mqtt_client);
while (true) {
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(500);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
sleep_ms(500);
}
}
On utilise la librairie Wire pour gérer le bus I2C comme cela a déjà été fait pour le capteur BH1745 du chapitre sur l'arduino dans systèmes embarqués ainsi que de la classe utilisée pour la partie SDK C de cette page.
Contenu du fichier hts221_defs.h
#ifndef __HTS221_DEFS_H
#define __HTS221_DEFS_H
#define GP_SDA 4
#define GP_SCL 5
#define HTS221_I2CADDR 0x5f
#define HTS221_WHO_AM_I_REG 0x0f
#define HTS221_AV_CONF_REG 0x10
#define HTS221_CTRL_REG1 0x20
#define HTS221_CTRL_REG2 0x21
#define HTS221_CTRL_REG3 0x22
#define HTS221_STATUS_REG 0x27
#define HTS221_HUMIDITY_OUT_L_REG 0x28
#define HTS221_TEMP_OUT_L_REG 0x2a
#define HTS221_H0_rH_x2_REG 0x30
#define HTS221_H1_rH_x2_REG 0x31
#define HTS221_T0_degC_x8_REG 0x32
#define HTS221_T1_degC_x8_REG 0x33
#define HTS221_T1_T0_MSB_REG 0x35
#define HTS221_H0_T0_OUT_REG 0x36
#define HTS221_H1_T0_OUT_REG 0x3a
#define HTS221_T0_OUT_REG 0x3c
#define HTS221_T1_OUT_REG 0x3e
#define HTS221_WHO_AM_I 0xbc
#define HTS221_AVGH_MASK 0x07
#define HTS221_AVGT_MASK 0x38
#define HTS221_AVGT_2 (0x00 << 3)
#define HTS221_AVGT_4 (0x01 << 3)
#define HTS221_AVGT_8 (0x02 << 3)
#define HTS221_AVGT_16 (0x03 << 3)
#define HTS221_AVGT_32 (0x04 << 3)
#define HTS221_AVGT_64 (0x05 << 3)
#define HTS221_AVGT_128 (0x06 << 3)
#define HTS221_AVGT_256 (0x07 << 3)
#define HTS221_AVGH_4 0x00
#define HTS221_AVGH_8 0x01
#define HTS221_AVGH_16 0x02
#define HTS221_AVGH_32 0x03
#define HTS221_AVGH_64 0x04
#define HTS221_AVGH_128 0x05
#define HTS221_AVGH_256 0x06
#define HTS221_AVGH_512 0x07
#define HTS221_REG1_PD 0x80
#define HTS221_REG1_BDU 0x04
#define HTS221_REG1_ORD_ONE_SHOT 0x00
#define HTS221_REG1_ORD_1HZ 0x01
#define HTS221_REG1_ORD_7HZ 0x02
#define HTS221_REG1_ORD_12_5HZ 0x03
#define HTS221_REG1_ORD_MASK 0x03
#define HTS221_REG2_BOOT 0x80
#define HTS221_REG2_HEATER 0x02
#define HTS221_REG2_ONE_SHOT 0x01
#define HTS221_REG3_DRDY_H_L 0x80
#define HTS221_REG3_PP_OD 0x40
#define HTS221_REG3_DRDY_EN 0x02
#define HTS221_STATUS_H_DA 0x02
#define HTS221_STATUS_T_DA 0x01
#define HTS221_T0_MSB_MASK 0x03
#define HTS221_T1_MSB_MASK 0x0c
#define HTS221_T0_MSB_SHIFT 8
#define HTS221_T1_MSB_SHIFT 6
#endif
Contenu du fichier hts221.h
#ifndef __HTS221_H
#define __HTS221_H
#include "hts221_defs.h"
class HTS221 {
private :
int i2cadresse;
int mode;
int16_t T0_degC;
int16_t T1_degC;
int16_t T0_OUT;
int16_t T1_OUT;
int16_t H0_T0_OUT;
int16_t H1_T0_OUT;
int16_t H0_rH;
int16_t H1_rH;
void setAdresse(int);
void ecrireRegistre(byte , byte );
int lireRegistre(byte );
int lireRegistreLH(byte );
void tempCalibration();
void registresInit();
void humCalibration();
public:
HTS221(void);
HTS221(int );
void begin(void);
void end(void);
int16_t getT0_OUT(void);
int16_t getT1_OUT(void);
int16_t getT0_degC(void);
int16_t getT1_degC(void);
int16_t getH0_T0_OUT(void);
int16_t getH1_T0_OUT(void);
int16_t getH0_rH(void);
int16_t getH1_rH(void);
byte getID(void);
void demandeMesure(void);
boolean mesurePrete(void);
float lireTemperature();
uint16_t lireHumidite();
};
#endif
Ici on est seulement en mode One-shot, le paramètre mode n'est pas implémenté.
Les fonctions de calibration sont inspirées de la documentation TN1218 fournie par le constructeur.
Contenu du fichier hts221.cpp
#include <Arduino.h>
#include <Wire.h>
#include "hts221.h"
void HTS221::setAdresse(int adresse) {
this->i2cadresse = adresse ;
}
void HTS221::ecrireRegistre(byte registre , byte valeur) {
Wire.beginTransmission(i2cadresse);
Wire.write(registre);
Wire.write(valeur);
Wire.endTransmission();
}
int HTS221::lireRegistre(byte registre){
int recu = -1;
Wire.beginTransmission(i2cadresse);
Wire.write(registre);
Wire.endTransmission();
Wire.requestFrom(i2cadresse, 1);
if (Wire.available() == 1) {
recu = Wire.read();
}
return recu ;
}
int HTS221::lireRegistreLH(byte LSBReg ) {
int valeur=0;
int lsb,msb;
lsb = lireRegistre(LSBReg);
msb = lireRegistre(LSBReg + 1);
valeur = (lsb & 0xff ) | (msb << 8);
return valeur;
}
void HTS221::tempCalibration() {
uint16_t Tx_degC_x8_msb_lsb ;
uint16_t Tx_degC_x8_lsb;
T0_OUT = lireRegistreLH(HTS221_T0_OUT_REG);
T1_OUT = lireRegistreLH(HTS221_T1_OUT_REG);
Tx_degC_x8_lsb = lireRegistre(HTS221_T0_degC_x8_REG);
Tx_degC_x8_msb_lsb = lireRegistre(HTS221_T1_T0_MSB_REG);
T0_degC = (Tx_degC_x8_lsb | ((( Tx_degC_x8_msb_lsb & HTS221_T0_MSB_MASK) << HTS221_T0_MSB_SHIFT))) >> 3;
Tx_degC_x8_lsb = lireRegistre(HTS221_T1_degC_x8_REG);
T1_degC = (Tx_degC_x8_lsb | ((( Tx_degC_x8_msb_lsb & HTS221_T1_MSB_MASK) << HTS221_T1_MSB_SHIFT))) >> 3;
}
void HTS221::humCalibration() {
uint8_t HO_rHx2;
uint8_t H1_rHx2;
H0_T0_OUT = lireRegistreLH(HTS221_H0_T0_OUT_REG);
H1_T0_OUT = lireRegistreLH(HTS221_H1_T0_OUT_REG);
HO_rHx2 = lireRegistre(HTS221_H0_rH_x2_REG);
H1_rHx2 = lireRegistre(HTS221_H1_rH_x2_REG);
H0_rH = HO_rHx2 >> 1;
H1_rH = H1_rHx2 >> 1;
}
void HTS221::registresInit() {
if (mode != 0) {
ecrireRegistre(HTS221_AV_CONF_REG,HTS221_AVGH_256 | HTS221_AVGT_256);
ecrireRegistre(HTS221_CTRL_REG1,HTS221_REG1_PD | mode);
}
else {
ecrireRegistre(HTS221_CTRL_REG1,HTS221_REG1_PD | HTS221_REG1_BDU);
}
ecrireRegistre(HTS221_CTRL_REG2,0);
ecrireRegistre(HTS221_CTRL_REG3,HTS221_REG3_PP_OD);
}
HTS221::HTS221() {
setAdresse(HTS221_I2CADDR);
}
HTS221::HTS221(int i2cadresse) {
setAdresse(i2cadresse);
}
int16_t HTS221::getT0_OUT() {
return T0_OUT;
}
int16_t HTS221::getT1_OUT() {
return T1_OUT;
}
int16_t HTS221::getT0_degC() {
return T0_degC;
}
int16_t HTS221::getT1_degC() {
return T1_degC;
}
int16_t HTS221::getH0_T0_OUT() {
return H0_T0_OUT;
}
int16_t HTS221::getH1_T0_OUT() {
return H1_T0_OUT;
}
int16_t HTS221::getH0_rH() {
return H0_rH;
}
int16_t HTS221::getH1_rH() {
return H1_rH;
}
byte HTS221::getID() {
return lireRegistre(HTS221_WHO_AM_I_REG);
}
void HTS221::begin() {
Wire.setSDA(GP_SDA);
Wire.setSCL(GP_SCL);
Wire.setClock(100000);
Wire.begin();
this->mode = 0;
registresInit();
humCalibration();
tempCalibration();
}
void HTS221::end() {
Wire.end();
}
void HTS221::demandeMesure(void) {
ecrireRegistre(HTS221_CTRL_REG2,HTS221_REG2_ONE_SHOT);
}
boolean HTS221::mesurePrete(void) {
uint8_t etat = lireRegistre(HTS221_STATUS_REG);
return ((etat & (HTS221_STATUS_H_DA | HTS221_STATUS_T_DA))==(HTS221_STATUS_H_DA | HTS221_STATUS_T_DA));
}
float HTS221::lireTemperature() {
int16_t valeur;
uint16_t T_out = lireRegistreLH(HTS221_TEMP_OUT_L_REG);
int32_t numerateur = (( int32_t)(T_out - T0_OUT)) * (( int32_t)(T1_degC - T0_degC)*10);
valeur = numerateur /(T1_OUT - T0_OUT) + T0_degC*10;
return valeur/10.0 ;
}
uint16_t HTS221::lireHumidite() {
uint16_t valeur;
int16_t H_T_out = lireRegistreLH(HTS221_HUMIDITY_OUT_L_REG);
int32_t numerateur = ((int32_t)(H_T_out - H0_T0_OUT)) * ((int32_t)(H1_rH - H0_rH));
valeur = (uint16_t) (numerateur/(H1_T0_OUT - H0_T0_OUT) + H0_rH);
return valeur ;
}
On utilise la librairie Wifi standard adaptée à la picow :
WiFi.mode(mode);
avec mode qui vaut WIFI_STA pour le client Wifi et WIFI_AP pour une borne Wifi et WiFi.begin(ssid, motpasse);
avec ssid qui est le ssid du réseau Wifi et motpasse qui est la passphrase, établit la connexion Wifi.WiFi.status()
retourne le statut de l'interface wifi
WL_NO_MODULE
: module Wifi non trouvéWL_CONNECTED
: connexion Wifi réussieWL_DISCONNECTED
: plus de connexion WifiWL_IDLE_STATUS
: Wifi en état d'attenteWiFi.firmwareVersion()
: retourne la version du firmware du Wifi sous la forme d'une chaîne (String).Contenu du fichier
#include <SPI.h>
#include <WiFi.h>
#include "wifi_connexion.h"
const char ssid[20] = BORNE_SSID;
const char passphrase[20] = BORNE_PASSPHRASE;
int statut = WL_IDLE_STATUS;
WiFiClient client;
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi.firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(const char *ssid,const char *passphrase) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("Connecte avec l'IP");
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(115200);
while(!Serial);
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi Ok");
connexionWifi(ssid,passphrase);
Serial.println("Wifi Connecte");
}
void loop() {
}
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID et BORNE_PASSPHRASE
Ici on utilise la librairie arduino standard. on retrouve la fonction CheckWifi
du chapitre précédent.
Dans la fonction connexionWifi
on a simplement ajouté WiFi.mode(WIFI_STA)
afin de préciser le mode WiFi client.
On utilise la librairie ArduinoHttpClient déjà utilisée dans le chapitre précédent.
Contenu du fichier
#include <SPI.h>
#include <WiFi.h>
#include <ArduinoHttpClient.h>
#include "wifi_connexion.h"
#include "hts221.h"
HTS221 hts221;
#define PGMCGI "/cgi-bin/sauvehts221.cgi"
WiFiClient client;
HttpClient http(client,HOSTNAME,80);
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi.firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(const char *ssid,const char *passphrase) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
delay(500);
}
Serial.println("");
}
int ConnexionHTTPGET(String parametres) {
int err = 0;
String sReponse ;
int coderetour;
err = http.get(parametres.c_str());
if (err == 0) {
int statutcode = http.responseStatusCode();
if (statutcode == 200) {
err = http.skipResponseHeaders();
if (err >= 0) {
sReponse = "";
while (http.connected() || http.available() ) {
if (http.available()) {
sReponse += http.read();
}
}
if (sReponse.startsWith("code=")) {
String scode = sReponse.substring(5);
coderetour = scode.toInt();
}
}
else {
Serial.println("erreur skip reponseheader");
coderetour = err ;
}
}
else {
Serial.println("erreur code retour ");
coderetour = statutcode ;
}
}
else {
Serial.println("erreur get");
coderetour = err ;
}
http.stop();
return coderetour ;
}
void setup() {
Serial.begin(115200);
while(!Serial);
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi Ok");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
Serial.println("Wifi Connecte");
hts221.begin();
unsigned char manufacture = hts221.getID();
Serial.print("id=");
Serial.println(manufacture,HEX);
}
String chainevaleurs;
String requete ;
void loop() {
hts221.demandeMesure();
while (!hts221.mesurePrete());
float temperature = hts221.lireTemperature();
uint16_t humidite = hts221.lireHumidite();
Serial.print("temperature ");
Serial.print(temperature,1);
Serial.print(" , humidite ");
Serial.println(humidite);
chainevaleurs = "temperature=" + String(temperature);
chainevaleurs += "&humidite=" + String(humidite);
requete = String(PGMCGI) + "?" + chainevaleurs ;
int coderetour = ConnexionHTTPGET(requete);
if (coderetour == 0) {
Serial.println("Transmission OK");
}
else {
Serial.print("Transmission ERREUR ");
Serial.print(coderetour);
}
delay(1000);
}
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID, BORNE_PASSPHRASE et HOSTNAME
Ce programme lit les valeurs de température et d'humidité depuis le capteur hts221 en utilisant la classe HTS221, puis transmet au serveur web ces données via le script CGI sauvehts221.cgi.
La fonction ConnexionHTTPGET
reste identique à celle du programme du chapitre précédent
En résumé ce programme reste une adaptation du programme du chapitre précédent en utilisant la classe HTS221 à la place de la librairie MKR ENV.
On utilise la librairie ArduinoJson déjà utilisée dans le chapitre précédent.
Contenu du fichier
#include <SPI.h>
#include <WiFi.h>
#include <ArduinoHttpClient.h>
#include <ArduinoJson.h>
#include "wifi_connexion.h"
#include "hts221.h"
HTS221 hts221;
#define DOMOTICZ_URL "/json.htm?type=command¶m=udevice&idx"
#define DOMOTICZ_UTILISATEUR_64 "dXNlcgo="
#define DOMOTICZ_MOTPASSE_64 "YWNjZXNzCg=="
#define IDXTEMPERATURE 1
#define IDXHUMIDITE 2
JsonDocument JsReponse;
#define HUMIDITE_NORMAL 0
#define HUMIDITE_CONFORT 1
#define HUMIDITE_SEC 2
#define HUMIDITE_HUMIDE 3
int statutHumidite(float humidite) {
int statut=0;
if (humidite < 20) {
statut = HUMIDITE_SEC;
}
else if (humidite < 50) {
statut = HUMIDITE_NORMAL;
}
else if (humidite < 70) {
statut = HUMIDITE_CONFORT;
}
else {
statut = HUMIDITE_HUMIDE;
}
return statut;
}
WiFiClient client;
HttpClient http(client,HOSTNAME,8080);
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi.firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(const char *ssid,const char *passphrase) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
delay(500);
}
Serial.println("");
}
int ConnexionHTTPGET(String parametres) {
int err = 0;
String sReponse ;
int coderetour;
int statutcode;
http.beginRequest();
http.get("/");
http.sendBasicAuth(DOMOTICZ_UTILISATEUR_64, DOMOTICZ_MOTPASSE_64);
http.get(parametres.c_str());
http.endRequest();
statutcode = http.responseStatusCode();
Serial.println(statutcode);
if (statutcode == 200) {
err = http.skipResponseHeaders();
if (err >= 0) {
sReponse = "";
while (http.connected() || http.available() ) {
if (http.available()) {
sReponse += (char)http.read();
}
}
deserializeJson(JsReponse, sReponse.c_str());
const char *statut = JsReponse["status"];
Serial.print("statut : ");
Serial.println(statut);
coderetour = 0 ;
}
else {
Serial.println("erreur skip reponseheader");
coderetour = err ;
}
}
else {
int taille = http.contentLength();
Serial.print(taille);
Serial.print(", erreur code retour ");
Serial.println(statutcode);
coderetour = statutcode ;
}
http.stop();
return coderetour ;
}
void setup() {
Serial.begin(115200);
while(!Serial);
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi Ok");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
Serial.println("Wifi Connecte");
hts221.begin();
unsigned char manufacture = hts221.getID();
Serial.print("id=");
Serial.println(manufacture,HEX);
}
String chainevaleurs;
String requete ;
void loop() {
hts221.demandeMesure();
while (!hts221.mesurePrete());
float temperature = hts221.lireTemperature();
uint16_t humidite = hts221.lireHumidite();
int statuthumidite = statutHumidite(humidite);
Serial.print("temperature ");
Serial.print(temperature,1);
Serial.print(" , humidite ");
Serial.println(humidite);
requete = String(DOMOTICZ_URL) + "=" + String(IDXTEMPERATURE) + "&nvalue=0&svalue=" + String(temperature) ;
ConnexionHTTPGET(requete);
requete = String(DOMOTICZ_URL) + "=" + String(IDXHUMIDITE) + "&nvalue=" + String(humidite) + "&svalue=" + String(statuthumidite) ;
ConnexionHTTPGET(requete);
delay(1000);
}
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID et BORNE_PASSPHRASE.
Ce programme est également une adaptation du programme du chapitre précédent
On utilise la librairie PubSubClient déjà utilisée dans le chapitre précédent.
Contenu du fichier
#include <SPI.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include "wifi_connexion.h"
#include "hts221.h"
#define TOPIC "domoticz/in"
HTS221 hts221;
#define IDXTEMPERATURE 1
#define IDXHUMIDITE 2
#define HUMIDITE_NORMAL 0
#define HUMIDITE_CONFORT 1
#define HUMIDITE_SEC 2
#define HUMIDITE_HUMIDE 3
int statutHumidite(float humidite) {
int statut=0;
if (humidite < 20) {
statut = HUMIDITE_SEC;
}
else if (humidite < 50) {
statut = HUMIDITE_NORMAL;
}
else if (humidite < 70) {
statut = HUMIDITE_CONFORT;
}
else {
statut = HUMIDITE_HUMIDE;
}
return statut;
}
WiFiClient client;
PubSubClient mqtt(client);
bool CheckWifi() {
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Problème module wifi");
return false;
}
String fv = WiFi.firmwareVersion ();
if ( fv < "1.0.0" ) {
Serial.println("Problème version wifi");
return false;
}
return true;
}
void connexionWifi(const char *ssid,const char *passphrase) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passphrase);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
delay(500);
}
Serial.println("");
}
void reconnect() {
while (!mqtt.connected()) {
Serial.print("MQTT connexion... ");
if (mqtt.connect(HOSTNAME)) {
Serial.println("OK");
}
else {
Serial.print("ERREUR, statut=");
Serial.println(mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
while(!Serial);
if (!CheckWifi()) {
Serial.println("Problème connexion wifi");
while (true);
}
Serial.println("Wifi Ok");
connexionWifi(BORNE_SSID,BORNE_PASSPHRASE);
Serial.println("Wifi Connecte");
hts221.begin();
unsigned char manufacture = hts221.getID();
Serial.print("id=");
Serial.println(manufacture,HEX);
mqtt.setServer(HOSTNAME,MQTT_PORT);
}
String chainevaleurs;
void loop() {
hts221.demandeMesure();
while (!hts221.mesurePrete());
float temperature = hts221.lireTemperature();
uint16_t humidite = hts221.lireHumidite();
int statuthumidite = statutHumidite(humidite);
Serial.print("temperature ");
Serial.print(temperature,1);
Serial.print(" , humidite ");
Serial.println(humidite);
if (!mqtt.connected()) {
reconnect();
}
chainevaleurs = "{ \"idx\" : " + String(IDXTEMPERATURE) + " , \"nvalue\" : 0 , \"svalue\" : \"" + String(temperature) + "\" }";
boolean ok=mqtt.publish(TOPIC,chainevaleurs.c_str());
if (ok) {
Serial.println("publish OK");
}
else {
Serial.println("publish ERREUR");
}
chainevaleurs = "{ \"idx\" : " + String(IDXHUMIDITE) + " , \"nvalue\" : " + String(humidite) + " , \"svalue\" : \"" + String(statuthumidite) + "\"}";
mqtt.publish(TOPIC,chainevaleurs.c_str());
if (ok) {
Serial.println("publish OK");
}
else {
Serial.println("publish ERREUR");
}
delay(1000);
}
Le fichier wifi_connexion.h contient les valeurs de BORNE_SSID, BORNE_PASSPHRASE et HOSTNAME.
Ce programme est également une adaptation du programme du chapitre précédent
Comme avec la pico, il est conseillé d'utiliser un timer à la place d'une boucle infinie while True.
On utilise la librairie I2C qui a déjà été utilisée dans le chapitre embarqué sur la programmation de la pico en python avec le BH1745.
Pour la partie spécifique au HTS221, on utilise les documents constructeurs.
Contenu du fichier hts221defs.py
class HTS221_defs():
GP_SDA = 4
GP_SCL = 5
I2CADDR = 0x5f
WHO_AM_I_REG = 0x0f
AV_CONF_REG = 0x10
CTRL_REG1 = 0x20
CTRL_REG2 = 0x21
CTRL_REG3 = 0x22
STATUS_REG = 0x27
HUMIDITY_OUT_L_REG = 0x28
TEMP_OUT_L_REG = 0x2a
H0_rH_x2_REG = 0x30
H1_rH_x2_REG = 0x31
T0_degC_x8_REG = 0x32
T1_degC_x8_REG = 0x33
T1_T0_MSB_REG = 0x35
H0_T0_OUT_REG = 0x36
H1_T0_OUT_REG = 0x3a
T0_OUT_REG = 0x3c
T1_OUT_REG = 0x3e
WHO_AM_I = 0xbc
AVGH_MASK = 0x07
AVGT_MASK = 0x38
AVGT_2 = 0x00
AVGT_4 = 0x08
AVGT_8 = 0x10
AVGT_16 = 0x18
AVGT_32 = 0x20
AVGT_64 = 0x28
AVGT_128 = 0x30
AVGT_256 = 0x38
AVGH_4 = 0x00
AVGH_8 = 0x01
AVGH_16 = 0x02
AVGH_32 = 0x03
AVGH_64 = 0x04
AVGH_128 = 0x05
AVGH_256 = 0x06
AVGH_512 = 0x07
REG1_PD = 0x80
REG1_BDU = 0x04
REG1_ORD_ONE_SHOT = 0x00
REG1_ORD_1HZ = 0x01
REG1_ORD_7HZ = 0x02
REG1_ORD_12_5HZ = 0x03
REG1_ORD_MASK = 0x03
REG2_BOOT = 0x80
REG2_HEATER = 0x02
REG2_ONE_SHOT = 0x01
REG3_DRDY_H_L = 0x80
REG3_PP_OD = 0x40
REG3_DRDY_EN = 0x02
STATUS_H_DA = 0x02
STATUS_T_DA = 0x01
T0_MSB_MASK = 0x03
T1_MSB_MASK = 0x0c
T0_MSB_SHIFT = 8
T1_MSB_SHIFT = 6
Le paramètre mode
permet de définir la période d'échantillonnage dans les bits 1 et 0) du registre de contrôle 1 (HTS221_CTRL_REG1) :
HTS221_REG1_ORD_ONE_SHOT
: pas de période, mode One-shot mesure à la demande (bit 0 du registre de contrôle 2), il faut également créer cette période d'échantillonnage par programme (ex sleep).HTS221_REG1_ORD_1HZ
: 1 ,période d'échantillonnage 1Hz (pas de demande manuelle).HTS221_REG1_ORD_7HZ
: 2 ,période d'échantillonnage 7Hz (pas de demande manuelle).HTS221_REG1_ORD_12_5HZ
: 3 , période d'échantillonnage 12.5Hz (pas de demande manuelle).Pour les choix 1 à 3, avec période, il faut que le traitement des données soit inférieures à cette période, sinon il faut mieux utiliser le mode manuel.
Les fonctions de calibration sont inspirées de la documentation TN1218 fournie par le constructeur.
Contenu du fichier hts221.py
from hts221defs import HTS221_defs
from machine import I2C
class HTS221():
mode = 0
T0_degC = 0
T1_degC = 0
T0_OUT = 0
T1_OUT = 0
H0_T0_OUT = 0
H1_T0_OUT = 0
H0_rH = 0
H1_rH = 0
def __init__(self,numero=0):
self.numport = numero
self.i2c = I2C(numero,scl=HTS221_defs.GP_SCL,sda=HTS221_defs.GP_SDA,freq=100000)
def ecrireReg(self,reg,valeur):
buffer = bytearray([ reg , valeur] )
self.i2c.writeto(HTS221_defs.I2CADDR, buffer)
def lireReg(self,reg):
buffer_in = bytearray([reg])
self.i2c.writeto(HTS221_defs.I2CADDR, buffer_in)
buffer_out = self.i2c.readfrom(HTS221_defs.I2CADDR, 1)
return buffer_out[0]
def u16toint(self,u16):
if (u16 < 0x8000):
n = u16
else:
n = u16 - 0x10000
return n
def lire(self,LSBReg):
lsb = self.lireReg(LSBReg)
msb = self.lireReg(LSBReg+1)
valeur = lsb + (msb << 8)
return valeur
def device_init(self,mode):
self.mode = mode
if (mode != 0) :
self.ecrireReg(HTS221_defs.CTRL_REG1, HTS221_defs.REG1_PD | mode )
self.ecrireReg(HTS221_AV_CONF_REG, HTS221_AVGH_256 | HTS221_AVGT_256);
else:
self.ecrireReg(HTS221_defs.CTRL_REG1, HTS221_defs.REG1_PD | HTS221_defs.REG1_BDU )
self.ecrireReg(HTS221_defs.CTRL_REG2,0)
self.ecrireReg(HTS221_defs.CTRL_REG3,HTS221_defs.REG3_PP_OD )
def device_close(self):
pass
def tempCalibration(self):
self.T0_OUT = self.lire(HTS221_defs.T0_OUT_REG);
self.T1_OUT = self.lire(HTS221_defs.T1_OUT_REG);
Tx_degC_x8_lsb = self.lireReg(HTS221_defs.T0_degC_x8_REG)
Tx_degC_x8_msb_lsb = self.lireReg(HTS221_defs.T1_T0_MSB_REG)
self.T0_degC = (Tx_degC_x8_lsb | ((( Tx_degC_x8_msb_lsb & HTS221_defs.T0_MSB_MASK) << HTS221_defs.T0_MSB_SHIFT))) >> 3;
Tx_degC_x8_lsb = self.lireReg(HTS221_defs.T1_degC_x8_REG)
self.T1_degC = (Tx_degC_x8_lsb | ((( Tx_degC_x8_msb_lsb & HTS221_defs.T1_MSB_MASK) << HTS221_defs.T1_MSB_SHIFT))) >> 3;
def humCalibration(self):
H0_out = self.lire(HTS221_defs.H0_T0_OUT_REG)
H1_out = self.lire(HTS221_defs.H1_T0_OUT_REG)
self.H0_T0_OUT = self.u16toint(H0_out)
self.H1_T0_OUT = self.u16toint(H1_out)
H0_rHx2 = self.lireReg(HTS221_defs.H0_rH_x2_REG)
H1_rHx2 = self.lireReg(HTS221_defs.H1_rH_x2_REG)
self.H0_rH = H0_rHx2 >> 1
self.H1_rH = H1_rHx2 >> 1
def manutactureId(self):
return self.lireReg(HTS221_defs.WHO_AM_I_REG)
def Mesure(self):
self.ecrireReg(HTS221_defs.CTRL_REG2,HTS221_defs.REG2_ONE_SHOT)
def Pret(self):
valeur = self.lireReg(HTS221_defs.STATUS_REG)
return bool((valeur & (HTS221_defs.STATUS_H_DA | HTS221_defs.STATUS_T_DA))==(HTS221_defs.STATUS_H_DA | HTS221_defs.STATUS_T_DA))
def lireTemperature(self):
T_out = self.lire(HTS221_defs.TEMP_OUT_L_REG)
numerateur = (T_out - self.T0_OUT) * ((self.T1_degC - self.T0_degC)*10)
valeur = numerateur / (self.T1_OUT - self.T0_OUT) + self.T0_degC*10
return valeur / 10.0
def lireHumidite(self):
u_H_T_out = self.lire(HTS221_defs.HUMIDITY_OUT_L_REG)
H_T_out = self.u16toint(u_H_T_out)
numerateur = (H_T_out - self.H0_T0_OUT) * (self.H1_rH - self.H0_rH)
valeur = numerateur/(self.H1_T0_OUT - self.H0_T0_OUT) + self.H0_rH
return valeur
On utilise les librairies network et urequests, on peut se référer au document Connecting to the Internet with Raspberry Pi Pico W de la page sur la raspberry picow. Les principales fonctions de la librairie network sont :
network.WLAN(mode)
avec mode qui est le mode de fonctionnement de l'interface Wifi, network.STA_IF pour un client Wifi et network.AP_IF pour une borne Wifi. Cette fonction retourne un objet (ex objetwlan) qui sera utilisé par les autres fonctions de gestion dela connexion.objetwlan.active(etat)
: active l'interface si etat est VRAI, désactive l'interface si etat est FAUX.objetwlan.connect(ssid,passphrase)
: établit la connexion avec le réseau ssid et le mot de passe passphrase.objetwlan.status()
retourne le code de statut de la connexion.objetwlan.isconnected()
retourne VRAI si la connexion est effective.objeytwlan.disconnect()
termine la connexion Wifi.Les principales fonctions de la librairie urequests sont :
urequests.get(url)
avec url qui est l'URL de connexion au serveur, retourne un objet (ex : data ) qui contient la réponse (data.text) ainsi que le code retour de la requête (data.status_code()).Contenu du fichier
import time
import network
import urequests
from wificnx import WIFIcnx
def connexionWifi():
wlan.active(True)
wlan.connect(WIFIcnx.SSID, WIFIcnx.PASSPHRASE)
while not wlan.isconnected() and wlan.status() >= 0:
print("attente connexion ...")
time.sleep(1)
def deconnexionWifi():
wlan.disconnect()
wlan.active(False)
def httpGET():
try:
data=urequests.get(WIFIcnx.URLSERVER)
print(f"code {data.status_code}")
print(data.text)
data.close()
except:
print("Erreur connexion (statut =" + str(wlan.status()) + ")")
wlan = network.WLAN(network.STA_IF)
connexionWifi()
while True:
if wlan.isconnected():
print("envoie une requete ...")
httpGET()
else:
print("deconnecte, reconnecte...")
wlan.disconnect()
connexionWifi()
time.sleep(1)
deconnexionWifi()
La classe WIFIcnx contient les valeurs de SSID, PASSPHRASE et URLSERVEUR
Le programme effectue la requête tant que la connexion est valide. Sauf problème, on a une boucle infinie.
Contenu du fichier
from hts221 import HTS221
import time
import network
import urequests
from wificnx import WIFIcnx
def connexionWifi():
wlan.active(True)
wlan.connect(WIFIcnx.SSID, WIFIcnx.PASSPHRASE)
while not wlan.isconnected() and wlan.status() >= 0:
print("attente connexion ...")
time.sleep(1)
def deconnexionWifi():
wlan.disconnect()
wlan.active(False)
def httpGET(url):
try:
data=urequests.get(url)
print(f"code {data.status_code}")
print(data.text)
data.close()
except:
print("could not connect (status =" + str(wlan.status()) + ")")
wlan = network.WLAN(network.STA_IF)
connexionWifi()
hts221 = HTS221(0)
hts221.device_init(0)
hts221.tempCalibration()
hts221.humCalibration()
identifiant = hts221.manutactureId()
print(f"ID= {hex(identifiant)}")
while (True):
hts221.Mesure()
pret = hts221.Pret()
while (not pret):
pret = hts221.Pret()
temperature = hts221.lireTemperature();
humidite = int(hts221.lireHumidite())
print(f"temperature = {temperature:.1f} humidite = {humidite}")
if wlan.isconnected():
print("sending get request...")
url = WIFIcnx.URLSERVER + "?temperature=" + str(temperature) + "&humidite=" + str(humidite)
httpGET(url)
else:
print("attempting to reconnect...")
wlan.disconnect()
connexionWifi()
time.sleep(1)
deconnexionWifi()
La classe WIFIcnx contient les valeurs de SSID, PASSPHRASE et URLSERVEUR
On retrouve l'algorithme de lectures des données du capteur hts221 et d'envoi de ces valeurs au serveur web.
L'accès à domoticz requiert un login et mot de passe qu'il faut ajouter à la requête URL sous la forme d'une chaîne de caractères au format JSON :
{ "Authorization" : "Basic dXNlcjphY2Nlc3M=" }
On utilise la librairie json pour traiter la réponse du serveur domoticz, la valeur de retour de la fonction data=urequests.get(url) retourne un objet data qui possède une fonction data.json() qui retourne un objet json (ex : objetjson), celui-ci est un tableau associatif ou l'index est la clé. Ce qui fait que ibjetjson['status'] retourne la valeur correspond à la clé status qui est 'OK' quand tout s'est bien passé.
Contenu du fichier
from hts221 import HTS221
import time
import network
import urequests
from wificnx import WIFIcnx
import json
class TYPEhumidite():
HUMIDITE_NORMAL= 0
HUMIDITE_CONFORT = 1
HUMIDITE_SEC = 2
HUMIDITE_HUMIDE = 3
def connexionWifi():
wlan.active(True)
wlan.connect(WIFIcnx.SSID, WIFIcnx.PASSPHRASE)
while not wlan.isconnected() and wlan.status() >= 0:
print("attente connexion ...")
time.sleep(1)
def deconnexionWifi():
wlan.disconnect()
wlan.active(False)
def httpGET(url,entete):
try:
data=urequests.get(url,headers=entete)
print(f"code {data.status_code}")
jsreponse = data.json()
print(jsreponse['status'])
data.close()
except:
print("could not connect (status =" + str(wlan.status()) + ")")
def statutHumidite(humidite):
statut = 0
if (humidite < 20) :
statut = TYPEhumidite.HUMIDITE_SEC
elif (humidite < 50) :
statut = TYPEhumidite.HUMIDITE_NORMAL
elif (humidite < 70) :
statut = TYPEhumidite.HUMIDITE_CONFORT
else :
statut = TYPEhumidite.HUMIDITE_HUMIDE
return statut
wlan = network.WLAN(network.STA_IF)
connexionWifi()
entete = { "Authorization" : "Basic " + WIFIcnx.BASICAUTH64 }
hts221 = HTS221(0)
hts221.device_init(0)
hts221.tempCalibration()
hts221.humCalibration()
identifiant = hts221.manutactureId()
print(f"ID= {hex(identifiant)}")
while (True):
hts221.Mesure()
pret = hts221.Pret()
while (not pret):
pret = hts221.Pret()
temperature = hts221.lireTemperature();
humidite = hts221.lireHumidite();
statuthumidite = statutHumidite(humidite)
print(f"temperature = {temperature} humidite = {humidite}")
if wlan.isconnected():
print("sending get request...")
url = WIFIcnx.URLSERVER + "1&nvalue=0&svalue=" + str(temperature)
httpGET(url,entete)
url = WIFIcnx.URLSERVER + "2&nvalue=" + str(humidite) + "&svalue=" + str(statuthumidite)
httpGET(url,entete)
else:
print("attempting to reconnect...")
wlan.disconnect()
connexionWifi()
time.sleep(1)
deconnexionWifi()
La classe Wificnx contient les valeurs de SSID, PASSPHRASE, URLSERVEUR et BASICAUTH64
On retrouve l'algorithme de lectures des données du capteur hts221 et d'envoi de ces valeurs au serveur domoticz.
On utilise la librairie umqtt (répertoire) qui possède deux composantes (fichiers) qui sont simple et robust Ici on utilise la fonction MQTTClient de simple :
MQTTClient(clientid,hostname,port)
: constructeur avec clientid qui est une chaîne qui identifie le client auprès du serveur MQTT, hostname le nom du serveur et port le numéro de port su serveur MQTT. Ce constructeur retourne un objet client qui est utilisé par les fonctions suivantes.client.connect()
qui établit la connexion au serveur.client.publish(topic,message)
qui publie le message correspondant au topic.client.disconnect()
termine la connexion au serveur.Contenu du fichier
from hts221 import HTS221
import time
import network
from wificnx import WIFIcnx
import json
from umqtt.simple import MQTTClient
TOPIC = "domoticz/in"
class TYPEhumidite():
HUMIDITE_NORMAL= 0
HUMIDITE_CONFORT = 1
HUMIDITE_SEC = 2
HUMIDITE_HUMIDE = 3
def connexionWifi():
wlan.active(True)
wlan.connect(WIFIcnx.SSID, WIFIcnx.PASSPHRASE)
while not wlan.isconnected() and wlan.status() >= 0:
print("attente connexion ...")
time.sleep(1)
def deconnexionWifi():
wlan.disconnect()
wlan.active(False)
def connect_mqtt():
try:
client = MQTTClient(client_id=WIFIcnx.MQTT_CLIENT,server=WIFIcnx.HOSTNAME,port=WIFIcnx.MQTT_PORT)
client.connect()
print("MQTT connecté")
return client
except Exception as e:
print('Error connecting to MQTT:', e)
def statutHumidite(humidite):
statut = 0
if (humidite < 20) :
statut = TYPEhumidite.HUMIDITE_SEC
elif (humidite < 50) :
statut = TYPEhumidite.HUMIDITE_NORMAL
elif (humidite < 70) :
statut = TYPEhumidite.HUMIDITE_CONFORT
else :
statut = TYPEhumidite.HUMIDITE_HUMIDE
return statut
wlan = network.WLAN(network.STA_IF)
connexionWifi()
client=connect_mqtt()
hts221 = HTS221(0)
hts221.device_init(0)
hts221.tempCalibration()
hts221.humCalibration()
identifiant = hts221.manutactureId()
print(f"ID= {hex(identifiant)}")
while (True):
hts221.Mesure()
pret = hts221.Pret()
while (not pret):
pret = hts221.Pret()
temperature = hts221.lireTemperature()
humidite = int(hts221.lireHumidite())
statuthumidite = statutHumidite(humidite)
print(f"temperature = {temperature:.1f} humidite = {humidite}")
if wlan.isconnected():
print("publish temperature ...")
message = "{ \"idx\" : 1 , \"nvalue\" : 0 , \"svalue\" : \"" + str(temperature) + "\" }"
client.publish(TOPIC,message)
print("publish humidite ...")
message = "{ \"idx\" : 2, \"nvalue\" : " + str(humidite) + " , \"svalue\" : \"" + str(statuthumidite) + "\"}"
client.publish(TOPIC,message)
else:
print("attempting to reconnect...")
wlan.disconnect()
connexionWifi()
time.sleep(1)
client.disconnect()
deconnexionWifi()
La classe Wificnx contient les valeurs de SSID, PASSPHRASE, HOSTNAME, MQTT_PORT et MQTT_CLIENT
umqtt est un répertoire qui contient les fichiers simple.py et robust.py.