BeneluxSpoor.net forum

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

Titel: De CanBus komt naar Kranenberg, Arduino's en de CanBus
Bericht door: meino op 11 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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: 44 op 11 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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 oktober 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: Karst Drenth op 12 november 2019, 00:05:03

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.


Ook ik lees het met interesse (y)

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.

Als je dat slim doet, hoef je ook geen objecten dynamisch te creeren (idd lasting in een Arduino omgeving) immers de nieuwe Class bevat beide georven types en zijn ook direct daarnaartoe te casten. Alle objecten zijn statische instanties van de gedefinieerde Classes, waarbij je via de constructor en zijn parameters de mogelijkheid hebt de aangemaakte instantie nog enigzins te "customizen".

Als werkende voorbeelden van deze techniek, kan ik de DR5000, DR5088, DR5013 en DR5052 aandragen. Met al z'n bussen en protocollen (die allemaal op basis van multiple inheritance werken) etc., die ook nog eens gemixt gebruikt kunnen worden, is er in een micro-controller omgeving bijna geen andere methode denkbaar c.q. haalbaar. (wil je het onderhoudbaar en gestructureerd houden ;) )

Hoe b.v. de class-hierarchie van de brugcontrollers van de DR5052 er uit zien, veel is weggelaten, maar de essentie kun je er wel uit aflezen ;)

class BridgeController : public StationSender, protected  EventHandler<TurnTable*, int> {
  public:
BridgeController(TurnTable* turnTable) {}
};

class PlusController : public StationSender, protected StationHandler<CMD::FeedbackInt_t> {
  public:
PlusController(BridgeController* bridgeController) : bridgeController(bridgeController) {}
};

class BasicController : public StationSender, protected Digital::Interrupt, protected Kernel ::BckGndHandler, protected Kernel ::IdleHandler {
  public:
BasicController(DR5052Controller* bridgeController, PORT_t& portM_SENS, byte pinM_SENS, byte interrupt) :
     Digital::Interrupt( portM_SENS,      pinM_SENS,      interrupt, PORT_ISC_BOTHEDGES_gc) {}
};

class RocoH0Controller : public StationSender, protected Kernel::BckGndHandler, protected StationHandler<CMD::FeedbackInt_t> {
  public:
RocoH0Controller(BridgeController* bridgeController) {}
};

class DR5052Controller : public BridgeController, protected Kernel::IdleHandler {
  public:
DR5052Controller(TurnTable* turnTable, PORT_t& portM_SENS, byte pinM_SENS) : BridgeController( turnTable) {}
};

class DR5052BasicController : public DR5052Controller, public    BasicController {
  public:
DR5052BasicController(TurnTable* turnTable, PORT_t& portM_SENS, byte pinM_SENS, byte interrupt) : DR5052Controller(turnTable, portM_SENS, pinM_SENS),
      BasicController(this, portM_SENS, pinM_SENS, interrupt) {}
};

class DR5052RocoH0Controller : public DR5052Controller, public    RocoH0Controller {
  public:
DR5052RocoH0Controller(TurnTable* turnTable, PORT_t& portM_SENS, byte pinM_SENS) : DR5052Controller(turnTable, portM_SENS, pinM_SENS), RocoH0Controller(this) {}
};

class DR5052PlusController : public DR5052Controller, public PlusController {
  public:
DR5052PlusController(TurnTable* turnTable, PORT_t& portM_SENS, byte pinM_SENS) : DR5052Controller(turnTable, portM_SENS, pinM_SENS), PlusController(this) {}
};

class DR5052ProfiController : public BridgeController, public PlusController {
  public:
DR5052ProfiController(TurnTable* turnTable) : BridgeController(turnTable), PlusController(this) {}
};

class DR5052StepperController : public BridgeController, protected StationHandler<CMD::FeedbackInt_t>, protected Kernel::BckGndHandler, protected Kernel::IdleHandler {
  public:
DR5052StepperController(TurnTable* turnTable) : BridgeController(turnTable) {}
};

Grtzz,
Karst

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: Erik Baas 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 ***, 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 januari 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 januari 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 januari 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 januari 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 januari 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 januari 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 januari 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 januari 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 januari 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