Menu

WS2812 (NeoPixel) clock.

mkstevo
2020-05-08
2020-05-28
  • mkstevo

    mkstevo - 2020-05-08

    Here is another clock. This one is designed to drive a NeoPixel type ring of 60 WS2812 LEDs to provide the display.

    As with the Dekatron Clock, there is a PCB with an LCD display, 16F1829, DS3231, and backup battery and little else. The type of processor is not critical, for the WS2812 it needs to be reasonably fast (greater than 16 MHz?) and have a timer interrupt.

    The original code used for driving the WS2812 devices was written by EvanV (Anobium). The DS3231 code is also from one of the demonstration files and uses the standard DS3231 library.

    'A demonstration of Great Cow BASIC supporting the WS2812 LED devices.
    '@author     EvanV
    '@licence    GPL
    '@version    1.00
    '@date       2.09.2016
    

    The WS2821 can be wired with the "first" LED in any orientation. The software provides an offset from 0 to 59 which rotates the clock face by as many LEDs as are needed to move the 12 o'clock position to the top.

    I was inspired by a Velleman version of this which faces the LEDs towards the wall, with the light from the LEDs reflected by the wall itself. With this in mind for a later version, there is an option to mirror the display. The mirror option selects a higher brightness for the LEDs to greater illuminate the wall in this mode.

    Both the offset, and mirror options can be selected from within the menu using the LCD making it possible to try each and decide which is preferred. These settings are then stored in EeProm so that when power is removed and reapplied the settings are returned to the preferences set.

    The first picture shows the first test of the software which was running using simple delays to simulate the time. The first LED is positioned close to where the wire comes out of the bottom of the ring, and the clock face has been offset by 31 pixels to move 12 o'clock into the top position. Here, the hours are red, minutes blue and seconds green. I have since changed the minutes to green, and the seconds to blue. This seemed to give extra clarity to the display.

     

    Last edit: mkstevo 2020-05-08
  • mkstevo

    mkstevo - 2020-05-08

    In this second picture, I've placed the ring behind a semi transparent square of perspex, with the LED ring pushed up to the rear of the perspex. I've also made the hour and minute "hands" three pixels wide. Hours are red, minutes are now green, and seconds now blue.

    It is difficult to capture on camera as the camera exposure is never right for the LEDs and the background together.

     

    Last edit: mkstevo 2020-05-08
  • mkstevo

    mkstevo - 2020-05-08

    Here is the code, and gerber files for a PCB. I've only ordered my PCBs today, once they arrive I'll build one up and take some pictures of the finished clock.

    A minor update to the code. The "five minute" or "hour" markers (which are on in each of the pictures) can now be selected from the menu. They can be turned off, one only shown in the 12 o'clock position, or shown at the 5 minute (hour), 10 minute or 15 minute positions on the clock face. This setting is then saved in EeProm.

    Fixed a silly error that caused part of the hour "hand" to disappear at midday only.
    All adjustments can now be made without referring to the LCD.
    LCD can optionally be "removed".
    LED brightness can be adjusted without entering the menu. Simply press Up or Down while displaying the time to increase or decrease the brightness.

    Minor improvements. The "minute/hour" markers could disappear under very specific values of the offset. This bad behaviour should be improved, if not cured.

    11/02/2021: Added and updated the code for the "new" display modes. Also added the option for changing the LED type to one where the 24bit data is encoded as "RGB" (MSB to LSB) or as "GRB". The temperature is now shown on the clockface by "winking" the relevant LED. The temperature can be shown in "full" by pressing the "OK" button for a short time, the display then returns to showing the time after around 30 seconds.

    There is a description and video of the additional modes here: https://sourceforge.net/p/gcbasic/discussion/579125/thread/ca45330873/#9f9a

    The display modes can be selected in the LCD menu and changed at will without needing to recompile. The position of the display can also be moved or rotated using the menu.

    The same code is used in my "Eiffel Tower" clock with more pictures and video here: https://sourceforge.net/p/gcbasic/discussion/projects&guides/thread/81697de67c/#7d6a

    This code will work with the 18F14k22 or the 16F1829 by defining the relevant chip model and a "#Define" which includes some additional delays in the WS2812 (NeoPixel) data send routine to allow it to work at 64MHz.

     

    Last edit: mkstevo 2021-02-12
  • mkstevo

    mkstevo - 2020-05-08

    A messy, but complete prototype, with the WS2812 ring hung on the wall out of sight.

     

    Last edit: mkstevo 2020-05-08
  • jackjames

    jackjames - 2020-05-08

    Very interesting.
    I am attaching a simple algorithm for calculating the leap year (in italian):

    '       ========================================================================
    '       E' vera se l'anno inviato e' bisestile.
    '       Leap year ------------------------------------------------------------------------
    Function AnnoBisestile (Annus As Byte) As Byte
            Dim Anno As word
    
            Anno = Annus + 2000
            If ((Anno % 4 = 0) And (Anno % 100 <> 0)) Or (Anno % 400 = 0) Then
               AnnoBisestile = 1
            Else
               AnnoBisestile = 0
            End If
    End Sub
    
     
    • mkstevo

      mkstevo - 2020-05-09

      Thanks Jack. I'm fairly sure that the DS3231 handles leap year calculation up until 2100. Well beyond me needing to worry about it!

       
  • stan cartwright

    stan cartwright - 2020-05-08

    I've seen kits for these on ebay just recent. Didn't know the leds show time...do they?
    I got a hp frequency counter with nixie tubes I was thinking of hacking but instead going
    to try draw 4 tubes on a glcd and draw the segments. There might be a gcb demo.
    A uno has a xtal so it might be stable enough for a simple clock demo.
    Interesting what others are doing.

     
    • mkstevo

      mkstevo - 2020-05-09

      The NeoPixel (WS2821) rings don't by themselves show the time. They need most of the "gubbins" on the bread board in the picture of the prototype in order for them to do so.

      In fact, without at least some "gubbins", they don't do anything at all. Wire them up with just 5V and ground and you'll get nothing. It is not until you start sending them some data in their DIN connection that they come to life. Only then if that data format is absolutely perfectly timed too.

      When developing the clock (and when I took the first picture of the plain ring) I had a simulated clock running on a 12F1840. This sent out the "clock face" with the hours minutes and seconds. The hours minutes and seconds were incremented after a delay to simulate one second. With the delay set to 893 mS with a further delay of 465 uS the "clock" was accurate to 5 seconds in 36 hours. With a crystal you might be able to improve on that, but as the DS1307 and DS3231 are fairly inexpensive, and have battery backup is there any point?

      Heres the very simple clock that uses no DS3231 to keep time, just the delays. It also has no methods to set the time, evrything is fixed so to set the time you have to compile it with the correct start time and then power it up at the correct moment. And never unplug it again...

      'Spins a 60 pixel NeoPixel ring.
      'Forms the basis of a NeoPixel ring clock.
      'The Seconds are indicated in green, the minutes in blue and the hours in red.
      'As the hours, minutes and seconds overlap, a combination of colours is shown.
      
      'Added five minute/hour markers
      Setup:
      #Chip 12F1840, 32
      #Option Explicit
      
      'WS2812 SPECIFIC CONSTANTS
      #Define DIN PortA.2 'Pin5
      #Define Period_uS   0x35  'Period for LED internal processing
      
      Dir     DIN     Out 'Pin5
      
      '0xFF0000 'GREEN
      '0x00FF70 'MAGENTA
      '0x00FF00 'RED
      '0x80FF00 'Yellow
      '0x0000FF 'Blue
      '0xFF00AA 'CYAN
      
      Declare_Variables:
      
          Dim ColourToSend    As Long
          Dim SecondsColour   As Long
          Dim MinutesColour   As Long
          Dim MinutesTColour  As Long
          Dim HoursColour     As Long
          Dim HoursTColour    As Long
          Dim HourMarker      As Long
          Dim TwelveMarker    As Long
      
          Dim SecondsPixel    As Byte
          Let SecondsPixel    = 0
          Dim MinutesPixel    As Byte
          Let MinutesPixel    = 0
          Dim MinutesTDisplay As Byte
          Let MinutesTDisplay = 0
          Dim MinutesFDisplay As Byte
          Let MinutesFDisplay = 0
          Dim HoursPixel      As Byte
          Let HoursPixel      = 0
          Dim HoursTDisplay   As Byte
          Let HoursTDisplay   = 0
          Dim HoursFDisplay   As Byte
          Let HoursFDisplay   = 0
          Dim HoursOffset     As Byte
          Let HoursOffset     = 0
          Dim SecondsDisplay  As Byte
          Let SecondsDisplay  = 0
          Dim MinutesDisplay  As Byte
          Let MinutesDisplay  = 0
          Dim HoursDisplay    As Byte
          Let HoursDisplay    = 0
      
          Dim Offset          As Byte
          Dim HourMarkPos     As Byte
          Dim TimeLoop        As Byte
          Dim MarkCount       As Byte
          Dim MirrorDisplay   As Byte
      
      
          Dim TimeOutOcurred  As Bit
      
      Start_Of_Program:
      WS2812_Init
      
      'Offset enables the display to be positioned with the "Twelve O'Clock LED
      'positioned at the top with the connections to LED zero ,and LED Zero not
      'positioned at the top. So the wires can be fed in at the bottom, yet with
      'twelve o'clock still displayed at the top.
      
      'With offset set to 29 for example, there will be 29 LEDs from the input
      'LED before twelve o'clock.
      
      'Offset has only had limited testing. There may be certain combinations
      'of offset and "MinuteMarkers - MarkSpacing" which give incorrect results!
      
      Let Offset = 29
      If Offset > 59 Then
          Let Offset = 0
      End If
      Let Offset = 60 - Offset
      
      'MarkSpacing generates markers at intervals around the clock face.
      'This makes most sense with them set to either 5 which marks the hours and
      'five minute intervals, or 15 which marks the 'quarter hour' intervals.
      'The final software may allow this to be selected.
      
      'Setting MarkSpacing to 1 shows a single marker at the twelve o'clock point
      #Define MarkSpacing 5
      
      Let MirrorDisplay = 0
      
      If MirrorDisplay = 0 Then
          Let TwelveMarker  = 0x050505 'White
          Let HourMarker    = 0x010101 'Pale White
          Let HoursColour   = 0x001F00 'Red
          Let HoursTColour  = 0x000200 'Pale Red
          Let MinutesColour = 0x1F0000 'Green
          Let MinutesTColour= 0x020000 'Pale Green
          Let SecondsColour = 0x000006 'Blue
      
          'Let MinutesColour = 0x00006F 'Blue
          'Let MinutesTColour= 0x000004 'Pale Blue
          'Let SecondsColour = 0x1F0000 'Green
      Else
          Let TwelveMarker  = 0x090909 'White
          Let HourMarker    = 0x050505 'Pale White
          Let HoursColour   = 0x002F00 'Red
          Let HoursTColour  = 0x000800 'Pale Red
          Let MinutesColour = 0x2F0000 'Green
          Let MinutesTColour= 0x080000 'Pale Green
          Let SecondsColour = 0x000040 'Blue
      
          'Let MinutesColour = 0x00009F 'Blue
          'Let MinutesTColour= 0x000040 'Pale Blue
          'Let SecondsColour = 0x2F0000 'Green
      End If
      
      Set_Time:
      'Set the time to start at 11.00
      Let HoursPixel   = 11
      Let MinutesPixel = 0
      Let HoursPixel   = HoursPixel * 5 'Correct the time position
      Wait 100 mS
      Time_Loop
      
      Sub Time_Loop
      
          Let ColourToSend = 0x000000 'Black (Off)
      
          Do
      
              'Do the display calculations at the start so that the initial display is correct
      
              If SecondsPixel > 59 Then
                  Let SecondsPixel = 0
                  Let MinutesPixel = MinutesPixel + 1
              End If
              If MinutesPixel > 59 Then
                  Let MinutesPixel = 0
                  Let HoursPixel = HoursPixel + 5
              End If
              If HoursPixel > 59 Then
                  Let HoursPixel = 0
              End If
      
              Let HoursOffset = HoursPixel
              If MinutesPixel > 12 Then
                  Let HoursOffset = HoursPixel + 1
              End If
              If MinutesPixel > 24 Then
                  Let HoursOffset = HoursPixel + 2
              End If
              If MinutesPixel > 36 Then
                  Let HoursOffset = HoursPixel + 3
              End If
              If MinutesPixel > 48 Then
                  Let HoursOffset = HoursPixel + 4
              End If
      
              If MirrorDisplay = 0 Then
                  Let SecondsDisplay = SecondsPixel
                  Let MinutesDisplay = MinutesPixel
                  Let HoursDisplay   = HoursOffset
              Else
                  Let SecondsDisplay = 60 - SecondsPixel
                  Let MinutesDisplay = 60 - MinutesPixel
                  Let HoursDisplay   = 60 - HoursOffset
                  If SecondsDisplay > 59 Then
                      Let SecondsDisplay = 0
                  End If
                  If MinutesDisplay > 59 Then
                      Let MinutesDisplay = 0
                  End If
                  If HoursDisplay > 59 Then
                      Let HoursDisplay = 0
                  End If
              End If
      
              Let MinutesTDisplay   = MinutesDisplay - 1
              If MinutesTDisplay > 59 Then
                  Let MinutesTDisplay = 59
              End If
              Let MinutesFDisplay   = MinutesDisplay + 1
              If MinutesFDisplay > 59 Then
                  Let MinutesFDisplay = 0
              End If
      
              Let HoursTDisplay   = HoursDisplay - 1
              If HoursTDisplay > 59 Then
                  Let HoursTDisplay = 59
              End If
              Let HoursFDisplay   = HoursDisplay + 1
              If HoursFDisplay > 59 Then
                  Let HoursFDisplay = 0
              End If
      
              Let TimeLoop  = Offset
              'Let MarkCount = 255
              let MarkCount = Offset % MarkSpacing
      
              Repeat 60
                  Wait While TimeOutOcurred =  0
                  IntOff
                  Let ColourToSend = 0x000000 'Black (Off)
                  If TimeLoop = SecondsDisplay Then
                      Let ColourToSend = ColourToSend + SecondsColour
                  End If
      
                  If TimeLoop = MinutesTDisplay Then
                      Let ColourToSend = ColourToSend + MinutesTColour
                  End If
                  If TimeLoop = MinutesDisplay Then
                      Let ColourToSend = ColourToSend + MinutesColour
                  End If
                  If TimeLoop = MinutesFDisplay Then
                      Let ColourToSend = ColourToSend + MinutesTColour
                  End If
      
                  If TimeLoop = HoursTDisplay Then
                      Let ColourToSend = ColourToSend + HoursTColour
                  End If
                  If TimeLoop = HoursDisplay Then
                      Let ColourToSend = ColourToSend + HoursColour
                  End If
                  If TimeLoop = HoursFDisplay Then
                      Let ColourToSend = ColourToSend + HoursTColour
                  End If
      
      
                  If MarkSpacing > 1 Then
                      If MarkCount = MarkSpacing Then
                          Let ColourToSend = ColourToSend + HourMarker
                          Let MarkCount = 0
                      End If
                      Let MarkCount = MarkCount + 1
                  End If
      
                  If MarkSpacing > 0 Then
                      If TimeLoop = 0 Then
                          Let MarkCount = 1
                          Let ColourToSend = ColourToSend + TwelveMarker
                      End If
                  End If
      
                  WS2812BitBangSendData(ColourToSend)
      
                  Let TimeLoop  = TimeLoop  + 1
      
                  If TimeLoop > 59 Then
                      Let TimeLoop = 0
                  End If
      
                  'Set InterPacketDelay running
                  SetTimer(0, Period_uS)  'Preload Count
                  TimeOutOcurred =  0
                  IntOn
              End Repeat
      
      '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
              'There must be a minimum 50 uS delay here before refreshing the display
              'Wait 50 uS
      '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      
      '##########################################################################################################
      '######################      This section to be removed in real clock     #################################
      '##########################################################################################################
      
              'Wait 895 mS 'Allow 105 mS for other routines?
              '895 mS loses about 10 seconds an hour.
              'I calculated that this needs speeding up by
              '2.5 mS
              'So: 892 mS + 490 uS might be quite close?
              Wait 893 mS
              'Wait 530 uS ' 8:20 03/05/2020 set to 530
              'Wait 500 uS ' 7:37 04/05/2020 set to 500
              Wait 465 uS  '11:00 05/05/2020 set to 465
      
              Let SecondsPixel = SecondsPixel + 1 ' simple clock with no time keeping device
      
      '##########################################################################################################
      '######################      This section to be removed in real clock     #################################
      '##########################################################################################################
      
          Loop
      End Sub
      
      WS2822_Initialisation:
      'The WS2812 routines are all taken from an original GCB demo program.
      'I have modified them and removed certain sections to suit my requirements.
      'Consider looking at the original code should this fail to work as expected.
      
      'Modified by MkStevo. May 2nd 2020
      
      '''A demonstration of Great Cow BASIC supporting the WS2812 LED devices.
      '''@author     EvanV
      '''@licence    GPL
      '''@version    1.00
      '''@date       2.09.2016
      '''********************************************************************************
      
      Sub WS2812_Init
          Set DIN Off
          Let TimeOutOcurred =  0
          InitTimer0(Osc, TMR0_FOSC4)
          SetTimer(0, Period_uS)  ' Preload Count
          StartTimer 0
          On Interrupt Timer0Overflow call ISR0
          Set DIN On
          Set DIN Off
          Wait 500 us
      End Sub
      
      WS2812_Bit_Banged_Send_Data:
      Sub WS2812BitBangSendData(In ColourData As Long)
          Repeat 24
              If ColourData.23 On Then
                  'High to send a "1"
                  Set DIN On
              Else
                  'High and Low as fast as practical for a "0"
                  Set DIN On
                  Set DIN Off
              End If
              Rotate ColourData Left
              'Low for the 1 bit
              Set DIN Off
          End Repeat
      End Sub
      
      WS2812_Timer_Interrupt:
      Sub ISR0
          TMR0IF = 0
          'Set InterPacketDelay running
          SetTimer(0, Period_uS)  ' Preload Count
          TimeOutOcurred =  1
      End Sub
      
       
  • stan cartwright

    stan cartwright - 2020-05-08

    I've seen kits for these on ebay just recent. Didn't know the leds show time...do they?
    I got a hp frequency counter with nixie tubes I was thinking of hacking but instead going
    to try draw 4 tubes on a glcd and draw the numbers.
    A uno has a xtal so it might be stable enough for a simple clock demo.
    Interesting what others are doing.

     

    Last edit: stan cartwright 2020-05-08
    • mkstevo

      mkstevo - 2020-05-09

      If I had a frequency counter with nixie tubes, I'd leave them where they were. I might be tempted to make a module that generated a one second pulse that I fed into the counter to turn it into a clock though.

      I've got a Nixie clock and it is one of my favourite clocks (of the many clocks I own). I've also got a fake "glixie" type clock with the numbers displayed by projecting light upwards through perspex engraved with the numerals. This works OK but it's nothing like a real nixie.

      I've seen some simulated nixie tube clocks for Android. They look quite good. If I could get a smart watch with a "nixie" face, I might be tempted to get one. My nixie watch is enormous!

      Here's the fake "glixie" clock I ordered, and the nixie watch.

       
  • stan cartwright

    stan cartwright - 2020-05-09

    I looked up neon lamp clock as a cheap display and found this, which was not what I was looking for but interesting http://www.pa3fwm.nl/projects/neonclock/

     
    • mkstevo

      mkstevo - 2020-05-10

      I like that neon clock.
      Shame it isn't reliable enough.

       
  • stan cartwright

    stan cartwright - 2020-05-09
     

    Last edit: stan cartwright 2020-05-09
  • stan cartwright

    stan cartwright - 2020-05-10

    Back to original clock code.. I'm going to try it with a basic clock on ili9341 cos it's interesting. Nice one.

     
    • mkstevo

      mkstevo - 2020-05-10

      I have never had a large enough LCD display that I could use for a clock. Wonder if I could make one using a VGA monitor? Maybe later...

       
      • stan cartwright

        stan cartwright - 2020-05-10

        Nice code..I will use it so not reinvent wheel. On a uno @16MHz to do a clock with it looking like real tubes. 320x240 dunno..see. Will post and credit if it works

         
      • stan cartwright

        stan cartwright - 2020-05-10

        Nice code..I will use it so not reinvent wheel. On a uno @16MHz to do a clock with it looking like real tubes. 320x240 dunno..see. Will post and credit if it works. The ili9341 is colourful and 320x240 and is well supported..and it's cheap and uses no ram. Uses a few lines but nice,fast display.I got a few cos I like them. nextion displays are same resolution but the graphics look better.Dunno why for static displays ok their dial is hi res and mine is lines and I used gcb trig but nextion graphics look better.
        The ili9341 and ssd1306 are the only display includes I could follow...a bit
        The only downer is they use 3.3V logic so a bit of messing round needed,yawn

         
  • stan cartwright

    stan cartwright - 2020-05-10

    mkstevo- anobium sent me a challenge of display to composite video. this would be a task..vga more so. I think interupts would be needed.

     
    • mkstevo

      mkstevo - 2020-05-11

      I have nothing left that can show composite video!

       
  • Moto Geek

    Moto Geek - 2020-05-12

    Very cool project mkstevo!!!!! Might have to give this one a try. Thanks for sharing!

     
  • mkstevo

    mkstevo - 2020-05-26

    Here are some pictures of the finished clock now the PCBs have been delivered. I rather like it with the "floating" clock face as in these pictures, rather than the wall mounted design in the earlier pictures.

    With this "reverse" type of LCD, as shown in two of the pictures, once the backlight is off, the LCD becomes close to invisible in low light. The backlight is set to time out after about 30 seconds following the last key press.

    The software has been updated slightly, fixing an error that caused the first minute (hour) marker not to be displayed under certain combinations of offset and marker intervals.

    For reference, the first WS2812 is at the bottom, in the (now corrected) six o'clock position. An offset of 30 has been applied to rotate the clock face so that twelve o'clock is in the traditional "top" position.

     

    Last edit: mkstevo 2020-05-26
  • Moto Geek

    Moto Geek - 2020-05-28

    Fabulous!!!! Congrats and thanks for sharing!

     

Log in to post a comment.