Well that was quite the break.. I’m back in school for a short time, and don’t have my commute time on the train. But here’s a bit about the code. There will not be invented any wheels here, but i thought I would write a bit about it anyway. I do recommend to read the code in the repo, though. The code is well commented.

First of all. Here is a temporary setup of the hardware collected in the last post.

While some of the connections might be a bit intermittent, it works enough to get the code up and running.

While some of the connections might be a bit intermittent, it works enough to get the code up and running.

What time is it?

Before it makes sense to move the hand, we need to know where it should be moved to. In other words; what time it is. For that I used the NTPClient and Timezone libraries.

The most complicated part (but certainly not difficult, thank you JChristensen) was to set the daylight saving rules:

// Daylight Saving Start = UTC + 2h
TimeChangeRule myDST = {"DST", Last, Sun, Mar, 2, 120};
// Daylight Saving Ends = UTC + 1h
TimeChangeRule mySTZ = {"STZ", Last, Sun, Oct, 3, 60};

These two TimeChangeRules simply sets which days the daylight saving starts and ends. In the lines above, daylight saving (DST) starts in the last Sunday in marts at 02:00, and changes the offset from UTC to 120 minutes. It ends (STZ) at last Sunday in October at 03:00, and and changes the offset from UTC to 60 minutes. After that, it was a piece of cake:

int getMinutesOfDay() {
  // Update time
  timeClient.update();

  // Set internal time to epoch time
  setTime(timeClient.getEpochTime());

  // From that, we can get local time with the time zone object myTZ
  time_t local = myTZ.toLocal(now(), &tcr);

  // With local time, taken care of;
  // multiply hours by 60 to get minutes and round minutes to quarters
  int minutesOfDay = (hour(local) * 60) + roundToQuarter(minute(local));

  // Then simply return that value
  return minutesOfDay;
}

Because I wanted a “resolution” of 15 minutes. The solution I ended up with, was a separate function (roundToQuarter), that rounds the time (in minutes) to the nearest 15. With that I could get the hours of day, multiply by 60, and add minutes (rounded to 15), by that, I had the amounts of “minutes passed” at the moment.

To round a integer to nearest 15, this simple math can be used:

int roundToQuarter(int i) {
    return (i + (15 / 2)) / 15 * 15;
}

‘Round it goes

With the time “on hand”, I then just needed to make the motor turn. I used the AccelStepper library for that. The AccelStepper library works by setting the step the motor should move to, and then tell the motor to start running. It might be normal for operation for stepper motors, but it took me some time to get used to. It works perfect for a clock though.

"Move to 15 o' clock when I say to."
...
"Move now."

In that way, I can calculate the step for a given time and move the hand to that step. Because the time is being given in minutes, the position based on the time can be calculated by:

// Takes time of day in minutes and return rotational position
int minutesToPos(int t) {
  float pos = 0;
  if(t >= 0 && t <= 1440){
    pos = (t*(4096.00/1440.00));
  }
  return pos;
}

That gives me the rotational position (in steps), and that can be used to tell the motor where it should run to. These stepper motors have 4096 steps per rotation, and there are 1440 minutes in 24 hours. I cannot remember why I used floating points for the pos. Probably some precision in the math.

I have seen some people write that their version of these steppers didn’t have exactly 4096 steps. My clock have been running for over a month now, and it still seems to be exact on time. But your mileage may vary.

With the step to run to, it was just a few commands away.

void moveTo(int d) {
  // Set new position to move to
  stepper.moveTo(d);
  // Move the motor to set position
  stepper.runToPosition();
  // Hack to reset hand position at midnight:
  if(d == 4096){
    // Resetting position to 0
    stepper.setCurrentPosition(0);
  }
  // Disabled pins after move. It does not seem to be necessary to lock the motor
  // The clock is not that heavy handed. (Only the code...)
  // This saves power.
  stepper.disableOutputs();
}

The “hack” to reset the position is done, because if the hand was left at position 4096, and then told to move to position 42,67 (15 minutes past midnight) it would simply turn anti clockwise to that position. But if the position was reset to 0, the hand would keep turning clockwise. I know the position at midnight always will be 4096, because the round off by the function roundToQuarter.

That’s more or less what makes this clock tick. Now it only has to tick to the right position according to the time.

Where is when?

To set the clock, I needed to give it a reference point. I decided the most user friendly way, would be to have to tell it where 00:00 was. Then it could set that as step 0, and work from there.

I did that by making a function moveHand that take a bool as input for the direction, and then move the hand/turns the motor at a speed set by the variable handSpeed, as long as the button is pressed. The direction changes if the button is pressed two times, semi-rapidly.

void moveHand(bool dir) {
  // Start with making sure that nextStep is zero
  nextStep = 0;

  // As the hand should move while the button is pressed;
  // the loop will run while the button is pressed
  while (debounce(buttonPin)) {
    // To give the hand some kind of acceleration, this adds the set speed
    // to the next position for each iteration of the loop
    // There probably should be a max, but I have not felt the need.
    nextStep += handSpeed;
    // Based on the given direction, set nextStep to move either counter clock wise:
    if (dir) {
      // Serial.println("backward!");
      stepper.move(-nextStep);
    }
    // Or clock wise
    else {
      // Serial.println("forward!");
      stepper.move(nextStep);
    }
    // Move the stepper motor to the newly set position
    stepper.runToPosition();
  }
  // Sets the new position as 0
  // And hope that the user did hit the 24/0 hour mark
  stepper.setCurrentPosition(0);
}

The comments should explain what is going on. The variable nextStep increases for each iteration of the while loop, for some acceleration.

While this isn’t all the code, it is all that I will run through. The rest is in the repo. Next time (soon) will be about getting the hardware ready and inside the scale.

Until then!