📜 ⬆️ ⬇️

Binary clock with alarm clock and timer on Arduino Uno

Hi, Habr!



For a long time, I was interested in the Arduino platform, but it didn’t get around to it. And so, recently I purchased two Arduino boards and various radio components. Having played enough with a pair of diodes, I decided to collect something useful and interesting. I have long wanted to get a binary clock and quickly realized that the acquisition of Arduino is a great opportunity to make a binary clock to your liking.
')
From the point of view of electronics, it is not so difficult to assemble a binary clock circuit. I complicated my task and decided not to deny myself the number of buttons and LEDs. According to the initial plan, the project should have gone 22 diodes, 6 buttons and 1 piezo snapper. At first I wanted to assemble a clock on the Arduino Mega, because on the Arduino Uno there are not enough pins to control all this directly, but then I abandoned this idea and decided to purchase several output shift registers 77HC595, since this is a more rational solution to the problem.

Training


I decided to start by calculating what is needed to build a device on a breadboard. In the end, it turned out this list:

1 Arduino Uno.
2 Breadboard (full-size, at 840 points).
24 LEDs (I took 7 red, 7 green, 6 blue, 2 yellow and 2 white).
25 220 Ohm resistors.
1 piezo squeaker.
6 clock buttons.
3 output shift register 74HC595 in DIP-16 package.
Connecting wires and / or jumpers (it took me about 90 pieces).
Grove RTC - Seeed Studio real-time clock module based on DS1307 RTC chip.

How it will work


There are 10 types of binary watches. Some show the time in the binary-decimal (BCD) representation, the second - in the form of binary numbers. I don’t like BCD watches at all, so I decided to make purely binary watches. It may be a bit more difficult to read than a BCD clock, but it seems to me that the difference is small, since it is not difficult to quickly convert short binary numbers (up to 6 bits) into the decimal number system. Also, I decided that I would definitely make an indication of the seconds on the clock.

The diode distribution scheme is as follows:



Also, I decided to make 6 buttons:

Set - switch to the clock / alarm / timer setting mode and save the current value of the parameter in the configuration mode.
Mode - switch between the clock, alarm and timer modes.
Up - when setting the clock / alarm clock / timer, increases the current parameter by one. In the timer and alarm mode, it is responsible for activating and deactivating the corresponding mode. When the alarm is triggered, it turns off the alarm / timer signal.
Down - when setting the clock / alarm clock / timer, reduces the current parameter by one. In timer mode, pauses the timer without resetting the count to the initial state. When the alarm goes off - the alarm is delayed for 5 minutes (snooze).
24/12 - switch between 24-hour and 12-hour time representation.
Dim - turns off / on the LEDs (when the LEDs are off, no buttons except Dim work).

Connection of components


All LEDs must be connected in series with a resistor (I used 220 Ohm resistors), otherwise you can burn not only the diode itself, but also damage the Arduino. The resistor can be connected to both the cathode of the LED and the anode. We will communicate with the diodes through the 74HC595 shift registers. This is a chip with 16 pins. They allow you to manage a large number of pins using only 3 digital pins on the Arduino.



74HC595 pinout:

Q0-Q7 are the shift register pins. We will connect LEDs to them.
Vcc is the power pin. For him, we serve 5V.
GND is the land. We connect it with GND on Arduino.
OE - activation of the findings. This pin is inverted, i.e. To activate the leads, you need to remove the voltage, and to shut it off - on the contrary, apply. In our case, it is not necessary to control this pin, so you can simply close it to the ground.
MR - clear the register. This pin is also inverted and we do not need to control it, so it can be connected to 5V.
ST_CP is the pin responsible for updating the state of the shift register. During the recording of the new state, this pin should be submitted LOW, and after the recording - HIGH to update the state of the pins. It needs to be connected to a digital pin on Arduino. You can connect all ST_CP on all three registers in parallel.
SH_CP is the pin responsible for register shift by 1 bit. It needs to be connected to a digital pin on Arduino. It is also possible to connect three SH_CP on all chips in parallel.
DS - Pin, to which we submit data. It needs to be connected to a digital pin on Arduino.
Q7 '- Pin for cascade connection with other 74HC595. It is necessary to connect the Q7 'of the first register with the DS of the second and Q7' of the second with the DS of the third register. On the third register Q7 'you do not need to connect anywhere.

It turns out about the following connection scheme:



I connected the piezo digester to the third pin of Arduino in series with a 220 ohm resistor. It should be noted that to work with the piezo-digger, you need a pin that supports PWM (PWM). On the Arduino, Uno are pins 3, 5, 6, 9, 10 and 11.



There were two options with buttons: either use external resistors, or use pull-up resistors built into Arduino, which are included as follows:

pinMode(pin,INPUT_PULLUP);

The only difference between this approach is that LOW will be read when the button is pressed, and HIGH will be read when released, but external resistors will not be required, so I chose this option. You just need to connect one side of the buttons with the ground, and the other with the digital pins of Arduino.



The final design should look like this:



Build a device on the Breadboard


When I purchased all the missing parts, I started to build the device on the Breadboard. In principle, the appearance was quite predictable:



Of course, the result was far from what I wanted to get. Still, Breadboard severely restricts the freedom to place components. In addition, the bundle of wires hanging over the breadboard does not add aesthetic pleasure. However, a prototyping board does exist to assemble device mockups on it, and not ready-made devices.

Code writing


I decided not to use other people's achievements and implement the software part completely independently, from scratch. I started the development by writing a subroutine, which, when turned on, flashes with all the LEDs and squeaks with a piezo squeaker. This allows you to make sure that the chain (not counting the buttons) is assembled correctly and has not collapsed since it was last turned on. Many devices do something like this when turned on.

I will not consider in detail the implementation of individual functions, since the sketch turned out to be quite voluminous. Therefore, I will briefly review some aspects of the implementation.

Work with LEDs

Since we communicate with diodes through a shift register, the first thing to do was to implement subroutines for convenient work with diodes. The state of all LEDs is stored in the led array of three elements of the unsigned char type and is output via the shift registers at the end of each iteration of the main loop. To simplify the work with diodes, a number of auxiliary functions are implemented, allowing you to easily set the necessary bits in the led according to the input arguments, for example, hours, minutes, etc. I also implemented various diode animation effects. For example, if the clock is not configured, the hour and minute diodes will flash (by analogy with a normal digital clock, which usually flashes “0:00” if they are not set). For the second diode there is animation, when one diode runs left-right along the strip of second diodes. It is used, for example, in the alarm mode (since the second diodes do not have a more rational use there) or during the setting of the clock. This gives a more interesting look to the watch.

Main loop

The basic logic of the program, in fact, is a finite state machine. Depending on the current state, the clock displays the corresponding information, and changes from one state to another when pressing buttons and timer events. Implemented as a large number of nested conditions. At each loop iteration, the state of the buttons and timers and the call of their handlers are checked, after which the state of the diodes is updated.

Input

To process the input, you will need an array containing the state of the buttons (so that when the button is pressed, the handler only works once for each click). When the voltage at the pin of the button goes to LOW - we set the array element corresponding to the button to true (if it is already true, then we do nothing) and call the click handler. When the voltage returns to HIGH, we reset the array to false. Check the status of the buttons is implemented as a single subroutine.

Timers

The main work is performed when the timers are triggered. In the project, I used two timers. One - with a second resolution (for processing the state of the clock, alarm clock and timer, as well as some animations) and the second - with a resolution of 1/8 second. It is used to display the current time (to increase the speed of the visual response of the clock when changing modes), to animate the second LEDs and to give the alarm signal. Both timers are implemented quite simply. Using the value obtained from millis (), we check whether a certain time interval has passed since the last timer operation, and if the interval has passed, we call the handler and save the new timer time. Also, a simple timer adjustment is implemented. If for some reason, the timer has triggered a few milliseconds later than necessary, then the next trigger interval will decrease by the same number of milliseconds to compensate for the delay.

The complete source code for the sketch is available on Github:

We look what happened


From the program point of view, the resulting device worked perfectly (at least, at first glance). All that I wanted to see in the device was implemented and worked stably.



But not without a spoon of tar. In practical testing, it turned out that the clock is about 1 second behind the hour, which leads to the accumulation of a very significant error in a short time. A detailed study of the issue showed that the problem was that the original Arduino Uno uses not a quartz crystal for timing, but a ceramic resonator that does not have sufficient accuracy to measure time over long periods of time. The result is a miscalculation - a large crystal oscillator on the board is used only for the USB-To-Serial controller and it is not possible to use it for timing. There were several solutions to the problem. The most interesting and convenient option seemed to me using a real time clock. In addition to solving the problem of lagging hours, they give a pleasant bonus - when the Arduino is disconnected from power, the clock does not get lost due to the battery-tablet.

I purchased the Grove RTC module from Seeed Studio. This is a ready-to-use board with a DS1307 real-time clock chip. Also on the board is a quartz clock, three resistors and a holder for the battery. It looks like this:



The RTC module communicates with the Arduino over the I2C bus. The SDA and SCL pins are connected to the A4 and A5 pins on the Arduino Uno, respectively. GND clings to the ground. 5V is already occupied by the clock board, so there is no place to connect the Vcc RTC module. However, since the RTC-module consumes little current (within the allowable load on the digital pins) - it can be powered from one of the digital pins, which will stay in HIGH all the time.

Code completion


A ready-made library is available for working with the RTC module on Seeed Studio. When implementing work with RTC, the first question that arose was how to determine when switching on what to read the current time from the RTC. For these purposes, it was decided to use the flag in the EEPROM. If the value of the zero byte in the EEPROM is different from zero - we take the time from the RTC, if the byte is zero - then we perform the “first run” with the unconfigured clock blinking 0:00. Also, it was logical at the same time to implement and save in the EEPROM of the last set alarm and timer time. In order to roll back to the “factory” settings, I slightly expanded the self-test procedure when it is turned on - if you press the SET button during the turn on - the EEPROM will be cleared and the device will perform a software reset (by setting the command counter to 0).

Then I implemented a subset of the necessary functions for working with RTC in the project and it only remained to integrate them correctly into the rest of the code. I transferred from the second timer to a separate second timer tied to the RTC all the handlers related to time. I left the old one-second timer for tasks that are not critical to accuracy (for example, LED blinking animation).

Unlike other timers, which are checked at each iteration of the main loop, the RTC timer check is nested in the timer handler for 1/8 second. Working with I2C is a fairly resource-intensive operation and the constant polling of the RTC in a cycle leads to undesirable effects, such as the lateness of the timers and the distorted sound of the piezo squeaker. Checking the RTC timer from the second timer would cause the clock to periodically count down two seconds (since my Arduino’s ceramic resonator lags slightly behind real time), which is highly undesirable, so the RTC timer processing is 1/8 second was the best option.

We give the device a complete look.

No matter how good the software part is, the appearance of the received device still leaves much to be desired. So I decided to transfer it from the Breadboard to a full-fledged PCB. First of all, it was necessary to make the layout of the board. For these purposes, I used Fritzing, since I already had a device map and a view on the Breadboard built there. I did not trust the work of the autorouter and traced the board manually. This process takes some time, but in the end I got a ready-made project for a printed circuit board:



I decided to order the production of PCB in China. Seeed Studio has a Fusion PCB PCB production service. Fritzing can export PCB designs to Extended Gerber (RS-274X) format, which most PCB manufacturers work with (including Seeed Studio). I ordered the production of boards and after two weeks I received a package with the manufactured boards:



It remains only to solder all the components on the board. I had never had the opportunity to do soldering before, but I quickly got comfortable. It seems to me, for the first time it turned out pretty well:



The end result looks much better than the breadboard layout.

Conclusion

I got what I wanted - my own binary clock with alarm clock and timer. If you use the battery compartment - you get a completely autonomous clock that can stand anywhere. The Arduino platform fully met my expectations and I think that I will use it more than once in my projects.

Links

Cascade connection of several output shift registers 74HC595
Using built-in pull-up resistors
The source code of the binary clock sketch

Source: https://habr.com/ru/post/242485/


All Articles