/*----------------------------------------------------------------------- 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 * Sleep mode not stable, forum at : "https://github.com/earlephilhower/arduino-pico/issues/345" ----------------------------------------------------------------------- Mod: 2026/05/27 added 2 bitmap to sreen saver -----------------------------------------------------------------------*/ #define BUTTONPLUS 14 // PullUp 10K I=.33 mA #define BUTTONMINUS 15 // PullUp 10K I=.33 mA #define RELAY 27 // Must be < 4mA #define PIEZO 8 // 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 SENSOR_HALL #define S_HALL 7 // Sense Hall sensor //#define ESSAIS enum screen_t {sLOGO,sTITRE,sCHOIX,sEXPOS,sREGLE,sDUREE,sINCRE,sSAUVE,sVEILLE,sNONE}; #ifdef ESSAIS int16_t defaultTime=80, stepTime=5, timeOut=3; // expo & setting time int16_t snoozeTimeOut=6; #else int16_t defaultTime=180, stepTime=10, timeOut=4; int16_t snoozeTimeOut=60; //60 #endif int16_t greetTime=2000; // display limit to 999s => 16mn 39s volatile uint16_t setTime=defaultTime; volatile uint16_t downTime; // count down time volatile uint16_t lastSetTime, lastStepTime, time2save=0; volatile boolean plusPushed=false; volatile boolean minusPushed=false; char sMsg[16]; screen_t screen=sNONE; #include #include "displays.h" // Oled functions & led RGB #include "pt.h" // proto threads //#include "scroll.h" // circular buffer for displays #define h42 0x2A #define h43 0x2B #define add42 254 #define add43 255 /*----------------- CPU 0 ---------------------------------*/ void die() {ledRGB(RED); for(;;); } bool isRamSet() { byte val1,val2; EEPROM.begin(256); //clearRAM(); val1 = EEPROM.read(add42); val2 = EEPROM.read(add43); if (val1==h42 && val2==h43) return true; // allready writed EEPROM.write(add42, h42); // 42 EEPROM.write(add43, h43); // 43 EEPROM.put(0, setTime); EEPROM.put(4, stepTime); EEPROM.commit(); // WIP? if (EEPROM.commit()) { do something ? } return false; } void SetTime2RAM() { EEPROM.put(0, setTime); EEPROM.commit(); } void StepTime2RAM() { EEPROM.put(4, stepTime); EEPROM.commit(); } void clearRAM() { for(int addr = 0; addr < 256; addr++) { EEPROM.write(addr, 0); } EEPROM.commit(); } void setup() { showInit(); sprintf(sMsg,"Value"); // read memory if (isRamSet()) { // setting values to be found in memory EEPROM.get(0, setTime); EEPROM.get(4, stepTime); #ifdef ESSAIS char l1[16], l2[16], l3[16]; sprintf(l1,"RAM time values"); sprintf(l2,"set: %3ds",setTime); sprintf(l3,"step: %3ds",stepTime); showInfo(l1, l2, l3); delay(5000); #endif } // EEPROM.end(); lastSetTime=setTime; lastStepTime=stepTime; showLOGO(greetTime); minusPushed=false; plusPushed=false; // in case button pushed while LOGO screen=sTITRE; } 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; case sSAUVE : showSAUVE(time2save,sMsg); break; case sVEILLE : delay(100); break; // snooze screen case sNONE : delay(100); break; // nothing to change yet, waiting for next screen default : ledRGB(RED); delay(6000); break;// Code error } delay(100); } /*----------------- CPU 1 ---------------------------------*/ // ProtoThreads local continuation structure static pt_t pMINUS,pPLUS,pALARM,pTITRE,pCHOIX,pEXPOS, pREGLE,pDUREE,pINCRE,pSAUVE,pVEILLE; void setup1() { // CPU 1 to manage buttons // ProtoThreads continuation structure is set to 0 PT_INIT(&pMINUS); PT_INIT(&pPLUS); PT_INIT(&pALARM); PT_INIT(&pTITRE); PT_INIT(&pCHOIX); PT_INIT(&pEXPOS); PT_INIT(&pREGLE); PT_INIT(&pINCRE); PT_INIT(&pDUREE); PT_INIT(&pSAUVE); PT_INIT(&pVEILLE); // IO pinMode(BUTTONMINUS, INPUT_PULLUP); pinMode(BUTTONPLUS, INPUT_PULLUP); pinMode(RELAY, OUTPUT); digitalWrite(RELAY, LOW);delay(500); #ifdef SENSOR_HALL pinMode (S_HALL, INPUT_PULLUP); // Enable pull-up as sensor output is open collector #endif } 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(pAlarm(pt_t *lc)) { //WIP PT_BEGIN(lc); // PT_WAIT_UNTIL(lc, (screen==sEXPOS) ); if (digitalRead(S_HALL)) ledRGB(RED); else ledRGB(GREEN); PT_DELAY(lc, 400); // reading hall sensor // PT_WAIT_UNTIL(lc, digitalRead(S_HALL));// Pullup pin: switch <=> magnet // digitalWrite(RELAY, LOW ); } PT_END(lc); } static PT_THREAD(pTitre(pt_t *lc)) { PT_BEGIN(lc); PT_WAIT_UNTIL(lc, (screen==sTITRE)); ledRGB(GREEN); 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 static unsigned long ttl,to= snoozeTimeOut * 1000; PT_BEGIN(lc); PT_WAIT_UNTIL(lc, (screen==sCHOIX)); ledRGB(ORANGE); minusPushed=false; plusPushed=false; ttl=millis()+to; PT_WAIT_UNTIL(lc, (plusPushed || minusPushed || (millis() > ttl) ) ); if (minusPushed) { minusPushed=false; screen=sEXPOS; } if (plusPushed) { plusPushed=false; screen=sREGLE; } if (millis() > ttl) screen=sVEILLE; //screen=sTITRE; // 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 if (plusPushed) { plusPushed=false; if (downTime < 1000 - stepTime) downTime += stepTime; } PT_DELAY(lc, 600); downTime -=1; if (minusPushed) { minusPushed=false; downTime = 0; } PT_DELAY(lc, 310); playClk(); // this take ~100ms } while ( downTime > 0); digitalWrite(RELAY, LOW ); screen=sCHOIX; ledRGB(ORANGE); 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); lastStepTime = stepTime; 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=sNONE; } while (screen==sINCRE); if (lastStepTime == stepTime) screen=sREGLE; else { sprintf(sMsg,"Step:"); screen=sSAUVE; } 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); lastSetTime = setTime; 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=sNONE; } while (screen==sDUREE); if (lastSetTime == setTime) screen=sREGLE; else { sprintf(sMsg,"Time:"); screen=sSAUVE; } PT_END(lc); } static PT_THREAD(pSauve(pt_t *lc)) { // static unsigned long ttl, to; to = (timeOut+4) * 1000; PT_BEGIN(lc); PT_WAIT_UNTIL(lc, (screen==sSAUVE) ); if (lastSetTime != setTime) time2save=setTime; // value for screen sSAUVE else if (lastStepTime != stepTime) time2save=stepTime; ledRGB(ROSE); PT_DELAY(lc, 1000); ttl=millis()+to; do { PT_WAIT_UNTIL(lc, (plusPushed || minusPushed || (millis() > ttl)) ); if (plusPushed) { if (lastSetTime != setTime) SetTime2RAM(); if (lastStepTime != stepTime) StepTime2RAM(); sprintf(sMsg,"done"); PT_DELAY(lc, 2000); screen=sREGLE; } if (millis() > ttl || minusPushed) screen=sREGLE; } while (screen==sSAUVE ); time2save=0; PT_END(lc); } static PT_THREAD(pVeille(pt_t *lc)) { // OK and avoid bounces const int nbSprites = 10; static t_iconMvt icons[nbSprites]; static t_sprite sprite; static uint16_t nbpass=0; PT_BEGIN(lc); PT_WAIT_UNTIL(lc, (screen==sVEILLE)); ledRGB(NONE); initSprite(&sprite,nbSprites,16,16, flake_bmp, icons); // load values to sprite do { drawSprite(&sprite); PT_DELAY(lc, 40); if (!(plusPushed || minusPushed)) { oled.display(); PT_DELAY(lc, 40); updateSprite(&sprite); // update coordinates of each sprite } nbpass = changeSprite(&sprite, nbpass); } while (!(plusPushed || minusPushed )); minusPushed=false; plusPushed=false; oled.clearDisplay(); PT_DELAY(lc, 200); screen=sTITRE; // sCHOIX;// PT_END(lc); // to try: exit & return flag ? } void loop1() { // CPU 1 dedicated to run threads pMinus (&pMINUS); // process minus button pPlus (&pPLUS); // process plus button // pAlarm (&pALARM); // WIP 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 pSauve (&pSAUVE); // process screen SAUVE pVeille(&pVEILLE); // process screen saver } /* * code debug Serial.begin(115200); delay(500); Serial.printf("CPU 0 on\n"); Serial.printf("CPU 1 on\n"); */