Rubans de LED Neopixel WS2812B et WS2813

Ce tutoriel explique comment mettre en oeuvre un ruban de LED WS2812B ou WS2813 avec MicroPython.

Matériel requis

  1. Une carte d’extension de base Grove, utile seulement si le ruban Neopixel est piloté via broche numérique et équipé d’un connecteur Grove ;
  2. La carte NUCLEO-WB55 :
  3. Un ruban de LED RGB WS2812B Grove ; un anneau de LED RGB Grove ou tout autre dispositif avec un contrôleur de la famille WS28xxx.

Description : Un ruban de led WS2812B ou WS2813, dit Neopixel est constitué d’une succession de LED RGB adressables. On peut définir l’intensité et la couleur de chaque LED via le contrôleur WS28xxx. Les afficheur Neopixel peuvent être gérés par la NUCLEO-WB55 selon deux protocoles :

  1. Soit en les connectant au bus SPI ;
  2. Soit en les connectant à une broche numérique exploitant un pilote spécifique disponible (au moins) depuis la révision 1.17 du firmware MicroPython.

Nous allons tour à tour présenter ces deux alternatives de montage.

1. Ruban Neopixel connecté au bus SPI

Le montage

La connectique du ruban de LED WS2812B ou WS2813 est illustrée par le schéma ci-dessous :

Schéma de montage d'un ruban de LED WS2812B

La sortie MOSI du SPI1 est utilisée pour envoyer les données au contrôleur WS2812B ou ou WS2813

Ruban de Led NUCLEO-WB55
5V 5V
D0 D11
GND GND

Le code MicroPython pour allumer toutes les LED en rouge

Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.

Il faut récupérer et ajouter le fichier ws2812bstm32.py dans le répertoire du périphérique PYBLASH. Editez maintenant le script main.py et collez-y le code qui suit :

# Ce code a été adapté à partir de 'https://github.com/JanBednarik/micropython-ws2812'.
# Objet du script :
# Programmer un ruban de LED piloté par un contrôleur WS2812.
# Il s'agit d'allumer toute les LED du ruban en rouge.
# Cet exemple utilise un contrôleur SPI paramétré avec un débit (baudrate) de 4000000 bauds/s

from machine import SPI
import ws2812bstm32  # Bibliothèque pour gérer le ruban de LED
from time import sleep_ms

# Le WS2812 est connecté au MOSI du bus SPI
spi = SPI(1)
led_number = const(30)

sleep_ms(1000)
# Initialisation dyu ruban de LED
# Pour le STM32WB55 le débit DOIT être de 4000000 bauds
strip = ws2812bstm32.WS2812B(spi_bus=1, ledNumber=led_number, intensity=0.5, baudrate=4000000)

# Efface le ruban
strip.clear()
strip.show()

# Allume toutes les LED en rouge
for index in range(0, led_number):
	strip.put_pixel(index, 255, 0, 0)
strip.show()

sleep_ms(2000)

# Allume / éteint les LED en séquence
for index in range(0, led_number):
	if index != 0:
		strip.clean(index-1)
	strip.put_pixel(index, 0, 0, 255)
	strip.show()
	sleep_ms(500)
strip.clean(index)
strip.show()
sleep_ms(500)

Le code MicroPython pour afficher un drapeau et le faire défiler

# Ce code a été adapté à partir de 'https://github.com/JanBednarik/micropython-ws2812'.
# Objet du script :
# Programmer un ruban de LED piloté par un contrôleur WS2812.
# Il s'agit d'afficher un drapeau et le faire défiler sur le ruban.
# Cet exemple utilise un contrôleur SPI paramétré avec un débit (baudrate) de 4000000 bauds/s

from machine import SPI
import ws2812bstm32 # Bibliothèque pour gérer le ruban de LED
from time import sleep_ms

french_flag = [
	(0,0,250), (0,0,250), (0,0,250),
	(250,250,250), (250,250,250), (250,250,250),
	(250,0,0), (250,0,0), (250,0,0)]

italien_flag = [
	(0,250,0), (0,250,0), (0,250,0),
	(250,250,250), (250,250,250), (250,250,250),
	(250,0,0), (250,0,0), (250,0,0)]

def dual_flag(leds, data0, data1, num_led, t, display):
	i = 0
	while i < num_led:
		if (i + len(data0)) < num_led:
			for d in data0:
				leds.put_pixel(i, d[0], d[1], d[2])
				if display:
					leds.show()
				sleep_ms(t)
				i+=1
			if not display:
				leds.show()
			i+=1
		else:
			break
		if (i + len(data1)) < num_led:
			for d in data1:
				leds.put_pixel(i, d[0], d[1], d[2])
				if display:
					leds.show()
					sleep_ms(t)
					i+=1
			if not display:
				leds.show()
			i+=1
		else:
			break
			

def flag_move(leds, data, num_led, t):
	i = 0
	while i < num_led:
		if (i + len(data)) < num_led:
			if i != 0:
				leds.clean(i-1)
			j = i
			for d in data:
				leds.put_pixel(j, d[0], d[1], d[2])
				j+=1
			leds.show()
			sleep_ms(t)
			i+=1
		else:
			break

led_number = const(16)

# Initialise le ruban de LED
# Le WS2812 est connecté au MOSI du bus SPI
# Pour le STM32WB55 le baudrate DOIT être 4000000
strip = ws2812bstm32.WS2812B(spi_bus=1, ledNumber=led_number, intensity=0.5, baudrate=4000000)

# Efface le ruban
strip.clear()
strip.show()

# Affiche deux drapeaux
dual_flag(strip, french_flag, italien_flag, led_number, 500, True)

while True:

	# Effacement du drapeau
	strip.clear()
	strip.show()

	# Décalage du drapeau
	flag_move(strip, french_flag, led_number, 500)

	# Effacement du drapeau
	strip.clear()
	strip.show()

	# Décalage du drapeau
	flag_move(strip, italien_flag, led_number, 500)

Pour aller plus loin

D’autres exemples sont disponibles dans l’archive téléchargeable MODULES.zip.

2. Ruban Neopixel connecté à une broche numérique

Le montage

On utilise un ruban Neopixel étanche éuipé d’un connecteur Grove, avec 60 LED RGB en série. Connectez le ruban à l’une des broches numériques du Grove Base Shield (dans notre cas, on a choisi la broche D2), lui même installé sur la NUCLEO-WB55.

Le code MicroPython pour afficher et faire défiler des drapeaux italiens et français

Les scripts présentés ci-après sont disponibles dans la zone de téléchargement.

Il faut récupérer et ajouter le fichier neopixel.py dans le répertoire du périphérique PYBLASH. Editez maintenant le script main.py et collez-y le code qui suit :

# Objet du script : Jeux de lumière avec un ruban Neopixel étanche de Grove (60 LED RGB)
# Nécessite également une NUCLEO-WB55 et un Grove Base Shield.
# Source : Christophe PRIOUZEAU, STMicroelectronics

import machine
import time

# Pour gérer le ruban de LED
import neopixel

class StripLED:
	def __init__(self, pin_number, ledNumber, intensity=0.5):
		self._pin = pin_number
		self._ledNumber = ledNumber
		self._intensity = intensity
		self._strip = neopixel.NeoPixel(machine.Pin(self._pin), self._ledNumber)

		self.french_flag = [
					(0,0,250), (0,0,250), (0,0,250),
					(250,250,250), (250,250,250), (250,250,250),
					(250,0,0), (250,0,0), (250,0,0)]
		self.italian_flag = [
					(0,250,0), (0,250,0), (0,250,0),
					(250,250,250), (250,250,250), (250,250,250),
					(250,0,0), (250,0,0), (250,0,0)]

	def _set_to_zero(self, addr):
		if addr < self._ledNumber:
			self._strip[addr] = (0,0,0)
	def _color_intensity(self, color):
		#print("[debug] [%d, %.2f] %d" % (color,  self._intensity, int(color * self._intensity) ))
		return int(color * self._intensity)
	def set_led(self, addr, red, green, blue):
		if addr < self._ledNumber:
			self._strip[addr] = (self._color_intensity(red), self._color_intensity(green), self._color_intensity(blue))

	def _flag_move(self, data, num_led, t):
		i = 0
		while i < (num_led+1):
			if (i + len(data)) < (num_led+1):
				if i != 0:
					self._set_to_zero(i-1)
				j = i
				for d in data:
					self.set_led(j, d[0], d[1], d[2])
					j+=1
				self._strip.write()
				time.sleep_ms(t)
				i+=1
			else:
				break

	def clear(self):
		for i in range(0, self._ledNumber):
			self._set_to_zero(i)
		self._strip.write()

	def get_max_led(self):
		return self._ledNumber

	def french_flag_move(self, t):
		self._flag_move(self.french_flag, self._ledNumber, t)

	def italian_flag_move(self, t):
		self._flag_move(self.italian_flag, self._ledNumber, t)

	def move_dual(self, t):
		self._dual_flag(self.french_flag, self.italian_flag, t)

	def _dual_flag(self, data0, data1, t):
		i = 0
		while i < (self._ledNumber+1):
			if (i + len(data0)) < (self._ledNumber+1):
				for d in data0:
					self.set_led(i, d[0], d[1], d[2])
					self._strip.write()
					time.sleep_ms(t)
					i+=1
				i+=1
			else:
				break
			if (i + len(data1)) < (self._ledNumber+1):
				for d in data1:
					self.set_led(i, d[0], d[1], d[2])
					self._strip.write()
					time.sleep_ms(t)
					i+=1
				i+=1
			else:
				break

# -------------------------------------
#				 MAIN
# -------------------------------------

_NB_LED_RGB = const(60) # Nombre de LED RGB sur le ruban
_INTENSITE_LED = 0.1 # Luminosité des LED (entre 0 et 1)

if __name__ == '__main__':

	# Ruban de LED connecté à D2 sur le Grove Base Shield
	strip_led = StripLED(pin_number='D2', ledNumber = _NB_LED_RGB, intensity= _INTENSITE_LED)

	# Fait défiler un drapeau français au long du ruban
	strip_led.clear()
	strip_led.french_flag_move(500)

	# Fait défiler un drapeau italien au long du ruban
	strip_led.clear()
	strip_led.italian_flag_move(500)
	
	# "Mappe" selon un cycle le ruban en alternant un drapeau français et un drapeau italien
	while True:
		strip_led.clear()
		strip_led.move_dual(100)
		time.sleep(4)