Première version du code du minuteur et schéma d'implantation.

This commit is contained in:
2026-05-08 00:37:26 +02:00
parent da3fa03aff
commit 71753703e8
9 changed files with 825 additions and 0 deletions

102
minuteur/bitmaps.h Normal file
View File

@@ -0,0 +1,102 @@
/*----------------------------------------------------------------------
Project: InsoL@b
Author : user@B0 Tetalab.org
File : bitmaps.h 2025-05-21
This work is copyrighted under CERN Open Hardware Licence Version 2
------------------------------------------------------------------------
----------------------------------------------------------------------*/
#ifndef _INSOLAB_BITMAP_H_
#define _INSOLAB_BITMAP_H_
#define LOGO_COLOR 1 // display SSD1306 black
#define LOGO_X0 0 // logo bitmap width offset to center bitmap
#define LOGO_Y0 0 // logo bitmap height offset to center bitmap
#define LOGO_WIDTH 128
#define LOGO_HEIGHT 32
// logoBN128x32.png | 128x32 | 1-bit vertical bytes (LSB=top)
const unsigned char logo_bmp[] PROGMEM = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x0f,
0xa0, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x0f, 0xff, 0xff, 0xff, 0xe8, 0x0f,
0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x0f,
0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x81, 0xff, 0xff, 0xff, 0xe3, 0x81,
0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x81, 0xff, 0xff, 0xff, 0xe3, 0x81,
0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x81, 0xff, 0xff, 0xff, 0xe3, 0x81,
0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xf1, 0xff, 0xff, 0xff, 0xeb, 0xf1,
0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xf1, 0xff, 0xff, 0xff, 0xe3, 0xf1,
0xfe, 0x3f, 0xc7, 0x00, 0x60, 0x00, 0x30, 0x38, 0x03, 0xe3, 0xf1, 0x80, 0x00, 0x03, 0xff, 0xf1,
0xfe, 0x3f, 0xd7, 0x01, 0x60, 0x00, 0xb0, 0xb8, 0x0b, 0xeb, 0xf1, 0xa0, 0x00, 0x0b, 0xff, 0xf1,
0xfe, 0x3f, 0xc7, 0x00, 0x60, 0x00, 0x30, 0x38, 0x03, 0xe3, 0xf1, 0x80, 0x00, 0x03, 0xff, 0xf1,
0xfe, 0x3f, 0xc7, 0x1c, 0x63, 0xfe, 0x31, 0xf8, 0xe3, 0xff, 0xf1, 0x8f, 0xff, 0xe3, 0xe3, 0x81,
0xfe, 0x3f, 0xc7, 0x1c, 0x6b, 0xfe, 0x31, 0xf8, 0xe3, 0xff, 0xf1, 0x8f, 0xff, 0xe3, 0xeb, 0x81,
0xfe, 0x3f, 0xc7, 0x1c, 0x63, 0xfe, 0x31, 0xf8, 0xe3, 0xff, 0xf1, 0x8f, 0xff, 0xe3, 0xe3, 0x81,
0xfe, 0x3f, 0xc0, 0x1c, 0x7f, 0xfe, 0x31, 0xf8, 0xe3, 0xe3, 0x81, 0x8e, 0x00, 0xe3, 0x00, 0x0f,
0xfe, 0x3f, 0xc0, 0x1c, 0x7f, 0xfe, 0x31, 0xfa, 0xe3, 0xeb, 0x81, 0x8e, 0x00, 0xe3, 0x40, 0x0f,
0xfe, 0x3f, 0xc0, 0x1c, 0x7f, 0xfe, 0x31, 0xf8, 0xe3, 0xe3, 0x81, 0x8e, 0x00, 0xe3, 0x00, 0x0f,
0xfe, 0x3f, 0xc7, 0xfc, 0x60, 0x00, 0x31, 0xff, 0xe3, 0x00, 0x0f, 0x8e, 0x38, 0xe3, 0xe3, 0xff,
0xfe, 0x3f, 0xc7, 0xfc, 0x60, 0x00, 0x31, 0xff, 0xe3, 0x40, 0x0f, 0x8e, 0x38, 0xe3, 0xe3, 0xff,
0xfe, 0x3f, 0xc7, 0xfc, 0x60, 0x00, 0x31, 0xff, 0xe3, 0x00, 0x0f, 0x8e, 0x38, 0xe3, 0xe3, 0xff,
0xfe, 0x3f, 0xc7, 0xfc, 0x63, 0xff, 0xf1, 0xff, 0xe3, 0xe3, 0xff, 0x8e, 0x00, 0x03, 0xe3, 0x81,
0xfe, 0x3f, 0xc7, 0xfc, 0x63, 0xff, 0xf1, 0xff, 0xe3, 0xe3, 0xff, 0x8e, 0x00, 0x03, 0xe3, 0x85,
0xfe, 0x3f, 0xc7, 0xfc, 0x63, 0xff, 0xf1, 0xff, 0xe3, 0xe3, 0xff, 0x8e, 0x00, 0x03, 0xe3, 0x81,
0xfe, 0x3f, 0xc7, 0xfc, 0x63, 0xfe, 0x31, 0xff, 0xe3, 0xe3, 0xf1, 0x8f, 0xff, 0xff, 0xe3, 0x8f,
0xfe, 0x3f, 0xc7, 0xfc, 0x63, 0xfe, 0xb1, 0xff, 0xe3, 0xe3, 0xf5, 0x8f, 0xff, 0xff, 0xe3, 0x8f,
0xfe, 0x3f, 0xc7, 0xfc, 0x63, 0xfe, 0x31, 0xff, 0xe3, 0xe3, 0xf1, 0x8f, 0xff, 0xff, 0xe3, 0x8f,
0x80, 0x00, 0xc7, 0xfc, 0x60, 0x00, 0x30, 0x00, 0x03, 0xe0, 0x01, 0x80, 0x00, 0x03, 0xe0, 0x0f,
0xa0, 0x02, 0xd7, 0xfd, 0x68, 0x00, 0x34, 0x00, 0x0b, 0xe8, 0x01, 0xa0, 0x00, 0x0b, 0xe8, 0x0f,
0x80, 0x00, 0xc7, 0xfc, 0x60, 0x00, 0x30, 0x00, 0x03, 0xe0, 0x01, 0x80, 0x00, 0x03, 0xe0, 0x0f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
// RTTTL sounds
#include <anyrtttl.h>
#include <binrtttl.h>
#include <pitches.h>
const char * _missip1_=
"mip:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f";
const char * _count_=
"Pacman:d=32,o=6,b=112:b.5,b.,f#.,d#.,b.,f#.,16p,16d#,c.,c.7,g.,e.,c.7,g.,16p,16e,b.5,b.,f#.,d#.,b.,f#.";
const char * _finish_=
"LightMyFire:d=4,o=5,b=140:8b,16g,16a,8b,8d6,8c6,8b,8a,8g,8a,16f,16a,8c6,8f6,16d6,16c6,16a#,16g,8g#,8g,8g#,16g,16a,8b,8c#6,16b,16a,16g,16f,8e,8f,1a,a";
const char * _logo_ = "Star Trek:o=5,d=16,b=63,b=63:8f.,a#,4d#6.,8d6,a#.,g.,c6.,4f6";
const char * _neon_ =
"Neonligh:d=4,o=5,b=100:8e#7,16p,16d#7,8d#7,8p,16c#7,8c#6,16a#6,8e#7,16p,16d#7,8d#7,8p,8c#7,16a#6,8e#7,16p,16a#6,8a#6,8p,16c#7,8d#7,16e#7,8e#7,16p,8d#7";
void playRTTTL (screen_t itune){
//#ifdef SOUND_ON
switch (itune) { // ugly code to manage SOUND off
case sNONE : noTone(PIEZO); // PIEZO defined in main
return;
case sLOGO : anyrtttl::blocking::play(PIEZO, _logo_); break;
case sTITRE : ; break; //rtune = _missip1_
case sCHOIX : anyrtttl::blocking::play(PIEZO, _neon_); break;
case sEXPOS : anyrtttl::blocking::play(PIEZO, _finish_); break;
case sREGLE : ; break; //tune = _count_
default : ; break;
}
}
/*
// flower
static const unsigned char PROGMEM flower_bmp[] = {
0b00000000, 0b11000000,
0b00000001, 0b11000000,
0b00000001, 0b11000000,
0b00000011, 0b11100000,
0b11110011, 0b11100000,
0b11111110, 0b11111000,
0b01111110, 0b11111111,
0b00110011, 0b10011111,
0b00011111, 0b11111100,
0b00001101, 0b01110000,
0b00011011, 0b10100000,
0b00111111, 0b11100000,
0b00111111, 0b11110000,
0b01111100, 0b11110000,
0b01110000, 0b01110000,
0b00000000, 0b00110000 };
*/
#endif // _INSOLAB_BITMAP_H_

View File

@@ -0,0 +1,77 @@
/*-------------------------------------------------------------------
Project: InsoL@b
Author : user@B0 mail@nowhere.void
File : etats_InsoLab.dot 2026-04-26
This work is copyrighted under CERN Open Hardware Licence Version 2.
----------------------------------------------------------------------
--------------------------------------------------------------------*/
digraph InsoLab {
graph [
rankdir=TB, // Top Bottom, BT,LR,Rl
overlap=false, splines=true, sep="+0.5",
ranksep=1.4, // in top down mode => Vert distance between nodes
nodesep=1.4, // in top down mode => horz distance between nodes
fontname="Arial", fontsize=50,
style=filled, fillcolor="#FFC0A0", color = "#FFC0A0"
];
size="7,6"; // h,l
pad=1; // margins / clusters
label="\nDiagramme d'enchaînement des écrans.";
subgraph cluster_ecrans {
fontsize=60;
style=filled; fillcolor="#FFFFFF";
label="Le minuteur de l'InsoL@b\n";
color = "#FFFFFF";//"#FFC0A0";//"#FFFFFF";
/* =============================== Noeuds ===============================*/
VCC [shape=circle, style="filled", fillcolor="black"];
node [ shape=box,style="filled,setlinewidth(2)",fontsize=30,
afixedsize=true, height=2.5, width=3.8 ];
edge [style="filled,setlinewidth(8)" fontsize=30 arrowsize=2 ]
CHOIX [label="CHOIX\nBoutton avec:\n- exposer UV\n+ régler durées",
fillcolor="#FFA550"];
EXPOS [label="EXPOS\nDécompte de\nla durée avec\nexposition UV",
fillcolor="#FFC0CB:#FF69B4", style=radial];
REGLE [label="REGLE\nRéglages avec:\n- changer le pas\n+ changer la durée",
fillcolor="#FFF080"] ;
node [ shape=box,style="filled,setlinewidth(2)",fontsize=30,
afixedsize=true, height=1.8, width=3.8 ];
LOGO [label="LOGO\nÉcran d'accueil\naffiché 4s", fillcolor="#FFFFFF"];
TITRE [label="TITRE\nAttente appui\nd'un boutton", fillcolor="#7CFC60"];
DUREE [label="DUREE\nChange la\ndurée sous UV", fillcolor="#FF98FF"] ;
INCRE [label="INCRE\nChange le pas\nréglant la durée", fillcolor="aqua"];
{ rank = same LOGO TITRE INCRE }
{ rank = same EXPOS CHOIX REGLE } //node [group = "R"] //REGLE; DUREE; INCRE;
node [shape=box, afixedsize=true, height=.7, width=3.8];
Réveil ; Veille;//[shape=box, afixedsize=true, height=.8, width=3.8];
/* ================================ Arcs ================================*/
VCC:s -> LOGO:n [ taillabel ="Mise\nsous\ntension " ];
Réveil:s -> TITRE:n [ taillabel= "\nSortie \nde veille ",label=" Bouton +" ];
LOGO:e -> TITRE:w [ taillabel =" Fin délai\n" ];
TITRE:e -> INCRE:w [ style=invis];
TITRE:s -> CHOIX:n [ taillabel ="\nBoutton + " ] ;
CHOIX:w -> EXPOS:e [ taillabel ="\nBoutton - " ];
CHOIX:e -> REGLE:w [ taillabel =" Boutton +" ] ;
CHOIX:s -> Veille:n [
taillabel= "\nMise en veille au\ndélai d'activité expiré " ];
EXPOS:n -> CHOIX:nw [ taillabel ="Fin du\ndécompte\n" ];
EXPOS:n -> LOGO:s [ style=invis];
REGLE:n -> INCRE:s [ taillabel=" - \n\n" ];
REGLE:s -> CHOIX:se [ taillabel ="\nFin délai inactif " ];
REGLE:s -> DUREE:n [ taillabel="\n + " ];
INCRE:w -> INCRE:nw [ taillabel="- " ];
INCRE:e -> INCRE:ne [ taillabel=" +" ];
INCRE:se -> REGLE:ne [ headlabel="\n\n Fin\n délai" ];
DUREE:w -> DUREE:sw [ taillabel="- \n\n" ];
DUREE:e -> DUREE:se [ taillabel=" +\n\n" ];
DUREE:ne -> REGLE:se [ headlabel=" Fin\n délai" ];
{ rank = same Veille DUREE };
EXPOS -> Veille [ style=invis];
Veille:e -> DUREE:w [ style=invis];
// n e s w = top right bottom left decorate=true =>line between label & edge
} // cluster_ecrans
}

View File

@@ -0,0 +1,77 @@
/*-------------------------------------------------------------------
Project: InsoL@b
Author : user@B0 mail@nowhere.void
File : etats_InsoLab.dot 2026-04-26
This work is copyrighted under CERN Open Hardware Licence Version 2.
----------------------------------------------------------------------
--------------------------------------------------------------------*/
digraph InsoLab {
graph [
rankdir=TB, // Top Bottom, BT,LR,Rl
overlap=false, splines=true, sep="+0.5",
ranksep=1.4, // in top down mode => Vert distance between nodes
nodesep=1.4, // in top down mode => horz distance between nodes
fontname="Arial", fontsize=50,
style=filled, fillcolor="#FFC0A0", color = "#FFC0A0"
];
size="7,6"; // h,l
pad=1; // margins / clusters
label="\nDiagramme d'enchaînement des écrans.";
subgraph cluster_ecrans {
fontsize=60;
style=filled; fillcolor="#FFFFFF";
label="Le minuteur de l'InsoL@b\n";
color = "#FFFFFF";//"#FFC0A0";//"#FFFFFF";
/* =============================== Noeuds ===============================*/
VCC [shape=circle, style="filled", fillcolor="black"];
node [ shape=box,style="filled,setlinewidth(2)",fontsize=30,
afixedsize=true, height=2.5, width=3.8 ];
edge [style="filled,setlinewidth(8)" fontsize=30 arrowsize=2 ]
CHOIX [label="CHOIX\nBoutton avec:\n- exposer UV\n+ régler durées",
fillcolor="#FFA500"];
EXPOS [label="EXPOS\nDécompte de\nla durée avec\nexposition UV",
fillcolor="#FFC0CB:#FF69B4", style=radial];
REGLE [label="REGLE\nRéglages avec:\n- changer le pas\n+ changer la durée",
fillcolor="#FFF030"] ;
node [ shape=box,style="filled,setlinewidth(2)",fontsize=30,
afixedsize=true, height=1.8, width=3.8 ];
LOGO [label="LOGO\nÉcran d'accueil\naffiché 4s", fillcolor="#FFFFFF"];
TITRE [label="TITRE\nAttente choix\ndu boutton +", fillcolor="#7CFC00"];
DUREE [label="DUREE\nChange la\ndurée sous UV", fillcolor="#FFF030"] ;
INCRE [label="INCRE\nChange le pas\nréglant la durée", fillcolor="#FFF030"];
{ rank = same LOGO TITRE INCRE }
{ rank = same EXPOS CHOIX REGLE } //node [group = "R"] //REGLE; DUREE; INCRE;
node [shape=box, afixedsize=true, height=.7, width=3.8];
Réveil ; Veille;//[shape=box, afixedsize=true, height=.8, width=3.8];
/* ================================ Arcs ================================*/
VCC:s -> LOGO:n [ taillabel ="Mise\nsous\ntension " ];
Réveil:s -> TITRE:n [ taillabel= "\nSortie \nde veille ",label=" Bouton +" ];
LOGO:e -> TITRE:w [ taillabel =" Fin délai\n" ];
TITRE:e -> INCRE:w [ style=invis];
TITRE:s -> CHOIX:n [ taillabel ="\nBoutton + " ] ;
CHOIX:w -> EXPOS:e [ taillabel ="\nBoutton - " ];
CHOIX:e -> REGLE:w [ taillabel =" Boutton +" ] ;
CHOIX:s -> Veille:n [
taillabel= "\nMise en veille au\ndélai d'activité expiré " ];
EXPOS:n -> CHOIX:nw [ taillabel ="Fin du\ndécompte\n" ];
EXPOS:n -> LOGO:s [ style=invis];
REGLE:n -> INCRE:s [ taillabel=" - \n\n" ];
REGLE:s -> CHOIX:se [ taillabel ="\nFin délai inactif " ];
REGLE:s -> DUREE:n [ taillabel="\n + " ];
INCRE:w -> INCRE:nw [ taillabel="- " ];
INCRE:e -> INCRE:ne [ taillabel=" +" ];
INCRE:se -> REGLE:ne [ headlabel="\n\n Fin\n délai" ];
DUREE:w -> DUREE:sw [ taillabel="- \n\n" ];
DUREE:e -> DUREE:se [ taillabel=" +\n\n" ];
DUREE:ne -> REGLE:se [ headlabel=" Fin\n délai" ];
{ rank = same Veille DUREE };
EXPOS -> Veille [ style=invis];
Veille:e -> DUREE:w [ style=invis];
// n e s w = top right bottom left decorate=true =>line between label & edge
} // cluster_ecrans
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

122
minuteur/displays.h Normal file
View File

@@ -0,0 +1,122 @@
/*----------------------------------------------------------------------
Project: InsoL@b
Author : user@B0 Tetalab.org
File : displays.h 2026-04-21
This work is copyrighted under CERN Open Hardware Licence Version 2
------------------------------------------------------------------------
Management of the I2C oled SH1306 for RP2040 Zero clone
----------------------------------------------------------------------*
------------------ Functions for Oled display ---------------------*/
#ifndef _INSOLAB_DISPLAYS_H_
#define _INSOLAB_DISPLAYS_H_
#include "bitmaps.h"
#include "ledRGB.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
// Declaration for SSD1306 display on I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// Default on RP2040 Zero: SDA GPIO 4 SCL GPIO 5
#include <Adafruit_SSD1306.h>
#define OLED_WIDTH 128 // OLED display width, in pixels
#define OLED_RST -1 // Reset pin
#ifdef ESSAIS
#define OLED_HEIGHT 64 // OLED display height, in pixels
#define OLED_ADR 0x3D //Address 0x3D/0x3C to 128x64, 0x3C to 128x32
#else
#define OLED_HEIGHT 32 // OLED display height, in pixels
#define OLED_ADR 0x3C
#endif
Adafruit_SSD1306 oled(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_RST);
char _oledmsg[32]; // general purpose character buffer
void showTxt1(uint8_t x, uint8_t y, char* txt) {
oled.clearDisplay();
oled.setTextSize(1); oled.setTextColor(SSD1306_WHITE);
oled.setCursor(x,y); // O,0 at top-left corner
sprintf(_oledmsg,"CR:%s",txt);
oled.println(txt);
oled.display();
}
void showText(String l1, String l2) {
oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE);
oled.setTextSize(2); oled.setCursor(0,0); oled.println(l1);
oled.setTextSize(1); oled.setCursor(0,18); oled.println(l2);
oled.display();
}
void showInit() {
ledInit(); ledRGB(BLANK); //ledRGB(RED);
if(!oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADR)) {
Serial.begin(115200); delay(100);
Serial.println(F("SSD1306 allocation failed"));
ledRGB(RED);
for(;;); // Don't proceed, loop forever
}
//sprintf(_oledmsg, "SSD1306 @ %2d OK", OLED_ADR); oledTxt1(0,0,_oledmsg);
delay(100); // Serial.println(_oledmsg);
}
void showLOGO(int16_t gt) { /*--------- logo on SSD1306 -------------*/
oled.clearDisplay();
oled.drawBitmap(
LOGO_X0, LOGO_Y0,logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, LOGO_COLOR);
oled.display();
playRTTTL(sLOGO);
delay(gt);
}
void showTITRE() { // TextSize(2)=>10char, TextSize(1)=>22char,
oled.clearDisplay();
oled.setCursor(0,0); oled.setTextSize(2); // doble scale
oled.setTextColor(SSD1306_WHITE);
oled.println(F("InsoLab UV")); // 10 chars max
oled.setTextSize(1); oled.setCursor(0,18); // Normal 1:1 pixel scale
oled.println(F(" Any button to start")); // 21 chars max
oled.display();
}
void showCHOIX(uint16_t t) {
oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE);
oled.setCursor(0,0); oled.setTextSize(2);
sprintf(_oledmsg,"Expo: %3ds",t); // 10 chars max
oled.println(_oledmsg);
oled.setCursor(0,18); oled.setTextSize(1); // 21 chars max
oled.println(F("- goto UV, + settings"));
oled.display();
}
void showEXPOS(uint16_t t, int16_t m) {
oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE);
oled.setCursor(0,0); oled.setTextSize(2);
sprintf(_oledmsg,"Count:%3ds",t); oled.println(_oledmsg);
oled.setCursor(0,18); oled.setTextSize(1);
sprintf(_oledmsg,"- stop, + add up %3ds",m); oled.println(_oledmsg);
oled.display(); delay(200);
}
void showREGLE() {
oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE);
oled.setCursor(0,0); oled.setTextSize(2);
oled.println(F("Set values"));
oled.setCursor(0,18); oled.setTextSize(1);
sprintf(_oledmsg,"- UVtime, + step time"); oled.println(_oledmsg);
oled.display();
}
void showDUREE(uint16_t t) {
oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE);
oled.setCursor(0,0); oled.setTextSize(2);
sprintf(_oledmsg,"Time: %3ds",t); oled.println(_oledmsg);
oled.setCursor(0,18); oled.setTextSize(1);
sprintf(_oledmsg,"- less, + more"); oled.println(_oledmsg);
oled.display();
}
void showINCRE(uint16_t t) {
oled.clearDisplay(); oled.setTextColor(SSD1306_WHITE);
oled.setCursor(0,0); oled.setTextSize(2);
sprintf(_oledmsg,"Step: %3d",t); oled.println(_oledmsg);
oled.setCursor(0,18); oled.setTextSize(1);
sprintf(_oledmsg,"- less, + more"); oled.println(_oledmsg);
oled.display();
}
#endif // _INSOLAB_DISPLAYS_H_

115
minuteur/ledRGB.h Normal file
View File

@@ -0,0 +1,115 @@
/*-------------------------------------------------------------------
Project: InsoL@b
Author : user@B0 mail@nowhere.void
File : led_RGB.h 2026-04-23
This work is copyrighted under CERN Open Hardware Licence Version 2.
----------------------------------------------------------------------
Aqua #00FFFF 0% 100% 100% 180° 100% 50% 100% 100% 11 (high cyan); cyan
Teal #008080 0% 50% 50% 180° 100% 25% 100% 50% 03 (low cyan)
Blue #0000FF 0% 0% 100% 240° 100% 50% 100% 100% 09 (high blue)
Navy #000080 0% 0% 50% 240° 100% 25% 100% 50% 01 (low blue)
Fuchsia #FF00FF 100% 0% 100% 300° 100% 50% 100% 100% 13 (high magenta); magenta
Purple #800080 50% 0% 50% 300° 100% 25% 100% 50% 05 (low magenta)
---------------------------------------------------------------------*/
#ifndef _INSOLAB_LEDRGB_H_
#define _INSOLAB_LEDRGB_H_
#include <SPI.h>
#include <Adafruit_NeoPixel.h>
#define PIN 16 // RGB LED (WS2812) clone of Waveshare rp2040-zero.
#define NUMPIXELS 1
Adafruit_NeoPixel pixels(
NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
enum {NONE, RED, YELLOW, ORANGE, GREEN, BLUE, PINK, FUCHS, BLANK};
int ledColor = NONE;
int ledMax=2;
void ledInit() {
pixels.begin();
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.show();
delay(100);
}
void ledRGB(int rgb) {
switch (rgb) {
case RED: pixels.setPixelColor(0, pixels.Color(ledMax, 0, 0));
break;
case YELLOW: pixels.setPixelColor(0, pixels.Color(ledMax, ledMax, 0));
break;
case ORANGE: pixels.setPixelColor(0, pixels.Color(ledMax, 1, 0));
break;
case GREEN: pixels.setPixelColor(0, pixels.Color(0, ledMax, 0));
break;
case BLUE: pixels.setPixelColor(0, pixels.Color(0, ledMax, ledMax)); // AQUA
break;
case PINK: pixels.setPixelColor(0, pixels.Color(1,0,3)); // (1,0,2));
break; // pink color (255, 192, 203) deep pink (255, 20, 147)
case FUCHS: pixels.setPixelColor(0, pixels.Color(ledMax,0,ledMax)); // FUCHSIA
break;
case BLANK: pixels.setPixelColor(0, pixels.Color(1, 1, 1));
break;
case NONE: pixels.setPixelColor(0, pixels.Color(0, 0, 0));
break;
}
pixels.show(); delay(100);
}
void ledScale(int rgb, int first, int aim, int inc, int period) {
int i = first;
do {
i = i + inc;
switch (rgb) {
case RED:
pixels.setPixelColor(0, pixels.Color(i, 0, 0));
break;
case GREEN:
pixels.setPixelColor(0, pixels.Color(0, i, 0));
break;
case BLUE:
pixels.setPixelColor(0, pixels.Color(0, 0, i));
break;
case BLANK:
pixels.setPixelColor(0, pixels.Color(i, i, i));
break;
}
pixels.show(); delay(period);
} while (i != aim );
}
uint16_t hue = 0; uint8_t sat = 0, val = 0;
int32_t couleur = 0;
void ledTest() {
int period=12;
switch (ledColor) {
case NONE:
for (val = 10; val < 50; val += 10) {
for (sat = 10; sat < 50; sat += 10) {
couleur = pixels.ColorHSV(765, sat, val);
pixels.setPixelColor(0, couleur);
pixels.show();
delay(6);
}
}
break;
case RED:
ledScale(RED, 1, 200, 1, period);
ledScale(RED, 200, 1, -1, period);
break;
case GREEN:
ledScale(GREEN, 1, 200, 1, period);
ledScale(GREEN, 200, 1, -1, period);
break;
case BLUE:
ledScale(BLUE, 1, 200, 1, period);
ledScale(BLUE, 200, 1, -1, period);
break;
default:
break;
}
ledColor++;
if (ledColor == 4) {
ledColor = NONE;
}
delay(1000);
}
#endif

252
minuteur/minuteur.ino Normal file
View File

@@ -0,0 +1,252 @@
/*-----------------------------------------------------------------------
Project: InsoL@b
Author : user@B0 mail@nowhere.void
File : minuteur.ino 2026-04-19
This work is copyrighted under the CERN Open Hardware Licence Version 2.
-------------------------------------------------------------------------
Serial port not available ? sudo fuser -k /dev/ttyACM0 & RST or Boot
-------------------------------------------------------------------------
* Power on: Greeting screen LOGO waits for timout and go to screen TITRE
- Show InsoL@b logo
* screen TITRE
- Show title & wait for any button to continue to Screen CHOIX
* Screen CHOIX
- displays default/curent exposure time
- minus button go to screen EXPO exposure timing sequence
- plus button go to setting screen REGLE
* Screen EXPO
- UV exposure
- display count down, minus button to stop, plus to increment time
- when done exit back to screen CHOIX
* Screen REGLE
- minus button go to screen INCRE
- plus button go to screen DUREE
- return to caller on time out
* Screen DURE
- minus decrement exposure time
- plus increment exposure time
- return to caller on time out
* Screen INCRE
- minus decrement step value
- plus increment step value
- return to caller on time out
-----------------------------------------------------------------------*/
#define BUTTONPLUS 14 // PullUp 10K I=.33 mA
#define BUTTONMINUS 15 // PullUp 10K I=.33 mA
#define RELAY 27 // Must be < 4mA
#define PIEZO 28 // Used by anyrtttl.h in bitmaps.h
/* I2C SDA & CLK PullUp 2*10K I=.66 mA
Sur le RP2040 le défaut du courant de sortie est limité à 4mA/broche IO,
C'est configurable par logiciel pour atteindre 2mA, 8mA ou 12mA.
Le courant maximal combiné que toutes les broches GPIO peuvent sourcer ou
absorber est limité à 50 mA. Au total le courant total doit être < à 50mA.
*/
//#define ESSAIS
enum screen_t {sLOGO,sTITRE,sCHOIX,sEXPOS,sREGLE,sDUREE,sINCRE,sNONE};
#ifdef ESSAIS
int16_t defaultTime=80, stepTime=5, timeOut=3; // expo & setting time
#else
int16_t defaultTime=180, stepTime=10, timeOut=2;
#endif
int16_t greetTime=1000;
// display limit to 999s => 16mn 39s
volatile uint16_t setTime=defaultTime;
volatile uint16_t downTime; // count down time
screen_t screen=sTITRE;
#include "displays.h" // Oled functions & led RGB
#include "pt.h" // proto threads
//#include "scroll.h" // circular buffer for displays
/*----------------- CPU 0 ---------------------------------*/
void die() {ledRGB(RED); for(;;); }
void setup() { showInit(); showLOGO(greetTime); ledRGB(GREEN);
}
void loop() { // CPU 0 dedicated to refresh display
// TODO not recall screen if values did not change
switch(screen) {
case sTITRE : showTITRE(); break;
case sCHOIX : showCHOIX(setTime); break;
case sEXPOS : showEXPOS(downTime,stepTime); break;
case sREGLE : showREGLE(); break;
case sDUREE : showDUREE(setTime); break;
case sINCRE : showINCRE(stepTime); break;
default : ledRGB(RED); delay(3000); break; // Code error
}
delay(100);
}
/*----------------- CPU 1 ---------------------------------*/
// ProtoThreads local continuation structure
static pt_t pMINUS, pPLUS, pTITRE, pCHOIX, pEXPOS, pREGLE, pDUREE, pINCRE;
volatile boolean plusPushed=false; volatile boolean minusPushed=false;
void setup1() { // CPU 1 to manage buttons
// ProtoThreads continuation structure is set to 0
PT_INIT(&pMINUS); PT_INIT(&pPLUS);
PT_INIT(&pTITRE); PT_INIT(&pCHOIX); PT_INIT(&pEXPOS);
PT_INIT(&pREGLE); PT_INIT(&pINCRE); PT_INIT(&pDUREE);
// IO
pinMode(BUTTONMINUS, INPUT_PULLUP); pinMode(BUTTONPLUS, INPUT_PULLUP);
pinMode(RELAY, OUTPUT); digitalWrite(RELAY, LOW);delay(500);
}
static PT_THREAD(pPlus(pt_t *lc)) { // OK and avoid bounces
PT_BEGIN(lc); // lc=local continuation byte, here set to previous call value
while (1) {
PT_WAIT_UNTIL(lc, digitalRead(BUTTONPLUS) == LOW); // lc set to __LINE__
plusPushed=true;
}
PT_END(lc); // to try: exit & return flag ?
}
static PT_THREAD(pMinus(pt_t *lc)) {
PT_BEGIN(lc);
while (1) {
PT_WAIT_UNTIL(lc, digitalRead(BUTTONMINUS) == LOW);
minusPushed=true;
}
PT_END(lc);
}
static PT_THREAD(pTitre(pt_t *lc)) {
PT_BEGIN(lc);
PT_WAIT_UNTIL(lc, (screen==sTITRE));
PT_WAIT_UNTIL(lc, (plusPushed || minusPushed ));
minusPushed=false; plusPushed=false;
screen=sCHOIX;
PT_DELAY(lc, 100);
PT_END(lc);
}
static PT_THREAD(pChoix(pt_t *lc)) { // TODO sleep & wake up
PT_BEGIN(lc);
PT_WAIT_UNTIL(lc, (screen==sCHOIX));
ledRGB(ORANGE);
PT_WAIT_UNTIL(lc, (plusPushed || minusPushed ) );
if (minusPushed) { minusPushed=false; screen=sEXPOS; }
if (plusPushed) { plusPushed=false; screen=sREGLE; }
PT_DELAY(lc, 100);
PT_END(lc);
}
static PT_THREAD(pExpos(pt_t *lc)) { // run count down UV light OK
PT_BEGIN(lc);
PT_WAIT_UNTIL(lc, (screen==sEXPOS) );
ledRGB(PINK);
downTime = setTime;
playRTTTL(sCHOIX);
digitalWrite(RELAY, HIGH);
minusPushed=false; plusPushed=false;
do { // count down loop
// playRTTTL(sCOUNT); send flag to CPU 0 ? or non blocking RTTTL
if (plusPushed) {
plusPushed=false;
if (downTime < 1000 - stepTime) downTime += stepTime;
}
PT_DELAY(lc, 800);
downTime -=1;
if (minusPushed) { minusPushed=false; downTime = 0; }
PT_DELAY(lc, 200);
} while ( downTime > 0);
digitalWrite(RELAY, LOW );
screen=sCHOIX;
playRTTTL(sEXPOS);
PT_END(lc);
}
static PT_THREAD(pRegle(pt_t *lc)) { // OK
static unsigned long ttl,to=500 + timeOut * 1000;
PT_BEGIN(lc);
PT_WAIT_UNTIL(lc, (screen==sREGLE) );
ledRGB(YELLOW);
minusPushed=false; plusPushed=false;
ttl=millis()+to;
PT_WAIT_UNTIL(lc, (plusPushed || minusPushed || (millis() > ttl) ) );
if (plusPushed) { plusPushed=false; screen=sDUREE; }
else if (minusPushed) { minusPushed=false; screen=sINCRE; }
if (millis() > ttl) screen=sCHOIX;
PT_DELAY(lc, 100);
PT_END(lc);
}
static PT_THREAD(pIncre(pt_t *lc)) { // OK
static unsigned long ttl, to=timeOut * 1000;
PT_BEGIN(lc);
PT_WAIT_UNTIL(lc, (screen==sINCRE) );
ledRGB(BLUE);
PT_DELAY(lc, 1000);
ttl=millis()+to;
minusPushed=false; plusPushed=false;
do {
PT_WAIT_UNTIL(lc, (plusPushed || minusPushed || (millis() > ttl)) );
if (plusPushed) {
plusPushed=false;
if (stepTime < 999) stepTime += 1 ;
ttl=millis()+to;
} else
if (minusPushed) {
minusPushed=false;
if (stepTime >1) stepTime-= 1 ;
ttl=millis()+to;
}
if (millis() > ttl) screen=sREGLE;
} while (screen==sINCRE);
PT_END(lc);
}
static PT_THREAD(pDuree(pt_t *lc)) { // OK
static unsigned long ttl, to=timeOut * 1000;
PT_BEGIN(lc);
PT_WAIT_UNTIL(lc, (screen==sDUREE) );
ledRGB(FUCHS);
PT_DELAY(lc, 1000);
ttl=millis()+to;
do {
PT_WAIT_UNTIL(lc, (plusPushed || minusPushed || (millis() > ttl)) );
if (plusPushed) {
plusPushed=false;
if (setTime < 1000 - stepTime) setTime += stepTime ;
ttl=millis()+to;
}
if (minusPushed) {
minusPushed=false;
if (setTime > stepTime) setTime -= stepTime ;
ttl=millis()+to;
}
if (millis() > ttl) screen=sREGLE;
} while (screen==sDUREE);
PT_END(lc);
}
void loop1() { // CPU 1 dedicated to run threads
pMinus (&pMINUS); // process minus button
pPlus (&pPLUS); // process plus button
pTitre (&pTITRE); // process screen TITRE
pChoix (&pCHOIX); // process screen CHOIX
pExpos (&pEXPOS); // process screen EXPOS
pRegle (&pREGLE); // process screen REGLE
pIncre (&pINCRE); // process screen INCRE
pDuree (&pDUREE); // process screen DUREE
}
/*
* provisions
#include <EEPROM.h>
v
void setup() {
EEPROM.begin(512);
// write a 0 to all 512 bytes of the EEPROM
for (int i = 0; i < 512; i++) {
EEPROM.write(i, 0);
}
// turn the LED on when we're done
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
EEPROM.end();
}
void loop() {
static int x = 0;
Serial.printf("C1: Stay on target...\n");
val++;
if (++x < 10) {
EEPROM.begin(512);
EEPROM.write(0,x);
EEPROM.commit();
}
delay(1000);
}
* code debug
Serial.begin(115200); delay(500); Serial.printf("CPU 0 on\n");
Serial.printf("CPU 1 on\n");
*/

80
minuteur/pt.h Normal file
View File

@@ -0,0 +1,80 @@
/*--------------------------------------------------------
Projet: InsoL@b
File : pt.h 08/28/20
Authors: initial work of Adam Dunkels, user@W500;
little dressing & minor changes to limit compile warnings
----------------------------------------------------------*/
#ifndef __PT_H__
#define __PT_H__
typedef struct pt { unsigned short lc; } pt_t ;
#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED 2
#define PT_ENDED 3
#define PT_INIT(pt) (pt)->lc = 0; // INIT
#define PT_THREAD(name_args) char name_args // THREAD
#define PT_BEGIN(pt) { \
/*char PT_YIELD_FLAG = 1 ; not used here */ \
switch((pt)->lc) \
{ case 0: // BEGIN
#define PT_END(pt) ((pt)->lc = 0 ); }; \
/*PT_YIELD_FLAG = 0; not used here */ \
PT_INIT(pt); return PT_ENDED; } // END
#define PT_WAIT_UNTIL(pt, condition) \
do { \
(pt)->lc = __LINE__; \
case __LINE__: \
if(!(condition)) { \
return PT_WAITING; \
} \
} while(0) // WAIT_UNTIL
#define PT_WAIT_WHILE(pt, cond) \
PT_WAIT_UNTIL((pt), !(cond)) // WAIT_WHILE
#define PT_WAIT_THREAD(pt, thread) \
PT_WAIT_WHILE((pt), PT_SCHEDULE(thread)) // WAIT_THREAD
#define PT_SPAWN(pt, child, thread) \
do { \
PT_INIT((child)); \
PT_WAIT_THREAD((pt), (thread)); \
} while(0) // SPAWN
#define PT_RESTART(pt) \
do { \
PT_INIT(pt); \
return PT_WAITING; \
} while(0) // RESTART
#define PT_EXIT(pt) \
do { \
PT_INIT(pt); \
return PT_EXITED; \
} while(0) // EXIT
#define PT_SCHEDULE(f) ((f) < PT_EXITED) // SCHEDULE
#define PT_YIELD(pt) \
do { \
PT_YIELD_FLAG = 0; \
(pt)->lc = __LINE__; \
case __LINE__: \
if(PT_YIELD_FLAG == 0) { \
return PT_YIELDED; \
} \
} while(0) // YIELD
#define PT_YIELD_UNTIL(pt, cond) \
do { \
PT_YIELD_FLAG = 0; \
(pt)->lc = __LINE__; \
case __LINE__: \
if((PT_YIELD_FLAG == 0) || !(cond)) { \
return PT_YIELDED; \
} \
} while(0) // YIELD_UNTIL
#define PT_DELAY(pt, ms) \
do { \
static unsigned long _PTTTL_ ; \
_PTTTL_ = millis() + (unsigned int)(ms); \
PT_WAIT_UNTIL((pt), (millis() > _PTTTL_)); \
} while(0) // DELAY
#define PT_SYNC(pt, mark, ms) \
do { \
PT_WAIT_UNTIL((pt), (millis() - (mark) > (ms) )); \
} while(0) // SYNC
#endif /* __PT_H__ */