#pragma once #include // Pure logic, shared by the sketch and the native tests. No Arduino libraries. namespace LumaDesk { enum class Phase : uint8_t { Focus, Break }; enum class Event : uint8_t { None, ShortPress, LongPress }; struct Timer { uint32_t focusMs, breakMs, remainingMs, lastMs; Phase phase; bool running, started; uint16_t cycles; Timer(uint32_t focus=25UL*60UL*1000UL, uint32_t rest=5UL*60UL*1000UL) : focusMs(focus),breakMs(rest),remainingMs(focus),lastMs(0),phase(Phase::Focus),running(false),started(false),cycles(0) {} // Returns true once at a phase boundary. The next phase waits for a press. bool tick(uint32_t now) { const uint32_t elapsed=now-lastMs; // Unsigned subtraction tolerates millis rollover. lastMs=now; if(!running) return false; if(elapsed=30 && stable!=raw){ stable=raw; if(stable){pressedAt=now;longSent=false;} else if(!longSent)event=Event::ShortPress; } if(stable&&!longSent&&(uint32_t)(now-pressedAt)>=1500){longSent=true;event=Event::LongPress;} return event; } }; struct Light { bool dim=false; void update(uint16_t adc,uint16_t reference,bool calibrated){ if(!calibrated){dim=false;return;} // ADC ratio thresholds, NOT lux or physical illumination percentages. if(!dim && (uint32_t)adc*100UL < (uint32_t)reference*65UL)dim=true; else if(dim && (uint32_t)adc*100UL >= (uint32_t)reference*75UL)dim=false; } }; }