Help with LEDs on model railway project

Hi,

I am looking for some advice on the best way to approach a project. I'll try and keep the description brief.

I have a model railway layout. Above it on three sides I have three wall lights. With these I want to simulate a day/night cycle over 24 minutes instead of 24 hours. So these lights will act like the sun. When appropriate then I want to turn on and off lights in the buildings at different times. Eg: As it starts to become night streetlights will come on and so on.

I imagined it would be easy to run a 24 minute timer and trigger the different lights from it. Mapping the "sun lights" brightness to its time and even running a 4 digit led display to show the current time. Unfortunately from doing some research this does not seem too easy.

My electronics knowledge isn't too bad, my coding is the inferior part.

I'm currently using delays to control the brightness and intervals but as is stated many times here on the forums, this is cumbersome.

Help me learn, what is the clever way to achieve what I'm looking for?

Thanks in advance for any help offered!



I think I would have a timer for 24hrs in your timeframe .
Use the millis timer and see it’s value at the start of your day and time events off that . At the end of your “‘day” take a new reading and start over.

So “24hrs “ in your time frame is 24x60x1000mS , and 1hr is 60000mS - use that for your clock .
So for example , some event at 2am occurs at your start millis value plus 2hrs , or 2x60000mS

To get away from using delay use millis() or micros() for non-blocking timing.
Non-blocking timing tutorials:
Blink without delay().
Beginner's guide to millis().
Several things at a time.

As @hammy says, the usual solution to dropping delays is to use millis instead. I think that I would build an array of structs that tells you what lights go on/off when. The times in there would be expressed as hours and minutes to make it easier to figure and the code can translate that into minutes and seconds to use millis to figure out when to act.

Thank you, the beginners guide to millis is very good, and probably a great starting point! Even has a fade led part I want to now try!

Any good suggested reading /videos on the array of structs approach? Thank you for your reply also.

I think a lot of my confusion was coming from all the examples I seen are triggering after specific intervals it seems as opposed to at an exact time.

For example, I assumed, wrongly I think, I could have written code along the lines of :

if currentMillis = 2*60000 digitalWrite(led1, HIGH)

And then to start the "day" again after "24 hours" has elapsed... How will that work?

If currentMillis = 24*60000 currentMillis =0

Something like that? Sorry I'm kind of just thinking out loud here..

You can work with intervals if you prefer, but then you end up having to figure out what they are manually whereas if you work with (simulated) clock time, it's easier to decide that at say, 6:30, street lights should come on.

The day starts when you tell it to by pressing a button perhaps or just at power up. You need to record the value of millis in a variable. When your cycle is over, you need to store the current value of millis in that variable again if you want to re-run.

When you talk about simulated clock time do you also mean referring to it in the code in millis like 2x60000 as was previously suggested or is there a way to convert this at the beginning to a time reference that would look like 2.30 or 16.45 ?

I'm suggesting that as per the links @groundFungus posted, you'll need to keep track of elapsed time using millis.

However, you can convert that elapsed time to hours and minutes for display purposes on your time of day LED. Since you chose such a convenient scaling down of the time, it's really just minutes and seconds. So you can calculate elapsed time, convert that to seconds and then derive a clock time for display from that.

That same clock time can be used to look through the array of structs to see if it's time to do something. I don't have a suitable link to a tutorial but it's standard C++, not Arduino specific so there will be loads.

I was thinking of something like this for the first try:

struct LightingAction
{
byte hour;
byte minute;
byte LedPin;
bool TurnOn;
bool IsItDone;
};

Hello
Do the LEDs 1 to 9 have a name too?

That chart was really just for demonstration purposes.. In reality I think there will be around 50 different leds turning on and off. I will give them names like house1Led, church Led etc.

Can you tell millis to start again after a specified time?

@wildbill. I don't think this is what you're suggesting but is there not an easier to just make my own "count up timer" that will constantly loop and work everything from its value..?

I do want the program start running from power up, ideally from "12 midday"... I didn't clarify this initially.

Hello
You may start with the design of the 'local' model clock.
This clock will be the origin to start/stop the different LED illuminations events.
Have a nice day and enjoy coding in C++.

Sounds like an excellent application for a MAX7219 module. One module to rule them all, controlled by three Arduino pins. Only 16 wires connect to all the LEDs. Only on/ off control (plus an overall brightness level).

There is probably some arcane hack to reset millis, but generally people just let it run, note what it is at some event and then use the saved value to derive elapsed time later.

You could indeed use the blink without delay method with millis to make your own counter, incrementing once a second I expect would be what you need.

I'd suggest that you start the clock at midnight to start with - you can tweak it to midday later.

An hour ain't long in a 24 minute day: :grinning:

unsigned long dayTimer;
const unsigned long aDay = 1440000, // 24 minutes in millis
                    minInterval = 1000; // 1 min in a 24 min day
const byte led = LED_BUILTIN;
void setup()
{
  pinMode(led,OUTPUT);
}

void loop()
{
  if(millis() - dayTimer >= aDay)
  {
    dayTimer += aDay; // new day
  }
  digitalWrite(led,millis() - dayTimer < minInterval * 60); //"hour"
}

Hello
Check out this model clock sketch as kickoff for your model railway project.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  https://forum.arduino.cc/t/help-with-leds-on-model-railway-project/927497
*/
#define ProjectName "Help with LEDs on model railway project"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr unsigned long ModelTimeSkale  {1000}; // time in msec = 1 minute in real world

// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct CLOCK {
  byte hours;
  byte minutes;
  unsigned long duration;
  unsigned long stamp;
} modelClock{0, 0, ModelTimeSkale, 0};

void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
}
void loop () {
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  if (currentTime - modelClock.stamp >= modelClock.duration) {
    modelClock.stamp = currentTime;
    ++ modelClock.minutes %= 60;
    if (!modelClock.minutes)  ++ modelClock.hours %= 24;
    if (modelClock.hours < 10) Serial.print("0");
    Serial.print(modelClock.hours); Serial.print(":");
    if (modelClock.minutes < 10) Serial.print("0");
    Serial.print(modelClock.minutes), Serial.println(" o´clock");
  }
}

Have a nice day and enjoy coding in C++.

This is brilliant, thank you so much, surely this is the majority of what i'm looking for?

Now it's just a case of how do i reference the "clock" to use if statements, is it? Or do i still need to reference millis?

I did try...

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  https://forum.arduino.cc/t/help-with-leds-on-model-railway-project/927497
*/
#define LED1 2 
#define ProjectName "Help with LEDs on model railway project"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr unsigned long ModelTimeSkale  {100}; // time in msec = 1 minute in real world

// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct CLOCK {
  byte hours;
  byte minutes;
  unsigned long duration;
  unsigned long stamp;
} modelClock{0, 0, ModelTimeSkale, 0};

void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  pinMode (LED1, OUTPUT);
}

void loop () {
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  if (currentTime - modelClock.stamp >= modelClock.duration) {
    modelClock.stamp = currentTime;
    ++ modelClock.minutes %= 60;
    if (!modelClock.minutes)  ++ modelClock.hours %= 24;
    if (modelClock.hours < 10) Serial.print("0");
    Serial.print(modelClock.hours); Serial.print(":");
    if (modelClock.minutes < 10) Serial.print("0");
    Serial.print(modelClock.minutes), Serial.println(" o´clock");
  }
  {
   if (modelClock.stamp = 01);
    digitalWrite(LED1, HIGH);
  }    
  }

I changed the scale to 100 to troubleshoot quicker..

Hello

I did it for testing purposes too.

You have to check the currrent model time in hours and minutes to the time of event in hours and minutes and run the desired ilimunation event.
Have a nice day and enjoy coding in C++.

{
 modelClock.stamp = currentTime;
if (modelClock.stamp = 5,0)
  {
    digitalWrite(LED1, HIGH);  
    }

I think you mean something like this, do you? Sorry, i really don't mind if you want to explain to me like i'm 5 ...I'm lost as to which part to reference..