Go Down

Topic: Arduino and Railway Modelling (Read 6832 times) previous topic - next topic

GrooveFlotilla

Check the {} braces for blinkF2()
Some people are like Slinkies.

Not really good for anything, but they bring a smile to your face when pushed down the stairs.

johnbet

OK No error messages now but Blink not working On F2 - no output at all?
Code: [Select]

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//DCC Function Decoder
//JHB
//Attiny 85 Development Board
//Four Functions
//Type of Function in separate routine
//Note - PWM/analogWrite only available on Pins 0 (F0),1 (F1) and 4 (F3)
//Do no forget to make sure binary code consistant with output pin!!!!!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IMPORTANT: GOTO lines 15 - 19 to configure some data!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int decoderAddress = 77; // This is the decoder address, change into the number you want.
#define F0_pin 0 // Define the output pin for every Function number in use
#define F1_pin 1 // Available pin numbers: 0,1,3,4,(5 is normally reset)
#define F2_pin 3 // Pins 0, 1 and 4 are PWM and can be used for analogWrite
#define F3_pin 4

#include <DCC_Decoder.h>
#define kDCC_INTERRUPT 0

byte Func[4]; //0=L4321, 1=8765, 2=CBA9, 3=F20-F13, 4=F28-F21
byte instrByte1;
int Address;
byte forw_rev=1; //0=reverse, 1=forward

boolean RawPacket_Handler(byte pktByteCount, byte* dccPacket) {
  Address=0;
  if (!bitRead(dccPacket[0],7)) { //bit7=0 -> Loc Decoder Short Address
    Address = dccPacket[0];
    instrByte1 = dccPacket[1];
  }
  else if (bitRead(dccPacket[0],6)) { //bit7=1 AND bit6=1 -> Loc Decoder Long Address
    Address = 256 * (dccPacket[0] & B00000111) + dccPacket[1];
    instrByte1 = dccPacket[2];
  }

  if (Address==decoderAddress) {
    byte instructionType = instrByte1>>5;
    switch (instructionType) {
      case 2: // Reverse speed
        forw_rev=0;
        break;
      case 3: // Forward speed
        forw_rev=1;
        break;
      case 4: // Loc Function L-4-3-2-1
        Func[0]=instrByte1&B00011111;
        break;
      case 5: // Loc Function 8-7-6-5
        if (bitRead(instrByte1,4)) {
          Func[1]=instrByte1&B00001111;
        }
        else { // Loc Function 12-11-10-9
          Func[2]=instrByte1&B00001111;
        }
        break;
    }
    //if (Func[0]&B00010000) digitalWrite(F0_pin,HIGH); else digitalWrite(F0_pin,LOW);
    //if (Func[0]&B00000001) digitalWrite(F1_pin,HIGH); else digitalWrite(F1_pin,LOW);
    //if (Func[0]&B00000010) digitalWrite(F2_pin,HIGH); else digitalWrite(F2_pin,LOW);
    if (Func[0]&B00000100) analogWrite(F3_pin, 100); else analogWrite(F3_pin, 0);
  }
}

void fadeF0() {
  static unsigned long lastFadeTimeF0 = 0;
  static int fadeLevelF0 = 0;

  if (millis() - lastFadeTimeF0 > 10) {
   
    lastFadeTimeF0 = millis();
   
    if (Func[0]&B00010000) {
      if (fadeLevelF0 < 255) analogWrite(F0_pin, ++fadeLevelF0);
      }
    else {
      if (fadeLevelF0 > 0) analogWrite(F0_pin, --fadeLevelF0);
    }
  }
}

void flickerF1() {

  static unsigned long startTimeF1 = 0;
  static unsigned long lastFlickerF1 = 0;
  static int stateF1 = HIGH;

  if (Func[0]&B00000001) {
    if (stateF1 == LOW) {
      startTimeF1 = millis();
      stateF1 = HIGH;
    }
    else {
      if (millis() - startTimeF1 > 3000) {
        digitalWrite(F1_pin, HIGH);
      }
      else {
        if (millis() - lastFlickerF1 > 25) {
          lastFlickerF1 = millis();
          if (random(2) == 0) digitalWrite(F1_pin, HIGH); else digitalWrite(F1_pin, LOW);
        }
      }
    }
  }
  else {
    digitalWrite(F1_pin, LOW);
    stateF1 = LOW;
  }
}

void blinkF2() {

  static int stateF2 = LOW;           
  unsigned long previousMillis = 0;       
  const long interval = 1000;           

  unsigned long currentMillis = millis();

  if (Func[0]&B00000010) {
    if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    if (stateF2 == LOW) {
      stateF2 = HIGH;
    } else {
      stateF2 = LOW;
    }

    digitalWrite(F2_pin,LOW);
    }
  }
}

void setup() {
  DCC.SetRawPacketHandler(RawPacket_Handler);
  DCC.SetupMonitor( kDCC_INTERRUPT );
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);

}

  void loop() {
  DCC.loop();
  fadeF0();
  flickerF1();
  blinkF2();
}

PaulRB

#47
Mar 13, 2017, 10:18 pm Last Edit: Mar 13, 2017, 10:19 pm by PaulRB
I can see one mistake, on this line:
Code: [Select]
  digitalWrite(F2_pin,LOW);
Fix that, it should work.
The only thing I can think of ammending there is changing Low to High
Well, you wrote this code immediately before it:
Code: [Select]

    if (stateF2 == LOW) {
      stateF2 = HIGH;
    } else {
      stateF2 = LOW;
    }

That works out the new state for the led, but then you don't use it?

johnbet


EnigmaTS

#49
Apr 26, 2017, 08:12 am Last Edit: Apr 26, 2017, 04:36 pm by EnigmaTS
I would like to build one of the Optocouplers mentioned above, but hardware is a whole other world.

I went to the website (https://rudysmodelrailway.wordpress.com/software/), but couldn't find any more than a Circuit there.

After a search, I found a really good description here:
  https://talkingtracks.com.au/TT_Projects.shtml

They said the project had a difficulty rating of 2, which means I need to know how to solder electronic parts.  Pretty easy if you ask me.

It has a really clear explanation of the circuit, regarding what they say are the 3 components that are key to success.  It provides the maths I didn't know I needed, and also stuff about something called a low pass filter.

They also have a Veroboard layout, so I can make one without a PCB, I just need the components.

I clicked on the link for their "Project", and it instantly downloaded a ZIP file containing all the stuff I needed (Circuit, Top & Bottom Veroboard diagrams, and a set of detailed notes)!  Kool  :)

I you're looking to make one of these, they give you everything you need.  FREE

PaulRB

Thanks for the update, but please learn how to post links correctly.

EnigmaTS

How did I do this time?

And, to be honest, I tried to find out how to post a Link, but I couldn't find one (Partly because I searched for "link").

Anyway, I re-read this page:
http://forum.arduino.cc/index.php/topic,148850.0.html

And, there (As the second last item) is this section:  "To quote this page in other threads:"

So, I decided that if using url instead of Link would work, then I would give it a try!

Obviously, there is some sort of pre-processor, as what I wrote was massaged into stuff that looked more like HTML than what I put in.

So, in summary, I trust that you will agree that there are no instructions for posting a URL or Link, but there is the way to figure it out.

johnbet

i have made some progress and now want to do the final function which is a strobe effect.

this sketch works fine on it's own
Code: [Select]

//This sketch produces a flash or strobe effect without using delay

#define LED_PIN 1       //define output pin
#define LED_ON  50      //milliseconds
#define LED_OFF 800     //milliseconds

unsigned long ms;       //time from millis()
unsigned long msLast;   //last time the LED changed state
boolean ledState;       //current LED state

void setup(void)
{
  pinMode(LED_PIN, OUTPUT);
}

void loop(void)
{
  ms = millis();
  blinkLED();
}

void blinkLED(void)
{
  if (ms - msLast > (ledState ? LED_ON : LED_OFF)) {
    digitalWrite(LED_PIN, ledState = !ledState);
    msLast = ms;
  }
}





but i am having trouble incorporating it into the following sketch where i could do with using it on F3
Code: [Select]

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//DCC Function Decoder - 4 Functions
//Note - PWM/analogWrite only available on Pins 0 (F0),1 (F1) and 4 (F3)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IMPORTANT: GOTO lines 9 - 12 to configure some data!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int decoderAddress = 100; // This is the decoder address, change into the number you want.
#define F0_pin 0 // Define the output pin for every Function number in use
#define F1_pin 1 // Available pin numbers: 0,1,3,4,(5 is normally reset)
#define F2_pin 3 // Pins 0, 1 and 4 are PWM and can be used for analogWrite
#define F3_pin 4

#define LED_ON  50      //milliseconds
#define LED_OFF 800     //milliseconds

#include <DCC_Decoder.h>
#define kDCC_INTERRUPT 0



byte Func[4]; //0=L4321, 1=8765, 2=CBA9, 3=F20-F13, 4=F28-F21
byte instrByte1;
int Address;
byte forw_rev = 1; //0=reverse, 1=forward

boolean RawPacket_Handler(byte pktByteCount, byte* dccPacket) {
  Address = 0;
  if (!bitRead(dccPacket[0], 7)) { //bit7=0 -> Loc Decoder Short Address
    Address = dccPacket[0];
    instrByte1 = dccPacket[1];
  }
  else if (bitRead(dccPacket[0], 6)) { //bit7=1 AND bit6=1 -> Loc Decoder Long Address
    Address = 256 * (dccPacket[0] & B00000111) + dccPacket[1];
    instrByte1 = dccPacket[2];
  }

  if (Address == decoderAddress) {
    byte instructionType = instrByte1 >> 5;
    switch (instructionType) {
      case 2: // Reverse speed
        forw_rev = 0;
        break;
      case 3: // Forward speed
        forw_rev = 1;
        break;
      case 4: // Loc Function L-4-3-2-1
        Func[0] = instrByte1 & B00011111;
        break;
      case 5: // Loc Function 8-7-6-5
        if (bitRead(instrByte1, 4)) {
          Func[1] = instrByte1 & B00001111;
        }
        else { // Loc Function 12-11-10-9
          Func[2] = instrByte1 & B00001111;
        }
        break;
    }
    //if (Func[0]&B00010000) digitalWrite(F0_pin,HIGH); else digitalWrite(F0_pin,LOW);
    if (Func[0]&B00000001) digitalWrite(F1_pin, HIGH); else digitalWrite(F1_pin, LOW);
    if (Func[0]&B00000010) digitalWrite(F2_pin, HIGH); else digitalWrite(F2_pin, LOW);
    if (Func[0]&B00000100) analogWrite(F3_pin, 30); else analogWrite(F3_pin, 0);
  }
}

void fadeF0() {
  static unsigned long lastFadeTimeF0 = 0;
  static int fadeLevelF0 = 0;

  if (millis() - lastFadeTimeF0 > 10) {

    lastFadeTimeF0 = millis();

    if (Func[0]&B00010000) {
      if (fadeLevelF0 < 255) analogWrite(F0_pin, ++fadeLevelF0);
    }
    else {
      if (fadeLevelF0 > 0) analogWrite(F0_pin, --fadeLevelF0);
    }
  }
}

void flickerF1() {

  static unsigned long startTimeF1 = 0;
  static unsigned long lastFlickerF1 = 0;
  static int stateF1 = HIGH;

  if (Func[0]&B00000001) {
    if (stateF1 == LOW) {
      startTimeF1 = millis();
      stateF1 = HIGH;
    }
    else {
      if (millis() - startTimeF1 > 3000) {
        digitalWrite(F1_pin, HIGH);
      }
      else {
        if (millis() - lastFlickerF1 > 25) {
          lastFlickerF1 = millis();
          if (random(2) == 0) digitalWrite(F1_pin, HIGH); else digitalWrite(F1_pin, LOW);
        }
      }
    }
  }
  else {
    digitalWrite(F1_pin, LOW);
    stateF1 = LOW;
  }
}


void setup() {
  DCC.SetRawPacketHandler(RawPacket_Handler);
  DCC.SetupMonitor( kDCC_INTERRUPT );
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);

}

void loop() {
  DCC.loop();
  fadeF0();
  flickerF1();
}


Can anyone assist please?

EnigmaTS

i have made some progress and now want to do the final function which is a strobe effect.
Given that almost 4 weeks has passed, and not a single person has responded, I'm happy to give you my input.  But, don't be surprised if lots of other members now come out to throw stones at someone else's solution.

Since you haven't updated this to say you have found any issue(s) causing F3 not to work, I will assume your problem remains unresolved.

Now, I'm not going to address YOUR Question with THIS response (My initial review didn't suggest any obvious issues).  In the mean time, I think there are simplifications you can make.  Once you've made any simplifications you decide, correcting your Code will become much easier.  This approach also allows the aforementioned stone-throwing members to have a free shot!

And, it allows me time to consider what your issues might be, so that I CAN respond to your original issue, and add some value.  In the mean time, you aren't left wondering if anyone is every going to respond.

___
This is your main loop
Code: [Select]

void loop() {
  DCC.loop();
  fadeF0();
  flickerF1();
}


That's nice and simple,, but there is a more efficient way to do it, which IMHO also makes the Code more readable.

With a MicroController, you have a REALLY Tiny Computer, so if there is efficiency to be had, and it doesn't make the code less Readable, then it's worth doing.

I haven't bothered to show you the Variable Definitions, because you have already demonstrated you understand that aspect of Writing Code for the Arduino.

So, I would write the Main Loop like this...
Code: [Select]

void loop() {
  DCC.loop();     // Call the DCC Library, so that no DCC Packets are lost
  Time_Now =  millis();    // Find out the Time now, to see if there is anything that needs to be done

  // Is it time to action the Task performed for F0
  if (Time_F0Task >= Time_Now) {
     fadeF0();
     Time_F0Task = Time_F0Task + Time_F0Task_Length;
  }

  // Is it time to action the Task performed for F1
  if (Time_F1Task >= Time_Now) {
     flickerF1();
     Time_F1Task  = Time_F1Task + Time_F1Task_Length;
  }

  // Is it time to action the Task performed for F3
  if (Time_F3Task >= Time_Now) {
     F3_Action();
     Time_F3Task  = Time_F3Task + Time_F3Task_Length;
  }

}



Now, I'm sure you understand that you can actually put all these Times into an Array, that has as many elements as you have Function Buttons.  So, for the sake of using less FLASH, you put all these Action Tasks into an Array, then have a Loop that has only 1 test.  Again, I haven't done this, so that my example looks as similar as possible to what you already have, so that change doesn't hinder your understanding on what I'm suggesting.  If you have heaps of Flash, then who case which way you do it.

OK, so what's the difference between Your Code, and My code?

1  -  If it's NOT Time to do anything yet, then the Loop as only a few Instructions, and takes very little time, before it starts again.

2  -  I have named the Variables quite differently, in the hope that one day, I will find out how to get a Symbol Table out of the Arduino IDE.

3  -  You can see from the Main Loop, how many Milliseconds it will be between Iterations (Kind off).



So, why do it like this, instead of the other (Easier?) way???


#1:
By calling the millis function multiple times, you burn MicroController Cycles, to get a similar Number.  But, each Function Key will see a different number of Milliseconds, so things might not be done in the Expected/Correct Sequence.

For example:
-  F0 needs to be done at: 1,000,200
-  F1 needs to be done at: 1,000,100
-  The time now is: 1,000,202

Can you see that you will do F0, THEN F1?
Can you see that this is Out of Sequence?

Now, if the Sequence does NOT Matter, then the Code is FINE.
If, the Sequence DOES matter, and you don't realise what's Causing it, you will scratch your head why.

This is why my own Implementation is NOT driven by the Function Keys (i.e. the Tasks to be done), but is driven by the Time that the Next Action is required.

So, if you had an Array, with elements that looked like this:
-  Time_Action_Required
-  Action_Function

With a Array Limit defined like this:
int  Time_Actions_Queue = 0;  // Number of Actions that are currently waiting to be done.

This way, the code could now look like this...
Code: [Select]

void loop() {
  DCC.loop();     // Call the DCC Library, so that no DCC Packets are lost

  if (Time_Actions_Queue > 0) {
     Time_Now =  millis();    // Find out the Time now, to see if there is anything that needs to be done

     // Is it time to action the Task performed for F0
     if (Time_F0Task >= Time_Now) {
        fadeF0();
        Time_F0Task = Time_F0Task + Time_F0Task_Length;
     }

     // Is it time to action the Task performed for F1
     if (Time_F1Task >= Time_Now) {
        flickerF1();
        Time_F1Task  = Time_F1Task + Time_F1Task_Length;
     }

     // Is it time to action the Task performed for F3
     if (Time_F3Task >= Time_Now) {
        F3_Action();
        Time_F3Task  = Time_F3Task + Time_F3Task_Length;
     }

  }

}


Now, the Main Loop executes even faster.  This means that you will come into the Main Loop closer to the time the next Action Task is required.  But, if you expect to do something almost EVERY Main Loop, then I wouldn't bother.

EnigmaTS

As there is a Character Limit, I have had to divide the Response into multiple parts.  This is Part 2.

Sorry for the delay, but this System also doesn't let you post more than 1 Message every 5 minutes.


#2:
If I could work out how to get a Symbol Table, then it would contain something like this:
fadeLevelF0
Time_F0Task
Time_F0Task_Length
Time_F1Task
Time_F1Task_Length
Time_F3Task
Time_F3Task_Length
Time_Now
stateF1
-  As you can see, All the Variables containing a Time (Or Time Length) are Grouped Together.

Compare this to what the Symbol Table from the Current code would be similar to:
fadeLevelF0
lastFadeTimeF0
lastFlickerF1
startTimeF1
stateF1
-  Can you see that you have no idea what to look for, to find out How many Milliseconds between Function events?


#3:
It's possible to calculate how many Milliseconds it will be between Iterations (Kind off).

You can at least Calculate the Main Loop Duration, then you can calculate the number the number of Instructions to process each Function Key Action.  This will give you a VERY Rough guide to how long each will take (Which depends on - AT least - the Path, and what Branching Instructions are executed in a Function).

If you find that one Function takes a lot more Time to execute that the others, this could impact on the correct processing of your code.

By knowing how long each Function takes, you can decide if your Mainline should actually look more like this:
Code: [Select]

void loop() {
  DCC.loop();     // Call the DCC Library, so that no DCC Packets are lost

  if (Time_Actions_Queue > 0) {
     Time_Now =  millis();    // Find out the Time now, to see if there is anything that needs to be done

     // Is it time to action the Task performed for F0
     if (Time_F0Task >= Time_Now) {
        fadeF0();
        Time_F0Task = Time_F0Task + Time_F0Task_Length;
        DCC.loop();     // Call the DCC Library, so that no DCC Packets are lost
     }

     // Is it time to action the Task performed for F1
     if (Time_F1Task >= Time_Now) {
        flickerF1();
        Time_F1Task  = Time_F1Task + Time_F1Task_Length;
        DCC.loop();     // Call the DCC Library, so that no DCC Packets are lost
     }

     // Is it time to action the Task performed for F3
     if (Time_F3Task >= Time_Now) {
        F3_Action();
        Time_F3Task  = Time_F3Task + Time_F3Task_Length;
        DCC.loop();     // Call the DCC Library, so that no DCC Packets are lost
     }

  }

}


As you can see, the extra calls to DCC.loop are only executed IF there was execution of an Action Routine.  If there was no Requirement to perform 'Work', then there is still plenty of time to get around to the start of the Main Loop again.

With the placement of the new "Time_xxTask" Arithmetic, you can also choose to put something different into the corresponding "Time_xxTask_Length" variable.  This allows you to have Asymmetric time periods, if that's what you need.  And, if all the Time Lengths are Defined prior to use, they can all be placed in the same area of the Source Code, making Maintenance much easier for you.  Regardless of how many times you might use the same Time Length in the Code, it's actual Value is only defined once.

If you happen to have Asymmetric time periods, that's going to become very obvious from the Variable Names (Even if you don't have a Symbol Table to look at).


_____
It is equally Valid to do either of these lines of code to set the next Time something needs to be done, depending on what you Need/Want...

Code: [Select]

        Time_F1Task  = Time_F1Task + Time_F1Task_Length;  // Alternative A
        Time_F1Task  = Time_Now    + Time_F1Task_Length;  // Alternative B



Alternative A:
This one keeps the Events the same time apart from the LAST Time.  The issue comes when:
--> Time_F1Task + Time_F1Task_Length > Time_Now


Alternative B:
This one keeps the Events the same time apart from NOW, but not the LAST Time.  This allows the Events to drift further and further apart.  That might NOT impact the purpose of these Actions, so might be PREFERABLE to Alternative A.



___
If you decide that an Event Queue Array would be useful, apart from what Data is in the Queue, you also need to decide how you are going to Insert & Remove Events, because they might not just be Added to the Bottom, and Removed from the Top - You might need to Insert one ahead of other Events still Outstanding.

Anyway, see what you think, while I look at what might be causing you any issues.

Go Up