Indledning
Denne gang er jeg på egen hånd. Jeg kunne vel godt bruge JMRI på en Raspberry Pi. Men det ville kræve en smart måde at definere og interface til en generaliseret Arduino styring.
I stedet vil jeg producere noget kode, der er dedikeret til lige netop den bane, jeg bygger. Og det skal eksekvere på en ESP32. Og hvis dens 40 IO porte eller dens processor eller RAM ikke er tilstrækkelig, så på flere Arduino-agtige moduler.
For at implementere en pæn WEB-side i ESP32 får jeg muligvis brug for et micro SD kort til at indeholde ikoner m.v. Det er sådan set heller ikke noget problem. Jeg har en adapter. Men det koster 4 IO porte: http://www.esp32learning.com/code/esp32-and-microsd-card-example.php
Derudover kommer min hardware til at bestå af:
– to strømforsyninger – en for hver af de to DAC’er i ESP32, og samtidig en for den nordlige og en for den sydlige ende af banen, så to tog kan køre samtidig med forskellig hastighed i hver sin ende af banen.
– en servo for hver sporskifte.
– et antal reed switche til at detektere togenes fysiske placering på banen.
– et antal relæer til retningsskifte og til ind- og udkobling af vigespor.
– to tog. Indtil videre har jeg kun et tog. Men banen skal indrettes til to.
Software byggeblokke
Jeg kunne godt opbygge en styretavle bestående af lysdioder og switche. Men jeg vil hellere udnytte ESP32’erens WIFI til at implementere en WEB-side, der viser status og tager imod kommandoer. Minimum start og stop kommandoer.
Softwaren skal derfor:
1. Sætte ESP32 op som access point, så hverken ESP32 eller min håndholdte device (smartphone, iPad eller lignende) behøver at være forbundet til en router.
2. Udstille en WEB-side som netop beskrevet.
3. Holde styr på, hvor hver af de to tog befinder sig. Denne information skal gemmes i non-volatile memory, dvs. så informationen overlever en strømafbrydelse.
4. Indeholde procedurer til at køre et tog fra et vigespor/perron til et andet. Det inkluderer at sætte sporskifter og accelerere toget langsomt op og ned i fart.
5. Holde øje med events, dvs. om en reed switch rapporterer tilstedeværelsen af et tog og reagere herpå ved at stoppe/starte tog og at vende retningen af vendesløjfer.
6. Sikre strømforsyningerne, dvs. afbryde alt i tilfælde af kortslutninger.
En af udfordringerne ved Arduino verdenen er, at softwaren er enkelttrådet og ikke interruptstyret. Dvs., at der er en hovedløkke, som kaldes igen og igen, men som kun må foretage sig meget simple operationer hver gang.
Første prototype
Jeg har fluks kastet mig ud i at lave en prototype. Hardwaren ser således ud:Det består af en Weemos D1 mini (klon fra Kina), som er en ESP8266, en reed switch forbundet mellem D1/GPIO5 og stel og en servo forbundet til stel, 5V og D2/GPIO4.
Jeg fandt nogle Arduino eksempler, som jeg kombinerede. Så lige nu har jeg opstillingen til at agere access point og WEB server for en yderst simpel WEB-side, der opdaterer hvert andet sekund. Og hver gang aflæser den status for reed switchen og drejer servoen alt efter om reed switchen er aktiveret (af en magnet) eller ej. Desuden indeholder siden status for reed switchen og den interne LED på Weemos’en samt en mulighed for at tænde eller slukke denne LED.
Udover selve Arduino IDE’en skal man også lige tilføje support for ESP32. Der er mængder af instrukser tilgængelige, f.eks. denne: https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-mac-and-linux-instructions/
Jeg har lagt min Arduino kode her: https://github.com/kjlisby/arduino_train.git
Så er vi i gang. Jeg skal have koden struktureret pænere og i flere filer, hhv. have gjort den objektorienteret og dermed lettere at læse. WEB serveren skal eksempelvis være en klasse for sig selv. Og den skal ikke være flettet ind i koden, men ligge som filer på et micro SD kort, som i dette eksempel: https://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/
Jeg har i øvrigt fundet ud af, at en ESP8266 og en ESP32 (selvom der er rapporteret fejl på denne funktion på sidstnævnte) kan fungere både i AP (access point) og STA (station) mode samtidig. Det er meget smart, for så kan jeg tilgå den via hjemmenetværket, når den er tæt nok på routeren, og direkte, når jeg vil være mere sikker på at forbindelsen skal være helt stabil.
Desuden har jeg fundet en glimrende kilde til viden om HTML5, Javascript, C++ og en del andre beslægtede emner: https://www.w3schools.com/ Jeg er ikke i stand til at vurdere sidens niveau indenfor alle emnerne. Men i hvert fald C++ delen kunne godt være mere udtømmende beskrevet.
En anden hjælpekilde, der viste sig at være til meget god hjælp er Programming Guiden fra Espressif (firmaet bag ESP). Specielt afsnittet om fatal errors, som hjalp mig videre med null-pointer fejl: https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/fatal-errors.html
En anden “lille” ting er ikoner. Jeg har prøvet at lave en animeret GIF med noget, der ligner skinner med sveller. Ideen er, at bruge en animeret gif, så skinnerne bevæger sig for at signalere, at der er spænding på. Men jeg har fundet ud af, at en animeret udgave af en simpel punkteret linie fra Powerpoint fungerer meget bedre.
Software arkitektur – ESP32
Selvom en ESP32 ikke fylder meget og ikke er særlig potent heller, så kommer min togstyring alligevel til at bestå af en hel del kodelinier. For at gøre det hele nogenlunde overskueligt vil jeg udnytte, at Arduino IDE sproget i virkeligheden er C++, selvom de fleste eksempler, jeg kan google mig til er ikke-objektorienteret ANSI C. Men ved at gøre min kode objektorienteret kan jeg isolere detaljerne i et antal klasser og objekter. Det følgende er udgangspunktet. Det endelige resultat kommer til at kunne ses på github.com/kjlisby/arduino_train:
Klasse: WEBserver
Metoder: Constructor, Init, GetServer, GetSDCard
Semantik: Der skal kun være et enkelt objekt af denne klasse, som implementerer en WEB server. Det afhænger helt og holdent af indholdet af SD kortet, hvad WEB serveren kan. For det er på SD kortet, at HTML/CSS/Java Script filerne ligger.
– Constructoren sætter sætter ESP32 op i STA_AP mode, dvs. både station og access point mode. Dernæst skabes en WEB server og micro SD kortlæseren initialiseres. Desuden sættes der diverse hooks op, så visse WEB adresser vil vise, hvad der er på SD kortet.
– Init gør constructorens arbejde færdig, idet den starter WEB serveren. Det vigtige ved Init metoden er dog det man kan gøre efter at WEBserver objektet er skabt, men før Init kaldes: Her tilføjer jeg flere hooks for at få de øvrige klasser i spil, så f.eks. status af IO porte kan læses via AJAX / Java Script af en WEB-side i en browser på en PC, iPad, mobiltelefon eller en anden device.
– GetServer returnerer en pointer til selve WEB serveren. Det skal bruges dels til at gøre ting og sager med WEB serveren før Init kaldes og dels til at kalde server.handleClient inde i loop funktionen.
– GetSDCard returnerer tilsvarende en pointer til SD kortet, så jeg kan få fat i det til at persistere status der.
Klasse: PowerSupply
Metoder: SetVoltage, SetPolarity, ReadVoltage, ReadPolarity, ReadStatus, Reset
Semantik: Der skal instantieres et objekt af denne klasse for hver strømforsyning i systemet. Lige nu tror jeg, at jeg får brug for to, nemlig en i hver ende af banen. Klassen bruger en DAC output port til at sætte spændingen, en input port til at sense, om der er sket en kortslutning, og i givet fald sørge for at slukke for udgangsspændingen og en digital output port til at styre et relæ, der bestemmer polariteten.
– Constructoren gør ikke noget særligt udover at sætte portmodes og initielt sætte spænding og polaritet til +0V.
– SetVoltage sætter værdien af output porten, så den ønskede udgangsspænding opnås.
– SetPolarity sætter relæet til den ønskede polaritet.
– ReadVoltage læser værdien af DAC porten og omsætter den til den faktiske spænding.
– ReadPolarity læser hvordan polariseringsrelæet er sat.
– ReadStatus returnerer enten true (alt OK) eller false (kortslutning detekteret og strømforsyningen derfor lukket ned).
– Reset genstarter strømforsyningen (på +0V) efter en kortslutning.
Klasse: Layout
Metoder: Constructor, Pulse, UpdateTrainPosition, GetStatus, UpdateStatus, ChangeState
Semantik: Her er logikken for lige netop mit layout implementeret. Der skal naturligvis kun være en enkelt instans af denne klasse. Det er dette objekts constructor, der instantierer PowerSupply, Turnout og Siding objekter. Og det er også her, at der registreres en metode for AJAX / Java Script koden i WEB siden, så man herfra kan kalde de øvrige metoder i Layout klassen, såvel som ChangeState metoderne i Turnout og Siding klasserne.
– Constructor er beskrevet ovenfor.
– Pulse skal kaldes så ofte som muligt, dvs. i Arduino loop() funktionen. Det er Pulse, der checker alle Contact objekter og (via callback) tager action, hvis et tog detekteres.
– UpdateTrainPosition kaldes når et tog er detekteret ved en reed switch. Opdaterer status – også på SD kortet samt tager passende action i forhold til sidings, strømforsyninger m.v.
– GetStatus bruges af WEB siden til at hente den totale status for, hvor begge tog befinder sig, hvordan sporskifterne står, spænding og polariseringfor strømforsyningerne og hvordan vigesporene er koblet.
– UpdateStatus bruges også af WEB siden til samme formål som GetStatus. Men svaret på UpdateStatus indeholder kun ændringerne siden seneste kald til GetStatus. Dette er for at speede tingene op, men betyder til gengæld, at der kun kan være én device med én WEB side i brug.
– ChangeState bruges af WEB siden til at tænde og slukke for hele layout’et. Når der slukkes, kører begge tog videre, indtil de er parkeret på rette plads.
Klasse: Contact
Metoder: Constructor, CheckSwitch, RegisterCallback
Semantik: Repræsenterer en reed switch, som kan føle, om et tog passerer.
– Constructor initialiserer den input port, som switchen er koblet til.
– CheckSwitch skal kaldes meget hyppigt for at aflæse portens status.
– RegisterCallback bruges til at fortælle et Contact objekt, hvilken aktion, der skal tages, hvis et tog registreres i et CheckSwitch kald.
Klasse: Turnout
Metoder: Constructor, SetStatus, GetStatus
Semantik: Styrer et sporskifte vha. en output port sat op i PWM mode. Et sporskifte ejes enten af et Siding objekt eller et ReverseLoop objekt.
– Constructor Initialiserer output porten og sætter sporskiftet til “Closed”.
– SetStatus Sætter sporskiftet til ønsket tilstand.
– GetStatus Læser sporskiftets tilstand og returnerer enten “Closed” eller “Thrown”.
Klasse: PassingSidingSelector
Metoder: Constructor, SetStatus, GetStatus
Semantik: Styrer to modsat rettede parallelle spor, hvor to tog kan passere hinanden på en ellers enkeltsporet strækning. To sporskifter og to dobbeltpolede skifterelæer styres. Hvert relæ (et for hvert spor) kobler sporet til enten den nordlige eller den sydlige del af banen.
– Constructor sætter det sydgående spor til at hænge sammen med den nordlige halvdel af banen og det nordgående spor til at hænge sammen med den sydlige del af banen.
– SetStatus sætter et af sporene til at være koblet til enten den sydlige eller den nordlige del af banen. Nærmere bestemt sættes relæet for det pågældende spor i den ønskede tilstand, og sporskiftet i den relevante ende af sporet sættes.
– GetStatus returnerer status for enten det ene eller det andet spor. Status kan være enten “North”, “South” eller “undefined”.
Klasse: ParallelSidingSelector
Metoder: Constructor, SetState, GetStatus
Semantik: Styrer to ensrettede parallelle spor, hvor togene altid kører i samme retning.
– Constructor gør ikke noget udover at initialisere til det ene spor.
– SetStatus skifter mellem to perroner på en station, dels ved at skifte status for sporskifterne i hver ende og dels ved at skifte et relæ, som giver strøm i skinnerne på enten det ene eller det andet spor.
– GetStatus returnerer navnet på det aktive spor.
Klasse: ReverseLoop
Metoder: Constructor, SetStatus, GetStatus
Semantik: Styrer en vendesløjfe, dvs. et sporskifte og polariseringen af en strømforsyning.
– Constructor Associerer med Turnout og PowerSupply objekter og sætter status til “InBound”.
– SetStatus sætter polarisering og sporskifte alt efter om toget er på vej ind i eller ud af vendesløjfen.
– GetStatus returnerer enten “InBound” eller “OutBound”.
Klasse: StatusStorage
Metoder: Constructor, Store, Get
Semantik: Gemmer status (Layout.GetStatus) på SD kortet, så det kan hentes igen efter en strømafbrydelse.
– Constructor associerer objektet med SD kortet, som allerede skal være initialiseret.
– Store gemmer status.
– Get henter status.
Klasse: Status
Metoder: Constructor, Get, Set, Serialize
Semantik: Denne klasse indeholder ingen logik og styrer ingenting. Den er blot en bærer af status for hele banen.
– Constructor Initialiserer alle attributter.
– Get og Set: Der findes Get og Set metoder for hver enkelt attribut.
– Serialize konstruerer et format, der dels kan bruges til persistering og dels kan bruges til at svare på HTTP forespørgsler fra WEB siden. Til sidstnævnte skal der kunne peges på en baseline status, så kun forskellene rapporteres til WEB siden..
Attributter:
– Train1Position: Current position of a train
– Train2Position: Current position of the other train
– SidingSouth: Indicates which of the two sidings that currently is part of the southern reverse loop.
– SidingEast: Indicates if the eastern siding is currently part of the northern or southern part of the layout (can be “undefined”).
– SidingWest: Indicates if the western siding is currently part of the northern or southern part of the layout (can be “undefined”).
– ReverseLoopNorth: Voltage on the northern reverse loop. Positive, if the polarity corresponds to “InBound”, negative if “OutBound”.
– ReverseLoopSouth: Same for southern reverse loop.
Software arkitektur – HTML
WEB siden består af 3 filer, som gemmes på SD kortet:
togbane.htm: En ret kortfattet HTML5 definition af siden, hvor der er defineret et ikon for hver blok på banen, 2 ikoner for hver sporskifte samt knapper m.v. nok til at kunne starte/stoppe kørslen og se status. Sporikonerne er som udgangspunkt “kolde” JPEG filer, der illustrerer spor uden spænding. Når der kommer spænding på sporene, kan Java Script koden udskifte disse JPEG filer med animerede GIF’er, som får sporene til at se ud som om de bevæger sig i den retning, som svarer til polariseringen.
Hvorvidt togene skal kunne køres manuelt er TBD. I så fald får jeg brug for at gøre sporskifter ikonerne aktive, så sporskifterne skifter, når jeg trykker på dem, sidesporene ligeledes aktive, så jeg kan styre relæerne, samt jeg får behov for sliders m.v. til at styre strømforsyningerne. Og desuden flere hooks ind i ESP32 koden.
togbane.css: Styling af alle elementerne på siden.
togbane.js: Implementerer UI logikken, dvs. de aktioner, der skal tages, når brugeren trykker på WEB sidens knapper og ikoner, samt et hyppigt kald til Layout.GetStatus hhv. Layout.UpdateStatus, hvor WEB siden hele tiden holdes opdateret i henhold til den status, som ESP32 rapporterer. Bliver muligvis delt op i flere filer svarende til de C++ klasser, der findes i ESP32 enden.
Status ultimo august
Jeg er så småt ved at finde ud af, hvordan man laver en HTML5 side. Jeg har lavet mig en animeret gif, som er det gennemgående element på siden:
Siden er måske noget rustik i udseendet. Men det er jeg lidt ligeglad med. Den skal bare være overskuelig, brugbar og responsiv. Og den skal tilpasse sig skærmstørrelsen, så den kan bruges på vilkårlige devices. Og det er altsammen opfyldt indtil videre. Knapperne er blot en prototype. De bliver fjernet igen.
Status medio september
Der sker ikke så meget. Men i aften har jeg fået lavet mig en prototype på et servodrevet sporskifte. Første forsøg gik ikke så godt. Katten må være løbet med målet. Dvs., hvis jeg ellers havde en kat. Men i hvert fald havde jeg glemt at tage hensyn til NEM 102 og 103 normerne:En lidt udvidet løsning, der tager hensyn til normerne, ser ud til at give god plads til et tog:Pladen er skruet på sporskiftet med to 3 mm skruer i de huller på sporskiftet, som er beregnet til de originale Piko (eller LGB) sporskiftedrev.
Pladen er lige nu kun et stykke stift pap. Men det skal naturligvis erstattes af noget vejrfast som f.eks. plexiglas. Eller endnu bedre, så vil jeg 3D printe et eller andet som kan ligne et sporskiftedrev fra virkeligheden, som har en form, der kan få regnvandet til at løbe af, som kan indkapsle servoen og holde den fast uden skruer og som har lidt kanter, der kan stive konstruktionen af. Jeg har bare ikke fået købt den der 3D printer endnu…..
Servoen vender på hovedet, fordi regnvand på den måde ikke kan løbe ind ved akslen, hvor servoen bevæger sig. Og bunden af servoen, som dermed vender opad, har jeg tænkt mig at dække fuldstændig til med silikone.
Og det berømmelige stykke pianotråd er lige nu på 1 mm. Jeg skal have lidt erfaring med denne skinnestørrelse for at finde ud af om det er den bedste dimension. Men p.t. ødelægger det i hvert fald ikke sporskiftet. For pap-pladen skal nok give efter.
Nu mangler jeg blot, at flere dingenoter ankommer fra Kina. Reed switche med plastikhus i stedet for glasrør, ESP 32, køleplader til de kraft-transistorer, der allerede er ankommet, m.v..
Jeg har købt skinner inklusive det sporskifte, der ses på billederne, til at udvide cirklen fra startsættet til at være den lille vendesløjfe, som på et tidspunkt skal udgøre den nordlige ende af banen i haven. Planen er, at toget skal køre frem og tilbage i den vendesløjfe rundt om juletræet i år. Det giver mig tre måneder fra nu.
Lige nu mangler jeg tid (så længe vejret er godt, bruges fritiden helst udendørs), som ydermere skal balanceres mellem have- og juletoget og min “rigtige” bane, som stadigvæk mangler enhver form for landskab. Jeg har dog prøvekørt det, og det kører fint. Og så har jeg “justeret” lidt på flamingoafskærmningerne på fronten af banen, dvs. limet en kant på for at gøre noget af det bredere, samt skåret lidt andre steder, så de lange vogne kan komme forbi. Der skal dog sikkert justeres mere.
10. oktober
I dag dumpede der en ESP32 ind i postkassen. Og køleplader til krafttransistorerne. Selve transistorerne ankom for nogle uger siden.
Og jeg har fundet en gammel 20 volt PC strømforsyning, der burde kunne fungere til formålet.
Desuden har jeg fundet et 3D print, som kan transmogryffe en servo til et sporskiftedrev: https://www.thingiverse.com/thing:3883510
Jeg må have møvet mig adgang til en 3D printer snarest og få sådan en printet. Ellers nøjes jeg med papstykket på billederne ovenfor. Det fungerer til stuebrug.
Nu kan jeg så småt begynde at bygge styringen til havetoget. Jeg mangler stadigvæk 1W modstande til kortslutningssikringen. Desuden mangler jeg de rigtige omskifter relæer til at vende polariteten. Men jeg har et 4-relæ modul, som nok godt kan bruges til en start. Og jeg kan godt vove at bygge en vendesløjfe rundt om juletræet uden kortslutningssikring.
Pga. det lune vejr er der dog stadigvæk tomater i drivhuset, så der går noget tid endnu, før der bliver gjort mere ved toget.
16.++ oktober – 3D printede objekter er i hus
Rigeligt med isoleringsstykker til at lave en vendesløjfe. Faktisk nok til to. Og også bemeldte sporskiftedrev.
Og jeg er i gang med at lave en fuglerede med ESP32 som midtpunkt, reed switch med pull-up modstand (der er ingen intern pullup i ESP32 på de GPIO porte, som jeg vil bruge), transistorer til at udgøre en strømforsyning som den tidligere beskrevne, servoficeret sporskifte vha. det 3D printede og så lige et trimmepotentiometer til at give kortslutningssikringen noget at arbejde med. For jeg vil ikke have et tog til at køre, før jeg har testet strømforsyningen på skrivebordet:Første billede viser begyndelsen, næste billede viser samme opstilling men tilføjet en 20 volt strømforsyning fra en for længst kasseret PC samt det relæmodul, der i første omgang kan bruges til polvendingen. Jeg har tegnet fuglereden op i et diagram, hvor jeg også har medtaget resten af strømforsyningen (TIP102 transistor, diode over udgangen, 5 volt strømforsyning m.v.). Mht. 5 volt forsyningen, så kan den nok ikke drive 6 servoer, som der ender med at blive brug for. Så på det tidspunkt må jeg erstatte den med en kraftigere ekstern eller supplere med endnu en 7805:
Største problem med at få det hele til at fungere stabilt var at finde ud af, hvad der skal forsynes med 5 volt hhv. 3,3 volt. Jeg har fået købt en SD kort adapter og et relæmodul, der skal have 5 volt. Relæmodulet dog 3,3 volt til logikken og 5 volt kun til selve relæerne. Det mest luskede var, at SD adapteren næsten fungerede med 3,3 volt. Dvs. det fungerede et par dage, derefter ikke. Men så rodede jeg lidt med ledningerne i fuglereden, og det virkede igen (formentlig helt tilfældigt) i et par dage.
Desuden har jeg lavet noget af den software, der skal til. Og denne gang nogenlunde skarpt opdelt i C++ klasser som beskrevet ovenfor.
Ultimo oktober
Jeg har fået omformet diagrammet til noget, der kan loddes sammen på et strip-board, så det også kan bruges i virkeligheden:Som det ses har jeg gjort plads til to strømforsyninger med polariseringsrelæer (hvor jeg i mellemtiden har fået de rigtige kompakte, der fungerer med 3,3 volt), seks servoer/sporskifter og fire reed relæer. Stort set hvad der bliver brug for i det endelige havetog. Oversigten over, hvordan jeg bruger GPIO portene på ESP32, ser sådan ud. Der bliver brug for flere GPIO porte til at styre relæer og til flere reed switche. Derfor I2C pindene:Desuden er jeg så klar med softwaren, at næste skridt, når jeg har fået loddet hardwaren sammen og testet strømforsyningerne, at prøve det af på det fysiske tog. Hvis det fungerer, er jeg klar til at holde jul. Jeg har således kodet logikken til en vendesløjfe, logikken til i det hele taget at køre toget og ikke mindst de hooks på ESP32, som skal få siden til at hænge sammen med strømforsyning m.v. og at gå ind i og ud af auto-mode. Jeg har samtidig tilsvarende udvidet WEB siden inklusive Javascript koden.
Og et par billeder fra den praktiske opbygning: