flyingangel
Published © GPL3+

Other Clocks

A combination of 3 different clocks in one frame.

IntermediateWork in progress5 hours1,193
Other Clocks

Things used in this project

Hardware components

Photon
Particle Photon
×1
Moving Coil 100uA
×2
MAX7219 8x8 LED-Matrix driver
×1

Software apps and online services

Blynk
Blynk

Story

Read more

Code

OtherClocks.ino

Arduino
Code for the particle photon to display all 3 clocks (commends are partly in german - translation in progress)
// 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

Arduino
In this file all secret numbers are stored. Add this code to the original-code or put it in an additional file.
https://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"

Credits

flyingangel

flyingangel

2 projects • 5 followers

Comments