verlichting huisjes aansturen met Arduino - hoe kan het anders/beter/...

Gestart door Dirky, 04 maart 2021, 11:55:03 AM

patrick smout

@ Saskia ,

Begrijp ik,het gaat nu wel heel snel.ook. Je zal merken dat met een extra woordje uitleg het niet zo moeilijk te volgen is.
Ik zal vanavond na het werk, als een collega modelspoorder er vandaag al niet aan aan begonnen is, een voorbeeldstukje code posten "hoe het anders kan". Beter is een woord dat ik niet in de mond durf nemen  ;). Dat moet ieder voor zich maar uitmaken.

Met vriendelijke groeten,

Patrick Smout

deevie

Citaat van: patrick smout op 15 maart 2021, 23:33:13 PM
Heb je dat getest ?  ;)
Wat als millis + random ( timer) bijna op max waarde staat en de volgende keer de loop doorlopen wordt millis terug naar de min waarde gevallen is. Kan voorkomen als ergens in de loop iets langer duurt en random kleine waarden geeft.
Een Arduino draait op 16Mhz en bevat een RISC processor en dus iedere instructie duurt 1 / 16 000 000 seconden. Uitvoeren van 16 instructies duurt dus 1 MICROseconde. Enige mogelijkheid is dat die millis + randomwaarde de max waarde krijgt wat al 1 kans is op een paar duizend. Daarnaast moet zijn interne teller voor de millis ook al bijna de maximum waarde hebben (rekeninghoudend dat die nog een jump naar de cmp en de cmp zelf moet doen), wat één kans is op een paar tienduizend. De kans dat die effectief na 50 dagen, 50 dagen niks doet is 1 op 1 miljoen en dus eigenlijk onbestaande. Als je dat wil testen zal dat heel lang duren.

Havoc

Citaat van: stoomlokje op 16 maart 2021, 01:37:44 AM
en nu kunnen we helemaal niet meer volgen >:(

Hiervoor moet je meer van de interne werking van een processor kennen. Ik zal trachten een héél simpel voorbeeldje te geven.

Elke waarde in een processor is begrensd door hoeveel bits er in kunnen. Laat ons om het simpel te houden dat er 8 bits zijn. Dus zo'n teller kan alle waarden van 0 t/m 255 bevatten. Als je die teller elke seconde met 1 verhoogt, dan zal die na 256 seconden van 255 naar 0 gaan. Dus als je dan elke keer er een random waarde bijtelt dan kan je hebben dat hierdoor de teller "ronddraait".

Nu kan je ofwel die teller groter maken (maar daar zijn grenzen aan) of je kan de code zo aanpassen dat het geen probleem is. Of je kan bekijken wat het gevolg is en beslissen dat je met het gevolg kan leven.

Hier loopt die teller dus na 50 dagen rond. Nu of dat van die 50 dagen een probleem is, is een andere vraag. Wie laat er hier 50 dagen de spanning op zijn baan staan?

"In het echt" zijn die tellers groter waardoor hier kennelijk het probleem optreedt na 50 dagen. Maar het is iets waar je bij programmeren altijd rekening mee moet houden. Als je gaat googlen naar "year 2038" dan kan je lezen dat zo'n problemen niet zeldzaam zijn. In het jaar 2038 zal de secondenteller overlopen.
Met vakantie voor onbepaalde duur.

Gerolf

Timers kan je met software maken (tellers, en inderdaad liefst zonder delay)
Ik verkies het om een hardware-timer te gebruiken, en interrupts. Zo heb je een betrouwbaar basis-interval van bvb 10 of 100 milliseconden.
Maar het gebruikt van een timer en interrupt vraagt natuurlijk wel wat extra programmeerwerk ...

Citaat van: stoomlokje op 16 maart 2021, 01:37:44 AM
en nu kunnen we helemaal niet meer volgen >:(

Het gaat inderdaad snel, en wat door elkaar heen. Als er iets meer citaten worden gebruikt, kunnen de verhaallijnen beter gevolgd worden

Citaat van: patrick smout op 15 maart 2021, 22:14:08 PMOnder het motto, hoe kan het anders  ;) ...

Een nog universelere aanpak is om alle data in arrays of structs te zetten, zowel patroon, schakeltijden, de te gebruiken uitgangen, state, starttijd en duurtijd. (1 array of structs per kanaal voor het patroon binnen één kanaal en 1 array of pointers to arrays of structs om over de kanalen te itereren)
Het voordeel van deze datadriven aanpak is dat je enkel maar tabellen moeten editeren om patronen aan te passen en de code universeel is.
Maakt niet uit welke uitgang je wil schakelen en in welke volgorde en /of duurtijd. Alles info zit in de data/tabellen.

Dat doe ik de laatste tijd vaak. Die tabellen maak ik dan aan in Excel, en daar genereer ik data-teksten mee.
Groeten uit "Marche-en-Bières"   ** Modelspoor is plezant **   TPIII-H0-DC-Zelfbouw

patrick smout

Belofte maakt schuld dus ...
Even vooraf. Zoals eerder aangegeven, ik heb geen arduino's. De code compileert (voor ander platform) maar kleine aanpassingen voor arduino kunnen noodzakelijk zijn. Ik ben bv. niet zeker of digitalWrite() een variabele van type bool aanneemt als 2e parameter dus werk ik even via een tussenfunctie die dat oplost. Code kan ook aangepst worden dat in de tabellen ineens HIGH/LOW staat maar dat kon ik niet testen.
Ik heb links en rechts wat commentaar bij gezet. In het voorbeeld zijn er 3 kanalen die elk hun eigen scenario  afspelen. Per stap kan je kiezen of een uitgang aan/uit moet zijn en voor hoe lang. Een kanaal koppel je aan een fysieke uitgang.
Initialisatie van de uitgangen staat niet in de code, kan men uit eerdere voorbeelden halen.
Het gedrag vn de uitgangen zit hier in data, niet in code (en is per kanaal onafhankelijk van elkaar instelbaar)
Dit voorbeeld kan nog verder opgekuist worden voor bv RAM geheugen te besparen maar dat gaat nu even te ver.
Vragen uiteraard altijd welkom, we zijn hier om te leren.



struct Scenes {
unsigned long tijd; // Tijd dat een uitgang actief/inactief is in deze stap
bool status; // Uitgang aan of uit
};

struct Kanaal {
struct Scenes *pScene; // Scenario voor dit kanaal
int scenariolengte; // Lengte van de scenario data
int uitgang; // Fysieke uitgang voor dit kanaal
unsigned long starttijd; // Moment dat de huidige stap actief is geworden
int huidigescene; // Huidige scene in scenario
bool tijdloopt; // true als we aan het wachten zijn tot de tijdverstreken is
};

// Scenario 1 bestaat uit 4 stappen
struct Scenes Scene_1[]  = {
{ 1000, true }, // Uitgang x gedurende 1 seconde aan
{ 2000, false }, // Uitgang x gedurende 2 seconden uit
{ 3000, true }, // Uitgang x gedurende 3 seconden aan
{ 4000, false } // Uitgang x gedurende 4 seconden uit
};
// Scenario 2 bestaat uit 2 stappen
struct Scenes Scene_2[]  = {
{ 200, true }, // Uitgang x gedurende 0,2 seconde aan
{ 200, false } // Uitgang x gedurende 0,2 seconde uit
};
// Scenario 3 bestaat uit 4 stappen
struct Scenes Scene_3[]  = {
{ 100, true }, // Uitgang x gedurende 0,1 seconde aan
{ 200, false }, // Uitgang x gedurende 0,2 seconden uit
{ 500, true }, // Uitgang x gedurende 0,5 seconden aan
{ 200, false } // Uitgang x gedurende 0,2 seconden uit
};

// We hebben 3 afzonderlijke kanalen met elk een eigen scene
struct Kanaal Kanalen[] = {
{ Scene_3, sizeof(Scene_3), 1, 0, 0, false }, // Scene 3 op uitgang 1 (de 3 laatste velden zijn altijd "0, 0, false")
{ Scene_2, sizeof(Scene_2), 2, 0, 0, false }, // Scene 2 op uitgang 2 (de 3 laatste velden zijn altijd "0, 0, false")
{ Scene_1, sizeof(Scene_1), 3, 0, 0, false } // Scene 1 op uitgang 3 (de 3 laatste velden zijn altijd "0, 0, false")
};

void zetuitgang(int uitgang, bool status)
{
    if(status)
    {
        digitalWrite(uitgang, HIGH);
    }
    else
    {
        digitalWrite(uitgang, LOW);
    }
}

void loop()
{
int HuidigKanaal;

// Voor kanaal 0 t.e.m. het aantal kanalen
for (HuidigKanaal = 0; HuidigKanaal<sizeof(Kanalen)/sizeof(struct Kanaal); HuidigKanaal++)
{
// Loopt de tijd al voor dit kanaal?
if(Kanalen[HuidigKanaal].tijdloopt==false)
{
// Zet uitgang x overeenkomstig stapwaarde
zetuitgang(Kanalen[HuidigKanaal].uitgang,Kanalen[HuidigKanaal].pScene[Kanalen[HuidigKanaal].huidigescene].status);
// Volgende stap is wachten ...
Kanalen[HuidigKanaal].starttijd = millis();
Kanalen[HuidigKanaal].tijdloopt = true;
}
else
{
if(millis() - Kanalen[HuidigKanaal].starttijd > Kanalen[HuidigKanaal].pScene[Kanalen[HuidigKanaal].huidigescene].tijd)
{
// tijd is vertreken
Kanalen[HuidigKanaal].tijdloopt = false;
// Volgende scene - Indien einde van de reeks begin dan opnieuw
if( ++Kanalen[HuidigKanaal].huidigescene >= Kanalen[HuidigKanaal].scenariolengte/sizeof(struct Scenes))
{
// begin terug met scenario 0 voor dit kanaal
Kanalen[HuidigKanaal].huidigescene = 0;
}
}
}
}
}


Met vriendelijke groeten,

Patrick Smout

patrick smout

Citaat van: deevie op 16 maart 2021, 09:12:50 AM
Als je dat wil testen zal dat heel lang duren.

unsigned long MyMillis()
{
    return millis()+ (0xffffffff - 5000);
}


50 dagen wachten kost teveel geld en is ook saai. Tussen 1 en 0xffffffff gebeurt er niets interessant. We focussen dus op boundary testing.
Vervang de aanroepen naar millis() door mymillis() en 5 seconden na opstart kan je eventuele sideeffects zien.
Bij "Timertick" tellers in een interrupt routine initialiseer je de beginwaarde ook op MAX-5000 en niet op 0. Zo krijg je gratis en voor niks interessante overgangen tijdens het ontwikkelen zonder dat je hoeft te wachten.
Overigens kost het geen moeite om dingen ineens goed te coderen. Bespaart je de kopzorgen of een besturing wel/niet 50 dagen of een andere onbekende periode ingeschakeld moet blijven.
Met vriendelijke groeten,

Patrick Smout

Havoc

Woha! Pointers, structs en bewerkingen daarop. Hier heb ik een dagje nodig om er terug in te komen. Enige die ik mis is "->" ...  En dat is te lang geleden zonder terug in de boeken te duiken en tekeningskes te maken, hier gaan er veel op afhaken.

En als ik nu echt eerlijk moet zijn... Voor wat ik van plan ben ga ik naar zo'n dingen terug moeten gaan. Maar voor een Arduino die in enkele huisjes licht aan en uit moet laten gaan...
Met vakantie voor onbepaalde duur.

Dirky

Voor mij gaat het in alle geval stukken te rap maar er staat begrijpbare uitleg bij, waarvoor dank, dus kan ik wellicht later bijbenen :-)!

Karel A.J.

CiteerOverigens kost het geen moeite om dingen ineens goed te coderen.

Patrick sorry hoor, maar dat klinkt toch een beetje elitair, al zal het zo wel niet bedoeld geworden geweest zijn.

Het kost iedereen moeite om voor zichzelf uit te maken wat goed is. Dan mag het over coderen gaan of over het lasersnijden van styreenplaat of over het spuiten van de make-up van een hoerke in de Aarschotstraat of over het 3d-printen van een TGV-koppeling.

Maar wat men ook voor zichzelf vastlegt als goed het zal altijd moeite kosten om te bereiken.

patrick smout

Dag Karel,

kleine toelichting, mijn opmerking "dingen ineens goed te coderen." kadert binnen de opmerkingen rond de overflow van millis().
Daar waren oplossingen met functionele problemen en dat kan je beter ineens goed doen.
Dat had ik beter kunnen duiden - bij deze misverstand uit de wereld  ;).


Met vriendelijke groeten,

Patrick Smout

Karel A.J.


patrick smout

Met vriendelijke groeten,

Patrick Smout

Havoc

Citaat van: Karel A.J. op 16 maart 2021, 21:53:07 PM
CiteerOverigens kost het geen moeite om dingen ineens goed te coderen.

Patrick sorry hoor, maar dat klinkt toch een beetje elitair, al zal het zo wel niet bedoeld geworden geweest zijn.

Waarschijnlijk niet, en ik kan Patrick geen ongelijk geven. Maar toch wel enkele bedenkingen:
- om op dat detail te gaan programmeren moet je al verdomd veel weten over de hardware. Meer dan 75% van diegenen die een arduino of andere processor als bijzaak in hun hobby gaan gebruiken weten dat niet. En hebben er ook geen interesse of noodzaak aan.
- alhoewel het principe ongetwijfeld correct is, blijft de vraag hoe belangrijk het is voor een LED sturing van huisjes op een treinbaan... Moesten we hier nu over iets bezig zijn zoals een machinesturing, medisch toestel, een raket... ja, dan ongetwijfeld. Maar de vraag is of er iemand ooit iets van zal merken als dit niet 100% is. en zelfs dan of er ooit iemand meer zal zeggen dan "dat is raar....".
- laat ons niet het doel uit het oog verliezen. Ergens is er een "return on investment". Ik kan begrijpen dat mensen die professioneel met software bezig zijn dit correct willen. Maar er zijn er ook die alleen wat lichtjes in een huisje willen zien aan en uit gaan.
Met vakantie voor onbepaalde duur.

patrick smout

Helemaal akkoord Johan en  dat is het leuke aan deze hobby. Iedereen beleeft die op zijn manier.
Ik krab me wel eens in de haren als ik opmerkingen lees of ruitenwissers van een loc nu bovenaan of onderaan horen vastgemaakt te zijn maar verder geef ik de ruimte aan iedereen om daar anders over te denken. Ondertussen leer ik wat bij en dat is ook mooi meegenomen (al wil dat niet zeggen dat ik het allemaal onthoud  ;) ).
Met vriendelijke groeten,

Patrick Smout

patrick smout

Citaat van: Dirky op 16 maart 2021, 21:24:02 PM
Voor mij gaat het in alle geval stukken te rap maar er staat begrijpbare uitleg bij, waarvoor dank, dus kan ik wellicht later bijbenen :-)!
Dag Dirk, herkenbaar voor mij maar dan op een ander vlak. Scenery is voor mij volledig buiten mijn comfortzone  en enige vooruitgang bij mij gaat tergend langzaam (en regelmatig ook achteruit). Als ik dan zie wat jij en anderen uit hun hoed toveren, daar kan ik enkel maar van dromen.
Neemt niet weg dat ik op eigen tempo mijn grenzen wat probeer te verleggen maar wat voor de één een molshoop is, is voor de ander een berg. ;)

Ik wil de code zeker nog wel verder in detail toelichten mocht het je (of iemand  anders) interesseren.
Met vriendelijke groeten,

Patrick Smout