Réseaux et Télécommunications
Fermer ×

Raspberry pico connectée

Raspberry picow

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.

Programmation avec le SDK C

Accès à la LED de la carte

On utilise les fonctions de l'interface Wifi CYW43439 :

Voir les fichiers du classique projet blink

Utilisation du capteur de température et d'humidité

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é.

Voir les fichiers de la librairie hts221

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);
}
					
Voir le programme de test

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);
	}
}
					

Utilisation du Wifi

L'utilisation du Wifi utilise également la librairie interface Wifi CYW43439, ici on utilise les fonctions :

Le projet wifi nécessite des fichiers supplémentaires :

Voir les fichiers du programme de test wifi

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);
	}
}
					

Utilisation d'un serveur web

On utilise les fonctions de la librairie lwip.

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.

Voir les fichiers du programme de test de la connexion HTTP

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ée 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);
	}
}
					

Utilisation d'un serveur MQTT

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

Ensuite on effectue les requête MQTT avec l'adresse IP

Exemple d'envoi de publication d'un message auprès d'un serveur MQTT

Voir les fichiers du programme de test 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

  1. Réaliser la connexion Wifi
  2. Effectuer la requête DNS pour obtenir l'adresse IP du serveur.
  3. Allouer la mémoire à MQTT
  4. Etablir la connexion MQTT
  5. SI connecté
    • Publier le message
  6. Se déconnecter du serveur MQTT
  7. Libérer la mémoire allouée à MQTT
  8. Faire 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 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);
	}
}
					

Programmation avec l'IDE arduino

Librairie hts221 pour arduino

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.

Voir les fichiers de la librairie hts221

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 ;
}
					

Utilisation du Wifi

On utilise la librairie Wifi standard adaptée à la picow :

Voir les fichiers du programme test Wifi

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.

Utilisation d'un serveur web

On utilise la librairie ArduinoHttpClient déjà utilisée dans le chapitre précédent.

Voir les fichiers du programme client HTTP

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.

Utilisation de Domoticz

On utilise la librairie ArduinoJson déjà utilisée dans le chapitre précédent.

Voir les fichiers du programme client Domoticz

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

Utilisation de Domoticz et MQTT

On utilise la librairie PubSubClient déjà utilisée dans le chapitre précédent.

Voir les fichiers du programme client Domoticz avec MQTT

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

Programmation en Python

Comme avec la pico, il est conseillé d'utiliser un timer à la place d'une boucle infinie while True.

Librairie hts221

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.

Voir les fichiers de la librairie hts221

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
					

Utilisation du Wifi et HTTP

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 :

Les principales fonctions de la librairie urequests sont :

Voir les fichiers du programme test Wifi et HTTP

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.

Utilisation du capteur hts221 et du serveur web

Voir les fichiers du programme d'envoi des données du capteur au serveur

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.

Utilisation de Domoticz

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=" }
La partie codée correspond au codage en base 64 de login:password.

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é.

Voir les fichiers du programme client Domoticz

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.

Utilisation de Domoticz et MQTT

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 :

Voir les fichiers du programme client Domoticz avec MQTT

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.