Help needed! - Creating a sketch on Arduino to read and transmit a reed switch input.

Chris Hall Jun 8, 2021

  1. Chris Hall

    Chris Hall TrainBoard Member

    188
    410
    11
    Hey guys, I've been scratching my head for a few days trying to work out how to solve this problem.

    I've got my JMRI set up with a node network of 3 Nano's and 1 Uno running at 19200. Everything is working ok, my turnouts move, CT block detection works fine and my reed switches operate.

    The problem I have is with getting a reed switch activation to be recognised by JMRI when the loco is traveling faster than snails pace.

    At the moment I am just using the standard;

    cmri.set_bit(3, !digitalRead(3));

    which works fine when the loco is going slowly over the reed switch but as soon as I introduce any sort of speed JMRI doesn't pick up the momentary action.

    Now I know I have to introduce some sort of way of delaying the clearing of the !digitalread bit long enough that the CMRI polling has enough time to read it, probably by using Millis the StateChangeDetection example and an array of some sort, but this is where my limited programming knowledge has hit the wall and Google is not throwing up anything useful.

    I could spend a few weeks knuckling down and trying to sort this out myself but I'm in a bit of a time crunch so was hoping someone out there with a bit of Arduino knowledge might be able to just throw the answer my way.

    Ideally I need to read pins 3-8 when the reed switch activates and then hold that state for 1000ms to allow plenty of time to be polled (normally about 100ms).

    Any help would be greatly appreciated.
     
  2. Sumner

    Sumner TrainBoard Member

    2,798
    5,837
    63
    Not sure this would help or not but...

    If JMRI is reading 3 I would have the reed switch on a different pin, say 2. Read 2 and when it goes high write 3 high for 1000ms.

    You use an extra set of pins for each reed switch but should work (maybe),

    Sumner

    P.S. If you are using an Uno and run out of pins buy a Mega for $16 or about half that if you can wait for and overseas order.
     
    Last edited: Jun 8, 2021
  3. Chris Hall

    Chris Hall TrainBoard Member

    188
    410
    11
    Thanks Sumner,

    I nearly used this method for the CT modules for the block detection but managed to work out a solution (took about two months!). Problem is I've sold the layout and wanted to get this issue solved for the new guy before he picks it up at the weekend. I have Nano's and Uno's so pin numbers is definitely an issue, plus adding in more Arduino's would require major rewiring.

    I think I know what I need to do,

    The sketch needs to count the number of times the reed switch is active in a 1000ms period. If it is greater than 1 then set a variable to "high" which can be seen when polled by CMRI, after 1000ms it resets back to 0 and starts again. I'm not too worried about the pin bouncing as I just need a least 1 activation during the millis count.

    I just don't quite know how to write this into code and cover all the inputs required.
     
  4. Ash

    Ash TrainBoard Member

    106
    66
    8
    You might want to post your sketch. In mine, there is a check which prevents too frequently updating the CMRI data. (or perhaps it is there for debouncing -- I'm open to improved explanations)
    Code:
      // Load Input Values -- from sensors and buttons
    
       if (millis()-TimerLoadInput>200) {
          TimerLoadInput = millis();
          LoadInputValues();
        }
    I had noticed that a quick press of a button was not seen by JMRI, so am interested in what you find to be the solution.
     
    Chris Hall likes this.
  5. Chris Hall

    Chris Hall TrainBoard Member

    188
    410
    11
    Good Idea!

    Code:
    /***************************************************
                    BRYNDERWYN LAYOUT                        
    
    This is an Arduino sketch for the Brynderwyn layout.
    
    This sketch is for Node 2 of the CMRI network. The sketch
    will process readings from the CT modules by reading the
    Analog pins 0-4. A max and min value is calculated and
    compared to the offset value to determine whether a block
    is occupied.
    
    The sensor process monitors digital pins 3-8. Sensors can
    consist of buttons, reed switches, or many other items.
    This can be adjusted if more sensors are required.
    
    The light process monitors CMRI and writes to digital
    pins 9-13. This can be used to switch on LED's, relays and
    other items. This can be adjusted if more lights are
    required.
     
      Author - Chris Hall,                  
      Part of the Brynderwyn Layout installation.    
         
     ****************************************************/
    // Libraries used
    #include <CMRI.h>
    #include <Wire.h>
    #include <Auto485.h>
    #include <DIO2.h>
    
    // Definitions used - These values can be changed
    #define   CMRI_ADDR 2   // CHANGE THIS TO MATCH NODE
    #define   DELAY   10    // DELAY ALLOWS TIME TO PROCESS MAX AND MIN VALUES
    #define   OFFSET  50    // CHANGE THIS TO ADJUST SENSITIVITY OF BLOCK DETECTION - LOWER NUMBER IS MORE SENSITIVE Note - Node 1 appears to need +4 compared to other nodes.
    #define   CHECK   10    // CHANGE THIS TO ADJUST THE NUMBER OF CHECKS EACH CT MODULE PERFORMS
    #define   DE_PIN  2     // 485 bus pin - DO NOT CHANGE
    
    // Integers used
    int       k;
    uint16_t  i;
    uint32_t  cal;
    uint16_t  currentReading;
    uint16_t  maxReading;
    uint16_t  minReading;
    uint16_t  offset;
    
    // Driver setup
    Auto485 bus(DE_PIN);
    CMRI cmri(CMRI_ADDR, 24, 48, bus);
    
    void setup() {
    
      bus.begin(19200UL, SERIAL_8N2);
    
      for (int k=3; k<=8; k++)  { pinMode(k, INPUT_PULLUP); }
     
      for (int k=9; k<=13; k++) { pinMode(k, OUTPUT); digitalWrite(k, LOW); }
    }
    void loop() {
    
    // This is the loop process, the order determines which process is first - although the speed of process shouldn't matter too much. change the order to modify the way the node processess information.
       cmri.process();
     
       BLOCK_DETECTION1();
       BLOCK_DETECTION2();
       BLOCK_DETECTION3();
       BLOCK_DETECTION4();
       LIGHTS();
       SENSORS();
    }
    
    // Block detection process - Send HIGH/LOW on bit 0-3 of packet depending on occupancy state. (CMRI bits 1-4)
    void BLOCK_DETECTION1() {
    
        maxReading  = 0;
        minReading  = 1024;
        for (i=0; i<=CHECK; i++)
        {
          delay(DELAY);
    
          currentReading = analogRead(A0);
          if (currentReading > maxReading) maxReading = currentReading;
          if (currentReading < minReading) minReading = currentReading;
        }
        offset = maxReading - minReading;
        if (offset >OFFSET) {  
                        cmri.set_bit(0, HIGH);
                        }
        else            {
                        cmri.set_bit(0, LOW);
                        }
        }
       
    void BLOCK_DETECTION2() {
                       
        maxReading  = 0;
        minReading  = 1024;
        for (i=0; i<=CHECK; i++)
        {
          delay(DELAY);
    
          currentReading = analogRead(A1);
          if (currentReading > maxReading) maxReading = currentReading;
          if (currentReading < minReading) minReading = currentReading;
        }
        offset = maxReading - minReading;
        if (offset >OFFSET) {  
                        cmri.set_bit(1, HIGH);
                        }
        else            {
                        cmri.set_bit(1, LOW);
                        }
        }
    
    void BLOCK_DETECTION3() {
                     
        maxReading  = 0;
        minReading  = 1024;
        for (i=0; i<=CHECK; i++)
        {
          delay(DELAY);
    
          currentReading = analogRead(A2);
          if (currentReading > maxReading) maxReading = currentReading;
          if (currentReading < minReading) minReading = currentReading;
        }
        offset = maxReading - minReading;
        if (offset >OFFSET) {  
                        cmri.set_bit(2, HIGH);
                        }
        else            {
                        cmri.set_bit(2, LOW);
                        }
        }
    
    void BLOCK_DETECTION4() {
     
        maxReading  = 0;
        minReading  = 1024;
        for (i=0; i<=CHECK; i++)
        {
          delay(DELAY);
    
          currentReading = analogRead(A3);
          if (currentReading > maxReading) maxReading = currentReading;
          if (currentReading < minReading) minReading = currentReading;
        }
        offset = maxReading - minReading;
        if (offset >OFFSET) {  
                        cmri.set_bit(3, HIGH);
                        }
        else            {
                        cmri.set_bit(3, LOW);
                        }
        }
    
    // 2: Sensor Process -  Get sensor information and send to JMRI, reads pins 3-8 and sends bit 4-9 on packet (CMRI bits 4-9)
    // Uncomment when additional sensors are required. JMRI 2004-2009
    void SENSORS(){
     
      cmri.set_bit(4, digitalRead2(3));
      cmri.set_bit(5, digitalRead2(4));
      cmri.set_bit(6, digitalRead2(5));
      cmri.set_bit(7, digitalRead2(6));
    //  cmri.set_bit(8, digitalRead2(7));
    //  cmri.set_bit(9, digitalRead2(8));
    }
    
    // 4: Output(lights etc) Process. Gets output information from JMRI. Reads bit 0 to 4 of packet and sets the pins 9-13 to high/low (CMRI bits 1-5)
    // Uncomment when additional lights are required. JMRI 2001 - 2005
    void LIGHTS(){
     
      digitalWrite2(9, cmri.get_bit(0));
      digitalWrite2(10, cmri.get_bit(1));
      digitalWrite2(11, cmri.get_bit(2));
    //  digitalWrite2(12, cmri.get_bit(3));
    //  digitalWrite2(13, cmri.get_bit(4));
    }
     
  6. Ash

    Ash TrainBoard Member

    106
    66
    8
    The delay( ) function is blocking other processing; so each of your block detection processes have the delay with a count of 10 -- that means the reed switch only gets checked once every 400ms. A quick way to see if more frequent polling is the answer would be to temporarily set CHECK=2.

    If that works for the reed switch, then you could modify the main loop so that it only calls one of the block detection functions in each pass (with CHECK=10 or 8).
    count 1 to 4. case 1, block 1. case 2, block 2...
     
  7. Chris Hall

    Chris Hall TrainBoard Member

    188
    410
    11
    Of course! didn't think about that. For each block there is 10 checks of the pin = 100ms multiply that by 4 and its quite some time.

    I'll have a go at reducing the delay first, then try reducing the checks done and see if that affects both the accuracy of the block detection and if the reed switches are better.
     
  8. Rasputen

    Rasputen TrainBoard Member

    565
    186
    27
    You really do not want to use the delay function at all, since the CPU does nothing else during the delay period. Instead, store the time that you wish to start something, example: starttime = millis ()
    Then, in each loop, compare the current time with your start time,
    example:
    if ((millis() -starttime) > DELAY)
    then {perform your desired action };

    That crazy Delay function was put in to make some of the examples easy to understand, but it has no place in a control system.
     
  9. Chris Hall

    Chris Hall TrainBoard Member

    188
    410
    11
    Thanks guys for the input.

    I've added a switch statement into the code now and the polling rate has gone from about 800ms to 80ms, which is a vast improvement. The reed switches are better and sense a train passing at slow speed rather than shunting speed, so the automation is now working and the trains are stopping in the right spots. I'm not convinced that the reed switches are the way to go, in future I might look into hall effect sensors, as the reed switches are very fragile (without a protective cover) and I've broken a few just moving the layout about.

    Code:
    // Driver setup
    Auto485 bus(DE_PIN);
    CMRI cmri(CMRI_ADDR, 24, 48, bus);
    
    void setup() {
    
      bus.begin(19200UL, SERIAL_8N2);
    
      for (int k=3; k<=8; k++)  { pinMode(k, INPUT_PULLUP); }
     
      for (int k=9; k<=13; k++) { pinMode(k, OUTPUT); digitalWrite(k, LOW); }
    }
    void loop() {
    switch(state){
          case  1:  // Sensors, lights and Block Detection 1
            if(state = 1) 
            cmri.process();
            BLOCK_DETECTION1();
            SENSORS();
            LIGHTS();
            transition = 0;
            state = 2;
          break;
    
          case  2:  // Sensors, lights and Block Detection 2
            if(state = 2)
            cmri.process();
            BLOCK_DETECTION2();
            SENSORS();
            LIGHTS();
            transition = 0;
            state = 3;
          break;
    
          case  3:  // Sensors, Lights and Block Detection 3
            if(state = 3)
            cmri.process();
            BLOCK_DETECTION1();
            SENSORS();
            LIGHTS();
            transition = 0;
            state = 4;
          break;
    
          case  4:  // Sensors, Lights and Block Detection 4
            if(state = 4) 
            cmri.process();
            BLOCK_DETECTION1();
            SENSORS();
            LIGHTS();
            transition = 0;
            state = 1;
          break;
       }
    }
    I ended up having to keep a small fixed delay of 5ms in there as it became inaccurate, but this way I have stopped getting ghost trains appearing and the response from all the other items, lights, sensors and turnouts is much better. Still looking into how to hold the sensor activation for a second but that can wait until I start the new layout.
     
    Last edited: Jun 9, 2021
  10. Ash

    Ash TrainBoard Member

    106
    66
    8
    Perhaps you've already found it, but your new code needs editing; it never executes
    BLOCK_DETECTION3();
    BLOCK_DETECTION4();
    And while it works, the If (state = _ ) lines are redundant -- that is what switch-case does.
     
  11. Chris Hall

    Chris Hall TrainBoard Member

    188
    410
    11
    Thanks Ash,

    I found the error not long after posting it. Silly mistake thanks to cut and paste.

    still new to this switch case stuff so thanks for the input, I’ll change the code tomorrow.
     

Share This Page