diff --git a/minuteur/bitmaps.h b/minuteur/bitmaps.h new file mode 100644 index 0000000..2d13677 --- /dev/null +++ b/minuteur/bitmaps.h @@ -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 +#include +#include + +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_ diff --git a/minuteur/concepdoc/etats_InsoLab.dot b/minuteur/concepdoc/etats_InsoLab.dot new file mode 100755 index 0000000..9e822aa --- /dev/null +++ b/minuteur/concepdoc/etats_InsoLab.dot @@ -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 +} diff --git a/minuteur/concepdoc/etats_InsoLab.dot~ b/minuteur/concepdoc/etats_InsoLab.dot~ new file mode 100755 index 0000000..4351708 --- /dev/null +++ b/minuteur/concepdoc/etats_InsoLab.dot~ @@ -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 +} diff --git a/minuteur/concepdoc/etats_InsoLab.png b/minuteur/concepdoc/etats_InsoLab.png new file mode 100644 index 0000000..83dfd82 Binary files /dev/null and b/minuteur/concepdoc/etats_InsoLab.png differ diff --git a/minuteur/displays.h b/minuteur/displays.h new file mode 100644 index 0000000..0770924 --- /dev/null +++ b/minuteur/displays.h @@ -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 +#include +#include + + +// 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 +#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_ diff --git a/minuteur/ledRGB.h b/minuteur/ledRGB.h new file mode 100644 index 0000000..a4f9223 --- /dev/null +++ b/minuteur/ledRGB.h @@ -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 +#include +#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 diff --git a/minuteur/minuteur.ino b/minuteur/minuteur.ino new file mode 100644 index 0000000..bbdaf28 --- /dev/null +++ b/minuteur/minuteur.ino @@ -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 +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"); +*/ diff --git a/minuteur/pt.h b/minuteur/pt.h new file mode 100644 index 0000000..d19df7b --- /dev/null +++ b/minuteur/pt.h @@ -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__ */ diff --git a/schéma/schema_minuteur.fzz b/schéma/schema_minuteur.fzz new file mode 100644 index 0000000..2c7e582 Binary files /dev/null and b/schéma/schema_minuteur.fzz differ