Note: I’ve finally gotten around to a much-needed rewrite for WiFi. You can find that code and another video here.
Back at Veracode’s last Hackathon, I published the video below. People have started discovering this and asking questions about it, so here is the code for it:
/*
Weasley Clock
-------------
Created by Doug "LegoDoug" Wilcox for Veracode Hackathon IX.
Video of the completed project is at https://www.youtube.com/embed/oRUViFnxKsg.
"Share and enjoy."
*/
// Arduino SPI (Serial Peripheral Interface) library - https://www.arduino.cc/en/Reference/SPI
#include <SPI.h>
// Arduino Ethernet library - https://www.arduino.cc/en/Reference/Ethernet
#include <Ethernet.h>
// Arduino Stepper library - https://www.arduino.cc/en/Reference/Stepper
#include <Stepper.h>
// Adafruit REST IO library - https://learn.adafruit.com/adafruit-io/arduino
// See also https://www.arduino.cc/en/Tutorial/WebClientRepeating
#include "Adafruit_IO_Client.h"
// assign a MAC address for the ethernet controller.
byte mac[] = {
0x8C, 0xDC, 0xD4, 0x4A, 0xC9, 0xC2
};
// initialize the library instance:
EthernetClient client;
// last time the Arduino connected to the server, in milliseconds
unsigned long lastConnectionTime = 0;
// delay between retrieving updates, in milliseconds
const unsigned long requestInterval = 5000L;
// Configure Adafruit IO access. You will need to create your own
// Adafruit IO account (free), and set up a feed, and provide your
// feed and AIO key in the code below.
#define AIO_FEED "weasleyclockstatus"
#define AIO_KEY "XXXXXXXXXXXXXXXXXXXXXXXXXXX"
// Create an Adafruit IO Client instance. Notice that this needs to take a
// WiFiClient object as the first parameter, and as the second parameter a
// default Adafruit IO key to use when accessing feeds (however each feed can
// override this default key value if required, see further below).
Adafruit_IO_Client aio = Adafruit_IO_Client(client, AIO_KEY);
// Alternatively to access a feed with a specific key:
Adafruit_IO_Feed clockFeed = aio.getFeed(AIO_FEED, AIO_KEY);
// States - These are the codes that correspond to specific clock positions.
const String LD_HOME = "ld_na";
const String LD_TRAVELING = "ld_tr";
const String LD_VERACODE = "ld_of";
const String LD_CHURCH = "ld_ch";
const String LD_MORTAL_PERIL = "ld_mp";
const String LD_GLOUCESTER = "ld_gl";
// Steps - How many steps the motor needs to move to point to a specific position
// on the clock.
const int STEPS_HOME = 0;
const int STEPS_TRAVELING = 750;
const int STEPS_VERACODE = 1600;
const int STEPS_CHURCH = 2450;
const int STEPS_MORTAL_PERIL = 3350;
const int STEPS_GLOUCESTER = 4350;
// Someday, I will determine what this actually does. (I don't think, functionally,
// it has any affect.)
const int stepsPerRevolution = 200;
long motorPosition = 0L; // Number of steps the motor has taken.
String fValue = ""; // Feed value.
Stepper clockStepper(stepsPerRevolution, 7, 6, 5, 4, 8);
void setup() {
// start serial port:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native
// USB port only, on certain Arduino models.
}
// give the ethernet module time to boot up:
delay(1000);
// start the Ethernet connection using a fixed IP address and DNS server:
//Ethernet.begin(mac, ip, myDns);
// Or, just start it with dynamic DNS by giving it a MAC address.
Ethernet.begin(mac);
// print the Ethernet board/shield's IP address:
Serial.print("My IP address: ");
Serial.println(Ethernet.localIP());
clockStepper.setSpeed(20);
}
void loop() {
// Wait for a bit and read the current feed value.
Serial.println(F("Waiting ..."));
delay(requestInterval);
// To read the latest feed value call the receive function on the feed.
// The returned object will be a FeedData instance and you can check if it's
// valid (i.e. was successfully read) by calling isValid(), and then get the
// value either as a text value, or converted to an int, float, etc.
FeedData latest = clockFeed.receive();
if (latest.isValid()) {
Serial.print(F("Received value from feed: ")); Serial.println(latest);
// By default the received feed data item has a string value, however you
// can use the following functions to attempt to convert it to a numeric
// value like an int or float. Each function returns a boolean that indicates
// if the conversion succeeded, and takes as a parameter by reference the
// output value.
// Want some fun? Learning about "conversion from 'FeedData' to non-scalar
// type 'String' requested" the hard way.
//
// If I remember correctly, it was a casting error caused by trying to use
// the 'latest' variable as a String, directly.
// The following line casts 'latest' to a string and lets us use it as 'fValue'.
fValue = latest;
if(fValue == LD_HOME) {
Serial.println("Nashua");
stepBySteps(STEPS_HOME);
}
if(fValue == LD_TRAVELING) {
Serial.println("Traveling");
stepBySteps(STEPS_TRAVELING);
}
if(fValue == LD_VERACODE) {
Serial.println("Veracode");
stepBySteps(STEPS_VERACODE);
}
if(fValue == LD_CHURCH) {
Serial.println("Church");
stepBySteps(STEPS_CHURCH);
}
if(fValue == LD_MORTAL_PERIL) {
Serial.println("Mortal Peril!");
stepBySteps(STEPS_MORTAL_PERIL);
}
if(fValue == LD_GLOUCESTER) {
Serial.println("Glostah");
stepBySteps(STEPS_GLOUCESTER);
}
} else {
Serial.print(F("Failed to receive the latest feed value!"));
}
}
void stepBySteps(int newPosition) {
if(motorPosition == newPosition) {
Serial.println("No movement required.");
return;
}
long steps = newPosition - motorPosition;
clockStepper.step(steps);
motorPosition = newPosition;
Serial.print("position should now be:" );
Serial.println(motorPosition);
}


















