BeneluxSpoor.net forum

Vraag en antwoord => Elektronica en analoog => Topic gestart door: meino op 11 October 2019, 13:07:40

Titel: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 October 2019, 13:07:40
Ik heb ooit een draadje gestart rond Automatisering voor Kranenberg (https://forum.beneluxspoor.net/index.php?topic=79241.msg3221842298#msg3221842298). Destijds had ik nog geen kennis en ervaring met de CanBus, dat kwam later tijdens de discussies in dat draadje. Momenteel ben ik zover dat ik een goed werkende CanBus implementatie aan de gang heb. Ik had verder kunnen gaan in het oorspronkelijke draadje, maar het leek me beter om met een schone lei te beginnen en een nieuw draadje te starten gewijd aan de combinatie van Arduino's met een CanBus.

Groet Meino

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Martin Hornis op 11 October 2019, 15:42:56
We gaan het beleven. Je hebt een mooie spreuk in jouw onderschrift staan.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 October 2019, 15:56:29
Ok laten we eens beginnen.

Het is begonnen met een idee. Oorspronkelijk had ik allerlei Arduino systemen aangesloten, via DCC en via S88. Sommige daarvan had ik met de I2C bus aan elkaar geknoopt. Ook door de wirwar van de bedrading die hierdoor ontstaan was, beviel me dat niet.  Conclusie; ik heb een bus nodig. Daar is veel keuze in, na wat discussies in het oorspronkelijke draadje, heb ik besloten om met de CanBus te gaan werken. Voor de geinterresseerden: https://en.wikipedia.org/wiki/CAN_bus (https://en.wikipedia.org/wiki/CAN_bus)

Het systeem dat ik uitgerold heb voor Kranenberg is gebaseerd op het volgende schema:
(https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg) (https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg)
Er zijn twee koppellingen tussen de klassieke DCC omgeving van de DCC Centrale, de DCC-Interface en de S88-Interface. De DCC-interface zet de ontvangen DCC commando'(op dit moment aleen de wissel/sein commando's) om in specifieke Canbus berichten. De S88-interface zet alle ontvangen bezetmeldingen om in een S88 shiftregister welke door de Centrale periodiek wordt uitgelezen. De S88-interface combineert meerdere 16 bit banks, het aantal daarvan is flexibel. Daardoor is er slechts een aansluiting nodig voor alle S88 bezetmelders met de centrale, dus geen noodzaak om zaken door te lussen.
   De Wissel-, Sein- en Draaischijf-controller, luisteren op de Canbus naar Wissel/sein commando's en bij ontvangst wordt het betreffende commando uitgevoerd, ten minste als het een wissel/sein betreft dat door een specifieke controller wordt bediend. Er kunnen n.l. meerdere van deze controllers aan de bus hangen, ieder met zijn eigen groep van seinen en wissels.
   De Bezetmelder-controllers monitoren hun bezetmelders en bij een wijziging wordt voor de betreffende bezetmelder een bezetmelder bericht op de bus gezet. Ook de Wissel-controller zal bij een wijziging van de wisselstand dit doorgeven in een bezetmelding (Hierdoor kan ik in Koploper bepaalde zaken regelen).

Een puntje van aandacht, ik heb tot nu toe geen behoefte gehad om al deze controllers dynamisch te kunnen configureren. Zo vaak wijzig ik de topologie van de baan niet, dus alles is gecodeerd in de Arduino schets en als er iets mocht wijzigen, dan pas ik de schets aan en laad ik de betreffende arduino's opnieuw. Wel is het zo dat ik bijvoorbeeld voor de wissel-, sein- en bezetmelder controllers slechts drie schetsen onderhoud, waarbij ik iedere specifieke Arduino zijn eigen systeem identificatie geef (Via jumpers op 4 pinnen), zodat hij weet welk gedeelte van de gemeenschappelijke configuratie voor hem geldt.

Tot zover even deze korte inleiding.

Groet Meino




Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 October 2019, 16:31:49
We gaan het beleven. Je hebt een mooie spreuk in jouw onderschrift staan.

Mijn werkplek  ;)

(https://images.beneluxspoor.net/bnls/20191011_162441_DSC_0925.jpg) (https://images.beneluxspoor.net/bnls/20191011_162441_DSC_0925.jpg)

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: AlbertG op 11 October 2019, 16:45:09
Hoi Meino,
Interessant onderwerp.
Bestaan CAN-bus, de interfaces en controllers uit Arduino's met schetches of moet er elektronica gesoldeerd worden?
In het eerste geval blijf ik aangesloten, in het laatste geval haak ik (waarschijnlijk) af.
Ik heb een werkende DCC++ centrale, en ben vooral geïnteresseerd in de S88 interface.

Succes met de ontwikkeling.
Albert.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: ex44 op 11 October 2019, 17:12:12
If a dirty desk means a dirty mind, what does an empty desk mean?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 October 2019, 17:30:11
Dag Albert

Er moet wel wat simpel soldeerwerk gedaan worden. Ik heb de S88 interface met een UTP patchkabel (ethernet) op de Centrale aangesloten. Daarvoor heb ik een connector op een bordje gesoldeerd. Verder de connector aan wat pinnen gesoldeerd, zodat de handel met jumpercables aan de Arduino is verbonden. Uiteindelijk gaat het er om dat de S88 signalen op een aantal pinnen van de Arduino uitkomen, daar zit verder geen electronica tussen.
Voor de DCC interface is er wel een klein schakelingetje met wat weerstanden en een opto isolator op een stukje stripboard gesoldeerd om de DCC-controller fysiek met de DCC te verbinden.

Dit is het schema dat ik daarvoor gebruikt heb.
(https://images.beneluxspoor.net/bnls/DCC-optoisolator-for-Arduino.png) (https://images.beneluxspoor.net/bnls/DCC-optoisolator-for-Arduino.png)

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 October 2019, 17:32:34
@44

 :D dat was het originele antwoord dat Albert Einstein gaf toen iemand het nodig vond om een opmerking over de chaos op zijn bureau te geven.
Overigens was dit motto al jaren in gebruik als weerwoord op baasjes die iedere keer probeerden om een "clean desk" policy in te voeren. Let wel we praten over een tijd voordat internet en google gemeen goed waren en ik samen werkte in een groep hackers die soms wedstrijden hielden wie de hoogste stapel op zijn bureau kon bouwen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 October 2019, 20:08:00
Grappig, dat motto heb ik al een hele tijd, en nu valt het een aantal mensen opeens op :D

Maar goed, terug naar het onderwerp van dit draadje.

Ik gebruik voor de fysieke interface naar de Can bus het volgende kaartje:

(https://images.beneluxspoor.net/bnls/MCP2515-TJA1050.jpg) (https://images.beneluxspoor.net/bnls/MCP2515-TJA1050.jpg)

Dit  is een MCP2515 met TJA1050 receiver, deze kaartjes zijn voor minder dan 2 euro te koop op Aliexpress of Ebay. Overigens zijn ze vrij beperkt voor wat betreft de prestaties. Voor een deel wordt dat waarschijnlijk veroorzaakt doordat ze op 8Mhz draaien. Ik ben van plan om nog eens de 8Mhz oscilator te vervangen door 16 of 20Mhz om te kijken wat dat voor invloed heeft. In ieder geval bij juist gebruik is de prestatie voldoende voor mijn toepassing.
    Voor de aansluiting aan de arduino verwijs ik naar het internet, daar zijn genoeg schema's te vinden, alhoewel het eigenlijk simpel is, de communicatie tussen Arduino en het kaartje vindt plaats d.m.v. de SPI bus. Alleen de CS pin (voor de selectie van het actieve SPI device) is vrij. Ik gebruik daar meestal pin 9, 10 (op de UNO) of 53,54 (op de Mega) voor.

Voor het testen van mijn CanBus bibliotheek heb ik ook een Testsysteem opgezet, bestaande uit 2 Arduinos en 4 MCP2515 kaartjes.

(https://images.beneluxspoor.net/bnls/CanBus-test-rig.jpg) (https://images.beneluxspoor.net/bnls/CanBus-test-rig.jpg)

Logisch schema van de test.
(https://images.beneluxspoor.net/bnls/TestSchema_2.jpg) (https://images.beneluxspoor.net/bnls/TestSchema_2.jpg)

De test is in principe vrij simpel, beide UNO's zenden in een continue stroom berichten naar de andere kant. Tegelijkertijd ontvangen beide UNO's ook de berichten van de andere kant. Door in de berichten tellers en andere identificatie op te nemen wordt bij ontvangst gecontroleerd of alles correct ontvangen is. De timing tussen de berichten is tijdens de test gevarieerd. Uit deze stresstest blijft het goed werken tot het moment dat beide UNO's 250 berichten/sec verzenden, Dus een totaal 500 berichten/sec is mogelijk. Dat is in mijn optiek voldoende voor ons gebruik, het is meer dan wat er via DCC verzonden kan worden (200-220/sec)

Een aantal observaties gedaan tijdens het testen.
- De bus snelheid die gebruikt wordt is 500Kb/sec, Theoretisch zou de MCP2515 chip ook 1Mb/sec aan kunnen, maar gebruik van die snelheid kan het kaartje niet echt aan, want dan stort de betrouwbaarheid in. Ook bij lagere snelheden neemt de prestatie weer sterk af. Dus 500Kb is optimaal.

- Een kaartje gebruiken voor gecombineerd ontvangen en zenden is zeer slecht voor de prestaties. In dat geval valt de prestatie met 80-90% terug. Vandaar dat voor systemen die zowel berichten ontvangen als wel berichten moeten zenden, er twee MCP2515 kaartjes gekoppeld worden, een voor zenden en een voor ontvangen. Dat is ook op het testsysteem gedaan.

- Het MCP2515 kaartje behoort niet tot de meest snelle systemen. Uit veel berichten op Internet blijkt dat vooral het gebruik van de SPI interface om data uit de kaarten en data naar de kaart te sturen veel tijd kost.

- Het gebruik van de interrupt pin met interrupt handlers in de Arduino verslechtert de prestaties aanmerkelijk, Vermoedelijk heeft dat te maken met de hoeveelheid interrupts die de Arduino dan voor zijn kiezen krijgt en de bijbehorende context switches welke in de Arduino code afgehandeld moet worden. De beste prestaties krijg je als je de MCP2515 kaart polt

- Volgens de specificaties heeft de MCP2515 3 transmit buffers en 2 receive buffers. Zonder in de specs te duiken, was mijn aanname dat de kaart dus 2 berichten kan opnemen voordat er een overrun plaats vindt. Dat is dus niet zo. De eerste buffer wordt gebruikt voor z.g. "High priority" berichten en de tweede buffer voor z.g. "Low Priority" Berichten. Wat High of Low is wordt bepaald door de mask en filter setting. Er zijn twee masker registers en 6 filter registers. 2 van de filter registers horen bij de eerste mask register, de andere 4 horen bij de tweede mask register. Een High priority bericht is een bericht dat geaccepteerd wordt door de eerste mask en filter registers. Een bericht dat pas door de tweede mask en filters wordt geaccepteerd is een low priority bericht. In de praktijk houdt dat dan ook in dat er eigenlijk maar een receive buffer gebruikt wordt. Overigens heb ik helaas moeten constateren dat het gebruik van de tweede mask en bijbehorende filters veel overhead op het MCP2515 kaartje tot gevolg heeft met de bijbehorende negatieve invloed op de prestaties. Ik gebruik dan voor de filtering maximaal 2 filters. Bij meer filters, zet ik de filtering in de kaart af en ga over naar filtering in de arduino code.

Tot zover. In de volgende bijdrage ga ik wat dieper in op de Bibliotheek die ik ontwikkeld heb om de hardware aan te sturen.

Met vriendelijk groet
Meino





Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 October 2019, 00:09:20
Tijd om weer verder te gaan.

Essentieel om Can-Bus te begrijpen is het feit dat je berichten niet naar een ander systeem stuurt. Op de Canbus publiceer je een bericht, en dat gepubliceerde bericht kan in principe door alle aangesloten systemen gelezen worden. Het is dan aan de andere systemen om te beslissen of ze een bericht moeten verwerken. De zender weet dan ook niet of er iemand is die er wat mee doet, het enige dat hij kan zien is dat het bericht met succes is gepubliceerd.
Om dit in goede banen te leiden begint ieder bericht met een ID (identificatie) veld. De inhoud van het ID veld moet uniek zijn voor ieder systeem dat aan de bus hangt. Er zijn 2 mogelijk groottes voor het ID veld, in std mode is het ID veld 11 bits, in extended mode is het 29 bits groot. Momenteel gebruik ik alleen de std mode, dus 11bit ID's.
In dit syteem zijn de ID's als volgt: bit 1-7 (LSB 0) bevat een uniek systeem nr dat gekoppeld is aan ieder specifiek systeem aan de Can-bus. Hierdoor kan ik maximaal 127 systemen koppelen. Om enige ordening te krijgen heb ik dit gekoppeld aan het systeemtype (DCC-Interface, S88-Interface, Bezetmelder-controller, Sein-controller en Wissel-controller) d.m.v Enum definities in de bibliotheek. Momenteel is daarvoor het volgende gedefinieerd:
-   DCC-Interface                 127
-   S88-Interface                  126
-   Bezetmelder-controller    1-16
-   Wissel-controller             17-32
-   Sein-controller                 33-48
Voor ieder type controller is er nu ruimte voor 16 exemplaren, dat is voor mij nu voldoende. Overigens is er nog genoeg ruimte omdat uit te breiden. Ook is er nog ruimte voor toekomstige uitbreidingen. Ieder type interface komt slechts 1 keer voor. Verder gebruiken de controllers pin a0-a3 om een digitaal nummer te zetten dat tijdens setup gelezen kan worden en gebruikt wordt om de type enum op te hogen zodat het echte systeemnr ontstaat dat in de ID kan worden gezet. Hoe dat in de code er uitziet daar zal ik later nog wel laten zien.
Bit 8-11 (4bits ook LSB 0) bevatten het berichttype. Op dit moment zijn er 3 berichttypes gedefinieerd.
-   OCC_DT_MSG       1         Berichten met de status van een bezetmelder
-   DCC_ACC_MSG     2         DCC wissel/sein commando
-   DCC_LOC_MSG     3         DCC lokomotief commando (nog niet in gebruik)

Ter verduidelijking, LSB 0 betekent Least Significant Bit heeft index 0. Dat betekend dat de bit nummering in het ID veld van rechts naar links gaat (analoog aan de bitSet() functie in een Arduino schets).

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 October 2019, 16:34:01
Door deze indeling ondersteun ik maximaal 15 berichttypes (0 wordt niet gebruikt omdat die waarde niet goed te filteren was op de MCP2515 kaart). Dat leek mij voldoende ook voor de toekomst.
   Een eigenschap van de CanBus is dat berichten met een lagere ID waarde automatisch een hogere prioriteit krijgen. Dat is de achtergrond voor de type keuzes. Hoogste prioriteit hebben de berichten van de bezetmelders, die zenden een status wijziging slechts 1 maal uit. De DCC accessory (wissel-seinen) commando's worden door de meeste centrales een aantal keren na elkaar verzonden, en de lokcommando's worden nog vaker herhaald.

Data in een Canbus bericht kan 1 tot 8 bytes lang zijn.

In het bezetmelder bericht, type OCC_DT_MSG, worden twee bytes als data verstuurd. De eerste byte bevat het nr van de bezetmelder en het tweede byte zijn status.
    Overigens de waarde van het bezetmeldernr volgt een andere conventie dan vaak gebruikt wordt in besturingsprogramma's. In Koploper heeft de eerste bezetmelder van de eerste groep van 16 bezetmelders het adres 1.01. Het bijbehorende nr in het bericht is echter 0x00. Bezetmelder 1.02 wordt 0x01, 1.15 wordt 0x0F, 2.03 wordt 0x12 etc. In totaal kunnen dus 256 bezetmelders ondersteund worden.
De tweede byte bevat de status, 0 is niet bezet, 1 is bezet.

In het Accessory (wissel/sein) bericht, type DCC_ACC_MSG, bevat de eerste byte het adres van het wissel of sein zoals dat in het treinbesturings programma is vastgelegd. Op dit moment ondersteunt de bibliotheek slechts 255 adressen en niet de maximale 2044 die volgens de DCC normen mogelijk zouden zijn. Dat is voor later en heeft ook te maken met de gebruikte centrale. De wissel/sein stand wordt bepaald door de waarde in het tweede byte. 0 betekend rechtdoor of onveilig wissel. en 1 betekend afbuigend/veilig.
Overigens dit hangt af van de centrale en hoe het treinbesturings programma zich gedraagt of hoe seinen en wissels zijn geconfigureerd. Dit is wat ik zelf krijg in mijn combinatie van Koploper met de MDRRC II centrale.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 16 October 2019, 23:38:21
Nog even een kleine update, tot nu toe had ik een maximum van 255 wissels/seinen en bezetmelders ingebouwd. Alhoewel dat voor mijn toepassing, Kranenberg meer dan voldoende is, leek het met toch bij nader inziens om wat binnen de DCC normen mogelijk is in te bouwen. Dus heb ik het adres veld in de berichten uitgebreid van 1 byte naar 2 bytes (een unsigned short) zodat ik nu 2044 wissels/seinen en 512 of meer bezetmelders kan ondersteunen. Dus de lengte van een OCC_DT_MSG en DCC_ACC_MSG is nu 3 bytes lang.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 24 October 2019, 13:21:38
Na de voorgaande introductie, is het tijd om wat dieper in te gaan op de code.
Er zijn twee bibliotheken gebruikt. Een bibliotheek https://github.com/autowp/arduino-mcp2515 (https://github.com/autowp/arduino-mcp2515) komt van Github, deze is er om het gebruikte MCP2515 kaartje direct via SPI en zijn hardware registers aan te sturen. Omdat ik zelf er niet van hou om iedere keer weer een soms,complexe serie commando's uit te voeren, heb ik zelf een bibliotheek gemaakt die dit in een aantal simpele methods (functie aanroepen) regelt. Dit is de KB_CanBus bibliotheek. Voor hen die zelf hiermee aan de slag willen, neem contact met mij op via een PB en ik maak dan wel een zip bestand met beide bibliotheken.

Wat doen we om de toegang naar de Canbus te activeren. Daarvoor zijn er twee klassen gedefinieerd in de KB_CanBus bibliotheek. N.l. de mcp2515 en de CanBus. Een mcp2515 object representeerd een fysiek Mcp2515 kaartje. De creatie van dit object kent slechts een parameter, n.l. de digitale pin op de Arduino waarmee de CS  pin (SPI device selectie) van het betreffende kaartje is verbonden.

Voorbeeld:
mcp2515 *sender = new mcp2515(10);

Dit object hoeven we verder niet in de code van de schets aan te spreken, het wordt slechts gebruikt om aan de echte interface door te geven. Deze inteface die we aanspreken is een Object van het type CanBus. Tijdens de creatie van dit object geven we het als parameters een unieke systeemidentificatie en  het adres van een of twee mcp2515 objecten mee.

Voorbeeld:
//
// Ieder systeem heeft een uniek nummertje nodig op de Canbus. Hoe we aan dat nummer komen
// laten we hier nog even open.
//
int systemId = .......;

//
// Canbus interface die alleen berichten moet ontvangen
//
CanBus *messageReceiver = new CanBus( systemId, nullptr, new mcp2515(9));

//
// Canbus interface die alleen berichten moet zenden
//
CanBus *messageSender = new CanBus(systemId, new mcp2515(10), nullptr);

//
// Canbus interface die berichten moet ontvangen en zenden (er zijn 2 mcp2515 kaartjes)
//
CanBus *cbSenderReceiver = new CanBus(systemId, new mcp2515(10), new mcp2515(9);

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 24 October 2019, 17:54:54
Even iets over de systemId die gebruikt wordt om het CanBus object te maken.
Iedere systeem dat berichten op de Canbus zet moet zorgen dat het ID (identificatie) veld van het bericht uniek is ten opzichte van de berichten die door andere producenten worden geproduceert. In voorgaande posts heb ik het daar al over gehad, deze bibliotheek maakt gebruik van standaard berichten, dus een ID veld van 11 bits. 4 bits bevatten het berichttype (DCC_ACC, OCC_DT etc) de overige 7 bits worden gevuld met het systemId zoals dat tijdens de creatie van het CanBus object wordt doorgegeven. Het is dus van belang dat iedere Arduino aan de Canbus zijn eigen unieke systemId krijgt.
Daarom zijn in CanBus.h een aantal type producenten gedefinieerd.

Een stukje uit CanBus.h
//
// Id's of the different types of controllers on the CanBus
//      Maximum nr of ID's is 127!!!
//
#define BEZETMELDER_CONTROLLER_ID 1   //  16 Bezetmelder controllers supported
#define WISSEL_CONTROLLER_ID 17       //  16 Wissel controllers supported
#define SEIN_CONTROLLER_ID 33         //  16 Sein controllers supported
#define DCC_INTERFACE_ID 126          //   1 DCC Interface system supported
#define S88_INTERFACE_ID 127          //   1 S88 Interface system supported

De beide interface systemen komen slechts een maal voor, dus die hebben een vast systemId. Voor de huidige controllers heb ik ruimte voor maximaal 16 stuks van een type gereserveerd (dat kan natuurlijk aangepast worden door de waarden in CanBus.h aan te passen).
Voor de controllers gebruik ik deze definities als start die ik daarna ophoog met een vaste waarde voor iedere controller. Dat kun je doen door in de EPROM van iedere Arduino een eigen nummer te zetten, wat tijdens setup uitgelzen wordt. of zoals ik het doe, d.m.v. jumpers naar aarde op pinnen A0,A1,A2 en A3 waarmee ik als het ware een digitaal (4bits) getal maak.

Een klein voorbeeld uit de setup method van de DCC Interface
//
//  The CanBus
//
CanBus *canBus;

void setup()
{


  //

  //  Start the CanBus communication
  //
  canBus = new CanBus(DCC_INTERFACE_ID, new Mcp2515(10), nullptr); // Only a sender
  canBus->setSendWaitTime(4); // Set minimum delay between messages
  canBus->begin();        // Make the CanBus interface active


}// End of setup

In deze twee voorbeelden zien we naast de creatie van de CanBus objecten ook het gebruik van twee methods (functies) van het gecreerde object. de begin method, zorgt er voor dat de interface start, dit is een

En een stukje uit de setup van de Bezetmelder Controller
void setup()
{
  //
  //  Based on which pin is connected to ground, the controller ID is established
  //
  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  pinMode(A3, INPUT_PULLUP);

  systemId = 0;
  if (!digitalRead(A0)) systemId = systemId+1;
  if (!digitalRead(A1)) systemId = systemId+2;
  if (!digitalRead(A2)) systemId = systemId+4;
  if (!digitalRead(A3)) systemId = systemId+8;

  //
  //  Start the CanBus communication
  //
  canBus = new CanBus(BEZETMELDER_CONTROLLER_ID+systemId, new Mcp2515(10), nullptr); // Only a sender
  canBus->begin();      // Make the CanBus interface active
}

We zien dat we na de creatie van het CanBus object er ook een aantal methods (functies) van het object uitgevoerd wordt. Wat altijd moet gebeuren is de begin() method, deze initializeerd en activeerd de inteface naar de Canbus. Maar voordat je dit doet, is er de mogelijkheid om een aantal parameters van het gecreerde object aan te passen. Bijv. setSendWaitTime(4) dat bij de DCC Interface wordt gebruikt, veranderd de minimale wachttijd tussen 2 op een volgende berichten die deze Arduino op de Canbus zet. Standaard is die 10ms (max 100berichten/sec). In dit geval wordt dit op 4ms gezet (250berichten/sec).
De reden achter dit is, dat het MCP2515 kaartje niet al te snel is, en dat als er teveel berichten te snel aangeboden worden, de kans op verloren berichten vrij groot is. Hiermee wordt gezorgd dat een enkel systeem niet de bus kan verzadigen.

groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 24 October 2019, 23:56:43
We hebben nu een CanBus object geinitialiseerd en klaar voor gebruik, maar verder doet hij nog niet veel. Om een receiver actief te laten luisteren naar berichten en een zender aangeboden berichten op de bus te laten zetten, moet in de loop() functie van een schets, constant de heartBeat() method van de CanBus object aangeroepen worden.

void loop()
{
  //
  //  Monitor the canBus
  //
  canBus->heartBeat();

  // Andere code

}

Deze heartBeat() method verzorgt een aantal zaken, Als het CanBus object een mcp2515 object als zender heeft, worden berichten die in de send queue staan in het tempo dat is gedefinieerd door de sendWaitTime eigenschap (zie setSendWaitTime()) eigenschap op de bus gezet.
Als het CanBus object een mcp2515 object als ontvanger heeft, dan wordt bij het betreffende MCP2515 kaartje geinformeerd of er een bericht is ontvangen, als dat het geval is, wordt het bericht van de kaart gelezen en aangeboden aan een geregistreerde call back routine voor het type van de ontvangen bericht.

We hebben het nu over het zenden en ontvangen van berichten.
Voordat we daar verder op ingaan eerst even iets over de berichten zelf. Op dit moment zijn er twee typen berichten die gebruikt worden, de derde is wel al gedefinieerd, maar wordt in de huidige systemen nog niet gebruikt.

Een klein stukje uit CanBus.h
//
//  There are several types of messages, each is identified by an unique number
//  the lowest id's have the highest priority
//
//  DCC_ACC   defines a DCC Accesory command
//  OCC_DT    defines an Occupance detector that triggered
//  DCC_LOC   defines a DCC locomotive command
//

enum messageId {
    OCC_DT = 1, DCC_ACC = 2, DCC_LOC = 3
};

typedef struct
{
    unsigned short    address;
    byte              state;
} OCC_DT_MSG;

typedef struct
{
    unsigned short    address;
    byte              direction;
} DCC_ACC_MSG;


Berichten die de status van een bezetmelder bevatten zijn van het type OCC_DT en hun inhoud is de struct OCC_DT_MSG. Berichten die een actie voor een DCC Accessory decoder aansturen zijn van het type DCC_ACC en de berichtdata staat in een struct DCC_ACC_MSG. DCC_LOC is een type voor een toekomstige uitbreiding en wordt nu nog niet gebruikt.

Een klein voorbeeld van het zenden van een bericht.
      //
      //  Send the new state to the S88 Interface
      //
      OCC_DT_MSG buf;
      buf.address = ((s88Bank-1)*16)+(s88Id-1);
      if (occupied)
        buf.state = 1;
      else
        buf.state = 0;

      //
      //  Repeat the message, to cover the occasional mishap
      //
      canBus->sendMsg(OCC_DT, sizeof(OCC_DT_MSG), &buf);
      canBus->sendMsg(OCC_DT, sizeof(OCC_DT_MSG), &buf);
Wat belangrijk is, dat sendMsg() het bericht niet direct op de bus zet, maar dat het bericht slechts op de interne send queue geplaatst wordt. Daarna is het de verantwoordelijkheid van de heartBeat() method om het bericht op de bus te zetten.
Tot zover het zenden van berichten, nu hoe ontvangen we een bericht, dat doen we door een z.g. Call back routine bij het CanBus object te registreren. Dit moet gebeuren in de setup() van de schets.
void setup()
{

  canBus->setMsgReceiver(OCC_DT, occDtReceiver);
 
}
De Call Back routine occDtReceiver wordt vanuit het CanBus object aangeroepen op het moment dat er een OCC_DT_MSG ontvangen is.
en voorbeeld van deze Call back routine:
/
//  A function that executes whenever a message is received from the Canbus
//  It's purpose is to analyze the received event Occupancy detector and
//  update the proper element in the semaphore table or turnout table.
//
void occDtReceiver(unsigned char aMsgLen, OCC_DT_MSG *msgData)
{
  if (msgData->address < nrOfOccDetectors)
  { 
    pendingStates[msgData->address] = msgData->state;

Dit is in grote lijnen hoe de CanBus bibliotheek gebruikt kan worden.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 25 October 2019, 16:02:28

(https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg) (https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg)
Ik snap je elektronica topologie niet helemaal. Arduino's kunnen met een beetje elektronica DCC direct uitlezen, dat doet je DCC interface waarschijnlijk ook. Als je bijvoorbeeld met een arduino wissels wilt aansturen om geld te besparen dan zou ik ze direct aan de DCC hangen. Ik zie geen reden om je DCC berichten eerst te vertalen voor de can bus en dan om via je can bus wissels aan te sturen. Met een ringleiding heb je toch overal DCC om af te tappen. Je sein en draaischijf controller zijn er ook alleen maar om te ontvangen die kunnen dus ook op de DCC bus.

Misschien dat ik er in alle haast over heen heb gelezen, maar ik snap de losse 'bezetmelder controller' en de S88 interace niet, moet dat niet 1 geheel zijn? Ik snap overigens ook niet waarom je de S88 bus gebruikt. Nu moet ik zeggen, ik weet niet wat die niet-Marklin varianten kosten tegenwoordig, maar ik kon me alleen niet voorstellen dat het voordeliger is dan simpele stroomdetectie printjes te maken voor je arduinos en die te gebruiken voor je terug melding, want ja.. je hebt immers al je eigen can bus aangelegd.

Je MDRRC-II heeft ook een ingang voor de s88 bus. Dus tenzij je can bus gebruikt om grotere lengtes af te leggen dan mogelijk is aan de S88 (geen idee hoe lang deze kan), lijkt het me ook overbodig om ook hier je S88 bus te vertalen naar can en naar je centrale te sturen.

Ik zie dus geen duidelijk voordeel in je can bus implementatie behalve dat je misschien minder kabels hoef te trekken.

Zoals je weet ben ik zelf ook met mijn eigen rs 485 netwerk bezig. En dat heb ik zo ontworpen opdat ik A. zo min mogelijk draden wil leggen en B. om goedkoop uit te zijn en C. het is leuk om te doen  (y).

Ik heb 1 universeel arduino programma die ik kan configureren. En met de I2C bus kan ik tot max ~50cm in beide richtingen van de arduino het netwerk iets uitbreiden en IO extenders plaatsen voor seinen, terugmelders(massa detectie), magneet artikelen, knoppen voor wisselstraten, ontkoppelaars en servo drivers. En ik kan erg veel arduinos aan de rs485 bus hangen. Ik heb hier een motto voor bedacht: "Als je al een bus heb liggen voor arduino's die nagenoeg geen limitaties kennen, dan waarom nog  DCC of terugmeld bussen gebruiken?".

Ik heb van 1 arduino een DCC centrale gemaakt om kosten te besparen. Dat ding bestaat uit nog 10 euro aan onderdelen (excl de voeding). Ik stuur hem aan met een zelf gemaakte handcontroller over bluetooth voor ook een prikkie en ik hoef geen S88 dingen te kopen. Als klap op de vuurpijl gebruik ik ook mijn eigen computer programma die ik graag deel wanneer hij een beetje af is.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 25 October 2019, 21:09:27
Zoals je weet ben ik zelf ook met mijn eigen rs 485 netwerk bezig. En dat heb ik zo ontworpen opdat ik A. zo min mogelijk draden wil leggen en B. om goedkoop uit te zijn en C. het is leuk om te doen  (y).

Om de zelfde reden (het is leuk om te doen) ben ik met de Canbus begonnen.
Maar het is niet helemaal zonder reden geweest dat ik naar dit soort oplossing heb gezocht. Oorspronkelijk had ik inderdaad een aantal systemen die rechtstreeks aan het spoor hinngen en luisterden naar de DCC commando's. Maar in de praktijk had ik best veel problemen doordat een Arduino DCC commando's niet goed ontving. Ook locs hadden en hebben soms een probleem dat ze niet direct op bijv een snelheidswijziging reageren. Bij locs is dat niet al te problematisch omdat deze commando's continue herhaald worden. Echter accessory commando's (voor wissels en seinen) worden niet constant herhaald. Dit hing er ook vanaf waar de betreffende Arduino op het spoor was aangesloten. Vooral als bepaalde locs op de baan reden konden deze problemen best heftig zijn. Waarschijnlijk zetten sommige locs stoorsignalen op de baan. Misschien heeft het met de leeftijd van de locs te maken, de oudste is nu ruim 40jr oud en de jongste is ongeveer 28jr oud.
Ook voor de S88 interface, de Arduino die daar zorg voor droeg, controleerde ook alle bezetmelders. Dat had tot gevolg dat er een forse hoeveelheid aan kabels onder de baan  hing omdat alle aansluitingen van blokken naar een centrale locatie onder de baan gingen waar ook alle bezetmelders gelocaliseerd waren. Vrijwel alle bezetmelders werken met stroomdetectie, behalve twee die gebaseerd zijn op ali lichtsluisjes.

Oorspronkelijk kwam dus alles op een centrale plek onder de baan bij elkaar, maar dat werd veel te complex, en eigelijk niet meer uitbreidbaar. Ik heb toen besloten om alles via ringleidingen en een eigen bus te gaan doen. Ik heb dat uitgebreid in mijn draadje over Kranenberg (https://forum.beneluxspoor.net/index.php?topic=77057.180) beschreven. Oorspronkelijk had ik ook I2C gebruikt maar diverse anderen op dit forum vonden dat geen goed idee, in de discussie over mogelijke alternatieven werd ook de Canbus genoemd. Ook omdat de benodigde hardware goedkoop (1,30-1,60euro voor een MCP2515 kaartje) verkrijgbaar is. Verder zijn er op Internet ook een aantal bibliotheken te vinden om dit kaartje op hardware nivo aan te spreken zodat ik dat niet zelf in de Arduino hoef te coderen.
Het resultaat is nu, dat alle bezetmelder controllers (nu 6 stuks) zijn gedistribueerd geplaatst. Op diverse plekken onder de baan, zo dicht mogelijk bij de bezetmelders die zij controleren, terwijl de S88 interface dicht bij de MDRRC-II centrale zit, er zijn genoeg horror stories op dit forum te vinden over spook meldingen met S88, veroorzaakt door lange kabels.

Maar goed, uiteindelijk vond ik dit (als gepensioneerd IT architect/systeem ontwerper) ook wel erg leuk om terug te keren naar mijn roots en weer wat met C++ te doen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 07 November 2019, 12:59:31
Hoi Meino,

Dit wordt een beetje een raar bericht, alvast excuses hiervoor. :-[ Maar ik had je eerste paar berichten gelezen en was begonnen met een reactie schrijven. Maar de PC waar ik dat op deed kreeg wat problemen dus is dat even blijven liggen :-\ Dus nu pak ik de draad weer op met wat ik had.

Allereest, leuk project! (y) CAN is inderdaad wel goed toepasbaar op de modelspoorbaan denk ik. Heb de modules ook wel liggen maar nog nooit mee gespeeld ::)

Allereerst vind ik het wel erg bijzonder dat twee modules sneller werken dan een enkele ??? Maar goed, zelf de modules nog steeds niet gebruikt. Maar ik keek even snel door de datasheet en kon zo snel niets vinden over kristal instellingen. Kan je die dus zomaar verhogen?


Ter verduidelijking, LSB 0 betekent Least Significant Bit heeft index 0. Dat betekend dat de bit nummering in het ID veld van rechts naar links gaat (analoog aan de bitSet() functie in een Arduino schets).
Ook analoog aan ons dagelijkse 10-tallig stelsel werkt. Maar zodra het om niet 10-tallig gaat willen sommige opeens van links naar rechts getallen lezen ::)

En even "zeuren", gebruik const ipv aan macro (#define) ;) Betere errors berichten, handelt echt als een variabele, type safe etc. Snap niet helemaal dat macro's nog zo veel gebruikt worden :angel:

En heb je een reden om new te gebruiken ipv gewoon het object te definiëren? Kan je gewoon de . notatie gebruiken etc.

Vooral als bepaalde locs op de baan reden konden deze problemen best heftig zijn. Waarschijnlijk zetten sommige locs stoorsignalen op de baan.
Toevallig loc's met LoSo 4's op de baan?

En ach, CAN en RS485 hebben overeenkomsten, beide om te beginnen differentiaal. CAN kent alleen ook al een protocol en topology implementatie waar RS485 alleen een pijp met bytejes is. Maar ben het mee eens dat een decentrale oplossing inderdaad heeeeel veel werk kan schelen (y) En inderdaad, S88 is niet zo heel gunstig voor afstand. S88n al wat beter maar blijft een wat gevoelig systeem, ooit geïntroduceerd om kosten efficiënt te zijn.


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 07 November 2019, 16:16:40
En even "zeuren", gebruik const ipv aan macro (#define) ;) Betere errors berichten, handelt echt als een variabele, type safe etc. Snap niet helemaal dat macro's nog zo veel gebruikt worden
"handelt echt als een variabele"  -> werkt zodoende niet in een case label. Daarom ben je imo het beste af met enums.

Also een #define voor een IO kan prima. Mensen zeggen ook dat je een macro met hoofdletters moet tikken, zodat je kan zien dat het een macro is. Dat kan je beschouwen als een soort type-safety.

"Snap niet helemaal dat macro's nog zo veel gebruikt worden" > macro's worden door veel mensen ten onrechte als "kwaadaardig" beschouwd  en dat slaat nergens op. Een macro is slechts een van velen tools die we hebben in o.a. C en C++. En zoals elke tool komt een macro met voor- en nadelden. Als je iemand dood mept met een hamer, dan is de hamer niet "kwaadaardig" maar "kwaadaardig" gebruikt. Als je hem gebruikt om een spijker in een plank te slaan, is het de perfecte tool. Dus een nadeel van de hamer: je kan er iemand dood mee meppen. Voordeel: je kan er spijker mee in hout tikken.

Het zelfde principe gaat ook op voor macro's. Je kan er de meest onduidelijke code mee maken en jezelf in een debug nachtmerrie werken. Maar je kan ze ook gebruiken om; overhead te verminderen, code te comprimeren en om code leesbaarder te maken.

Ik gebruik zelf macro's om state machines mee op te bouwen. Ik heb dan een lijstje met een enum van states en een switch-case die functie calls uitvoert. De woorden 'case' en 'break' worden door de macro vervangen door 'State'. De functie call wordt vanuit een if statement gedaan en wacht tot de state in kwestie true returnt. Tevens heb ik bij deze macro ook nog ingebakken dat de state automatisch geprint wordt. Dat is soms best handig. Met de # operator zet ik de enum om in een string en met de ## doe ik een functie call naar   someState + F()

#define State(x) break; case x: if(prevState != state) {print("State ");print(#x); print("\n\r");prevState=state;} if(x##F())
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 November 2019, 17:20:43
Dag Timo

toch leuk dat je nog even een reactie geeft.

Allereerst vind ik het wel erg bijzonder dat twee modules sneller werken dan een enkele ??? Maar goed, zelf de modules nog steeds niet gebruikt. Maar ik keek even snel door de datasheet en kon zo snel niets vinden over kristal instellingen. Kan je die dus zomaar verhogen?
Ansich werken ze niet sneller, maar veel betrouwbaarder. Ik denk dat het met de firmware op de MCP2515 chip zelf te maken heeft. Als je een kaartje gebruikt voor zowel zenden als ontvangen dan valt de (betrouwbare) throughput terug naar 10-20 berichten per seconde. Ik speel met het idee om de clockchip op deze kaartjes te vervangen (is nu 8Mhz, maar de chip kan tot 20Mhz aan). Ik weet alleen niet of de andere componenten op het kaartje dat ook aankunnen. Maar omdat het gebruikte kaartje slechts 1,5 euro kost, doe ik niet moeilijk en gebruik 2 kaartjes voor de systemen die ontvangen en zenden.

En even "zeuren", gebruik const ipv aan macro (#define) ;) Betere errors berichten, handelt echt als een variabele, type safe etc. Snap niet helemaal dat macro's nog zo veel gebruikt worden :angel:
Dat zeuren geeft niet, ik weet wel waarom. Maar het zal wel te maken hebben met mijn leeftijd. Ik heb het vak geleerd op een EL X8 met Algol, later jaren als systeemprogrammeur op mainframes in BAL gewerkt. In 83 in het UNIX gebeuren verzeild. Alles deed je met macro's en #defines. Zelfs toen ik begin 90 in Berkeley werkte (C, C++) deden we nog alles met #defines en macro's. Kortom oude gewoontes slijten slecht. Overigens gebruik ik echt wel const's en enums, maar #defines gebruik ik ook nog bewust, vooral als (pre)compiler directives,

En heb je een reden om new te gebruiken ipv gewoon het object te definiëren? Kan je gewoon de . notatie gebruiken etc.
Ja, objecten kun je slecht als parameter in een method gebruiken, dus probeer ik zoveel mogelijk met pointers te werken. Met new kan ik een object creeeren in de constructor van een ander object. Bijkomend voordeel is isolatie.

Toevallig loc's met LoSo 4's op de baan?
Nee, ik heb geen locs met geluid. De probleemgevallen ware een Lima mat46 met de oorspronkelijke rondmotor en een paar Roco locs met de oude, zilveren motor.

En ach, CAN en RS485 hebben overeenkomsten, beide om te beginnen differentiaal. CAN kent alleen ook al een protocol en topology implementatie waar RS485 alleen een pijp met bytejes is. Maar ben het mee eens dat een decentrale oplossing inderdaad heeeeel veel werk kan schelen (y) En inderdaad, S88 is niet zo heel gunstig voor afstand. S88n al wat beter maar blijft een wat gevoelig systeem, ooit geïntroduceerd om kosten efficiënt te zijn.
Yep, daarom ook voor de MCP2515 gekozen omdat de onderste lagen al aanwezig zijn, inclusief CD (Collision Detectie). Dat moet je met RS485 allemaal nog implementeren. Ik heb genoeg kennis en ervaring om dat te doen, maar bij de Can bus is dat allemaal al gedaan, wel zo gemakkelijk.
 
Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 November 2019, 18:02:47
@timo & @bask185

Ik ga zaterdag naar Houten digitaal. Is er een kans om jullie te ontmoeten? Rond 12:00 zal ik wel de BNLS tafel proberen te vinden.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 07 November 2019, 18:31:38
Per toeval ga ik heen. Ben er nog nooit geweest. Het is ook m'n verjaardag dus ik moet ff grondig shoppen.

Edit ik ga op zondag
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 09 November 2019, 14:30:09
Allereest het jammere:
Ik ga zaterdag naar Houten digitaal. Is er een kans om jullie te ontmoeten? Rond 12:00 zal ik wel de BNLS tafel proberen te vinden.
Helaas was ik deze week aardig druk en heb een paar lange dagen gemaakt. Ik zie dit bericht dus helaas nu pas. Had ik hem eerder gezien was ik denk ik nog wel naar Houten gereden. Maar ben bang dat de kroketten nu al wel op zijn :-\

werkt zodoende niet in een case label.
Hoe bedoel je? Kan je een voorbeeld geven?

Overigens wel met je eens dat een enum voor "state"-achtige zaken, waar de echte waarde van je "variabele" er niet toe doet, een enum een mooiere oplossing is. Maar om een pin nummer nu als enum te definiëren  ;D

Also een #define voor een IO kan prima.
Zeg ook niet dat het niet kan  ;D Zeg alleen dat er een beter/nieuwer alternatief is.

Mensen zeggen ook dat je een macro met hoofdletters moet tikken, zodat je kan zien dat het een macro is. Dat kan je beschouwen als een soort type-safety.
Met het laatste ben ik het niet eens. Het is wel een clue voor jezelf maar heeft niets met type-safe te maken maar met style. Maar een macro voor constanten gebruiken is naar mijn idee een beetje als een Tesla met alle opties kopen maar nooit met autopilot rijden. ::) Waarom zou je jezelf niet laten helpen door de compiler ipv alleen op jezelf te vertrouwen? Alle C++ toevoegingen aan C zijn er alleen maar om jou te helpen en je intentie duidelijk te maken aan zowel de compiler als een ieder die de code leest. En de eerste beloont je dan ook met duidelijkere errors en warnings.

Ja, macro's hebben nog steeds hun plek in code, in 99% van de gevallen voor pre-processor zaken. Maar ook voor die dingen wordt er hard gewerkt aan betere alternatieven binnen C++ zodat men af kan van de niet eenduidige search-and-replace functie die een macro geeft.

macro's worden door veel mensen ten onrechte als "kwaadaardig" beschouwd  en dat slaat nergens op. [...]
Zie het ook absoluut niet als kwaadaardig. Zie het alleen als het gebruik van een handboor terwijl er ook een mooie accu-boormachine in je gereedschapskoffer ligt ;)

[...] De woorden 'case' en 'break' worden door de macro vervangen door 'State'. [...]
Tja, van dat soort constructies ben ik dan ook geen fan. Naar mijn idee maakt dat code namelijk niet leesbaarder (door geen standaard code te zijn) maar is het alleen een soort lui-heid (niet negatief bedoelt!). Maar goed, dat is een kwestie van stijle (en zolang iemand een stijl consequent toepast zal ik daar geen opmerking over maken) en geeft inderdaad een geval aan waar geen C++ alternatief is. (Afgezien van je enum ;D) Maar dat betekend niet dat er andere gevallen zijn waar je wel meer voordeel uit moderne C++ toevoegingen kunt halen.

En ja, naar mijn idee is de switch() nog een zwak punt in C++ :-\

Dus nogmaals, vind (nog ;D) niet dat een macro per definitie slecht is. Maar voor gevallen waar een C++ alternatief beschikbaar is zie ik die vaak wel als "beter" door de toegevoegde meerwaarde zoals het laten zien van je intentie, code safety en betere compiler berichten. Maar ja, ook met een handboor krijg je een gat in beton, kost je misschien alleen wat blaren ;D

Ansich werken ze niet sneller, maar veel betrouwbaarder. [...]
Maakt dat ik er toch ook eens mee moet gaan spelen. Maarja, altijd te veel projectjes lopen ::) Scheelt dat de donkere dagen binnen er weer aan komen, misschien dat ik eens een opstellingkje kan maken. Moet ik eerst de modules wel weer vinden ::)

Dat zeuren geeft niet, ik weet wel waarom. Maar het zal wel te maken hebben met mijn leeftijd. Ik heb het vak geleerd op een EL X8 met Algol, later jaren als systeemprogrammeur op mainframes in BAL gewerkt. In 83 in het UNIX gebeuren verzeild. Alles deed je met macro's en #defines. Zelfs toen ik begin 90 in Berkeley werkte (C, C++) deden we nog alles met #defines en macro's. Kortom oude gewoontes slijten slecht. Overigens gebruik ik echt wel const's en enums, maar #defines gebruik ik ook nog bewust, vooral als (pre)compiler directives,
Viel me vooral op omdat je wel de overstap naar objecten hebt gemaakt maar wel veel macro's gebruikte. Zoals hierboven ook uitgelegd dus vooral een voorstander van omdat ik denk dat het een nuttige tool is, niet omdat het anders onmogelijk of fout is. Maar ik zie bijvoorbeeld newbies zo vaak verdrinken in cryptische error messages of bijwerkingen van macro's.

Ja, objecten kun je slecht als parameter in een method gebruiken, dus probeer ik zoveel mogelijk met pointers te werken.
Bedoelde meer, waarom een pointer en niet gewoon het object zelf in je eigen class opnemen?

Ik heb genoeg kennis en ervaring om dat te doen, maar bij de Can bus is dat allemaal al gedaan, wel zo gemakkelijk.
Kan ik het alleen maar mee eens zijn  (y)


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 09 November 2019, 18:06:54
Allereest het jammere:Helaas was ik deze week aardig druk en heb een paar lange dagen gemaakt. Ik zie dit bericht dus helaas nu pas. Had ik hem eerder gezien was ik denk ik nog wel naar Houten gereden. Maar ben bang dat de kroketten nu al wel op zijn :-\
Er komen nog wel vaker beurzen.

Viel me vooral op omdat je wel de overstap naar objecten hebt gemaakt maar wel veel macro's gebruikte. Zoals hierboven ook uitgelegd dus vooral een voorstander van omdat ik denk dat het een nuttige tool is, niet omdat het anders onmogelijk of fout is. Maar ik zie bijvoorbeeld newbies zo vaak verdrinken in cryptische error messages of bijwerkingen van macro's.
Dat mag ook wel voor een IT architect die vanaf de midden jaren 90 een fanatieke advocaat van OO is geweest. Als je eenmaal het licht hebt gezien, dan begrijp je niet meer hoe je het daarvoor deed. Overigens was dat ook de tijd dat we met JAVA begonnen en daar speelt het gebruik van macro's niet. Het is jammer dat een Arduino niet het platform is om echt puur "message driven" en in OO te ontwikkelen. Maar dat heeft ook wel weer zijn charme.

Bedoelde meer, waarom een pointer en niet gewoon het object zelf in je eigen class opnemen?
Omdat deze objecten weer hun eigen initialisatie parameters hebben (de CS pin) en ik in de constructor van de CanBus niet extra verborgen parameters wil meegeven. De mcp2515 objecten zijn de parameters voor het CanBus object. Dat deze laatste klasse een CS pin als parameter gebruikt is niet relevant voor de CanBus klasse zelf.  Daardoor ontkoppel ik de creatie van het mcp2515 object van het CanBus object en zou ik makkelijk dat kunnen wijzigen. Als ik het mcp2515 object in de CanBus klasse initialiseer, dan creeer ik een verborgen object (en relatie) in de CanBus zelf. Dat behoort IMHO niet tot een goede ontwerp/programmeer stijl.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 09 November 2019, 18:55:32
Er komen nog wel vaker beurzen.
(y)

Dat mag ook wel voor een IT architect die vanaf de midden jaren 90 een fanatieke advocaat van OO is geweest.
;D Dat is al wel even ja :) (y) Zelf nooit een fan van Java geworden. ::) Ook al was dat het vak op de uni waar we de basis van OO mee kregen.

Omdat deze objecten weer hun eigen initialisatie parameters hebben (de CS pin) en ik in de constructor van de CanBus niet extra verborgen parameters wil meegeven.
Maar dat is toch ook gewoon mogelijk met het object zelf?

Dus
mcp2515 *sender = new mcp2515(10);
//wordt
mpc2515 sender(10);
Of via de initializer list als je een object in je klasse wilt gebruiken. Of wil je geen objecten in je klasse hebben?

Nogmaals, puur uit nieuwsgierigheid. Niets fout aan pointers :) Viel me gewoon op door alle structure dereferences.


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 09 November 2019, 20:19:45
Nogmaals, puur uit nieuwsgierigheid. Niets fout aan pointers :) Viel me gewoon op door alle structure dereferences.
Voor een deel is het mijn stijl van werken. Maar die is ook gevormd door ervaring. In C++ (erfenis van C) kun je object via een referentie (pointer) of direct benaderen, verschil is de aanroep van een method, met en . of met een -> In de praktijk levert dit vaak verwarring op (niets is erger dan na dagen door legacy code spitten, om er dan achter te komen dat er een & is vergeten om een adres door te geven. Probleem was ook dat het meeste van de code nog K&R volgde, dus moesten we in GNU C het nivo van error checking nogal laag zetten).
Vandaar mijn voorkeur om zoveel mogelijk met pointers te werken, omdat je dan de referentie altijd kunt gebruiken, ook als parameter in methods. Een object "by value" doorgeven in een method kan nu eenmaal niet. Dit is ook wat mij sterk aansprak in JAVA omdat daar, afgezien van primitieven (int, float, etc) alles "by reference" is en dus de verwarring die soms bij C en C++ kan voorkomen, minder gebeurt.

Groet
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 10 November 2019, 11:50:11
Hoi Meino,

Bedankt voor de toelichting :) Zelf ben ik wat later begonnen en was C++ er al volwaardig. Ook heb ik zelf meer een elektrotechniek achtergrond dan informatica ;D

Maar moet je dan zo vaak je objecten als parameter gebruiken? Van wat je gepost hebt leek het er meer op dat je gewoon een object aanmaakt in je constructor en deze in een pointer van je klasse opslaat. Ofwel, onderdeel van je klasse maakt. Of mis ik iets ;D


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 10 November 2019, 17:36:20
Maar moet je dan zo vaak je objecten als parameter gebruiken? Van wat je gepost hebt leek het er meer op dat je gewoon een object aanmaakt in je constructor en deze in een pointer van je klasse opslaat. Ofwel, onderdeel van je klasse maakt. Of mis ik iets ;D

Hoi Timo
Het heeft niet eens zoveel te maken met het doorgeven van een object in een method aanroep. Het is meer stijl en standaardisatie. Maar het feit dat ik de Mcp2515 objecten niet binnen de CanBus klasse aanmaak, heeft te maken met het spreiden van verantwoordelijkheid wat hoort bij een goed OO ontwerp.

Het Mcp2515 object isoleert het mcp2515 kaartje en zijn bijbehorende interface bibliotheek. Het Mcp2515 object houdt zich puur bezig met het zenden en ontvangen van berichten, het bijhouden van de send queue (vanwege het reduceren van de hoeveelheid berichten per seconde), verder is hij verantwoordelijk voor de initialisatie en configuratie van de onderliggende interface bibliotheek.

Het CanBus object daarentegen is met dat soort zaken niet bezig, hij heeft kennis van de berichttypen, beheert de bijbehorende interrupt routines etc. Verder kan hij verschillende configuraties aan, alleen zender, alleen receiver of zender en receiver. Verder zou het kunnen dat ik in de toekomst een ander type interface kaart zou willen gebruiken i.p.v. een mcp2515 kaart. In dat geval moet er een andere klasse komen die dat ondersteund. Dus zou de Mcp2515 eigenlijk een interface definitie moeten zijn zodat ik daar makkelijk een andere klasse achter kan plaatsen.
Verder als ik de Mcp2515 objecten binnen de constructor van het CanBus object zou maken, dan moet ik kennis van al deze objecten in de CanBus klasse inbouwen. Dat betekend dat in de constructor van het CanBus object ik informatie moet verstrekken over wat hij moet fabriceren. Maar dat is informatie die op geen enkele wijze relevant is voor de werking van het CanBus object, een situatie die je in een goed ontwerp moet proberen te voorkomen.

Kortom het heeft veel te maken met hoe ik een OO ontwerp maak en hoe ik graag werk. Maar dat is wel gevormd in een omgeving waar de heap size niet relevant was (4GB of groter). Helaas is een Arduino niet het meest geschikte platform om dit zonder restricties te kunnen doen (2Kb voor de heap is erg weinig). Aan de andere kant, als je weet wat je beperkingen zijn, kom je toch een heel eind.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 11 November 2019, 19:34:17
Hoi Meino,

Bedankt voor deze lange toelichting :)

En ben het met je eens dat het je de vrijheid geeft. Of je die altijd nodig hebt ;D Misschien was ik zelf eerder voor een reference gegaan.

En hoe wil je dat makkelijk opvangen als je wel een andere library wilt ondersteunen, er is dan denk ik niet een makkelijke virtual class / interface voor de mcp2515-class of wel?

Nogmaals, iet om te zeuren! (y) Puur interesse omdat jij er wel echt over na hebt gedacht. (y) Zit nog wel eens op het Arduino forum en daar zie ik juist vaak mensen alleen maar dingen doen zonder gedachten omdat ze het zomaar ergens zagen en het dus zo MOET ::)


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 November 2019, 20:23:11
En hoe wil je dat makkelijk opvangen als je wel een andere library wilt ondersteunen, er is dan denk ik niet een makkelijke virtual class / interface voor de mcp2515-class of wel?

Dag Timo

op dit moment is de Mcp2515 een klasse en geen interface, maar als ik een andere library zou willen ondersteunen, dan hoef ik slechts de Mcp2515 klas aan te passen, of een nieuwe klasse te schrijven met de zelfde interface. Vandaar de scheiding tussen de Mcp2515 en CanBus klassen. Waarbij de Mcp2515 klasse de CanBus klasse isoleert van de specifieke hardware en bijbehorende bibliotheek. Op dit moment is het zeker nog geen volwassen systeem, dan had ik gelijk de Mcp2515 als een interface of base class uitgewerkt, helaas is dat in C++ wat minder intuitief dan in JAVA, dus het is gebouwd direct op wat ik nu gebruik en nodig heb. Het enige is dat ik wel achterdeurtjes in het ontwerp open hou om dit later eventueel makkelijk te kunnen doen.

Overigens dit zijn leuke discussies, maar ik realiseer me dat dit aan de de meeste forumleden voorbij zal gaan. Tenslotte is dit een treintjesforum, dus misschien verder gaan via PB's?

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 11 November 2019, 21:49:41
Blijf maar lekker hier doorgaan. Van het meeste begrijp ik geen hol, maar ik lees jullie discussie wel.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Hennik op 11 November 2019, 23:30:27
Eens met Klaas! Ik lees ook met zeer veel interesse mee en probeer er wat van op te steken.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 12 November 2019, 00:39:42
Maar je schrijft zoiets als "... eigenlijk een interface definitie ..."  Als professioneel C++ programmeur met bijna 30 jaar ervaring in dat "taaltje" zou ik zeggen: Gebruik het onvolprezen (en vaak niet begrepen) C++ mechanisme van multiple inheritance! Het Java en C# concept van Interfaces is daar direct van afgeleid ;) en werkt in C++ met multiple inheritance nagenoeg identiek aan wat je in Java gewend bent met Interfaces. Een extra plus daarbij is, dat je in tegenstelling tot het Interface concept ook al een, deel van, de implementatie mag en kunt schrijven. Iets dat de "moderne" talen pas sinds ca. 2 jaar kunnen met hun "default methods" in Interfaces ;) Wel is het dan natuurlijk slim kiezen van de methodes die je Virtual wilt/moet maken.

Dag Karst bedankt voor je reactie. En je informatie en kennis van C++.
Ik moet eerlijk zeggen dat ik gepokt en gemazeld ben in C (vanaf 1985). Met C++ kwam ik in aanraking ergens rond 87-88 via een cursus bij AT Computing in Nijmegen. Maar toen was het idee van OO totaal nog niet bekend, ik kan me herinneren dat het meeste ging over operator overloading. C++ heeft bij mij altijd op het tweede plan gestaan. Pas rond 1995 kwam het idee van OO (Object Oriented) meer op de voorgrond. Dat is de zelfde tijd dat we met JAVA in aanraking kwamen. Dat was ook de periode dat het kwartje viel en ik samen met een paar geestverwanten de OO werkwijze binnen ons bedrijf heb geintroduceerd.  Dus veel van mijn manier van denken en werken (ook in C++) is gebaseerd op JAVA (en UML). C++ kent niet een echte interface definitie zoals JAVA dat kent. Een abstracte, virtuele base class komt nog het dichts bij het principe van een interface, daar ben ik dan nu ook mee bezig om dat in de CanBus te implementeren.

Maar gezien de reacties blijf ik toch actief met deze discussie in dit draadje.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: ikbenerevenniet op 12 November 2019, 00:54:04
Ga er vooral mee door, alsjeblieft! Als beginnend Arduino-gebruiker, weliswaar met veel ervaring in Clipper en VB, steek ik er weer eens wat van op! :-)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 12 November 2019, 08:26:10
Hoe bedoel je? Kan je een voorbeeld geven?
Nou ik heb vandaag weer iets nieuws geleerd. Ik had technisch gezien geen ongelijk, maar ook geen gelijk. Het verschilt namelijk per compiler of een const int is toe gestaan in een switch-case label: bron (https://stackoverflow.com/questions/14069737/switch-case-error-case-label-does-not-reduce-to-an-integer-constant)

Van GCC mag het wel. Van mijn C-compiler mag het niet  :-\

Wat voor vervanging heb je in C++ eigenlijk voor een switch case.
En waarom vind je een switch case 'zwak' in C++.

En ik ben ook erg lui :-D. Dat is namelijk een goede programmeurs eigenschap. Ik wil zo veel mogelijk doen met zo min mogelijk werk, daarom maak ik scripts om zo veel taken van me over te nemen als mogelijk.  Ik laat scripts zo veel als mogelijk code voor me kloppen. Dan hoef ik niet meer na te denken over de structuur. Waar welke code hoort te komen, staat al vast. Door de code generatie kan ik minder tikfouten maken, en door de structuur, logica en simpelheid maak ik aanzienlijk minder bugs. Ik heb nu een gestripte werk versie sinds kort op github gezet, maar die is nog niet klaar.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Te 2/2 op 12 November 2019, 17:26:25
Wat voor vervanging heb je in C++ eigenlijk voor een switch case.
Bedoel je zoiets als dit (https://github.com/Dobiasd/articles/blob/master/creating_a_replacement_for_the_switch_statement_in_cpp_that_also_works_for_strings.md)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 14 November 2019, 09:25:44
Bedoel je zoiets als dit (https://github.com/Dobiasd/articles/blob/master/creating_a_replacement_for_the_switch_statement_in_cpp_that_also_works_for_strings.md)

Dit is toch ruk in elk mogelijk opzicht. Het enige wat het meer kan, is strings gebruiken als case-labels. Heb je die syntax wel eens bekeken? En dan wordt mijn switch-case met een macro 'onduidelijk' genoemd omdat het 'niet standaard' is.
Dat C++ is echt een draak van een programmeer taal.  Waardeloze syntax is onleesbaar. De relatie tussen C en C++ is als de relaties tussen een mens en een gezwel. Ik vind alleen de klasses en OO stijl een waardevolle toevoeging.

void switch2(const Key_t& key,
    const std::vector<std::pair<Key_t, std::function<void()>>>& pairs,
    const std::function<void()>& def)
{
    std::unordered_map<Key_t, std::function<void()>> dict;
    for (const auto& entry : pairs)
        dict.insert(entry);
    assert(dict.size() == pairs.size());
    const auto it = dict.find(key);
    if (it != dict.end())
        it->second();
    else
        def();

...


{
        std::string input;
        std::cin >> input;

        switch2(input, {
            {"hi", defer(say, "hey")},
            {"whazzup", defer(say, "wazzuuuup")},
            {"bye", defer(quit, "see ya", &running)}},
            defer(say, "wat?"));
    }
}

Ik geloof ook niet dat dit efficient is. Om te beginnen, vervang je een echte switch-case door een functie call met variabelen argumenten. Dat is al trager dan een switch-case. Bovendien is het gebruik van strings voor case labels niet praktisch. Ik weet niet precies hoe het werkt, maar volgens mij moet je strings gaan vergelijken om de juiste case uit te voeren. Daar verlies je ook al efficiency.

Waarom zou je eigenlijk strings willen hebben ipv een simpele constante? Het voornaamste verschil zijn de "" om de 'label' heen.

Als je een Arduino (of een ander embedded apparaat) programmeert, wil je dat:
A. je code leesbaar
B. dat je code snel wordt uitgevoerd
C. Je programma niet onnodig groot wordt qua geheugen gebruik.

Het voorbeeld wat je gaf, voldoet volgens mij aan geen van de punten. A is duidelijk, of eigenlijk 'bijzonder onduidelijk'. Aan B twijfel ik, ik weet niet wat de compiler daar van bakt. Maar denk nog steeds niet dat het sneller is dan een conventionele switch-case. En wat C betreft... Heb je gezien wat er allemaal geinclude wordt en wat voor aanvullende functies er allemaal bij komen kijken?

Dit proberen te gebruiken in een Arduino om...ja wat willen we hiermee nu bereiken?  is wat ik noem: "deliberately shooting yourself in the foot".
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 November 2019, 14:24:19
Ok, ik wil toch even reageren, omdat ik het gevoel krijg dat hier een richtingenstrijd aan staat komen. Dat lijkt me niet nuttig want alleen insiders zullen dat begrijpen. Ik heb gemerkt dat er ook anderen, met misschien wat minder ervaring, dit volgen.

In het kort gaat het om wel of geen macro's gebruiken.
Ik weet het niet zeker, maar ik krijg ook het gevoel dat dit sterk beinvloed is door wat er momenteel op universiteiten geleerd wordt. Dit doet me denken aan midden jaren 80, toen vooral onder universitaire docenten een sterke minachting voor de taal C bestond. Een taal die niet bruikbaar was, vol met ambiguiteiten, niet type vast, geen controle op de goede functie aanroep, etc. Dat klopt want de K&R C kent dat allemaal niet. En toen werden vooral talen als Pascal en Modula-2 sterk gepropageerd als de nieuwe supertalen. Ook een taal als Lisp was daar erg populair, waar ik toen werkte werdt Lisp gekscherend bestempeld als een taal voor haakjes liefhebbers (zijn er hier nog Lisp programmeurs aanwezig?).
Pas met de ANSI C standaard werd een deel van deze intrensieke problemen opgelost, maar ook in de 90er jaren werd nog veel in K&R stijl gewerkt. Ik meen dat Kernighan ooit gezegd heeft "C provides all the rope to hang yourself", waarop iemand anders zei, en "C++ doet dat in het kwadraat". Kortom dat Bask185 C++ een draak van een taal vindt, daar herken ik wel wat in. Ik zelf in de beginntijd heb ook nooit sterk de behoefte gevoeld om veel met C++ te doen. Dat veranderde pas begin jaren 90 toen het begrip OO (Object Oriented ..) opeens in het voetlicht kwam te staan. Toen werden de intrinsieke OO kwaliteiten van C++  zichtbaar. Wat nog versterkt werd toen JAVA op het toneel verscheen.

Maar wat we moeten beseffen is niet wat je allemaal met een taal kunt doen (dat is alleen van belang als je aan de Obfuscated C competitie wilt meedoen) maar de beperkingen die je jezelf oplegt in het gebruik van een taal, waardoor een programma ook voor een ander leesbaar en begrijpbaar wordt.
Ik heb ooit een Coding standaard geschreven voor mijn team programmeurs, om te trachten dit voormekaar te krijgen. Je wilt niet weten hoeveel weerstand dit opgeroepen heeft.

Maar waar draaide deze discussie ook al weer over, het gebruik van #defines en macro's. Ik ben een oude lul, uit een tijd dat dit handige tools waren, zeker als je in BAL (Basic Assembler Language) programmeert. Ook in C heb ik uitgebreid macro's ontwikkeld voor precies die zaken die bask185 ook beschrijft. Dat kan heel handig zijn. Maar het blijven extensie van de oorspronkelijke taal, en als zodanig voor buitenstaanders onbekend, wat een nadeel kan zijn. Binnen een ontwikkelteam hoeft dat geen probleem te zijn zolang er leden zijn die wel van de hoed en de rand weten.
Dus enige zelfrestrictie ten aanzien van het gebruik binnen deze doelgroep (Forumleden die iets met Arduino's willen doen) is wel op zijn plaats. Verder het bij voorkeur gebruiken van enums i.p.v een #define heeft zeker nut. Maar het is niet zo dat al het gebruik van een #define uit de boze is. Ik blijf dat gebruiken om bijvoorbeeld debug code wel of niet op te nemen, dus als een instructie aan de compiler.

preekmode=off;

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 14 November 2019, 15:51:26
Je wilt niet weten hoeveel weerstand dit opgeroepen heeft.
Ik geloof je meteen. Zodra je iets niet standaard doet in programmeerwereld of elektronicawereld-> meteen tegenstand.

Heb ik ook nog een grappig klein verhaaltje over. Ik begon hier als eerste in C. Tijdens het ontwikkelen, moest ik ook nog structuur zaken leren ed. Ik kwam net van school af. Zo kwam ik ook tot een bepaalde keuze van accolade gebruik. Ik had een klein hapje genomen van python destijds. En de accolade gebruik van python vond ik erg interessant, want die was er niet. Ik vond de standaard accoladegebruiken in C niet perfect. 'else' achter een '}' en case labels op half 11. En ik wilde het eigenlijk net als python hebben. Dus ik ontwierp:
if(x < 5) {
    if(y > 6) {
        doeIets();
        if(z == 7) {
            doeIetsAnders(); } }
    helloWorld(); }
Mijn methode lijkt op de lisp methode zoals ik dat op wikipedia (https://en.wikipedia.org/wiki/Indentation_style#Lisp_style) zie. Vind het wel grappig dat mijn gekozen stijl er nog niet tussen staat::)

Als je al aan de indentation kan zien wat waar bij hoort. Dan waarom een trap van sluit accolades maken? Waarom extra wit regels toevoegen. Volgens python was dat niet nodig. Tijdens het gebruik, begon ik hem alsmaar beter te vinden. Ik hou een stricte logica aan. 1x openen is 1 inspring voorwaarts en 3x sluit accolade = volgende regel 3 inspringingen terug. Vooral bij lange complexe functies vond ik het niet onduidelijker dan de andere formats. Ik had alleen minder onnodige witregels.

Je kan natuurlijk raden hoe nieuwe collega's er op reageerden, en hoe verscheidene forum mensen reageerden. "het is zo moeilijk te lezen", "please format your code properly so that everybody can read it" en dan gingen m'n collega's ook nog zo hard muiten dat ze het niet voor dat ene enkele project de stijl eventjes overnamen. Ik had me al neergelegd om een andere stijl te pakken voor het volgende project, maar nu hadden we 1 project met 2 stijlen door elkaar, stelletje ketters!

Mensen willen het ook gewoon geen eerlijke kans geven.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 November 2019, 19:06:20
Yep, het blijft verwonderlijk dat zoiets simpels als een indentatie stijl zoveel weerstand kan oproepen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 17 November 2019, 00:23:59
Wow, wist niet dat we zo veel meelezende "fans" hadden  :o ;D

Maar wat Karst liet zien is inderdaad een mooi (complexer) voorbeeld van wat ik bedoelde om al het dynamisch creëren te kunnen elimineren. (y)

Zelf ben ik een elektrotechnieker die vroeger al interesse had in computers en zo toen al her en der verschillende talen gebruikte. HTML, CSS, PHP, shell, C, Ladder, Java, ActionScript, Sass etc. Maar toen nooit echt verdiept in een taal. Maar dit heeft denk ik wel geholpen met en systematisch kunnen denken. Tijdens mijn studie voor het eerst echt kennis gemaakt met OO (via Java) maar ook meer met microcontrollers gaan doen. Eerst in C (PIC) en later meer en meer C++ (Atmel en later Arduino, ESP en STM etc). En daarom wel steeds meer gaan verdiepen in C++ maar ik ben er geen professional in. Ik leer ook nog steeds bij ieder project wat ik doe. Tegenwoordig maak ik testsystemen voor allerhande elektronica. Dus naast elektronica kennis gebruik ik ook daar weer alle mogelijke talen die mogelijk zijn of moet ik er in ieder geval wat zinnigs mee kunnen ;D C/C++, Python, LabView (kan je dat een taal noemen :angel:), HTML, Ladder, SQL, FBD, SCL, Shell, Batch file, VB(A) etc. Help wel om gestructureerd door een programma te gaan maar niet om de ins en outs van een taal te onthouden ::) Moet het dus nog steeds van de hobby en vrij tijd hebben maar dan heeft C++ (voor uC's) wel de meeste interesse. Nu alleen nog wat meer tijd... ::)

Hier was ik dan ook vooral nieuwsgierig. Helemaal omdat macro's juist vaak gebruikt worden omdat het makkelijk is of de enige bekende manier. Maar de compiler kan je er dan niet bij helpen. Vandaar meer soort vraag naar het er bewust over gedacht hebben of niet. Zo ja, prima :)

En het wel of niet gebruiken van macro's, zoals ik al aan gaf vind ik het wel mooi dat de compiler je kan helpen (zowel in optimalisatie als errors) bij gebruik van C++ alternatieven. Maar niet alles heeft een alternatief en de meeste zijn uit latere revisies (C++11 bracht er veel). Maar je kunt het ook te ver doordraven en er iets onleesbaars van maken. Maar dat is niet gelimiteerd aan C++ maar geld denk ik in elke taal wel.

Ik heb ooit een Coding standaard geschreven voor mijn team programmeurs, om te trachten dit voormekaar te krijgen. Je wilt niet weten hoeveel weerstand dit opgeroepen heeft.
Dat snap ik dan ook wel ;D Ik snap dat het binnen een team/bedrijf het lezen van andermans code makkelijker/sneller maakt maar iedereen ontwikkelt een eigen stijl. En ik wil me daar vooral niet mee bemoeien 8) Zolang een stijl maar consequent is. Want dan is het een tool om makkelijker/sneller/foutlozer code te maken. Inderdaad een compititie Obfuscated C  ;D

Van GCC mag het wel. Van mijn C-compiler mag het niet  :-\
Ahh, vandaar! Wat als je er expliciet constexpr van maakt?

En waarom vind je een switch case 'zwak' in C++.
Omdat het eigenlijk maar op een stel conditional goto's uit komt. Zeker met de beperking dat het alleen met een integer type werkt en dat de cases constexpr maakt dat er wat "onverwachte" beperkingen aan zitten. Nu moet ik zeggen dat ik het gelinkte alternatief ook ruk vind ;D Dan laat je het op runtime aankomen. Om over de syntax inderdaad nog maar te zwijgen. ::)

C. Je programma niet onnodig groot wordt qua geheugen gebruik.
Eens! Maar key word is wel "onnodig". Naar mijn idee mag een kleine overhead wel om het schrijven (enorm) te versnellen. Een Arduino voorbeeld: digitalWrite(). Naar mijn idee een mooie oplossing met beperkte overhead. Helaas komen de String-class (te veel operator overloading in combinatie met malloc's) en float's (om maar een decimale punt te hebben ::)) bij beginners snel om de hoek kijken. Voor beide helaas nog geen alternatief tegen gekomen (en niet de moeite genomen het te schrijven :angel:) maar zou OO prima kunnen. Bijvoorbeeld, als de String-class fixed-size zou zijn ipv dynamisch dan zou dat veel meer geheugen bewustzijn kweken.

Dan waarom een trap van sluit accolades maken?
Om te "forceren" (lees: makkelijk kunnen controleren) dat ik genoeg sluit-accolades plaats om niet per ongeluk de boel verkeerd te groeperen.
if(x < 5) {
    if(y > 6) {
        doeIets();
        if(z == 7) {
            doeIetsAnders(); }
    helloWorld(); } }
Zal prima compilen maar doet wat anders dan de indentatie doet vermoeden. Ik zou dan wel een nieuwe kop koffie nodig hebben om de bug te vinden ;D De discussie of code accolades of indentatie voor groepering zou moeten gebruiken is dan weer een andere. Zou het te maken hebben met het verschil in regeleinde tussen verschillende OS'en? Of het feit dat je eventueel code kleiner zou kunnen maken door leading non-printable characters weg kan laten? Voor de leesbaarheid snap ik de indentatie in ieder geval zeker!

Yep, het blijft verwonderlijk dat zoiets simpels als een indentatie stijl zoveel weerstand kan oproepen.
Daarom zeur ik dan ook niet al te vaak over stijl*. Zoals gezegd, consequente stijl is belangrijker. Maar dan kan wel voor "irritaties"zorgen als er meerdere mensen aan moeten werken ;D Indent met tab of spaties? 2 of 4 spaties? "){" of ") {". else achter een }? Commentaar uitlijnen? Commentaar ervoor of erachter? Geef je invulling in commentaar? camelCase? UpperCamelCase? Snake case? Kebab case?

Maar nee, ik ben hier niet om een stijl oorlog te starten of iemand te overtuigen :angel: Goed, ik ga het CAN verhaal verhaal weer volgen. (y) Weer even genoeg BBCode geschreven 8)


Timo

* Ook al zou ik gek worden code zonder indentatie en ben ik wel van de "noting follows a ;" :angel:
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 17 November 2019, 15:16:02
Ik kan het niet laten, maar een klein voorbeeldje, een winnaar uit de 2019 Obfuscated C Contest.

#include <stdio.h>
#define  f(f,g){z e=0;for(;e<f;e++)g;}
#define  i(f,g)static z f(z a){return g;}
#define  j(f,g)static void f(z*a,z*b,z*c){g}
#define  h(f,g)static z f(z a,z b,z c){return g;}
#define  g(f,g,h,i,j)static z f(z b){z a=g,c=h;for(;i)a=j;return a;}
typedef unsigned char y;typedef unsigned long long z;extern y*w;static z b(z a,z b){return a>>b|a<<(64-b);}i(_,
(a>>6)^b(a,61)^b(a,19))i(_a,b(a,39)^b(a,28)^b(a,34))h(x,((a^b)&c)^(a&b))i(u,b(a,41)^b(a,18)^b(a,14))h(t,(((((3*(a*c+b*b)>>9)+(3*
b*c>>32))*a>>21)+(3*a*a*b>>6)+((b>>4)*(b>>4)*b>>46))>>18)+a*a*a)h(m,t((b<<16)|(c>>48),(c>>24)%(1<<24),c%(1<<24))>>48<a)h(s,(a&b)
^(~a&c))i(r,b(a,1)^b(a,8)^(a>>7))g(o,0,0,c<8;c++,a*256+w[b*8+c])g(d,0,0,c<13;c++,a*31+w[b*13+c]-96)g(p,0,4,c;c/=2,a|c*m(b,a|c,a)
)g(q,0,1ull<<63,c;c/=2,a|c*m(b,p(b),a|c))g(v,b>1,2,c<b;c++,a&&b%c)g(l,b?l(b-1)+1:2,a,!v(c);c++,c+1)j(n,z d=a[7]+u(a[4])+s(a[4],a
[5],a[6])+q(l(*b))+c[*b%16];f(8,a[7-e]=e-3?e-7?a[6-e]:d+_a(a[0])+x(a[1],a[2],a[3]):d+a[3])f(16*(*b%16>14),c[e]+=c[(e+9)%16]+r(c[
(e+1)%16])+_(c[(e+14)%16])))j(k,f(8,b[e]=a[e])f(80,n(a,&e,c))f(8,a[e]+=b[e]))int main(){z a[8],b[8],c[16];f(8,a[e]=d(e))f(16,c[e
]=e-15?o(e):d(8))k(a,b,c);f(16,c[e]=e?e-15?0:11264:1ull<<63)k(a,b,c);f(8,printf("%016llx%s",a[e],e-7?"":"\n"))return!w;}y*w=(y*)
"crsmyiajqhwy{unwa|hjoi`hlxhpxrzb~edko~rtr~ileqyjk`znqgsuitvgqnfdfa||wedvnmhozkpokootqzcexeld~oibqzpcsuw{ib{x`m`hsa`jmn}wcfzpb";

Het genereert eem SHA-512 hash van zich zelf.

groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 17 November 2019, 20:12:05
Zo, daar heb je wel even een avond voor nodig om te verteren  ;D


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 17 November 2019, 20:29:11
Dat probeer ik niet eens. Ik heb helaas niet meer een FreeBSD of Solaris bak om het te compileren en te proberen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Robert E op 17 November 2019, 21:11:52
Citaat
Maar dan kan wel voor "irritaties"zorgen als er meerdere mensen aan moeten werken

Ident tool gebruiken....


Citaat
2 of 4 spaties?

Spaties, die ellende als je file eens opent op andere machine die toevallig niet de juiste IDE heeft en file opent in kladblok / Notepad++

Trouwens, als C "man" is die hele OO ook een overgang ondanks dat ik OO achtig programmeer in C....
Al helemaal de struggle / overgang naar C# die ik moet en wil maken  :)
Naja, uitdaging :)

Mvg

Robert
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 17 November 2019, 23:42:44
In Arduino IDE zit ergens een optie die de code netjes maakt als ik me goed herinner.
<Ctrl>t

Overigens gebruik ik meestal Netbeans, daarin kan ik in de Editor options de gewenste formatting(indentatie) per taal opgeven, die dan automatisch wordt uitgevoerd.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Te 2/2 op 18 November 2019, 12:55:51
Netbeans
Kun je Netbeans of Eclipse ook gebruiken bij Arduino schrijfsels, dan?
(ben er zelf kennelijk al weer een tijdje uit)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 18 November 2019, 13:34:46
if(x < 5) {
    if(y > 6) {
        doeIets();
        if(z == 7) {
            doeIetsAnders(); }
    helloWorld(); } }
Zal prima compilen maar doet wat anders dan de indentatie doet vermoeden.
In tegendeel. De identatie laat precies zien waar wat bijhoort.
Als je het normaal "standaard" noteert. Veranderen de indentaties namelijk niet.

if(x < 5) {
    if(y > 6) {
        doeIets();
        if(z == 7) {
            doeIetsAnders();
        }
        helloWorld();
    }
}
of nog "erger":
if(x < 5)
{
    if(y > 6)
   {
        doeIets();
        if(z == 7)
        {
            doeIetsAnders();
        }
        helloWorld();
    }
}
Ik heb dus ondervonden dat methode 1 (die van mij dus) niet onderdoet aan de "standaards". En methode #3 heeft dubbel zo veel regels als #1

Bovendien!
Citaat
Om te "forceren" (lees: makkelijk kunnen controleren) dat ik genoeg sluit-accolades plaats om niet per ongeluk de boel verkeerd te groeperen.
Ik gebruik hiervoor: de bracket highlighting tool die in bijna elke half knappe editor zit. We mogen niet vergeten dat we gezegend zijn met schitterende alles kunnende text editors zoals Vs code, of sublome of netbeans of ...
#indentatie flaming off  :P

Er zijn trouwens 3 dingen die ik met macro's doe om fouten en onduidelijkheden te voorkomen

In C zijn macro's ook handig voor functies met "verschillend aantal argumenten". Eigenlijk heeft C dat niet muv die var args ellende ed. En printf() enzo.
Met macro's maak ik dan 'lookalike' functies waarbij sommige argumenten voor je worden ingevuld. Een mooi voorbeeldje:
Op werk gebruikten we ooit de functie:
startAfter(&foo, 500, 200, 0);
Deze functie startte functie foo na 500 ms en bleef dat vervolgens om de 200ms doen. Als het laatste argument 1 is, dan wordt foo maar 1x uitgevoerd. Dat noemden we oneshot timers.
Voor het 'gemak' hadden we 2 simpele macro's:
#define trigger(func,interval) startAfter(func,interval,1)
#define start(func,interval) startAfter(func,interval,0)
ipv startAfter tikten we of start of trigger, afhankelijk of een functie herhaaldelijk of 1 malig uitgevoerd moest worden. En heel af en toe was het nodig dat we wel startAfter moesten gebruiken. Super elegant als je geen method overloading (gebruik ik de goede term) tot je beschikking heb, als zeg ik het zelf.
macro flaming ook off  ::)

Ik heb zelf ook assembly hel gedaan en ik weet dat een goede switch-case zich transleert naar een jumptabel. Ik zie echter niet het probleem met de restrictie van het gebruik van int en char... of wat ik gewoon als bytes zie. Dat werkt toch gewoon?  ::). met uControllers raak ik sws geen floats of doubles aan.

Wat ik zelf wel mis aan een switch-case is dat je niet cases kan uitvoeren adhv getallen gebieden. Je hebt geen case (var >= 0 && var < 10):

Je kan hier nog wel eens omheen werken met de handige Map(var, 0,10,0,100) functie. Die dan je getallengebiedjes omzet naar een bruikbare int voor een switch case. Maar soms ook niet en dan zit je toch vast aan een if-else ketting. :-[


Kun je Netbeans of Eclipse ook gebruiken bij Arduino schrijfsels, dan?
(ben er zelf kennelijk al weer een tijdje uit)
Je kan ook kladblok gebruiken. Als je in arduino naar settings ga (Ctrl + comma) dan kan je het boxje (externe editor) aanvinken en kan je elke editor naar keuzen gebruiken. De arduino IDE kan dan geen code meer aanpassen en de coder verandert meteen mee met je andere editor bij elke save. Dan gebruik je Arduino alleen maar om te compilen.

Je kan het ook meteen goed doen en arduino met Vs code en een arduino plugin compileren. Of je compileert via een terminal. Dan kan je met elke editor compileren waarmee je scripts kan aanroepen.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 18 November 2019, 15:17:53
Kun je Netbeans of Eclipse ook gebruiken bij Arduino schrijfsels, dan?
(ben er zelf kennelijk al weer een tijdje uit)
Ja dat kan. Ik heb zelf Netbeans opgetuigd om een Arduino project te kunnen maken, Dat werkt met makefiles. Maar ik gebruik het zelf niet meer om een schets mee te bouwen, dat gaat goed, maar het switchen van platform (UNO->Mega etc) kost vrij veel handelingen, en ook het automatisch hercompileren van de bibliotheken gaat niet. Verder vond ik ook het uploaden complex.
Aangezien mijn schetsen over het algemeen vrij simpel zijn, het meeste werk doe ik in een bibliotheek, blijf ik daarvoor de Arduino IDE gebruiken. Ik gebruik Netbeans wel voor het maken en onderhouden van de gebruikte bibliotheken, maar dat beperkt zich tot edit werk van de .cpp en .h bestanden, waarbij de Netbeans editor en navigator het werk aanmerkelijk makkelijker maakt.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Robert E op 18 November 2019, 21:08:31
Citaat
of Eclipse ook gebruiken bij Arduino schrijfsels,

Sloeber is kant en klare Eclipse omgeving voor Arduino inc. support voor andere arduino varianten als de EPS8266 / STM32's enz.

https://eclipse.baeyens.it/

Die Arduino IDE omgeving is toch net een typemachine maar dan net iets slimmer, maar niet veel :)

Citaat
Ik heb dus ondervonden dat methode 1 (die van mij dus) niet onderdoet aan de "standaards".

Voor mij persoonlijk visuele puinhoop, ik ben van de

Citaat
of nog "erger":
manier van werken...

Mvg

Robert
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 19 November 2019, 00:00:30
of nog "erger":

Ik ben ook van die school. Komt uit de tijd van vi op een VT100 of xterm window. Dan kon je makkelijk met een % checken of de blokken correct waren. Als de plaats van de brackets in de regel versprong dan had je een probleem.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 19 November 2019, 08:06:34
Voor mij persoonlijk visuele puinhoop

Kijk Meino, ik heb er weer "zo'n eentje" gevonden  :o

Wat de boer (spreekwoordelijk bedoeld) niet kent dat noemt ie maar een puinhoop
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 19 November 2019, 10:10:44
@bask185
Dat Robert een sterke mening hier over heeft, is zijn visie. Als je in een grote ontwikkelgroep werkt, waarbij het onderhoud door anderen dan de oorspronkelijke ontwikkelaar plaats vindt, dan kan een standaard stijl belangrijk zijn. Maar uiteindelijk gaat het er om dat de programmeur zich happy voelt met een bepaalde stijl. Dus geef ik geen waardeoordeel over een bepaalde stijl. Wel heb ik zelf een voorkeur stijl, die ik in Netbeans heb geconfigureerd.

Ik denk dat het tijd wordt om deze discussie te stoppen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 19 November 2019, 11:16:11
Ik denk dat het tijd wordt om deze discussie te stoppen.
Akkoord!

Heb je nog voortgang gemaakt met je baan?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Patrick Smout op 19 November 2019, 13:47:06
Hoi Meino,

niet zeker of je deze kent

https://openlcb.org/about-openlcb/introduction-to-openlcb/ (https://openlcb.org/about-openlcb/introduction-to-openlcb/)

Misschien een idee om (mee) op een standaard in wording te springen.

mvg,

Patrick Smout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 19 November 2019, 14:26:55
Patrick

bedankt voor het linkje, ik ga het zeker eens goed bestuderen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Karst Drenth op 19 November 2019, 14:46:17
Citaat van: meino
ik ga het zeker eens goed bestuderen.

En als je dan toch bezig bent ;)

https://www.nmra.org/lcc

Een nieuwe NMRA standaard gebaseerd op OpenLCB.  8)

Grtzz,

Karst
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 19 November 2019, 17:35:35
Na al deze discussies wordt het weer tijd om naar het onderwerp terug te keren.
Wel is het zo, dat ik toch nog even goed naar mijn code heb gekeken. En naar aanleiding van de discussie over interfaces, heb ik toch nog wat gewijzigd. Ik heb nu een echte interface gedefinieerd in de vorm van een virtuele klas (CbusInterface).
Ik laat nu het CanBus.h bestand zien en aan de hand hiervan iets te vertellen.

Ik begin met een stel definities:

#include <SPI.h>
#include <mcp2515.h>

#define CANBUS_DEBUG 0
#define CANBUS_DEBUG1 0

//
// Id's of the different types of controllers on the CanBus
//      Maximum nr of ID's is 127!!!
//
enum systemId {
    BEZETMELDER_CONTROLLER_ID = 1,   //  16 Bezetmelder controllers supported
    WISSEL_CONTROLLER_ID = 17,       //  16 Wissel controllers supported
    SEIN_CONTROLLER_ID = 33,         //  16 Sein controllers supported
    DCC_INTERFACE_ID = 126,          //   1 DCC Interface system supported
    S88_INTERFACE_ID = 127           //   1 S88 Interface system supported
};

//
//  Error codes
//
enum cbError {
    CB_OK = 0,
    CB_FAIL = 1
};

//
//  There are several types of messages, each is identified by an unique number
//  the lowest id's have the highest priority
//
//  DCC_ACC   defines a DCC Accesory command
//  OCC_DT    defines an Occupance detector that triggered
//  DCC_LOC   defines a DCC locomotive command
//

enum messageId {
    OCC_DT = 1, DCC_ACC = 2, DCC_LOC = 3
};

//
//  Struct used for the Occupance Detector message
//
typedef struct
{
    unsigned short    address;
    byte              state;
} OCC_DT_MSG;

//
//  Struct used for the DCC Accessory command
//
typedef struct
{
    unsigned short    address;
    byte              direction;
} DCC_ACC_MSG;

//
// A request for sending a message is handled by putting it on
// the send queue. The heartbeat method is the responsible for
// the actual sending.
//
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define NROFSENDBUFFERS 32      // Buffers on the MEGA
#endif
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
#define NROFSENDBUFFERS 4       // Buffers on the UNO
#endif

//
//  Type definition for the call back routine which will handle a received
//  message.
//
typedef void (*msgReceiver_t)(unsigned char aMsgLen, void *msgData);
Hierin worden zaken als Systeem id's, bericht layouts, bericht types etc gedefinieerd.

De interface definitie die de klasse die met de physieke CanBus/bibliotheek communiceerd, moet implementeren.
//
//  The Interface which has to be implemented by all classes that seperate the
//  CanBus class from the used hardware and supporting library.
//
class CBusInterface {
protected:
    friend class CanBus; // Give CanBus access to the internals
       
    virtual void begin(byte aSystemId) = 0;
    virtual cbError sendMsg(messageId anId, unsigned int aMsgLen, void *msgData) = 0;
    virtual void heartBeat() = 0;
    virtual void setMsgReceiver(messageId aMsgId, msgReceiver_t aMsgReceiver) = 0;
    virtual void prepareForSend() = 0;
};
Alle methods van deze interface zijn protected, dit omdat deze niet direct vanuit de gebruikers schets aangeroepen mogen worden, maar dat altijd onder controle van de CanBus klasse moet gebeuren.

De klasse die met het MCP2515 kaartje via de "arduino_mcp2515_master" bibliotheek communiceert.
//
//  This class implents the CBusInterface and supports the MCP2515 chip in
//  combination with the "arduino-mcp2515-master" library.
//
class Mcp2515 : public CBusInterface {
private:
   
//
//  Stuff for the send buffers
//
#define MAXRETRIES 10
    int  sendRetryCount = 0;
    int  nrOfSendBuffers = 0;
    volatile long lastTimeSend = 0;

    volatile int firstEmptySB = 0;
    volatile int firstUsedSB = -1;
    volatile can_frame *sendBuffers = nullptr;
 
#define MESSAGE_WAIT_TIME 10                // Max 100 messages per second
    int  sendWaitTime = MESSAGE_WAIT_TIME;  // Minimum time between successive
                                            // sends, this to throttle the
                                            // the rate of messages produced
                                            // by this sender.
 
    byte systemId;
    MCP2515 *can_bus;
   
    msgReceiver_t msgReceiver[16];
   
    int activeFilter = 0;
   
    void setMaskAndFilter(const unsigned long aFilter);
    void acceptMsgId(messageId aMsgId);
   
    cbError sendMsg();
    void receiveMsg();
   
    init(int aCsPin, int aSendWaitTime, int aNrOfSendBuffers);
   
protected:
    friend class CanBus; // Give CanBus access to the internals
     
    void begin(byte aSystemId);
    cbError sendMsg(messageId anId, unsigned int aMsgLen, void *msgData);
    void heartBeat();
    void setMsgReceiver(messageId aMsgId, msgReceiver_t aMsgReceiver);
    void prepareForSend();
   
public:
    Mcp2515(int aCsPin);
    Mcp2515(int aCsPin, int aSendWaitTime, int aNrOfSendBuffers);
   
    int getSendWaitTime();
    void setSendWaitTime(int aSendWaitTime);
};
De methods van de CBusInterface zijn protected, zodat alleen de CanBus klasse toegang heeft. De constructors Zijn publiek, zodat deze objecten in de gebruikers schets gemaakt kunnen worden. Daar zijn er twee varianten van, een die een object met default waarden voor de sendbuffer en wacht tijd aanmaakt, en een variant waarin dit expliciet gedefinieerd wordt.
Daarnaast kan ook de wachttijd tussen twee Canbus berichten dynamisch gevarieerd worden, als daar behoefte aan zou zijn.

De klasse die binnen de schets het gebruik van de Canbus ondersteund.
//
//  The CanBus class that provides the sketch all the facilities to send and
//  receive CBus messages for the defined systems and message types.
//
class CanBus {
private:
    byte systemId;
    CBusInterface *sender;
    CBusInterface *receiver;

public:
    CanBus(byte aSystemId, CBusInterface *aSender, CBusInterface *aReceiver);
    void begin();
    void setMsgReceiver(messageId aMsgId, msgReceiver_t aMsgReceiver);
    cbError sendMsg(messageId anId, unsigned int aMsgLen, void *msgData);
    void heartBeat();
};
Dit zijn alle methods die beschikbaar zijn voor de gebruikers schets. De constructor. De method begin() waarmee je het CanBus object verteld dat hij moet starten.
De method setMsgReceiver() geeft het CanBus object een functie die hij zal aanroepen als er een bericht van het gegeven type is ontvangen, sendMsg() geeft het CanBus object een bericht om te versturen en heartBeat() moet continue aangeroepen worden om het CanBus object te laten werken.

Groet Meino

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 19 November 2019, 21:44:00
Nu we volgens mij allemaal op dezelfde pagina zitten ("we gaan elkaar niet overtuigen dus pak er een biertje bij" ;D) dus ik nog wel even losjes in te gaan.

In tegendeel. De identatie laat precies zien waar wat bijhoort.
Gemist dat ik de opmaak vernagelt heb? ;) (om helloWorld() te draaien moet x < 5 && y > 6, indentatie suggereert dat alleen x nodig is). Ofwel, precies mijn punt :angel:

Ik gebruik hiervoor: de bracket highlighting [...]
(https://miro.medium.com/max/659/1*8xraf6eyaXh-myNXOXkqLA.jpeg)
 ;D

Maar goed, zolang je het zelf consequent doet en dus als tool kunt gebruiken (y) Maar ook ik zou me achter de oren krabbel voor ik echt edits aan die code zou maken en dus gegarandeerd de consequentie zou slopen :police:

En absoluut, in C zijn macro's meestal de enige oplossing wat het onmisbaar maakt. Maar in C++ kennen we function overloading of inline functions :)

Ennuh, had startAfter() niet 4 parameters :angel: ;D

Als Arduino IDE alternatief kun je inderdaad sloeber gebruiken. Maar ook de Arduino CLI werkt wel aardig waarvoor mensen ook bijvoorbeeld Notepad++ integratie voor maken.

OpenLCB en LCC, ik heb weer genoeg te lezen ::)

@meino, en top dat je nog lekker bezig bent! (y)


Timo die de LP gaat omkeren en nog even een hoofdstukje analoog gaat lezen...
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Robert E op 19 November 2019, 22:05:55
Citaat
dus pak er een biertje bij

Wijntje ook goed ? :)

Goed te zien dat Meino verder gaat :)

Die int en byte, in gcc (wat onderhuids in Arduino zit) kun je denk ik beter gebruik maken van int16_t / uint16_t  en int8_t / uint8_t al die typen.
Maar wie ben ik ......

https://www.gnu.org/software/libc/manual/html_node/Integers.html

Maakt het net iets duidelijker (laat staan de mogelijke ellende als je van een 8 bits (Atmel AVR) geval naar een 32 bits (STM32F1 en hoger bijv) geval gaat).

Citaat
OpenLCB en LCC, ik heb weer genoeg te lezen

Sluit ik me bij aan...

Alleen nog een bus variant dus ..... Zo is er nog deze

http://bidib.org/

Mvg

Robert
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 22 November 2019, 20:04:26
Heb je nog voortgang gemaakt met je baan?

Ik heb even een tussendoortje gedaan, zie https://forum.beneluxspoor.net/index.php?topic=77057.229 (https://forum.beneluxspoor.net/index.php?topic=77057.229), een brandweerkazerne.

Maar de meeste tijd de afgelopen week is besteed aan een irritant probleem. Namelijk dat de CanBus zo nu en dan blokkeerde, tenminste daar leek het op. Ik had al uitgezocht dat het waarschijnlijk de DCC_Controller was die stopte. Het probleem was dat het probleem met logging via de seriele monitor niet te traceren was want dan gebeurde het nooit (dat had me toen al aan het denken moeten zetten). Maar na veel knutselen en in bouwen van recovery werkte het redelijk. Van de week, naar aanleiding van de discussies, had ik de bibliotheek omgegooid, met een virtuele class en nog wat interne reorganisaties. Helaas werkte het voor geen meter, de DCC_Controller blokkeerde nu al na 5-10 minuten en soms kwam hij niet op. Dus logging actief maken en kijken wat er gebeurd. Jammer was dat het probleem dan nooit optrad. ????
Dus de logging weer afgezet, en bingo het hing weer. Zaken verandert (ik verdacht dat in de heap, die is slechts 2k groot op een UNO, storage overschreven werd), mallocs en new verandert tot assignments op de stack. Helaas het bleef instabiel. Weer getracht met logging het probleem te tackelen. Maar weer als de logging actief was, geen problemen.
Kortom ik stond op het punt om weer terug te keren naar de oorspronkelijk code. Maar gisteravond toch nog een keer iets veranderen eerst met logging actief, uploaden en draaien.Dat was stabiel, dus logging weer uitgezet en weer testen, he dat is ook stabiel.
Maar toen viel het kwartje. Normaal als ik geupload heb en de logging is niet actief, dan trek ik gelijk de USB kabel los tussen de laptop en de Arduino, dat had ik nu niet gedaan. Dus de kabel los getrokken, en ja binnen 10 minuten blokkeerde de DCC_Controller, USB kabel terug, en hij ging weer lopen. Ok hoe staat het met de 5V voeding? (Ja Timo ik weet wat je nu gaat zeggen, de spullen om naar 12V te gaan zijn in huis maar nog niet aangesloten). Wat bleek de 5V voeding voor de DCC_Controller had ik op de Vin pin aangesloten en niet op een 5V pin. Dus deze Arduino kreeg te weinig spanning om stabiel te zijn en dat was de oorzaak van alle problemen.
Het was niet leuk de afgelopen week, maar ik ben wel blij dat ik nu weet wat de oorzaak was, niets is vervelender dan een probleem waarvan je hoopt dat het opgelost is, maar waar je geen verklaring voor hebt kunnen vinden.

Het is nu weer tijd voor leukere dingen .

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 22 November 2019, 21:44:59
Ik voel je pijn...Ik krijg m'n bluetooth module niet in AT modus desondanks ik alles goed doe.... nu loopt mn DCC centrale vertraging op
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 25 November 2019, 10:19:02
Oei, ja, dat kan je aardig bezig houden :-\ Maar ik moest toch even gniffelen :angel:

Wel een klassiek geval van per ongeluk twee parameters aanpassen tijdens het storingzoeken :-\ Wel heel fijn/goed dat je het probleem gevonden hebt! (y)


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 01 December 2019, 16:40:51
Het is weer tijd om weer eens verder te gaan met het oorspronkelijke doel van dit draadje, n.l. het gebruik van de Canbus en de verschillende systemen die daarmee verbonden zijn.
Even terug naar het schema:
(https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg) (https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg)
Ik wil nu de diverse systemen bespreken en ik begin met de DCC Interface.

(https://images.beneluxspoor.net/bnls/20191201_115604_DSC_0174.jpg) (https://images.beneluxspoor.net/bnls/20191201_115604_DSC_0174.jpg)

Dit systeem vertaalt de commando's die de DCC centrale verstuurd naar berichten die op de Canbus gezet worden. Op dit moment worden alleen DCC accessory (voor wissels en seinen) overgezet.
Dus dit systeem heeft naast het TMC2515 kaartje (voor de Canbus) ook een interface voor het DCC gebeuren nodig. Die interface is gebouwd op een stukje stripboard met een Optocoupler.

(https://images.beneluxspoor.net/bnls/DCC_optoisolator_1.jpg) (https://images.beneluxspoor.net/bnls/DCC_optoisolator_1.jpg)

Het gebruikte schema heb ik ooit van het Internet geplukt.
(https://images.beneluxspoor.net/bnls/DCC-optoisolator.jpg) (https://images.beneluxspoor.net/bnls/DCC-optoisolator.jpg)
Dat werkt goed, en door de optocoupler geen risico van opgeblazen inputs (of erger) op de Arduino.

Het complete aansluitschema ziet er dan als volgt uit.
(https://images.beneluxspoor.net/bnls/DCC-Interface.jpg) (https://images.beneluxspoor.net/bnls/DCC-Interface.jpg)

Wordt vervolgd.

Groet Meino

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 01 December 2019, 18:10:33
Laten we eens naar de Arduino schets kijken die het benodigde werk verzet.

/*
  A program to convert DCC commands to Canbus messages

  Copyright (C) Meino, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

*/

#include <DCC_Decoder.h>
#include <CanBus.h>

//
//  Other pin's used
//
#define DCCPIN     2

//
// Definitions for the DCC interface
//
#define K_DCC_INTERRUPT   0

//
//  The CanBus
//
CanBus *canBus;

We beginnen met een paar zaken zoals de bibliotheken die we gebruiken en de interrupt pin die voor DCC gebruikt wordt.
De bibliotheken die we gebruiken zijn DCC_Decoder (https://github.com/MynaBay/DCC_Decoder (https://github.com/MynaBay/DCC_Decoder)), arduino-mcp2515-master (https://github.com/autowp/arduino-mcp2515/archive/master.zip (https://github.com/autowp/arduino-mcp2515/archive/master.zip) en de KB_CanBus.

//
//  DCC Accessory packet handler
//
void DCC_PacketHandler(int address, bool activate, byte data)
{
  //
  //  Convert NMRA packet address to a useable address
  //
  //  This calculation seems to be a little weird. However it has to do with
  //  the way NMRA has implemented these things. Accessory decoders have an
  //  basic address and a sub address.
  //  F.I. decoder address 1 means first decoder (address 1) sub address 0.
  //  Decoder address 2 means first decoder (address 1) sub address 1.
  //  Decoder address 5 means second decoder (address 2), sub address 0, etc.
  //  So the basic decoder address is not a C like index but a counter like in
  //  Cobol, it doesn't start with 0.
  //

  //
  //  Start with the base address
  //
  address -= 1;
  address *= 4;

  //
  //  Add de sub address
  //
  address += 1;
  address += (data & 0x06) >> 1;

  //
  //  Do we perform a normal(-) or a switch(/) operation?
  //  For a normal operation enable is true, for a switch operation
  //  enable is false.
  //
  bool enable = (data & 0x01) ? 1 : 0;
  // This bit actual specifies which of the two coils need
  // to be activated. (or deactivated if activate is false, which is not used
  // by this code)

  DCC_ACC_MSG buf;
  buf.address = address;
  if (enable)
  {
    buf.direction = 0;
  }
  else
  {
    buf.direction = 1;
  }

  //
  //  Send the received accessory command
  //
  canBus->sendMsg(DCC_ACC, sizeof(DCC_ACC_MSG), &buf);
}

Dit is een routine die aangeroepen wordt door de DCC_Decoder bibliotheek wanneer er een compleet wissel/sein (accessory) commando is binnengekomen. Deze routine zal de binnengekomen commando analiseren en omzetten naar een Canbus bericht en dat bericht dan op de zendqueue van de Canbus interface zetten.

void setup()
{
  //
  //  Setup and start the CanBus communication
  //
  static const int nrOfBuffers = 7;
  static const int waitTime = 4;
  canBus = new CanBus(DCC_INTERFACE_ID,
                        new Mcp2515(10, waitTime, nrOfBuffers),
                        nullptr); // Only a sender, 250msg/sec, 10 sendbuffers
  canBus->begin();

  //
  //  Setup the DCC packet handler
  //
  DCC.SetBasicAccessoryDecoderPacketHandler(DCC_PacketHandler, true);
  DCC.SetupDecoder(0x00, 0x00, K_DCC_INTERRUPT); 
}

In de setup-routine creeren we de interface naar de Canbus ,alleen een zender en met de mogelijkheid om meer boodschappen per sec. te kunnen verzenden dan standaard mogelijk is. Verder initialiseren we de DCC_Decoder bibliotheek, melden de packethandler aan, en vertellen wat de interrupt pin is.

void loop()
{
  //
  //  Check for DCC packets
  //
  DCC.loop();

  //
  //  Activate the CanBus
  //
  canBus->heartBeat(); 
}

In de loop-routine hoeven we nu niet veel meer te doen dan de DCC_Decoder bibliotheek en de Canbus aan de gang te houden zodat binnenkomende DCC berichten aan de packethandler aangeboden kunnen worden en dat de  berichten die op de zendqueue van de Canbus interface staan verzonden worden.

Tot zover de DCC-Interface.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 03 December 2019, 18:45:28
Het volgende systeem is de S88-Interface.

(https://images.beneluxspoor.net/bnls/S88-Interface-bord.jpg) (https://images.beneluxspoor.net/bnls/S88-Interface-bord.jpg)

Dit systeem is verantwoordelijk voor het ontvangen en afhandelen van OCC_DT bericht. Gebruikmakend van het adres van de bezetmelder wordt een bezetmelding in het S88 schuifregiaster gezet.

De koppeling tussen de S88 Interface en mijn centrale (MDRRC-II) vind plaats d.m.v. een Cat-5 netwerkkabel. Dat betekend dat de aansluiting voor de kabel op de S88-Interface een RJ45 connector is. De pinlayout hiervan is als volgt.
(https://images.beneluxspoor.net/bnls/s88-N_schalt_1.gif) (https://images.beneluxspoor.net/bnls/s88-N_schalt_1.gif)
Ook op de MDRRC-II is dit de pin layout, dus de cat5 kabel moet wel een een-op-een kabel zijn.

Het aansluitschema is dan als volgt.
(https://images.beneluxspoor.net/bnls/S88-Interface.jpg) (https://images.beneluxspoor.net/bnls/S88-Interface.jpg)
Zoals te zien is worden niet alle pinnen gebruikt. Omdat de Canbus sytemen hun eigen voeding hebben, wordt bijv. de 12V/5V aanwezig op de S88 aansluiting niet gebruikt. Een andere mogelijkheid is het feit dat meerder S88 systemen aan elkaar verbonden kunnen worden. Dit omdat in het originele S88 ontwerp ieder systeem maar een schuifregister met 16bits hebben. Heb je meer dan 16 detectors, dan moet je meerdere koppelen. Dat probleem heb ik met een Arduino niet, ik kan in de schets het schuifregister zo groot maken als ik zelf wil, dus de mogelijkheid om meerdere S88-interfaces aan elkaar te koppelen ondersteun ik niet (meer).

Wordt vervolgd.


Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 04 December 2019, 08:27:35
Maak je eigenlijk zelf je terugmeld modules?

Ik ben nu zelf bezig om de terugmelding van 1 van onze clubbanen te ontwerpen. Ik kwam al gauw tot de ontdekking dat de S-88 bus een niet al te beste bus is. Bovendien zijn die standaard terugmeld modules rete duur.

Als ik die s88 bus aanleg moest ik eerst vanaf een CS3 2 meter naar links bekabelen om vervolgens 20+ meter een kabel in de andere kant te trekken. Zelfs met patchkabels zie ik dat somber in. Ik gebruik iig arduino's om de prijs te drukken. Toen kwam ik nog met het idee om elke arduino als een 'node' in stellen waarbij elke node de s88 bus uitleest en zelf de bus naar de volgende klokt om zo het signaal te 'boosten'.

Dit idee kreeg ik laatst toen ik met werk een excursie/workshop had bij Beckhoff. Zij hebben de ethercat bus ontwikkeld, de beste veldbus ooit. Bij deze bus wordt doet een master een lange golf informatie op de bus zetten en elke slave leest dit signaal in bit voor bit en klokt de bits zelf weer uit naar de volgende. Daarbij kan een slave zelf de data bits zetten of uitlezen on the fly.

Maar toen dacht ik dat het nog simpeler kan. Ik wil nu elke arduino iets van 32 bezetmelders geven en met de rs485 bus wil ik informatie doorsluizen naar de arduino die 5cm voor de Cs3 ligt. Die kan dan via de S88 bus alle bezetmelder informatie in de Cs3 stoppen.

Vind je het niet fijner om je arduino's zelf te terugmelding laten doen? Ik weet niet wat voor terugmeld modules je precies gebruikt?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 04 December 2019, 10:30:40
Ik heb een aantal verschillende typen detectors in gebruik. Ik ben lid geworden van https://www.merg.org.uk/ (https://www.merg.org.uk/) voor de kits. In dit geval ging het om de DTC8/DTC2 en PMP7 detectors. Verder heb ik twee z.g. Ali lichtsluisjes gebruikt. Dat zijn gemodificeerde infrarood benadering detectors. Deze detectors hangen allemaal aan diverse arduino's die hun staat via de Canbus door geven aan de S88 Interface. Dit zijn de Bezetmelder controllers, waar ik het later nog over zal hebben. Over de schets voor de S88 Interface, die had oorspronkelijk de mogelijkheid om meerdere van deze S88 arduino's in een daisy chain te hangen, waarbij de S88 arduino's ook de detectors zou bewaken. Dat concept heb ik verlaten en vervangen door het huidige concept waarbij de verantwoordelijkheid voor de Detector bewaking afgesplitst is naar aparte arduino's.

Overigens weet ik niet of jouw idee van signaal versterking gaat werken, de signalen die je zou moeten versterken zijn de CLOCK en de PS signalen. die zijn best tijdskritisch. Ik denk dat 1 tussenstap nog wel zal gaan, maar ik denk dat meerdere stappen een probleem wordt. Maar omdat ik nu in de S88 Interface meer dan 16 bits kan nabootsen, hoef ik geen daisy chain te maken en beperk ik de S88 connectie tot 1 korte Cat5 netwerk kabel.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 04 December 2019, 11:33:46
(https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg) (https://images.beneluxspoor.net/bnls/Overzicht-Kranenberg-Controllers-V4.jpg)
Ah ik vat em. Ik dacht eerst dat je een wat langere s88 bus met echte S88 terugmeldmodules in gebruik heb. Maar als ik het nu wel goed snap, loopt de blauwe s88 bus op het schema slechts tussen 1 arduino en de centrale? En dan heb je meerdere arduino's die spoor bezet statussen doorgeven aan de interface arduino via de can bus? Dan denken we toch hezelfde  (y)
Citaat
Overigens weet ik niet of jouw idee van signaal versterking gaat werken
Zullen we iig nooit weten, ga het toch niet uitproberen  :P. Differentiaal spanning ftw

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 04 December 2019, 11:40:16
OK, tijd voor de Arduino schets.

/*
  A program to accept occupancy detector status from the Canbus
  and set the proper bit in the S88 shift register

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  email: meino@innerside.demon.nl
*/
#include <CanBus.h>

//
//  Pin layout is for the Arduino Uno
//

// Connections for S88 bus:
//
// s88 pin 1 Data - Arduino pin 8 = Data_Out to Oommand Station, or pin 7 (Data in) from the next Arduino in the chain
// s88 pin 2 GND  - Arduino GND
// s88 pin 3 Clock - Arduino pin 2, interrupt 0
// s88 pin 4 PS - Arduino pin 3, interrupt 1
// S88 pin 5 Reset (not used)
// s88 pin 6 V+ - Arduino 5V (not used)
//

// Connections for S88N (RJ45)
//
// RJ45 pin 1 12V/5V   (not used)
// RJ45 pin 2 DATA  - Arduino pin 8 Data_Out to Oommand Station, or pin 7 Data In from the next Arduino in the chain
// RJ45 pin 3 GND   - Arduino GND
// RJ45 pin 4 CLOCK - Arduino pin 2, interrupt 0
// RJ45 pin 5 GND   - Arduino GND
// RJ45 pin 6 PS    - Arduino pin 3, interrupt 1
// RJ45 pin 7 RESET    (not used)
// RJ45 pin 8 RAILDATA (not used)
//


#define S88_DATAOUT_PIN     8
#define S88_DATAIN_PIN      7
#define S88_CLOCK_PIN       2
#define S88_PS_PIN          3

#define S88_CLOCK_INTERRUPT 0
#define S88_PS_INTERRUPT    1

const int nrOfOccDetectors = 6 * 16;

volatile byte pendingStates[nrOfOccDetectors];
volatile byte dataRegister[nrOfOccDetectors];

volatile long clockTicks = 0;

CanBus *canBus;
Hier zijn een aantal zaken gedefinieerd die in de schets gebruikt worden. De KB_canbus bibliotheek (CanBus.h), pin definities voor de gebruikte pinnen en verder twee byte arrays die de (pseudo) schuifregisters vormen. De grootte van deze (pseudo) schuifregisters is variabel en moet tenminste zo groot zijn als wat gedefinieerd is in de DCC centrale.
In de bytearray pendingStates wordt de staat van een specifieke detector bijgehouden (0 is vrij, 1 is bezet). De bytearray dataRegister bevat een copie van pendingStates, en levert de informatie voor de S88. De werking is vrij simpel, op het moment dat het PS signaal binnen komt, wordt de inhoud van pendigStates gecopieerd naar dataRegister en wordt de clock index terug gezet op 0. Bij iedere activering van het CLOCK signaal, word de DATA pin gezet aan de hand van de inhoud van de byte uit dataRegister waar de clock index naar verwijst, gevolgd door een ophoging van de clock index naar de volgende byte.

void PS_Int()
{
  //
  //  Load the detector states in the data register
  //
  memcpy(dataRegister,pendingStates,nrOfOccDetectors);

  clockTicks = 0;     //  reset index in the dataRegister
}

//
//  Set the state of the next detector on the output pin
//
void clock_Int()
{
  digitalWrite(S88_DATAOUT_PIN, dataRegister[clockTicks]);
  //
  //  If we are chained to additional detectors, reads a bit from the chain and
  //  store it in the current, just written, position
  //

  //
  //  Delay makes reading output signal from next Arduino in chain more reliable.
  //
  //delayMicroseconds(2);
  //dataRegister[clockTicks]=digitalRead(S88_DATAIN_PIN);
 
  clockTicks = (++clockTicks) % nrOfOccDetectors; // Goto next bit in dataRegister
}

Dat wordt gedaan door deze twee interupt routines, die gekoppeld zijn aan pin 2 en 3 (op een Arduino UNO).

//
//  A function that executes whenever a message is received from the Canbus
//  It's purpose is to analyze the received event Occupancy detector and
//  update the proper element in the semaphore table or turnout table.
//
void occDtReceiver(unsigned char aMsgLen, OCC_DT_MSG *msgData)
{
  if (msgData->address < nrOfOccDetectors)
  { 
    pendingStates[msgData->address] = msgData->state;
  }
}

De pendingStates byte array wordt gevoed door de berichten die van de Bezetmelder controllers (en wissel controllers) versturen. Dat geveurd in de bovenstaande interrupt routine. Het is de verantwoordelijkheid van de Bezetmelder controller om te weten welk bit/byte in de arrays te zetten. Dus de Bezetmelder controllers hebben kennis van de bezetmelder adressen in de S88 registers, zoals dat door de Centrale en het treinbesturings programma gebruikt wordt.

void setup()
{
  //
  //  Intialize the dataRegister
  //
  memset(pendingStates,0,nrOfOccDetectors);
  memset(dataRegister,0,nrOfOccDetectors);

  //
  //  Setup the input/output pins and the interrupts for the S88 communication
  //
 
  //pinMode(S88_CLOCK_PIN, INPUT_PULLUP);
  //pinMode(S88_PS_PIN, INPUT_PULLUP);
 
  pinMode(S88_DATAIN_PIN, INPUT_PULLUP);
  pinMode(S88_DATAOUT_PIN, OUTPUT);
  digitalWrite(S88_DATAOUT_PIN, LOW);
 
  attachInterrupt(S88_CLOCK_INTERRUPT, clock_Int, RISING);
  attachInterrupt(S88_PS_INTERRUPT, PS_Int, RISING);

  //
  //  Setup and start the CanBus communication
  //
  canBus = new CanBus(S88_INTERFACE_ID, nullptr, new Mcp2515(10)); // Only a receiver
  canBus->setMsgReceiver(OCC_DT, occDtReceiver);
  canBus->begin();
}

De setup routine waarin het een en ander wordt klaargezet.

void loop()
{
  //
  //  Are there messages on the CanBus?
  //
  canBus->heartBeat();
}

In de loop functie wordt alleen de Canbus aan de gang gehouden. De S88 bus draait volledig op de interrupts die binnenkomen via de PS en CLOCK signalen.

Tot zover de S88 Interface.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 04 December 2019, 13:26:32
CanBus *canBus;
Heeft het gebruik van een pointer is dit scenario nog een specifiek voordeel over het niet gebruiken van een pointer?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 04 December 2019, 15:49:02
Dat hangt er vanaf. Daar heb ik met Timo ook al een uitgebreide discussie over gehad. Laten we het houden op een persoonlijke voorkeur om objecten zoveel mogelijk d.m.v. van pointers te refereren. Pointers kunnen als parameters aan methods door gegeven worden, de physieke objecten niet. Die voorkeur heb ik van Java overgehouden, daar heb ik veel meer ervaring in dan C++ en daar is alles "By Reference", lees pointers. Daardoor hoef ik me in de code niet af te vragen of ik een -> of een . moet gebruiken.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Patrick Smout op 04 December 2019, 22:29:19
Even ter zijde maar bij EtherCat is het de 32 bit crc foutcontrole in het frame die ervoor zorgt dat een verdwaalde clockpuls geen onaangename gevolgen heeft. Een S88 bus heeft geen foutcontrole en daar wringt het schoentje. Als elke s88 melder een 4 of 8 bit Crc achter de data zou plakken en dus 20 of 24 bit zou doorschuiven dan zou dit een prima foutongevoelige bus zijn. De andere oplossingen maken allen gebruik van foutcontrole en zo hoort het ook.

Mvg

Patrck
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 December 2019, 13:32:39
Laten we weer eens verder gaan. Het volgende systeem waar ik het over wil hebben is de Bezetmelder Controller. Dit systeem houdt een of meerdere bezetmelders in de gaten en geeft hun status (vrij of bezet) door aan de S88 Interface. Maar voor ik daarmee begin wil ik eerst het eens hebben over de bezetmelders zelf.
Ik heb momenteel drie verschillende typen in gebruik.

Het is duidelijk dat iedere bezetmelder schakeling zijn eigen aansturing nodig heeft.
Bij de DTC8/DTC2 moet de pin in INPUT staan en bezet is LOW, verder geen extra voorzieningen om spookmeldingen/spikes op te vangen.
Voor de PMP7 moet de pin in INPUT_PULLUP staan en bezet is ook LOW. Echter voor deze bezetmelder moet de software korte veranderingen uitfilteren.
Voor de lichtsluis moet de pin ook in INPUT_PULLUP staan, maar de waarde voor bezet is nu HIGH (de lichtstraal wordt onderbroken, dus de benadering sensor triggered niet). Verder omdat deze melder direct uit slaat (tussen wagons) moet er een wachtijd ingebouwd zijn als de melder van bezet naar vrij gaat.

Daarom heb ik een kleine bibliotheek gemaakt waarin ieder type bezetmelder door zijn eigen klasse vertegenwoordigd is.

Wordt vervolgd

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 December 2019, 16:59:06
Ik heb dus voor het gemak een kleine bibliotheek gemaakt voor die klassen die met de verschillende detectors kunnen omgaan.
Ik zal dat bespreken aan de hand van de het KB-Bezetmelder.h bestandje. Als iemand de code wil hebben, stuur me een PB dan stuur ik wel een zip bestandje met de bibliotheek.

//
//  Type definition for the call back routine which will handle a state change
//
typedef void (*stateHandler_t)(byte s88Bank, byte s88Id, bool occupiedState);

Omdat ik de definities van de detector klassen zo schoon mogelijk wilde houden. bevat deze geen specifieke (Canbus) code hoe een bezetmelding afgehandeld wordt. De stateHandler is en externe interrupt routine die je bij het detector object registreerd en door hem wordt aangeroepen als het detector object dat nodig vindt. Deze typedef definieert het prototype van deze routine. In mijn geval is het deze routine die de bezetmelder staat omzet naar een Canbus bericht en dat verstuurd.

class Detector
{
protected:
    short pin;              // Digital pin in use by Detector
    int  occupiedValue;     // Value of the digitalRead() when the detector
                            // is occupied
    int  occupiedPinMode;   // value set by pinMode() during attach
   
    bool occupied;          // true is occupied, false is free
   
    char *description;      // Just a description, used for debugging
    byte s88Bank;           // The S88 bank (containg 16 detectors)
    byte s88Id;             // The number of the detector in the S88 bank
   
    //
    //  A number of timer values (in mS) used while switching from
    //  free to occupied or switching from occupied to free
    //
    short waitTimeOn;
    short waitTimeOff;
    long pendingTime;
    bool pendingState;
   
    long timeOfLastMessageSend; // Last time the state handler was invoked

    stateHandler_t stateHandler; // An external interrupt routine that handles
                                 // a state switch of the detector

    //
    //  Default functionality, common for most of the detector classes.
    //
   
    //
    //  Initial setup of the class.
    //
    void init(char *aDescription, short aPin, byte aS88Bank, byte aS88Id);
   
    //
    //  The basic standard heartbeat method, reponsible for monitoring the
    //  detector and invoking the state handler when a state changed.
    //
    void doHeartBeat();
   
    //
    //  The basic attach functionality, responsible for setting up the
    //  digital pin in use by a detector.
    //
    void doAttach();
   
public:
    //
    //  Register the external state handler, respomsible for the proper handling
    //  of a detector state change.
    //
    void registerStateHandler(stateHandler_t aStateHandler);

    virtual void heartBeat() = 0;
    virtual void attach() = 0;
};

Dit is de abstracte base class waarvan alle detector klassen afgeleid zijn. Hij bevat zoveel mogelijk de standaard functionaliteit. Zoals init(..), doAttach(..) en doHeartBeat(..). De init(..) initialiseerd de gemeenschappelijke eigenschappen van de base class, doAttach(..) activeerd de pin waar de detector aanhangt en doHeartBeat(..) verzorgt de standaard monitoring van de pin.
De registerStateHandler(..) registreerd in het object wat de interrupt routine is die door doHeartBeat(..) aangeroepen wordt als de staat van de bezetmelder wijzig, doHeartBeat(..) roept deze routine ook verder nog iedere seconde opnieuw aan, als herinnering van de huidige staat van een bezetmelder.
De twee virtuele methods attach(..) en heartBeat(..) moeten physiek door de afgeleide klassen worden geimplementeerd. Op dit moment roepen ze in de afgeleide klassen alleen de doAttach en doHeartBeat aan, maar het biedt de mogelijkheid on afwijkende strategien te implementeren.
Dit is een abstracte klas omdat hij geen constructor bevat, en kan dus niet zelfstandig geconstrueerd worden, dat kan alleen maar door de afgeleide klassen, die zetten n.l. nog een paar andere eigenschappen die nodig zijn voor de correcte werking.

//
// A Class that handles the MERG DTC8/DTC2 detector. See www.merg.org.uk
//
// it uses the following settings:
//  PinMode INPUT
//  pinValue for occupancy LOW
//  waitTimeOn 0
//  waitTimeOff 0
//
class DTC8 : public Detector
{
public:
    DTC8(char *aDescription, short aPin, byte aS88Bank, byte aS88Id);
   
    void heartBeat();
    void attach();
};

//
// A Class that handles the MERG PMP7 (Piocket money project) detector.
// See www.merg.org.uk
//
// it uses the following settings:
//  PinMode INPUT_PULLUP
//  pinValue for occupancy LOW
//  waitTimeOn 15mS       to counteract bounce behavior
//  waitTimeOff 15ms      to counteract bounce behavior
//
class PMP7 : public Detector
{
public:
    PMP7(char *aDescription, short aPin, byte aS88Bank, byte aS88Id);
       
    void heartBeat();
    void attach();
};

//
// A Class that handles a modified Arduino infrared sensor detector.
//
// it uses the following settings:
//  PinMode INPUT_PULLUP
//  pinValue for occupancy HIGH
//  waitTimeOn 5mS       
//  waitTimeOff 2000ms    to counteract short interrupts caused by the wagons
//
class LS : public Detector
{
public:
    LS(char *aDescription, short aPin, byte aS88Bank, byte aS88Id);
   
    void heartBeat();
    void attach();
};

Dit zijn de definities voor de drie afgeleide klassen die ik gebruik voor de physieke detectors. Iedere klas zet nog vier andere eigenschappen die toegespitst zijn op de specifieke physieke detector, Zie het commentaar in de kode.

Groet Meino

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 December 2019, 21:48:46
De Bezetmelder Controllers zijn ook weer Arduino Uno's.

(https://images.beneluxspoor.net/bnls/20191206_195532_DSC_0174.jpg) (https://images.beneluxspoor.net/bnls/20191206_195532_DSC_0174.jpg)

Een fotootje van de DTC en PMP7 bezetmelders.

(https://images.beneluxspoor.net/bnls/20191206_195700_DSC_0176.jpg) (https://images.beneluxspoor.net/bnls/20191206_195700_DSC_0176.jpg)

Laten we nu even naar de schets kijken die de Bezetmelder Controller bestuurd.

/*
  A program to monitor occupance detectors and send their status to the S88 interface

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  email: meino@innerside.demon.nl
*/
#include <KB-Bezetmelder.h>
#include <CanBus.h>

#define DEBUG 0

typedef struct {
  short systemId;
  Detector *occ;
} occDt;

Twee bibliotheken worden gebruikt, de Canbus en de KB-Bezetmelder bibliotheek. Verder definieren we een structure die we gebruiken om een array te maken. Hiermee kunnen we bepalen welke Detector objecten door dit specifiek systeem worden gecontroleerd. Ieder systeem krijgt via een paar jumpers zijn eigen uniek id. Hierdoor hoef ik niet voor iedere controller een aparte schets bij te houden.

const int nrOfDetectors = 32;

occDt occDetectors[nrOfDetectors] =
{
  /* Detectors on controller 1 */
  { 1, new DTC8("S4",  2,1, 1)},            //  1.01
  { 1, new DTC8("S4C", 3,2,14)},            //  2.14
  { 1, new DTC8("S5C", 4,2,15)},            //  2.15
  { 1, new DTC8("S6",  5,1, 2)},            //  1.02
  { 1, new DTC8("S6B", 6,1, 8)},            //  1.08
  { 1, new DTC8("S11B",7,1, 6)},            //  1.06

  /* Detectors on controller 2 */
  { 2, new PMP7("S4B", 3,2,12)},            //  2.12
  { 2, new LS("S11A",4,1, 3)},              //  1.03
  { 2, new PMP7("S7A", 5,2,13)},            //  2.13
  { 2, new LS("S10B",6,1,15)},              //  1.15
 
  /* Detectors on controller 3 */
  { 3, new DTC8("S2",  2,1,10)},            //  1.10
  { 3, new PMP7("S3B", 3,2,11)},            //  2.11
  { 3, new PMP7("S7",  4,1,16)},            //  1.16
  { 3, new DTC8("S8A", 5,1, 7)},            //  1.07
  { 3, new PMP7("S9B", 6,1,14)},            //  1.14
  { 3, new PMP7("S10A",7,2,10)},            //  2.10
  { 3, new DTC8("S8",  8,1,13)},            //  1.13
  { 3, new PMP7("S3",  9,1,11)},            //  1.11
 
  /* Detectors on controller 4 */
  { 4, new DTC8("S1B", 2,2, 2)},            //  2.02
  { 4, new DTC8("S4A", 3,2, 9)},            //  2.09
  { 4, new DTC8("S5A", 4,1, 4)},            //  1.04
  { 4, new DTC8("S12C",5,2, 3)},            //  2.03
  { 4, new DTC8("S19A",6,1, 5)},            //  1.05
  { 4, new DTC8("S19C",7,2, 6)},            //  2.06
   
  /* Detectors on controller 5 */
  { 5, new DTC8("S1",  2,1, 9)},            //  1.09
  { 5, new DTC8("S2B", 3,2, 7)},            //  2.07
  { 5, new DTC8("S5B", 4,2, 1)},            //  2.01
  { 5, new DTC8("S8B", 5,2,16)},            //  2.16
  { 5, new DTC8("S9A", 6,2, 8)},            //  2.08
  { 5, new PMP7("S9C", 7,1,12)},            //  1.12
  { 5, new DTC8("S12B",8,2, 4)},            //  2.04
  { 5, new DTC8("S19B",9,2, 5)}             //  2.05
};

In totaal 32 bezetmelders. De eerste parameter is de naam van de sectie die ik in AnyRail aan de specifieke sectie heb gegeven, dit wordt alleen in debugging mode gebruikt. De tweede parameter is de digitale pin waar de detector aan verbonden is. De derde en vierde parameter zijn de S88 adressen zoals die in mijn besturings programma (Koploper) gedefinieerd zijn.

//
//  The CanBus
//
CanBus *canBus;

//
//  Interrupt routine for the detectors, that is called when an
//  Detector wants to report a state.
//
void stateHandler(byte s88Bank, byte s88Id, bool occupiedState)
{
    //
    //  Send the new state to the S88 Interface
    //
    OCC_DT_MSG buf;
    buf.address = ((s88Bank-1)*16)+(s88Id-1); // convert to a bit index
    if (occupiedState)
      buf.state = 1;
    else
      buf.state = 0;

    //
    //  Repeat the message, to cover the occasional mishap
    //
    canBus->sendMsg(OCC_DT,sizeof(OCC_DT_MSG), &buf);
    canBus->sendMsg(OCC_DT, sizeof(OCC_DT_MSG), &buf);

#if (DEBUG)
    Serial.print("[stateHandler], detectornr: ");
    Serial.print(buf.address);
    Serial.print(", state: ");
    Serial.println(buf.state);
    Serial.flush();
#endif // DEBUG
}

Het CanBus object en de stateHandler interrupt routine. In deze stateHandler interrupt routine wordt de informatie komend van de Detector vertaald naar een OCC_DT bericht en op de zend queue van het CanBus object gezet.

//
//  Pins used to define the ID of this controller
//  It is used to filter out all semaphores which aren't controlled by
//  this instance of the SeinController
//
#define PIN_ID_0  A0
#define PIN_ID_1  A1
#define PIN_ID_2  A2
#define PIN_ID_3  A3

//
//  ID that defines this specific instance of the controller.
//  It's actual value is established during setup and is based on some jumpers
//
int systemId = 0;

void setup()
{
  //
  //  Based on which pin is connected to ground, the controller ID is established
  //
  pinMode(PIN_ID_0, INPUT_PULLUP);
  pinMode(PIN_ID_1, INPUT_PULLUP);
  pinMode(PIN_ID_2, INPUT_PULLUP);
  pinMode(PIN_ID_3, INPUT_PULLUP);

  systemId = 0;
  if (!digitalRead(PIN_ID_0)) systemId = systemId+1;
  if (!digitalRead(PIN_ID_1)) systemId = systemId+2;
  if (!digitalRead(PIN_ID_2)) systemId = systemId+4;
  if (!digitalRead(PIN_ID_3)) systemId = systemId+8;
 

#if (DEBUG || BEZETMELDER_DEBUG || CANBUS_DEBUG || CANBUS_DEBUG1)
  Serial.begin(115200);
  while (!Serial);
  Serial.print("Starting Controller ID: ");
  Serial.println(systemId);
  Serial.flush();
#endif //DEBUG

  //
  //  Setup and start the CanBus communication
  //
  canBus = new CanBus(BEZETMELDER_CONTROLLER_ID+systemId, new Mcp2515(10), nullptr); // Only a sender
  canBus->begin();

  //
  //  Activate all the occupancy detectors and attach them to their pins
  //
  for (int i = 0; i < nrOfDetectors; i++)
  {
    if (occDetectors[i].systemId == systemId)
    {
      occDetectors[i].occ->registerStateHandler(stateHandler);
      occDetectors[i].occ->attach();
    }
  }

  //
  // Wait a second, so the S88 interface is ready
  //
  delay(1000);
 
#if (DEBUG)
  Serial.println("ready");
  Serial.flush();
#endif
}

De Arduino setup routine, deze bepaald aan de hand van de PIN_ID pinnen de systemid. Het CanBus object wordt gecreerd en gestart. Vervolgens worden die Detectors uit de occDetectors array met een overeenkomend systemId geactiveerd. Hun pinnen worden in gebruik genomen en de stateHandler wordt geregistreerd bij hen. Om zeker te zijn dat de S88 Interface ook klaar is, wachten we 1 seconde voor we de setup routine verlaten. Dit omdat op het moment dat we de setup verlaten de Bezetmelder Controller direct zal beginnen met het zenden van OCC_DT berichten met de huidige status van de Detectors.

void loop()
{
  //
  //  Monitor all attached occupancy detectors
  //
  for (int i = 0; i < nrOfDetectors; i++)
  {
    if (occDetectors[i].systemId == systemId)
    {
      occDetectors[i].occ->heartBeat();
     
      //
      // Keep the canBus running
      //
      canBus->heartBeat();
    }
  }
}

De Arduino loop functie gaat bij iedere aanroep weer door de occDetectors array en iedere detector die hoort bij onze systemId wordt geactiveerd via de heartBeat method. Ook de canbus wordt actief gehouden door dan ook de heartBeat method op het CanBus object aan te roepen.

Tot zover de Bezetmelder Controller.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 12 December 2019, 11:25:12
Even een tijdje niet meegelezen dus probeer weer even bij te komen ;D Want je bent erg lekker bezig geweest in de tussentijd! (y)

const int nrOfOccDetectors = 6 * 16;

volatile byte pendingStates[nrOfOccDetectors];
volatile byte dataRegister[nrOfOccDetectors];
Oei, zonde. Waarom niet gewoon de bytes echt vullen? Kost het je maar 1/8e

PS_Int() is een ISR?

  //
  //  Delay makes reading output signal from next Arduino in chain more reliable.
  //
  //delayMicroseconds(2);
Zou niet moeten, zet je de data er wel op de juiste edge op?

Dat wordt gedaan door deze twee interupt routines, die gekoppeld zijn aan pin 2 en 3 (op een Arduino UNO).
Wat doet de tweede ISR dan? ???

void setup()
{
  //
  //  Intialize the dataRegister
  //
  memset(pendingStates,0,nrOfOccDetectors);
  memset(dataRegister,0,nrOfOccDetectors);
Vertrouw je de initialisatie niet? ;D

Pointers kunnen als parameters aan methods door gegeven worden, de physieke objecten niet.
Hoezo niet?  ???

daar heb ik veel meer ervaring in dan C++ en daar is alles "By Reference", lees pointers. Daardoor hoef ik me in de code niet af te vragen of ik een -> of een . moet gebruiken.
Als je voor alles references zou gebruiken ipv pointers is het gewoon altijd een .  ;D (Let op, geen commentaar! Lekker zo doorgaan!)

Als elke s88 melder een 4 of 8 bit Crc achter de data zou plakken en dus 20 of 24 bit zou doorschuiven dan zou dit een prima foutongevoelige bus zijn.
Foutongevoelig, ja. Robuust, nee. De thoughput zal er dan flink onder leiden.

  • DTC8/DTC2 Dit is een stroomdetector die werkt met een resonantie kring.
Is dit niet erg gevoelig voor lange rails etc? Ken het verder niet namelijk.

[edit]Zag later pas aan het plaatje dat ze op basis van CT's zijn. Maakt hij überhaupt wel gebruik van een resonantie kring?

  • PMP7 [...] dat betekend dat voor de bezetmelder sectie beide railstaven onderbroken moeten worden en via de schakeling gevoed worden.
Ook deze ken ik niet, maar de conclusie dat beide staven onderbroken moeten worden voor stroomdetectie is niet standaard.

Waarom heb je doAttach() en doHeartBeat() niet gewoon als attach() en heartBeat() gedefinieerd en uitgebreid in de afgeleiden? (Puur interesse!)

Dit is een abstracte klas omdat hij geen constructor bevat, en kan dus niet zelfstandig geconstrueerd worden,
Dat komt niet door het ontbreken van een constructor. Een abstract mag gewoon een constructor hebben en een (normale) classe hoeft geen expliciete constructor te hebben. De virtuele methodes maken hem abstract.

Misschien nog een dingetje wat je kunt doen, maak 'pinMode', 'waitTimeOn', 'waitTimeOff' etc const als ze toch niet veranderen in de afgeleiden.

  pinMode(PIN_ID_0, INPUT_PULLUP);
  pinMode(PIN_ID_1, INPUT_PULLUP);
  pinMode(PIN_ID_2, INPUT_PULLUP);
  pinMode(PIN_ID_3, INPUT_PULLUP);

  systemId = 0;
  if (!digitalRead(PIN_ID_0)) systemId = systemId+1;
  if (!digitalRead(PIN_ID_1)) systemId = systemId+2;
  if (!digitalRead(PIN_ID_2)) systemId = systemId+4;
  if (!digitalRead(PIN_ID_3)) systemId = systemId+8;
Arrays? :P


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 12 December 2019, 13:09:07
Ik dacht al, geen commentaar van Timo  ;)

Oei, zonde. Waarom niet gewoon de bytes echt vullen? Kost het je maar 1/8e
Dit begrijp ik niet helemaal ???, of bedoel je het gebruik van volatile? Dat is bij mij standaard voor data die in ISR (Interupt Service Routine) gebruikt worden.

Citaat
PS_Int() is een ISR?
Ja, zie setup.
  attachInterrupt(S88_CLOCK_INTERRUPT, clock_Int, RISING);
  attachInterrupt(S88_PS_INTERRUPT, PS_Int, RISING);

Citaat
Zou niet moeten, zet je de data er wel op de juiste edge op?
Stukje oude code, dat niet meer gebruikt wordt. Overigens was dat een advies van Internet.

Citaat
Wat doet de tweede ISR dan? ???
Zie de code. De S88 gebruikt 3 signalen, CLOCK, PS en RESET. Ik heb een ISR op de CLOCK en de PS signalen. RESET wordt niet gebruikt.

Citaat
Vertrouw je de initialisatie niet? ;D
Weet jij wat het verschil tussen C en C++ is? Het niet initialiseren van variabelen in C is een van de grootse valkuilen. Ik kom uit de C wereld, daar leer je op de harde manier dat je variabelen moet initialiseren voor je ze kunt gebruiken. Maar dat zit er bij mij zo ingesleten dat ik dat ook in C++ doe.

Citaat
Is dit niet erg gevoelig voor lange rails etc? Ken het verder niet namelijk.

Zag later pas aan het plaatje dat ze op basis van CT's zijn. Maakt hij überhaupt wel gebruik van een resonantie kring?
Dat is wat ik uit de technische beschrijving begreep. Nee ze zijn in mijn ervaring niet gevoelig voor lange rails (>3M).

Citaat
Ook deze ken ik niet, maar de conclusie dat beide staven onderbroken moeten worden voor stroomdetectie is niet standaard.

Ze moeten gevoed worden vanuit het DCC signaal. Dat beide rails onderbroken moeten worden volgt uit de beschrijving en dat wordt ook bevestigd door mijn ervaring met deze detector.

Citaat
Waarom heb je doAttach() en doHeartBeat() niet gewoon als attach() en heartBeat() gedefinieerd en uitgebreid in de afgeleiden? (Puur interesse!)
Dat doe ik om te voorkomen dat deze routines direct aangeroepen worden. Ik dwing nu dat afgeleide klassen een attach() en heartBeat() method implementeren.

Tot zover.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Patrick Smout op 12 December 2019, 21:24:52
Foutongevoelig, ja. Robuust, nee. De thoughput zal er dan flink onder leiden.

Hangt ervan af wat je flink noemt. 1,25 tot 1,50 maal trager uitlezen van de bus maar wel foutloos. Geen gedoe met 2 x uitlezen, tragere clock of andere pleisters. Geen slecht compromis, althans zo denk ik erover.
En als iemand zou beslissen om de s88 terugmelder met een microcontroller te maken dan kan je een crc8 snel berekenen door een 256 byte lookuptable , de data en de vorige crc waarde.
Fluitje van een cent.

Mvg, Patrick Smout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 12 December 2019, 23:48:30
Dit is natuurlijk een pure "What if" discussie.
De realiteit is dat S88 een standaard is geworden, niet omdat het de beste manier is, maar gewoon omdat het destijds simpel te implementeren was. Dus we moeten het daar mee doen of we dat leuk vinden of niet. Er zijn natuurlijk alternatieven zo als Loconet. Maar dat ondersteund mijn centrale niet.
Daarom gebruik ik de Canbus voor de echte communicatie en blijft de kabel voor de S88 communicatie zo kort mogelijk en gebruik ik een cat5 kabel (S88-n).

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Patrick Smout op 13 December 2019, 06:16:13
Meino,

Akkoord mbt de S88 bus standaard. We moeten het er mee doen.  Mijn originele opmerking mbt de crc was een reactie op een vergeljking van de S88 bus en Ethercat. Beiden kan je niet vergelijken en ik heb aangegeven wat er nodig was om een vergelijk mogelijk te maken. Jouw laatste opmerking was, technisch gezien, te kort door de bocht en dat heb ik dan ook even onder ogen gebracht. In mijn zelfbouwcentrale ( zie mijn artikel in Elektor DCC Centrale uit 2008) lees ik de S88 bus 2x uit alvorens er events van te maken ( sliding window principe). De S88 bus op mijn baan is ongeveer 8m lang. Geen last van spookdetecties maar een goede bus vind ik het niet. Vandaag zou ik zeker kiezen voor CAN. Ik moet ook wel zeggen dat al mijn S88 melders zelfbouw zijn. Er is heel wat aandacht gegaan naar PCB layout want, en dat wordt wel eens vergeten, daar kan ook wel wat gewonnen worden. Als het overgelaten wordt aan de software om de boel te redden dan kom je soms van een kale kermis thuis.

Mvg, Patrick


Mvg, Patrick
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 13 December 2019, 12:12:45
Patrick

Ik ben het volledig met je eens over de gebreken van S88. We noemen dat een bus (ook ik doe dat) maar eigenlijk is het dat niet. Het is gewoon een schuifregister op afstand. Als ik naar me zelf kijk, dan heb ik alle kennis en kunde om software te ontwikkelen, maar voor de hardware ben ik volledig afhankelijk van standaard componenten , zoals MCP2515 kaartjes voor de Canbus, dus daar moet ik het mee doen.
Ik denk het overgrote deel van de lezers op dit forum niet de diepe kennis van software en electronica hebben om dit soort zaken zelf te kunnen tackelen. Jij hebt duidelijk wel de kennis en kunde om dat anders te kunnen doen, en dat heb je ook gedaan. Als ik zelf de kennis had gehad om mijn eigen electronica te ontwikkelen, dan was er best een grote kans geweest dat ik dat ook had gedaan. Tenslotte ben ik ook met de Canbus aan de gang gegaan, ik had ook iets met een standaard Loconet of ExpressNet kunnen doen.
Overigens zijn dit soort discussies best nuttig, ik hoop alleen dat dit voor lezers met een minder diepere kennis niet al te verwarrend wordt en dat ze afhaken.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 13 December 2019, 12:38:24
….ik hoop alleen dat dit voor lezers met een minder diepere kennis niet al te verwarrend wordt en dat ze afhaken.
Ik snap lang niet alles wat hier wordt verteld, maar ik ben nog niet afgehaakt.
Wat betreft je opmerking dat s88 geen bus is maar een uit elkaar getrokken schuifregister: ik stond op het punt om zelf die opmerking te maken maar ik heb me even ingehouden. ;D
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 13 December 2019, 13:52:03
Op onze clubbaan hang in 1 arduino direct aan onze cs3 + en ik gebruik een zelf bedacht bussysteem.

Ik laat de s88 arduino (de master) via rs485 een bericht sturen naar de Rx van de 1e slave. Dit bericht begint met een ID byte, die de slaves zelf ophogen. Adhv deze byte weet de arduino welke van de te volgen bytes for hem zijn. De 2e byte bevat de packetgrootte. En verder zijn er 2 bytes per arduino slave gereserveerd om input status op te zetten.

De slaves lezen elke byte in, zetten hun input status in de voor hen bestemde byte en sturen de byte meteen weer door naar de volgende. Ik gebruik een 8 polige patchkabel tussen de slaves. In deze kabel loopt dus 1 rs485 bus van slave naar slave en er zit een 2e rs485 bus naast die doorloopt tot aan het einde van de bus. Bij de allerlaatste slave worden de rs 485 lijnen aan elkaar verbonden, zodat de laatste arduino het bericht doorsluist via deze 2e bus helemaal naar de Rx pin van de master.

Samengevat: de master stuurt een hele reeks bytes naar een ring van slaves. Elke slave leest elke byte in, doet iets of niets met de byte en stuurt hem onmiddelijk door naar de volgende slave totdat de bytes weer aankomen bij de master.

Het idee is dat de eerste bytes alweer bij de master terug zijn gekomen alvorens de master het hele packet heeft verstuurd. Ik heb dit idee direct gekopieerd van ethercat en ik gebruikt het principe van informatie on the fly aanpassen en meteen doorsturen. Alleen doe ik het via de seriele port en ik doe het byte voor byte ipv bit voor bit (en ik kan zo natuurlijk geen snelheid van 1GHz behalen en ethercat wel....)

De master berdrijft deze bus continu zodat deze ten alle tijden up-to-date is met de laatste bezetmeldingen. Er kunnen meer dan 100 updates per seconde (<- wat ruim genoeg is) plaatsvinden. Dus op het moment dat de CS3+ om informatie komt vragen, is de informatie er al.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 13 December 2019, 17:19:53
Ethercat ken ik niet echt, maar als ik dit zo lees dan krijg ik een deja vu gevoel, want het lijkt wel op het oude tokenring van IBM. Dat performde voor geen meter, maar had het grote voordeel dat de toenmalige netwerkgurus er aan konden rekenen en dat het een gegarandeerde doorvoersnelheid had. Ik heb in de vroege jaren 80 daar nog wel eens met de netwerk specialisten over gesteggeld, die beweerden dat Ethernet en IP prutprotocollen waren en dat je daar niet een fatsoenlijk netwerk mee kon uitrollen. Leuk voor het kleine grut (unix boxjes), maar niet voor het echte werk voor mainframes. Oorzaak; geen mogelijkheden om berichten te priortiseren, instabiel gedrag bij hoge belastingen (>40-50%) etc, etc  ;D.
Ter informatie, mijn implementatie van de Canbus kan nu ongeveer 250-350 berichten/sec afhandelen. Dat zou hoger kunnen zijn, maar dat kunnen de gebruikte MCP2515 kaartjes niet aan. De maximale bussnelheid die deze kaartjes aankunnen is 500kbps. De 1mbps kan hij niet aan. Ik ben nog van plan om te kijken wat er gebeurt als ik de kristal (8Mhz) vervang door een snellere (16 -20Mhz) de mcp2515 chip zou dat moeten aankunnen. Maar goed, de huidige prestaties zijn voldoende voor wat ik doe en nog van plan ben, ik wil in de toekomst ook de DCC lokcommando's nog over de Canbus verspreiden voor Arduino's met geluid. Dat zijn er ongeveer 220/sec.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 05 January 2020, 16:03:36
Het is alweer een tijdje geleden, maar ik wil toch nog even wat vertellen over de twee laatste systemen die ik gebruik, n.l. de wisselcontroller en de seincontroller. Deze beide systemen reageren op wissel/sein commando's ontvangen via de DCC interface.
Afgezien van de functionele verschillen, de wisselcontroller zet een wissel om en de Seincontroller bevat veel inteligentie om het seinbeeld voor seinstelsel 46 te bepalen, dat is heel specifiek voor mijn baan, als er simpeler seinen gebruikt worden kan de seincontroller het zelfde zijn als de wisselcontroller. In mijn situatie is het verschil t.a.v. de Canbus, dat de Wisselcontroller ook een bezetmelding doet over de stand van de wissel. Daarom heeft de wisselcontroller ook twee MCP2515 kaartjes.

(https://images.beneluxspoor.net/bnls/20191211_231023_DSC_0174.jpg) (https://images.beneluxspoor.net/bnls/20191211_231023_DSC_0174.jpg)

Zoals ik in mijn draadje over Kranenberg heb geschreven, worden de wissels aangedreven door gemodificeerde micro servo's. De modificatie is het loskoppelen van de interne electronica en het motortje wordt nu direct aangedreven waarbij twee microschakelaars de uitslag beperken en in combinatie met diodes wordt de bewegingsrichting bepaald door de polariteit van de spanning, 3V om de bewging niet al te heftig te maken. Dat was makkelijk op de eerste (analoge) opzet van de baan omdat de wissel simpel met een 2polige schakelaar te zetten was.
Maar nu is dat via een Arduino wat lastiger, een reguliere niet gemodificeerde servo was makkelijker geweest. Het omschakelen van de polariteit gebeurd nu door relais, die via een digitale pin op de Arduino aangestuurd wordt. Per wissel heb ik een relaispaar nodig.
De aansluiting is volgens het volgende schema.

(https://images.beneluxspoor.net/bnls/Wisselcontroller.jpg) (https://images.beneluxspoor.net/bnls/Wisselcontroller.jpg)

Wat ik niet in het schema heb opgenomen is dat nu de spanning voor de wisselmotor niet rechtstreeks naar de wissel gaat, maar via een bedieningspaneel loopt. Op dit paneel laat ik door leds zien wat de stand van de wissel is. Dat wordt ook weer met diodes geregeld. Ik laat dat hier verder rusten omdat ik van plan ben om dat te veranderen en de wisselcontroller niet meer de leds op het paneel aan te sturen, maar daarvoor een aparte Arduino op te tuigen die het paneel gaat beheren. Ik wil via het paneel in de toekomst ook zaken als rijwegen, en het bezetten van het hoofdspoor voor rangeerbewegingen kunnen regelen, Dit via drukknoppen op het paneel. Het idee is omdat met bezetmeldingen te doen. Deze kunnen dan in Koploper speciale acties triggeren om dit te kunnen integreren met het automatisch rijden. Maar dat is nog toekomst.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 January 2020, 12:09:04
Ok, tijd voor wat code.

Voor de Wisselcontrole heb ik ook een tweetal bibliotheken ontwikkeld. Een bibliotheek bevat de object klassen die de wisselmotoren aansturen, de andere bibliotheek is er voor om de stand van de wissels in EEPROM te kunnen opslaan.
De bibliotheek voor de aansturing van de wisselmotoren heet KB_Turnout. Deze bibliotheek bevat momenteel nog maar een objectklas om de relais voor de wissel aan te sturen. In de planning staat nog een uitbreiding om dit met reguliere servos te doen. Alle klassen in deze bibliotheek zitten weer achter de facade van een interface. Hierdoor isoleer ik de gebruiker van het object van de specifieke eigenschappen van de gebruikte techniek van de wisselmotor. Verder hebben deze klassen ook geen kennis van de omgeving waar ze in werken, dus geen kennis van de Canbus etc. De objecten koppelen het bereiken van een nieuwe stand weer terug via een z.g. "Callback" routine, zodat het aan de gebruiker overgelaten wordt wat er daarna precies mee gedaan kan worden. In mijn Wisselcontroller schets, is het in deze "Callback" routine dat de nieuwe stand in de EEPROM wordt bewaard en dat de Canbus boodschap voor de bezetmelder verzonden wordt.

Ik ga nu weer aan de hand van KB_turnout.h het een en ander bespreken
#define TURNOUT_STRAIGHT  false
#define TURNOUT_DIVERGING true
Twee gemaksdefinities voor de stand van de wissels.

//
//  Type definition for the call-back routine which will handle a state change
//
typedef void (*toStateHandler_t)(int toDccAddress, bool toDirection);
De definitie van de "Callback" routine.

//
//   The abstract base class for handling turnouts
//
class Turnout
{
protected:
    int toPin; //  Arduino pin connected to the relay
    bool targetDirection; //  Straight or Diverging
    bool currentDirection;
    int dccAddress; //  DCC address used for this turnout
   
    toStateHandler_t toStateHandler = nullptr;

    void initTo(int newDccAddress, int newToPin);

public:
    void setTargetDirection(bool newDirection);
    bool getCurrentDirection();
    int  getDccAddress();
    void registerToStateHandler(toStateHandler_t toStateHandler);
   
    virtual void attach(bool initialDirection) = 0;
    virtual void heartBeat() = 0;
};

De interface definitie voor de wissel objecten. De methods/functies setTargetDirection(..), getCurrentDirection(), getDccAdress() en registerToStateHandler(..) zijn voor alle klasimplementaties gelijk en hoeven voor een specifieke klas implementatie niet herschreven te worden.
Voor een specifieke implementatie moeten wel de attach(..) en heartBeat() methods gemaakt te worden. De attach(..) method initialiseert de wisselmotor en zet hem in de gegeven stand. De heartBeat() method zorgt er voor dat de wisselmotor blijft lopen. In de implementatie van de huidige wissels die d.m.v. een relais geschakeld worden doet deze routine niet veel omdat het simpel het hoog of laag zetten van een digitale pin op de Arduino is. Maar bij een echte servo, wil je dat de servo stap voor stap naar zijn nieuwe locatie gaat, dus hier moet de heartBeat() daar voor zorgen.

//
//  Class for turnouts controlled by a relay.
//
class RelayTurnout : public Turnout
{
public:
    RelayTurnout(int newDccAddress, int newToPin);
   
    void attach(bool initialDirection);
    void heartBeat();   
};
Tot nu toe de enige type wisselmotor diet nu in gebruik is.

Tot zover de bibliotheek voor de wisselmotors.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 January 2020, 14:51:10
Zoals genoemd, heb ik ook een kleine bibliotheek gemaakt om de wisselstanden in de EEPROM te kunnen opslaan. Dat doe ik omdat de relais die ik gebruik niet bistabiel zijn, dus tijdens de initialisatie worden alle wissels in de stand rechtdoor gezet. In principe zorgt koploer er wel voor dat de wissels uiteindelijk in de laatst bekende stand komen, maar dat werkt niet altijd goed, als er nog een wagon of loc op een wissel staat, kan er sluiting zijn als de wissel niet correct staat. Dit voorkomt dan het opstarten van het DCC signaal. Daarom sla ik de wisselstanden op in de EEPROM. Dit gebeurd niet continue, maar ik houd een cache bij (2 bytes groot, voor maximaal 16 wissels). Ieder bit in deze buffer vertegenwoordigd de stand van een wissel. Deze twee bytes worden om de minuut naar de EEPROM geschreven. Dit schrijven gebeurd cyclisch, zodat ik iedere keer een nieuwe locatie gebruik. Aan het eind gekomen van de beschikbare EEPROM ruimte begin ik weer aan het begin. Ik doe dit om zolang mogelijk gebruik te kunnen maken van het EEPROM.

#include <Arduino.h>
#include <EEPROM.h>

#define EEPROM_DEBUG 0
De standaard Arduino includes die nodig zijn plus een optie om debugging actief te maken.

class EepromController
{
private:
    const byte eepromStart = 255;
    const byte eepromEnd = 0;
    int eepromLength;
    byte turnoutData[2] = {0, 0};   // For 16 turnouts
    bool eepromRead = false;
    int eepromIndex = 0;
    bool eepromDirty = false;

public:
    EepromController();

    //
    //  Load turnout states from EEPROM into the cache buffer
    //
    void loadData();

    //
    //  Save the cache buffer to EEPROM
    //
    void saveData();
   
    //
    //  Retrieve the direction of a turnout from the cache buufer
    //
    bool getTurnoutDirection(int turnoutId);

    //
    //  Set the direction of a turnout in the cache buffer
    //
    void setTurnoutDirection(int turnoutId, bool turnoutState);
           
};
De klasdefinitie voor de EepromController. Ik denk dat het commentaar voldoende verklaring geeft voor het gebruik.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 January 2020, 16:08:26
Uiteindelijk draait het om de schets,  die is nu aan de beurt

/*
  A program to control turnouts (switches)

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  email: meino@..........
*/
#include "CanBus.h"
#include <KB_Turnout.h>
#include <KB_Eeprom.h>
De inleiding en de includes

//
//  ID that defines this specific instance of the controller.
//  It's actual value is established during setup and is based on some jumpers
//
int systemId = 1;

//
//  Pins used to define the ID of this controller
//  It is used to filter out all semaphores which aren't controlled by
//  this instance of the SeinController
//
#define PIN_ID_0  A0
#define PIN_ID_1  A1
#define PIN_ID_2  A2
#define PIN_ID_3  A3
Ook de wisselcontroller heeft een nummer, zodat ik met 1 schets de verschillende exemplaren kan laden. Dit zijn de pinnen die gebruikt worden om de systeem id te maken.

//
//  The CanBus
//
CanBus *canBus;

//
//  Data that is stored in EEPROM to remember the state of the
//  turnouts. It is handled by a eepromController class
//
EepromController *EepromControl = new EepromController();
De CanBus en EepromController objecten.

typedef struct {
  short toSystemId;
  Turnout *to;
} toTable;

const int nrOfTurnouts = 12;

toTable turnouts[nrOfTurnouts] = {
  {1, new RelayTurnout(1, 25)},
  {1, new RelayTurnout(2, 27)},
  {1, new RelayTurnout(3, 29)},
  {1, new RelayTurnout(4, 31)},
  {1, new RelayTurnout(5, 33)},
  {1, new RelayTurnout(6, 35)},
  {1, new RelayTurnout(7, 37)},
  {1, new RelayTurnout(8, 39)},
  {1, new RelayTurnout(9, 41)},
  {1, new RelayTurnout(10, 43)},
  {1, new RelayTurnout(11, 45)},
  {1, new RelayTurnout(12, 23)} // Replaced to a new shield, one of the originals failed
};
De tabel waarmee de 12 gebruikte wissels gedefineerd worden.

//
//  A function that executes whenever a message is received from the Canbus
//  It's purpose is to analyze the received event (Signal or turnout) and
//  update the proper element in the semaphore table or turnout table.
//
void dccAccReceiver(unsigned char aMsgLen, DCC_ACC_MSG *msgData)
{
  unsigned short dccId = msgData->address;
  byte dccState = msgData->direction;

  //
  //  Find the proper turnout detector with the matching address
  //  and sets its new direction based on the enable field from the
  //  received dcc command
  //
  for (int i = 0; i < nrOfTurnouts; i++)
  {
    if ((turnouts[i].toSystemId == systemId) &&
           (turnouts[i].to->getDccAddress() == dccId))
    {
      if (dccState == 0)
      {
        //
        //  Set the straight direction
        //
        turnouts[i].to->setTargetDirection(TURNOUT_STRAIGHT);
      }
      else
      {
        //
        //  Set the diverging direction
        //
        turnouts[i].to->setTargetDirection(TURNOUT_DIVERGING);
      }

      //
      //  End the for loop
      //
      return;
    }
  }
}
De routine die bij de CanBus is geregistreerd en door het CanBus object aangeroepen wordt als er een wissel commando ontvangen is. Deze routine gaat de wissel uit de tabel opzoeken, en als deze wissel onder controle van deze WisselController is, zal hij de ontvangen richting zetten op het gevonden wisselobject. A;ls het commando voor een wissel is die niet voorkomt, dan wordt het bericht niet verder behandeld.

void turnoutStateHandler(int aDccAddress, bool aDirection)
{
  //
  //  The occupance detectors start on bank 4, each bank has 16 occupancies, so a single
  //  bank is enough for the 12 turnouts in use
  //
  int s88Bank = 4;
 
  //
  //  The turnout tells us that it has reached it new state, so now we can save it to EEPROM
  //
  EepromControl->setTurnoutDirection(aDccAddress, aDirection);

  //
  //  Send the new state to the S88 Interface
  //
  OCC_DT_MSG buf;

  //
  //  At this moment we have no more the 12 turnouts, so don't bother about the S88 bank
  //
  buf.address = ((s88Bank - 1) * 16) + (aDccAddress - 1);
  if (aDirection)
    buf.state = 1;
  else
    buf.state = 0;

  canBus->sendMsg(OCC_DT, sizeof(OCC_DT_MSG), &buf);
}
Dit is de "Callback" routine die bij de wisselobjecten is geregistreerd. Deze routine wordt aangeroepen. Hier wordt de nieuwe richting in de cachebuffer van de EEprom geschreven en de bezetmelding voor deze wissel op de Canbus gezet.

static unsigned long lastTimeSaved = 0;

void setup()
{
  //
  //  Based on which pin is connected to ground, the controller ID is established
  //
  pinMode(PIN_ID_0, INPUT_PULLUP);
  pinMode(PIN_ID_1, INPUT_PULLUP);
  pinMode(PIN_ID_2, INPUT_PULLUP);
  pinMode(PIN_ID_3, INPUT_PULLUP);

  systemId = 0;
  if (!digitalRead(PIN_ID_0)) systemId = systemId + 1;
  if (!digitalRead(PIN_ID_1)) systemId = systemId + 2;
  if (!digitalRead(PIN_ID_2)) systemId = systemId + 4;
  if (!digitalRead(PIN_ID_3)) systemId = systemId + 8;

  //
  //  Setup and start the CanBus communication
  //
  canBus = new CanBus(WISSEL_CONTROLLER_ID + systemId, new Mcp2515(49), new Mcp2515(53)); // Sender + Receiver
  canBus->setMsgReceiver(DCC_ACC, dccAccReceiver);
 
  //
  //  Before we start setting the turnouts, introduce a small delay,
  //  so the S88 interface is ready
  //
  delay(1000);

  //
  //  Start the Canbus
  //
  canBus->begin();

  //
  //  Load the EEPROM data for the turnout directions
  //
  EepromControl->loadData();

  //
  //  Activate the turnout relays
  //  Set them in the direction which was stoored in the EEPROM
  //
  for (int i = 0; i < nrOfTurnouts; i++)
  {
    if (turnouts[i].toSystemId == systemId) // Handle only our turnouts
    {
      //
      //  Set the stateHandler
      //
      turnouts[i].to->registerToStateHandler(turnoutStateHandler);

      //
      //  Activate the pin and set the initial direction
      //
      turnouts[i].to->attach(EepromControl->getTurnoutDirection(turnouts[i].to->getDccAddress()));
    }
  }

  //
  // Initialize the time for the save loop
  //
  lastTimeSaved = millis();

}//--(end setup )---
De Arduino setup routine, waarin we de CanBus initialiseren, de systeem id bepalen en alle wissels die door deze controller worden beheerd in gebruik nemen en de nodige initialisaties doen, zoals de laatst bewaarde stand uit het EEPROM gebruiken om de richting van de wissel te zetten.

void loop()
{
  //
  //  Keep the turnouts and the CanBus busy
  //
  for (int i = 0; i < nrOfTurnouts; i++)
  {
    if (turnouts[i].toSystemId == systemId) // Handle only our turnouts
    {
      //
      // Keep on running
      //
      turnouts[i].to->heartBeat();
      canBus->heartBeat();
    }
  }

  //
  //  Calculate the time interval between two consequtive saves to EEPROM
  //
  long interval = 60000; // Every minute
  long timeDifference = millis() - lastTimeSaved;

  //
  //  When the internal timer does a wrap around, reset stuff
  //
  if (timeDifference < 0)
  {
    lastTimeSaved = 0;
    timeDifference = 0;
  }

  //
  //  When the time exceeds the set interval, save data to EEPROM
  //
  if ( timeDifference > interval )
  {
    EepromControl->saveData();
    lastTimeSaved = millis();
  }

}//-- (end loop)--
De Arduino loop functie. Bij iedere keer dat deze actief is, voeren we de heartBeat op alle actieve wissels en de CanBus uit, zodat die ook bezig blijven. Daarna checken we of de tijdsinterval tussen twee EEPROM saves is verstreken, als dat het geval is schrijven we de cache buffer naar het EEPROM geheugen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 January 2020, 20:49:06
Als laatste systeem wil ik het nog even over de SeinController hebben. Deze systemen zijn Arduino Mega's. De keuze voor Mega's is gemaakt omdat ik driehoogte seinen, stelsel 46 moest aansturen. Dit gebeurd met 3kleuren RGB ledjes. Om de benodigde kleuren Rood, Groen, Geel en Wit te krijgen, moest er gemengd worden. Dat lukte alleen goed als ik de intensiteit van de leds onderling en ook van de 3 kleuren (RGB) kon instellen. De makkelijkste manier is via een PWM pin. Om een driehoogtesein te kunnen aansturen heb ik 9 pwm pinnen nodig, vandaar de keuze voor de Mega.
Ook hier heb ik weer een bibliotheek ontwikkeld, de KB_Seinstelsel46 bibliotheek. Oorspronkelijk had ik het idee dat de aansturing van deze seinen volledig door Koploper plaats kon vinden. Helaas had Koploper geen ondersteuning van het sein stelsel 46 en om dat te gaan definieren zag ik op dat moment niet zitten. De kennis ontbrak mij en ik vond het erg gecompliceerd. Daarom heb ik het anders opgelost, de seinen zijn in koploper gedefinieerd als simpele armseinen en door Koploper worden ze op veilig of onveilig gezet. Om nu toch het correcte seinbeeld te kunnen tonen wordt in de definitie van een sein ook de route die na het sein gevolgd wordt opgenomen en het volgende sein voor die route be over automatisering kend is. Hierdoor kan de bibliotheek de juiste stand berekenen. Dit heb ik in het draadje https://forum.beneluxspoor.net/index.php?topic=79241.msg3221850071#msg3221850071 ooit eens geprobeerd uit te leggen, dat ga ik hier niet herhalen.

Toch nog een paar stukjes code van de SeinController.

//
//  Table with all active turnouts
//
const int nrOfTurnouts = 12;

Turnout turnouts[nrOfTurnouts] =
{
  Turnout(1),
  Turnout(2),
  Turnout(3),
  Turnout(4),
  Turnout(5),
  Turnout(6),
  Turnout(7),
  Turnout(8),
  Turnout(9),
  Turnout(10),
  Turnout(11),
  Turnout(12)
};

//
//  Table of all defined semaphores
//
const int nrOfSemaphores = 20;

Semaphore semaphores[nrOfSemaphores] = {
 
  // Semaphore 00a on controller 6 (3hoog Voorsein)
  Semaphore(CONTROLLER(6), SEMAPHORE(00), NO_STOP_DELAY,
            new Route(SEMAPHORE(58), SPEED_HIGH, DANGER),
            VOORSEIN46_DRIEHOOG,
            new RGBLed(PWM_PIN(2), INTENSITY(200), PWM_PIN(3), INTENSITY(150), PWM_PIN(4), INTENSITY(100)),
            new RGBLed(PWM_PIN(5), INTENSITY(48), PWM_PIN(6), INTENSITY(20), PWM_PIN(7), INTENSITY(50)),
            new RGBLed(PWM_PIN(8), INTENSITY(175), PWM_PIN(9), INTENSITY(150), PWM_PIN(10), INTENSITY(40))),

 
  // Semaphore 51 on controller 2
  Semaphore(CONTROLLER(2), SEMAPHORE(51), DELAY_STOP,
            new Route(TURNOUT(1),
                      new Route(SEMAPHORE(63)),             // Turnout 1 straight
                      SPEED_HIGH,
                      new Route(ROUTE_BARRED),              // Turnout 1 diverting
                      SPEED_STOP),
            SEIN46_EENHOOG,
            new RGLed(PWM_PIN(2), INTENSITY(18), PWM_PIN(3), INTENSITY(20))),   // (blok 2)
 
  // Semaphore 52 on controller 2
  Semaphore(CONTROLLER(2), SEMAPHORE(52), NO_STOP_DELAY,
            new Route(TURNOUT(3),
                      new Route(TURNOUT(2),               // Turnout 3 straight
                                new Route(ROUTE_BARRED),             // Turnout 2 straight
                                SPEED_STOP,
                                new Route(TURNOUT(1),                // Turnout 2 Diverting
                                          new Route(ROUTE_BARRED),                       // Turnout 1 straight
                                          SPEED_STOP, 
                                          new Route(SEMAPHORE(63),SPEED_LOW,DANGER),     // Turnout 1 diverting
                                          SPEED_LOW),
                                SPEED_LOW),
                      SPEED_LOW,
                      new Route(ROUTE_BARRED),            // Turnout 3 diverting
                      SPEED_STOP),
            SEIN46_EENHOOG,
            new RGLed(PWM_PIN(4), INTENSITY(9), PWM_PIN(5), INTENSITY(10))),     // (blok 6)
........
Dit zijn de  configuraties voor de wissels en de seinen. Daar ga ik hier niet verder op in. Een verklaring is in hier te vinden https://forum.beneluxspoor.net/index.php?topic=79241.msg3221850071#msg3221850071

//
//  A function that executes whenever a message is received from the Canbus
//  It's purpose is to analyze the received event (Signal or turnout) and
//  update the proper element in the semaphore table or turnout table.
//
void dccAccReceiver(unsigned char aMsgLen, DCC_ACC_MSG *msgData)
{
  //
  //  Find the proper turnout detector with the matching address
  //  and sets its state.
  //
  if (Turnout::updateDirection(msgData->address, msgData->direction))
  {
    //
    //  The dcc id matched a turnout no need to try the semaphores
    //
    return;
  }
  else
  {
    //
    //  the dcc id didn't match a turnout, so try the semaphores
    //
    Semaphore::updateState(msgData->address, msgData->direction);
  }
}
Dit is de routine die bij de CanBus is geregistreerd en aangeroepen wordt als er een wissel commando (Accessory decoder bericht) ontvangen is. Als het voor een wissel is, dan wordt de richting van de wissel gezet, is het voor een sein dan wordt de staat (veilig-onveilig) gezet.

in de Arduino setup() routine worden weer de CanBus, seinen en wissel objecten geinitialiseerd. Ook wordt weer een systeem id bepaald aan de hand van een aantal digitale pinnen.

void loop()
{
  //
  //  Are there messages on the CanBus?
  //
  canBus->heartBeat();
 
  //
  //  Trigger an update on all semaphores
  //
  Semaphore::loop();
}
De Arduino loop() functie hoeft slechts de CanBus actief te houden via de heartBeat(). Verder worden de seinen ook actief gehouden door de loop() functie op het overkoepelend statisch Semaphore object. Deze loop() functie zal alle seinen die onder controle van deze SeinControler staan actief houden. Dit houdt in dat het seinbeeld bepaald wordt (alleen als de staat van het sein is gewijzigd) en dat wordt getoond. Ook het knipperen van een geel sein (langzaam en snel knipper) wordt hierdoor gerealiseerd. In het geval van een armsein wordt de servo aangestuurd zodat de seinarm zich langzaam van een positie naar een andere positie beweegt.

Diot zijn tot nu toe de systemen die ik in gebruik heb. Een toekomstige uitbreiding die ik van plan ben te bouwen zijn controllers die luidsprekertjes kunnen aansturen om eventueel het geluid van stoomloks te kunnen maken. Het idee is dat dit geluid ongeveer de lok over de baan blijft volgen. Ik heb in China wat nano's met versterkertjes in bestelling staan. Of dit gaat lukken weet ik nog niet, maar mocht dat lukken, dan volgt daarvan nog wel een update.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 07 January 2020, 07:41:23
Had ik ook ooit bedacht om geluid te maken onder de baan ipv in de locs. Ik was toen 14 ofzo en ging niet echter verder dan een idee. Het heeft zeker wel toegevoegde waarde om dit uit te ontwikkelen.

Als ik dat nu zou doen, zou ik elke speaker exact hetzelfde geluid laten afspelen waarbij je alleen de versterkers regelt afhankelijk van de positie van de locs. Daarbij moet eigenlijk elke route zn eigen speakers hebben. Zodat elke trein een volg geluid kan krijgen.
Wat voor versterkers heb je gekocht?

Je hebt voor je seinen ook andere oplossingen dan een arduino mega in in te prakken voor slechts 9 pwm pinnen. Als je op een uno een timer ISR elke 10us ofzo laat draaien kan je een software PWM realiseren. Je kan ook de ingebouwde micros() functie gebruiken van Arduino. Je kan misschien wel of niet flikkeringen waarnemen als je andere interrupts hebt draaien die te veel tijd in beslag nemen.

Je kan ook PCA9685 boards kopen. Dat zijn I2C PWM drivers. Kan je gebruiken voor leds en voor servo's, ze kosten geen drol en ze zijn makkelijk in het gebruik. klik mij (https://www.banggood.com/PCA9685-16-Channel-12-bit-PWM-Servo-Motor-Driver-I2C-Module-p-1170343.html?akmClientCountry=NL&&cur_warehouse=CN)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 January 2020, 11:04:54
Over dat uitbreidings kaartje voor PWM pinnen, ik zal eerlijk zeggen dat ik lang niet alles weet wat er te krijgen is voor een Arduino. Maar ik had destijds een aantal Mega's gekocht voor 5,50 euro per stuk, een UNO deed toen 3,50 euro. Als je dat kaartje zou gaan gebruiken (dat is ook nog 3,20Euro per stuk) en een NANO (1,50-2 euro) dan is de besparing toch niet zo heel groot. En voorwat de ruimte betreft, dat is geen probleem onder de baan. Maar het is wel goed om te weten dat dat soort zaken ook beschikbaar zijn.

Over het geluid onder de baan. Ik heb een vaag idee hoe ik dat ga doen. Ieder blok in de hoofdrijbaan, een (of twee) speakertjes en een eigen NANO die het geluid voor dit blok controlleert. Dan Koploper een accesssory bericht laten genereren als een lok een blok binnen gaat. Op basis van dit bericht en de snelheid van de lok laat ik de blok NANO het geluid maken. Dat op basis van een roseruis (pink noise) generator. Snelheid en omtrek van de drijfwielen geeft je het tempo van de pufjes. Verder als de lok aan het optrekken is, dan is het geluid ook anders. Ook bij het verminderen van de snelheid (dichte regulator) is het een ander geluid. Kortom het is best wel complex, maar ook een uitdaging en daar houd ik wel van.
Om het een en ander te gaan proberen heb ik 2 NANO's en 2 PAM8403 (a 1euro per stuk) besteld. Ik heb net even een pdf over de PAM8403 gelezen en zie dat ik niet het volume kan regelen, dus misschien moet dat toch nog iets anders worden. Ik ga eerst maar eens proberen of ik uberhaupt het puffend geluid van een stoomlok kan maken met dit spul. Als dat lukt dan is de rest vrij simpel.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 07 January 2020, 13:16:34
Je kan wel 16 servo/leds op het boardje aansluiten + het consumeert geen performance van de arduino  :police:.

Ik heb ooit .WAV bestanden gespeeld van een SD kaart met een uno. Ik gebruikte slechts 1 transistor als versterker. Het geluid werd ook met pwm gedaan. Het volume kon best goed vanuit software geregeld worden.

Later had ik een wekker gemaakt die ook geluid kon afspelen. Deze had ik een digitale potmeter gegeven om volume mee te regelen.

Kan je koploper echt een accessory decoder laten voorzien van informatie van meer dan 1 byte ???. Je kan de nano natuurlijk wel zowel loc commando's als accessory laten uitlezen. Dan weet de nano hoe hard het ding rijd, zoals je al zei. En adhv het adres weet hij ook nog welke loc er rijdt. Moet je dan ook elke nano een eigen adres geven als accesory decoder? Je bent dan ook nog eens afhankelijk van de lengte van de baanvlakken in kwestie en ook de wiel groottes ed. Elke nano moet dan ook nog eens weten hoe lang zijn baanvlak is.

Misschien dat I2C eeproms hier voor handig zijn. Dan kan je 1 malig 10 of 100 EEPROMS branden voor alle nano's die je vervolgens dan op de printjes kan prikken.

Wat ook nog een issue wordt, is dat niet elke trein even hard rijdt op dezelfde stand. Ook dit is iets om over na te denken. Je kan wel alle decoders misschien instellen om even hard te rijden. Anders moet elke arduino weten hoe hard elke trein rijdt op elke stand. Optie 1 lijkt me hiervoor makkelijker.

Hoeveel speakers wil je ongeveer gaan plaatsen? Dit is ook belangrijk voor het maken van een goede keuze.

Bas
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 January 2020, 16:15:25
Nee een accessory decoder bericht bevat slechts de waarde 0 of 1. Maar dat is geen probleem, waar ik geluid wil hebben zijn 10 blokken. En ik kan in totaal 2040 accessory decoder adressen gebruiken. Zeg dat ik 500 daarvan reserveer voor seinen en wissels, dan kan ik nog ruim 1500 gebruiken voor andere zaken. In de locomotief configuratie van Koploper kun je iedere loc voor ieder blok apart een accessory decoder laten activeren. Bij voorbeeld, BR01 gebruikt adressen 1500-1510, BR23 gebruikt 1511-1520 etc. Op het moment dat een lok een blok inrijdt laat ik Koploper een rechtdoor stand genereren en als de lok het blok weer uitrijdt de afbuigende stand. De NANO's hebben geen eigen adres, dat is niet nodig omdat ze via de CanBus alle berichten zien. Het enige dat ik moet doen, is deze NANO's configureren met de blok layout zoals wissels en mogelijke routes, verder bloklengtes en daar de gebruikte accessory adressen aan te koppelen in combinatie met de loc. Voor een deel heb ik dat al gedaan in de SeinController. Mijn locs rijden allemaal met geijkte snelheden, dus de koppeling tussen rijstap en snelheid is ook bekend.
Het is wel een berg aan extra configuratie, maar daar zit ik niet zo mee, tenslotte ben ik niet van plan om constant mijn baanplan te veranderen.

Maar eerst maar eens kijken, dat zal op zijn vroegst de zomer worden, of ik die NANO's het geluid van een stoomlok kan laten produceren.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 28 January 2020, 16:10:53
Wow, dat is al weer even geleden! De beste wensen nog ;D Ik was met onderstaand antwoord al in december begonnen. Ik dacht deze ook al afgemaakt te hebben ::) Maar schijnbaar heeft deze gewoon al die tijd open gestaan :angel: Maar goed om te zien dat jij niet stil gezeten hebt! (y)

Eén interessante vraag heb je onbeandword gelaten:
Pointers kunnen als parameters aan methods door gegeven worden, de physieke objecten niet.
Hoezo niet?  ???
Nogmaals, niet dat je het anders zou moeten doen hoor ;D

Ik dacht al, geen commentaar van Timo  ;)
Joop niet dat je het vervelend vind ::)

Dit begrijp ik niet helemaal ???, of bedoel je het gebruik van volatile? Dat is bij mij standaard voor data die in ISR (Interupt Service Routine) gebruikt worden.
[/quote]
Nee, niet, volatile. Dat is goed voor een ISR. Let wel op dat je niets aan atomic doet maar dat het alleen goed gaat doordat je alleen atomic operaties doet.

Nee, het feit dat je nu een array van 6 x 16 = 96 bytes ofwel 768 bits aan maakt. Terwijl je maar 96 bits = 12 bytes nodig hebt.

Zie de code. De S88 gebruikt 3 signalen, CLOCK, PS en RESET. Ik heb een ISR op de CLOCK en de PS signalen. RESET wordt niet gebruikt.
Ah, ja. Een ISR voor Clock en een voor PS/latch :)

Weet jij wat het verschil tussen C en C++ is? Het niet initialiseren van variabelen in C is een van de grootse valkuilen. Ik kom uit de C wereld, daar leer je op de harde manier dat je variabelen moet initialiseren voor je ze kunt gebruiken. Maar dat zit er bij mij zo ingesleten dat ik dat ook in C++ doe.
Dat je variabelen moet initialiseren klopt natuurlijk, maar globals (en volgens mij alle statische variabelen) worden altijd al geïnitialiseerd naar 0 :) Maar ook
byte eenArray[100] = {0};Zal alle waardes 0 initialiseren. (Niet expliciet genoemde elementen worden 0, de 0 in de initialisatie lijst slaat alleen op element 0.)

Ze moeten gevoed worden vanuit het DCC signaal. Dat beide rails onderbroken moeten worden volgt uit de beschrijving en dat wordt ook bevestigd door mijn ervaring met deze detector.
Doelde er meer op dat het niet de standaard is bij stroomdetectie. Misschien bedoelde je het niet zo maar je beschrijving lijkt te zeggen dat stroomdetectie altijd twee onderbroken spoorstaven nodig heeft. Kan zijn dat de PMP7 wel moet (kan de documentatie niet vinden) maar bij de meeste volstaat één staaf.

Dat doe ik om te voorkomen dat deze routines direct aangeroepen worden. Ik dwing nu dat afgeleide klassen een attach() en heartBeat() method implementeren.
(y) Maar is het dan echt nodig dat iedere child een redefined versie daarvan heeft?

Hangt ervan af wat je flink noemt. 1,25 tot 1,50 maal trager uitlezen van de bus maar wel foutloos. Geen gedoe met 2 x uitlezen, tragere clock of andere pleisters. Geen slecht compromis, althans zo denk ik erover.
Nouwja, als je gewoon alleen de geldigheid van de data checkt weet je wel dat je alleen goede data gebruikt maar niet of je data mist. Vandaar het "niet robuust". Maar zeker eens, liever wat trager en betrouwbaar dan snel en rommel.

[...] Daarom gebruik ik de Canbus voor de echte communicatie en blijft de kabel voor de S88 communicatie zo kort mogelijk en gebruik ik een cat5 kabel (S88-n).
Wat ik een hele slimme en gebruiksvriendelijke manier vind om het op te lossen (y), gewoon transporteren op een andere manier en weer terug bouwen.

Per wissel heb ik een relaispaar nodig.
Waarom heb je geen wisselspanning gepakt? Of loopt hij daar niet zo mooi op? Ook aan een H-brug gedacht? L293D of Mini-L298/MX1508 ofzo.

Dit schrijven gebeurd cyclisch, zodat ik iedere keer een nieuwe locatie gebruik.
Hoe hoe je bij waar in geheugen de laatste geldige stand staat? En neem aan dat je EEPROM.update() gebruikt?

Heb je specifieke Nano's met sd-kaart/geluid/wav optie? Of gewoon standaard ATmega328p Nano's?


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Patrick Smout op 28 January 2020, 19:24:33

Nouwja, als je gewoon alleen de geldigheid van de data checkt weet je wel dat je alleen goede data gebruikt maar niet of je data mist. Vandaar het "niet robuust". Maar zeker eens, liever wat trager en betrouwbaar dan snel en rommel.


In deze toepassing niet echt een probleem dunkt me. Dezelfde info wordt keer op keer om de x ms verzonden. Je moet maar pech hebben om net een puls te missen van enkele ms. Robuust " genoeg" maar onder techneuten is er altijd ruimte voor verbetering  ;) . Overigens kan je dan ook het dcc protocol en bij uitbreiding nog wat andere op de schop gooien. Net hetzelfde "probleem". Belangrijkste is hier dat de dataintegriteit gecontroleerd kan worden. Enkele tellen later komt de volgende boodschap. Een ander protocol dat kan aangeven om de boodschap te herhalen brengt geen soelaas hier als er op de physical layer gestaag iets fout loopt. Het meest zwakke punt nog zijn de controle signalen reset/load/clock. Als die grondig verstoort worden helpt een crc ook niet.

Mvg,  Patrick
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 29 January 2020, 10:54:51
Helemaal met je eens! Fysieke laag blijft hier ook extreem zwak. En denk dat het pas een beetje robuust is als er error correctie in zit. Maar eens dat de kans dat het fout gaat als je in ieder geval een checksum hebt (verkeerde data weg gooit en moet "wachten" op juiste data) met deze snelheden op een baan niet zo groot is.

En de grap is eigenlijk dat je zou kunnen stellen dat DCC een per bit "checksum" heeft doordat elk bitje twee keer in een pakket zit. Helaas doet dat niets tegen het dan niet ontvangen van data dus robuust is dat ook niet ;D


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 29 January 2020, 17:12:05
Helemaal met je eens! Fysieke laag blijft hier ook extreem zwak. En denk dat het pas een beetje robuust is als er error correctie in zit. Maar eens dat de kans dat het fout gaat als je in ieder geval een checksum hebt (verkeerde data weg gooit en moet "wachten" op juiste data) met deze snelheden op een baan niet zo groot is.

En de grap is eigenlijk dat je zou kunnen stellen dat DCC een per bit "checksum" heeft doordat elk bitje twee keer in een pakket zit. Helaas doet dat niets tegen het dan niet ontvangen van data dus robuust is dat ook niet ;D


Timo

Eigenlijk vindt ik dit erg grappig. De hele wereld communiceert tegenwoordig met IP (Internet protocol), Maar dat heeft op de fysieke laag ook geen enkele error protectie, of hoogstens iets heel rudimentair. Pas op de (software) IP laag zit wat error checking, geen recovery, een packet dat niet door de checksum komt wordt gewoon weggegooid. Pas op de TCP (een transport laag) zit een primitieve vorm van recovery. Overigens als een router onderweg problemen heeft, mag hij ook zonder meer packetjes op de grond laten vallen, Het is de verantwoordelijkheid van de twee eindpunten om op TCP nivo maar te proberen de handel te repareren. Toen ik in begin 80er jaren met Ethernet en IP begon te werken, was de mening van de communicatie guru's, dat IP een prut protocol was dat absoluut ongeschikt was voor een bedrijfs zeker netwerk, nee daar had je iso/osi, sna, decnet etc voor nodig. Dat waren tenslotte protocollen met een fatsoenlijke error checking en recovery op alle lagen (fysieke, session en transport laag en eventueel op de applicatie laag). Dat dit voor geen meter presteerde ten opzichte van IP was niet belangrijk.

Groet Meino, die vanuit Tierra del Fuego, keurig met TCP/IP, dit draadje kan volgen.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 29 January 2020, 18:11:13
Doelde er meer op dat het niet de standaard is bij stroomdetectie. Misschien bedoelde je het niet zo maar je beschrijving lijkt te zeggen dat stroomdetectie altijd twee onderbroken spoorstaven nodig heeft. Kan zijn dat de PMP7 wel moet (kan de documentatie niet vinden) maar bij de meeste volstaat één staaf.
Wat ik weet, is dat deze detector een stroomdetector is, maar hij onttrekt ook zijn voeding van het DCC signaal en moet dus verbonden worden met beide railstaven. Het DCC signaal wordt op de detector aangeslote en gaat vanaf de detector naar beide railstaven. Ik heb geen elektronica kennis, maar als je slechts een staaf onderbreekt, dan werkt het niet goed.

(y) Maar is het dan echt nodig dat iedere child een redefined versie daarvan heeft?
Gewoonte, daarmee dwing je iemand die een afgeleide klas maakt, om bewust met deze methods om te gaan.

Waarom heb je geen wisselspanning gepakt? Of loopt hij daar niet zo mooi op? Ook aan een H-brug gedacht? L293D of Mini-L298/MX1508 ofzo.
Wederom, ik bezit geen electronica kennis. De wissels worden aangedreven door verbouwde 9g servos. De electronica is kortgesloten en de voeding is direct op het motortje aangesloten. Ik gebruik een voedingspanning van 3V, dan draaien ze niet te snel. De richting wordt bepaald door de polariteit van de voeding, 2 microswitches met diodes begrenzen de uitslag, en een aparte microswitch voor de puntstuk polarisatie. Het omschakelen doe ik met een dubbelpolige schakelaar. Helaas toen ik een aantal wissels via Koploper en Arduino's wilde aansturen was dat wat lastig, Het makkelijkste was om daar een relais paar voor te gebruiken.

Hoe hoe je bij waar in geheugen de laatste geldige stand staat? En neem aan dat je EEPROM.update() gebruikt?
Ik schrijf per update 3 bytes weg, 2 bytes met de wisselstanden gevolgd door een byte met een uniek bitwaarde die niet kan voorkomen bij de wisselstanden. Er wordt een index bijgehouden, die na iedere update met 2 wordt opgehoogd, voor de volgende update. Als de Arduino start, lees ik de EEPROM op zoek naar die unieke waarde, als die gevonden is, weet ik waar ik gebleven ben en heb ook de laatste 2 bytes met wisselstanden te pakken.

Heb je specifieke Nano's met sd-kaart/geluid/wav optie? Of gewoon standaard ATmega328p Nano's?
Gewoon standaard Nano's. Ik heb het idee om niet met wav files te werken, maar met een pink noise generator. Maar dat is nog allemaal experimenteel. Het kan zijn dat ik het uiteindelijk heel anders ga doen. eerst maar eens wat proberen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 29 January 2020, 19:52:41
Ligt er maar net aan wat je als fysieke laag gebruikt voor TCP/IP ;D Hebben we het over ethernet over CAT dan zou ik willen zeggen dat het wel een vorm van error protectie (niet error correctie) door het differentiaal paar door de selectiviteit te verhogen en common noise te ondervangen. Ofwel, geen zwakke fysieke laag. Maar het gaat dan natuurlijk om de gehele stack, net als hier. Dus ja, het is pas echt robuust met TCP/IP (of een andere vorm met error correctie of ACK ofzo) ;D

Wat betreft de PMP7 kan ik alleen dit (https://platform1mrc.com/p1mrc/index.php?threads/dcc-block-detectors.355/#post-4665) als referentie vinden. Ofwel, ook die heeft maar één onderbreking nodig maar dat moet wel precies in de juiste rail zijn. ;D En net als vele (https://www.ldt-infocenter.com/dokuwiki/doku.php?id=nl:rm-gb-8-n) andere (https://people.zeelandnet.nl/zondervan/bonte%20verzameling.html#02b) is de tweede draad alleen voor de voeding. Kan dus handig zijn te weten omdat het bijvoorbeeld draad kan besparen :)

Ken inderdaad de "servo truc" ook van Klaas. Als je van een mechanische oplossing af wilt (en eventueel ook de snelheid wilt kunnen regelen) zou je dus ook kunnen kijken naar die kleine H-brugjes. Laten zich net zo makkelijk (misschien zelfs makkelijker want geen "spoel storing") aansturen als een relais. Maarja, don't fix it if it ain't broke* ;D

Ik schrijf per update 3 bytes weg, 2 bytes met de wisselstanden gevolgd door een byte met een uniek bitwaarde die niet kan voorkomen bij de wisselstanden.
Maar dan kan je in 2 byte dus maximaal 15 wisselstanden opslaan.

Gewoon standaard Nano's. Ik heb het idee om niet met wav files te werken, maar met een pink noise generator. Maar dat is nog allemaal experimenteel. Het kan zijn dat ik het uiteindelijk heel anders ga doen. eerst maar eens wat proberen.
Ben benieuwd!


Timo

* Vrij vertaald: Ga geen problemen oplossen die er niet zijn
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 29 January 2020, 22:30:40
Ligt er maar net aan wat je als fysieke laag gebruikt voor TCP/IP ;D Hebben we het over ethernet over CAT dan zou ik willen zeggen dat het wel een vorm van error protectie (niet error correctie) door het differentiaal paar door de selectiviteit te verhogen en common noise te ondervangen. Ofwel, geen zwakke fysieke laag. Maar het gaat dan natuurlijk om de gehele stack, net als hier. Dus ja, het is pas echt robuust met TCP/IP (of een andere vorm met error correctie of ACK ofzo) ;D
Ik weet dat op diverse hardware lagen ook checksums gebruikt worden. Maar voor IP is dat niet echt relevant. IP is zo gemaakt dat het gebruik kan maken van de meest primitieve hardware laag. Sommigen beschouwen de IP laag als een pseudo hardware laag, waar de echte checksum controle plaats vindt.

Maar dan kan je in 2 byte dus maximaal 15 wisselstanden opslaan.
Ik heb nu 12 wissels die via DCC en Koploper worden aangestuurd, met de 2 bytes kan ik maximaal 14 wisselstanden opslaan, 7 bits per byte. Worden dat meer wissels, dan schrijf ik gewoon meer bytes weg, Voor 21 wissels heb ik 3 data bytes nodig, etc.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 29 January 2020, 22:48:58
Ken inderdaad de "servo truc" ook van Klaas.
Ik gebruik inderdaad ook "gecastreerde" servo's. Maar ik bedien ze met halvegolfsturing. Geen gedoe met H-bruggen of PWM, maar gewoon een paar diodes die de stroom het goede pad wijzen. Dat heeft dan weer als bijkomstigheid dat ik over dezelfde draad ook de terugmelding krijg. Staat allemaal uitgelegd in mijn bouwdraadje over Without.

Wat ik weer bijzonder vind in deze discussie dat Meino kennelijk heel diep in de software en de overdrachtsprotocollen zit, maar van redelijk simpele elektronica geen kaas heeft gegeten. Bij mij is dat precies andersom, ik volg dit draadje nog steeds, maar de helft snap ik geen bal van.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Martin Hornis op 30 January 2020, 00:45:02
Dat soort vreemde zaken kwam ik 45 jaar geleden ook tegen. De elektronica-leraren op een HTS konden mij geen raadgeving geven met betrekking tot een impedantie-'overdracht'. Een energietechniek-leraar zei dat een emittervolger een standaardoplossing voor dat probleem is. Na 45 jaar gebruik ik dat treindetectie-systeem nog steeds op mijn modelbaan.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 30 January 2020, 02:11:09
Wat ik weer bijzonder vind in deze discussie dat Meino kennelijk heel diep in de software en de overdrachtsprotocollen zit, maar van redelijk simpele elektronica geen kaas heeft gegeten. Bij mij is dat precies andersom, ik volg dit draadje nog steeds, maar de helft snap ik geen bal van.
Ik denk dat dat redelijk klopt, mijn electronica kennis is gebaseerd op zaken die ik lang,lang geleden op de HBS heb geleerd, zaken als de wet van Ohm. Daar red ik me aardig mee, maar ik ga niet proberen om zelf iets te bedenken. Simpele zaken als voorloop weerstanden voor leds berekenen dat lukt wel. Verder heb ik in mijn werk best veel met Hardware engineers samengewerkt en dan pik je ook nog wel eens wat op. Maar hoe een zg H brug werkt? geen idee. Maar dat spreekt me ook zo aan de Arduino's aan, want daarmee kun je het meeste in programmatuur oplossen in combinatie met standaard interface kaartjes.
Overigens de truck met halve golf aansturing met diodes begrijp en ken ik ook. Maar ik had in mijn verzameling stekkervoedingen een 3V dc voeding liggen, en in kombinatie met mini schakelaars kon ik dat ook goed werkend krijgen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 30 January 2020, 12:59:41
Overigens de truck met halve golf aansturing met diodes begrijp en ken ik ook. Maar ik had in mijn verzameling stekkervoedingen een 3V dc voeding liggen, en in kombinatie met mini schakelaars kon ik dat ook goed werkend krijgen.
Het enige nadeel dat ik zie bij halvegolfsturing is dat je daar een gewone trafo voor nodig hebt. Is duurder en zwaarder dan een schakelende voeding. Voordeel voor mij is dan weer dat ik nog een hele bak met trafo's heb liggen en voor die servo's kun je met een vrij kleine trafo volstaan.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Martin Hornis op 31 January 2020, 03:07:29
Maar hoe een zg H brug werkt? geen idee.
Het horizontale deel van de 'H' is een motor. De 4 verticale delen van de 'H' zijn elektronische schakelaars. Beide uiteinden aan de bovenkant van de 'H' zijn met de PLUS verbonden. Beide onderkanten van de 'H' met de MIN. Als de linker bovenpoot en de rechter onderpoot van de 'H' in geleiding zijn dan gaat de stroom van linksboven naar rechtsonder. Door de motor dus naar rechts. Als je de rechter bovenpoot en de linker onderpoot in geleiding brengt dan gaat de stroom door de motor naar links. Het is de kunst om er voor te zorgen dat de boven- en onderpoot aan de linkerkant niet tegelijkertijd in geleiding zijn, want dan heb je een kortsluiting. Idem aan de rechterkant van de 'H'.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 31 January 2020, 09:53:00
Verlicht mij eventjes wat is halve golf aan sturing precies?

 Ik moet nu opeens aan zo'n racebaan denken waarbij een positieve spanning werd gebruikt voor 1 auto en de negatieve spanning voor de andere...maar nu heb ik het over iets anders dan jullie denk ik ???
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Timo op 31 January 2020, 09:58:37
Maar hoe een zg H brug werkt? geen idee. [...] in combinatie met standaard interface kaartjes.
Gewoon behandelen als standaard interface kaartje ;D Je voeding eraan, motor aan de uitgang en dan heb je een ingang voor linksom en een ingang voor rechtsom. Volgens mij gewoon een drop in replacement voor hoe je nu de relais gebruikt. Daar vorm je nu ook een mechanische H-brug mee ;D Dus het principe snap je gewoon al ;D

Zou alleen kunnen dat ze niet heel blij worden van 3V ingangsspanning en 5V logic. Maar niets wat een weerstand niet op kan lossen :police:

Verlicht mij eventjes wat is halve golf aan sturing precies?

 Ik moet nu opeens aan zo'n racebaan denken waarbij een positieve spanning werd gebruikt voor 1 auto en de negatieve spanning voor de andere...maar nu heb ik het over iets anders dan jullie denk ik ???
Nee, eigenlijk niet. Gewoon AC door een enkele diode. Al naar gelang de oriëntatie van de diode heb je dan positieve "halve golf" of negatieve "halve golf". Het voordeel is dat je maar één zijde van de motor hoeft te schakelen.


Timo
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 31 January 2020, 12:00:55
Mijn toepassing van de halvegolfsturing: Without, vanaf reactie#98 (https://forum.beneluxspoor.net/index.php?topic=66995.90)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 31 January 2020, 14:41:05
@Martin en @timo

bedankt voor de uitleg van een H brug. Zo leer je iedere keer wat bij. Het lijkt inderdaad erg op mijn relais oplossing.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 05 May 2020, 23:40:17
De afgelopen weken ben ik bezig geweest met het bouwen van een AKI (Automatische Knipperlicht Installatie). Afgezien van het modelbouw gedeelte, is het meeste werk gaan zitten in het bouwen van de C++ objecten om het gedrag van de AKI d.m.v. een Arduino aan te sturen.

Even een filmpje van het resultaat:
https://www.youtube.com/v/15PcaT44G3o

Laten we even kijken naar waar het om draait.
(https://images.beneluxspoor.net/bnls/AKI-secties.jpg) (https://images.beneluxspoor.net/bnls/AKI-secties.jpg)

Dit schema toont de belangrijkste Componenten (C++ objecten) die in dit systeem te onderscheiden zijn.

Ik heb bewust gekozen om in de AKI geen kennis van de secties te geven. Dit omdat de AKI een stabiel object is, met een goed omschreven gedrag, terwijl de sectie objecten dat niet zijn, er zijn veel verschillende manier waarmee bepaald kan worden of een spoordeel bezet is. Dus de terugkoppeling van een of meerdere secties naar het licht- en geluidsbeeld van de AKI vindt door een externe partij (de loop() method) plaats.

OK to zover, morgen verder.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 May 2020, 16:03:42
We gaan verder.

De aansturing van de AKI is, afgezien van een paar technische probleempjes, redelijk simpel. Het is gewoon een kwestie van een paar ledjes aansturen en het geluid van een bel produceren. Verder is de functionaliteit stabiel, de overweg is open of is gesloten en beide toestanden kennen een wel omschreven functionaliteit.
De overweg (AKI, AHOB, etc) heeft twee toestanden, open of gesloten voor het kruisend verkeer. Deze toestand wordt bepaald door de aanwezigheid of afwezigheid van treinen in alle secties(spoordelen) van de overweg. De overweg gaat dan open als alle secties aangeven dat er geen trein aanwezig is anders is de overweg gesloten.

Het controleren van een sectie op de aanwezigheid van een trein is eigenlijk het meest complexe deel, daarom wil ik het daar eerst over hebben.
De technische kant is simpel, alleen de logica is wat ingewikkelder. Dat komt omdat er verschillende manieren zijn om het wel of niet aanwezig zijn van een trein te bepalen. Er zijn diverse omstandigheden die van invloed zijn, bijv. kan de hele trein (loc+wagons) gedetecteerd worden of alleen de loc, werken we met stroomdetectie of met lichtsluisjes, etc. Verder worden iedere sectie slechts in een richting bereden of kunnen een of meerdere secties in beide richtingen bereden worden., kunnen er rangeerbewegingen plaatsvinden enz.
Ik gebruik zelf stroomdetectie en lichtsluisjes, verder werk ik alleen met gehele detectie (alle wagons hebben verlichting, of assen met weerstandslak) en de beschreven AKI beveiligd een dubbelspoor waarbij ieder spoor slechts in een richting bereden kan worden. Dat zijn dus de randvoorwaarden van wat ik hier beschrijf.

Het simpelst is als de gehele sectie (die de overweg) aanstuurt in zijn geheel op 1 stroomdetecterende bezetmelder aangesloten is. In dat geval is het makkelijk, deze bezetmelder geeft dan aan of er een trein in de sectie is of niet. Echter dat is niet altijd mogelijk. In mijn geval had ik te maken met al aanwezige scheidingen in blokken, wissels en bestaande bezetmelders die dit helaas onmogelijk maakt.

SectionControl

(https://images.beneluxspoor.net/bnls/AKI-secties2.jpg) (https://images.beneluxspoor.net/bnls/AKI-secties2.jpg)
 
Iedere sectie wordt gecontroleerd door een SectionControl object. Dit object heeft contact met twee Sensor objecten, een entrySensor en een exitSensor, welke de toestand van een bij behorende bezetmelder aangeven. In mijn systeem hebben de Sensor objecten geen contact met een fysieke bezetmelder, zij ontvangen hun toestand via Canbus boodschappen. Het is echter maareen kleine wijziging om dezeobjecten  een fysieke pin op de Arduino te laten monitoren en hun toestand daar van te laten afhangen waardoor dit buiten een Canbus omgeving ook werkt.

De toestand van dit SectionControl object doorloopt 3 toestanden. Initieel is de toestand leeg. Op het moment dat een trein de entrySensor bereikt en die aanslaat, zal de toestand van leeg naar sensor 1 komend gaan. Daarna bij het bereiken van de exitSensor gaat de toestand naar senso 2 gaand. Op het moment dat de trein de exitSensor volledig heeft verlaten en deze weer afgaat, wordt de toestand opnieuw leeg. Alleen in de toestand leeg, zal het SectionControl object melden dat er geen trein in de sectie is.
Ik weet dat dit niet volmaakt is, want er zijn een aantal scenario's die niet goed worden afgehandeld, zoals bijv. rangeerbewegingen, een trein rijdt de sectie in, stopt en rijdt terug zonder de exitSensor te bereiken. Maar voorlopig voldoet het voor mij.

OK, een paar stukjes code. Dit zijn de .h en .cpp bestanden die je in een Arduino bibliotheek kunt gebruiken.
SectionControl.h
//
//  A class that encapsulated sensors and detectors used for controlling
//  a rail section. This specific sensor receives it's state updates through
//  Canbus messages
//
class Sensor
{
private:
    int sensorAddress;  // The address/number of this sensor, derived from
                        // position on a S88 bank.
    volatile bool state;

public:
    Sensor(int aSensorAddress);

    void attach();

    int getSensorAddress();

    void heartBeat();

    void setState(bool aState);
    bool getState();
};

//
//  A finite state machine that guards a occupance of a single track
//
class SectionControl
{
public:
    enum SectionState
    {
        leeg,
        sensor1komend,
        sensor2komend,
        sensor1gaand,
        sensor2gaand
    };

    //
    // Create an instance with two sensors
    //
    SectionControl(Sensor *anEntrySensor, Sensor *anExitSensor);

    //
    //  Attach pins
    //
    void attach();

    //
    //  Get give the sensors a kick and check their state so we can update our own
    //  state.
    //
    void heartBeat();

    //
    // Is the section occupied by a train?
    //
    bool isOccupied();

    void setEntrySensor(Sensor *aSensor);
    Sensor *getEntrySensor();
    void setExitSensor(Sensor *aSensor);
    Sensor *getExitSensor();

private:
    SectionState state = leeg;

    Sensor *entrySensor = nullptr;
    Sensor *exitSensor = nullptr;
   
    SectionControl::SectionState getState();

    //
    //  Based on the state of sensor 1 and the current state of the FSM, a new state
    //  is calculated.
    //
    void handleEntrySensor(bool trigger);

    //
    //  Based on the state of sensor 2 and the current state of the FSM, a new state
    //  is calculated.
    //
    void handleExitSensor(bool trigger);
};

SectionControl.cpp
Sensor::Sensor(int aSensorAddress)
{
    sensorAddress = aSensorAddress;
    state = false;
}

void Sensor::attach()
{

}

int Sensor::getSensorAddress()
{
    return sensorAddress;
}

void Sensor::heartBeat()
{

}

void Sensor::setState(bool aState)
{
    state = aState;
}

bool Sensor::getState()
{
    return state;
}

//
//  Based on the state of entry sensor and the current state of the FSM, a new state
//  is calculated.
//

void SectionControl::handleEntrySensor(bool trigger)
{
    if (trigger) // Sensor 1 is ON
    {
        if (state == leeg) // If the state is "leeg" it means that a train enters the section
        {
#if (RRX_DEBUG)
            Serial.print("[SectionControl::handleEntrySensor] ON, nr: ");
            Serial.print(sectionNr);
            Serial.println(" state is sensor1komend");
            Serial.flush();
#endif
            state = sensor1komend; // so set it to state komend
        }
    } else // Sensor is OFF
    {
#if (RRX_DEBUG)
        Serial.print("[SectionControl::handleEntrySensor] OFF, nr: ");
        Serial.print(sectionNr);
        Serial.println(" ignored");
        Serial.flush();
#endif
    }
}

//
//  Based on the state of the exit sensor and the current state of the FSM, a new state
//  is calculated.
//

void SectionControl::handleExitSensor(bool trigger)
{
    if (trigger) // Sensor 2 is ON
    {
        if (state == sensor1komend) // A train has entered the section
        {
#if (RRX_DEBUG)
            Serial.print("[SectionControl::handleExitSensor] ON, nr: ");
            Serial.print(sectionNr);
            Serial.println(" state is sensor2gaand");
            Serial.flush();
#endif
            state = sensor2gaand; // Start the leaving process
        }
    } else
    {
        if (state == sensor2gaand) // A train is in the process of leaving
        {
#if (RRX_DEBUG)
            Serial.print("[SectionControl::handleExitSensor] OFF, nr: ");
            Serial.print(sectionNr);
            Serial.println("state is leeg");
            Serial.flush();
#endif
            state = leeg; // Section is empty again
        }
#if (RRX_DEBUG)
        else
        {
            Serial.print("[SectionControl::handleExitSensor] OFF, nr: ");
            Serial.print(sectionNr);
            Serial.println(" ignored");
            Serial.flush();
        }
#endif
    }
}

//
// Create an instance with two sensors
//

SectionControl::SectionControl(Sensor *anEntrySensor, Sensor *anExitSensor)
{
    entrySensor = anEntrySensor;
    exitSensor = anExitSensor;

    state = leeg; // initial state
}

//
//  Initialise and setup
//

void SectionControl::attach()
{

}

//
//  Get give the sensors a kick and check their state so we can update our own
//  state.
//

void SectionControl::heartBeat()
{
    if ((entrySensor == nullptr) || (exitSensor == nullptr))
    {
        state = leeg;
    } else
    {
        entrySensor->heartBeat();
        exitSensor->heartBeat();

#if (RRX_DEBUG)
        Serial.print("[SectionControl::heartBeat] entry sensor address: ");
        Serial.print(entrySensor->getSensorAddress());
        Serial.print(", state: ");
        Serial.println(entrySensor->getState());
#endif     
        handleEntrySensor(entrySensor->getState());

#if (RRX_DEBUG)
        Serial.print("[SectionControl::heartBeat] exit sensor address: ");
        Serial.print(exitSensor->getSensorAddress());
        Serial.print(", state: ");
        Serial.println(exitSensor->getState());
        Serial.flush();
#endif 
        handleExitSensor(exitSensor->getState());
    }
}

//
//  Ask if the section is occupied by a train. only if it's state equals
//  'leeg' we may deny it, all other states tells us that a train is active
//  in the section.
//
bool SectionControl::isOccupied()
{
    if (state == leeg)
    {
        return false;
    }
    else
    {
        return true;
    }
}

//
// Return the current state of the section
//
SectionControl::SectionState SectionControl::getState()
{
    return state;
}

void SectionControl::setEntrySensor(Sensor *aSensor)
{
    entrySensor = aSensor;
}

Sensor *SectionControl::getEntrySensor()
{
    return entrySensor;
}

void SectionControl::setExitSensor(Sensor *aSensor)
{
    exitSensor = aSensor;
}

Sensor *SectionControl::getExitSensor()
{
    return exitSensor;
}

Wat voorbeelden van het gebruik hiervan in de arduino schets.

Definities:
const int nrOfSensors = 4;
Sensor sensors[nrOfSensors] = {
  Sensor(12),   // 1.13   SectionControl 2 exit
  Sensor( 9),   // 1,10   SectionControl 1 entry
  Sensor(16),   // 2.01   SectionControl 2 entry
  Sensor(22)    // 2.07   SectionControl 1 exit
};

SectionControl section1(&sensors[1], &sensors[3]);
SectionControl section2(&sensors[2], &sensors[0]);

setup()
    //
    // Initialize the sensors
    //
    for (int i=0; i<nrOfSensors; i++)
    {
      sensors[i].attach();
    }

    section1.attach();
    section2.attach();

loop()
    //
    //  Keep the sections running, so they will update their states
    //
    section1.heartBeat();
    section2.heartBeat();

Tot zover over de spoorsecties.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 06 May 2020, 17:08:10
Je hebt qua detecties ongeveer hetzelfde gedaan als ik voor de AHOB. Ik had alleen mijn sporen bi-directioneel gemaakt. En zodoende had ik 5 states. Er zijn per spoor 2 melders die beiden als entry en exit sensors kunnen fungeren. Afhankelijk van welke sensor er als eerst gemaakt werd, werd er gewacht tot de andere sensor gemaakt was en dan werd er gewacht totdat deze sensor een x aantal tijd verbroken is geweest. En dan pas werd het de bezetmelding opgeheven

Ik leerde daarna dat in het grootbedrijf dat er ook een midden detectie gedeelte is, wist ik veel.....  maar goed, ik bedacht me dat mijn SW ook werkt als je de 2 inrij detecties parallel aansluit en als entrySensor inzet en de midden sectie als exitSensor gebruikt. Voor de exit sensor is het raadzaam om stroomdetectie te gebruiken mits je hele trein dan gedetecteerd wordt zoals je zei.
 
entry                       exit                     entry
------   ---------------------------------   ------

Ik denk dat jij hetzelfde kan doen voor jouw SW. Je kan de 2 buitenste terugmelders als entrySensor gebruiken en die parallel aansluiten. Dan moet je alleen nog melders aanbrengen voor het middengedeelte. In deze opstelling moet je er wel op letten dat de trein nog wat extra tijd heeft, om de entrySensor voorbij te gaan, wanneer hij weer weg rijdt. Dit zou dan ook met rangeerbewegingen moeten werken. Maar nogmaals als je nu net een lok parkeert op de entry sensor en er dan van af rijdt zonder de exit te raken, dan staat je overgang dus permanent dicht.

Dit allemaal gezegd hebbende. Volgens mij is wat jij doe, wat ik doe en het grootbedrijf doet allemaal onnodig moeilijk doen. Ik denk als je gewoon 1 enkele bezetmelder gebruikt voor de gehele lengte dus van je huidige entrySensor t/m je huidige exitSensor dat je dan al een perfect werkend en feilloos systeem heb. In een taal die we allemaal snappen:

 if ( Sensor == waar ) { overgang dicht }
 else                          { overgang open }

Mvg

Bas
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 May 2020, 18:55:04
Dag bas
Je hebt qua detecties ongeveer hetzelfde gedaan als ik voor de AHOB. Ik had alleen mijn sporen bi-directioneel gemaakt. En zodoende had ik 5 states.
Ik heb voort geborduurt op een stukje code dat ik ooit voor Koen (NS264) had ontwikkeld. Dat was voor een enkel spoor dat van 2 kanten bereden kon worden. In dat systeem heb ik inderdaad 5 toestanden. Maar voor mijn situatie had ik dat niet nodig, dus heb ik het wat versimpeld.

Dit allemaal gezegd hebbende. Volgens mij is wat jij doe, wat ik doe en het grootbedrijf doet allemaal onnodig moeilijk doen. Ik denk als je gewoon 1 enkele bezetmelder gebruikt voor de gehele lengte dus van je huidige entrySensor t/m je huidige exitSensor dat je dan al een perfect werkend en feilloos systeem heb.
Dat klopt, dat geef ik ook aan in mijn verhaaltje, alleen had ik niet een stuk spoor van de goede afmetingen, de sectie is onderbroken door een aantal isolators met meerdere bezetmelders, verder zit er ook een wissel in zodat ik dynamisch de entrysensor moet aanpassen afhankelijk van de wisselstand. Dat betreffende wissel heeft ook een bezetmelder, maar die kan ik niet gebruiken omdat er ook routes over dat wissel gaan die geen invloed op de sectie mogen hebben.

Ik weet dat rangeren over deze sectie problemen gaat geven. Hoe ik dat ga oplossen heb ik voorlopig nog maar even geparkeerd.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 06 May 2020, 23:16:07
Goed we gaan verder met de software die de AKI zelf aanstuurt.

Het systeem dat de AKI aanstuurt kent een aantal objecten

AKI
De AKI zelf kent twee toestanden.
Het AKI object heeft methods om de AKI in een van deze toestanden te brengen.

AKI.h
//
//  A simple class that represents a complete AKI (Automatische Knipperlicht Installatie)
//  with all its leds and the sound of a bell
//
class AKI
{
public:

    enum CrossingState
    {
        OPEN, CLOSED
    };

    //
    //  Create an instance of the AKI, using the specified Led's
    //
    AKI(BlinkingLed *aRedLed, BlinkingLed *aGreenLight, int aBellPin);

    //
    //  Attach pins
    //
    void attach();

    //
    //  Activate the connected leds. TBD: the sound
    //
    void heartBeat();
   
    //
    //  Close the crossing
    //
    void close();
   
    //
    //  Open the crossing
    //
    void open();

private:
    BlinkingLed *redLight;
    BlinkingLed *greenLight;
    int bellPin;
   
    CrossingState state;
   
    void setCrossingState(CrossingState aState);
    CrossingState getCrossingState();
};

AKI.cpp
//
//  Create an instance of the AKI, using the specified Led's
//
AKI::AKI(BlinkingLed *aRedLight, BlinkingLed *aGreenLight, int aBellPin)
{
    redLight = aRedLight;
    greenLight = aGreenLight;
    bellPin = aBellPin;
}

//
//  Attach pins
//
void AKI::attach()
{
    redLight->attachPin();
    greenLight->attachPin();

    pinMode(bellPin, OUTPUT);
    digitalWrite(bellPin, LOW);
   
    setCrossingState(OPEN);
}

//
//  Activate the connected leds.
//
void AKI::heartBeat()
{
    //
    //  The leds have a flicker, so we have to keep them running
    //
    redLight->heartBeat();
    greenLight->heartBeat();
}

void AKI::setCrossingState(AKI::CrossingState aState)
{
    state = aState;

    if (state == OPEN)
    {
        redLight->setLedOn(false);
        greenLight->setLedOn(true);
        digitalWrite(bellPin, LOW); // Bell is off
    } else
    {
        redLight->setLedOn(true);
        greenLight->setLedOn(false);
        digitalWrite(bellPin, HIGH); // Bell is on
    }
}

AKI::CrossingState AKI::getCrossingState()
{
    return state;
}

void AKI::open()
{
    setCrossingState(AKI::OPEN);
}

void AKI::close()
{
    setCrossingState(AKI::CLOSED);
}

BlinkingLed
Dit object is verantwoordelijk voor de aansturing van de  rode of een groene led. Dit zijn simpele eenkleurige leds. Daarom worden ze simpel door een digitale pin aangestuurd zonder dat PWM nodig is. Omdat de lichtintensiteit van rode en groene leds onderling sterk kan afwijken moet met verschillende voorloop weerstanden de onderlinge lichtintensiteit in balans gebracht worden. Als een led is aangezet dan zorgt het object zelf dat de led met het geconfigureerde tempo aan en uit gaat. De AKI heeft dus twee van deze objecten, een voor het rode licht en een voor het groene licht.

BlinkingLed.h
class   BlinkingLed
{
private:
    int   blPin;
    short blinkInterval;
    bool  blinking;
    bool  ledOn;
    long  lastTime;
   
public:
    BlinkingLed(int, int);
    void setBlinkInterval(int);
    int  getBlinkInterval();
    void setLedOn(bool);
    bool getLedOn();
    void attachPin();
    void heartBeat();
};

BlinkingLed.cpp
BlinkingLed::BlinkingLed(int aPin, int aBlinkInterval)
{
    blPin = aPin;
    blinking = false;
    ledOn = false;
    blinkInterval = aBlinkInterval;
}

void BlinkingLed::setBlinkInterval(int aBlinkInterval)
{
    blinkInterval = aBlinkInterval;
}

int  BlinkingLed::getBlinkInterval()
{
    return blinkInterval;
}
   
void BlinkingLed::setLedOn(bool aLedOn)
{
    if (ledOn != aLedOn)
    {
        ledOn = aLedOn;
        if (!ledOn)
        {
            digitalWrite(blPin, LOW);
        }
    }
}
   
bool BlinkingLed::getLedOn()
{
    return ledOn; 
}
   
void BlinkingLed::attachPin()
{
    pinMode(blPin, OUTPUT);
    delay(100);
    lastTime = millis();
}
   
void BlinkingLed::heartBeat()
{
    if (ledOn)
    {
        unsigned long currTime = millis();
        if ((currTime - lastTime) >= blinkInterval)
        {
            lastTime = currTime;
            if (blinking)
            {
                digitalWrite(blPin, HIGH);
                blinking = false;
            }
            else
            {
                digitalWrite(blPin, LOW);
                blinking = true;
            }
        }
    }
}

Bel
Dit is het systeem dat het geluid van de bel maakt. Dit is geen klassiek object, maar een volledig apart systeem van een Arduino Namo met een speaker en RC schakeling. Deze Nano is via een digitale pin verbonden met de Arduino waar de AKI software op draait. Als deze pin hoog gezet wordt door de AKI software dan zal dit systeem de bel gaan luiden. Omdat dit een apart systeempje is, zal ik daar in de volgende post verder op in gaan.

Wat voorbeelden van het gebruik.

Definities:
AKI aki(new BlinkingLed(23,333), new BlinkingLed(22,667), 24); // Red blinks 90/minute, green 45/minute

setup()
aki.attach();

loop()
    //
    //  Update the state of the AKI based on the stateS of the sections
    //
    if ((section1.isOccupied()) || (section2.isOccupied()))
    {
      aki.close();
    }
    else
    {
      aki.open();
    }

    //
    //  Keep the AKI running
    //
    aki.heartBeat();

Tot zover de AKI software.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Martin Hornis op 07 May 2020, 01:33:48
Mooie toepassing. Maar het duurt wel erg lang voordat de overweg weer 'vrij' is.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 07 May 2020, 09:36:40
[Kritiek mode]
Ik weet dat deze SW past in je OO stijl manier van programmeren maar....

Je doet wel erg veel moeite en werk om een led te laten knipperen zeg  ;D en dan nog het geheugen wat je er aan toewijdt. Een hele classe om een led te laten knipperen...
private:
    int   blPin;
    short blinkInterval;
    bool  blinking;
    bool  ledOn;
    long  lastTime;
De long lastTime (volgens mij moet deze unsigned zijn, kwenie of dit problemen geeft na 25 dagen ::) nog-stop gebruik) mag hier ook een unsigned int zijn. Mits deze:
void BlinkingLed::heartBeat()
{
    if (ledOn)
    {
        unsigned long currTime = millis();

        if ((currTime - lastTime) >= blinkInterval)
dat ook wordt. Dan worden alleen de 2 LSB opgeslagen van millis() en omdat je interval ook maar een 'short' (moest nog opzoeken wat het was) is, zal dat geen problemen geven in de if-statement.

je int blPin mag hier ook een byte (aka unsigned char) zijn. Het is in dit programma al geen constante en zal daarom geheugen gebruiken. En om dan ook nog eens een signed integer te gebruiken voor een getal dat nooit hoger zal zijn dan het max aantal pinnen op een arduino mega... Wat ik eigenlijk niet weet, is of je in een constructor een const int 1 malig van een waarde kan voorzien.

Ik vind bool's  persoonlijk ondingen. Ik had er een keer ruzie mee toen ik een state wilde toevoegen en het getal 2 er in wilde stoppen. Een bool neemt ook 8 bits in gebruik terwijl je er maar 1 van nodig heb. Je hebt 2 bits nodig en je gebruikt er 16. Je kan dit geval beter gebruik maken van een bitfield (https://www.tutorialspoint.com/cprogramming/c_bit_fields.htm)
struct {
   unsigned int blinking: 1;
   unsigned int ledOn: 1;
} status;
Dit bespaart je 8 bits aan geheugen. De struct heeft nog steeds 1 byte nodig alleen beide 'bool's staan nu in dezelfde bytes.
Door de 'bare mininum' variabele types te gebruiken, kan je hier al 4 bytes per led sparen.

Deze OO manier maakt alleen het gebruik van constantes niet mogelijk. Ze verschillen per object en dat maakt het lastig. Daarom zou ik voor de leds zelf niet kiezen voor een classe. Maar zoals ik altijd zeg, what ever grinds your gears (doe wat jou goeddunkt) (y). Hij doet het iig.

Je hebt natuurlijk ook nog de standaard 'getters' (de get functies) die je eigenlijk niet gebruikt. Ik weet nog van Java les dat je die eerder tikt uit gewoonte dan uit noodzaak. Ik weet niet of de functies daadwerkelijk worden meegecompileerd op dat ze door de compiler worden weggeoptimaliseerd (schitterend woord)

Wat ik standaard ook graag als advies geef, is om ten alle tijden de intX_t en de uintX_t notaties te gebruiken vanwege de duidelijkheid er van. Ik moest nu bijvoorbeeld opzoeken wat een short ook alweer was en een 'int' kan per platform verschillen. En iedereen kan in het eerste oog opslag zien, hoeveel bits er gebruikt worden en of de waarde signed of unsigned is als je dus de uintX_t en de intX_t gebruikt.
[/kritiek mode]

Goed bezig iig (y) Kun je misschien een foto maken van het geheel zodat we ook de wissels en bezetmelders ed op 1 plaatje hebben?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: ikbenerevenniet op 07 May 2020, 11:43:17
bool's [..] ruzie mee toen ik [..] het getal 2 er in wilde stoppen

Tja... ;-p
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 May 2020, 17:05:23
Mooie toepassing. Maar het duurt wel erg lang voordat de overweg weer 'vrij' is.

Dat ben ik met je eens. Maar ik maak nu gebruik van bestaande bezetmelders en die zitten niet allemaal op een gunstige plek. Ik ben nog van plan om twee nieuwe te gaan installeren.

[Kritiek mode]
Ik weet dat deze SW past in je OO stijl manier van programmeren maar....

Je doet wel erg veel moeite en werk om een led te laten knipperen zeg  ;D en dan nog het geheugen wat je er aan toewijdt. Een hele classe om een led te laten knipperen...
....
....
Ik vind bool's  persoonlijk ondingen. Ik had er een keer ruzie mee toen ik een state wilde toevoegen en het getal 2 er in wilde stoppen. Een bool neemt ook 8 bits in gebruik terwijl je er maar 1 van nodig heb. Je hebt 2 bits nodig en je gebruikt er 16.
[preekmode=ON,  ;D]
Dat je een bool een onding vindt begrijp ik niet echt, dat soort zaken zou toch bij de minimale kennis moeten  behoren  :police:

Maar ik vind de overzichtelijkheid en helderheid van de code belangrijker dan het besparen van 8 bits. Als je op dat nivo met de code bezig moet zijn dan denk ik dat je beter een ander platform voor je systeem, met meer resources, moet vinden. Ik zelf denk dat als je alles uit de kast trekt met speciale technieken om geheugen te besparen, dat je dan eindigt met een onderhoudsnachtmerrie.
[/preekmode]

Misschien kom ik wat hard over, maar dat ik dit vindt heeft alles te maken met de jarenlange ploetering om het "werk" van anderen aan de praat te houden  ;).

Ik weet niet of je ooit van de volgende termen gehoord hebt:


Dit zijn de drie hoofdzaken waarmee je een goed stuk software kunt onderscheiden van rommel software.

Dat is ook de reden dat ik OO gebruik omdat dat je de mogelijkheid biedt tot het concentreren van zaken in kleine compacte stukjes code, waarbij de functionaliteit en verantwoordelijkheid belegd wordt in kleine en compacte stukjes code.

Deze OO manier maakt alleen het gebruik van constantes niet mogelijk. Ze verschillen per object en dat maakt het lastig. Daarom zou ik voor de leds zelf niet kiezen voor een classe. Maar zoals ik altijd zeg, what ever grinds your gears (doe wat jou goeddunkt) (y). Hij doet het iig.
Huh, wat dacht je van enums, static of const. Dit zie ik niet helemaal. sorry.

Je hebt natuurlijk ook nog de standaard 'getters' (de get functies) die je eigenlijk niet gebruikt. Ik weet nog van Java les dat je die eerder tikt uit gewoonte dan uit noodzaak. Ik weet niet of de functies daadwerkelijk worden meegecompileerd op dat ze door de compiler worden weggeoptimaliseerd (schitterend woord)
Dat is nog een overblijfsel van jaren praktijk. En komt voort uit het topic van "Data Hiding". Laat nooit de interne data van een klasse direct toonbaar en modificeerbaar zijn door de buitenwereld.  Het kan zijn dat ze nu niet gebruikt worden, maar dat kan later anders zijn.

Wat ik standaard ook graag als advies geef, is om ten alle tijden de intX_t en de uintX_t notaties te gebruiken vanwege de duidelijkheid er van. Ik moest nu bijvoorbeeld opzoeken wat een short ook alweer was en een 'int' kan per platform verschillen. En iedereen kan in het eerste oog opslag zien, hoeveel bits er gebruikt worden en of de waarde signed of unsigned is als je dus de uintX_t en de intX_t gebruikt.
Het probleem is dat types als uintX-t en intX-t geen standaard types van C, C++ of Java zijn en dat je dus afhankelijk bent van de specifieke include of import bestanden die op je platform aanwezig zijn (bijv. arduino.h). Dus als in een specifiek omgeving deze niet bekend zijn, maar er andere definities geincluded worden heb je een probleem. De datatypes die jij noemt ben ik zelf nooit tegengekomen, wel heb ik vaak de typen int8_t, uint8_t, int16_t, uint16_t etc, gebruikt zien worden, maar ook die komen uit een include bestand (stdint) en zou ik zelf dus eerder gebruiken. Het probleem zit hem eigenlijk alleen in het gebruik van int, omdat dat platform afhankelijk kan zijn. Ik zelf heb eigenlijk alleen te maken gehad met systemen waarop de int 32 bit was, dus daar ben ik soms slordig in. Wil je veilig programmeren dan gebruik je byte (unsigned 8bit), short (signed 16bit), unsigned short, long (signed 32bit), unsigned long, long long (signed 64bit) of unsigned long long en de diverse floats. Dat is wat ik zelf prefereer, maar dat komt omdat ik lang geleden C geleerd heb en de laatste 25 jr hoofdzakelijk met Java bezig geweest ben waar dit allemaal niet speelt  ;D.

Goed tot zover.
Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 May 2020, 20:05:28
Op verzoek even een overzicht fotootje.

(https://images.beneluxspoor.net/bnls/AKI_secties.jpg) (https://images.beneluxspoor.net/bnls/AKI_secties.jpg)

Alle wissels die hier te zien zijn geen onderdeel van de blokken zoals die in Koploper zijn gedefinieerd. Voor het vrijgeven van wisselstraten hebben ze alle een eigen bezetmelder. De exitsensor van sectie 1 en een van de entrysensors voor sectie 2 zijn een bezetmelder van een wissel. De andere entrysensor voor sectie1 is een stopmelder van een blok. De exitsensor voor sectie2 is gekoppeld met de stopmelder voor het zelfde blok en de entrysensor voor sectie1 is gekoppeld met de entrymelder van het betreffende blok. Die ga ik nog veranderen en vervangen door nieuwe melders die beter gepositioneerd zijn.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 May 2020, 22:24:13
Ok nadat we elkaar de les gelezen hebben ;D is het weer tijd om verder te gaan. Zoals beloofd is, zou ik ook het systeem dat het bel geluid produceert nog proberen uit te leggen.

Ik had een tijdje geleden een Arduino Nano en een versterkerkaartje (PAM8403) aangeschaft, dat voor een ander projectje. Maar die kon ik hier ook mooi voor gebruiken. De meeste voorbeelden die ik op het internet kon vinden werken echter met geluidsfragmenten die op een SD kaartje staan en maken gebruik van een bibliotheek die dat regelt, dat wilde ik nou net niet. Mijn idee was om de wav-data als byte array in het geheugen op te slaan.

Gelukkig kwam ik de volgende site tegen http://wiki.openmusiclabs.com/wiki/PWMDAC (http://wiki.openmusiclabs.com/wiki/PWMDAC). Waar uitgebreid beschreven wordt hoe je een geluidsbron gekoppeld aan een analoge pin, weer kunt afspelen op 2 PWM pinnen via een ISR voor timer1. Dat was precies wat ik zocht. De beschrijving is inclusief een RC netwerkje (R 3,9K, C 47nF) dat nodig is tussen de Arduino en de versterker. Dus dat in elkaar gezet samen met een 5cm luidsprekertje, gesloopt uit een reclame ding van een grote bierbrouwerij.

dat ziet er nu als volgt uit.
(https://images.beneluxspoor.net/bnls/20200504_094621_DSC_7115.jpg)
(https://images.beneluxspoor.net/bnls/20200504_100000_DSC_7116.jpg)

De versterkerkaart is via het RC netwerkje aangesloten op pin 9 van de NANO, pin 10 gebruik ik niet omdat ik wav bestandjes van 16kHz en 8 bit gebruik. De Arduino schets van deze openmusiclabs site heb ik gemodificeerd (alles er uitgesloopt wat ik niet gebruik) voor mijn toepassing.

dat leverde uiteindelijk dus de volgende Arduino schets op.
AKI_Bel
#include <avr/pgmspace.h>

const PROGMEM byte wav_0[3936] = {
  0x80, 0x82,
...........
  0x78, 128
};

const PROGMEM byte wav_1[3934] = {
  0x79, 0x76,
..........
  0x79, 128
};

const PROGMEM byte wav_2[3932] = {
  0x82, 0x87,
..........
  0x79, 128
};

const PROGMEM byte wav_3[3934] = {
  0x80, 0x85,
.........
  0x73, 128
};

const PROGMEM byte wav_4[3946] = {
  0x7f, 0x8a,
..........
  0x75, 128
};

const PROGMEM byte wav_5[3955] = {
  0x6e, 0x60,
.........
  0x81, 128
};

//
// options table at http://wiki.openmusiclabs.com/wiki/PWMDAC
//
// Timer1 PWM.  9b, Phase Correct, 15.6kHz, single pin
// The WAV samples are 8it, 16kHz,
//
#define PWM_FREQ 0x01FF   // pwm frequency - see table, 0x01FF is 15.6kHz
#define PWM_MODE 0        // Fast (1) or Phase Correct (0)
#define PWM_QTY 1         // number of pwms, either 1 or 2
#define PWM_SHIFT 1       // 15.6kHz and 1 pin gives 9bit resolution, so shift the sample 1 bit left

#define WAV_VOLUME 20
#define BEL_PIN 2

void setup()
{
  cli();  // turnoff interrupts

  // setup PWM
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); //
  TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1
  TIMSK1 = 0x20; // interrupt on capture interrupt
  ICR1H = (PWM_FREQ >> 8);
  ICR1L = (PWM_FREQ & 0xff);
  DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs

  sei(); // turn on interrupts

  // Setup the bell activation pin
  pinMode(BEL_PIN, INPUT);
}

//
//  When the bell has to ring, set this on
//
volatile bool bellRinging = false;

void loop() {
  if (digitalRead(BEL_PIN) == HIGH)
  {
    bellRinging = true;
  }
  else
  {
    bellRinging = false;
  }
}

//
// Some state data for the ISR
// Some state data for getNextSoundSample with their initial values.
//
int debugCnt = 0;
unsigned int wavValue = 128; // start at the zero point of the sound
int wavByteIndex = 0;

int wavTableSize = 3935;
byte *wavTable = wav_0;

ISR(TIMER1_CAPT_vect)
{
  //
  //  If at the begin of the sound sample, the sound toggle is of,
  //  return immediate, so no bell is ringing
  //
  if ((wavByteIndex == 0) && (!bellRinging))
  {
    wavTableSize = 3935;
    wavTable = wav_0;
    return;
  }

  // output high byte on OC1A
  OCR1AH = wavValue >> 8; // takes top 8 bits
  OCR1AL = wavValue;      // takes bottom 8 bits

  wavValue = (getNextSoundSample() * WAV_VOLUME) / 100;
}

unsigned int getNextSoundSample()
{
  unsigned int sample = (pgm_read_byte_near(wavTable + wavByteIndex)) << PWM_SHIFT;

  if (++wavByteIndex >= wavTableSize)
  {
    //
    // Restart the table
    //
    wavByteIndex = 0;
    switch (random(0, 6))
    {
      case 0:
        wavTableSize = 3935;
        wavTable = wav_0;
        break;

      case 1:
        wavTableSize = 3933;
        wavTable = wav_1;
        break;

      case 2:
        wavTableSize = 3931;
        wavTable = wav_2;
        break;

      case 3:
        wavTableSize = 3933;
        wavTable = wav_3;
        break;

      case 4:
        wavTableSize = 3945;
        wavTable = wav_4;
        break;

      case 5:
        wavTableSize = 3954;
        wavTable = wav_5;
        break;

      default:
        wavTableSize = 3935;
        wavTable = wav_0;
        break;
    }
  }

  return sample;
}

Dit is puur C code, niks OO en eerlijk gejat onder het motto "Beter goed gestolen dan slecht gecomponeerd".  Overigens heb ik op de site van openmusiclabs nergens iets kunnen vinden over licencies, dus ga ik er van uit dat dit voorbeeld vrijelijk gebruikt kan worden :angel:

De grootste horde die ik moest nemen was, hoe krijg ik de byte arrays in het programma geheugen en niet op de stack (want die is maar 2kb groot). Het benoemen van de arrays tot const werkte niet. Gelukkig bleek er een andere qualifier te zijn, PROGMEM met bijbehorende include file, die de compiler vertelt dat deze data niet op de stack moet, maar in de instructieruimte geplaatst moet worden (die is op de NANO 30kb). Helaas liep ik daarna in een mooie RTFM situatie, wat me twee dagen puzzelen opleverde waarbij het geheel wel een puike hoeveelheid herrie opleverde, maar niet het geluid dat ik verwachte. Nadat ik met wat andere voorbeelden, zoals Tone en sinus generaties wel keurige toontjes produceerde, toch maar eens naar de include file en op internet gekeken. Toen bleek dat de array data niet direct benaderbaar is, maar dat daar speciale functies voor gebruikt moeten worden. Na aanpassing kreeg ik keurig het bel geluid.

In eerste instantie speelde ik steeds het zelfde wav bestandje af. Dat klonk aardig maar ook erg monotoon, als je het echte ding hoort, dan zit er toch variatie tussen de verschillende belslagen. Dus heb ik meerdere verschillende belslagen omgezet tot een wav bestand en dat opgenomen. Ik heb nu 6 verschillende byte arrays in de schets, die ik daarna in een willekeurige volgorde afspeel. Dit gebeurt zolang pin 2 hoog gehouden wordt. Is die laag, dan is dit systeem stil. Deze pin is verbonden met een pin op het AKI systeem, die daarmee de bel kan laten horen wanneer de AKI de overweg sluit.

De belslagen heb ik gehaald uit een mp3 bestand dat ik hier op het forum vond in het volgende draadje https://forum.beneluxspoor.net/index.php?topic=19817.0 (https://forum.beneluxspoor.net/index.php?topic=19817.0).
Met behulp van Wavpad van NCH heb ik hier de belslagen uitgeknipt en gesaved als een 16kHz, 8 bit, ongecomprimeerd, mono wav bestand. Daarna heb ik met een Java programmaatje (met methods van AudioSystem) de 8bit muziekstream omgezet naar een tekst bestand, dat ik als byte array in de Arduino schets kon opnemen. Het enige waar ik op gelet heb is dat ieder belslag ongeveer 240mSec lang was en dat er geen grote verschillen in de onderlinge lengte zijn, omdat dat niet goed klonk.

In het code voorbeeld ontbreekt de wav data, als iemand de complete code wil hebben, stuur me een PB dan wil ik de complete code wel naar iemands prive mail sturen. De volledige code is te groot om hier te kunnen publiceren.

Dat was het voorlopig weer.

Groet Meino




Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 07 May 2020, 22:26:15
met uintX_t bedoelde ik dus uint8_t, uint16_t etc :police: maar ik had geen zin om telkens die hele rijtjes te tikken  :P en ik dacht dat het wel duidelijk was. I was wrong.
Citaat
Maar ik vind de overzichtelijkheid en helderheid van de code belangrijker dan het besparen van 8 bits.
precies daarom noemde ik dus de int8_t en uint16_t notaties. :D  Ja en wat ik tegen de bool heb. De bool heeft in mijn ogen geen voordeel op een uint8_t. Zo had ik dus een slechte dag toen ik een derde state wilde invoeren en was vergeten dat ik een bool had gebruikt. 1 slechte ervaring is all it takes.

Ik had vandaag wat research gedaan en je kan idd constantes meegeven aan een constructor. Dat wist ik eerst niet. Ik dacht namelijk dat het lastig was als je 2 dezelfde type objecten maakt die elk een verschillende constante nodig hebben. En dat je om die reden een variabele had gebruikt. Zoals je programma nu is, gebruik je 2 bytes aan onnodig geheugen om een constante waarde te gebruiken. Maar aan de andere hand, je kan hem zo wel nog achteraf beinvloeden, zou je dat ooit willen. Maar ik snap dat bedoeld is.

Waar ik dus in SW op doelde was:
class   BlinkingLed {
private:
    const byte   blPin;
    const unsigned short blinkInterval;
    bool  blinking;
    bool  ledOn;
    unsigned long  lastTime;

public:
    BlinkingLed(byte pin, unsigned short interval)
       : blPin(pin), blinkInterval(interval)  {}
Je kan zo dus de compiler vertellen dat hij met constantes te maken heeft voor de objecten. Dit zou dus hetzelfde doen, met minder geheugen mits je ze ook wilt dat het constantes zijn. Dit leerde ik dus vandaag  :police:

Die 3 termen van je moet ik eens opzoeken. Geen idee wat het is. Ik weet wel dat ik in de wonderlijke assembly wereld op werk soms bijzonder van streek wordt gemaakt door de meest verschrikkelijke "abominations" dat zij daar software noemen.

Back to buisness:
Als in koploper een trein van punt A naar punt B laat rijden en hij komt halverwege een bepaalde melder tegen kan je dan een instructie voor een soort overweg ding aansturen? Je kan natuurlijk seinstanden en wisselstanden kan opsturen, maar onbekend als ik ben met koploper, weet ik niet of je zo ook een overgang kan dichtgooien. Hoe ga je dat aanpakken?

Wat wel interessant is, is dat je arduino informatie kan ontvangen over de wisselstanden. Daar moet je toch zeker mee kunnen werken?

mvg

Bas
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 07 May 2020, 23:37:11
Die 3 termen van je moet ik eens opzoeken. Geen idee wat het is. Ik weet wel dat ik in de wonderlijke assembly wereld op werk soms bijzonder van streek wordt gemaakt door de meest verschrikkelijke "abominations" dat zij daar software noemen.
Dat is heel spijtig, want IMHO vind ik dat dit tot de geestelijke bagage van iedereen die (semi)beroepsmatig software ontwikkelt zou moeten behoren. Dus deze begrippen zijn nooit op je opleiding behandeld? Overigens zijn dit al heel oude begrippen.

Als in koploper een trein van punt A naar punt B laat rijden en hij komt halverwege een bepaalde melder tegen kan je dan een instructie voor een soort overweg ding aansturen? Je kan natuurlijk seinstanden en wisselstanden kan opsturen, maar onbekend als ik ben met koploper, weet ik niet of je zo ook een overgang kan dichtgooien. Hoe ga je dat aanpakken?

Wat wel interessant is, is dat je arduino informatie kan ontvangen over de wisselstanden. Daar moet je toch zeker mee kunnen werken?
Koploper kan inderdaad een instructie sturen naar een accessory decoder als een specifieke loc een blok binnengaat. Maar daar maak ik geen gebruik van. De titel van dit draadje geeft al aan hoe ik werk. Alle status wijzigingen van alle bezetmelders worden op de CanBus gepubliceerd, en kunnen door ieder systeem opgepikt worden. Uiteindelijk worden ze dan door een systeem (de S88_Interface) via de centrale naar Koploper gestuurd. Ook alle wisselopdrachten worden op de CanBus gepubliceerd en kunnen ook door ieder aangesloten systeem gelezen worden. Kortom elk systeem dat met de CanBus verbonden is, is in staat om alle wisselstanden en de status van alle bezetmelders te kennen. Verder heb ik de AKI software op de SeinController gezet, die houdt al de standen van de wissels bij omdat die nodig zijn om het juiste seinbeeld voor de driehoogte seinen systeem46 te kunnen tonen

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 08 May 2020, 22:26:04
Object georiënteerd programmeren hoera! Ik vind er altijd een zekere elegantie in zitten om gewoon eens naar de verschillende soorten objecten te kijken. Een "AKI", die ervoor moet zorgen dat de boel knippert en geluid maakt enzo. Een "sensor" die alleen hoeft te detecteren of er iets over z'n vingers rijdt of niet. Enz.

Bas: je lijkt booleans niet leuk te vinden? Waarom niet? Er zijn in mijn dagelijkse praktijk een heleboel dingen die "wel/niet", "ja/nee" zijn. Als je er een derde iets bij wilt hebben wordt het een beetje quantumcomputing achtig ofzo: "ja/nee/misschien"? Tenminste, zo komt het op mij over. Een aki is aan of uit. Een wissel staat recht of afbuigend. Een spoor is vrij of niet. "Misschien is wat gevaarlijk in het laatste geval :)

Natuurlijk zijn er uitzonderingen. Zo'n Duits twee-armen-sein als ik heb heeft netjes drie staten (rood=0, groen=1 en geel=2). Maar dat weerhoud mij er niet van om voor de ja/nee zaken de IJzeren Logica van boolean True/False te gebruiken.

(Zeg ook ik, die sinds 2000 met Python werkt en zich alleen voor arduino's met C-achtige zaken hoef bezig te houden :) Ook leuk voor de verandering.)

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 08 May 2020, 22:33:38
Een wissel staat recht of afbuigend.
Een wissel kent nog een derde toestand: in beweging. Dan staat hij niet recht en ook niet afbuigend.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: ikbenerevenniet op 08 May 2020, 22:50:15
Nee, dat geldt niet als een toestand.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 08 May 2020, 23:24:04
Wat is het dan? Reinout stelt dat een wissel recht of afbuigend kan zijn. Maar gedurende de beweging is hij allebei niet. Hoe moet ik dat dan noemen?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Martin Hornis op 08 May 2020, 23:30:33
Er zijn twee soorten toestanden:
a) statische (stilstaand);
b) dynamische (bewegende).

Pierre-Simon Laplace heeft daar zo'n 200 jaar geleden het een en ander over geschreven. Zie Wikipedia. Vooral bij Statistiek staan opmerkelijke zaken. Daar staan o.a. "posities en snelheden". Hij benoemt daar ook de kansrekening. Dus wat is de kans dat een wissel in een bepaalde positie stilstaat of aan het bewegen is. Je zult met beide toestanden rekening moeten houden.

Vergelijk het maar eens met (twee) afzonderlijke flitspalen en trajectcontrole.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 09 May 2020, 00:12:30
Grappig hoe er op eens een discussie op gang komt.

Klaas heeft wel gelijk dat het wissel ook in beweging kan zijn. Echter dat is alleen van belang voor het wissel object zelf. De buitenwereld hoeft dat niet te weten. Die zet gewoon het wissel in rechtdoor of afbuigend, het is dan aan het wisselobject zelf om het wissel in beweging te brengen en die beweging te controleren tot dat het wissel in de gewenste stand staat. Kortom voor de buitenwereld heeft het wisselobject slechts twee toestanden, rechtdoor of afbuigend. In de praktijk werkt dit prima, het omzetten gebeurd snel genoeg binnen de tijdspanne die bijv. Koploper reserveert.
Mocht de buitenwereld besluiten om opnieuw de toestand te wijzigen terwijl het wisselobject nog in beweging is, dan is dat een probleem voor het wisselobject om dat correct af te handelen, niet voor de gebruiker. Mocht het om wat voor reden ook nodig zijn dat de buitenwereld wel moet weten of het wissel al goed ligt, dan zou ik een tweede toestand invoeren, n.l. gereed of niet gereed.
Dat is het prettige van OO. Je kunt zaken die voor de gebruiker niet relevant zijn makkelijk isoleren (eigenlijk is dat een vereiste om te doen).

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Martin Hornis op 09 May 2020, 00:20:41
Die zet gewoon het wissel in rechtdoor of afbuigend, het is dan aan het wisselobject zelf om het wissel in beweging te brengen en die beweging te controleren tot dat het wissel in de gewenste stand staat.
Dus controleren d.m.v. eindstandschakelaars. Derhalve twee onderling onafhankelijke contacten. Dus geen wisselcontact.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 09 May 2020, 09:47:07
Op dit moment is de eneige terugkoppeling die ik heb is visueel, door middel van ledjes op een paneel.
Voor een geplande uitbreiding met een schaduwstation zit ik wel te denken aan een vorm van een simpele terugkoppeling op een digitale pin van de Arduino. Dusdit zou een mogelijkheid zijn.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 09 May 2020, 10:01:53
Ik ben al jaren gewend om op mijn wissels terugmelding toe te passen. Tijdens het omlopen staat het wissel niet rechtdoor en ook niet afbuigend. En dat is voor mij de derde toestand: stand onbepaald. Bij handbediening kan ik dat op het paneel ook zien, beide leds zijn uit.
Als je geen terugmelding hebt dan ga je er van uit dat het wissel staat in de stand waarin je het hebt gestuurd, en dan ken je inderdaad maar 2 toestanden.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 10 May 2020, 15:20:07
Op video's van Duitse bedieningspanelen zie je inderdaad het ledje van de ene stand uitgaan en pas na een seconde ofzo het ledje van de andere stand aangaan. Ik had dat niet met de terugmelding geassocieerd, maar dat zal het wel zijn, ja.

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Klaas Zondervan op 10 May 2020, 17:00:14
Waar zouden die ledjes anders voor zijn?  ;)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 10 May 2020, 22:38:13
Als je erover nadenkt: logisch inderdaad.
Ik zat naar het leuke visuele effect te kijken i.p.v. na te denken :)

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 10 May 2020, 23:01:35
Reinout

heb je een linkje naar die video?

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: ikbenerevenniet op 11 May 2020, 01:30:52
derde toestand: stand onbepaald

Je zegt het zelf al: stand onbepaald, en dat mag je geen toestand noemen. Dat mag bovendien alleen als het object in rust is, en dat is het tijdens de overgang uiteraard niet.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 11 May 2020, 08:41:57
Bas: je lijkt booleans niet leuk te vinden? Waarom niet?
No offense maar volgens mij heb ik het 2x gezegd... letterlijk  ;D:
Reden 1: Ze nemen 1 hele byte geheugen in beslag. Als je er maar 1 heb in je programma, dan zei het zo. Heb je 8 booleans, dan gooi je dus 7 bytes weg.
Reden 2: Als je een derde state wilt toevoegen, bijvoorbeeld aan een bewegende wissel of aan een sein met 3 standen, en je bent vergeten dat je ooit zo 'dom' was om 'bool' ipv byte te tikken, dan heb je dus pech.

Voor deze reden alleen al, zou je al byte moeten tikken ipv bool. Een byte kan wat een bool kan en meer en een bool kan niet wat een byte kan. Een bool gebruiken ipv een byte is wat ik ook beschrijf als: 'shooting yourself in the foot.'. Je mag ook prima de woorden true en false gebruiken in arduino voor een byte type variabele.

Ik had in Processing trouwens nog eens een 3e probleem met een bool. Om mijn layout op te slaan, gebruik ik een .txt bestand. Er was een bepaald rail item en die had ook een bool.
Reden 3: Bij het opslaan werd de state van die bool opgeslagen met de woorden 'true' en 'false'. Ik had net mijn code ingericht om uit dat txt bestandje getallen uit te lezen en geen woorden. Ik verwachtte een '0' en een '1' maar kreeg dus iets anders. En ook deze ellende loste ik op door bool te vervangen door byte.

Dus daarom Reinout, gebruik ik nooit meer bools. Ze voegen helemaal niks toe behalve potentiele bugs.

Ik gebruik dus standaard bit fields voor precies dit.
struct {
    unsigned int seinStand: 2; // 2 bits
    unsigned int wisselStand : 2; // 2 bits
    unsigned int terugmelder : 1; // 1 bit
} status;  // gebruikt 1 byte aan geheugen

// gebruik:
#define STRAIGHT 0
#define CURVED 1
#define MOVING 2 // of UNDEFINED of TRANSITIONING of net wat je wilt
// nog een 4e stand voor een drieweg wissel is ook nog mogelijk.

status.wisselStand = MOVING ;

if( status.terugmelder ) {
// etc

Dit is ook 1 van de redenen waarom ik classes en objecten probeer te vermijden waar mogelijk. Als je 20 objecten maak in SW die allemaal 1 enkel bitje nodig hebben. Dan heb je dus minimaal 20 bytes nodig. En volgens mij kan je met de OO structuur geen bitfields toepassen die objecten samen kunnen delen.

Die atmega328 heeft slechts 2kb. Meestal volstaat dit en dan maakt het niet zoveel uit. Immers "unused space is wasted space", maar soms dan kom je er toch aan.

@Erik
Stand onbepaald is nog steeds een bestaande fysieke toestand. Wanneer puntje bij paaltje bij komt, mag een trein niet rijden over een bewegende wissel. Voor hetzelfde zit de wissel vast door een stukje ballast. Dan heb je dus wel degelijk de noodzaak om deze stand op te slaan in je software.

En @Reinout als je derde wissel state in een bool probeert te stoppen, nou dan kan je je ontspoorde trein gaan opruimen  ;D ;D ;D

Mvg,

Bas




Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: ikbenerevenniet op 12 May 2020, 01:03:46
Ik weet niet waar te beginnen... Kan eigenlijk alleen maar zeggen: als het jou goed bevalt is dat prima, maar ga het asjeblieft niet aanprijzen als "the way to go".
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 12 May 2020, 08:13:06
Prima. Beter dat we de discussie staken, want hier gaan we niet uitkomen blijkbaar. Denk dat Meino er ook niet meer vrolijk van wordt :-[
(sorry Meino)

Mvg,

Bas
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 12 May 2020, 14:56:09
Meino: deze video (link gaat naar 6:14) (https://youtu.be/DJebSv8HFbo?t=374) bijvoorbeeld. Het zat net iets anders dan ik me herinnerde: tijdens het omleggen knippert de "doel"-led. Maar goed.

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 12 May 2020, 17:19:07
@Reinout, bedankt voor het linkje,.

@Erik en @Bas
Ik heb maar niet gereageerd, want het werd een welles/nietes gebeuren en dat is zinloos.
Om met Rudyard Kipling te spreken  "East is east and west is west, and never the twain shall meet".

Maar Bas laat ik je niet weerhouden om je mening te plaatsen.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 20 December 2021, 22:07:05
Ik zie dat het al ruim 1,5jaar geleden is dat ik hier iets publiceerde. Toch is er best wel het een en ander gebeurd. Sommige zaken zijn wel eens terloops ter sprake geweest in mijn Kranenberg draadje. Dus laten we dit draadje maar weer eens oprakelen.
Om te beginnen met een systeem waarmee ik mijn eerste ervaring met Arduino's kreeg, n.l. een Arduino voor de aansturing van een draaischijf, de Draaischijf Controller. Ik realiseerde me vandaag, dat ik het regelmatig over de draaischijf gehad heb in andere draadjes, maar dat er nooit een complete beschrijving geweest is. Dus bij deze.

De draaischijf in kwestie.
(https://images.beneluxspoor.net/bnls/20210409-115159-DSC-2209-Edit-61c0e7ff25643.jpg) (https://images.beneluxspoor.net/bnls/20210409-115159-DSC-2209-Edit-61c0e7ff25643.jpg)

Het begon een aantal jaren geleden toen ik in de eerste fases van de bouw van Kranenberg was. Er moest een klein locdepot met een draaischijf komen. Vanwege de kosten besloten om zelfs iets te bouwen. In eerste instantie op basis van een heel goedkope Dapol kit, maar dat werd niets, dus eens gekeken naar de Peco LK55. Maar die wilde ik wel laten werken. Op het RMWeb vond ik het volgende artikeltje Automatisering Peco LK55 met een Arduino (https://www.rmweb.co.uk/community/index.php?/topic/78578-dcc-controlled-peco-turntable-project-using-a-arduino-uno), dat leek me wel wat, ik kende de Arduino niet, maar C en C++ hadden weinig geheimen, dus spul besteld, NEMA17 400step motor, Adafruit motorshield, etc. en aan de gang gegaan.
Omdat de Adafruit nogal een irritante pieptoon genereerde bij microstepping is die uiteindelijk vervangen door een TMC2130 Silentstepstick.

Goed, een paar uitgangspunten.

Om puur met het tellen van stapjes de brug te kunnen positioneren, moet het systeem een nulpunt kennen. Een stappenmotor heeft één nadeel, n.l. als hij spanningsloos is, kan hij vrij bewegen. Dus na het inschakelen van het systeem is het in principe niet bekend waar de brug precies gepositioneerd is. Daar dient dus de Hallsensor en magneet voor. Hiermee kan de draaischijf gekalibreerd worden zodat er een exacte locatie van een nulpunt bepaald wordt tijdens het opstarten, en de posities van iedere rail aansluiting vast ligt.

De koppeling van de railaansluiting met DCC accessory commando's is simpel, iedere railaansluiting heeft zijn eigen DCC adres, waarbij het commando "--" (rechtdoor) voor de ene kant van de brug geldt en het commando "/" (afbuigend) voor de andere kant geldt.

Zie dit schema
(https://images.beneluxspoor.net/bnls/Peco-LK55-61c0ef0c123f9.jpg) (https://images.beneluxspoor.net/bnls/Peco-LK55-61c0ef0c123f9.jpg)

Groet Meino


Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 21 December 2021, 00:27:07
Ok we gaan even verder.
Het hart van het systeem is één klasse, Turntable, die alle nitty gritty details van het aansturen van de draaischijf verbergt achter een paar simpele methoden.
Ik heb deze klasse in een Arduino bibliotheek gezet, welke twee bestanden bevat, KB_Turntable.h en KB_Turntable.cpp. Helaas laat het forum maximaal 20k tekens toe, dus lukt het me niet om de source hier op te nemen. Mocht iemand daar belangstelling voor hebben stuur me een PB.

Maar voor het gebruik zijn eigenlijk alleen de publieke methoden van de klasse van belang, dat is niet erg uitgebreid, dus die zal ik hier even bespreken.

De publieke methoden:
  public:
    //
    //  Turntable constructor
    //
    Turntable(int anHallSensorPin, int aBounceSize, AccelStepper *aStepper, long aFullRotation);
 
    //
    //  Set a new turntable position and start the movement
    //  to the new position.
    //
    void gotoTTPosition(long newTTPosition);

    //
    //  Keep the turntable moving if necessary
    //
    void heartBeat();

    //
    //  This method performs a calibration operation for the turntable. During this operation it
    //  executes a full turn counter clockwise looking for the hall sensor. When found, it will
    //  skip past the sensor and do a turn clockwise until the sensor is again found.
    //  Based on the four points (2 points counter clockwise and 2 points clockwise) where the sensor
    //  tripped, the 0 position of the TT is calculated and the TT will go to its last known position.
    //  This whole process is blocking, during the execution of this method no other DCC commands are honored.
    //
    void calibrate();
   
    //
    //  This method performs a calibration operation for the turntable. During this operation it
    //  executes a full turn counter clockwise looking for the hall sensor. When found, it will
    //  skip past the sensor and do a turn clockwise until the sensor is again found.
    //  Based on the four points (2 points counter clockwise and 2 points clockwise) where the sensor
    //  tripped, the 0 position of the TT is calculated and the TT will go to the specified new position.
    //  This whole process is blocking, during the execution of this method no other DCC commands are honored.
    //
    void calibrate(long newTTPosition);

Wat doen deze methoden?

Turntable(int anHallSensorPin, int aBounceSize, AccelStepper *aStepper, long aFullRotation)

      Dit is de constructor. De volgende parameters moeten meegegeven worden;

void gotoTTPosition(long newTTPosition)

     Deze methode start de beweging van de draaischijf naar de opgegeven positie.


void heartBeat()

     Het bewegen van de draaischijf gebeurt non-blocking, de aanroep van deze methode zorgt er voor dat, indien nodig, de stappenmotor een stapje verder gaat.

void calibrate()

     Aangezien een stappenmotor vrij kan bewegen als hij spanningsloos is, heeft de draaischijf niet een vast nulpunt. Deze methode gaat op zoek naar de Hallsensor, en wanneer gevonden, wordt op basis daarvan het nulpunt vast gelegd. Na afloop van de calibratie gaat de draaischijf terug naar de laatst ingenomen positie. Deze methode is blocking.

void calibrate(long newTTPosition)
    Zie de beschrijving van calibrate(), met dit verschil dat nu na afloop van de calibratie de draaischijf naar de opgegeven positie zal gaan.

Alle posities worden gespecificeerd in stapjes, modulo aFullRotation

Groet Meino

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: zuylen op 21 December 2021, 10:23:46
Mooi systeem! IK heb mijn beide draaischijven van Fleischmann. En dan niet de nieuwste draaischijf met een klein relais ingebouwd. Mijn draaischijven hebben beiden dat relais nog niet. Ik gebruik het systeem dat Dick Koning ooit ontwikkeld heeft. Dat zit in een Arduino Nano gepropt. En die stuurt mijn twee Fleischmann schijven per stap (= spoor en er zijn 48 mogelijke sporen dus stappen) van de draaischijf. Het gevolg is dat de schijf iedere keer steeds even stopt en dan weer doorgaat naar het ingestelde eindpunt.
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 21 December 2021, 16:21:14
Goed, we hebben het even gehad over het hart van het systeem, de klasse die de draaischijf aanstuurd. Maar die staat niet alleen, de schets moet er wel voor zorgen dat het zijn werk goed kan doen. Dus gaan we het nu even over de schets hebben.
Een paar opmerkingen;

We gaan nu even stukje bij beetje door de schets.
#include <Servo.h>
#include <TMC2130Stepper.h>
#include <AccelStepper.h>
#include "CanBus.h"
#include "KB_Eeprom.h"
#include "KB_Turntable.h"
#include "KB_Servo.h"
De bibliotheken die gebruikt worden.

//
//  KB_CB-TurntableController  Turntable control
//
//      Author: Meino de Graaf
//      email: meino@innerside.demon.nl
//
//  This program controls a NEMA-17 bi-polar stepper motor, which turns a Peco LK-55 turntable.
//  The stepper motor has 400 full steps/rotation (0.9 degree per step). The motor is driven
//  by an TMC2130 SilentDrive using micro stepping (32 micro steps/full step).
//
//  Further two micro servos are also controlled by this program. These two servos are responsible for
//  opening or closing of the doors of a two track loco shed.
//
//  The program listens for DCC commands (coming from the Canbus) that activates the different functions
//  (moving to a track or opening/closing of a door).
//  Currently there are 9 tracks defined in the program, each track has it's own DCC address and has two
//  positions for the stepper motor defined. One position defines the 0 degree ('-') position for the
//  control platform and the other one defines the 180 degree ('/') opposite position.
//
//  Due to the fact that an unpowered stepper motor is free to turn (contrary to a micro servo, which stays
//  put due to the friction of it's gears), the position of the stepper motor is unknown after a power-up.
//  Therefore the turntable performs a calibration during the initialisation phase. This calibration is done
//  by locating a Hall sensor using a small magnet attached to the turntable. The location of the Hall sensor
//  defines the zero position from which all track positions are derived.
//
//  Two door servos are defined. Each has it's own DCC address and the open/close position for the servo.
//  If a DCC command for a servo is received, the '-' command activates a door close, while the reception
//  of a '/' command activates the opening of the door. Further the speed of the movement is also part of
//  the servo definition.
//  At this moment all definitions are part of the code and no provision are made to make these remotely
//  configurable.
//
Standaard beschrijving en "disclaimer" tekst.

#define SERVO_DEBUG 0
#define DEBUG 0

//
//  Pins used for the Hall sensor on the Turntable
//
#define HALL_SENSORPIN  5

//
//  Pin definitions for the TMC2130 SilentStepper on the UNO
//
#define EN_PIN    7  // enable (CFG6)
#define DIR_PIN   8  // direction
#define STEP_PIN  6  // step
#define CS_PIN    10  // chip select TMC2130 (Silent stepper driver)

//
//  Pins used by the SPI interface (only for documentation)
//
#define MOSI_PIN 11  //SDI/MOSI (ICSP: 4, Uno: 11, Mega: 51)
#define MISO_PIN 12  //SDO/MISO (ICSP: 1, Uno: 12, Mega: 50)
#define SCK_PIN  13  //CLK/SCK  (ICSP: 3, Uno: 13, Mega: 52)

//
//  Pin for the CanBus
//
#define CB_CS_PIN  9 // Chip select MCP2515 (Canbus)

//
//  The door servo pins
//
#define SERVO_2_PIN 4
#define SERVO_1_PIN 3
Standaard debug en pin definities (Gelieve niet te zeuren over Enums!!)

//
//  The CanBus
//
CanBus *canBus;

//
//  Data that is stored in EEPROM to remember the state of the
//  turnouts. It is handled by a eepromController class
//
EepromController *EepromControl = new EepromController();
Canbus en EepromController objecten. EEpromController is een kleine klasse die ik gebruik om de stand van de draaischijf en locloods deuren in EEProm op te slaan, zodat bij een herstart de handel in de laatste situatie terugkomt.

//
//  The definitions for the geometry of the TT-stepper combination
//
#define MICROSTEPS   32L        // This parameter defines the number of microsteps used for the stepper.
//                                 16 is the minimum you may use (otherwise you have to change the
//                                 ADJUSTMENT define). Settings of 16 & 32 are save to use.
//                                 Higher values (64,128 or 256) are also save to use for controlling the stepper
//                                 however these values create such an interrupt load, that the DCC commands
//                                 will fail while the stepper is moving.
//
#define FULL_STEPS    400L      // A Nema 17/23 motor with steps of 0.9degree per step
#define HALF_STEPS    200L

#define FULL_ROTATION (MICROSTEPS * FULL_STEPS)     //  The nr of steps for a 360degree turn
#define ROTATION180   (MICROSTEPS * HALF_STEPS)     //  The nr of steps needed for 180degree turn

#define BOUNCE        (MICROSTEPS/4)               //  In real life there is always an
//                                                     overshoot and a rebounce

//
// The following defines give a full rotation in aprox. 2 minutes like the prototype
//
#define MAX_SPEED     (2.8*MICROSTEPS) // Avoid speed settings 2.3-2.7 (step time 1400-1700 microsec). All
//                                        with 256 microsteps, because in this speed window a faint whine
//                                        is noticeble, probably caused by interference.
//                                        This is valid when stealthChop is active.
#define ACCELERATION  (MICROSTEPS/1.4)
#define START_SPEED   (MICROSTEPS/1.4)

//
//  Rough Definitions for the Turntable track positions
//
#define TRACK_POSITION1  (376L*MICROSTEPS)  //  DCC address 201
#define TRACK_POSITION2  (350L*MICROSTEPS)  //  DCC address 202
#define TRACK_POSITION3  (293L*MICROSTEPS)  //  DCC address 203
#define TRACK_POSITION4  (281L*MICROSTEPS)  //  DCC address 204
#define TRACK_POSITION5  (269L*MICROSTEPS)  //  DCC address 205
#define TRACK_POSITION6  (257L*MICROSTEPS)  //  DCC address 206
#define TRACK_POSITION7  (245L*MICROSTEPS)  //  DCC address 207
#define TRACK_POSITION8  (233L*MICROSTEPS)  //  DCC address 208
#define TRACK_POSITION9  (215L*MICROSTEPS)  //  DCC address 209

//
//  Structure which defines the coupling between the used Accessory address and turntable
//  position
//
typedef struct
{
  short address;     // Accessory address
  long position0;    // Straight (-) location
  long position180;  // Diverging (/) location, 180 degrees turned.
}
TrackPosition;

const int nrOfTracks = 10;

//
//  A table with all the track positions. Each track position is fine tuned using small amounts
//  of microsteps. All these position have to be derived by Error and trial from the physical Turntable
//
TrackPosition trackPositions[nrOfTracks] = {
  {200, 0, 0},          // Triggers a Calibration action
  {201, TRACK_POSITION1 + 0, TRACK_POSITION1 + ROTATION180 - 4},
  {202, TRACK_POSITION2 + 2, TRACK_POSITION2 + ROTATION180 - 2},
  {203, TRACK_POSITION3 + 0, TRACK_POSITION3 + ROTATION180 + 0},
  {204, TRACK_POSITION4 + 6, TRACK_POSITION4 + ROTATION180 + 6},
  {205, TRACK_POSITION5 + 6, TRACK_POSITION5 + ROTATION180 + 4},
  {206, TRACK_POSITION6 + 0, TRACK_POSITION6 + ROTATION180 - 2},
  {207, TRACK_POSITION7 + 6, TRACK_POSITION7 + ROTATION180 + 0},
  {208, TRACK_POSITION8 + 0, TRACK_POSITION8 + ROTATION180 + 0},
  {209, TRACK_POSITION9 + 4, TRACK_POSITION9 + ROTATION180 + 4}
};
Een aantal defines voor de geometrie van de stepper/draaischijf. Defines voor het gedrag van de AccelStepper die gebruikt word en een tabel met het DCC adres per railpositie en de bijbehorende positie. Een opmerking, er is een extra DCC adres (200) dat hoort niet bij een railpositie maar triggered een kalibratie van de draaischijf.

//
//  Structure which defines the coupling between the used Accessory address and a door servo
//
typedef struct
{
  short address;     // Accessory address
  DoorServo *doorServo;
}
Door;


//
//  Number of door servo's
//
#define NR_OF_DOORS 2

Door doors[NR_OF_DOORS] = {
  { 108, new DoorServo(SERVO_1_PIN, 20, 108, 48)}, // DCC address 108, 20mS between steps, pos 108 open, pos 48 closed
  { 109, new DoorServo(SERVO_2_PIN, 20, 54, 116)}  // DCC address 109, 20mS between steps, pos 54 open, pos 116 closed
};
Ook de servos van de locloods deuren hebben hun eigen tabelletje.

//
// Create an interface to the TMC2130 driver card
//
TMC2130Stepper driver = TMC2130Stepper(EN_PIN, DIR_PIN, STEP_PIN, CS_PIN);

//
// Two methods used by the AccelStepper object for driving the stepper.
// This is done by using the defined STEP_PIN and the shaft_dir method
// on the TMC2130 interface object
//
void forwardStep()
{
  driver.shaft_dir(1);
  digitalWrite(STEP_PIN, LOW);
  //delayMicroseconds(1);       // Make sure that the pulse is detected
  digitalWrite(STEP_PIN, HIGH);
  //delayMicroseconds(10);
}

void backwardStep()
{
  driver.shaft_dir(0);
  digitalWrite(STEP_PIN, LOW);
  //delayMicroseconds(1);       // Make sure that the pulse is detected
  digitalWrite(STEP_PIN, HIGH);
  //delayMicroseconds(10);
}

//
// Time to wrap the stepper in an AccelStepper object
//
AccelStepper stepper(forwardStep, backwardStep);
We maken nu een interface naar het TMC2130 kaartje. Het aansturen daarvan gebeurd in de twee functies (forwardStep en backwardStep) die gebruikt worden om het AccelStepper object te maken.

//
// Create the Turntable object, give it the pin for the Hall Sensor, the AccellStepper object and
// the nr of steps for doing a full rotation
//
Turntable TT(HALL_SENSORPIN, BOUNCE, &stepper, FULL_ROTATION);
Nu kunnen we eindelijk het Turntable object aanmaken waarme we de draaischijf aansturen.

//
//  A function that executes whenever a DCC_ACC message is received from the Canbus
//  It's purpose is to analyze the received event (Signal or turnout) and
//  update the track position of the TT or update the position of the doors.
//
void dccAccReceiver(unsigned char aMsgLen, DCC_ACC_MSG *msgData)
{
#if (DEBUG)
  Serial.print("[dccAccReceiver] dccId: ");
  Serial.print(msgData->address);
  Serial.print(", dccState: ");
  Serial.println(msgData->direction);
  Serial.flush();
#endif //DEBUG

  unsigned short address = msgData->address;
 
  //
  //  Do we perform a straight(-) or a diverging(/) operation?
  //  For a straight operation enable is true, for a diverging operation
  //  enable is false.
  //
  bool enable = (msgData->direction) ? 0 : 1;

  //
  //  Search for the specified accessory address, when found lookup the
  //  specified position for the straight or diverging operation and start moving
  //  to that target position.
  //
  for (int i = 0; i < nrOfTracks; i++)
  {
    if ( address == trackPositions[i].address )
    {
#if (DEBUG)
      Serial.print("[dccAccReceiver] TT Address: ");
      Serial.print(address, DEC);
#endif //DEBUG
      //
      //  The new position of the TT
      //
      long newTargetPosition = 0;

      if (enable)     // Received a "-" command
      {
#if (DEBUG)
        Serial.print(" (-)");
#endif //DEBUG
        //
        //  Go to the straight position (0 degree)
        //
        newTargetPosition = trackPositions[i].position0;

        EepromControl->setTTposition(i);
        EepromControl->saveData();
      }
      else            // Received a "/" command
      {
#if (DEBUG)
        Serial.print(" (/)");
#endif //DEBUG
        //  Go to the diverging (180 degree) position.
        //
        newTargetPosition = trackPositions[i].position180;

        EepromControl->setTTposition(i+nrOfTracks);
        EepromControl->saveData();
      }

#if (DEBUG)
      Serial.print(" Position: ");
      Serial.println(newTargetPosition, DEC);
      Serial.flush();
#endif //DEBUG

      //
      //  If the choosen position equals 0, then we have to recalibrate
      //
      if (newTargetPosition == 0)
      {
        TT.calibrate();
      }
      else
      {
        //
        //  Not a recalibrate, goto the choosen position
        //
        TT.gotoTTPosition(newTargetPosition);
      }

      return;
    }
  }

  //
  //  If we didn't find an address match with a track location
  //  check if a door servo matches the received address
  //
  for (int i = 0; i < NR_OF_DOORS; i++)
  {
    if ( address == doors[i].address )
    {
#if (DEBUG)
      Serial.print("[dccAccReceiver] Door address: ");
      Serial.print(address, DEC);
#endif //DEBUG
      if (enable)
      {
#if (DEBUG)
        Serial.println(" (-)");
        Serial.flush();
#endif //DEBUG
        //
        //  Go to the closed position
        //
        doors[i].doorServo->closeDoor();

        EepromControl->setDoorPosition(i,DoorClosed);
        EepromControl->saveData();
      }
      else
      {
#if (DEBUG)
        Serial.println(" (/)");
        Serial.flush();
#endif //DEBUG
        //
        //  Go to the open position.
        //
        doors[i].doorServo->openDoor();

        EepromControl->setDoorPosition(i,DoorOpen);
        EepromControl->saveData();
      }

      return;
    }
  }

#if (DEBUG)
  Serial.print("[dccAccReceiver] Unknown address: ");
  Serial.println(address, DEC);
  Serial.flush();
#endif //DEBUG
}
Dit is de functie die we straks gaan registreren bij de Canbus en die daarna aangeroepen gaat worden als er een Accessory commando binnenkomt.

void setup()
{
#if (DEBUG || TT_DEBUG || SERVO_DEBUG || EEPROM_DEBUG)
  //Serial.begin(9600);
  Serial.begin(115200);
  while (!Serial);
  Serial.println("Starting");
#endif //DEBUG || TT_DEBUG || SERVO_DEBUG ||EEPROM_DEBUG

  //
  //  Setup the TMC2130 silent driver
  //
  driver.begin();              // Initiate pins and registeries
  driver.rms_current(900);     // Set stepper current to 900/1100mA. The stepper is rated at 0.9A.
  //                              The command is the same as command TMC2130.setCurrent(900, 0.11, 0.5);
  driver.hold_current(22);     // Hold current 70%
  driver.run_current(31);      // Run current 100%
  driver.hold_delay(1);        // Time between switch from run to hold 1sec
  //
  driver.microsteps(MICROSTEPS);   // Set the nr of microsteps
  //
  driver.interpolate(1);       // Use internally always the maximum nr of microsteps (256). This creates
  //                              the smoothest movement, when not set a small hum of the stepping is
  //                              noticeble
  driver.diag1_steps_skipped(1);
  //
  driver.stealthChop(1);       // Enable extremely quiet stepping

  digitalWrite(EN_PIN, LOW);
  //
  //   Enabling of the TMC2130 driver done
  //
Start van de Setup. We beginnen met het initialiseren van de TMC2130 driver kaart.

#if (DEBUG)
  for (int i = 0; i < nrOfTracks; i++)
  {
    Serial.print(trackPositions[i].address);
    Serial.print(", pos0 ");
    Serial.print(trackPositions[i].position0);
    Serial.print(", pos180 ");
    Serial.println(trackPositions[i].position180);
  }
#endif //DEBUG

  //
  //  Load the EEPROM data for the turnout directions
  //
  EepromControl->loadData();

  byte eePromData = EepromControl->getTTposition();

#if (DEBUG)
  Serial.print("[setup] eePromData: ");
  Serial.println(eePromData);
  Serial.flush();
#endif

  if (eePromData > 2*nrOfTracks)
  {
    eePromData = 1;
  }
 
  //
  //  Get the in EEPROM stored track position, it wil be used when we calibrate the
  //  turntable
  //
  long ttPosition;
  if (eePromData < nrOfTracks)
  {
    ttPosition = trackPositions[eePromData].position0;
  }
  else
  {
    ttPosition = trackPositions[eePromData-nrOfTracks].position180;
  }

  //
  //  Initialize the door servos with the in EEPROM stored position
  //
  for (int i = 0; i < NR_OF_DOORS; i++)
  {
    if (EepromControl->getDoorPosition(i) == DoorOpen)
    {
#if (DEBUG)
      Serial.print("[setup] door: ");
      Serial.print(i);
      Serial.println(" open");
      Serial.flush();
#endif

      //
      //  Go to the open position
      //
      doors[i].doorServo->initDoorOpen();
    }
    else
    {
#if (DEBUG)
      Serial.print("[setup] door: ");
      Serial.print(i);
      Serial.println(" closed");
      Serial.flush();
#endif
      //
      //  Go to the closed position
      //
      doors[i].doorServo->initDoorClosed();
    }
  }
We halen wat gegevens op uit de EEprom, de stand van de deuren zetten we gelijk, maar de positie van de draaischijf bewaren we even voor later.

  //
  //  Set some parameters of the AccelStepper object
  //
  stepper.setMaxSpeed(MAX_SPEED);
  stepper.setAcceleration(ACCELERATION);
  stepper.setSpeed(START_SPEED);

  //
  //  Initialy the 0 position of the stepper motor is not known, so we
  //  have to do a calibration to find the Hall sensor and establish a
  //  known position for the stepper motor.
  //
  TT.calibrate(ttPosition);
Initialiseer het AccelStepper object voor wat betreft, maximum snelheid, acceleratie en start snelheid. Daarna doen we de kalibratie van de draaischijf, zodat het nulpunt weer vastligt. Let op, dit is een blocking operatie, dus de schets zal tijdens het uitvoeren van de kalibratie nergens op reageren.

  //
  //  Create an interface to the CanBus and start the CanBus communication
  //
  canBus = new CanBus(TT_CONTROLLER_ID, nullptr, new Mcp2515(CB_CS_PIN)); // Only a Receiver

  //
  //  Register a callback routine for handling the Accessory(DCC_ACC type) messages
  //
  canBus->setMsgReceiver(DCC_ACC, dccAccReceiver);
  canBus->begin();

#if (DEBUG)
  Serial.println("[setup], Canbus ready");
  Serial.println("[setup] ready");
#endif

}//--(end setup )---
Het is nu tijd om de Canbus zelf te initialiseren, de callback functie te registreren en daarna is de setup klaar.

void loop()
{
  //
  //  Check for Canbus messages
  //
  canBus->heartBeat();
 
  //
  //  Activate the door servos
  //
  for (int i = 0; i < NR_OF_DOORS; i++)
  {
    //
    //  Activate the servo, so it can go to it's next position.
    //
    doors[i].doorServo->heartBeat();
  }

  //
  //  Check for CanBus packets
  //
  canBus->heartBeat();

  //
  //  Keep the turntable running
  //
  TT.heartBeat();
}
De loop functie doet niet zoveel, het enige waar we voor moeten zorgen is dat het Canbus object, de servo objecten en de Turntable object aan de gang blijven, dat doen we door constant hun heartBeat methods aan te roepen.

Hiermee werkt dus de draaischijf. Een klein filmpje van de draaischijf in werking.
https://www.youtube.com/v/CDENbNU9pK8

Groet Meino



Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 21 December 2021, 22:15:30
Die draait netjes en soepel en stil. Mooi gedaan.

Vraagje: dat initiële rondjes draaien om het nulpunt te bepalen, draait 'ie dan sneller? Hoe lang is 'ie daarmee bezig?

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 21 December 2021, 23:30:00
Reinout

Het initiëele rondje draait even snel. Hoe lang duurt het? dat hangt er van af waar de brug van de draaischijf staat.  De brug van de draaischijf heeft een voorkant (waar het bedieningspaneel en het mannetje staat) en een achterkant. De magneet zit aan de achterkant van de brug. De Hallsensor zit tussen spoor 209-- en 201/. Tijdens de kalibratie zal de brug altijd tegen de klok in bewegen. Dus als de achterkant van de brug op spoor 209-- staat (voorkant staat dan op 209/) dan moet hij de grootste afstand afleggen, dat kost ongeveer 150sec. Als de achterkant van de brug op 201/ staat (voorkant staat dan op 201--) dan is de afstand kort en kost het iets meer dan 25sec. Ik heb daar tot nu toe geen problemen mee, want dit gebeurd alleen als ik de baan onder spanning zet (dan worden alle Arduino's in een keer gestart). Daarna nog Koploper starten, en het rijden opstarten. Meestal is dan de draaischijf wel klaar. Maar het is zeker mogelijk om nog het een en ander te optimaliseren.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 22 December 2021, 20:31:02
Duidelijk, bedankt!

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 22 December 2021, 20:54:57
Dag Reinout

je hebt me wel even aan het piekeren gezet, dus toch nog even wat vertimmerd. De kalibratie houdt nu rekening met de vermoedelijk laatste positie en gaat nu klokwijs of antiklokwijs afhankelijk van wat de kortste afstand is. Verder verdubbel ik de maxSpeed van de AccelStepper tijdens de kalibratie. Dat halveert de maximale kalibratie tijd tot rond 75-80sec.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 22 December 2021, 22:09:39
Ik heb dus nog wat vertimmerd. Daarom laat ik toch even zien wat er veranderd is.

Eerst heb ik de kalibratie in de Turntable klasse (KB_Turntable.cpp en KB_Turntable.h) verbouwd. Dat betekend concreet dat de void calibrate() van public naar private is gegaan, en ook het prototype daarvan iets veranderd is. Dus is nu alleen void calibrate(long) publiek. Dat heeft wel tot gevolg dat ik iets moest veranderen in de schets.

Dat heeft zich beperkt tot de functie die door de Canbus wordt aangeroepen als er een accessory commando binnenkomt. In de setup blijft alles zoals het was.

//
//  A function that executes whenever a DCC_ACC message is received from the Canbus
//  It's purpose is to analyze the received event (Signal or turnout) and
//  update the track position of the TT or update the position of the doors.
//
bool calibrated = false;
void dccAccReceiver(unsigned char aMsgLen, DCC_ACC_MSG *msgData)
{
#if (DEBUG)
  Serial.print("[dccAccReceiver] dccId: ");
  Serial.print(msgData->address);
  Serial.print(", dccState: ");
  Serial.println(msgData->direction);
  Serial.flush();
#endif //DEBUG

  unsigned short address = msgData->address;
 
  //
  //  Do we perform a straight(-) or a diverging(/) operation?
  //  For a straight operation enable is true, for a diverging operation
  //  enable is false.
  //
  bool enable = (msgData->direction) ? 0 : 1;

  //
  //  Search for the specified accessory address, when found lookup the
  //  specified position for the straight or diverging operation and start moving
  //  to that target position.
  //
  for (int i = 0; i < nrOfTracks; i++)
  {
    if ( address == trackPositions[i].address )
    {
#if (DEBUG)
      Serial.print("[dccAccReceiver] TT Address: ");
      Serial.print(address, DEC);
#endif //DEBUG
      //
      //  The new position of the TT
      //
      long newTargetPosition = 0;

      if (enable)     // Received a "-" command
      {
#if (DEBUG)
        Serial.print(" (-)");
#endif //DEBUG
        //
        //  Go to the straight position (0 degree)
        //
        newTargetPosition = trackPositions[i].position0;

        //
        //  If the new target address equals zero, a calibration is asked for
        //  In that case we don't save the new position, because the current
        //  position stay active after the calibration.
        //
        if (newTargetPosition != 0)
        {
          EepromControl->setTTposition(i);
          EepromControl->saveData();
        }
      }
      else            // Received a "/" command
      {
#if (DEBUG)
        Serial.print(" (/)");
#endif //DEBUG
        //  Go to the diverging (180 degree) position.
        //
        newTargetPosition = trackPositions[i].position180;

        //
        //  If the new target address equals zero, a calibration is asked for
        //  In that case we don't save the new position, because the current
        //  position stay active after the calibration.
        //
        if (newTargetPosition != 0)
        {
          EepromControl->setTTposition(i+nrOfTracks);
          EepromControl->saveData();
        }
      }

#if (DEBUG)
      Serial.print(" Position: ");
      Serial.println(newTargetPosition, DEC);
      Serial.flush();
#endif //DEBUG

      //
      //  If the choosen position equals 0, then we have to recalibrate
      //  After the calibration we return to the current position, so we have to
      //  get that from Eeprom. Further after a calibration, we block new calibrations
      //  until a regular movement is executed.
      //
      if ((newTargetPosition == 0) && (!calibrated))
      {
          //
          //  Load the EEPROM data for the turnout directions
          //
          EepromControl->loadData();
       
          byte eePromData = EepromControl->getTTposition();

          //
          //  Savety measure, for when the Eeprom contains garbage
          //
          if (eePromData > 2*nrOfTracks)
          {
            eePromData = 1;
          }
         
          //
          //  Get the in EEPROM stored track position, it wil be used after calibration
          //  of the turntable to return to the current position
          //
          if (eePromData < nrOfTracks)
          {
            TT.calibrate(trackPositions[eePromData].position0);
          }
          else
          {
            TT.calibrate(trackPositions[eePromData-nrOfTracks].position180);
          }

          calibrated = true;
      }
      else if (newTargetPosition != 0)
      {
        //
        //  Not a recalibrate, goto the choosen position
        //
        TT.gotoTTPosition(newTargetPosition);

        //
        //  Free to recalibrate
        //
        calibrated = false;
      }

      return;
    }
  }

  //
  //  If we didn't find an address match with a track location
  //  check if a door servo matches the received address
  //
  for (int i = 0; i < NR_OF_DOORS; i++)
  {
    if ( address == doors[i].address )
    {
#if (DEBUG)
      Serial.print("[dccAccReceiver] Door address: ");
      Serial.print(address, DEC);
#endif //DEBUG
      if (enable)
      {
#if (DEBUG)
        Serial.println(" (-)");
        Serial.flush();
#endif //DEBUG
        //
        //  Go to the closed position
        //
        doors[i].doorServo->closeDoor();

        EepromControl->setDoorPosition(i,DoorClosed);
        EepromControl->saveData();
      }
      else
      {
#if (DEBUG)
        Serial.println(" (/)");
        Serial.flush();
#endif //DEBUG
        //
        //  Go to the open position.
        //
        doors[i].doorServo->openDoor();

        EepromControl->setDoorPosition(i,DoorOpen);
        EepromControl->saveData();
      }

      return;
    }
  }

#if (DEBUG)
  Serial.print("[dccAccReceiver] Unknown address: ");
  Serial.println(address, DEC);
  Serial.flush();
#endif //DEBUG
}
Er zijn nu twee zaken gewijzigd.

Dat was het verhaal over de draaischijf controller.

Overigens als er belangstelling is dat ik ook de interne werking van de Turntable klasse zelf bespreek, laat me het weten. Dat moet dan wel in een aantal seperate posts gebeuren omdat de source van deze klasse te groot is voor een enkel posting.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 09 May 2022, 15:58:20
Naar aanleiding van een vraagje over het aansturen van wissels met DCC en een schakelaar, realiseerde ik me dat ik een soortgelijk probleem had aangepakt. Een geruime tijd geleden heb ik een systeem gebouwd voor het aansturen van een paneel. De tableaux-controller. Dit systeem is ook ontstaan naar aanleiding van mijn poging om de spaghetti aan kabels onder de baan te reduceren, verder wilde ik wat extra faciliteiten via het paneel mogelijk maken.

Oorspronkelijk had ik een simpel paneel met ledjes die de stand van de door Koploper gecontroleerde wissels aan gaven, en voor de overige wissels een simpele schakelaar die direct de betreffende servo aanstuurde. Deze laatste wissels zijn niet gedefinieerd in Koploper en deel van het emplacement wat alleen via de hand bedient kan worden.

Dat zag er toen zo uit..
(https://images.beneluxspoor.net/bnls/20201108_182849_DSC_1456.jpg) (https://images.beneluxspoor.net/bnls/20201108_182849_DSC_1456.jpg)

De spaghetti aan kabels.
(https://images.beneluxspoor.net/bnls/20201103_171239_DSC_1343.jpg) (https://images.beneluxspoor.net/bnls/20201103_171239_DSC_1343.jpg)
De ledjes voor de wisselstand waren rechtstreeks via een diode netwerkje aangesloten op de aandrijving van de wissels en de polariteit daarvan bepaalde welk led aan ging.

Maar zoals ik al zei, ik wilde van de kabels af en er moest het een en ander aan functionaliteit toegevoegd worden.

Dus een Arduino opgetuigt met een Canbus kaartje, een schets geschreven en aangesloten.
Toen zag het er als volgt uit..
(https://images.beneluxspoor.net/bnls/20201119_155934_DSC_1468.jpg) (https://images.beneluxspoor.net/bnls/20201119_155934_DSC_1468.jpg)

(https://images.beneluxspoor.net/bnls/20201119_155438_DSC_1466.jpg) (https://images.beneluxspoor.net/bnls/20201119_155438_DSC_1466.jpg)
Draaiknop voor de draaischijf, extra drukknoppen voor het bezet maken van blokken en handmatig wisselstanden aanpassen.

Een uitgebreidere beschrijving van de werking en mogelijkheden had ik ooit al eens hier in het Kranenberg (https://forum.beneluxspoor.net/index.php?topic=77057.msg3222169004#msg3222169004) draadje behandeld.

Ik ga het nu in het vervolg even hebben over de schets die dit geheel aan knoppen en ledjes beheert.

Groet Meino


Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 13 May 2022, 22:31:34
Voor de Tableau-Controller heb ik dit keer een Arduino Mega gebruikt. Daar had ik er nog een aantal van liggen, en alhoewel de schets betrekkelijk klein is, ten opzichte van het beschikbare geheugen, was het grote aantal pinnen wel erg makkelijk.
Dit systeem is verder weer uitgerust met twee Mcp2515 Canbus kaartjes omdat het berichten moet ontvangen, maar ook zelf produceert.

Dit systeem heeft op dit moment de volgende taken te verzorgen:

Ik begin met de wissel indicatie leds.
(https://images.beneluxspoor.net/bnls/20220512-214317-DSC-8806-627eb50a37038.jpg) (https://images.beneluxspoor.net/bnls/20220512-214317-DSC-8806-627eb50a37038.jpg)

Een opmerking, de gebruikte leds hebben een standaard verbruik van 20mA. Maar er zijn 12 wissels, dus 24 leds. Gelukkig brand er altijd per wissel 1, maar het gebruik van die 12 leds is dan wel 240mA totaal, wat meer is dan de Arduino in totaal op zijn pinnen kan leveren. Verder moeten er nog een stel andere leds aangesloten worden. Dus heb ik de voorloop weerstand per led zodanig gekozen dat het verbruik per led gereduceerd is tot 5mA. Het blijkt dat ook bij deze lagere stroomsterkte meer dan voldoende helderheid geeft. Hierdoor kan ik alle leds op het paneel rechtstreeks door de pinnen aansturen.

Ok, tijd voor wat C++ code.
//
//  The occupance detectors for the switches start on bank 4, each bank has 16 occupancies, so a single
//  bank is enough for the 12 turnouts in use.
//  The push buttons used for controlling these switches generate Occupancy detector events
//  starting on bank 6
//
#define S88_SWITCH_BANK 4
#define S88_SWITCHBUTTON_BANK 6

We maken gebruik van het feit dat de wissel-controller de stand van een wissel door middel van een bezetmelding publiceert op de Canbus. Deze bezetmeldingen hebben momenteel als adres 4.01-4.12 (S88 bank 4)

//
//  Table definitions for the turnout/switch leds
//
const int nrOfTurnouts = 12;

typedef struct {
  int s88Bank;
  int s88Nr;
  int pinStraight;
  int pinDiverting;
} tolTable;

tolTable turnoutLed[nrOfTurnouts] = {
  { S88_SWITCH_BANK, 1, 22, 23},
  { S88_SWITCH_BANK, 2, 25, 24},
  { S88_SWITCH_BANK, 3, 26, 27},
  { S88_SWITCH_BANK, 4, 29, 28},
  { S88_SWITCH_BANK, 5, 30, 31},
  { S88_SWITCH_BANK, 6, 33, 32},
  { S88_SWITCH_BANK, 7, 35, 34},
  { S88_SWITCH_BANK, 8, 36, 37},
  { S88_SWITCH_BANK, 9, 39, 38},
  { S88_SWITCH_BANK,10, 40, 41},
  { S88_SWITCH_BANK,11, 43, 42},
  { S88_SWITCH_BANK,12, 44, 45}
};
Voor ieder wissel zijn twee leds gedefinieerd in een simpele array. Ieder element uit de array bevat de S88 bank, S88 nr, de pin van de led voor het rechte pad en de pin van de led voor het afbuigend pad.

In de setup() functie moeten nu een paar zaken geregeld worden.

  //
  //  Setup and start the CanBus communication
  //
  canBus = new CanBus(PANEL_CONTROLLER_ID, new Mcp2515(49), new Mcp2515(53)); // Sender + Receiver
  canBus->setMsgReceiver(OCC_DT, occDtReceiver);
  canBus->setMsgReceiver(DCC_ACC, dccAccReceiver);
  canBus->begin();
De Canbus wordt opgezet en we registreren twee functies, de occDtReceiver(..) functie, die ontvangt alle bezetmeldingen van de Canbus en gaat de wisselleds aan of uit zetten. De dccAccReceiver(..) functie ontvangt alle DCC Accessory berichten. Deze worden gebruikt als een blok handmatig bezet gezet wordt.

  //
  //  Activate the turnout leds
  //
  for (int i = 0; i < nrOfTurnouts; i++)
  {
      //
      //  Switch both leds off
      //
      pinMode(turnoutLed[i].pinStraight,OUTPUT);
      digitalWrite(turnoutLed[i].pinStraight, LOW);
      pinMode(turnoutLed[i].pinDiverting, OUTPUT);
      digitalWrite(turnoutLed[i].pinDiverting, LOW);
  }
De leds worden geactiveerd. Tot zover de activiteiten in setup()

Het echte werk gebeurd in de callback routine die door de Canbus aangeroepen wordt als er een bezetmelding bericht binnen komt.

void occDtReceiver(unsigned char aMsgLen, OCC_DT_MSG *msgData)

  //
  //  Find the proper turnout detector with the matching address
  //  and sets its new direction based on the enable field from the
  //  received dcc command
  //
  for (int i = 0; i < nrOfTurnouts; i++)
  {
    if ((turnoutLed[i].s88Nr+((turnoutLed[i].s88Bank - 1) * 16)-1) == msgData->address)
    {
      //
      //  Switch both leds off
      //
      digitalWrite(turnoutLed[i].pinStraight, LOW);
      digitalWrite(turnoutLed[i].pinDiverting, LOW);

      if (msgData->state == 0)
      {
        //
        //  Set the straight led
        //
        digitalWrite(turnoutLed[i].pinStraight, HIGH);
      }
      else
      {
        //
        //  Set the diverging direction
        //
        digitalWrite(turnoutLed[i].pinDiverting, HIGH);

      }

      //
      //  End the for loop
      //
      return;
    }
  }
}
De bezet melding wordt geanaliseerd, en het adres van de bezetmelder wordt vergeleken met de adressen van de led tabel. Als er een match is, dan wordt de stand van het wissel in de bijbehorende leds uit gewerkt.

Tot zover het afhandelen van de leds voor de wisselstanden.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 16 May 2022, 00:02:32
Het aansturen van de Leds is een vrij simpele toepassing en het beperken van de bedrading was misschien niet voldoende reden geweest om dit systeem te introduceren. Maar ik wilde ook op het emplacement handmatig (met een lokmaus R3) kunnen rijden en rangeren terwijl op de rest van de baan alles onder besturing van Koploper (met automatisch rijden) door gaat. Om te kunnen rangeren moet het mogelijk zijn om een aantal wissels die normaal door Koploper gezet worden, handmatig de stand te kunnen aanpassen. Daarnaast moet het ook mogelijk zijn om een aantal blokken in de hoofdbaan op slot te zetten (handmatig bezet) zodat Koploper stopt met rijden naar die blokken. Dat is allemaal prima te doen via het scherm op de PC waar Koploper op draait, maar ik wilde dat via het Tableau doen met drukknoppen. Het idee was om via een knop een bezetmelder aan te zetten, Koploper moet dan via een speciale actie (die reageert op die bezetmelding) de wissel omzetten of het blok handmatig bezet maken.

(https://images.beneluxspoor.net/bnls/20220512-214347-DSC-8807-6281743876613.jpg) (https://images.beneluxspoor.net/bnls/20220512-214347-DSC-8807-6281743876613.jpg)
De knoppen voor de wissels en een knop voor het handmatig bezetten van een blok, de rode led geeft aan dat het betreffend blok handmatig bezet is.

Een klein probleempje is dat voor de goede werking van deze acties in Koploper, de bezetmelding actief moet blijven, zolang de actie uitgevoerd moet worden. De betreffende knoppen maken alleen contact zolang je ze ingedrukt houdt. Daarom een kleine C++ klasse gemaakt die dit oplost. Een object uit deze klasse emuleert een knop die bij de eerste keer indrukken contact maakt en dat vast houdt, om vervolgens bij de volgende keer indrukken dat contact weer te verbreken.

//
//  Class for a push button that controls a switch by generating an
//  occupance detector event when it is pushed.
//
class SwitchButton {
  private:
    bool state = false;          // initial the button is not pushed
   
    int buttonPin;
    bool pushed = false;
    bool stateSet = false;
    bool bounceState = false;
    unsigned long bounceTime;    // Last time a state switch happened
    int bounceWait = 4 ;         // Ignore bounce for 4mS
   
  public:
    //
    //  Create a new instance of a SwitchButton
    //
    SwitchButton(int aButtonPin);
    bool getState();
 
    //
    //  Attach the digital pin to the push button
    //
    void attach();
   
    //
    //  Check the state of the buttonPin. If it stays long enough
    //  send out an occupance detector event.
    //
    void heartBeat();
};
Het .h bestand van deze klasse

//
//  Create a new instance of a SwitchButton
//
SwitchButton::SwitchButton(int aButtonPin)
{
    buttonPin = aButtonPin;

    pushed = false; // If pushed, it asks for diverging.
    state = false;
    bounceState = false;
}

//
//  Return the current state of the SwitchButton
//
bool SwitchButton::getState()
{
    return state;
}

//
//  Attach the digital pin to the push button
//
void SwitchButton::attach()
{
    pinMode(buttonPin, INPUT_PULLUP);
}

//
//  Check the state of the buttonPin. If it stays long enough
//  send out an occupance detector event.
//
void SwitchButton::heartBeat()
{
    if (!pushed)
    {
        //
        //  The button state is not pushed, so wait until the pin goes HIGH
        //  signaling a pushed button.
        //
        if (digitalRead(buttonPin) == HIGH)
        {
            //
            //  Wait a small amount of time, this to make sure that the
            //  state of the pin is constant
            //
            if (!bounceState)
            {
                bounceState = true;
                bounceTime = millis();
            } else if (((millis() - bounceTime) > bounceWait))
            {
                pushed = true; // Flip the state
                bounceState = false; // Reset the bounce state for the next time
            }
        }
    }
    else
    {
        //
        //  The button state is pushed, so wait until the pin goes LOW
        //  signaling a released button.
        //
        if (digitalRead(buttonPin) == LOW)
        {
            //
            //  Wait a small amount of time, this to make sure that the
            //  state of the pin is constant
            //
            if (!bounceState)
            {
                bounceState = true;
                bounceTime = millis();
            } else if (((millis() - bounceTime) > bounceWait))
            {
                pushed = false; // Reset the pushed state
                stateSet = false;
                bounceState = false; // Reset the bounce state for the next time
            }     
        }
    }
   
    //
    //  When the button was pushed and this is the first time we detect that,
    //  flip the state
    //
    if ((pushed) && (!stateSet))
    {
        stateSet = true;    // The state must be set once, so this prevent
                            // that their is a constant flipping of the state
       
        //
        //  Flip the state
        //
        if (state == true)
        {
            state = false;
        }
        else
        {
            state = true;
        }   
    }
}
De C++ klasse zelf.
Het meeste is recht toe recht aan, een creator, een getter voor de state, en een method om de drukknop met zijn Arduino pin te verbinden. Het echte werk gebeurd in de heartbeat() method. Daar wordt de pin gemonitored en als die hoog wordt (en lang genoeg blijft, i.v.m. bounce effecten) wordt de state geinverteerd (als hij aan was dan gaat hij uit, als de state uit was dan gaat hij aan). Als de pin laag gaat en blijft, dan gebeurt er niet zoveel met de state, alleen wordt het drukknop object weer klaar gezet voor de volgende keer dat de knop wordt ingedrukt.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 16 May 2022, 12:05:00
We hebben het even gehad over een klasse die wat functionaliteit van een bepaald soort schakelaar nabootst, maar nu hoe gebruiken we dat in de schets.

Eerst even de definities
//
//  Table containing all definitions for pushbuttons on the panel
//
const int nrOfButtons = 10;

typedef struct {
  SwitchButton button;
  bool currentState;
  int s88Bank;
  int s88Nr;
} switchTable;

switchTable buttonTable[nrOfButtons] = {
  {SwitchButton(A2), false, S88_SWITCHBUTTON_BANK, 1}, // Button for "Handmatige bezet melding"
  {SwitchButton(A3), false, S88_SWITCHBUTTON_BANK, 2}, // Button for "Handmatige bezet melding"
  {SwitchButton(A4), false, S88_SWITCHBUTTON_BANK, 3}, // Button for "Handmatige bezet melding"
  {SwitchButton(A5), false, S88_SWITCHBUTTON_BANK, 4}, // Button for "Omzetten wissel 4"
  {SwitchButton(A6), false, S88_SWITCHBUTTON_BANK, 5}, // Button for "Omzetten wissel 5"
  {SwitchButton(A7), false, S88_SWITCHBUTTON_BANK, 6}, // Button for "Omzetten wissel 6"
  {SwitchButton(A8), false, S88_SWITCHBUTTON_BANK, 7}, // Button for "Omzetten wissel 7"
  {SwitchButton(A9), false, S88_SWITCHBUTTON_BANK, 8}, // Button for "Omzetten wissel 8"
  {SwitchButton(A10),false, S88_SWITCHBUTTON_BANK, 9}, // Button for "Omzetten wissel 9"
  {SwitchButton(A11),false, S88_SWITCHBUTTON_BANK, 10} // Button for "Handmatige bezet melding"
};
Een simpele tabel met voor ieder drukknop een SwitchButton object, de bijbehorende S88 beztmeld adres en een status waarmee we bepalen of de status van het SwitchButton object is veranderd.

Wat initialisatie code voor in de setup() method.

  //
  //  Activate the turnout push buttons
  //
  for (int i = 0; i < nrOfTurnouts; i++)
  {
    (buttonTable[i].button).attach();
  }
De SwitchButtons worden met hun Arduino pinnen verbonden, hun huidige status is niet belangrijk omdat die initieel altijd uit staat.

//
  //  Check the turnout push buttons, and if they were pushed, send an occupancy event
  //
  for (int i = 0; i < nrOfButtons; i++)
  {
    (buttonTable[i].button).heartBeat();         // The buttons have to monitor their pins

    //
    //  If the state of the button has changed (it differs from the last stored state)
    //  put an occupance message on the Canbus
    //
    if (buttonTable[i].currentState != (buttonTable[i].button).getState())
    {
      //
      //  The state has changed, so store the new state
      //
      buttonTable[i].currentState = (buttonTable[i].button).getState();   
      if ((buttonTable[i].button).getState() == true)
      {
        occDtHandler(buttonTable[i].s88Bank, buttonTable[i].s88Nr, true);
      }
      else
      {
        occDtHandler(buttonTable[i].s88Bank, buttonTable[i].s88Nr, false);
      }     
    }
  }
In de loop() method zorgen we dat de SwitchButtons actief hun pin monitoren via de heartBeat() method en daarna checken we of de status van de switchbutton is veranderd. Afhankelijk van de nieuwe staat sturen we een Bezetmelder bericht naar de Canbus om de relevante bezetmelder aan of uit te zetten.

Dat sturen van een Bezetmelder bericht doen we in de volgende functie.

void occDtHandler(int aS88Bank, int aS88BankNr, bool aDirection)
{
  //
  //  Send the new state to the S88 Interface
  //
  OCC_DT_MSG buf;
  //
  //  At this moment we have no more the 12 turnouts, so don't bother about the S88 bank
  //
  buf.address = ((aS88Bank - 1) * 16) + (aS88BankNr - 1);
  if (aDirection)
    buf.state = 1;
  else
    buf.state = 0;

  canBus->sendMsg(OCC_DT, sizeof(OCC_DT_MSG), &buf);
}

To zover het afhandelen van de drukknoppen op het tableau.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 16 May 2022, 22:58:22
Tot nu toe is het alleen gegaan over de knoppen en hoe die door de Arduino schets worden afgehandeld. Maar de Arduino schets activeert alleen een bezetmelding, die dan door Koploper wordt verwerkt tot een specifieke actie. Alhoewel het een beetje buiten de scope van dit Arduino gerelateerde draadje ligt, wil ik het er toch even over hebben. Binnen Koploper zijn drie Speciale Acties gedefinieerd.

De Speciale Actie "Wissel rechtdoor zetten" wordt actief als "Alle bezetmelders bezet/Logische acties waar". De trigger voor deze actie is als de bezetmelder door een drukknop aan gezet wordt. Er zijn geen logische acties gedefinieerd. De actie die dan uitgevoerd wordt is "Adres recht(waar) of afbuigend(niet waar)". Het adres is van de specifieke wissel.

De Speciale Actie "Blok handmatig bezetten" wordt actief als "Alle bezetmelders bezet/Logische acties waar". De trigger voor deze actie is als de bezetmelder door een drukknop aan gezet wordt. Er zijn geen logische acties gedefinieerd. De actie die dan uitgevoerd wordt is "Blok handmatig geblokkeerd(zolang waar)".

De Speciale Actie "Led aansturing blok" wordt actief als "Alle bezetmelders bezet/Logische acties waar". De trigger voor deze actie is als de bezetmelder voor dit blok aan gezet is en als de volgende condities (logische acties) waar zijn, "Blok is niet bezet door trein(alle locomotieven)" en "Blok is niet gereserveerd". De actie die dan wordt uitgevoerd wordt is "Adres afbuigend(waar) of recht(niet waar).
De combinatie van condities, bezetmelder en logische acties, heeft tot gevolg dat het DCC accessory comando pas verstuurd wordt op het moment dat het blok beschikbaar komt (dit is niet helemaal waar, maar goed genoeg voor mij. Het probleem is dat binnen Koploper de conditie "Blok is handmatig bezet" niet beschikbaar is).

Tijd voor wat code.

//
//  Definitions for the table with block leds. Leds that wil signal when a block is
//  reserved and may be used for switching operations.
//
const int nrOfBlokLeds = 4;

typedef struct {
  int dccId;
  int ledPin;
} BlokLed;

BlokLed blokLeds[nrOfBlokLeds] = {
  {101, A12},
  {102, A13},
  {103, A14},
  {110, A15}
};
Er zijn vier blokken die we kunnen reserveren voor het rangeren, dus ook vier leds. Ieder led heeft zijn eigen DCC accessory adres in de tabel. Dat zijn ook de adressen die door de Speciale Acties aangestuurd worden.

In de setup() method worden de Arduino pinnen waar de leds mee verbonden zijn actief gemaakt. Ook hier is de voorloopweerstand van ieder led zodanig gedimensioneerd dat iedere led niet meer dan 5mA verbruikt.

  //
  //  Activate the blok leds
  //
  for (int i = 0; i < nrOfBlokLeds; i++)
  {
      //
      //  Switch led off
      //
      pinMode(blokLeds[i].ledPin,OUTPUT);
      digitalWrite(blokLeds[i].ledPin, LOW);
  }

Omdat de Speciale Actie een DCC accessory commando stuurt, wordt het echte werk gedaan in een callback routine die we bij de Canbus hebben gereserveerd. Zie het volgende stukje code.

  //
  //  Setup and start the CanBus communication
  //
  canBus = new CanBus(PANEL_CONTROLLER_ID, new Mcp2515(49), new Mcp2515(53)); // Sender + Receiver
  canBus->setMsgReceiver(OCC_DT, occDtReceiver);
  canBus->setMsgReceiver(DCC_ACC, dccAccReceiver);
  canBus->begin();

Deze callback routine is dccAccReceiver().

//
//  A function that executes whenever a message is received from the Canbus
//  It's purpose is to analyze the received event and set the led for this address
//
void dccAccReceiver(unsigned char aMsgLen, DCC_ACC_MSG *msgData)
{
  unsigned short dccId = msgData->address;
  byte dccState = msgData->direction;

  //
  //  Find the led with the received address
  //
  for (int i=0; i<nrOfBlokLeds; i++)
  {
    if (blokLeds[i].dccId == dccId)
    {
      if (dccState == 0)
      {
        digitalWrite(blokLeds[i].ledPin, LOW);
      }
      else
      {
        digitalWrite(blokLeds[i].ledPin, HIGH);
      }

      return;
    }
  }
}

Op basis van het ontvangen DCC adres wordt de led geselecteerd en afhankelijk van de aansturing (rechtdoor/afbuigend) wordt de LED aan of uit gezet.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 18 May 2022, 22:48:18
Er is nog een stuk functionaliteit dat nog niet besproken is. Ik had ooit een draaischijf gebouwd met een stappenmotor en een Arduino. Die kan ik aan sturen met DCC accessory commando's, ieder spoor aansluiting met een eigen DCC accessory adres. Die wilde ik ook via een draaiknop op het tableau kunnen aansturen. Daartoe een 24polige draaiknop aangeschaft. Dat was voldoende voor de 18 posisties.

(https://images.beneluxspoor.net/bnls/20220512-214447-DSC-8809-628551d781992.jpg) (https://images.beneluxspoor.net/bnls/20220512-214447-DSC-8809-628551d781992.jpg)

(https://images.beneluxspoor.net/bnls/20220512-214603-DSC-8810-628551edbb277.jpg) (https://images.beneluxspoor.net/bnls/20220512-214603-DSC-8810-628551edbb277.jpg)

Naast deze draaiknop heb ik ook nog twee schakelaartjes waarmee ik de deuren van het lokdepot open en dicht kan doen. Aangezien ik die op de zelfde manier afhandel als de draaischakelaar behandel ik die tegelijkertijd.  Ook voor deze schakelaars heb ik een kleine C++ klasse geschreven die
het gedrag van deze schakelaars verwerkt.

De .h definitie.
class TTPositionSwitch {
  enum SWITCHSTATE {
    PIN_HIGH = 1,
    GOING_HIGH = 2,
    PIN_LOW = 3,
    GOING_LOW = 4
  };
 
  private:
    int ttPin;
    bool reverseDirection;
    SWITCHSTATE switchState;
    bool state;

    bool bounceState = false;
    unsigned long bounceTime;   // Last time a Pin state switched, used to handle bounce
    int bounceWait = 1000 ;       // Let the state changes settle for for 1 sec
     
  public:
    TTPositionSwitch(int aTTPin, bool aReverseDirection);
    bool getReverseDirection();

    bool getState();

    //
    //  Attach the digital pin to the push button
    //
    void attach();
   
    //
    //  Check the state of the buttonPin. If it stays long enough
    //  send out an occupance detector event.
    //
    void heartBeat();
};


De C++ implementatie van de klasse.
TTPositionSwitch::TTPositionSwitch(int aTTPin, bool aReverseDirection)
{
    ttPin = aTTPin;
    reverseDirection = aReverseDirection;
    switchState = PIN_HIGH;
    state = false;
}

bool TTPositionSwitch::getReverseDirection()
{
    return reverseDirection;
}

//
//  Return it's current state
//
bool TTPositionSwitch::getState()
{
    return state;
}
De creator en een aantal getter methods. Een opmerking, het atribuut reverseDirection heeft te maken met het DCC commando dat gesturrd wordt om naar een positie te gaan. DCC accessory commandos kan rechtdoor of afbuigend schakelen. Hier maken we gebruik van bij de draaischijf, voor de 18 aansluiting zijn 9 DCC adressen in gebruik, waarbij door een afbuigend commando te geven de draaischijfbrug naar de andere kant gaat.

//
//  Attach the digital pin to the push button
//
void TTPositionSwitch::attach()
{
    pinMode(ttPin, INPUT_PULLUP);
   
    //
    //  Initialize the current state of the switch. Don't wait for bouncing,
    //  because it is save to asume that the switch is currently in a
    //  stable position.
    //
    if (digitalRead(ttPin) == LOW)
    {
        state = true;
        switchState = PIN_LOW;
    }
    else
    {
        state = false;
        switchState = PIN_HIGH;
    }
}
Deze schakelaars hebben bij het starten altijd een bepaalde stand. Dus tijdens de attach, wordt de pin geactiveerd, maar tevens wordt gelijk bepaald of de schakelaar aan of uit staat.

//
//  Check the state of the buttonPin. If it stays long enough
//  send out an occupance detector event.
//

void TTPositionSwitch::heartBeat()
{
    //
    //  The switch is a rotating switch, so always one position is engaged,
    //  so only when a pin goes HIGH we have to do something, send a DCC_ACC message
    //
    switch (switchState)
    {
        case PIN_HIGH:
            if (digitalRead(ttPin) == LOW)
            {
                switchState = GOING_LOW;
                bounceTime = millis();
            }
            break;

        case GOING_HIGH:
            if ((millis() - bounceTime) > bounceWait)
            {
                if (digitalRead(ttPin) == HIGH)
                {
                    state = false;
                    switchState = PIN_HIGH;
                } else
                {
                    switchState = PIN_LOW;
                }
            }
            break;

        case PIN_LOW:
            if (digitalRead(ttPin) == HIGH)
            {
                switchState = GOING_HIGH;
                bounceTime = millis();
            }
            break;

        case GOING_LOW:
            if ((millis() - bounceTime) > bounceWait)
            {
                if (digitalRead(ttPin) == LOW)
                {
                    //
                    // If the state switches from LOW to HIGH, report it
                    //
                    state = true;
                    switchState = PIN_LOW;
                } else
                {
                    //
                    //  After the bounce time, the state of the pin has returned to HIGH
                    //  so reset the ttSwitchState
                    //
                    switchState = PIN_HIGH;
                }
            }
            break;
    }
}
De heartBeat() method (die constant aangeroepen moet worden) monitored de pin, en verandert, indien nodig, de staat van de schakelaar.

Groet Meino

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 20 May 2022, 00:09:47
We hebben weer een aantal tabellen in de Arduino schets.

//
//  Table containg all definitions for the rotary turntable switch
//
const int nrOfTTPositions = 18;

typedef struct {
  int dccId;
  bool currentPosition;
  TTPositionSwitch ttPosition;
} TurnoutSwitchTable;

TurnoutSwitchTable TTPositions[nrOfTTPositions] = {
  {201, false, TTPositionSwitch(13, false)},
  {202, false, TTPositionSwitch(12, false)},
  {203, false, TTPositionSwitch(11, false)},
  {204, false, TTPositionSwitch(10, false)},
  {205, false, TTPositionSwitch( 9, false)},
  {206, false, TTPositionSwitch( 8, false)},
  {207, false, TTPositionSwitch( 7, false)},
  {208, false, TTPositionSwitch( 6, false)},
  {207, false, TTPositionSwitch( 5, false)},
  {201, false, TTPositionSwitch( 4, true)},
  {202, false, TTPositionSwitch( 3, true)},
  {203, false, TTPositionSwitch( 2, true)},
  {204, false, TTPositionSwitch(14, true)},
  {205, false, TTPositionSwitch(15, true)},
  {206, false, TTPositionSwitch(16, true)},
  {207, false, TTPositionSwitch(17, true)},
  {208, false, TTPositionSwitch(18, true)},
  {209, false, TTPositionSwitch(19, true)},
};

//
//  Table with the definition for the micro switches controlling
//  the doors of the locoshed.
//
const int nrOfDoors = 2;

TurnoutSwitchTable doorSwitches[nrOfDoors] = {
  {108, false, TTPositionSwitch(A0, false)},
  {109, false, TTPositionSwitch(A1, false)}
};

Een tabel, TTPositions, bevat 18 TTPositionSwitch objecten en de andere tabel, doorSwitches, bevat de 2 objecten welke de schakelaars voor de deuren representeren. Naast de objecten, bevatten de rijen in de tabellen ook het DCC accessory adres waar de DCC commandos naar toe gestuurd worden. Een ander klein verschil is dat in de TTPositions tabel op een moment slechts een van de 18 TTPositionSwitch objecten aan kan staan (op de positie waar de rotary switch op dat moment gepositioneerd is) terwijl de 2 deurschakelaar beide onafhankelijk van elkaar aan of uit kunnen staan.

In de setup() functie worden de TTPositionSwitch objecten met hun pinnen verbonden en slaan we gelijk de staat van de schakelaar op. Die staat wordt ook gelijk doorgestuurd naar de Draaischijf Controller.

  //
  //  Activate the TT position switches and store the current state
  //
  for (int i=0; i<nrOfTTPositions; i++)
  {
    (TTPositions[i].ttPosition).attach();
    TTPositions[i].currentPosition = (TTPositions[i].ttPosition).getState();

    //
    //  Checkt the state, and if active inform the TT controller
    //
    if ((TTPositions[i].ttPosition).getState())
    {
      //
      //  Send the initial position to the TT controller.
      //
      dccAccHandler(TTPositions[i].dccId, (TTPositions[i].ttPosition).getReverseDirection());
    }
  }

  //
  //  Activate the door switches and store the current state
  //
  for (int i=0; i<nrOfTTPositions; i++)
  {
    (doorSwitches[i].ttPosition).attach();
    doorSwitches[i].currentPosition = (doorSwitches[i].ttPosition).getState();
   
    //
    // Send the initial state to the TT controller.
    //
    dccAccHandler(doorSwitches[i].dccId, !(doorSwitches[i].ttPosition).getState());
  }

In de loop() functie wordt dan vervolgens deze TTPositionSwitches constant gemonitored en als we constateren dat een staat is gewijzigd wordt de Draaischijf Controller daarvan d.m.v een bericht verwittigd.

  for (int i = 0; i < nrOfTTPositions; i++)
  {
    //
    //  Force an update of the switch
    //
    (TTPositions[i].ttPosition).heartBeat();
   
    //
    //  If this Position is changed, send the proper DCC accessory message
    //
    if (TTPositions[i].currentPosition != (TTPositions[i].ttPosition).getState())
    {
      TTPositions[i].currentPosition = (TTPositions[i].ttPosition).getState();

      if ((TTPositions[i].ttPosition).getState())
      {
        //
        // The state of the rotational switch has changed, so send the new position
        // to the TT controller.
        //
        dccAccHandler(TTPositions[i].dccId, (TTPositions[i].ttPosition).getReverseDirection());
      }
    }
  }
Dit is de afhandeling van de rotary switch. In dit geval wordt alleen als de switch aangaat een bericht verstuurd omdat de Draaischijf daardoor een opdracht krijgt om naar een nieuwe positie te gaan. Het uitgaan van een switch is niet relevant  voor de draaischijf.

  for (int i = 0; i < nrOfDoors; i++)
  {
    //
    //  Force an update of the switch
    //
    (doorSwitches[i].ttPosition).heartBeat();

    //
    //  If this Position is changed, send the proper DCC accessory message
    //
    if (doorSwitches[i].currentPosition != (doorSwitches[i].ttPosition).getState())
    {
      doorSwitches[i].currentPosition = (doorSwitches[i].ttPosition).getState();
     
      //
      // The state of the door switch has changed, so send the new position
      // to the TT controller.
      //
      dccAccHandler(doorSwitches[i].dccId, !(doorSwitches[i].ttPosition).getState());
    }
  }
Bij de schakelaar voor de deuren worden alle staat wijzigingen doorgestuurd, een deur kan open gaan, maar ook dicht.

De functie dccAccHandler(..) is verantwoordelijk voor het maken van het bericht en het door geven aan de Canbus voor publicatie.

void dccAccHandler(int dccId, bool aDirection)
{
  //
  //  Create the Accessory message
  //
  DCC_ACC_MSG buf;
 
  buf.address = dccId;
 
  if (!aDirection)
  {
    buf.direction = 0;
  }
  else
  {
    buf.direction = 1;
  }

  //
  //  Send the received accessory command
  //
  canBus->sendMsg(DCC_ACC, sizeof(DCC_ACC_MSG), &buf);
}

Tot zover de huidige TableauController. Er zijn nog wel een aantal zaken die in de toekomst als uitbreiding gemaakt gaan worden, want ook de seinen in het station moeten nog op de een of andere manier via het tableau aan gestuurd worden. Verder wil ik in de toekomst nog een paar rangeerpaalseinen (rouwbrief) toevoegen, die zullen dan ook via het tableau gecontroleerd moeten worden.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 July 2023, 18:29:33
Voor een uitbreiding zijn weer een aantal extra wissels geinstalleerd. Die heb ik nu met standaard servo's aangedreven. Daarvoor heb ik een nieuwe klasse geschreven om deze aan te drijven. Daarvoor zijn de .h en .cpp bestanden uitgebreid. Kortom even voor de liefhebbers.

KB_Turnout.h
/*
  A Library to control turnouts (switches)

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

 */
#include <Arduino.h>
#include "KB_Servo.h"

#define TO_DEBUG 0

#define TURNOUT_STRAIGHT  false
#define TURNOUT_DIVERGING true

//
//  Type definition for the call-back routine which will handle a state change
//
typedef void (*toStateHandler_t)(int toDccAddress, bool toDirection);


//
//   The abstract base class for handling turnouts
//
class Turnout
{
protected:
    int toPin; //  Arduino pin connected to the relay
    bool targetDirection; //  Straight or Diverging
    bool currentDirection;
    int dccAddress; //  DCC address used for this turnout
   
    toStateHandler_t toStateHandler = nullptr;

    void initTo(int newDccAddress, int newToPin);

public:
    void setTargetDirection(bool newDirection);
    bool getCurrentDirection();
    int  getDccAddress();
    void registerToStateHandler(toStateHandler_t toStateHandler);
   
    virtual void attach(bool initialDirection) = 0;
    virtual void heartBeat() = 0;
};

//
//  Class for turnouts controlled by a relay.
//
class RelayTurnout : public Turnout
{
public:
    RelayTurnout(int newDccAddress, int newToPin);
   
    void attach(bool initialDirection);
    void heartBeat();   
};

//
//  Class for turnouts controlled by a servo.
//
class ServoTurnout : public Turnout
{
private:
    int straightPosition;
    int divergingPosition;
   
    int currentPosition;
   
    int stepDelay;
   
    ArmServo* turnoutServo;
   
    bool servoRunning = false;
   
public:
    ServoTurnout(int newDccAddress, int newToPin, int newStraightPosition,
                                int newDivergingPosition, int newStepDelay);
   
    void attach(bool initialDirection);
    void heartBeat();   
};

KB_Turnout.cpp
/*
  A Library to control turnouts (switches)

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

 */
#include <KB_Turnout.h>

void Turnout::initTo(int newDccAddress, int newRelayPin)
{
    toPin = newRelayPin;
    dccAddress = newDccAddress;
    targetDirection = TURNOUT_STRAIGHT;
    currentDirection = TURNOUT_STRAIGHT;
}

bool Turnout::getCurrentDirection()
{
    return currentDirection;
}

int Turnout::getDccAddress()
{
    return dccAddress;
}

void Turnout::registerToStateHandler(toStateHandler_t aToStateHandler)
{
    toStateHandler = aToStateHandler;
}

void Turnout::setTargetDirection(bool newDirection)
{
    //
    //  When the new direction differs from the current direction, update the turnout direction
    //  and set the relayPin according to the new direction.
    //
    if (targetDirection != newDirection)
    {
        targetDirection = newDirection; // Set target direction
       
#if (TO_DEBUG)
        Serial.print("[RelayTurnout::setTurnoutDirection] DCC address: ");
        Serial.print(dccAddress);
        Serial.print(", new direction: ");
        if (targetDirection == TURNOUT_STRAIGHT)
        {
            Serial.println(" -");
        }
        else
        {
            Serial.println(" /");
        }
        Serial.flush();
#endif           
    }
}

RelayTurnout::RelayTurnout(int newDccAddress, int newRelayPin)
{
    initTo(newDccAddress, newRelayPin);
}

void RelayTurnout::attach(bool initialDirection) //  Intialize the Relay
{
#if (TO_DEBUG)
    Serial.print("[RelayTurnout::attach] pin: ");
    Serial.print(toPin);
#endif //DEBUG

    pinMode(toPin, OUTPUT);
   
#if (TO_DEBUG)
    Serial.println(" attached");
    Serial.flush();
#endif  //DEBUG
   
    //
    //  Initialize the turnout in a fixed direction.
    //
    targetDirection = initialDirection;
    currentDirection = initialDirection;

    //
    //  Force the initial direction
    //
    if (currentDirection == TURNOUT_STRAIGHT)
    {
        digitalWrite(toPin, HIGH);
    } else
    {
        digitalWrite(toPin, LOW);
    }
   
    //
    //  give it some time to settle down
    //
    delay(100);

#if (TO_DEBUG)
    Serial.print("[RelayTurnout::heartBeat] DCC address: ");
    Serial.print(dccAddress);
    if (currentDirection == TURNOUT_STRAIGHT)
    {
        Serial.println(" -");
    }
    else
    {
        Serial.println(" /");
    }
    Serial.flush();
#endif

    //
    //  Invoke the state handler
    //
    if (toStateHandler != nullptr)
    {
        toStateHandler(dccAddress, currentDirection);
    }
}

void RelayTurnout::heartBeat()
{
    if (currentDirection != targetDirection)
    {
        currentDirection = targetDirection; // Make target direction current direction

        if (currentDirection == TURNOUT_STRAIGHT)
        {
            digitalWrite(toPin, HIGH);
        } else
        {
            digitalWrite(toPin, LOW);
        }
       
#if (TO_DEBUG)
        Serial.print("[RelayTurnout::heartBeat] DCC address: ");
        Serial.print(dccAddress);
        if (currentDirection == TURNOUT_STRAIGHT)
        {
            Serial.println(" -");
        }
        else
        {
            Serial.println(" /");
        }
        Serial.flush();
#endif

        //
        //  Invoke the state handler
        //
        if (toStateHandler != nullptr)
        {
            toStateHandler(dccAddress, currentDirection);
        }
    }
}

ServoTurnout::ServoTurnout(int newDccAddress, int newServoPin, int newStraightPosition,
                                int newDivergingPosition, int newStepDelay)
{
    initTo(newDccAddress, newServoPin);
   
    straightPosition = newStraightPosition;
    divergingPosition = newDivergingPosition;
    stepDelay = newStepDelay;
   
    /*
     * An ArmServo is used, because it has the proper functionality
     */
    turnoutServo = new ArmServo(newServoPin, newStepDelay, newStraightPosition, newDivergingPosition, true);
}

void ServoTurnout::attach(bool initialDirection) //  Intialize the Servo
{
    //
    //  Initialize the turnout in a fixed direction.
    //
    targetDirection = initialDirection;
    currentDirection = initialDirection;
   
    //
    //  Initialize the position of the servo arm
    //
    if (initialDirection == TURNOUT_STRAIGHT)
    {
       turnoutServo->initArmHigh();
    }
    else
    {
        turnoutServo->initArmLow();
    }
   
    //
    //  Invoke the state handler
    //
    if (toStateHandler != nullptr)
    {
        toStateHandler(dccAddress, currentDirection);
    }
}

void ServoTurnout::heartBeat()
{
    if (currentDirection != targetDirection)
    {
        //
        //  Start the movement
        //
        currentDirection = targetDirection; // Make target direction current direction

        if (currentDirection == TURNOUT_STRAIGHT)
        {
            turnoutServo->armHigh();
        }
        else
        {
            turnoutServo->armLow();
        }
       
        //
        //  We start a movement, when finished the state need to be reported
        //
        servoRunning = true;
    }
   
    //
    // Keep the servo running
    //
    turnoutServo->heartBeat();

    //
    //  When the servo has reached it's final position, we have to invoke
    //  the state handler
    //
    if (servoRunning)
    {
        if (((currentDirection == TURNOUT_STRAIGHT) && turnoutServo->isHigh())
                || ((currentDirection == TURNOUT_DIVERGING) && turnoutServo->isLow()))
        {
            //
            //  Invoke the state handler
            //
            if (toStateHandler != nullptr)
            {
                toStateHandler(dccAddress, currentDirection);
            }
           
            servoRunning = false;
        }
    }
}



Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 July 2023, 19:32:00
De nieuwe klasse "ServoTurnout" stuurt niet zelf de servo aan, maar gebruikt een klasse die ik al ontwikkeld had om armseinen aan te drijven. Kwa functionaliteit maakt het niet uit of je een seinarm omhoog of omlaag doet of dat je wisseltongen heen en weer laat gaan. Het enige is dat de benaming van de klasse en de methods op het gebruik voor een armsein is gebaseerd. Maar "who cares".

Dus ook maar even de bestanden.

KB_Servo.h
#pragma once
/*
  A library to control servos used for semaphores and doors

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

 */

#define PWM_SERVO 0

#if (PWM_SERVO)
#include <PWMServo.h>
#else
#include <Servo.h>
#endif // PWM_SERVO

//
//  Some definitions for convenience
//
#define PWM_PIN(r) (r)

/*
   A wrapper class for controlling RC servo's
 */
enum servoActivity {
    IDLE = 1,
    HIGHPOS = 2,
    HIGHBOUNCE1 = 3,
    HIGHBOUNCE2 = 4,
    HIGHBOUNCE3 = 5,
    LOWPOS = 6,
    LOWBOUNCE1 = 7,
    LOWBOUNCE2 = 8,
    LOWBOUNCE3 = 9
};

class ArmServo {
private:
#define REFRESH_TIME  60000
#define DETACH_WAIT_TIME 200
#if (PWM_SERVO)
    PWMServo *myServo;
#else
    Servo *myServo;
#endif //PWM_SERVO
    bool bounce;

    unsigned long timeOfLastStep;
    unsigned long detachWaitTime;
   
    bool detachable;
    bool needRefresh;
    unsigned long lastTimeRefreshed;

    unsigned int target;
    unsigned int current;

    unsigned int highPosition;  //  Arm position high
    unsigned int lowPosition;   //  Arm position low
   
    unsigned int stepWaitTime; // The wait time between steps
    unsigned int servoWaitTime; //  Initial wait time between 2 steps

    servoActivity state = IDLE;

    int servoPin;
    bool active;

    //
    //  Activate the servo by attaching it's servo pin.
    //
    void attachServo();
   
    //
    //  Deactivate the servo by detaching it's servo pin
    //
    void detachServo();

public:
   
    //
    //  Creator for a new instance of an ArmServo
    //
    //  Parameters:
    //      int newServoPin         
    //          Arduino pin used for controlling the servo
    //      int newWaitTime
    //          Time in mS between to consequtive servo steps
    //      int newHighPosition
    //          Servo position for the semaphore arm in it's high position
    //      int newLowPosition
    //          Servo position for the semaphore arm in it's low position
    //
    ArmServo(int newServoPin, int newWaitTime, int newHighPosition, int newLowPosition);
   
    //
    //  Creator for a new instance of an ArmServo
    //
    //  Parameters:
    //      int newServoPin         
    //          Arduino pin used for controlling the servo
    //      int newWaitTime
    //          Time in mS between to consequtive servo steps
    //      int newHighPosition
    //          Servo position for the semaphore arm in it's high position
    //      int newLowPosition
    //          Servo position for the semaphore arm in it's low position
    //      bool newNeedRefresh
    //          When true, servo needs to be refreshed (repositioned) at regular
    //          intervals
    //
    ArmServo(int newServoPin, int newWaitTime, int newHighPosition, int newLowPosition, bool newNeedRefresh);
           
    //
    //  Creator for a new instance of an ArmServo
    //
    //  Parameters:
    //      int newServoPin         
    //          Arduino pin used for controlling the servo
    //      int newWaitTime
    //          Time in mS between to consequtive servo steps
    //      int newHighPosition
    //          Servo position for the semaphore arm in it's high position
    //      int newLowPosition
    //          Servo position for the semaphore arm in it's low position
    //      bool newNeedRefresh
    //          When true, servo needs to be refreshed (repositioned) at regular
    //          intervals
    //      bool aDetachable
    //          When true, the servo is detached after a second when the target
    //          position is reached.
    //
    //ArmServo(int newServoPin, int newWaitTime, int newHighPosition, int newLowPosition, bool newNeedRefresh, bool aDetachable);
   
    //
    //  Set the target of the servo at it's defined low position.
    //
    void armLow();
   
    //
    //  Set the target and current possition of the servo at it's defined
    //  low position.
    //
    void initArmLow();
   
    //
    //  If the servo is on it's defined low position, return true, else
    //  return false.
    //
    bool isLow();
   
    //
    //  Set the target of the servo at it's defined high position.
    //
    void armHigh();
   
    //
    //  Set the target and current possition of the servo at it's defined
    //  high position.
    //
    void initArmHigh();
   
    //
    //  If the servo is on it's defined high position, return true, else
    //  return false.
    //
    bool isHigh();
   
    //
    //  Check the status of the servo. If it reached it's tarfet position
    //  do nothing, else when the wait time has expired move it one step
    //  further in the direction of the target.
    //
    bool heartBeat();
};

Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 July 2023, 19:32:46
We vervolgen met.

KB_Servo.cpp
/*
  A Library for controling servos used by semaphores and doors

  Copyright (C) Meino de Graaf, all rights reserved

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation, version 2
  of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

 */

#include <stdlib.h>
#include <arduino.h>

#include <KB_Servo.h>

//
//  A wrapper class to control RC servo's
//

//
//  Activate the servo by attaching it's servo pin.
//
void ArmServo::attachServo() //  Intialize the servo
{
    this->myServo->attach(this->servoPin);
    this->active = true;

    this->myServo->write(this->current); // Restore last known position
}

//
//  Deactivate the servo by detaching it's servo pin
//
void ArmServo::detachServo()
{
    if ((this-detachable) && (this->active)) // skip when the servo is not
                                             // attached and not detachable
    {
        if (detachWaitTime == 0)    // First time? intialize wait time
        {
            detachWaitTime = millis();
        }
        else if ((millis()-detachWaitTime) >= DETACH_WAIT_TIME) // Waited long enough?
        {
            this->myServo->detach(); // Detach the servo
            this->active = false;
        }
    }
}

//
//  Creator for a new instance of an ArmServo
//
//  Parameters:
//      int newServoPin         
//          Arduino pin used for controlling the servo
//      int newWaitTime
//          Time in mS between to consequtive servo steps
//      int newHighPosition
//          Servo position for the semaphore arm in it's high position
//      int newLowPosition
//          Servo position for the semaphore arm in it's low position
//
ArmServo::ArmServo(int newServoPin, int newWaitTime, int newHighPosition, int newLowPosition)
{
#if (PWM_SERVO)
    this->myServo = new PWMServo();
#else
    this->myServo = new Servo();
#endif
    this->servoPin = newServoPin;
    this->servoWaitTime = newWaitTime;
    this->highPosition = newHighPosition;
    this->lowPosition = newLowPosition;
    this->active = false;
    this->state = LOWPOS;
    this->bounce = false; // disable bounce
    //
    //  Set the arm at a default (LOW) position
    //
    this->target = newLowPosition;
    this->current = (2*newLowPosition+newHighPosition)/3;    // Let the servo make a small movement
   
    //
    //  This servo doesn't need to be repositioned
    //
    this->needRefresh = false;
   
    //
    //  This servo doest need automatic detach
    //
    this->detachable = false;
}

//
//  Creator for a new instance of an ArmServo
//
//  Parameters:
//      int newServoPin         
//          Arduino pin used for controlling the servo
//      int newWaitTime
//          Time in mS between to consequtive servo steps
//      int newHighPosition
//          Servo position for the semaphore arm in it's high position
//      int newLowPosition
//          Servo position for the semaphore arm in it's low position
//      bool newNeedRefresh
//          When true, servo needs to be refreshed (repositioned) at regular
//          intervals
//
ArmServo::ArmServo(int newServoPin, int newWaitTime, int newHighPosition, int newLowPosition, bool newNeedRefresh)
{
#if (PWM_SERVO)
    this->myServo = new PWMServo();
#else
    this->myServo = new Servo();
#endif
    this->servoPin = newServoPin;
    this->servoWaitTime = newWaitTime;
    this->highPosition = newHighPosition;
    this->lowPosition = newLowPosition;
    this->active = false;
    this->state = LOWPOS;
    this->bounce = false; // disable bounce
    //
    //  Set the arm at a default (LOW) position
    //
    this->target = newLowPosition;
    this->current = (2*newLowPosition+newHighPosition)/3; // Let the servo make a small movement
   
    //
    //  This servo does need to be repositioned
    //
    this->needRefresh = newNeedRefresh;
   
    //
    //  This setvo needs also an automatic detach
    //
    this->detachable = true;
}

//
//  Set the target of the servo at it's defined low position.
//
void ArmServo::armLow()
{
    if (this->current != this->lowPosition) {

        this->target = this->lowPosition;
        //this->stepWaitTime = (this->servoWaitTime * 2) / 3; // The downwards movement is faster, gravity
        this->stepWaitTime = this->servoWaitTime;

        this->state = LOWPOS;
    }
}

//
//  Set the target and current possition of the servo at it's defined
//  low position.
//
void ArmServo::initArmLow()
{
    this->current = this->lowPosition;
    this->target = this->lowPosition;
    this->stepWaitTime = this->servoWaitTime;
    this->lastTimeRefreshed = millis()-REFRESH_TIME-1; // force an immidiate refresh;
    this->state = IDLE;
}

//
//  If the servo is on it's defined low position, return true, else
//  return false.
//
bool ArmServo::isLow()
{
    return (current == lowPosition);
}

//
//  Set the target of the servo at it's defined high position.
//
void ArmServo::armHigh()
{
    if (this->current != this->highPosition) {
        this->target = this->highPosition;
        this->stepWaitTime = this->servoWaitTime;
        this->state = HIGHPOS;
    }
}

//
//  Set the target and current possition of the servo at it's defined
//  high position.
//
void ArmServo::initArmHigh()
{
    this->current = this->highPosition;
    this->target = this->highPosition;
    this->stepWaitTime = this->servoWaitTime;
    this->lastTimeRefreshed = millis()-REFRESH_TIME-1; // force an immidiate refresh
    this->state = IDLE;
}

//
//  If the servo is on it's defined high position, return true, else
//  return false.
//
bool ArmServo::isHigh()
{
    if (current == highPosition)
        return true;
    else
        return false;
}

//
//  Check the status of the servo. If it reached it's tarfet position
//  do nothing, else when the wait time has expired move it one step
//  further in the direction of the target.
//
bool ArmServo::heartBeat()
{
    //
    //  When the arm's position isn't at the target, we have to move the arm
    //
    if (this->target != this->current)
    {
        //
        //  Is the servo active? if not attach it to it's pin so it can start moving
        //
        if (!this->active)
        {
            this->attachServo();
        }

        this->lastTimeRefreshed = millis();

        if ((this->lastTimeRefreshed - this->timeOfLastStep) >= this->stepWaitTime)
        {
            //
            //  The servo is moving to it's new location.
            //
            if (this->target > this->current)
            {
                this->current++;
            }
            else
            {
                this->current--;
            }

            this->myServo->write(this->current);
            this->timeOfLastStep = lastTimeRefreshed;
            //
            //  A little thing, force a refresh after 100msec
            //
            this->lastTimeRefreshed = lastTimeRefreshed-REFRESH_TIME+250;
        }

        return true; // The arm is still moving
    }
    else    // The arm is at it's target position
    {
        switch (this->state) // Arm has reached it's target position, the follow-up depends on it's state
        {
            case IDLE:
            {
                //
                //  Detached servo's can creep to a different position, so
                //  at regular intervals we reset the current position.
                //
                if ((this->needRefresh) && ((millis() - this->lastTimeRefreshed) >= REFRESH_TIME))
                {
                    //
                    //  The actual movement of the servo is done in the
                    //  regular non idle state. so we set everything so, that
                    //  it wil activate that in the non-idle part of the heartbeat.
                    //  In this situation we have to wait 200ms before we can
                    //  continue.
                    //         
                    //  Is the servo active? if not attach it to it's pin so it can start moving
                    //
                    if (!this->active)
                    {
                        this->attachServo();
                    }
                   
                    this->myServo->write(this->current);
                   
                    this->lastTimeRefreshed = millis();
                    this->timeOfLastStep = millis();
                   
                    //
                    //  Set the proper state
                    //
                    if (this->target == this->highPosition)
                    {
                        this->state = HIGHPOS;
                    }
                    else
                    {
                        this->state = LOWPOS;
                    }
                   
                    detachWaitTime = 0; // Reset wait for actual detach
                    return true; // The arm is moving
                }
                else
                { 
                    //
                    //  The arm reached it's position, so we can now detach the servo (if it was attached)
                    //
                    if (this->active)
                    {
                        this->detachServo();
                    }
                   
                    return false; // The arm is not moving
                }
            } // IDLE

            case HIGHPOS:
            {
                if (!this->bounce)
                {
                    this->state = IDLE;
                    return false;
                }
               
                //
                //  We reached the High position, we are now starting the bounce
                //  sequence. Calculate the new position
                //
                if (this->highPosition > this->lowPosition)
                {
                    this->target = this->highPosition - ((this->highPosition - this->lowPosition) / 4);
                }
                else
                {
                    this->target = this->highPosition + ((this->lowPosition - this->highPosition) / 4);
                }

                this->stepWaitTime = this->servoWaitTime / 2;
                this->state = HIGHBOUNCE1;
                return true;
            } // HIGHPOS

            case HIGHBOUNCE1:
            {
                if (this->current != this->highPosition)
                {
                    this->target = this->highPosition;
                }
                else
                {
                    if (this->highPosition > this->lowPosition)
                    {
                        this->target = this->highPosition - ((this->highPosition - this->lowPosition) / 6);
                    }
                    else
                    {
                        this->target = this->highPosition + ((this->lowPosition - this->highPosition) / 6);
                    }

                    this->state = HIGHBOUNCE2;
                }
                return true;
            } // HIGHBOUNCE1

            case HIGHBOUNCE2:
            {
                if (this->current != this->highPosition)
                {
                    this->target = this->highPosition;
                }
                else
                {
                    if (this->highPosition > this->lowPosition)
                    {
                        this->target = this->highPosition - ((this->highPosition - this->lowPosition) / 8);
                    }
                    else
                    {
                        this->target = this->highPosition + ((this->lowPosition - this->highPosition) / 8);
                    }

                    this->state = HIGHBOUNCE3;
                }
                return true;
            } // HIGHBOUNCE2

            case HIGHBOUNCE3:
            {
                if (this->current != this->highPosition)
                {
                    this->target = this->highPosition;
                    detachWaitTime = 0; // Reset wait for actual detach
                    return true;
                }
                else
                {
                    //
                    //  The arm reached it's position, so we can now detach the servo (if it was attached)
                    //
                    if (this->active)
                    {
                        this->detachServo();
                    }

                    this->state = IDLE;
                }
                return false;

            case LOWPOS:
                if (!this->bounce)
                {
                    this->state = IDLE;
                    return false;
                }
                //
                //  We reached the High position, we are now starting the bounce
                //  sequence. Calculate the new position
                //
                if (this->lowPosition > this->highPosition)
                {
                    this->target = this->lowPosition - ((this->lowPosition - this->highPosition) / 4);
                }
                else
                {
                    this->target = this->lowPosition + ((this->highPosition - this->lowPosition) / 4);
                }

                stepWaitTime = servoWaitTime / 2;

                this->state = LOWBOUNCE1;
                return true;
            } // HIGHBOUNCE3

            case LOWBOUNCE1:
            {
                if (this->current != this->lowPosition)
                {
                    this->target = this->lowPosition;
                }
                else
                {
                    if (this->lowPosition > this->highPosition)
                    {
                        this->target = this->lowPosition - ((this->lowPosition - this->highPosition) / 6);
                    }
                    else
                    {
                        this->target = this->lowPosition + ((this->highPosition - this->lowPosition) / 6);
                    }

                    this->state = LOWBOUNCE2;
                }
                return true;
            } // LOWBOUNCE1

            case LOWBOUNCE2:
            {
                if (this->current != this->lowPosition)
                {
                    this->target = this->lowPosition;
                }
                else
                {
                    if (this->lowPosition > this->highPosition)
                    {
                        this->target = this->lowPosition - ((this->lowPosition - this->highPosition) / 8);
                    }
                    else
                    {
                        this->target = lowPosition + ((this->highPosition - this->lowPosition) / 8);
                    }

                    this->state = LOWBOUNCE3;
                }
                return true;
            } // LOWBOUNCE2

            case LOWBOUNCE3:
            {
                if (this->current != this->lowPosition)
                {
                    this->target = this->lowPosition;

                    detachWaitTime = 0; // Reset wait for actual detach
                    return true;
                }
                else
                {
                    //
                    //  The arm reached it's position, so we can now detach the servo (if it was attached)
                    //
                    if (this->active)
                    {
                        this->detachServo();
                    }
                    state = IDLE;
                }
                return false;
            } // LOWBOUNCE3
        }
    }
}

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 14 July 2023, 19:46:05
Waar staat kb voor?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 14 July 2023, 20:16:15
Kranenberg.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Patrick Smout op 15 July 2023, 08:19:38
In de de functie detachServo staat m.i.iets heel vreemd.

this-detachable

Vreemd dat de compiler daar niet over klaagt. Moet zijn:

this->detachable
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 15 July 2023, 10:04:36
Heel scherp dat je dat zag.
Het is niet wat er bedoeld wordt, dat de compiler niet klaagt is omdat het wel een correct statement is, this is een pointer, dus hier wordt de pointer met de waarde van detachable (een bool, maar dat geeft niet dat is fysiek gewoon een int) verlaagd. Ook tijdens runtime gaat het goed, alleen is het effect anders dan wat de bedoeling is.
Soms is het wel goed dat een ander even een code review doet, bedankt voor het attenderen op dit foutje.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 15 July 2023, 11:28:06
Waarom is myServo eigenlijk een pointer?
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 15 July 2023, 13:04:47
Dag Bas

Ik geloof dat we daar al eens eerder een discussie over hebben gehad. Laat ik het houden op programmeerstijl en voorkeur.
Ik houd er van om met New objecten te maken. Dat heeft er mee te maken dat ik op die manier een onderscheid probeer te houden tussen primitieven zoals int, long etc en objecten, Verder kun je in een functie aanroep (in C en C++) alleen primitieven (int, long, pointers, etc) doorgeven maar niet een object. Dus wil je een object aan een functie doorgeven zal dat altijd in de vorm van een pointer moeten. Dus prefereer ik om zo vaak mogelijk met pointers te werken.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: reinderlf op 15 July 2023, 19:06:59
Hoi Meino,

Ook objecten die op de stack gemaakt zijn kun je ook doorgeven, dat kan op verschillende manieren in C++:

Object op de stack:
Object object;
1. Als referentie: void functie(Object& object); aanroepen als functie(object);
2. Als const referentie: void functie(const Object& object); aanroepen als functie(object);
3. Als pointer: void functie(Object* object); aanroepen als functie(&object);
4. Als const pointer: void functie(const Object* object); aanroepen als functie(&object);
5. Als kopie: void functie(Object object); aanroepen als functie(object);
6. Via move (r-value reference): void functie(Object&& object); aanroepen als functie(std::move(object));
Voor objecten die het hele programma leven is het handiger om ze als global (of static binnen een class) te declareren, groot voordeel is dat je tijdens het compileren weet of het in het geheugen past van de microcontroller.
Het nadeel van heap allocaties (via new) is dat je pas tijdens het draaien van je code weet of er genoeg geheugen is. De wat kleinere microcontrollers hebben vaak geen MMU (memory management unit), hier moet de compiler dan wat mee doen.

Een reference heeft over het algemeen de voorkeur t.o.v. een pointer, het voordeel van een reference is dat deze altijd geldig is :) Een pointer daarin tegen kan NULL zijn. (Wat dan wel weer handig kan zijn als de parameter optioneel is :))

Mocht je nog vragen hebben, let me know :)

Reinder

p.s. Het is niet betweterig bedoeld ;) Puur een stukje kennisdeling :)
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 15 July 2023, 19:58:07
Dag Reinder

Bedankt voor je kennis. Ik moet eerlijk zijn, ik kom nog uit de old school (K&R) C tijd en houd zaken zo simpel mogelijk. Zoals het gezegde luidt "C provides all the rope to hang your self", daarom heb ik een hekel aan het gebruik van exotische constructies. Dus houdt ik het simpel en geef objecten als pointer door. Daarom was ik ook een grote fan van Java toen dat begin jaren 90 beschikbaar kwam, omdat dit sterk gestandaardiseerd was (objecten altijd by reference). Voor de liefhebbers raad ik de Obfuscated C competition (https://www.ioccc.org/) aan

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 17 July 2023, 20:59:47
Ik geloof dat we daar al eens eerder een discussie over hebben gehad
Nou je het zegt.. begint bij mij ook een belletje rinkelen. Straal vergeten  :-X No further questions  :P
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 15 August 2023, 22:54:26
Wat voorbeeldcode is altijd handig, bedankt.
Wat me opviel is dat je de servopin voor/na de beweging aan/uit zet, tenminste als ik goed genoeg keek. Dat werkt bij jou in de praktijk goed, kennelijk? Ik laat bij mij de pin continu actief, ik dacht dat dat bij servo's de bedoeling was. Bij mij gaat het om twee wissels, dat is misschien ook wat anders dan een seinpaal omdat een wissel echt netjes in positie gehouden moet worden?

(Over een paar maanden ga ik denk ik weer eens wat klussen, ik heb de arduino's al 7 jaar niet meer aangeraakt... Met te lange draden jitteren ze af en toe een beetje. Vandaar dat ik ook wat aan pin aan/uit zit te denken. Beter probleem v/d lengte bij de bron oplossen, maar goed :) )

Reinout
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: bask185 op 16 August 2023, 09:25:44
Citaat
Dat werkt bij jou in de praktijk goed
Niet alleen bij Meino. Voor wissels is het vrij gebruikelijk om de servo's af te schakelen. Ik had het probleem dat ze of niet ver genoeg doordrukten of ze drukten te ver door en gingen brommen (met risico op doorbranden). En uiteraard iets van een verend iets gebruiken. Als een servo uit staat, heeft hij zoveel wrijving dat ze bij onze wissels wel willen blijven staan.

Citaat
et te lange draden jitteren ze af en toe een beetje
Als je ze uit zet en er komt een trein voorbij, dan zal je zien dat ze heel ver gaan uitzwiepen. Dat kan je idd door kortere kabels oplossen. Meino hier gebruikt shielded kabels maar ik weet niet welke. Ik heb volgens mij ook ergens gelezen dat je om je kabels aluminium folie kan wikkelen. Een pull-up weerstand van een niet te hoge waarden zoals 4k7 dicht op de servo motor lost ook veel op. Die zorgt er ook voor dat de servo niet dat kleine schokje geeft wanneer je de spanning er op zet.

[offtopic]
Ik heb hiervoor een servoSweep class geklopt die de servo langzaam beweegt, die optioneel een punstuk relais kan aansturen halverwege de slag en die gebruik kan maken EEPROM om de standen inleerbaar te maken en hij kan ook onthouden wat de servo stand is. Met die pull-up weerstand in plek, blijft de servo gewoon op zijn plek bij opstarten en hij weet dan ook waar hij is.
[/offtopic]

Mvg,

Bas
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 16 August 2023, 11:02:04
Ja ik schakel de servo's af als ze de eindstand bereikt hebben. Dit om jitter en brom te voorkomen. Wat ik wel doe, is dat een servo die in een eindstand staaat, periodiek (zeg na 10-30sec) even actief maak en opnieuw naar de geldende eindstand zet. Mocht een servo toch iets van plaats zijn veranderd dan wordt dat daardoor gecorrigeerd.
Verder had ik met een paar servo's problemen door lange signaal kabels die pulsjes oppikten. Die draden heb ik vervangen door afgeschermd signaaldraad. Hierna geen problemen meer gehad.

Groet Meino
Titel: Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: Reinout van Rees op 16 August 2023, 16:19:51
Bedankt voor de tips  (y)

Reinout