Another Non Blocking Code Example (Works)

This is not a question but code is below for review and improvement. If you're struggling with non blocking code, this may lead you to a solution.

Goal: At the top of the crow's nest on a pirate ship there is a crow's skeleton. One servo to move the wings, multiple LED's around the edge of the nest, one with a "heartbeat" effect for this project. More details as it emerges.

The hard part (for me) was getting the heartbeat. There are many examples out there but I settled on the "timeline" approach. As the millis increases and reaches a specific point, the LED switches on or off depending on where we are in the "timeline" array. Works as expected.

  • 2 amp 5v wall wart power supply
  • Nano clone
  • 8 red LED's around nest
  • 1 red LED for heartbeat
  • 2 small yellow LED's for eyes
  • Lots of hot melt glue and a little 22ga wire
  • Piano wire to move the wings and beak
/* Crow's Nest 09-28-24
 * See finished video for effect. We have a crow skeleton in a pirate
 * ship crow's nest. Servo moves the wings and beak, LED is positioned
 * in the chest to emulate a heartbeat from a single LED. There are
 * static LED's in the eyes and rim of the nest, powered by the same
 * 5v source as the arduino.
 */
#include <Servo.h>

// pin assignments
const int heartPin = 3;
const int servoPin = 4;

// Servo variables
Servo myservo;

// Keep track of servo direction. 1 increase angle, 0 decrease angle
int dir = 1; 
// Keep track of the last time servo was moved
unsigned long startTimeServo;
// Pause at the end of each sweep
unsigned long servoPause = 1000;
// Increasing the value increases servo speed.
unsigned int servoSpeed = 4;
// Start and stop, 0 to 180.
int startAngle = 20;
int stopAngle = 110;
// Keep track of current servo position
int pos = startAngle;

// LED heartbeat blink variables. Set up an array as a "timeline" to switch on
// and off. off, on, off, on, off with pause.
int timeline[] = { 0, 50, 150, 190, 1000 };
// Track the current index of the array
int index = 0;
// Track the last time the LED was turned on or off.
unsigned long ledCurrentTime = 0;

/*
 * attach pins and servo, delay by half a second to allow the servo to home.
 * @return void
 */
void setup()
{
  // Serial.begin(9600);
  pinMode(heartPin, OUTPUT);
  myservo.attach(servoPin);
  myservo.write(pos);
  delay(500);
  startTimeServo = ledCurrentTime = millis();
}

/*
 * Call the heartbeat and servo move.
 * @return void
 */
void loop()
{
  heartBeat();
  moveServo();
}

/*
 * Heartbeat
 * move along the timeline array to turn led's on and off
 * @return void
 */
void heartBeat()
{
  unsigned long currentMillis = millis();

  if (index >= 5) { index = 0; }
// Ensure LED starts LOW or it will invert the pattern.
  if ((index == 0) && (digitalRead(heartPin)) == HIGH) {
    digitalWrite(heartPin, LOW);
  }

  if (currentMillis - ledCurrentTime >= timeline[index]) {
    //Serial.println("Switch led index " + String(index) + " value " + String(timeline[index]) + " read pin " + String(digitalRead(heartPin)) + " Set to " + String(!digitalRead(heartPin)));
    ledCurrentTime = currentMillis;
    index++;
    digitalWrite(heartPin, !digitalRead(heartPin));
  }
}

/*
 * Move the servo. Pauses by servoPause at the end of each sweep.
 * This code is a little smelly but it works and enough is enough. :-P
 * @return void
 */
void moveServo()
{
  unsigned long currentTimeServo = millis();

  if (currentTimeServo - startTimeServo >= servoSpeed) {

    if (pos >= stopAngle) {
        if (currentTimeServo - startTimeServo <= servoPause) {
          return;
        }
      dir = 0;
    }

    if (pos <= startAngle) {
      if (currentTimeServo - startTimeServo <= servoPause) {
        return;
      }
      dir = 1;
    }
    startTimeServo = currentTimeServo;
    pos = (dir == 1)? ++pos : --pos;
    myservo.write(pos);
  }
}
  if (index >= 5) { index = 0; }
// Ensure LED starts LOW or it will invert the pattern.
  if ((index == 0) && (digitalRead(heartPin)) == HIGH) {
    digitalWrite(heartPin, LOW);
  }

Little improvement, there is no need to do this part constantly, do it only once when it's needed.

For example, replace line index++; with if ( ++index == 5 ) { index = 0; }

Nice. I look forward to running it for a closer look.

This

// Keep track of servo direction. 1 increase angle, 0 decrease angle
int dir = 1; 

obvsly works, but everyone does that IDK why. You could

// Keep track of servo direction. 1 increase angle, -1 decrease angle
int dir = 1; 

and then

  pos += did;

to move the servo because you set dir not to 0 or 1 but to -1 and 1. So it is more like step since it has magnitude and sign build in:

    if (pos >= stopAngle) {
        if (currentTimeServo - startTimeServo <= servoPause) {
          return;
        }
      dir = -1;
    }

a7

you can use dir to do some of the math and only check the time once

void moveServo()
{
    unsigned long currentTimeServo = millis();
    if (currentTimeServo - startTimeServo < servoPause)
        return;

    pos += dir;
    myservo.write(pos);

    if (pos >= stopAngle || pos <= startAngle) {
        dir = -dir;
        startTimeServo = currentTimeServo;
    }
}