From 099a2f8bb14f44a2596b663fccd3942424257f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Canada?= Date: Fri, 10 Apr 2026 12:33:45 +0200 Subject: [PATCH 1/5] feat(wsen-pads): Climate Station example with OLED Display. --- lib/hts221/examples/env_station.py | 146 ++++++++++++++++++++++++ lib/wsen-pads/examples/station_env.py | 156 ++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 lib/hts221/examples/env_station.py create mode 100644 lib/wsen-pads/examples/station_env.py diff --git a/lib/hts221/examples/env_station.py b/lib/hts221/examples/env_station.py new file mode 100644 index 00000000..66b9e5e9 --- /dev/null +++ b/lib/hts221/examples/env_station.py @@ -0,0 +1,146 @@ +from machine import I2C, SPI, Pin +from time import sleep_ms +import ssd1327 +from hts221 import HTS221 + +# === Ecran === +spi = SPI(1) +dc = Pin("DATA_COMMAND_DISPLAY") +res = Pin("RST_DISPLAY") +cs = Pin("CS_DISPLAY") +display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) + +# === Capteur === +i2c = I2C(1) +sensor = HTS221(i2c) + +# === Calibration === +print("Calibration en cours...") +temps = [] +hums = [] +for _ in range(10): + temps.append(sensor.temperature()) + hums.append(sensor.humidity()) + sleep_ms(200) + +OFFSET_TEMP = 20.0 - (sum(temps) / len(temps)) +OFFSET_HUM = 50.0 - (sum(hums) / len(hums)) +print(f"Offset T: {OFFSET_TEMP:.2f} Offset H: {OFFSET_HUM:.2f}") +print("Calibration OK") + +# === Dessin === +# Zone safe pour ecran rond : carré 90x90 centré => x:[19,109] y:[19,109] +SAFE_X = 19 +SAFE_Y = 19 +SAFE_W = 90 +SAFE_H = 90 + +def draw_hline(x, y, w, color=255): + for i in range(w): + display.pixel(x + i, y, color) + +def draw_vline(x, y, h, color=255): + for i in range(h): + display.pixel(x, y + i, color) + +def draw_rect(x, y, w, h, color=255): + draw_hline(x, y, w, color) + draw_hline(x, y + h - 1, w, color) + draw_vline(x, y, h, color) + draw_vline(x + w - 1, y, h, color) + +def draw_fill_rect(x, y, w, h, color=255): + for i in range(h): + draw_hline(x, y + i, w, color) + +def draw_circle(cx, cy, r, color=255): + x = r + y = 0 + err = 0 + while x >= y: + display.pixel(cx + x, cy + y, color) + display.pixel(cx + y, cy + x, color) + display.pixel(cx - y, cy + x, color) + display.pixel(cx - x, cy + y, color) + display.pixel(cx - x, cy - y, color) + display.pixel(cx - y, cy - x, color) + display.pixel(cx + y, cy - x, color) + display.pixel(cx + x, cy - y, color) + y += 1 + err += 1 + 2 * y + if 2 * (err - x) + 1 > 0: + x -= 1 + err += 1 - 2 * x + +def draw_bar_h(x, y, w, h, value, min_val, max_val, color=200): + draw_rect(x, y, w, h, 80) + ratio = (value - min_val) / (max_val - min_val) + ratio = max(0.0, min(1.0, ratio)) + filled = int((w - 2) * ratio) + if filled > 0: + draw_fill_rect(x + 1, y + 1, filled, h - 2, color) + +def comfort_label(temp, hum): + if 18 <= temp <= 26 and 40 <= hum <= 60: + return "OK", 255 + elif temp > 30 or hum > 75: + return "CHAUD", 180 + elif temp < 15 or hum < 25: + return "FROID", 150 + else: + return "BIEN", 200 + +def draw_screen(temp, hum): + display.fill(0) + + # Bordure circulaire decorative + draw_circle(64, 64, 62, 60) + draw_circle(64, 64, 60, 40) + + # === TITRE === + display.text("ENV STATION", 19, 22, 200) + draw_hline(19, 31, 90, 80) + + # === TEMPERATURE === + display.text("TEMP", 19, 37, 120) + temp_str = f"{temp:.1f}C" + display.text(temp_str, 19, 47, 255) + + # Barre temperature (0-50C) + draw_bar_h(19, 57, 90, 7, temp, 0, 50, 220) + + # === SEPARATEUR === + draw_hline(19, 67, 90, 70) + + # === HUMIDITE === + display.text("HUM", 19, 72, 120) + hum_str = f"{hum:.1f}%" + display.text(hum_str, 19, 82, 255) + + # Barre humidite (0-100%) + draw_bar_h(19, 92, 90, 7, hum, 0, 100, 200) + + # === CONFORT === + label, c_color = comfort_label(temp, hum) + draw_hline(19, 102, 90, 70) + cx = 64 - len(label) * 4 + display.text(label, cx, 106, c_color) + + display.show() + +# === Boucle principale === +print("Station environnementale demarree") +while True: + try: + temp = sensor.temperature() + OFFSET_TEMP + hum = sensor.humidity() + OFFSET_HUM + hum = max(0.0, min(100.0, hum)) + print(f"T:{temp:.2f}C H:{hum:.2f}%") + draw_screen(temp, hum) + except Exception as e: + print("Erreur:", e) + display.fill(0) + display.text("ERREUR", 35, 55, 255) + display.text(str(e)[:16], 0, 70, 150) + display.show() + sleep_ms(1000) \ No newline at end of file diff --git a/lib/wsen-pads/examples/station_env.py b/lib/wsen-pads/examples/station_env.py new file mode 100644 index 00000000..ba1cab42 --- /dev/null +++ b/lib/wsen-pads/examples/station_env.py @@ -0,0 +1,156 @@ +from machine import I2C, SPI, Pin +from time import sleep_ms +import ssd1327 +from wsen_pads import WSEN_PADS +from hts221 import HTS221 + +# === Ecran === +spi = SPI(1) +dc = Pin("DATA_COMMAND_DISPLAY") +res = Pin("RST_DISPLAY") +cs = Pin("CS_DISPLAY") +display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) + +# === Capteurs === +i2c = I2C(1) +pads = WSEN_PADS(i2c) # pression + temperature +hts = HTS221(i2c) # humidite + +# === Calibration === +print("Calibration en cours...") +temps = [] +hums = [] +press = [] +for _ in range(10): + temps.append(pads.temperature()) + hums.append(hts.humidity()) + press.append(pads.pressure_hpa()) + sleep_ms(200) + +OFFSET_TEMP = 20.0 - (sum(temps) / len(temps)) +OFFSET_HUM = 50.0 - (sum(hums) / len(hums)) +OFFSET_PRES = 1013.0 - (sum(press) / len(press)) +print(f"Offset T:{OFFSET_TEMP:.2f} H:{OFFSET_HUM:.2f} P:{OFFSET_PRES:.2f}") +print("Calibration OK") + +# === Dessin === +def draw_hline(x, y, w, color=255): + for i in range(w): + display.pixel(x + i, y, color) + +def draw_vline(x, y, h, color=255): + for i in range(h): + display.pixel(x, y + i, color) + +def draw_rect(x, y, w, h, color=255): + draw_hline(x, y, w, color) + draw_hline(x, y + h - 1, w, color) + draw_vline(x, y, h, color) + draw_vline(x + w - 1, y, h, color) + +def draw_fill_rect(x, y, w, h, color=255): + for i in range(h): + draw_hline(x, y + i, w, color) + +def draw_circle(cx, cy, r, color=255): + x = r + y = 0 + err = 0 + while x >= y: + display.pixel(cx + x, cy + y, color) + display.pixel(cx + y, cy + x, color) + display.pixel(cx - y, cy + x, color) + display.pixel(cx - x, cy + y, color) + display.pixel(cx - x, cy - y, color) + display.pixel(cx - y, cy - x, color) + display.pixel(cx + y, cy - x, color) + display.pixel(cx + x, cy - y, color) + y += 1 + err += 1 + 2 * y + if 2 * (err - x) + 1 > 0: + x -= 1 + err += 1 - 2 * x + +def draw_bar_h(x, y, w, h, value, min_val, max_val, color=200): + draw_rect(x, y, w, h, 80) + ratio = (value - min_val) / (max_val - min_val) + ratio = max(0.0, min(1.0, ratio)) + filled = int((w - 2) * ratio) + if filled > 0: + draw_fill_rect(x + 1, y + 1, filled, h - 2, color) + +def comfort_label(temp, hum, pres): + if 18 <= temp <= 26 and 40 <= hum <= 60 and 1000 <= pres <= 1025: + return "IDEAL", 255 + elif temp > 30 or hum > 75: + return "CHAUD", 180 + elif temp < 15 or hum < 25: + return "FROID", 150 + elif pres < 1000: + return "BASSE P", 150 + elif pres > 1025: + return "HAUTE P", 200 + else: + return "CORRECT", 200 + +def draw_screen(temp, hum, pres): + display.fill(0) + + # Bordure circulaire decorative + draw_circle(64, 64, 62, 60) + draw_circle(64, 64, 60, 40) + + # === TITRE === + display.text("CLIMAT", 35, 20, 255) + draw_hline(19, 29, 90, 100) + + # === TEMPERATURE === + display.text("T", 19, 35, 120) + temp_str = f"{temp:.1f}C" + display.text(temp_str, 29, 35, 255) + draw_bar_h(19, 44, 90, 5, temp, 0, 50, 220) + + # === HUMIDITE === + display.text("H", 19, 52, 120) + hum_str = f"{hum:.1f}%" + display.text(hum_str, 29, 52, 255) + draw_bar_h(19, 61, 90, 5, hum, 0, 100, 180) + + # === PRESSION === + display.text("P", 19, 69, 120) + pres_str = f"{pres:.0f}hPa" + display.text(pres_str, 29, 69, 255) + draw_bar_h(19, 78, 90, 5, pres, 950, 1050, 160) + + # === SEPARATEUR === + draw_hline(19, 86, 90, 70) + + # === CONFORT === + label, c_color = comfort_label(temp, hum, pres) + cx = 64 - len(label) * 4 + display.text(label, cx, 92, c_color) + + # === CAPTEURS === + draw_hline(19, 102, 90, 50) + display.text("PADS+HTS221", 22, 106, 60) + + display.show() + +# === Boucle principale === +print("Station climatique demarree") +while True: + try: + temp = pads.temperature() + OFFSET_TEMP + hum = hts.humidity() + OFFSET_HUM + pres = pads.pressure_hpa()+ OFFSET_PRES + hum = max(0.0, min(100.0, hum)) + print(f"T:{temp:.2f}C H:{hum:.2f}% P:{pres:.1f}hPa") + draw_screen(temp, hum, pres) + except Exception as e: + print("Erreur:", e) + display.fill(0) + display.text("ERREUR", 35, 55, 255) + display.text(str(e)[:16], 0, 70, 150) + display.show() + sleep_ms(1000) + \ No newline at end of file From debcf524adc99c03f499c0e5480a811465aec2eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matt=C3=A9o=20Canada?= Date: Fri, 10 Apr 2026 13:16:18 +0200 Subject: [PATCH 2/5] fix(wsen-pads): Fix ruff lint issues and missing final newlines. --- lib/wsen-pads/examples/station_env.py | 150 ++++++++++++++------------ 1 file changed, 83 insertions(+), 67 deletions(-) diff --git a/lib/wsen-pads/examples/station_env.py b/lib/wsen-pads/examples/station_env.py index ba1cab42..4277500e 100644 --- a/lib/wsen-pads/examples/station_env.py +++ b/lib/wsen-pads/examples/station_env.py @@ -1,58 +1,71 @@ -from machine import I2C, SPI, Pin +"""Climate station example using WSEN-PADS, HTS221 and SSD1327 OLED. + +Reads temperature and pressure from the WSEN-PADS sensor and humidity +from the HTS221 sensor. Includes a startup calibration phase to compensate +sensor offsets. Displays live temperature, humidity and pressure readings +with horizontal progress bars and a comfort index on a round 128x128 OLED. +""" from time import sleep_ms + import ssd1327 -from wsen_pads import WSEN_PADS from hts221 import HTS221 - +from machine import I2C, SPI, Pin +from wsen_pads import WSEN_PADS + # === Ecran === spi = SPI(1) -dc = Pin("DATA_COMMAND_DISPLAY") +dc = Pin("DATA_COMMAND_DISPLAY") res = Pin("RST_DISPLAY") -cs = Pin("CS_DISPLAY") +cs = Pin("CS_DISPLAY") display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) - + # === Capteurs === i2c = I2C(1) -pads = WSEN_PADS(i2c) # pression + temperature -hts = HTS221(i2c) # humidite - +pads = WSEN_PADS(i2c) +hts = HTS221(i2c) + # === Calibration === print("Calibration en cours...") temps = [] -hums = [] +hums = [] press = [] for _ in range(10): temps.append(pads.temperature()) hums.append(hts.humidity()) press.append(pads.pressure_hpa()) sleep_ms(200) - -OFFSET_TEMP = 20.0 - (sum(temps) / len(temps)) -OFFSET_HUM = 50.0 - (sum(hums) / len(hums)) + +OFFSET_TEMP = 20.0 - (sum(temps) / len(temps)) +OFFSET_HUM = 50.0 - (sum(hums) / len(hums)) OFFSET_PRES = 1013.0 - (sum(press) / len(press)) print(f"Offset T:{OFFSET_TEMP:.2f} H:{OFFSET_HUM:.2f} P:{OFFSET_PRES:.2f}") print("Calibration OK") - + + # === Dessin === -def draw_hline(x, y, w, color=255): +def draw_hline(x, y, w, color=15): for i in range(w): display.pixel(x + i, y, color) - -def draw_vline(x, y, h, color=255): + + +def draw_vline(x, y, h, color=15): for i in range(h): display.pixel(x, y + i, color) - -def draw_rect(x, y, w, h, color=255): + + +def draw_rect(x, y, w, h, color=15): draw_hline(x, y, w, color) draw_hline(x, y + h - 1, w, color) draw_vline(x, y, h, color) draw_vline(x + w - 1, y, h, color) - -def draw_fill_rect(x, y, w, h, color=255): + + +def draw_fill_rect(x, y, w, h, color=15): for i in range(h): draw_hline(x, y + i, w, color) - -def draw_circle(cx, cy, r, color=255): + + +def draw_circle(cx, cy, r, color=15): x = r y = 0 err = 0 @@ -70,87 +83,90 @@ def draw_circle(cx, cy, r, color=255): if 2 * (err - x) + 1 > 0: x -= 1 err += 1 - 2 * x - -def draw_bar_h(x, y, w, h, value, min_val, max_val, color=200): - draw_rect(x, y, w, h, 80) + + +def draw_bar_h(x, y, w, h, value, min_val, max_val, color=13): + draw_rect(x, y, w, h, 5) ratio = (value - min_val) / (max_val - min_val) ratio = max(0.0, min(1.0, ratio)) filled = int((w - 2) * ratio) if filled > 0: draw_fill_rect(x + 1, y + 1, filled, h - 2, color) - + + def comfort_label(temp, hum, pres): if 18 <= temp <= 26 and 40 <= hum <= 60 and 1000 <= pres <= 1025: - return "IDEAL", 255 + return "IDEAL", 15 elif temp > 30 or hum > 75: - return "CHAUD", 180 + return "CHAUD", 11 elif temp < 15 or hum < 25: - return "FROID", 150 + return "FROID", 9 elif pres < 1000: - return "BASSE P", 150 + return "BASSE P", 9 elif pres > 1025: - return "HAUTE P", 200 + return "HAUTE P", 13 else: - return "CORRECT", 200 - + return "CORRECT", 12 + + def draw_screen(temp, hum, pres): display.fill(0) - - # Bordure circulaire decorative - draw_circle(64, 64, 62, 60) - draw_circle(64, 64, 60, 40) - + + # Bordure circulaire + draw_circle(64, 64, 62, 4) + draw_circle(64, 64, 60, 2) + # === TITRE === - display.text("CLIMAT", 35, 20, 255) - draw_hline(19, 29, 90, 100) - + display.text("CLIMAT", 35, 20, 15) + draw_hline(19, 29, 90, 6) + # === TEMPERATURE === - display.text("T", 19, 35, 120) + display.text("T", 19, 35, 7) temp_str = f"{temp:.1f}C" - display.text(temp_str, 29, 35, 255) - draw_bar_h(19, 44, 90, 5, temp, 0, 50, 220) - + display.text(temp_str, 29, 35, 15) + draw_bar_h(19, 44, 90, 5, temp, 0, 50, 13) + # === HUMIDITE === - display.text("H", 19, 52, 120) + display.text("H", 19, 52, 7) hum_str = f"{hum:.1f}%" - display.text(hum_str, 29, 52, 255) - draw_bar_h(19, 61, 90, 5, hum, 0, 100, 180) - + display.text(hum_str, 29, 52, 15) + draw_bar_h(19, 61, 90, 5, hum, 0, 100, 11) + # === PRESSION === - display.text("P", 19, 69, 120) + display.text("P", 19, 69, 7) pres_str = f"{pres:.0f}hPa" - display.text(pres_str, 29, 69, 255) - draw_bar_h(19, 78, 90, 5, pres, 950, 1050, 160) - + display.text(pres_str, 29, 69, 15) + draw_bar_h(19, 78, 90, 5, pres, 950, 1050, 10) + # === SEPARATEUR === - draw_hline(19, 86, 90, 70) - + draw_hline(19, 86, 90, 4) + # === CONFORT === label, c_color = comfort_label(temp, hum, pres) cx = 64 - len(label) * 4 display.text(label, cx, 92, c_color) - + # === CAPTEURS === - draw_hline(19, 102, 90, 50) - display.text("PADS+HTS221", 22, 106, 60) - + draw_hline(19, 102, 90, 3) + display.text("PADS+HTS221", 22, 106, 4) + display.show() - + + # === Boucle principale === print("Station climatique demarree") while True: try: temp = pads.temperature() + OFFSET_TEMP - hum = hts.humidity() + OFFSET_HUM - pres = pads.pressure_hpa()+ OFFSET_PRES - hum = max(0.0, min(100.0, hum)) + hum = hts.humidity() + OFFSET_HUM + pres = pads.pressure_hpa() + OFFSET_PRES + hum = max(0.0, min(100.0, hum)) print(f"T:{temp:.2f}C H:{hum:.2f}% P:{pres:.1f}hPa") draw_screen(temp, hum, pres) except Exception as e: print("Erreur:", e) display.fill(0) - display.text("ERREUR", 35, 55, 255) - display.text(str(e)[:16], 0, 70, 150) + display.text("ERREUR", 35, 55, 15) + display.text(str(e)[:16], 0, 70, 9) display.show() sleep_ms(1000) - \ No newline at end of file From b9e00279f6ff287473cd5cf0cf4e0135b45aceba Mon Sep 17 00:00:00 2001 From: MatteoCnda1 Date: Mon, 13 Apr 2026 11:21:40 +0200 Subject: [PATCH 3/5] fix(wsen-pads): Simplify display using steami_screen and fix lint issues. --- lib/wsen-pads/examples/station_env.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/wsen-pads/examples/station_env.py b/lib/wsen-pads/examples/station_env.py index 4277500e..098b4f8a 100644 --- a/lib/wsen-pads/examples/station_env.py +++ b/lib/wsen-pads/examples/station_env.py @@ -5,6 +5,7 @@ sensor offsets. Displays live temperature, humidity and pressure readings with horizontal progress bars and a comfort index on a round 128x128 OLED. """ + from time import sleep_ms import ssd1327 From efa9a7a2e5df5bb1fb11412931af47bd8f46b926 Mon Sep 17 00:00:00 2001 From: MatteoCnda1 Date: Mon, 13 Apr 2026 11:41:54 +0200 Subject: [PATCH 4/5] fix(hts221): Remove unused env_station.py example. --- lib/hts221/examples/env_station.py | 146 ----------------------------- 1 file changed, 146 deletions(-) delete mode 100644 lib/hts221/examples/env_station.py diff --git a/lib/hts221/examples/env_station.py b/lib/hts221/examples/env_station.py deleted file mode 100644 index 66b9e5e9..00000000 --- a/lib/hts221/examples/env_station.py +++ /dev/null @@ -1,146 +0,0 @@ -from machine import I2C, SPI, Pin -from time import sleep_ms -import ssd1327 -from hts221 import HTS221 - -# === Ecran === -spi = SPI(1) -dc = Pin("DATA_COMMAND_DISPLAY") -res = Pin("RST_DISPLAY") -cs = Pin("CS_DISPLAY") -display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) - -# === Capteur === -i2c = I2C(1) -sensor = HTS221(i2c) - -# === Calibration === -print("Calibration en cours...") -temps = [] -hums = [] -for _ in range(10): - temps.append(sensor.temperature()) - hums.append(sensor.humidity()) - sleep_ms(200) - -OFFSET_TEMP = 20.0 - (sum(temps) / len(temps)) -OFFSET_HUM = 50.0 - (sum(hums) / len(hums)) -print(f"Offset T: {OFFSET_TEMP:.2f} Offset H: {OFFSET_HUM:.2f}") -print("Calibration OK") - -# === Dessin === -# Zone safe pour ecran rond : carré 90x90 centré => x:[19,109] y:[19,109] -SAFE_X = 19 -SAFE_Y = 19 -SAFE_W = 90 -SAFE_H = 90 - -def draw_hline(x, y, w, color=255): - for i in range(w): - display.pixel(x + i, y, color) - -def draw_vline(x, y, h, color=255): - for i in range(h): - display.pixel(x, y + i, color) - -def draw_rect(x, y, w, h, color=255): - draw_hline(x, y, w, color) - draw_hline(x, y + h - 1, w, color) - draw_vline(x, y, h, color) - draw_vline(x + w - 1, y, h, color) - -def draw_fill_rect(x, y, w, h, color=255): - for i in range(h): - draw_hline(x, y + i, w, color) - -def draw_circle(cx, cy, r, color=255): - x = r - y = 0 - err = 0 - while x >= y: - display.pixel(cx + x, cy + y, color) - display.pixel(cx + y, cy + x, color) - display.pixel(cx - y, cy + x, color) - display.pixel(cx - x, cy + y, color) - display.pixel(cx - x, cy - y, color) - display.pixel(cx - y, cy - x, color) - display.pixel(cx + y, cy - x, color) - display.pixel(cx + x, cy - y, color) - y += 1 - err += 1 + 2 * y - if 2 * (err - x) + 1 > 0: - x -= 1 - err += 1 - 2 * x - -def draw_bar_h(x, y, w, h, value, min_val, max_val, color=200): - draw_rect(x, y, w, h, 80) - ratio = (value - min_val) / (max_val - min_val) - ratio = max(0.0, min(1.0, ratio)) - filled = int((w - 2) * ratio) - if filled > 0: - draw_fill_rect(x + 1, y + 1, filled, h - 2, color) - -def comfort_label(temp, hum): - if 18 <= temp <= 26 and 40 <= hum <= 60: - return "OK", 255 - elif temp > 30 or hum > 75: - return "CHAUD", 180 - elif temp < 15 or hum < 25: - return "FROID", 150 - else: - return "BIEN", 200 - -def draw_screen(temp, hum): - display.fill(0) - - # Bordure circulaire decorative - draw_circle(64, 64, 62, 60) - draw_circle(64, 64, 60, 40) - - # === TITRE === - display.text("ENV STATION", 19, 22, 200) - draw_hline(19, 31, 90, 80) - - # === TEMPERATURE === - display.text("TEMP", 19, 37, 120) - temp_str = f"{temp:.1f}C" - display.text(temp_str, 19, 47, 255) - - # Barre temperature (0-50C) - draw_bar_h(19, 57, 90, 7, temp, 0, 50, 220) - - # === SEPARATEUR === - draw_hline(19, 67, 90, 70) - - # === HUMIDITE === - display.text("HUM", 19, 72, 120) - hum_str = f"{hum:.1f}%" - display.text(hum_str, 19, 82, 255) - - # Barre humidite (0-100%) - draw_bar_h(19, 92, 90, 7, hum, 0, 100, 200) - - # === CONFORT === - label, c_color = comfort_label(temp, hum) - draw_hline(19, 102, 90, 70) - cx = 64 - len(label) * 4 - display.text(label, cx, 106, c_color) - - display.show() - -# === Boucle principale === -print("Station environnementale demarree") -while True: - try: - temp = sensor.temperature() + OFFSET_TEMP - hum = sensor.humidity() + OFFSET_HUM - hum = max(0.0, min(100.0, hum)) - print(f"T:{temp:.2f}C H:{hum:.2f}%") - draw_screen(temp, hum) - except Exception as e: - print("Erreur:", e) - display.fill(0) - display.text("ERREUR", 35, 55, 255) - display.text(str(e)[:16], 0, 70, 150) - display.show() - sleep_ms(1000) \ No newline at end of file From 502363b4d6af3c4643ceff065c170d4754873ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20NEDJAR?= Date: Thu, 16 Apr 2026 21:59:51 +0200 Subject: [PATCH 5/5] refactor(wsen-pads): Rewrite climate station with steami_screen widgets. Major rewrite of the station_env example: 1. Use steami_screen (Screen, SSD1327Display, gauge, title, value, subtitle) instead of 100+ lines of pixel-by-pixel manual drawing (draw_hline, draw_vline, draw_rect, draw_fill_rect, draw_circle). The native framebuf primitives behind steami_screen are implemented in C and are orders of magnitude faster than Python pixel loops. 2. Remove fake calibration that forced readings toward arbitrary "ideal" values (20C, 50%RH, 1013hPa). A sensor reading 23C at startup was being shifted to 20C, making every subsequent reading wrong by -3C. The example now shows raw sensor values. For real calibration, use steami_config (see calibrate_temperature.py). 3. Translate all UI text and comments from French to English to match the project convention. 4. Add try/except/finally with power_off() for both sensors so Ctrl+C does not leave WSEN-PADS and HTS221 running. 5. Use named comfort thresholds (TEMP_MIN/MAX, HUM_MIN/MAX, PRES_MIN/MAX) instead of inline magic numbers. --- lib/wsen-pads/examples/station_env.py | 208 ++++++++------------------ 1 file changed, 64 insertions(+), 144 deletions(-) diff --git a/lib/wsen-pads/examples/station_env.py b/lib/wsen-pads/examples/station_env.py index 098b4f8a..49c4fef7 100644 --- a/lib/wsen-pads/examples/station_env.py +++ b/lib/wsen-pads/examples/station_env.py @@ -1,9 +1,13 @@ """Climate station example using WSEN-PADS, HTS221 and SSD1327 OLED. Reads temperature and pressure from the WSEN-PADS sensor and humidity -from the HTS221 sensor. Includes a startup calibration phase to compensate -sensor offsets. Displays live temperature, humidity and pressure readings -with horizontal progress bars and a comfort index on a round 128x128 OLED. +from the HTS221 sensor. Displays live readings with a comfort index +on the round 128x128 OLED using steami_screen widgets. + +Hardware: + - WSEN-PADS (temperature + pressure) + - HTS221 (humidity) + - SSD1327 128x128 OLED display (round) """ from time import sleep_ms @@ -11,163 +15,79 @@ import ssd1327 from hts221 import HTS221 from machine import I2C, SPI, Pin +from steami_screen import GRAY, GREEN, LIGHT, RED, WHITE, Screen, SSD1327Display from wsen_pads import WSEN_PADS -# === Ecran === +# --- Display --- spi = SPI(1) dc = Pin("DATA_COMMAND_DISPLAY") res = Pin("RST_DISPLAY") cs = Pin("CS_DISPLAY") -display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) +display = SSD1327Display(ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs)) +screen = Screen(display) -# === Capteurs === +# --- Sensors --- i2c = I2C(1) pads = WSEN_PADS(i2c) hts = HTS221(i2c) -# === Calibration === -print("Calibration en cours...") -temps = [] -hums = [] -press = [] -for _ in range(10): - temps.append(pads.temperature()) - hums.append(hts.humidity()) - press.append(pads.pressure_hpa()) - sleep_ms(200) - -OFFSET_TEMP = 20.0 - (sum(temps) / len(temps)) -OFFSET_HUM = 50.0 - (sum(hums) / len(hums)) -OFFSET_PRES = 1013.0 - (sum(press) / len(press)) -print(f"Offset T:{OFFSET_TEMP:.2f} H:{OFFSET_HUM:.2f} P:{OFFSET_PRES:.2f}") -print("Calibration OK") - - -# === Dessin === -def draw_hline(x, y, w, color=15): - for i in range(w): - display.pixel(x + i, y, color) - - -def draw_vline(x, y, h, color=15): - for i in range(h): - display.pixel(x, y + i, color) - - -def draw_rect(x, y, w, h, color=15): - draw_hline(x, y, w, color) - draw_hline(x, y + h - 1, w, color) - draw_vline(x, y, h, color) - draw_vline(x + w - 1, y, h, color) - - -def draw_fill_rect(x, y, w, h, color=15): - for i in range(h): - draw_hline(x, y + i, w, color) - - -def draw_circle(cx, cy, r, color=15): - x = r - y = 0 - err = 0 - while x >= y: - display.pixel(cx + x, cy + y, color) - display.pixel(cx + y, cy + x, color) - display.pixel(cx - y, cy + x, color) - display.pixel(cx - x, cy + y, color) - display.pixel(cx - x, cy - y, color) - display.pixel(cx - y, cy - x, color) - display.pixel(cx + y, cy - x, color) - display.pixel(cx + x, cy - y, color) - y += 1 - err += 1 + 2 * y - if 2 * (err - x) + 1 > 0: - x -= 1 - err += 1 - 2 * x - - -def draw_bar_h(x, y, w, h, value, min_val, max_val, color=13): - draw_rect(x, y, w, h, 5) - ratio = (value - min_val) / (max_val - min_val) - ratio = max(0.0, min(1.0, ratio)) - filled = int((w - 2) * ratio) - if filled > 0: - draw_fill_rect(x + 1, y + 1, filled, h - 2, color) +# --- Comfort thresholds --- +TEMP_MIN, TEMP_MAX = 18.0, 26.0 +HUM_MIN, HUM_MAX = 40.0, 60.0 +PRES_MIN, PRES_MAX = 1000.0, 1025.0 + +POLL_MS = 1000 def comfort_label(temp, hum, pres): - if 18 <= temp <= 26 and 40 <= hum <= 60 and 1000 <= pres <= 1025: - return "IDEAL", 15 - elif temp > 30 or hum > 75: - return "CHAUD", 11 - elif temp < 15 or hum < 25: - return "FROID", 9 - elif pres < 1000: - return "BASSE P", 9 - elif pres > 1025: - return "HAUTE P", 13 - else: - return "CORRECT", 12 + """Return a (label, color) tuple describing indoor comfort.""" + if TEMP_MIN <= temp <= TEMP_MAX and HUM_MIN <= hum <= HUM_MAX and PRES_MIN <= pres <= PRES_MAX: + return "IDEAL", GREEN + if temp > 30 or hum > 75: + return "HOT", RED + if temp < 15 or hum < 25: + return "COLD", GRAY + if pres < PRES_MIN: + return "LOW P", GRAY + if pres > PRES_MAX: + return "HIGH P", LIGHT + return "OK", WHITE def draw_screen(temp, hum, pres): - display.fill(0) - - # Bordure circulaire - draw_circle(64, 64, 62, 4) - draw_circle(64, 64, 60, 2) - - # === TITRE === - display.text("CLIMAT", 35, 20, 15) - draw_hline(19, 29, 90, 6) - - # === TEMPERATURE === - display.text("T", 19, 35, 7) - temp_str = f"{temp:.1f}C" - display.text(temp_str, 29, 35, 15) - draw_bar_h(19, 44, 90, 5, temp, 0, 50, 13) - - # === HUMIDITE === - display.text("H", 19, 52, 7) - hum_str = f"{hum:.1f}%" - display.text(hum_str, 29, 52, 15) - draw_bar_h(19, 61, 90, 5, hum, 0, 100, 11) - - # === PRESSION === - display.text("P", 19, 69, 7) - pres_str = f"{pres:.0f}hPa" - display.text(pres_str, 29, 69, 15) - draw_bar_h(19, 78, 90, 5, pres, 950, 1050, 10) - - # === SEPARATEUR === - draw_hline(19, 86, 90, 4) - - # === CONFORT === - label, c_color = comfort_label(temp, hum, pres) - cx = 64 - len(label) * 4 - display.text(label, cx, 92, c_color) - - # === CAPTEURS === - draw_hline(19, 102, 90, 3) - display.text("PADS+HTS221", 22, 106, 4) - - display.show() - - -# === Boucle principale === -print("Station climatique demarree") -while True: - try: - temp = pads.temperature() + OFFSET_TEMP - hum = hts.humidity() + OFFSET_HUM - pres = pads.pressure_hpa() + OFFSET_PRES + """Render one frame with all readings and comfort index.""" + label, color = comfort_label(temp, hum, pres) + + screen.clear() + screen.gauge( + int(temp), min_val=0, max_val=50, color=color, + ) + screen.title("CLIMATE") + screen.value("{:.1f}".format(temp), unit="C") + screen.subtitle( + "H:{:.0f}% P:{:.0f}hPa".format(hum, pres), + label, + ) + screen.show() + + +# --- Main loop --- +print("Climate station started") +print("Press Ctrl+C to exit.") + +try: + while True: + temp = pads.temperature() + hum = hts.humidity() + pres = pads.pressure_hpa() hum = max(0.0, min(100.0, hum)) - print(f"T:{temp:.2f}C H:{hum:.2f}% P:{pres:.1f}hPa") + print("T:{:.1f}C H:{:.1f}% P:{:.0f}hPa".format(temp, hum, pres)) draw_screen(temp, hum, pres) - except Exception as e: - print("Erreur:", e) - display.fill(0) - display.text("ERREUR", 35, 55, 15) - display.text(str(e)[:16], 0, 70, 9) - display.show() - sleep_ms(1000) + sleep_ms(POLL_MS) +except KeyboardInterrupt: + print("\nClimate station stopped.") +finally: + screen.clear() + screen.show() + pads.power_off() + hts.power_off()