Software er som sædvanlig det der usynlige, der bringer det hele til live, binder alle komponenter sammen og får det hele til at fungere. Desuden skal det indeholde en WEB server, så man fra en PC eller en tablet kan styre elevatoren og se samme information som på displayet.
Kravspecifikationen:
- Det skal være hårdt kodet (eller bedre: gemt på SD kort eller i intern flash memory), hvor lang afstanden er mellem de 12 etager. Der er 12 etager, fordi det er urealistisk at få de to elevatorstole og ophænget af dem præcis nok til at have 6 dobbelt-etager, hvor begge elevatorstole er præcis ud for skinnerne, så tog kan køre ud og ind. For at tage højde for små skævheder i træværk m.v. skal dette ske ud fra målinger og ikke kun ud fra CAD tegningen. Det giver et afledet krav om noget software til at foretage denne kalibrering af disse højder.
- Når strømmen tændes, skal elevatoren selv finde til etage 1. Dvs. check konstant sensorer for endestop, idet der gøres følgende: Indtil elevatoren er ved endestop: Kør langsomt nedad. Derefter: Kør langsomt opad indtil endestop netop er deaktiveret. Elevatoren er nu i nul-position. Kør til etage 1 ud fra det kalibrerede antal steps. Husk, at der er to sider, der begge skal ramme samme etage.
- Displayet skal vise, hvilken etage elevatoren er på (“?” under initialisering), seneste kommando (DCC eller WEB samt etage) og status (“står stille”, “kører op”, “kører ned” eller forskellige fejlmeddelelser, som f.eks. “flere etager aktive” eller “indkørsel blokeret”).
- Elevatoren må ikke sættes i bevægelse, hvis der befinder sig en genstand i en af de fire “porte” ind i elevatoren.
- WEB siden skal vise samme information som displayet.
- Der skal konstant lyttes efter og reageres på ordrer fra både WEB side og DCC interface.
Design:
For at implementere disse krav skal der implementeres følgende objekter (definition af klasserne følger) i C++ koden på ESP32. Derudover noget JavaScript i HTML siden, som eksekveres i browseren:
- To positionssensorer (PosSensor): Et endestop i hver side.
- Fire IR sikkerheds-sensorer (SecSensor) til sikring af krav 4.
- To stepper motorer (StepMotor).
- En positionerings logik (PosLogic), der dels kan initialisere til etage 1 og dels flytte til en anden etage – begge dele koordineret mellem højre og venstre side.
- Kalibreringsfunktionalitet (Calibrator), der kan bruges til at lave og gemme et antal steps i forhold til nul nivauet pr. 12 etager pr. side i en fil på SD kortet.
- WEB server (WebServer), der kan kommunikere med JavaScript koden i browseren på PC/tablet og med JMRI og derved modtage kommandoer og vise status.
- Display styring (Display), der kan vise status.
- DCC interface styring (DccInterface), der kan modtage kommandoer.
Derudover skal der være et JMRI script, der sammen med Warrant afvikleren (se http://lisby.dk/wordpress/?page_id=2424) hele tiden ved hvilket tog, der findes på hvilken hylde i elevatoren – og hvordan toget vender.
Definition af de enkelte klasser:
Klasse: PosSensor (måske er der ikke brug for en klasse – det er vel bare en I/O pin)
Metoder: Constructor, IsActive
Semantik: Repræsenterer en micro switch, som bruges til at positionere (“home” hedder det vist nok på en 3D printer) elevatoren.
– Constructor: Initialiserer den input port, som switchen er forbundet til.
– IsActive: Returnerer sand, hvis switchen er aktiv. Ellers falsk.
Klasse: SecSensor (måske er der ikke brug for en klasse – det er vel bare en I/O pin)
Metoder: Constructor, IsActive
Semantik: Repræsenterer en IR sensor, som føler på, om der er et tog eller et andet fysisk objekt i en “indkørsel” til elevatoren.
– Constructor: Initialiserer den input port, som sensoren er forbundet til.
– IsActive: Returnerer sand, hvis IR sensoren er aktiv. Ellers falsk.
Klasse: StepMotor (Måske er det bare AccelStepper klassen?)
Metoder: Constructor, Move
Semantik: Repræsenterer en stepper motor, som kan bevæges et antal steps den ene eller den anden vej.
– Constructor: Initialiser motoren inklusive de tre I/O porte, der bruges til motoren.
– Move: Få motoren til at bevæge sig det angivne antal steps i den angivne retning.
Klasse: PosLogic
Metoder: Init, Home, MoveTo, GetStatus
Semantik: Logik til at bevæge hele elevatoren til en bestemt etage under anvendelse af kalibreringsdata. Sørger også for initielt at bevæge elevatoren til etage 1 vha. endestop sensorerne, samt at begge sider følges ad. Og endelig kan JMRI koden spørge, om elevatoren er i en veldefineret position og i givet fald hvilken etage.
– Init: Sætter steppermotorer og sensorer op.
– Home: Initialiserer elevatoren, dvs. ved anvendelse af begge endestop micro-switche og begge motorer at få elevatoren i nul-positionen og derfra vha. kalibreringsdata til etage 1. Samt sætte status og statusmeddelelser på display og WEB side.
– MoveTo: Flyt elevatoren til den angivne etage. Opdater status og status meddelelser som ovenfor.
– GetStatus: Bruges af JMRI til at sikre, at elevatoren står stille det rigtige sted, før toget køres ud eller ind i elevatoren.
– Lock og Unlock: Bruges af JMRI koden til at sikre, at elevatoren ikke bevæges via WEB siden eller direkte på ECOS, mens tog køres ud og ind af elvatoren. Dvs. WEB og DCC kalder blot MoveTo, mens JMRI udfører en hel sekvens: MoveTo, Lock, poll GetStatus indtil elevatoren er på rette etage, kør tog ud eller ind af elevatoren Unlock.
Klasse: Calibrator
Metoder: GetOffset, SetOffset, ReadFromSD, WriteToSD
Semantik: Ved anvendelse af WEB serveren og JavaScript koden i browseren vises og justeres kalibreringsdata, dvs. antal steps pr. etage pr. side, som motoren skal bevæges i forhold til nul-positionen for at ramme pågældende etage præcist.
– GetOffset: Returner offset for en side af en etage.
– SetOffset: Sæt nyt offset (i RAM) for en etage.
– ReadFromSD: Læs alt kalibreringsdata fra SD kortet (efter power up).
– WriteToSD: Skriv alt kalibreringsdata til SD kortet for at bevare det efter power off.
Klasse: WebServer
Metoder: Init, m.fl.
Semantik: Genbrug af WebServer koden fra havetoget. Se Software til havetog. Eller rettere SDWebServer klassen her. Som så igen kræver en tilpasset version af AjaxHandler samme sted fra. AjaxHandler håndterer kommandoer fra den JavaScript kode, der kører i browseren. Dvs. de filer, der anbringes på micro SD kortet. Det handler for havetogets vedkommende om disse tre filer: index.html, togbane.css og togbane.jss, der findes her. Bemærk i øvrigt, at der ingen events flyder imellem WEB server og JavaScript koden i forbindelse med styring af havetoget. Og det er kun JavaScript koden, der sender kommandoer – herunder en GetStatus kommando med faste tidsintervaller. Der er dog også en WEB socket, som bruges til at sende log information til client siden. Den kunne sådan set godt bruges til at opdatere status på WEB siden. Det skal overvejes, hvad der er smartest.
– Init:
– m.fl.: Der skal implementeres nogen kald, som kun bruges af JMRI. Måske er det de samme som WEB siden bruger, altså via AjaxHandler.
Klasse: Display
Metoder: Init, NewLevel, AtLevel, Homing
Semantik: Et simpelt interface til at styre displayet.
– Init: Initialiser display interfacet.
– NewLevel: Vis bevægelsen fra en etage til en anden på displayet.
– AtLevel: Vis den etage, hvor elevatoren står, på displayet.
– Homing: Vis teksten “Homing..” på displayet.
Klasse: DccInterface
Metoder: Init, Loop, Callback
Semantik:
– Init: Initialiserer NmraDcc interfacet.
– Loop: Kalder videre ind i NmraDcc for at holde det i live.
– Callback: Ikke en del af klassen. NmraDcc kræver, at det er en simpel funktion. Reagerer på de relevante sporskiftekommandoer ved at kalde ind i PosLogic.
Desuden kan OTAHandler klassen fra havetoget genbruges. Den gør det muligt at uploade en ny software version til ESP32 over WIFI.
Mht. DCC interfacet, så kan ECOS styre en Märklin 7686 drejeskive. Og en drejeskive og en elevator er begge noget, der kan flytte tog til en ud af flere mulige positioner. Så det er tæt nok på til at være bedre end alt andet. Jeg har fundet en beskrivelse af, hvordan den drejeskive “ser ud” på et DCC system: 7686_turntable_quick_start.pdf
Det vil altså sige, at drejeskiven optræder som et antal sporskifter. Jeg får kun brug for 6 sporskifter-definitioner, fordi elevatoren kun kan sættes i 12 forskellige stillinger:
Etage 1: Sporskifte 229 rød knap (close?)
Etage 2: Sporskifte 229 grøn knap (throw?)
Etage 3: Sporskifte 230 rød knap (close?)
Etage 4: Sporskifte 230 grøn knap (throw?)
Etage 5: Sporskifte 231 rød knap (close?)
osv. osv.
Og så må både JMRI og elevatorens DCC interface bare rette ind efter ECOS’ muligheder. Jeg har fundet et par Arduino DCC drejeskive dekodere til inspiration: Pete GSX og T Koning Se også https://github.com/lucadentella/arduino-dccshield og hardware diagrammet her. Kun input delen er nødvendig. Her behøves ingen CV programmering. Alternativ (næsten identisk) hardware her.
Med hensyn til displayet er der en artikel at komme i gang på her: I2C OLED display. Vha. artiklens link til en stump kode, der læser adressen på forbundne I2C devices, fandt jeg ud af, at mit display har adresse 0x3C.
Og da jeg igen begyndte at “lege” ESP32, havde jeg glemt, at når en ESP32 skal have downloadet en ny software, skal man trykke “enable” knappen (den til højre, når USB stikket vender hen mod en) ned, når Arduino IDE er næsten færdig med at kompilere, og dernæst holde den nede indtil der ikke længere tegnes prikker efter “connecting” ordet i output vinduet. Husk endvidere, at serial monitor skal åbnes via Arduino IDE menuen. Det er ikke det samme som output vinduet.
Til motorstyringen ser det ud til, at AccelStepper er et godt bud: AccelStepper på GitHub hhv. AccelStepper hjemmeside – dog med det lille problem, at man kan vælge enten at synkronisere flere motorer eller at have styr på acceleration og deceleration. Jeg har behov for begge dele på samme tid.
Design af WEB siden:
I første omgang er det OK med en helt tekstbaseret side. Grafik kan altid tilføjes.
Til drift af elevatoren skal der være følgende:
- Knapper til at vælge, hvilken etage, der skal flyttes til
- De samme status informationer, som vises på OLED displayet
- Måske noget debuginformation – en log eller lignende
Og til kalibreringen skal der være:
- Et antal rubrikker med hver sin kalibreringsparameter:
- Øjeblikkelig værdi
- +/- knapper til at ændre værdien
Det ser sådan ud i virkeligheden (screenshot fra den “tykke” UI nævnt længere nede, men samme princip i HTML udgaven):
Mht. kalibrering:
Der er 100 mm eller 50 spindel-omdrejninger a 200 steps = 10.000 steps mellem to etager. Dvs.:
- Etage 1 og 2 er 0 steps
- Etage 3 og 4 er 10.000 steps
- Etage 5 og 6 er 20.000 steps
- Etage 7 og 8 er 30.000 steps
- Etage 9 og 10 er 40.000 steps
- Etage 11 og 12 er 50.000 steps
Disse tal skal kunne kalibreres for hver side. Og skulle det vise sig, at der er forskelle på opadgående retning og nedadgående retning, så må PosLogic laves, så der altid positioneres ved at stoppe lidt for lavt, og dernæst køre langsomt op til den kalibrerede position. Dvs., at der skal kalibreres på samme måde.
Det skal være sådan, at der først vælges en etage, hvorefter elevatoren bevæger sig hen til den etage, ud fra den kalibrering, der allerede findes, hvorefter kun de to relevante kalibrerings-paramtere (dvs. højre og venstre) kan ændres. Resten skal være disablede.
Og så snart en parameter ændres, skal den pågældende stepper motor bevæge sig tilsvarende.
12. oktober 2023
Software’n fungerer i store træk. Men det gør ESP32 WIFI forbindelsen ikke. Den er så svag, at jeg stort set ikke kan få den til at connecte. Og selv når forbindelsen kommer op, vil den ikke rigtigt blive oppe. Måske kan det fikses med et WIFI access point lige ved siden af elevatoren. Eller en bedre antenne på ESP32. Der kan loddes en ekstern antenne på. Eller det kan løses med en ESP32 med Ethernet port. De findes.
Men jeg overvejer i stedet at flytte WEB serveren over på PC’en. F.eks. implementeret vha. Flask. Og så ellers bruge Serial forbindelsen i ESP32 til at kommunikere mellem PC og ESP32. Og Web Serial API i JavaScript koden i browseren for at få fat i USB porten i PC enden.
Eller jeg kunne lave det som en gammeldags applikation med et user interface. For at holde mig til noget, der delvis kan genbruges inde fra JMRI, bliver det nok en kombination af Python, PySerial og TkInter. Som her.
Så må jeg senere bestemme mig for, om jeg også bruger PySerial fra JMRI, eller om jeg bruger den serielle kommunikation fra Java / Jython.
Ved at gå Web server vejen vil jeg kunne have både stand-alone applikationen (i det tilfælde browseren) og JMRI til at kommunikere med elevatoren samtidig. Det vil jeg ikke kunne med den “tykke” applikation, idet både JMRI og applikationen skal bruge samme COM/USB port i PC’en. Altså med mindre JMRI i stedet kommunikerer igennem min applikation. Eller med mindre jeg udstyrer ESP32 med et ekstra USB interface. Det kan man sagtens. Men jeg ser ikke det behov. Lad os se på use cases:
- Ved automatisk togdrift har jeg ikke brug for stand-alone applikationen. Jeg kan holde øje med elevatoren både på det fysiske display og i JMRI.
- Ved kalibrering har jeg ikke brug for JMRI. Den funktionalitet har jeg kun tænkt mig at implementere i stand-alone applikationen. JMRI skal kun skulle skifte etage og måske vise status.
- Når jeg bare vil køre tog direkte fra ECOS, kan jeg styre elevatoren derfra. Eller fra enten stand-alone applikationen eller JMRI.
Jeg tror ikke nødvendigvis, at det er super svært. I grove træk handler det om at fjerne SDWebServer, OTAHandler og AjaxHandler. Og til gengæld indføre en AjaxOverSerial i både ESP32 enden og browser / JavaScript / Python / JMRI / Jython enden. Jeg kunne også fjerne SD kortet og i stedet gemme kalibreringsdata på PC’en. Men derved ville elevatoren ikke kunne fungere uden PC, så det ville være en dårlig ide. Men eftersom SD kortet kun ville komme til at indeholde en enkelt fil med i alt 24 long integers, så vil jeg nu nok fjerne den alligevel og i stedet bruge Preferences biblioteket og den indbyggede flash memory.
Definition af den serielle kommunikation
Alle kommandoer og svar er en linie tekst.
Kommandoer:
- get status (returnerer statusinformation som defineret nedenfor)
- get calibration <etage> (returnerer kalibrerede data for etagen)
- get position <etage> (returnerer status og position for steppere og end-stop switche)
- get lock (returnerer låsestatus)
- set calibration <etage> <venstre> <højre> (sætter og persisterer kalibreringsdata for etagen)
- set level <etage> (flytter elevatoren til den angivne etage)
- set levelfromcalibration <etage> <venstre> <højre> (flytter elevatoren til de angivne stepper positioner)
- set lock <ja/nej> (sætter låsestatus)
- move <left/right/both> <steps> (flytter den ene (eller begge) stepper(e) som angivet)
Svar:
- CALIBRATION <etage> <venstre> <højre>
- LOCKED <ja/nej>
- STATUS <statusinformation>
Parametre:
- <etage>: et tal mellem 1 og 12
- <venstre>: antal steps for venstre motor, der svarer til etagen
- <højre>: antal steps for højre motor, der svarer til etagen
- <ja/nej>: enten YES eller NO
- <left/right/both> enten “left” eller “right” eller “both”
- <steps> ved angivelse af fortegn (f.eks. +14 eller -14) menes et relativt antal steps. Uden fortegn flyttes til den absolutte position.
- <statusinformation>, som er en af følgende:
- BLOCKED Noget blokerer lyset for en af IR sensorerne.
- HOMING 1 Elevatoren homer. På vej ned til endestop.
- HOMING 2 Elevatoren homer. Leder efter det sted, hvor endestop deaktiveres.
- HOMING 3 Elevatoren homer. På vej lidt højere end 1. etage.
- HOMING 4 Elevatoren homer. På vej ned til etage 1.
- MOVING <fra> <til>
- IDLE <etage>
STATUS svaret bliver også sendt som event, når der sker en ændring, Dvs. når status skifter, når enten der bliver sendt kommandoer via DCC eller når en etageændring er udført.
19. oktober – Initialisering
Efter at jeg har prøvet at forbinde mit Python UI til elevatoren, har jeg opdaget, at ESP32 genstarter hver gang jeg genstarter UI’et (dvs. formentlig hver gang UI’et forbinder til ESP32). Det har fået mig ind på den tanke, at homing ikke er noget man selv skal styre og ej heller noget, der kun sker lige efter opstart. Det skal være just-in-time, dvs., når der bliver bedt om at skifte etage. Og selvfølgelig kun, hvis det ikke allerede er gjort.
20. oktober 2023 – Næsten alt fungerer
Nu er det hele implementeret. Og kun JMRI delen er helt utestet. Desuden er homing funktionen ikke fuldt testbar, før elevatoren findes i virkeligheden. Men motorerne kører skam jvf. ovenstående.
I dag har jeg både testet DCC interfacet og skrevet (men altså ikke testet) JMRI interfacet.
DCC interfacet havde et par fejl. For det første havde jeg ikke forbundet optokobleren rigtigt. Og for det andet kan jeg ikke bruge Märklin drejeskive definitionen i ECOS. Den kommunikerer naturligvis ikke via DCC. Så i stedet har jeg nu defineret tre signaler med hver fire aspekter. Og det fungerer fint. 7 minutter inde i denne video bruges der dog en anden opsætning af ECOS, som nok er lidt smartere: https://youtu.be/sEMklCIiHEo?si=Sb-RR_vqEFoCq4I6 Og det samme her: https://github.com/jvstrn/DCC-Controlled-Kato-Turntable
JMRI interfacet har jeg implementeret som en udvidelse af mit script til automatisk drift via warrants. Også dette har jeg lagt på GitHub til almindelig afbenyttelse.
Udvidelsen består af denne klasse, som kun instantieres i et eksemplar (i sin egen tråd), og som kommunikerer med RunTrain trådene vha. nogen Jython Queue køer. Se definitionen af interfacet samt selve implementeringen i ovenstående link.
16/9-2024: Relæmodul tilføjet
Jeg har besluttet at erstatte noget mekanik med noget elektronik og noget software. Og det er allerede gjort. Det handler om strøm til skinnerne, hvor jeg er gået væk fra mekaniske kontakter og over til relæmoduler koblet til ESP32’eren vha. en MCP23017. Opdateringen kan ses på GitHub.
28/9: IR sensorer tilføjet
Jeg besluttede mig for at dømme elevatoren blokeret indtil 20 sekunder efter, at noget er detekteret i bare en af de fire portaler.
4/10-2024: IR modtager tilføjet
Ved hjælp af Arduino biblioteket IRremote (den som shirriff har lavet), har jeg tilføjet yderligere en måde at få elevatoren til at bevæge sig til et nyt niveau, nemlig vha. fjernbetjeningen til mit TV. Så nu er der disse muligheder:
- DCC
- JMRI (via mit script)
- USB / min serielle protokol fra min Python GUI
- USB / min serielle protokol fra en seriel kommandolinie, f.eks. fra Arduino IDE’en
- TV’ets fjernbetjening
- WIFI / HTML siden (ikke færdigudviklet, og bliver det heller ikke)
19/10-2024: Testet med næsten samlet elevator
Det var med noget bæven, at jeg tændte for strømmen til stepperne efter at have samlet hele mekanikken. For motorerne er muligvis stærke nok til at ødelægge mekanikken, hvis den er gal med software’n.
Jeg begyndte forsigtigt med at granske PosLogic koden omkring homing. Hvor der da også var adskillige fejl.
Desuden implementerede jeg et par kommandoer på den serielle konsol til at bevæge en motor samt til at aflæse nuværende position og status på stepperne og end-stop switche. Derved kunne jeg teste, at motorerne kørte den rigtige vej, og at der ikke var byttet rundt på højre og venstre.
Men til slut måtte jeg til det og trykkede 1 på fjernbetjeningen. Det virkede skidt. Meget skidt.
I starten bevægede elevatoren sig meget lidt. Det viste sig at være nogen DIP switche på stepper driverne, der var sat til at køre 8 microsteps pr. step. Så det var nemt at løse. Men derefter kørte det for hurtigt. Jeg havde konfigureret AccelStepper librariet til at køre med en maksimal hastighed på 1500 steps pr. sekund. Det satte jeg først ned til 100, dernæst 200, så jeg kunne teste videre uden at smadre alting.
På et tidspunkt kom der meget grimme lyde og det hele satte sig fast, fordi motorerne ikke bevægede sig synkront og trak hele elevatoren skæv.
Udover mine egne kodefejl, kæmpede jeg længe med, at efter at homing funktionen havde bevæget motorerne ganske forsigtigt og fundet det eksakte sted, hvor endestop switchene deaktiveres, så flyttede den ene eller begge motorer pludselig elevatoren en centimeter eller to. Øv!
Det lod til, at AccelStepper librariet blev forvirret over, at jeg gentagne gange beder motorerne om at flytte 1/10 omdrejning (20 steps), samtidig med at der skal accelereres op og ned. Jeg endte med at initialisere AccelStepper librariet til at sætte hastigheden på motorerne til 600 steps (svarende til tre omdrejninger eller 6 mm) pr. sekund og accelerationen til et meget højt tal (50.000 steps pr. sekund pr. sekund), så der i praksis startes og stoppes så abrupt, som hardwaren fysisk er i stand til. Det betyder, at motorerne nu stopper, hvor jeg beder dem om at stoppe.
Og det betyder, at det tager et sekund at bevæge elevatoren seks millimeter. Dvs. 90 mm / 6 mm/s = 15 sekunder at bevæge elevatoren fra en etage til den næste. Og det passer meget godt i praksis. Jeg har målt det med et stopur. Der er i øvrigt kun 5 etager (eller 5×15 = 75 sekunder) fra top til bund af elevatoren.
Sluttelig har jeg rettet homing logikken og groft rekalibreret elevatoren, idet der kun er 90 mm (og dermed 9000 steps) og ikke 100 mm / 10000 steps pr. etage og idet etage 1 og 2 ligger på -2500 steps, fordi det svarer til, at endestop switchene er 25 mm inde på switch aktuatorerne.
20/10: UP/DOWN knapperne på TV fjernbetjeningen implementeret.