samedi 27 août 2022

Raspberry Pi Pico W: lire le contenu d'une page web (Micropython)

Dans cet article, nous utilisons un Raspberry Pi Pico W (programmé en Micropython) pour lire le contenu d'une page web. 


La page web que j'ai choisie à titre d'exemple sera... le blog "Électronique en Amateur" ! Nous allons faire en sorte que le Raspberry Pi Pico visite le blog et y trouve le titre de la publication la plus récente. Vous pourrez ensuite modifier ce script pour que votre Raspberry Pi Pico W affiche les conditions météorologique, les cours de la bourse, les résultats de votre équipe sportive préférée, etc.

La bibliothèque "urequests" de Micropython est spécialement conçue pour naviguer sur le web. On accède à la page web de cette façon:

page_web = urequests.get("http://electroniqueamateur.blogspot.com")

Tout le contenu html de la page est alors placé dans la chaîne de caractères (string) "page_web.text". 

Il reste ensuite ensuite à extraire de cette longue chaîne de caractères les informations qui nous intéressent. 

En affichant le code source de la page web, j'ai remarqué que le titre d'un article du blog est toujours précédé de ce tag html:

<h3 class='post-title entry-title' itemprop='name'>

Donc, en utilisant la méthode find pour localiser cette sous-chaîne à l'intérieure de la chaîne complète...

    position = page_web.text.find("<h3 class='post-title entry-title' itemprop='name'>")
    extrait = page_web.text[position:position + 300]

... j'obtiens une chaîne de 300 caractères qui contient, entre autres choses, le titre de l'article le plus récent du blog (qui s'intitule, au moment où j'écris ces lignes, "Dans un blog près de chez vous (24)"):


Mon extrait contient toutefois une grande quantité de caractères qui ne sont pas pertinents. On peut facilement omettre quelques dizaines de caractères au début de la chaîne. Ici, par exemple, ma chaîne débute 80 caractères plus loin:

    position = page_web.text.find("<h3 class='post-title entry-title' itemprop='name'>")
    extrait = page_web.text[position + 80:position + 300]

Le contenu de la variable "extrait" est maintenant:


L'url qui précède le titre de l'article n'aura pas toujours la même longueur, mais on peut détecter où il se termine en recherchant le caractère ">":

    position = extrait.find(">")
    extrait = extrait[position + 1:]

Le contenu de la variable "extrait" est maintenant:


...et il ne reste plus qu'à rechercher le caractère "<" pour retirer le texte superflu qui suit le titre de l'article:

    position = extrait.find("<")
    extrait = extrait[:position]

Maintenant, la variable "extrait" ne comporte plus que le titre désiré:



Voici le script complet:

-
'''
Lecture d'une page web par le Raspberry Pi Pico W
(Le Raspberry Pi Pico W récupère le
titre du plus récent article publié
sur le blog "Électronique en Amateur")
Pour plus d'informations:
https://electroniqueamateur.blogspot.com/2022/08/raspberry-pi-pico-w-lire-le-contenu.html
'''
import network
import urequests
import time
# à remplacer par le nom du réseau wifi et le mot de passe
ssid = 'nom_du_reseau_wifi'
password = 'mot_de_passe'
# connexion au réseau wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
max_wait = 10 # si pas connecté en 10 secondes, on abandonne
print('Attente de la connection', end='')
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('.', end='')
time.sleep(1)
print ('')
if wlan.status() != 3:
raise RuntimeError('Echec de la connexion a ' + ssid)
else:
print('Connexion reussie')
status = wlan.ifconfig()
print('')
# on récupère la page web
print("Le plus recent article publie sur le blog est:")
page_web = urequests.get("http://electroniqueamateur.blogspot.com")
# extraction de l'information recherchée
position = page_web.text.find("<h3 class='post-title entry-title' itemprop='name'>")
extrait = page_web.text[position + 80:position + 300]
position = extrait.find(">")
extrait = extrait[position + 1:]
position = extrait.find("<")
extrait = extrait[:position]
print(extrait)
page_web.close()
-

... et le résultat affiché dans la console:



À lire également:

jeudi 11 août 2022

Utilisation d'un écran OLED SPI SSD1306 avec Arduino

Dans cet article, nous explorons l'utilisation d'un écran OLED SPI SSD1306 avec un Arduino.

OLED signfie "organic light-emitting diode": chaque pixel de l'écran peut s'allumer comme s'il s'agissait d'une LED (par opposition à d'autres technologies d'affichage qui nécessitent un rétroéclairage). Ces petits écrans monochromes ne coûtent pas cher et offrent un excellent contraste peu importe l'éclairage ambiant. 


J'ai fait mes tests avec un afficheur dont la résolution est 128 par 64 pixels (c'est la résolution la plus fréquente). 

Jusqu'à tout récemment, j'avais exclusivement utilisé des écrans OLED qui utilisaient une interface I2C: 4 connecteurs étaient suffisants pour les faire fonctionner. Les modèles SPI, qui sont en principe plus rapides, comportent 7 connecteurs.

Les broches de mon afficheur sont identifiées de cette façon:

  • GND (la masse)
  • VCC (alimentation qui, sur ce modèle, peut être de 5 V ou de 3,3 V)
  • D0 : qui est en fait le signal d'horloge SPI (SCLK)
  • D1: qui transmet les infos du microcontrôleur vers l'écran (MOSI)
  • RES: reset
  • DC: permet permuter entre les modes "data" ou "command"
  • CS: "chip select", essentiel si plusieurs appareils sont branchés sur le même bus SPI.


Par défaut, l'afficheur fonctionne en "SPI à 4 fils". Mais il est possible de le transformer en afficheur "SPI à 3 fils" ou I2C en modifiant la configuration des résistances au verso de l'écran.

Connexions de l'écran OLED à un Arduino Uno

J'ai fait mes premiers tests avec un Arduino Uno, et j'ai branché l'écran OLED de cette façon:

  • GND de l'écran OLED : GND de l'Arduino Uno
  • VCC de l'écran OLED: sortie 5 V de l'Arduino Uno
  • D0 : broche 13 de l'Arduino Uno
  • D1: broche 11 de l'Arduino Uno
  • RES: broche 8 de l'Arduino Uno
  • DC: broche 9 de l'Arduino Uno
  • CS: broche 10 de l'Arduino Uno

Les broches RES, DC et CS peuvent être branchées à n'importe quel broche de l'Arduino: nous les définissons dans le sketch. Les broches D0 et D1, toutefois, doivent être connectées aux broches dédiées à la communications SPI.

Autres cartes

J'ai aussi fait des tests sur d'autres cartes (ESP8266, ESP32, STM32), également programmées au moyen de l'IDE Arduino. J'indique ici les numéros des broches utilisées pour D0 et D1 (SCLK et MOSI):

ESP8266:   D0: GPIO14  et D1: GPIO13
ESP32:  D0: 18  et D1: 23
STM32 "Blue Pill":  D0: A5 et D1: A7

Vous branchez RES, DC et CS où vous voulez, en n'oubliant pas de modifier le sketch en conséquence.


Bibliothèque u8g2 ou Adafruit?

Les deux bibliothèques les plus utilisées pour piloter un écran OLED avec une carte Arduino sont u8g2 et la bibliothèque d'Adafruit. Chacune des deux constitue un excellent choix.

J'ai testé les deux bibliothèques avec un Arduino Uno, un ESP32, un ESP8266 et un STM32 "Blue Pill". Tout a fonctionné correctement avec la bibliothèque u8g2, mais je ne suis pas parvenu à faire fonctionner la Blue Pill avec la bibliothèque d'Adafruit (l'écran demeurait noir).

Il me semble que la bibliothèque d'Adafruit est un peu plus conviviale, et que u8g2 présente un plus grand choix de polices de caractères (incluant des polices accentuées, ce qui est bien pratique en français).

Évidemment, chacune des deux bibliothèques est accompagnée d'un grand nombre de fichiers d'exemples qui permettent de visualiser rapidement plusieurs résultats possibles.

Vous trouverez ci-dessous deux sketches qui sont assez similaire: ils affichent un peu de texte et quelques formes géométriques simples, ainsi qu'une illustration bitmap. Le premier sketch est conçu pour être utilisé avec la bibliothèque u8g2, alors que l'autre nécessite la bibliothèque d'Adafruit.

Sketch utilisant la bibliothèque u8g2

Ce premier sketch utilise la bibliothèque u8g2, que vous pouvez installer à partir du gestionnaire de bibliothèques.

-

/*******************************************************
Utilisation d'un écran OLED SSD1306 SPI 128*64
(bibliothèque u8g2)
Plus d'infos:
https://electroniqueamateur.blogspot.com/2022/08/utilisation-dun-ecran-oled-spi-ssd1306.html
********************************************************/
#include <U8g2lib.h>
#include <SPI.h>
// connexion hardware spi:
U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
// U8G2_R0 ou U8G2_R2: mode paysage, U8G2_R1 ou U8G2_R3: mode portrait
/* Définition d'une image bitmap: logo du blog Électronique en Amateur
sauvegardé dans un fichier XBM, puis ouvert avec un éditeur de texte: */
static const unsigned char logoBitmap[] PROGMEM = {
0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0x06, 0xf0, 0xff, 0x03, 0x3f, 0x04,
0x00, 0xf8, 0x03, 0x1f, 0x06, 0x00, 0x80, 0x03, 0x0f, 0x84, 0x0f, 0x85,
0x03, 0x07, 0x86, 0x9f, 0x8f, 0x03, 0x07, 0xc4, 0xdf, 0x8f, 0x03, 0x03,
0xc4, 0xff, 0x9f, 0x03, 0x03, 0xc6, 0xff, 0x9f, 0x03, 0x03, 0xc4, 0xf9,
0x9f, 0x03, 0x03, 0xc4, 0x70, 0x8e, 0x03, 0x03, 0xc4, 0x30, 0x8c, 0x03,
0x03, 0x84, 0x39, 0x84, 0x03, 0x03, 0x04, 0x6f, 0x86, 0x03, 0x03, 0x04,
0xc4, 0x81, 0x03, 0x03, 0x84, 0x01, 0x80, 0x03, 0x03, 0x84, 0x01, 0x98,
0x03, 0x03, 0x04, 0x03, 0x98, 0x03, 0x03, 0x04, 0x0e, 0x8e, 0x03, 0x03,
0x0c, 0xf8, 0x83, 0x03, 0x03, 0xff, 0x17, 0x80, 0x03, 0x83, 0x00, 0xfc,
0x97, 0x03, 0x63, 0x00, 0x00, 0xfc, 0x03, 0x23, 0x00, 0x00, 0x80, 0x03,
0x13, 0x00, 0x00, 0x80, 0x03, 0x0b, 0x00, 0x00, 0x80, 0x03, 0x8f, 0x07,
0x00, 0x80, 0x03, 0x07, 0x06, 0x06, 0x80, 0x03, 0x03, 0x07, 0x07, 0x86,
0x03, 0x03, 0x06, 0x86, 0x86, 0x03, 0x03, 0x06, 0x06, 0x86, 0x03, 0x03,
0x06, 0x06, 0xc6, 0x03, 0x07, 0x07, 0x06, 0xe6, 0x03, 0x0f, 0x06, 0x06,
0xf6, 0x03, 0x1f, 0x06, 0x06, 0xf6, 0x03, 0xff, 0x06, 0x06, 0xf6, 0x03,
0xff, 0x06, 0x76, 0xf6, 0x03, 0xff, 0x76, 0x76, 0xf6, 0x03, 0xff, 0x76,
0x76, 0xf6, 0x03, 0xff, 0x76, 0x76, 0xf6, 0x03, 0xff, 0x76, 0x76, 0xf6,
0x03, 0xff, 0x76, 0x76, 0xf6, 0x03, 0xff, 0x76, 0x76, 0xf6, 0x03, 0xff,
0x76, 0x76, 0xf6, 0x03, 0xff, 0x76, 0x76, 0xf6, 0x03, 0xff, 0x70, 0x74,
0xf6, 0x03, 0xff, 0x7f, 0x7c, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xf8, 0x03
};
void setup(void) {
u8g2.begin();
u8g2.enableUTF8Print(); //nécessaire pour écrire des caractères accentués
}
void loop(void) {
u8g2.firstPage();
do {
u8g2.drawXBMP( 2, 8, 34, 48, logoBitmap); // position, largeur, hauteur
/************** On écrit "Électronique en amateur" *********************/
u8g2.setFont(u8g2_font_7x13B_tf); // choix d'une police de caractère
u8g2.setCursor(40, 25); // position du début du texte
u8g2.print("Électronique"); // écriture de texte
u8g2.setCursor(70, 35);
u8g2.print("en");
u8g2.setCursor(55, 45);
u8g2.print("amateur");
} while ( u8g2.nextPage() );
delay(2000);
/******************** Dessiner une ligne ******************************************/
u8g2.firstPage();
do {
// ligne horizontale au centre de l'écran
u8g2.drawLine(0, 32, 128 , 32); // point de départ, point d'arrivée
// ligne verticale au centre de l'écran
u8g2.drawLine(64, 0, 64, 64);
/********************* Dessiner des contours de formes géométriques ******************/
u8g2.drawFrame(15, 5, 30, 15); // coin supérieur gauche, coin inférieur droit
u8g2.drawCircle(95, 15, 8); // centre et rayon
u8g2.drawRFrame(15, 40, 30, 16, 5); // coin supérieur gauche, coin inférieur droit, rayon du coin
//il n'y a pas de fonction pour tracer un triangle vide
u8g2.drawLine(95, 40, 80, 55);
u8g2.drawLine(80, 55, 110, 55);
u8g2.drawLine(110, 55, 95, 40);
} while ( u8g2.nextPage() );
delay(2000);
/******************* Dessiner des formes géométriqeus pleines *************************/
u8g2.firstPage();
do {
// ligne horizontale au centre de l'écran
u8g2.drawLine(0, 32, 128 , 32); // point de départ, point d'arrivée
// ligne verticale au centre de l'écran
u8g2.drawLine(64, 0, 64, 64);
u8g2.drawBox(15, 5, 30, 15); // coin supérieur gauche, coin inférieur droit
u8g2.drawDisc(95, 15, 8); // centre et rayon
u8g2.drawRBox(15, 40, 30, 16, 5); // coin supérieur gauche, coin inférieur droit, rayon du coin
u8g2.drawTriangle(95, 40, 80, 55, 110, 55); // les 3 coins
} while ( u8g2.nextPage() );
delay(2000);
/***************** dessiner pixel par pixel ***********************/
u8g2.firstPage();
do {
// on trace une fonction sinusoidale, point par point
for (int x = 1; x < 128; x++) {
int y = 32 + round(16.0 * sin(x / 5.0));
u8g2.drawPixel(x, y);
}
} while ( u8g2.nextPage() );
delay(2000);
/******************* Écrire du texte *******************************/
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB10_tf); // choix de la police
u8g2.setCursor(5, 20); // position du début du texte
u8g2.print("Caractères"); // on écrit le texte
u8g2.setCursor(5, 35);
u8g2.print("accentués:");
u8g2.setCursor(5, 55);
u8g2.print("ÀàÂâÉéÈèÊêÇç");
} while ( u8g2.nextPage() );
delay(2000);
}

-

Sketch utilisant la bibliothèque Adafruit

La bibliothèque Adafruit SSD1306 nécessite également la bibliothèque Adafruit GFX. Les deux bibliothèques peuvent être installées dans le gestionnaire de bibliothèques.


-

/*********************************************************************
Utilisation d'un écran OLED SSD1306 SPI 128*64
(avec les biliothèques SSD1306 et GFX d'Adafruit)
Pour plus d'infos:
https://electroniqueamateur.blogspot.com/2022/08/utilisation-dun-ecran-oled-spi-ssd1306.html
*********************************************************************/
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_DC 9
#define OLED_CS 10
#define OLED_RESET 8
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
&SPI, OLED_DC, OLED_RESET, OLED_CS);
/* Définition d'une image bitmap: logo du blog Électronique en Amateur
Réalisée avec l'outil en ligne http://javl.github.io/image2cpp/ */
const unsigned char myBitmap [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xf0, 0x00, 0x00, 0x03, 0x9f, 0xff, 0xe0, 0x00, 0x07,
0x9f, 0xff, 0xfe, 0x00, 0x0f, 0xdf, 0x0f, 0xbe, 0x00, 0x1f, 0xde, 0x06, 0x0e, 0x00, 0x3f, 0xdc,
0x04, 0x0e, 0x00, 0x3f, 0xdc, 0x00, 0x06, 0x00, 0x3f, 0xdc, 0x00, 0x06, 0x00, 0x3f, 0xdc, 0x60,
0x06, 0x00, 0x3f, 0xdc, 0xf1, 0x8e, 0x00, 0x3f, 0xdc, 0xf3, 0xce, 0x00, 0x3f, 0xde, 0x63, 0xde,
0x00, 0x3f, 0xdf, 0x09, 0x9e, 0x00, 0x3f, 0xdf, 0xfc, 0x7e, 0x00, 0x3f, 0xde, 0x7f, 0xfe, 0x00,
0x3f, 0xde, 0x7f, 0xf6, 0x00, 0x3f, 0xdf, 0x3f, 0xe6, 0x00, 0x3f, 0xdf, 0x8f, 0x8e, 0x00, 0x3f,
0xdf, 0xe0, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0xfe, 0x00, 0x3e, 0xff, 0xc0, 0x1e, 0x00, 0x3d, 0xff,
0xff, 0xc0, 0x00, 0x3b, 0xff, 0xff, 0xfe, 0x00, 0x37, 0xff, 0xff, 0xfe, 0x00, 0x2f, 0xff, 0xff,
0xfe, 0x00, 0x0e, 0x1f, 0xff, 0xfe, 0x00, 0x1f, 0x9f, 0x9f, 0xfe, 0x00, 0x3f, 0x9f, 0x9f, 0x9e,
0x00, 0x3f, 0x9f, 0x9f, 0x9e, 0x00, 0x3f, 0x9f, 0x9f, 0x9e, 0x00, 0x3f, 0x9f, 0x9f, 0x9c, 0x00,
0x1f, 0x9f, 0x9f, 0x98, 0x00, 0x0f, 0x9f, 0x9f, 0x90, 0x00, 0x07, 0x9f, 0x9f, 0x90, 0x00, 0x00,
0x9f, 0x9f, 0x90, 0x00, 0x00, 0x9f, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91,
0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91,
0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90, 0x00, 0x00, 0x91, 0x91, 0x90,
0x00, 0x00, 0xf1, 0xd1, 0x90, 0x00, 0x00, 0x01, 0xc1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00
};
void setup() {
display.begin(); // initialisation de l'afficheur
display.clearDisplay(); // ça efface à la fois le buffer et l'écran
}
void loop() {
/**************** Éxcriture de texte ***************/
// taille par défaut
display.setCursor(30, 15); // coordonnées du point de départ du texte
display.setTextColor(WHITE);
display.setTextSize(1); // taille par défaut
display.println("Petit...");
display.display(); // affichage à l'écran
delay(1000);
// deux fois plus gros
display.setCursor(30, 35); // coordonnées du point de départ du texte
display.setTextSize(2); // taille double
display.println("GROS!");
display.display(); // affichage à l'écran
delay(1000);
display.clearDisplay(); // on efface tout
/******************** Dessiner une ligne ******************************************/
// ligne horizontale au centre de l'écran
display.drawLine(0, display.height() / 2, display.width() , display.height() / 2, WHITE);
// ligne verticale au centre de l'écran
display.drawLine(display.width() / 2, 0, display.width() / 2, display.height(), WHITE);
/********************* Dessiner des contours de formes géométriques ******************/
display.drawRect( 15, 5, 30, 15, WHITE); // contour d'un rectangle
display.drawCircle(95, 15, 8, WHITE); // contour d'un cercle
display.drawRoundRect(15, 40, 30, 16, 5, WHITE); // contour d'un rectangle à coins arrondis
display.drawTriangle(95, 40, 80, 55, 110, 55, WHITE); // contour d'un triangle
display.display();
delay(1000);
/******************* Dessiner des formes géométriqeus pleines *************************/
display.fillRect( 15, 5, 30, 15, WHITE); // contour d'un rectangle
display.fillCircle(95, 15, 8, WHITE); // contour d'un cercle
display.fillRoundRect(15, 40, 30, 16, 5, WHITE); // contour d'un rectangle à coins arrondis
display.fillTriangle(95, 40, 80, 55, 110, 55, WHITE); // contour d'un triangle
display.display();
delay(1000);
display.clearDisplay();
/********************** Dessiner un pixel à la fois ***********************************************/
// on trace une fonction sinusoidale, point par point
for (int x = 1; x < 128; x++) {
int y = 32 + round(16.0 * sin(x / 5.0));
display.drawPixel(x, y, WHITE);
}
display.display();
delay(1000);
display.clearDisplay();
/**************************** Dessiner une image bitmap **********************************/
// on affiche l'image stockée dans la constante myBitmap définie au début de ce fichier.
display.fillRect(5,5,44,58,WHITE); // fond blanc derrière l'image bitmap
display.drawBitmap(10, 10, myBitmap, 34, 48, BLACK);
display.setCursor(60, 30); // coordonnées du point de départ du texte
display.setTextColor(WHITE);
display.setTextSize(1); // taille par défaut
display.println("Au revoir!");
display.display();
delay(2000);
display.clearDisplay();
}

-

Affichage d'un dessin bitmap

Les deux scripts ci-dessus affichent un dessin bitmap dont les données se trouvent au début du sketch. Pour transformer un dessin bitmap en données exploitables par un Arduino, plusieurs options sont possible: on peut utiliser un logiciel comme Gimp pour sauvegarder l'illustration sous la forme d'un fichier XBM qu'on  ouvre ensuite avec un éditeur de texte, ou on peut utiliser un outil en ligne comme celui-ci.


À lire également:

J'ai publié quelques articles concernant l'utilisation d'un écran OLED I2C SH1106: avec un Arduino Uno,  avec une carte ESP8266 ou ESP32, ou avec une carte STM32.

D'autres articles concernent l'utilisation d'autres afficheur avec un Arduino: Écran couleur ST7735,  afficheur Nokia 5110, afficheur LCD 2 X 16, etc.


Yves Pelletier (Facebook)

samedi 6 août 2022

Lire des fichiers mp3 avec Raspberry Pi Pico et VS1053 (micropython)

Dans un récent article sur l'utilisation d'un module I2S, je déplorais que le décodage du format mp3 ne soit pas supporté par le langage micropython pour l'instant. Une solution consiste à déléguer le décodage à un circuit intégré spécialement conçu pour cette tâche, comme le VS1053, qui peut décoder les principaux formats de fichiers musicaux: Ogg Vorbis, MP3, AAC, FLAC, WMA, MIDI...

Dans cet article, j'utilise un Raspberry Pi Pico (programmé en micropython) et un module VS1053 pour faire jouer les fichiers mp3 enregistrés sur une carte SD.


Module VS1053

Mon module VS1053 a été fabriqué par LCSOFT STUDIO et porte la mention "VS1003/1053 MP3 CODEC". Il existe d'autres modules VS1053 différents de celui-ci, mais j'imagine qu'ils sont équivalents. Je crois qu'il existe des modules VS1053 qui comportent un lecteur de carte SD intégré. Dans mon cas, j'ai dû utiliser un lecteur de carte SD en plus du module VS1053.


Connexions

Le module VS1053 et le module de carte SD partagent le même bus SPI.

Connexions du module VS1053:

  • Broche 5 V du module VS1053 : Broche VBUS du Raspberry Pi Pico 
  • Broche DGND du module VS1053: Broche GND du Raspberry Pi Pico
  • Broche MISO du module VS1053: Broche GP4 du Raspberry Pi Pico
  • Broche MOSI du module VS1053: Broche GP7 du Raspberry Pi Pico
  • Broche SCK du module VS1053: Broche GP6 du Raspberry Pi Pico
  • Broche DREQ du module VS1053: Broche GP10 du Raspberry Pi Pico
  • Broche XRST du module VS1053: Broche GP13 du Raspberry Pi Pico
  • Broche XCS du module VS1053: Broche GP12 du Raspberry Pi Pico
  • Broche XDCS du moudle VS1053: Broche GP11 du Raspberry Pi Pico

Connexions du module lecteur de carte SD:

  • GND du lecteur SD: Broche GND du Raspberry Pi Pico
  • +3.3 du lecteur SD: Sortie 3,3 V du Raspberry Pi Pico
  • Broche SCK du lecteur SD: broche GP6 du Raspberry Pi Pico
  • Broche MOSI du lecteur SD: broche GP7 du Raspberry Pi Pico
  • Broche MISO du lecteur SD: broche GP4 du Raspberry Pi Pico
  • Broche CS du lecteur SD: broche GP15 du Raspberry Pi Pico

Bibliothèques

Deux pilotes micropython sont nécessaires pour ce projet: micropython-vs1053 par Peter Hinch, et le pilote officiel pour la carte SD.

Les fichiers sdcard.py et  vs1053_syn.py devront être copiés dans la mémoire flash du Raspberry Pi Pico pour que le script ci-dessous fonctionne correctement.

Script

Voici un script qui exécute tous les fichiers mp3 qui se trouvent sur la carte SD. Comme vous pouvez le constater, il s'agit d'un script très court: les deux bibliothèques font tout le travail pour nous!

-

'''
Raspberry Pi Pico et VS1053. Tous les fichiers mp3
sur la carte SD sont joués l'un après l'autre.
Pour plus d'infos, consultez le blog:
https://electroniqueamateur.blogspot.com/2022/08/lire-des-fichiers-mp3-avec-raspberry-pi.html
'''
from vs1053_syn import * # https://github.com/peterhinch/micropython-vs1053
from machine import SPI, Pin
import time
import os
# initialisation de SPI
spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4))
# broche CS de la carte SD:
sdcs = Pin(15, Pin.OUT, value=1)
# broches du module VS1053
reset = Pin(13, Pin.OUT, value=1)
xcs = Pin(12, Pin.OUT, value=1)
xdcs = Pin(11, Pin.OUT, value=1)
dreq = Pin(10, Pin.IN)
player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs=sdcs, mp='/fc')
player.volume(-25, -25) # réglage du volume sonore
songs = sorted([x for x in os.listdir('/fc') if x.endswith('.mp3')])
for song in songs:
print("En train de jouer: ", song) # affichage du titre
fn = ''.join(('/fc/', song))
with open(fn, 'rb') as f:
player.play(f)

-

À lire également:

Yves Pelletier (Facebook)


jeudi 4 août 2022

Jouer de la musique (fichiers WAV) avec un Raspberry Pi Pico et un décodeur I2S (micropython)

Dans cet article, nous utilisons un Rasbperry Pi Pico programmé en micropython et un décodeur I2S UDA1334A pour jouer la musique stockée sous forme de fichiers .wav sur une carte SD.


Module I2S

I2S est le nom d'un protocole de communication série permettant de connecter entre eux des appareils audio numériques. Un décodeur I2S est un convertisseur numérique-analogique: il produit un signal analogique audio à partir des informations numériques que lui envoie le Raspberry Pi Pico

J'ai utilisé le décodeur I2S stéréo UDA1334A qui était fabriqué par Adafruit, mais qui ne l'est plus. Adafruit l'a remplacé par un nouveau modèle basé sur le MAX98357A, et il existe d'autres modules du même genre (basés sur le PCM5102). En principe, vous devriez obtenir des bons résultats avec tous ces modules, mais moi je n'ai testé que l'UDA1334A d'Adafruit.


Bibliothèques wavplayer.py et sdcard.py

Mike Teachman a placé sur github de nombreux exemples d'utilisation du I2S en micropython.  Dans le répertoire "examples", vous trouverez un script qui génère un simple signal sinusoïdal (particulièrement utile pour vérifier les connexions du module I2S et s'assurer qu'il fonctionne correctement), d'autres qui jouent un fichier .wav enregistré en mémoire flash ou sur une carte SD, etc.

Pour exécuter un fichier audio en format .wav, nous devons copier le fichier wavplayer.py de Mike Teachman dans la mémoire flash du Raspberry Pi Pico. Puisque les fichiers .wav sont souvent volumineux, nous aurons également besoin du pilote sdcard.py.


Connexions

J'ai utilisé les connexions proposées par Mike Teachman.

Connexions du module décodeur I2S:

  • Broche VIN du module I2S : Sortie 3,3 V du Raspberry Pi Pico
  • Broche GND du module I2S: Broche GND du Raspberry Pi Pico
  • Broche BCLK du module I2S: Broche GP16 du Raspberry Pi Pico
  • Broche WSEL du module I2S: Broche GP17 du Raspberry Pi Pico
  • Broche DIN du module I2S: Broche GP18 du Raspberry Pi Pico

(les autres broches du décodeur I2S peuvent être laissées déconnectées)

Connexions du module lecteur de carte SD:

  • GND du lecteur SD: Broche GND du Raspberry Pi Pico
  • +3.3 du lecteur SD: Sortie 3,3 V du Raspberry Pi Pico
  • Broche SCK du lecteur SD: broche GP14 du Raspberry Pi Pico
  • Broche MOSI du lecteur SD: broche GP15 du Raspberry Pi Pico
  • Broche MISO du lecteur SD: broche GP12 du Raspberry Pi Pico
  • Broche CS du lecteur SD: broche GP13 du Raspberry Pi Pico


Script

Voici un script qui joue l'un après l'autre tous les fichiers .wav enregistrés à la racine de la carte SD (avant de l'utiliser, il faut évidemment avoir préalablement enregistré quelques fichiers en format .wav sur la carte). Vous pouvez écouter le résultat en branchant un casque d'écoute à la sortie jack du module I2S.

-

'''
Raspberry Pi Pico, décodeur I2S UDA1334A et lecteur
de cartes SD.
Les fichiers .wav présent sur la carte SD sont lus
tour à tour.
Pour plus d'infos, consultez le blog:
https://electroniqueamateur.blogspot.com/2022/08/jouer-de-la-musique-fichiers-wav-avec.html
'''
import os
import time
from machine import Pin, SPI
from wavplayer import WavPlayer # https://github.com/miketeachman/micropython-i2s-examples
from sdcard import SDCard # https://github.com/micropython/micropython/blob/master/drivers/sdcard/sdcard.py
# lecteur de carte SD
cs = Pin(13, Pin.OUT)
spi = SPI(1, baudrate=1_000_000, polarity=0,
phase=0, bits=8, firstbit= SPI.MSB,
sck=Pin(14), mosi=Pin(15), miso=Pin(12),)
sd = SDCard(spi, cs)
sd.init_spi(25_000_000)
os.mount(sd, "/sd")
# module décodeur I2S
wp = WavPlayer(id=0, sck_pin=Pin(16),ws_pin=Pin(17),
sd_pin=Pin(18),ibuf=40000,)
# on joue chaque fichier .wav présent sur la carte SD
for filename in (os.listdir("/sd")):
print("En train de jouer: ", filename)
wp.play(filename, loop=False)
while wp.isplaying() == True:
pass
os.umount("/sd")
view raw pico_wav.py hosted with ❤ by GitHub

-

Mais pas de mp3?

Tout ceci fonctionne très bien, mais je préférerais pouvoir jouer de la musique en format mp3: un fichier wav, ça prend beaucoup de place! Malheureusement, ça ne semble pas possible pour l'instant en Micropython.  CircuitPython, en revanche, supporte le décodage du format mp3; c'est assez tentant d'aller faire quelques essais de ce côté...


À lire également:

Yves Pelletier (Facebook)

 

dimanche 31 juillet 2022

Clavier numérique et Raspberry Pi Pico (Micropython)

Dans cet article, nous branchons à un Raspberry Pi Pico un petit clavier numérique à 16 touches, et nous utilisons micropython pour interpréter correctement les touches qui ont été enfoncées.


Il s'agit d'un clavier matriciel: les 16 interrupteurs sont reliés entre eux par groupes de quatre, de façon à former des lignes et des colonnes. 


Au moyen d'un multimètre, on peut facilement constater que les 4 connecteurs de gauche sont reliés aux lignes horizontales, alors que les 4 connecteurs de droite sont reliés aux colonnes verticales. Par exemple, si j'appuie sur la touche 1, la résistance électrique devient nulle entre le connecteur "ligne 1" et le connecteur "colonne 1":

L'avantage de cette disposition matricielle, c'est l'économie de connecteurs: on peut brancher les 16 touches du clavier avec 8 fils conducteurs alors qu'il faudrait au moins 17 fils pour brancher chaque touche à sa propre entrée du microcontrôleur. En contrepartie, la programmation est un petit peu plus compliquée car pour détecter et identifier une touche enfoncée, il faut modifier successivement le niveau logique de chaque ligne et vérifier, dans chaque cas, le niveau logique de toutes les colonnes.

Connexions du clavier au Raspberry Pi Pico

Le script montré à la prochaine section suppose que les 8 connecteurs  du clavier numérique sont branchés, dans l'ordre, aux broches GP1 à GP8 du Raspberry Pi Pico:

  • Broche 1 du clavier (à l'extrême gauche): Broche GP1 du Raspberry Pi Pico
  • Broche 2 du clavier: Broche GP2 du Raspberry Pi Pico
  • Broche 3 du clavier: Broche GP3 du Raspberry Pi Pico
  • ...
  • Broche 8 du clavier (à l'extrême droite): Broche GP8 du Raspberry Pi Pico

Il est parfaitement possible d'utiliser une connexion différente, mais il faudra alors modifier le script.

Script "mot de passe"

Le script en micropython présenté ci-dessous demande un mot de passe, et il exécute ensuite le programme principal si on a entré le bon mot de passe (dans ce cas, le "programme principal" se limite à faire clignoter la LED du Pico).

Pour que le script soit facile à utiliser peu importe votre matériel, j'ai choisi un affichage dans la console. Vous pourrez facilement adapter le script pour l'afficheur (OLED, LCD, etc.) de votre choix (ou, bien entendu, pour toute application où le clavier servirait à écrire autre chose qu'un mot de passe).


Pour simplifier le débogage, j'écris le mot de passe à l'écran. Vous pouvez facilement le remplacer par des astérisques en modifiant la ligne 71 de cette façon:  

          print('*', end='')

-

'''
Clavier 4 X 4 branché à un Raspberry Pi Pico
Il faut entrer le bon mot de passe
Pour plus de détails:
https://electroniqueamateur.blogspot.com/2022/07/clavier-numerique-et-raspberry-pi-pico.html
'''
from machine import Pin
import utime
# les lignes du clavier sont branchées aux broches
# GP1, GP2, GP3, GP4 du pico
broches_lignes = [1,2,3,4]
# les colonnes du clavier sont branchées aux broches
# GP5, GP6, GP7, GP8 du pico
broches_colonnes = [5,6,7,8]
# matrice définissant chaque touche du clavier
clavier = [["1","2","3","A"],["4","5","6","B"],\
["7","8","9","C"],["*","0","#","D"]]
# les lignes sont réglées en sorties, niveau logique haut
for x in range(0,4):
broches_lignes[x]=Pin(broches_lignes[x], Pin.OUT)
broches_lignes[x].value(1)
# les colonnes réglées en entrée
for x in range(0,4):
broches_colonnes[x] = Pin(broches_colonnes[x], Pin.IN, Pin.PULL_UP)
# routine qui identifie la touche enfoncée:
def lecture_touche (cols,lignes):
for i in lignes: # une ligne à la fois
i.value(0) # on met la ligne au niveau logique bas
# on mesure chaque colonne de cette ligne
resultat=[cols[0].value(),cols[1].value(),cols[2].value(),cols[3].value()]
if min(resultat)==0:
reponse = clavier[int(lignes.index(i))][int(resultat.index(0))]
i.value(1) # au cas où une touche est maintenue enfoncée
return(reponse)
i.value(1) # retour au niveau logique haut
# variables relatives à la demande de mot de passe:
correct = '662607004' # le bon mot de passe
tentative = '' # le mot de passe écrit par l'utilisateur
autorisation = False # le mot de passe n'a pas encore été accepté
led = Pin(25, Pin.OUT) # pour faire clignoter la LED embarquée
print("Bonjour, veuillez entrer le mot de passe")
print("(# pour terminer)")
while not autorisation:
touche = lecture_touche(broches_colonnes, broches_lignes)
if touche != None:
if (touche == '#'): # on vérifie le mot de passe
if (tentative == correct):
print('')
print ("C'est le bon mot de passe!")
autorisation = True
else:
print('')
print("Ce n'est pas le bon mot de passe. Recommencez.")
tentative = ''
else:
tentative = tentative + touche
print(touche, end='') ## ou encore print('*', end='')
utime.sleep(0.3) # delai anti-rebond
while autorisation:
# on peut maintenant exécuter le programme principal
# ... faire clignoter la LED...
led.value(1)
utime.sleep(1)
led.value(0)
utime.sleep(1)

-

Pourquoi ce mot de passe?

Le choix du mot de passe "662607004" est une référence à la série télévisée Stranger Things: dans le dernier épisode de la troisième saison, Hopper et Joyce, qui sont à l'intérieur de la basse secrète russe,  doivent entrer un code secret qui est la constante de Planck.

(Il semble toutefois que les chiffres utilisés correspondent à la constante de Planck telle que révisée en 2014, alors que l'action se déroule en 1985).



À lire également:
Yves Pelletier (Facebook)

vendredi 15 juillet 2022

Écran OLED SSD1306 et Raspberry Pi Pico (micropython)

Dans cet article, nous étudions l'utilisation d'un petit écran OLED SSD1306 I2C avec un Raspberry Pi Pico programmé en micropython. 

J'ai utilisé un écran OLED dont la résolution est 128 X 32, mais les exemples pourront facilement être modifiés pour un résolution de 128 X 64. Si vous cherchez plutôt des informations pour un écran OLED à base de SH1106, j'ai déjà publié un article à ce sujet.


Connexions de l'écran au Raspberry Pi Pico

Il s'agit d'un modèle I2C, qui comporte donc 4 connecteurs (deux pour l'alimentation, et deux pour la communication avec le Pico).

J'ai branché l'écran OLED de la façon suivante:

  • Broche GND de l'écran: une des broches GND du Raspberry Pi Pico
  • Broche VCC de l'écran: sortie 3,3 V du Raspberry Pi Pico
  • Broche SCL de l'écran: broche GP9 du Raspberry Pi Pico
  • Broche SDA de l'écran: broche GP8 du Raspberry Pi Pico


Installation du pilote SSD1306.py

Nous allons utiliser le pilote pour micropython SSD1306. Le fichier "ssd1306.py" doit être copié dans le Raspberry Pi Pico.



Afficher du texte et des formes géométriques simples

Le module framebuf de micropython nous fournit des méthodes qui permettent d'écrire du texte et de tracer des formes géométriques à l'écran. 

Ainsi, si je programme "oled.text('BONJOUR',20,10)", le mot "BONJOUR" sera affiché. Le début du mot sera positionné au 20e pixel horizontal et au 10e pixel vertical.

Il est possible de dessiner quelques formes géométriques de base:
  • hline pour tracer une droite horizontale
  • vline pour tracer une droite verticale
  • line pour tracer une droite
  • rect pour tracer un rectangle
  • pixel pour régler un seul pixel
  • fill pour remplir tout l'écran en noir ou en blanc

Les changements ne sont visibles à l'écran que lorsque qu'on écrit "oled.show()".

Voici un script qui affiche un peu de texte (parfois défilant), ainsi que quelques lignes:

-

'''
Écran OLED ssd1306 branché à un Raspberry Pi Pico.
Texte défilant, lignes droites, etc.
Pour plus d'infos:
https://electroniqueamateur.blogspot.com/2022/07/ecran-oled-ssd1306-et-raspberry-pi-pico.html
'''
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import utime
#dimensions de l'écran
LARGEUR = 128
HAUTEUR = 32
#initialisation i2c
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=200000)
# initialisation oled
oled = SSD1306_I2C(LARGEUR, HAUTEUR, i2c)
while True:
for i in range (0,11):
oled.fill(0)
oled.text('COMPTE',48,4)
oled.text('A REBOURS',36,13)
oled.text(str(10-i),66,22)
# cadre:
oled.rect(16, 1, 112, 31, 1)
oled.show()
utime.sleep(0.5)
# texte défilant
texte_complet = "Ce texte est beaucoup trop long pour qu'on puisse l'afficher au complet sur cet ecran OLED"
longueur = len(texte_complet)
for n in range(0,longueur - 1):
oled.fill(0)
texte = texte_complet[n:16 + n]
oled.text(texte,0,15)
#lignes horizontales
oled.hline(0,7,127,1)
oled.hline(0,30,127,1)
oled.show()
utime.sleep(0.15)

-

Cette séquence vidéo montre le résultat sur l'écran OLED:


Afficher une image bitmap

On peut aussi afficher une image bitmap stockée dans un fichier.


Dans ce cas, il faut produire une image monochrome de format .pbm ayant une résolution maximale de 128 X 32 pixels.

Voici un exemple de conversion d'image réalisée avec le logiciel Gimp. L'image de départ était en couleur, avec une définition bien meilleure que 128 X 32 pixels.


J'ai sélectionné le menu Image / Mode / Couleurs Indexées.  Dans la boîte de dialogue, j'ai choisi "Utiliser un palette noir et blanc (1-bit)".



Ensuite, je choisi le menu Image / Échelle et taille de l'image... Dans la boîte de dialogue, on règle les dimensions de l'image à un maximum de 128 pixels de largeur et 32 pixels de hauteur.


Sur l'écran OLED, les parties noires de l'image seront affichées en blanc, et les parties blanches seront en noir. Si ce n'est pas ce que vous désirez, vous pouvez inverser l'image grâce au menu Couleurs / Inverser.

(si vous préférez, il est également possible de programmer le Raspberry Pi Pico pour que les couleurs soient inversées :  oled.invert(1) )


J'ai finalement utilisé le menu Fichier / Exporter sous ... et j'ai choisi un nom de fichier qui se termine par l'extension ".pbm" (il faut choisir un formatage de type "Raw" et non "ASCII").

Ce fichier de format pbm doit être enregistré dans le Raspberry Pi Pico avant l'exécution du script ci-dessous.


Voici le script en micropython qui permet l'affichage de l'image bitmap sur l'écran OLED:

-

'''
Affichage d'une image bitmap sur
un écran OLED ssd1306 branché à un
Raspberry Pi Pico.
Pour plus d'informations:
https://electroniqueamateur.blogspot.com/2022/07/ecran-oled-ssd1306-et-raspberry-pi-pico.html
'''
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
import framebuf
# dimensions de l'écran
LARGEUR_OLED = 128
HAUTEUR_OLED = 32
# dimensions de l'image
LARGEUR_IMAGE = 128
HAUTEUR_IMAGE = 32
# nom du fichier qui contient l'image
NOM_FICHIER = 'image.pbm'
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=200000) # initialisation i2c
oled = SSD1306_I2C(LARGEUR_OLED, HAUTEUR_OLED, i2c) # initialisation écran OLED
oled.fill(0) # pour effacer le contenu précédent
with open(NOM_FICHIER, 'rb') as f:
f.readline() # on ignore les 3 premieres lignes du fichier
f.readline()
f.readline()
data = bytearray(f.read())
fbuf = framebuf.FrameBuffer(data, LARGEUR_IMAGE, HAUTEUR_IMAGE, framebuf.MONO_HLSB)
oled.blit(fbuf, 0, 0)
oled.show()

-

À lire également:

Yves Pelletier (Facebook)



lundi 11 juillet 2022

Raspberry Pi Pico W: contrôler des LEDs à partir d'une page web

Continuons notre exploration du nouveau Raspberry Pi Pico W. Maintenant que nous savons comment afficher la mesure d'un capteur dans une page web, nous allons contrôler, à partir d'une page web, 3 LEDs branchées au Raspberry Pi Pico W. Ce sera facile de modifier le script pour qu'il réponse à vos besoins spécifiques: plutôt que contrôler des LEDs, la page web pourra servir à contrôler des relais, des moteurs, etc.

Comme d'habitude, le Pico W sera programmé en micropython; si vous avez besoin d'informations sur la façon de programmer un Raspberry Pi Pico (W ou non) en micropython avec Thonny, c'est par ici.

Circuit

J'ai branché une LED à chacune de ces broches du Raspberry Pi Pico W: GP13, GP14 et GP15. La résistance de protection de chaque LED devrait être d'au moins 100 Ω, mais la valeur exacte n'est pas importante.

Page web

À mon avis, la façon la plus conviviale d'allumer et d'éteindre des LEDs à partir d'une page web consiste à offrir à l'utilisateur un bouton à cocher pour chacune des LEDs: on coche la case pour que la LED s'allume, on la décoche pour qu'elle s'éteigne, et les changements sont appliqués lorsqu'on clique sur le bouton "Appliquer".

Notre page web comportera donc un formulaire html.


Script micropython

Voici le script en micropython; il a été inspiré d'un exemple fourni dans le guide officiel Connecting to the internet with Raspberry Pi Pico W.

Avec ce programme, le pico se branche à un réseau wifi (il faut écrire le nom du réseau et le mot de passe avant d'exécuter le script), puis il construit la page web contenant le formulaire et modifie l'état des 3 LEDs en fonction des cases qui ont été cochées.

Le contenu de la page web est presque entièrement statique. Le seul élément qui peut changer est la mention "checked" qui peut apparaître ou non dans les paramètres de chaque case à cocher, pour indiquer si la case est cochée ou non: c'est la raison des variables bouton1Str, bouton2Str et bouton3Str qui sont insérées aux endroits appropriés du code html lors de la construction de la page.

-

'''
On contrôle 3 LEDs branchées au Raspberry Pi Pico W
au moyen des cases à cocher d'une page web.
Pour plus d'information, voir le blog;
https://electroniqueamateur.blogspot.com/2022/07/raspberry-pi-pico-w-controler-des-leds.html
'''
# importation des bibliothèques pertinentes
import machine
import socket
import network
import time
#écrivez le nom du réseau wifi et le mot de passe
ssid = 'nom-du-reseau-wifi'
password = 'mot-de-passe-du-wifi'
# Les LEDs sont branchées aux broches 13, 14 et 15
LED1 = machine.Pin(13, machine.Pin.OUT)
LED2 = machine.Pin(14, machine.Pin.OUT)
LED3 = machine.Pin(15, machine.Pin.OUT)
# au départ, les boutons ne sont pas cochés
bouton1Str = ''
bouton2Str = ''
bouton3Str = ''
# contenu de la page web; les accolades seront remplacées
# par les strings bouton1Str , bouton2Str et bouton3Str
html = """<!DOCTYPE html>
<html>
<head> <title>Raspberry Pi Pico W</title> </head>
<body> <h1>Controle de LEDs:</h1>
<form action='/' method='POST'>
<p> <INPUT type='checkbox' name='LED1' value='1' {}>LED 1 &nbsp; &nbsp; &nbsp; </p>
<p> <INPUT type='checkbox' name='LED2' value='1' {}>LED 2 &nbsp; &nbsp; &nbsp; </p>
<p> <INPUT type='checkbox' name='LED3' value='1' {}>LED 3 &nbsp; &nbsp; &nbsp; </p>
<INPUT type='submit' value='Appliquer'><br><br>
</form>
</body>
</html>
"""
# connexion au réseau wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
max_wait = 10
print('attente de la connection a ' + ssid, end='')
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('.', end='')
time.sleep(1)
print ('')
if wlan.status() != 3:
raise RuntimeError('echec de la connexion a ' + ssid)
else:
print('connexion reussie')
status = wlan.ifconfig()
print( 'adresse ip = ' + status[0] )
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
while True:
cl, addr = s.accept()
request = cl.recv(1024)
request = str(request)
# si on a reçu notre réponse de formulaire:
if (not request.find('POST') == -1):
# si le bouton LED1 est coché:
if (not request.find('LED1=1') == -1):
bouton1Str = 'checked'
LED1.value(1)
print("LED 1 allumee")
# si le bouton LED1 n'est pas coché:
else:
bouton1Str = ''
LED1.value(0)
print("LED 1 eteinte")
# si le bouton LED2 est coché:
if (not request.find('LED2=1') == -1):
bouton2Str = 'checked'
LED2.value(1)
print("LED 2 allumee")
# si le bouton LED2 n'est pas coché:
else:
bouton2Str = ''
LED2.value(0)
print("LED 2 eteinte")
# si le bouton LED3 est coché:
if (not request.find('LED3=1') == -1):
bouton3Str = 'checked'
LED3.value(1)
print("LED 3 allumee")
# si le bouton LED3 n'est pas coché:
else:
bouton3Str = ''
LED3.value(0)
print("LED 3 eteinte")
# construction de la page web
reponse = html.format(bouton1Str,bouton2Str,bouton3Str)
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(reponse)
cl.close()

-

Après la réussite de la connexion au réseau wifi, l'adresse IP du Raspberry Pi Pico est affichée. Cette adresse vous permet d'accéder à la page web à partir de n'importe quel navigateur web.


Le programme affiche également l'état des LEDs chaque fois qu'on clique sur le bouton"Appliquer", ce qui vous permet de tester le script même si aucune LED n'est réellement connectée à votre Raspberry Pi Pico W.


À lire également:


Yves Pelletier (Facebook)


samedi 9 juillet 2022

Raspberry Pi Pico W: afficher les mesures d'un capteur sur une page web

Avec le nouveau Raspberry Pi Pico W, c'est maintenant possible de communiquer en WiFi puisque le fabricant a remplacé le logo en forme de framboise par une puce Infineon CYW4343.


Dans cet article, nous transformons le Raspberry Pi Pico W en serveur web, afin d'afficher dans une page web la valeur mesurée par un capteur.

Le Raspberry Pi Pico W sera programmé en Micropython. Il faut donc avoir préalablement installé le firmware Micropython de la façon habituelle: vous pressez le bouton BOOTSEL au moment où vous branchez le Raspberry Pi Pico W à un port USB, puis vous enregistrez le fichier uf2 approprié. Si vous n'êtes pas familier avec la programmation d'un Raspberry Pi Pico en Micropython, cet article d'introduction pourrait vous être utile.


En ce qui concerne le capteur, il s'agira pour l'instant d'un simple potentiomètre branché à la broche GP26 du Raspberry Pi Pico W. Ce sera facile d'adapter le script à d'autres capteurs selon vos besoins.


Voici le script en Micropython: le Raspberry Pi Pico se connecte d'abord à un réseau WiFi (vous devrez écrire le nom du réseau et le mot de passe dans le script pour que ça fonctionne). Ensuite, on créé une page web qui présente la valeur du potentiomètre (puisque j'avais plein d'espace sur la page web, j'ai présenté la valeur brute, le pourcentage et la conversion en volts). 

La page web est réglée pour se rafraîchir automatiquement toutes les 2 secondes, vous réglez évidemment cette fréquence selon vos besoins.

-
'''
On affiche dans une page web la valeur d'un potentiomètre
branché au Raspberry Pi Pico
Plus d'informations sur le blog:
https://electroniqueamateur.blogspot.com/2022/07/raspberry-pi-pico-w-afficher-les.html
'''
# importation des bibliothèques pertinentes
import machine
import socket
import network
import time
# écrivez le nom du réseau wifi et le mot de passe
ssid = 'nom_du_reseau'
password = 'mot_de_passe'
# potentiomètre branché à la broche GP26
pot = machine.ADC(26)
# Contenu de la page web, en html
# les accolades indiquent la position des valeurs numériques variables
html = """<!DOCTYPE html>
<html>
<head> <title>Raspberry Pi Pico W</title> <meta http-equiv=Refresh content=2></head>
<body> <h1>Valeur du potentiom&egrave;tre:</h1>
<h2> {} / 65535 </h2>
<h2> {} % </h2>
<h2> {} V </h2>
</body>
</html>
"""
# connexion au réseau wifi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
max_wait = 10 # si pas connecté en 10 secondes, on abandonne
print('attente de la connection a ' + ssid, end='')
while max_wait > 0:
if wlan.status() < 0 or wlan.status() >= 3:
break
max_wait -= 1
print('.', end='')
time.sleep(1)
print ('')
if wlan.status() != 3:
raise RuntimeError('echec de la connexion a ' + ssid)
else:
print('connexion reussie')
status = wlan.ifconfig()
print( 'adresse ip = ' + status[0] )
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
while True:
cl, addr = s.accept()
cl_file = cl.makefile('rwb', 0)
while True:
line = cl_file.readline()
if not line or line == b'\r\n':
break
# lecture du potentiomètre et conversion en pourcentage et en volts:
valeur = pot.read_u16()
pourcentage = valeur / 65535 * 100
volts = valeur / 65535 * 3.3
# construction de la page web (on insère les variables)
reponse = html.format(str(valeur), str("%.1f" % pourcentage), str("%.2f" % volts))
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
cl.send(reponse)
cl.close()
-

Au démarrage du script, si la connexion au réseau wifi est réussie, le programme affiche l'adresse IP du Raspberry Pi Pico W.  Il s'agit de coller cette adresse dans un navigateur web pour accéder à la page web.


Si vous tournez le bouton du potentiomètre, la valeur affichée sera modifiée.



À lire également:

Yves Pelletier (Facebook)

jeudi 7 juillet 2022

Affichage sur un PC d'un graphique montrant les mesures prises par un Raspberry Pi Pico

Ce projet consiste à afficher à l'écran d'un ordinateur un graphique cartésien qui montre les données transmises par un Raspberry Pi Pico branché à un de ses ports USB (il s'agit d'un projet similaire à celui que j'avais réalisé il y a quelques années avec un Arduino).


Les donnés sont acheminées du Raspberry Pi Pico à l'ordinateur par une communication série à travers le câble USB. Le Raspberry Pi Pico est programmé en MicroPython, alors que l'ordinateur affichant le graphique exécute un script en Python en utilisant la bibliothèque Matplotlib.

Circuit

Pour faire mes tests, j'ai utilisé un potentiomètre branché à la broche GP26 du Raspberry Pi Pico. Il sera facile de modifier le script pour remplacer le potentiomètre par un capteur de votre choix.


Script en Micropython exécuté par le Raspberry Pi Pico

Le rôle du Raspberry Pi Pico se résume à peu de chose. D'abord, il doit vérifier s'il reçoit un message en provenance de l'ordinateur. Si c'est le cas, il doit envoyer à l'ordinateur la mesure du capteur ainsi que le moment où cette mesure a été prise.

L'envoi de données du Raspberry Pi Pico vers l'ordinateur se fait tout simplement par la commande "print()".

La réception d'un message série par le port USB est un peu plus compliquée et, surtout, moins intuitive. Il s'agit des lignes 20, 21, 24 et 25 du script (ça nécessite également l'inclusion des bibliothèques sys et select):

poll = select.poll() 
poll.register(sys.stdin, select.POLLIN)
res = poll.poll()     
mess = res[0][0].read(1) 

-

'''
Affichage d'un graphique à l'écran d'un ordinateur
auqel est branché un Raspberry Pi Pico, par USB.
Ceci est le script du Pico.
Pour plus d'infos, lire cet article:
https://electroniqueamateur.blogspot.com/2022/07/affichage-sur-un-pc-dun-graphique.html
'''
import time
import sys
import select
import machine
tempsDebut = time.time_ns() # sera soustrait de chaque temps
pot = machine.ADC(26) # broche GPIO26 en entrée analogique
poll = select.poll() # pour la réception des messages par USB
poll.register(sys.stdin, select.POLLIN) # pour la réception des messages par USB
while True:
res = poll.poll() # avons-nous reĉu un message?
mess = res[0][0].read(1) # lecture du message
if (mess == 'e'): # on nous demande une donnée
temps = int((time.time_ns()-tempsDebut)/1000000)
print(str(temps) + ' ' + str(pot.read_u16()))
if (mess == 'i'): # on initialise le temps pour un nouveau graphique
tempsDebut = time.time_ns()
view raw graphPico.py hosted with ❤ by GitHub

Script en Python exécuté par l'ordinateur auquel est branché le Raspberry Pi Pico

Sur l'ordinateur hôte, le script est plus élaboré. Si ce n'est pas déjà fait, il faut d'abord installer la bibliothèque Matplotlib , qui prend en charge le tracé du graphique (si vous utilisez Thonny, l'installation est très facile en passant par le menu "Outil - Gérer les plugin...").  La méthode FuncAnimation() permettra une mise à jour régulière du graphique en y ajoutant de nouvelles mesures.

Le script cherche d'abord un port série disponible, et il s'y branche s'il n'en trouve qu'un seul (il demande à l'utilisateur de faire un choix s'il en trouve plusieurs). L'utilisateur doit ensuite choisir le nombre de secondes qui s'écouleront entre deux mesures consécutives.


Un graphique s'affiche alors à l'écran de l'ordinateur, et il se met à jour au rythme qui a été choisi par l'utilisateur. Voici un graphique obtenu pendant que je tournais lentement le bouton du potentiomètre.


Lorsqu'il est temps de prendre une nouvelle mesure, le script envoie le message "e" au Raspberry Pi Pico.
Le message "i" est envoyé au début d'un graphique, pour que le temps initial du graphique soit nul.

-

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Affichage d'un graphique à l'écran d'un ordinateur
auqel est branché un Raspberry Pi Pico, par USB.
Ceci est le script du PC. Il faut avoir installé Matplotlib.
Pour plus d'infos, lire cet article:
https://electroniqueamateur.blogspot.com/2022/07/affichage-sur-un-pc-dun-graphique.html
'''
# bibliothèques nécessaires pour la communication série
import serial
import serial.tools.list_ports
#bibliothèques nécessaires pour le tracé du graphique
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
temps_pause = 1 # nombre de millisecondes entre 2 mesures consécutives
valeurs_x = [] # données en abcisse
valeurs_y = [] # données en ordonnée
baud = 115200 #vitesse de la liaison série (doit être synchro avec l'Arduino)
# routine de mise à jour du graphique
def animate(i):
#on envoie au pico le message de requête
message = 'e'
pico.write(message.encode('utf-8'))
donneesBrutes = pico.readline()[:-2]
if (donneesBrutes): # si on a reçu quelque chose
listeDonnees = donneesBrutes.split() # on sépare l'abcisse et l'ordonnée
valeurs_x.append(float(listeDonnees[0])/1000.0) # conversion des millisecondes en secondes
valeurs_y.append(float(listeDonnees[1]))
plt.cla() # on efface le graphique déjà tracé
plt.plot(valeurs_x, valeurs_y) # tracé de la courbe
plt.title('Mesures du capteur') # titre du graphique
plt.xlabel('temps (s)') # identification de l'abcisse
plt.ylabel('Valeur mesuree') # identification de l'ordonée
print("Recherche d'un port serie...")
ports = serial.tools.list_ports.comports(include_links=False)
if (len(ports) != 0): # on a trouvé au moins un port actif
if (len(ports) > 1): # affichage du nombre de ports trouvés
print (str(len(ports)) + " ports actifs ont ete trouves:")
ligne = 1
for port in ports : # affichage du nom de chaque port
print(str(ligne) + ' : ' + port.device)
ligne = ligne + 1
portChoisi = input('Ecrivez le numero du port desire:')
else:
print ("1 port actif a ete trouve:")
portChoisi = 1
# on établit la communication série
pico = serial.Serial(ports[portChoisi - 1].device, baud, timeout=1)
print('Connexion a ' + pico.name + ' a un baud rate de ' + str(baud))
reponse = input("Nombre de secondes entre 2 mesures consecutives: ")
temps_pause = float(reponse) * 1000.0 # conversion en millisecondes
#on envoie au pico le message d'initialisation
message = 'i'
pico.write(message.encode('utf-8'))
ani = FuncAnimation(plt.gcf(), animate, interval=temps_pause) # mise à jour du graphique
plt.show() #affichage à l'écran
else: # on n'a pas trouvé de port actif
print("Aucun port actif n'a ete trouve")
view raw graphOrdi.py hosted with ❤ by GitHub

-

Astuce: exécution des deux scripts avec Thonny

Il est possible d'exécuter les deux scripts en utilisant Thonny, sans que Thonny n'intercepte les messages envoyés par le Raspberry Pi Pico. Voici comment j'ai procédé:

- J'ai d'abord exécuté le script sur le Pico au moyen de Thonny, en utilisant l'interpréteur Micropython approprié.

- J'ai ensuite sélectionné le menu "Exécuter - Déconnecter" sur Thonny. Cette action a pour effet d'interrompre les communications entre Thonny et le Raspberry Pi Pico, mais elle n'interrompt pas le script, qui continue son exécution de façon autonome.


- Finalement, j'ai changé d'interpréteur (menu "Exécuter - Sélectionner l'interpréteur...") et j'ai exécuté le deuxième script directement sur le PC.


Il existe d'autres options possibles, comme installer le premier script dans le Pico en le nommant "main.py" pour qu'il s'exécute automatiquement.


À lire également:

Yves Pelletier (Facebook)