@rew: Bijna goed.
Jouw voorbeeld gaat uit van een StartTijd in de toekomst, dus wanneer je de aktie wilt uitvoeren, en dat levert inderdaad een negatief getal op als het nog geen tijd is. Maar dat is lastig testen want millis() is unsigned en is dus altijd positief.
Mijn voorbeeld gaat uit van een StartTime in het verleden, dus wanneer je begint met de delay.
if ((millis() - StartTime) > Delay)
{ StartTime += Delay;
StartTime is een moment in het verleden. En millis() loopt steeds door. Dus (millis() - StartTime) is altijd positief. En je start de aktie (en zet een nieuwe StartTijd) als het verschil groot genoeg is. De getallen hoeven niet signed te zijn. De enige eis is dat alle getallen even groot zijn, want als millis() bijv een 16-bit getal geeft en StartTime is 32-bit dan gaat het mis
Veel mensen zetten StartTime dmv millis()
if ((millis() - StartTime) > Delay)
{ StartTime - millis();
Dat werkt ook, maar geeft extra vertraging als je de test te laat uitvoert. Bijv als je systeem zo traag is dat je de controle maar 1 keer per meerdere milliseconden kunt uitvoeren. Dat is soms een probleem, niet altijd.
Rekenvoorbeeld:
Stel StartTime is unsigned long (32 bit). En de waarde is 0xFFFFFFFE (dus tegen overflow aan.
En je wilt een actie starten telkens na 5 milliseconden (Delay = 5).
Millis() StartTime Millis() - StartTime
0xFFFFFFFE 0xFFFFFFFE 0x00000000 Nog niet
0xFFFFFFFF 0xFFFFFFFE 0x00000001 Nog niet
0x00000000 0xFFFFFFFE 0x00000002 Nog niet
0x00000001 0xFFFFFFFE 0x00000003 Nog niet
0x00000002 0xFFFFFFFE 0x00000004 Nog niet
0x00000003 0xFFFFFFFE 0x00000005 Nu wel
Zet nieuwe StartTime (StartTijd + Delay)
0x00000003 0x00000003 0x00000000 Nog niet
0x00000004 0x00000003 0x00000001 Nog niet
enz ...
PS Dat het goed blijft gaan ook igv overflow is 'by design'. Dat is inherent aan het gebruik van "2's complement" binaire notatie.
Mijn ervaring is dat wanneer je over zoiets nadenkt en dat uitwerkt met de diverse functies dat het resultaat wat complex wordt en dat anderen juist weer afschrikt. Vaak geeft dit een hoop gezeik of gezijk 
Ik wil hem nog wel een stukje complexer maken.
Ik werk veel met een statemachine in een SQL database. Dat is voor embedded misschien wat overdreven, maar ipv in "usercode" zoals hierboven zou je dus states in een array kunnen definieren en zo kunnen aanpassen zonder opnieuw programmeren. Bijvoorbeeld over-the-air of met een SD kaartje.
Ik doe een poging, in onderstaande array kan dus vrij makkelijk 'in memory' wijzingen aanbrengen zoals het overslaan van de betaling of bepaalde producten alleen op bepaalde tijden toestaan:
{
"states": {
"state": [
{
"id": "WaitPayment",
"OnSuccessNextState": "ProductSelection",
"OnFailNextState": "WaitPayment",
"prereq": "time>8:00 and time < 18:00"
"action": "read_payment_terminal"
},
{
"id": "ProductSelection",
"OnSuccessNextState": "MakeProduct",
"OnFailNextState": "ProductSelection",
"action": "$product=read_selection_terminal"
},
{
"id": "MakeProduct",
"OnSuccessNextState": "WaitForDrops",
"OnFailNextState": "WaitPayment",
"action": "start_product($product)"
},
{
"id": "WaitForDrops",
"OnSuccessNextState": "DoneOnDisplay",
"OnFailNextState": "WaitForDrops",
"action": "checkTimer($timer1,3s)"
}, .....................
]
}
}
edit: ik heb die prereq met tijden van een ander systeem, maar ik bedenk me dat je net zo goed een state "checkTime" zou kunnen maken.
hardbass
PE2BAS
Op die manier kan je de software zeer flexibel maken via configuratie. Ik heb ook zoon tool gemaakt voor het werk die op zoon soort manier te configureren is. Wel een nadeel, je kan het ook 'kapot' configureren. Daar moet je dan in de software weer rekening mee houden. Het configureren is ook niet voor iedereen weggelegd. Andere optie is iets als lua ondersteunen, maar dat is allemaal weer afhankelijk van de toepassing.
Voor zij die het leuk vinden, dit idee is hier ontstaan. Op deze site staan veel meer leuke design patterns.
https://refactoring.guru/design-patterns
Op dinsdag 12 november 2024 11:43:49 schreef deKees:
Rekenvoorbeeld:
....
PS Dat het goed blijft gaan ook igv overflow is 'by design'. Dat is inherent aan het gebruik van "2's complement" binaire notatie.
Nu snap ik het!
In principe komt dit ook overeen met @rew's eerdere uitleg.
@rew en @deKees, dank voor de uitleg! 
henri62
1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.
Over die timeout. De timer value moet wel unsigned zijn waarmee je begint. Het beste is een macro gebruiken hiervoor zoals in de linux kernel waar de wrapping weggewerkt wordt waardoor dit altijd goed gaat mits de timeout < 1/2 * resolutie van de timer variable.
Zie deze discussie: https://stackoverflow.com/questions/8206762/how-does-linux-handle-over…
In mijn beleving komt het weinig voor dat verschillende state machines gemeenschappelijke behoeften hebben. Meestal draaien de state machines volledig onafhankelijk van elkaar.
Bijv:
void loop()
{ Knipperlicht.Run();
ADC.Run();
TempControl.Run();
SerialPort.Run();
}
Dus voor iedere state machine maak ik dan een object zodat je de bijbehorende data mee kunt nemen. En het enige dat de machines gemeenschappelijk hebben is dat ze allemaal een Run() functie hebben. De implementatie is dan voor elke functie verschillend. Het Knipperlicht is bijv timer gestuurd, de ADC reageert op het ADC status register, de SerialPort reageert op inkomende data.
Op dinsdag 12 november 2024 19:55:57 schreef henri62:
Over die timeout. De timer value moet wel unsigned zijn waarmee je begint. Het beste is een macro gebruiken hiervoor zoals in de linux kernel waar de wrapping weggewerkt wordt waardoor dit altijd goed gaat mits de timeout < 1/2 * resolutie van de timer variable.
Zie deze discussie: https://stackoverflow.com/questions/8206762/how-does-linux-handle-over…
Strict genomen klopt dit niet. De timer value hoeft niet signed of unsigned te zijn. Feitelijk maakt dat geen verschil. Waar het om gaat is dat alle getallen bij dezelfde waarde over de kop gaan.
Je berekent eerst het verschil tussen 'nu' en de 'StartTime' en dat levert altijd een klein positief getal op. En de af te tellen delay is ook een klein positief getal. Of het type dan al dan niet signed is maakt geen verschil. Behalve als je een timeout value gaat zetten van meer dan 25 dagen. Het verschil wordt dan ooit groter dan 0x8000000 en dan maakt signed/unsigned weer wel verschil.
Die Macro van linux forceert een timeout berekening door eerst de verschiltijd te berekenen. Zonder die macro loop je het risico om het fout te doen.