Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 2 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
|
I love clocks, especially when they are different from standard. There are millions of standard-clocks - analog and digital. What I mean are clocks with a different way to show the time.
A view weeks ago I found the particle photon, and my first idea was to program a clock, that is synchronizing over the internet. I found many solutions to realize that, and so I collected many ideas. But my problem was, to sort all of them and make a decision which clock to build. Finally I had three favorites:
- Analog with moving-coil instruments (example)
- a TIX-clock (example)
- a binary clock (example)
So I thought to build all of them. And to save money I decided to build all 3 clocks at once in one enclosure. Now I had the next problem: which enclosure? And then I saw the "Ribba-Frames" at IKEA and I knew - "That's it!"
Analog clock with moving-coil instrumentsHere I learned how to synchronize the RTC of the photon with a time-server and how to calculate the summertime. Because I'm from Austria, I implemented the European rules (last sunday in march till last sunday in october always at 2 o'clock).
Here I learned how to program a 8x8 LED matrix with a MAX7219 and how to implement Blynk, to turn the display (or parts of it) on/off and dim the whole display via iPhone.
For the final clock I have to combine both parts to one. And I have to split the matrix to individual LEDs.
More to come...
OtherClocks.ino
Arduino// This #include statement was automatically added by the Particle IDE.
/*
Ausgabe der Uhrzeit auf 3 unterschiedliche Arten:
1. Binary-Clock
2. TIX-Clock
3. Analog mit 2 Drehspulinstrumenten
Die Ansteuerung erfolgt über einen MAX7219/7221 IC.
https://playground.arduino.cc/Main/MAX72XXHardware
http://playground.arduino.cc/Main/LedControl
Die Matrix wird um 90 Grad im Uhrzeigersinn gedreht, damit die Ansteuerung
einfacher realisiert werden kann.
Die Uhrzeit wird 1x am Tag über das Netzwerk synchronisiert
*/
// SYSTEM_MODE(SEMI_AUTOMATIC) // Manual control of WiFi (start even WiFi ist not available)
// SYSTEM_THREAD(ENABLED) // process code even Network not available
// def Bit-Operationen
#ifndef bitRead
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))
#endif
#include "LedControl-MAX7219-MAX7221.h" // Library for MAX7219
#include "blynk.h" // Connection to Blynk
#include "privateStuff.h" // Place, where private Tokens and Passwords are stored
// Definition of WiFi-Connection
const uint32_t msRetryDelay = 5*60000; // retry WiFi-Connection every 5min
const uint32_t msRetryTime = 30000; // stop trying WiFi-Connect after 30sec
bool retryRunning = false;
Timer retryTimer(msRetryDelay, retryConnect); // timer to retry connecting
Timer stopTimer(msRetryTime, stopConnect); // timer to stop a long running try
// create the LedControl
#define lc_data A5
#define lc_load A4
#define lc_myclock A3
LedControl lc = LedControl(lc_data, lc_myclock, lc_load,1);
// Pins for Analog-Clock
#define hourPin D0
#define minutePin D1
// Definition of Variables
uint8_t tixWait = 4; // new Sequence for Tix-Clock all x Seconds
bool showBinary = true;
bool showTix = true;
bool showAnalog = true;
uint8_t actHour = 0; // actual Time
uint8_t actHour12 = 0;
uint8_t actMinute = 0;
uint8_t actSecond = 0;
uint8_t actDay = 1;
uint8_t actMonth = 1;
uint8_t actWeekday = 0;
uint8_t row[8]; // 8 Rows of LED-Matrix
uint8_t sequence[9]; // generated Sequence Tix
uint8_t sequenceMinE[9]; // Sequence Einerstelle Minute Tix
uint8_t sequenceMinZ[6]; // Sequence Zehnerstelle Minute Tix
uint8_t sequenceHourE[9]; // Sequence Einerstelle Hour Tix
uint8_t sequenceHourZ[3]; // Sequence Zehnerstelle Hour Tix
uint8_t oldMinute, oldSecond = 0; // Buffer of "old Minute" and "old Second"
#define ONE_DAY_MILLIS (24 * 60 * 60 * 1000) // 24 Hours in ms
unsigned long lastSync = millis();
// define Blynk-Buttons (Blynk-Remote on iPhone)
BLYNK_WRITE(V0) {
int i=param.asInt();
if (i==1) {
showBinary = true;
}
else {
showBinary = false;
}
}
BLYNK_WRITE(V1) {
int i=param.asInt();
if (i==1) {
showTix = true;
}
else {
showTix = false;
}
}
BLYNK_WRITE(V2) {
int i=param.asInt();
if (i==1) {
showAnalog = true;
}
else {
showAnalog = false;
}
}
BLYNK_WRITE(V9) { // Einstellen der Helligkeit
int i=param.asInt();
lc.setIntensity(0,i);
}
void setup() {
Time.zone(+1); // Definition of timezone
// Serial Console
Serial.begin(9600);
// wake up the MAX72XX from power-saving mode
// set a medium brightness for the Leds (0-15)
lc.shutdown(0,false);
lc.setIntensity(0,1);
// Set Pinmode
pinMode(hourPin, OUTPUT);
pinMode(minutePin, OUTPUT);
// Verbinden mit der Cloud --> Synchronisierung der Zeit
Particle.connect();
if (!waitFor(Particle.connected, msRetryTime)) {
Serial.println("Not connected to Cloud");
WiFi.off(); // no luck, no need for WiFi
}
else {
Serial.println("Connected to Cloud");
}
// Time.setTime(1512172680); // Testzeit
// activate Blynk
Blynk.begin(BLYNK_AUTH_TOKEN); // Code is defined in privateStuff.h
}
void loop() {
Blynk.run(); //start Blynk
if (millis() - lastSync > ONE_DAY_MILLIS && Particle.connected()) {
// Request time synchronization from the Particle Cloud once a day
Particle.syncTime();
lastSync = millis();
}
// Get the current time
actHour = Time.hour();
actHour12 = Time.hourFormat12();
actMinute = Time.minute();
actSecond = Time.second();
actDay = Time.day();
actMonth = Time.month();
actWeekday = Time.weekday() - 1; // Sonntag = 0; Montag = 1; ... Samstag = 6
checkSummertime();
// clear Array (löscht die LED-Matrix)
memset(row,0,sizeof(row));
// generate Binary-Clock
if (showBinary == true) {
BinaryClock();
}
// generate Tix-Clock
if (showTix == true) {
TixClock();
}
// generate Analog-Clock
if (showAnalog == true) {
AnalogClock();
}
// Show on 8x8 LED-Matrix
for(int i = 0; i <= 7; i++) {
lc.setRow(0,i,row[i]);
}
// Reconnect to WiFi if not available in the past
if (!retryRunning && !Particle.connected()) { // if we have not already scheduled a retry and are not connected
Serial.println("schedule");
stopTimer.start(); // set timeout for auto-retry by system
retryRunning = true;
retryTimer.start(); // schedul a retry
}
}
// **************************
// ***** Binary Clock *****
// **************************
void BinaryClock() {
// zerlegen in Zehner- und Einer-Stellen
row[5] = numZ(actHour);
row[4] = numE(actHour);
row[3] = numZ(actMinute);
row[2] = numE(actMinute);
row[1] = numZ(actSecond);
row[0] = numE(actSecond);
}
// ***********************
// ***** Tix Clock *****
// ***********************
void TixClock() {
int i;
// generieren einer neuen Sequenz alle "tixWait" Sekunden
if(actSecond % tixWait == 0 && oldSecond != actSecond) {
// Einerstelle Minuten
generateSequence(9);
for(i = 0; i < 9; i++) {
sequenceMinE[i] = sequence[i];
}
// Zehnerstelle Minuten
generateSequence(6);
for(i = 0; i < 6; i++) {
sequenceMinZ[i] = sequence[i];
}
// Einerstelle Stunden
generateSequence(9);
for(i = 0; i < 9; i++) {
sequenceHourE[i] = sequence[i];
}
// Zehnerstelle Stunden
generateSequence(3);
for(i = 0; i < 3; i++) {
sequenceHourZ[i] = sequence[i];
}
oldSecond = actSecond; // maximal 1x pro Sekunde eine neue Sequenz
}
// Aktivieren Anzahl LEDs Einer-Stelle Minuten
for(i = 0; i < numE(actMinute); i++) {
bitSet(row[sequenceMinE[i]/3], 7-sequenceMinE[i]%3);
}
// Aktivieren Anzahl LEDs Zehner-Stelle Minuten
for(i = 0; i < numZ(actMinute); i++) {
bitSet(row[sequenceMinZ[i]/3+3], 7-sequenceMinZ[i]%3);
}
// Aktivieren Anzahl LEDs Einer-Stelle Stunden
for(i = 0; i < numE(actHour); i++) {
bitSet(row[sequenceHourE[i]/3+5], 7-sequenceHourE[i]%3);
}
// Aktivieren Anzahl LEDs Zehner-Stelle Stunden
for(i = 0; i < numZ(actHour); i++) {
bitSet(row[sequenceHourZ[i]/3+7], 2-sequenceHourZ[i]%3);
}
}
// **************************
// ***** Analog Clock *****
// **************************
void AnalogClock() {
analogWrite(hourPin, actHour12*20);
analogWrite(minutePin, actMinute*4);
}
// *******************************************************************
// ***** Funktionenen zum Zerlegen in Zehner- und Einerstellen *****
// *******************************************************************
int numZ(int num) { // Zehnerstelle berechnen
num = num / 10; // Integer division by 10 (discard remainder)
return num;
}
int numE(int num) { // Einerstelle berechnen
num = num % 10; // Modulo division by 10 (keep remainder only)
return num;
}
// ****************************************************
// ***** Funktion zur Ermittlung der Sommerzeit *****
// ****************************************************
void checkSummertime() {
if ( ( actMonth > 3 && actMonth < 10 ) || // April - September
( actMonth == 3 && actDay >= 25 && actWeekday == 0 && actHour >= 2 ) || // letzter Sonntag im März, ab 2 Uhr
( actMonth == 3 && actDay - actWeekday >= 25 && actWeekday > 0 ) || // Tag liegt nach dem letzten Sonntag im März
( actMonth == 10 && actDay >= 25 && actWeekday == 0 && actHour < 2 ) || // letzter Sonntag im Oktober, bis 2 Uhr
( actMonth == 10 && actDay - actWeekday < 25 ) ) { // Tag liegt vor dem letzten Sonntag im Oktober
Time.beginDST(); // Sommerzeit
} else {
Time.endDST(); // keine Sommerzeit
}
}
// ************************************************************
// ***** Ausgabe der Uhrzeit auf serielle Schnittstelle *****
// ************************************************************
void printKonsole() {
Serial.print(actHour);
Serial.print(":");
Serial.print(actMinute);
Serial.print(" (DST: ");
Serial.print(Time.isDST());
Serial.print(")");
Serial.print(" Wochentag: ");
Serial.print(actWeekday);
Serial.println();
}
// **********************************************************
// ***** generate Sequence (0 to n-1 in random order) *****
// **********************************************************
void generateSequence(int n) {
int i, j, temp;
// generate sorted sequence from 0 to n-1
for (i = 0; i < n; i++) {
sequence[i] = i;
}
// random permutation (algorithm by Ronald Fisher and Frank Yates)
// randomSeed(millis()); // really random
for (i = n-1; i > 0; i--) {
j = random(0, i+1);
temp = sequence[i]; // swap sequence[i] with sequence[j]
sequence[i] = sequence[j];
sequence[j] = temp;
}
}
// ******************************************
// ***** Routinen für WLAN-Verbindung *****
// ******************************************
void retryConnect() {
if (!Particle.connected()) { // if not connected to cloud
Serial.println("reconnect");
stopTimer.start(); // set of the timout time
WiFi.on();
Particle.connect(); // start a reconnection attempt
}
else { // if already connected
Serial.println("connected");
retryTimer.stop(); // no further attempts required
retryRunning = false;
}
}
void stopConnect()
{
Serial.println("stopped");
if (!Particle.connected()) // if after retryTime no connection
WiFi.off(); // stop trying and swith off WiFi
stopTimer.stop();
}
privateStuff.h
Arduinohttps://docs.particle.io/guide/getting-started/build/photon/#adding-files-to-your-app
/*
AUTH_TOKEN.h
Store private tokens in AUTH_TOKEN.h and add that
file to .gitignore so personal data is not shared
*/
// #define BLYNK_AUTH_TOKEN "store-real-blynk-token-here"
#define BLYNK_AUTH_TOKEN "store-real-blynk-token-here"
Comments