Programming: Arduino

Check out my other articles:


Analogue Clock

Originally I intended to build a clock using a set of Nixie tubes - a cold cathode display that screams '60s and '70s.

As supply for these was limited, I searched the internet to find someone who could sell me a set. Luckily, I found two suppliers who could supply the four I needed – one in Russia and one in Australia - and placed an order from the local company. After a few days I received a message saying that they were out of stock (despite the web site indicating otherwise) and that there would be a delay in processing my order. A few weeks later, they finally responded saying that they could not source any. I quickly logged back onto the Russian site but found they had sold out in the meantime. At this point the project was shelved

Whilst browsing the Ardufruit site, I happened to see a NEOPixel ring of RGB LEDs .. mmm, interesting especially when one of the rings contained 24 LEDs which lends itself to being mapping against a clock face. I ordered one but this time it turned up with no hassles along with a Real Time Clock and a spare Arduino board. The build was back on.


Overview

The Analogue Clock uses the following arts:

  • Arduino Uno (of course)
  • DS1307 Real Time Clock
  • Adafruit NeoPixel 24 Segment
  • Rotary Encoder

Although it is easy to set the time programmatically when uploading the program to the Arduino, I wanted to be able to set the time and the alarm without having to connect to a computer but I didn't eant a myriad of switches to control these functions. This was achieved using a Rotary Encoder. By pressing the button on the rotary encoder the clock switches between four modes (show the time, set the time, set the alarm, turn the alarm on / off). Rotating the switch one way will change the hours settings - turning it the other way will adjust the minutes.

As mentioned, I wanted to build in an alarm function but I didn't have a speaker or buzzer in my parts box. As such, when the alarm is on the Neopixel flashes through a range of colours until the rotary encoder switch is pressed. It is a beautiful demonstration of what the NeoPixel can do and I wish I could claim credit for the code but it came in the same code with the NeoPixel library.

Δ Top

Rendering the Time

Rendering a clock on a 24 element NeoPixel presents a couple of problems that are not found on real analogue clock. In a real clock, the long minute hand is visible as it passes under the top-most hour hand whereas on the NeoPixel we can only light the one pixel resulting in a display where one of the hands is missing.

I tried a couple of different approaches to this including making the hour hand a red pixel and the minute hand a green pixel. When the two hands clashed, I attempted to render the single pixel a different colour but this was simply confusing! Another approach was to render the minute hand on every odd pixel and the hour hand on the even pixels and although this never resulted in a clash, the results looked weird - especially at times like midday when the two hands never lined up.

Finally, I settled on the hour hand being rendered as a single pixel and the minute hand being rendered as three contiguous elements. In this way, when the clock showed a time like midday the two hands would be both pointing to the 12 o'clock position and would both be visible.

Δ Top

Pips and the AM / PM Indicator

Rendering the time on a blank clock face makes reading the time on quite hard - especially in the the dark where the entire face may not be visible. I have included an option SHOW_PIPS that will render lightly illuminated 'pips' at the 12, 3, 6 and 9 o'clock positions.

A second setting, SHOW_PM_INDICATOR, provides an AM / PM indicator by highlighting the elements on either side of the 12 o'clock pip when the time is after midday.

   
Δ Top

Telling the Time

As detailed above, I settled on the hour hand being rendered as a single pixel and the minute hand being rendered as three contiguous elements. The examples below show approximately 10:07 AM (on the left) and 9:15 PM (on the right with the PM indicated by the display of three white / blue pixels at the 12 o'clock position).

   
Δ Top

Δ Top


Code Fragments

The following code highlights areas of interest in the application. It is not intended to be a tutorial or a complete solution. I have provided the complete code for download and you may use or modify it as required.

Setup

Like most Arduino programs, the clock sketch is a simple state engine with the various states correlated to the modes of the clock - normal operation, setting the time, setting the alarm, activating / deactivating the alarm and an alarm ringing mode. These states are enumerated as constants:

001
002
003
004
005
006
007
008
class Player {
 
  public:

  boolean enabled; 
  char* playerName;
  char* imageName;
  char playerNameLength;
  int runningScore;
  int grandScore;
  int throwNumber;
  int playerNumber;
  
  char die1[10]; 
  char die2[10]; 
  char die3[10]; 
  char die4[10];
  char die5[10];
  char die6[10];
  char throwNo[10];
  int thisScore[10];
  int totalScore[10];
  boolean divider[10];

  void addThrowHistory(int throwNoValue, int dValue1, int dValue2, 
                       int dValue3, int dValue4, int dValue5, int dValue6, 
                       int thisScoreValue, int totalScoreValue, 
                       boolean dividerValue) {
    
    for (int x = 8; x >= 0; x--) {
      
      die1[x + 1] = die1[x];
      die2[x + 1] = die2[x];
      die3[x + 1] = die3[x];
      die4[x + 1] = die4[x];
      die5[x + 1] = die5[x];
      die6[x + 1] = die6[x];
      throwNo[x + 1] = throwNo[x];
      thisScore[x + 1] = thisScore[x];
      totalScore[x + 1] = totalScore[x];
      divider[x + 1] = divider[x];
  
    }

    die1[0] = dValue1;
    die2[0] = dValue2;
    die3[0] = dValue3;
    die4[0] = dValue4;
    die5[0] = dValue5;
    die6[0] = dValue6;
    throwNo[0] = throwNoValue;
    thisScore[0] = thisScoreValue;
    totalScore[0] = totalScoreValue;
    divider[0] = dividerValue;

  }
  
  void reset(int playerNo) {
    
    runningScore = 0;
    grandScore = 0;
    throwNumber = 1;
    playerNumber = playerNo;

    for (int i=0; i<=10; i++) {
      
      die1[i] = 0;
      die2[i] = 0;
      die3[i] = 0;
      die4[i] = 0;
      die5[i] = 0;
      die6[i] = 0;
      throwNo[i] = 0;
      thisScore[i] = 0;
      totalScore[i] = 0;
      divider[i] = false;
      
    }
    
  }

};
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
class Player {
 
  public:

  boolean enabled; 
  char* playerName;
  char* imageName;
  char playerNameLength;
  int runningScore;
  int grandScore;
  int throwNumber;
  int playerNumber;
  
  char die1[10]; 
  char die2[10]; 
  char die3[10]; 
  char die4[10];
  char die5[10];
  char die6[10];
  char throwNo[10];
  int thisScore[10];
  int totalScore[10];
  boolean divider[10];

  void addThrowHistory(int throwNoValue, int dValue1, int dValue2, 
                       int dValue3, int dValue4, int dValue5, int dValue6, 
                       int thisScoreValue, int totalScoreValue, 
                       boolean dividerValue) {
    
    for (int x = 8; x >= 0; x--) {
      
      die1[x + 1] = die1[x];
      die2[x + 1] = die2[x];
      die3[x + 1] = die3[x];
      die4[x + 1] = die4[x];
      die5[x + 1] = die5[x];
      die6[x + 1] = die6[x];
      throwNo[x + 1] = throwNo[x];
      thisScore[x + 1] = thisScore[x];
      totalScore[x + 1] = totalScore[x];
      divider[x + 1] = divider[x];
  
    }

    die1[0] = dValue1;
    die2[0] = dValue2;
    die3[0] = dValue3;
    die4[0] = dValue4;
    die5[0] = dValue5;
    die6[0] = dValue6;
    throwNo[0] = throwNoValue;
    thisScore[0] = thisScoreValue;
    totalScore[0] = totalScoreValue;
    divider[0] = dividerValue;

  }
  
  void reset(int playerNo) {
    
    runningScore = 0;
    grandScore = 0;
    throwNumber = 1;
    playerNumber = playerNo;

    for (int i=0; i<=10; i++) {
      
      die1[i] = 0;
      die2[i] = 0;
      die3[i] = 0;
      die4[i] = 0;
      die5[i] = 0;
      die6[i] = 0;
      throwNo[i] = 0;
      thisScore[i] = 0;
      totalScore[i] = 0;
      divider[i] = false;
      
    }
    
  }

};

Wrapping the logic of a player in a single class allows it to be passed to functions as a parameter, right? Wrong. This is the first thing I discovered about inner classes in Arduino - the compiler is simply not smart enough to compile the following piece of code ..

001
002
003
void someFunction(Player *thePlayer) {
  
}

The compiler spits out the following errors:

001
002
003
farkle:126: error: variable or field 'someFunction' declared void
farkle:126: error: 'Player' was not declared in this scope
farkle:126: error: 'thePlayer' was not declared in this scope

A search of the forums reveals the following post http://forum.arduino.cc/index.php/topic,41848.0.html which seems to describe the problem.

The Arduino IDE automatically generates prototypes for your functions. Unfortunately, this doesn't work for functions whose arguments or return value are types defined in the sketch, since the prototypes get inserted above the type definitions. I'm not sure there's really a good workaround for this.

I finally gave up and simply declared four instances of my class for the four possible players and a 'spare' reference (lines 001 - 005) that I assign to the current player (line 020 in the Setup routine starts the game with referencing the first player. All of the functions in the program refer to the global variable player.

A function called setNetUser() is called at the end of a player's turn to determine who the next player is based on the number of players in the game. For example, if the current player is the second player (line 040) and there are more than two players in the game (line 041) then the current player reference is updated to the third player (line 042). If there were only two players in the game, then the current player reference is updated to the first player (lines 044 - 046).

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
Player *player1;
Player *player2;
Player *player3;
Player *player4;
Player *player;

...

void setup() { 

   ...
  
  player1 = new Player(); 
  player2 = new Player(); 
  player3 = new Player(); 
  player4 = new Player(); 

  ...
  
  player = player1;
  
}

...

void setNextUser() {
    
    switch (player->playerNumber) {
            
        case 1:
            if (numberOfPlayers > 1) {
                player = player2;
            }
            else {
                player = player1;
            }
            break;

            
        case 2:
            if (numberOfPlayers > 2) {
                player = player3;
            }
            else {
                player = player1;
            }
            break;
            
        case 3:
            if (numberOfPlayers > 3) {
                player = player4;
            }
            else {
                player = player1;
            }
            break;
            
        case 4:
            player = player1;
            break;
            
    }
    
}
Δ Top

Wiring Diagram

The diagram below show the wiring diagram when using a standard Arduino Uno (or compatible) and the SmartGPU2. Click on the images to see a detailed drawing.


Δ Top

Downloads

Feel free to download, modify and adapt this code to your needs. Please credit me if you use some or all of this code in your own project. If you alter the code to fix bugs or add functionality, please submit them back to me and I will add them to the download.

   Arduino Source Code
Δ Top

Comments?

 Anything to say?
Name :
Email :
Comments :
Confirmation: