Mariano Elia
Published © GPL3+

Particle Photon Flip Dot Clock

The flip dot display uses an electromechanical dot matrix that spin from black to yellow to display text, numbers and more!

IntermediateFull instructions provided2 hours6,666

Things used in this project

Hardware components

Photon
Particle Photon
×1
SparkFun Logic Level Converter - Bi-Directional
SparkFun Logic Level Converter - Bi-Directional
×1
SparkFun RS485 Transceiver
This board does not need a logic level converter as it operates at 3.3V A regular MAX485 breakout board can also be used instead of this, but with a converter to 5V
×1
Alfa Zeta XY5 Flip Dot Display 7x28
×1
24V 0.8A min DC Power Supply
×1

Software apps and online services

Particle Web IDE

Story

Read more

Schematics

Fritzing Diagram

Screen shot 2017 05 05 at 2 54 46 pm tahst4grz5

Code

FlipDotTimeCode

Arduino
/*
* Using the Particle Photon to control an Alfa Zeta 7x28 flip dot display.
* The program displays the respective time in either 12 or 24 hour format.
* 
* Hardware Needed:
* - Particle Photon
* - Alfa Zeta flip dot display
* - RS485 converter (5V operating logic! The photon is 3.3V)
* - Logic level converter (optionally a MAX3485 chip for the RS485 converter can be used which operates at 3.3V OR 5V therefore eliminating this component)
* - 24V Power supply
* - 5V Power supply (USB power is fine)
* Date: May 5, 2017
* By: Mariano Elia
*/

//Each number from left to right in the 2D array, is the respective dots from right to left to be lit up 
//
  byte numbersInBinary[][4]{
  {0b111110, 0b100010, 0b100010, 0b111110}, //zero
  {0b111110, 0b000000, 0b000000, 0b000000}, //one
  {0b111010, 0b101010, 0b101010, 0b101110}, //two
  {0b111110, 0b101010, 0b101010, 0b100010}, //three
  {0b001000, 0b111110, 0b001000, 0b111000}, //four
  {0b101110, 0b101010, 0b101010, 0b111010}, //five
  {0b001110, 0b001010, 0b001010, 0b111110}, //six
  {0b111110, 0b100000, 0b100000, 0b110000}, //seven
  {0b111110, 0b101010, 0b101010, 0b111110}, //eight
  {0b111110, 0b101000, 0b101000, 0b111000} //nine

};

//To invert white with black, a ~ is used as a prefix
byte invertedNumbersInBinary[][4]{
  {~0b111110, ~0b100010, ~0b100010, ~0b111110}, //zero
  {~0b111110, ~0b000000, ~0b000000, ~0b000000}, //one
  {~0b111010, ~0b101010, ~0b101010, ~0b101110}, //two
  {~0b111110, ~0b101010, ~0b101010, ~0b100010}, //three
  {~0b001000, ~0b111110, ~0b001000, ~0b111000}, //four
  {~0b101110, ~0b101010, ~0b101010, ~0b111010}, //five
  {~0b001110, ~0b001010, ~0b001010, ~0b111110}, //six
  {~0b111110, ~0b100000, ~0b100000, ~0b110000}, //seven
  {~0b111110, ~0b101010, ~0b101010, ~0b111110}, //eight
  {~0b111110, ~0b101000, ~0b101000, ~0b111000} //nine
};
byte colonOn[]  = {0x0, 0x14, 0x0};
byte colonOff[] = {0x0, 0x0, 0x0};
byte invertedColonOn[] = {0x7F, 0x6D, 0x7F};
byte invertedColonOff[] = {0x7F, 0x7F, 0x7F};
byte space[] = {0x0};
byte invertedSpace[] = {0x7F};
//All pixels white
byte allWhite[] { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f}; //equivalent to 0b1111111
//All pixels black
byte allBlack[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //equivalent to 0b0000000

//prepare the display to receive data.
byte startTransmission[] = {0x80, 0x83, 0xFF};
//confirm the end of the bytes sent
byte endTransmission[] = {0x8F};
int colonState = true;
unsigned long previousMillis = 0; 

/*
* Variables to change for customization
*/
const long interval = 1000; //interval in which to flip on and off the colon    
bool inverted = false; //choose to have black as digits, or as blank space
bool flashingColon = true;  //change to false if a static colon is wanted instead
int timeZone = -4; //change time zone


void setup() {
    //Serial is the usb port, Serial1 are the TX/RX pins, and Serial2 are the built in LED pins.
    //Serial1 is being used for this project.
   Serial1.begin(57600);  
   flipBlackAndWhite(); //start with a cool effect
}


void loop() {
    displayTime();
}


/*
* Retrieves the most recent time, and displays it on the flip dot.
* A flashing center colon is optional. 
*/
void displayTime(){

    //Retrieve the most recent time, and separate it up into individual integers
    int currentTime = getTime();
    int thousands = currentTime/1000; 
    int hundreds = currentTime%1000/100;
    int tens = ((currentTime%1000)%100)/10;
    int ones = ((currentTime%1000)%100)%10;
    //The display is 28 columns long. The columns are displayed right to left
    Serial1.write(startTransmission, 3); //send a 3 byte message to start the Serial communication
    displayDigit(-1); //three spaces on the right
    displayDigit(-1);
    displayDigit(-1);
    displayDigit(ones); //each digit is 4 columns long
    displayDigit(-1); //add a space in between digits
    displayDigit(tens);
    displayColon(); // 3 columns long
    displayDigit(hundreds);
    displayDigit(-1);
    displayDigit(thousands);
    displayDigit(-1); //add 4 spaces to the beginning
    displayDigit(-1);
    displayDigit(-1);
    displayDigit(-1);
    Serial1.write(endTransmission, 1); //Tell the flip dots to change
}
/*
* Uses Particle's time library to retrieve internet time. The time zone can be altered.
* Returns an int with the concatinated hour and minute. Ex 12:45 is 1245
*/
int getTime(){
    Time.zone(timeZone);
    //Uncomment if a 24 hour variant is wanted
    //return (Time.hour())*100+Time.minute(); //24 hour
    return (Time.hourFormat12())*100+Time.minute(); //12 hour
}

/*
* A single integer number to be displayed on the flip dots.
* A -1 signifies a space, which is one column wide and all black.
*/
void displayDigit(int num){
    if (!inverted){ 
    if (num == -1){
        Serial1.write(space, 1);
    }
    else{
    Serial1.write(numbersInBinary[num], 4);
    }
    }
    //else if it is inverted
    else {
        if (num == -1){
        Serial1.write(invertedSpace, 1);
    }
    
    else{
    Serial1.write(invertedNumbersInBinary[num], 4);
    }
    }
    
}

/*
* Displays the center colon in a flashing or static manner.
*/
void displayColon(){
     /*
    Instead of using a delay which stops the processor, a much better approach is to use a timer to
    turn on and off the colon separator when the proper time is reached.
    */
    if (flashingColon){ //*hint*, the same as if (flashingColon == true){
     unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval) { //use a timer based delay
         previousMillis = currentMillis;
        if (colonState){
            if(!inverted) //if it is not inverted, display a normal colon
            Serial1.write(colonOn, 3);
            else
        Serial1.write(invertedColonOn, 3); //display an inverted colon
        colonState = false;
        }
        else {
            if(!inverted)
        Serial1.write(colonOff, 3);
        else
        Serial1.write(invertedColonOff, 3);
        colonState = true;
        }
    }
    }
    
    else { //choose between a static colon, or no colon
    if (!inverted){
    Serial1.write(colonOn, 3);
    //Serial1.write(colonOff, 3);
    }
    else{
    Serial1.write(invertedColonOn, 3);
    //Serial1.write(invertedColonOff, 3); 
    }
}
}
/*
* An effect which turns all pixels white, delays half a second, then black, then white again.
*/
void flipBlackAndWhite(){
    Serial1.write(startTransmission, 3);
    Serial1.write(allWhite, 28);
    Serial1.write(endTransmission, 1);
    delay(500);
    Serial1.write(startTransmission, 3);
    Serial1.write(allBlack, 28);
    Serial1.write(endTransmission, 1);
    delay(500);
     Serial1.write(startTransmission, 3);
    Serial1.write(allWhite, 28);
    Serial1.write(endTransmission, 1);
    delay(500);
}

Credits

Mariano Elia

Mariano Elia

4 projects • 10 followers
Queen's University: Computer Engineering. Owner of Argon Prototypes Inc. RSGC High School
Thanks to iizukak.

Comments