Doel:€250.00
Donaties:€345.00

Per saldo:€95.00

Steun ons nu!

Laatst bijgewerkt
op 21-01-2020
Algemeen

De stichting

Recente berichten

Wissel puntstuk, ervaringen met Frog juicer gevraagd. door jowi
Vandaag om 20:36:55
Heris 2020 door Bert van Gelder
Vandaag om 20:21:42
Toon hier je nieuwe (model-) spooraanwinst(en)... door Gerrit F
Vandaag om 20:18:14
Dordrecht jaren 70 door Rob Hagg
Vandaag om 20:17:54
Mijn eerste H0-modeltreinbaan in aanbouw door Benrail
Vandaag om 20:13:35
Elektronisch relais? door Boerammetje
Vandaag om 20:08:35
Rheinburgh, TP V/VI door Rondje_HO
Vandaag om 20:00:04
NCS 7/8 tot NS 61 62 Maffei lok in spoor 0 door roadster36
Vandaag om 19:55:53
Sprog 2V3, werkt niet en foutmelding. door Ronaldp
Vandaag om 19:52:45
Nederlandse baan in voorbereiding genaamd Anninkshoeve door valkyrienineball
Vandaag om 19:35:07
Heen, En en Weer door DE-II
Vandaag om 19:08:53
Zolderbaan 5 bij 3 meter, Märklin, C-Rail, Digitaal IB+Win Digipet # De bouw door Timaximus
Vandaag om 18:33:45
Albulabahn in H0m door Swiss_fan
Vandaag om 18:19:15
Artitec 2020, raad-draadje door barry1972
Vandaag om 18:18:07
Exacttrain 2020 door Remystoom
Vandaag om 18:11:46
RAIL 2020 21, 22 en 23 februari, Houten. door Floris
Vandaag om 17:48:59
Nieuwe baan, nieuwe schaal van N naar H0e door Wim1969
Vandaag om 17:44:14
Blokken maken met Peco spoor en Peco SL-88/ST-244 wissels door AlbertG
Vandaag om 17:37:29
Het 01TREFF 2020: Spoor 0 en 1 puur! door JeanR
Vandaag om 17:32:37
Videoserie: Rocrail voor beginners door AlbertG
Vandaag om 17:08:29
RSU stapje 3: HSM zand en kolen wagens MK131 and 134 door Tulpendaal
Vandaag om 17:07:32
Van Swarzach naar Swarzburg door nkob
Vandaag om 16:55:34
Flickr niet meer gratis? door FritsT
Vandaag om 16:47:57
Lenz spoor 0 en digital 2020 door Floris
Vandaag om 16:42:09
De fabriek - Spoor 0 door BartvD
Vandaag om 16:22:35
Onlangs gespot - gefotografeerd, de foto's door Chris Westerduin
Vandaag om 15:03:43
Lemke 2020 door ceuleer
Vandaag om 14:59:44
Welk merk en type decoder is dit? door Marco Staartjes
Vandaag om 14:55:26
3D/RM: GVB 12G (Amsterdamse gelede tram in H0) door FS
Vandaag om 14:53:36
Een Rotterdams gebouw door Gerco van Beelen
Vandaag om 14:47:14
  

Auteur Topic: De CanBus komt naar Kranenberg, Arduino's en de CanBus  (gelezen 9419 keer)

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #75 Gepost 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

« Laatst bewerkt op: 06 december 2019, 17:01:08 door meino »
A clean desk is a sign of an empty mind

Kranenberg

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #76 Gepost op: 06 december 2019, 21:48:46 »
De Bezetmelder Controllers zijn ook weer Arduino Uno's.



Een fotootje van de DTC en PMP7 bezetmelders.



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
A clean desk is a sign of an empty mind

Kranenberg

Timo

  • Team encyclopedie
  • Offline Offline
  • Berichten: 4541
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #77 Gepost 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
Verzonden vanaf mijn desktop met Firefox

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #78 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg

Patrick Smout

  • Offline Offline
  • Berichten: 302
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #79 Gepost 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

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #80 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg

Patrick Smout

  • Offline Offline
  • Berichten: 302
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #81 Gepost 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

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #82 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg

Klaas Zondervan

  • Offline Offline
  • Berichten: 18098
    • Pagina van klaas
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #83 Gepost 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
Spoorbaan "Uit en Thuis" in aanbouw.

bask185

  • Offline Offline
  • Berichten: 328
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #84 Gepost 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.

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #85 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #86 Gepost 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.



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.



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
A clean desk is a sign of an empty mind

Kranenberg

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #87 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #88 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg

meino

  • Offline Offline
  • Berichten: 616
Re: De CanBus komt naar Kranenberg, Arduino's en de CanBus
« Reactie #89 Gepost 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
A clean desk is a sign of an empty mind

Kranenberg