Arduino clock with LED indicator. DIY LED clock


The photo shows a prototype that I assembled to debug the program that will manage this entire facility. The second arduino nano in the upper right corner of the breadboard does not belong to the project and sticks out there just like that, you don’t have to pay attention to it.

A little about the principle of operation: Arduino takes data from the DS323 timer, processes it, determines the light level using a photoresistor, then sends everything to the MAX7219, and it, in turn, lights up the required segments with the required brightness. Also, using three buttons, you can set the year, month, day, and time as desired. In the photo, the indicators display time and temperature, which is taken from a digital temperature sensor

The main difficulty in my case is that the 2.7-inch indicators have a common anode, and they had to, firstly, somehow make friends with the max7219, which is designed for indicators with a common cathode, and secondly, solve the problem with their power supply, since they need 7.2 volts for glow, which max7219 alone cannot provide. Having asked for help on one forum, I received an answer.

Solution in the screenshot:


A microcircuit that inverts the signal is attached to the outputs of the segments from max7219, and a circuit of three transistors is attached to each pin that should be connected to the common cathode of the display, which also invert its signal and increase the voltage. Thus, we get the opportunity to connect displays with a common anode and a supply voltage of more than 5 volts to the max7219

I connected one indicator for the test, everything works, nothing smokes

Let's start collecting.

I decided to divide the circuit into 2 parts due to the huge number of jumpers in the version that was separated by my crooked paws, where everything was on one board. The clock will consist of a display unit and a power and control unit. It was decided to collect the latter first. I ask aesthetes and experienced radio amateurs not to faint because of the cruel treatment of parts. I have no desire to buy a printer for the sake of LUT, so I do it the old fashioned way - I practice on a piece of paper, drill holes according to the template, draw paths with a marker, then etch.

The principle of attaching indicators remained the same as on.

We mark the position of the indicators and components using a plexiglass template made for convenience.

Markup process







Then, using a template, we drill holes in the right places and try on all the components. Everything fit perfectly.

We draw paths and etch.




bathing in ferric chloride

Ready!
control board:


indication board:


The control board turned out great, the track on the display board was not critically eaten up, it can be fixed, it’s time to solder. This time I lost my SMD virginity and included 0805 components in the circuit. At the very least, the first resistors and capacitors were soldered into place. I think I'll get better at it, it will be easier.
For soldering I used flux that I bought. Soldering with it is a pleasure; now I use alcohol rosin only for tinning.

Here are the finished boards. The control board has a seat for an Arduino nano, a clock, as well as outputs for connecting to the display board and sensors (a photoresistor for auto-brightness and a digital thermometer ds18s20) and a power supply with adjustable output voltage (for large seven-segment devices) and for powering the clock and Arduino, on the display board there are mounting sockets for displays, sockets for max2719 and uln2003a, a solution for powering four large seven-segment devices and a bunch of jumpers.




rear control board

Rear display board:

Terrible smd installation:


Launch

After soldering all the cables, buttons and sensors, it's time to turn it all on. The first launch revealed several problems. The last large indicator did not light up, and the rest glowed dimly. I dealt with the first problem by soldering the leg of the SMD transistor, and with the second - by adjusting the voltage produced by lm317.
IT'S ALIVE!

After making a lot of Arduino prototypes on a breadboard, I decided to make something useful that I could use at home. And what could be more useful than a luminous watch, which for some reason has been discontinued since 2010? I started assembling a digital watch by searching for the necessary parts. One of the criteria that helped to quickly collect the necessary components was the availability of parts in local stores and from manufacturers from China and Malaysia.

Arduino real time clock (RTC) with 7-segment displays

When assembling the watch, several options appeared for setting the exact time on it. First: set time on Arduino , keeping it powered all the time. But this method is not very practical, since every time you need to set the time, you will need to turn on power to the Arduino.

The second option was the idea connection of 7-segment LED - indicators for the GPS module . Since the GPS signal gives a very accurate time, this option should solve the problem, and would not have to adjust the clock every time you turn it on. I took my Garmin GPS 60 C pocket navigator, serialized it into the Arduino and loaded several GPS libraries, thus obtaining a very accurate time signal.

The problem with the GPS method turned out to be that since I live in the center of the city, a concrete jungle with impenetrable high-rise buildings surrounds my house, and it would be necessary to place an external GPS antenna outside the window in order to receive a GPS signal from a clear sky. Without satellite coverage, no GPS device is able to receive a time-locked signal. Either the clock should be on the window, or it was necessary to take out the GPS antenna and lay a 7-meter cable to it.

The third method of setting the clock turned out to be the best. It lies in work Arduino together with DS1307 real time clock (RTC). They are powered by a 3-volt button cell battery, which retains settings when the device is turned off and when the microcontroller is disconnected.

I went to the local "electronic heaven" located in the city center to try my luck in finding the necessary components. To my surprise, there I found all the necessary parts to assemble a digital watch.

The required parts are:

  1. Arduino board for prototyping and loading the sketch into the microcontroller;
  2. ATmega328P microcontroller for clock operation;
  3. four red 7-segment LED indicators (or other cooler colors you can find at your local market);
  4. real time clock DS1307;
  5. quartz resonator at 32.768 kHz;
  6. holder for coin cell battery CR2025 or CR2032;
  7. four 74HC595 shift register chips for driving 7-segment LED indicators;
  8. resistors 220 Ohm 0.25 W each;
  9. single row pin connectors;
  10. integrated circuit (IC) sockets;
  11. connecting wires.

If you don’t have the skills to make printed circuit boards, I recommend using soldered breadboard (textolite plate with many holes for fixing components on it by soldering, which is mistakenly called circuit board) and solder all IC sockets of microcircuits and pin connectors to it. Thanks to these quick-release contacts, all 7-segment LEDs and ICs can be easily replaced if necessary.

Since the size of the prototyping board is very limited, we were able to place only 35mm LED indicators on it, because there still had to be room for a button cell battery holder. I would like to install much larger 7-segment indicators, but the larger ones require increased voltage, over 5 V, and it was already necessary to complicate the circuit with double power circuits. I don’t want to deal with a stabilizer for two output voltages; it’s better to focus on a simpler version of a digital clock.

100 nF ceramic blocking capacitors on the Vcc power pin of each 74HC595 register are added to prevent any low frequency interference problems.

The assembled digital clock uses only 5 Arduino pins:

  • 3 digital outputs for 74HC595 shift registers;
  • 2 analog outputs for real time clock connected using I2C connection.

The advantage of an assembled digital clock on Arduino compared to a factory one is that you can easily add any functions that may be useful to them.

Here are some ideas for modifying your watch:

  1. Alternating display of hours/minutes and minutes/seconds;
  2. Playing a melody every hour;
  3. Installing the LM35 sensor and using the watch as a thermometer;
  4. Morning alarm function;
  5. Even control of other electrical devices using an electromechanical relay that turns on in accordance with time-defined events or readings from connected sensors.

Since the four indicators are quite large and bright, they can also be used to display letter information.

After I soldered the first digit of the 7 segment common cathode LED to the 74HC595 shift register, the first problem appeared. I used only one 220 ohm resistor connected to the common cathode of the LED indicator to save resistors, and found that when the number 8 turned on, all the segments lit up very dimly. This is fine for a prototype, but not suitable for a live digital watch. It would be very annoying to have a watch with numbers that glow differently. So I had to cut individual wires and get more 220 ohm resistors to connect them to each of the seven LED segments.

The second problem was that I forgot to allocate space for two 5mm LEDs to act as the flashing colon seconds indicator. And the indicator of the third digit was already soldered.

Since it takes too much work to solder one indicator, along with attaching all the resistors to the wires, I decided to make a remote board with two LEDs as seconds indicators. I'll find a way to set two dots between the hour and minute digits! In the photo below, I simply shoot the LED on pin 13 blinking at 500 ms intervals.

  • There are codes .
  • Final sketch

Here are some photos of the assembled, working device. Now I just need something like acrylic to secure the breadboard and hide the Arduino clock in the overall enclosure.

This clock is powered by an Arduino remote board in the version with FTDI cable and DC socket.

The Arduino clock assembly is completed after installing the DHT11 humidity and temperature sensor.

Homemade temperature and humidity sensor DHT11 and DHT22 - connection to Arduino GPS clock on Arduino

There are many ways to assemble an electronic watch with your own hands: diagrams are widely presented in the literature and on the Internet. Most modern implementations are based on microcontrollers. The implementation of such projects often requires extensive practical skills and theoretical knowledge in the field of electronics: the ability to use specialized software, create printed circuit boards at home using ferric chloride etching, and good soldering. You also need to have a variety of tools and supplies.

However, there is a simple and affordable way to assemble an electronic watch with your own hands at home: use the Arduino platform. It is a software and hardware complex specifically designed for teaching the basics of programming and electronics. With the help of Arduino, anyone, even without special prior training, can build an electronic clock with their own hands: circuit diagrams, engineering programs and even a soldering iron are not needed!

The connection of all electronic components is carried out on a special contact (“solderless”) breadboard, which eliminates the risk of burns, cuts and other injuries - therefore, you can work with the Arduino designer together with children. A visual way of presenting a circuit diagram will help you avoid making mistakes when assembling the device.

Step 1. List of components

To assemble a simple watch on LED matrices you will need only a few cheap components:

  • Arduino platform. The simplest models will do - or Micro;
  • contact breadboard;
  • connecting wires for breadboard;
  • Adafruit DS3231 real-time clock module;
  • LED matrix module 32x8 MAX7219;
  • two buttons.

You will also need a personal computer and a USB-mini-USB cable to load the control program into memory. That's all - a soldering iron, insulation strippers, assembly knives and other professional tools are not needed: all operations are performed by hand. Perhaps in some cases it is more convenient to use tweezers, but you can do without it.


Step 2. Assembling the electronic circuit

The circuit of an electronic clock with LED display using Arduino will seem quite simple even for inexperienced radio amateurs. Only a few wires are required for assembly. Connection table:

Arduino module → 32x8 MAX7219 LED matrix

Arduino module → Adafruit DS3231 real time clock

Arduino module → buttons

D2 - button 1

D3 - button 2

The second pin of the buttons is connected to GND.

You just need to pay attention and remember how the contact holes on the breadboard are connected to each other. The following diagram illustrates the method of internal connection of contact holes:


Two rows (1 and 4) on both sides are shorted horizontally - they are usually used as the +5V power line and GND ground. All internal contacts (2 and 3) are closed vertically. In this case, the circuit board is divided both vertically and horizontally into two symmetrical parts independent of each other. This allows, for example, to assemble two different devices on one board.

The diagram of an electronic clock with LED indication, as well as the arrangement of elements on the circuit board, is shown in the illustration:

Carefully check that all connections comply with the diagram shown. Also make sure that the conductors are well secured in the contact holes of the circuit board.


Step 3. Arduino firmware

Once the assembly and testing of the circuit is completed, you can begin loading the control program (or “firmware”) into the Arduino memory.


To do this, you need to install the free official development environment - . You will also need the source code of the project, which you can download below in the archive with all the libraries and a sketch, and if you just need a sketch, you can copy it separately:

//include libraries: #include "LedControl.h" #include // Font library #include // DS1307 clock #include "RTClib.h" // DS1307 clock #include // Button library by Alexander Brevig // Setup LED Matrix // pin 12 is connected to the DataIn on the display // pin 11 is connected to the CLK on the display // pin 10 is connected to LOAD on the display LedControl lc = LedControl(6, 5, 4, 4); //sets the 3 pins as 12, 11 & 10 and then sets 4 displays (max is 8 displays) //global variables byte intensity = 7; // Default intensity/brightness (0-15) byte clock_mode = 0; // Default clock mode. Default = 0 (basic_mode) bool random_mode = 0; // Define random mode - changes the display type every few hours. Default = 0 (off) byte old_mode = clock_mode; // Stores the previous clock mode, so if we go to date or whatever, we know what mode to go back to after. bool ampm = 0; // Define 12 or 24 hour time. 0 = 24 hour. 1 = 12 hour byte change_mode_time = 0; // Holds hour when clock mode will next change if in random mode. unsigned long delaytime = 500; // We always wait a bit between updates of the display int rtc; // Holds real time clock output char days = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ); //day array - used in slide, basic_mode and jumble modes (The DS1307 outputs 1-7 values ​​for day of week) char daysfull = ( "Sunday", "Monday", "Tuesday", "Wed", "Thursday", "Friday", "Saturday" ); char suffix = ("st", "nd", "rd", "th" ); //date suffix array, used in slide, basic_mode and jumble modes. e,g, 1st 2nd ... //define constants #define NUM_DISPLAY_MODES 3 // Number display modes (conting zero as the first mode) #define NUM_SETTINGS_MODES 4 // Number settings modes = 6 (conting zero as the first mode) # define SLIDE_DELAY 20 // The time in milliseconds for the slide effect per character in slide mode. Make this higher for a slower effect #define cls clear_display // Clear display RTC_DS1307 ds1307; // Create RTC object Button buttonA = Button(2, BUTTON_PULLUP); // Setup button A (using button library) Button buttonB = Button(3, BUTTON_PULLUP); // Setup button B (using button library) void setup() ( digitalWrite(2, HIGH); // turn on pullup resistor for button on pin 2 digitalWrite(3, HIGH); // turn on pullup resistor for button on pin 3 digitalWrite(4, HIGH); // turn on pullup resistor for button on pin 4 Serial.begin(9600); //start serial //initialize the 4 matrix panels //we have already set the number of devices when we created the LedControl int devices = lc.getDeviceCount(); //we have to init all devices in a loop for (int address = 0; address< devices; address++) { /*The MAX72XX is in power-saving mode on startup*/ lc.shutdown(3-address, false); /* Set the brightness to a medium values */ lc.setIntensity(3-address, intensity); /* and clear the display */ lc.clearDisplay(3-address); } //Setup DS1307 RTC #ifdef AVR Wire.begin(); #else Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino #endif ds1307.begin(); //start RTC Clock if (! ds1307.isrunning()) { Serial.println("RTC is NOT running!"); ds1307.adjust(DateTime(__DATE__, __TIME__)); // sets the RTC to the date & time this sketch was compiled } //Show software version & hello message printver(); //enable red led digitalWrite(13, HIGH); } void loop() { //run the clock with whatever mode is set by clock_mode - the default is set at top of code. switch (clock_mode){ case 0: basic_mode(); break; case 1: small_mode(); break; case 2: slide(); break; case 3: word_clock(); break; case 4: setup_menu(); break; } } //plot a point on the display void plot (byte x, byte y, byte val) { //select which matrix depending on the x coord byte address; if (x >= 0 && x<= 7) { address = 3; } if (x >= 8 && x<= 15) { address = 2; x = x - 8; } if (x >= 16 && x<= 23) { address = 1; x = x - 16; } if (x >= 24 && x<= 31) { address = 0; x = x - 24; } if (val == 1) { lc.setLed(address, y, x, true); } else { lc.setLed(address, y, x, false); } } //clear screen void clear_display() { for (byte address = 0; address < 4; address++) { lc.clearDisplay(address); } } //fade screen down void fade_down() { //fade from global intensity to 1 for (byte i = intensity; i >0; i--) ( for (byte address = 0; address< 4; address++) { lc.setIntensity(address, i); } delay(30); //change this to change fade down speed } clear_display(); //clear display completely (off) //reset intentsity to global val for (byte address = 0; address < 4; address++) { lc.setIntensity(address, intensity); } } //power up led test & display software version number void printver() { byte i = 0; char ver_a = "MADE"; char ver_b = "IN"; char ver_c = "RUSSIA"; //test all leds. for (byte x = 0; x <= 32; x++) { for (byte y = 0; y <= 7; y++) { plot(x, y, 1); } } delay(300); fade_down(); while (ver_a[i]) { puttinychar((i * 4), 1, ver_a[i]); delay(35); i++; } delay(500); fade_down(); i = 0; while (ver_b[i]) { puttinychar((i * 4), 1, ver_b[i]); delay(35); i++; } delay(500); fade_down(); i = 0; while (ver_c[i]) { puttinychar((i * 4), 1, ver_c[i]); delay(35); i++; } delay(500); fade_down(); } // puttinychar // Copy a 3x5 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate // This is unoptimized and simply uses plot() to draw each dot. void puttinychar(byte x, byte y, char c) { byte dots; if (c >= "A" && c<= "Z" || (c >= "a" && c<= "z")) { c &= 0x1F; // A-Z maps to 1-26 } else if (c >= "0" && c<= "9") { c = (c - "0") + 32; } else if (c == " ") { c = 0; // space } else if (c == ".") { c = 27; // full stop } else if (c == ":") { c = 28; // colon } else if (c == "\"") { c = 29; // single quote mark } else if (c == "!") { c = 30; // single quote mark } else if (c == "?") { c = 31; // single quote mark } for (byte col = 0; col < 3; col++) { dots = pgm_read_byte_near(&mytinyfont[c]); for (char row = 0; row < 5; row++) { if (dots & (16 >> row)) plot(x + col, y + row, 1); else plot(x + col, y + row, 0); ) ) ) void putnormalchar(byte x, byte y, char c) ( byte dots; // if (c >= "A" && c<= "Z" || (c >= "a" && c<= "z")) { // c &= 0x1F; // A-Z maps to 1-26 // } if (c >= "A" && c<= "Z") { c &= 0x1F; // A-Z maps to 1-26 } else if (c >= "a" && c<= "z") { c = (c - "a") + 41; // A-Z maps to 41-67 } else if (c >= "0" && c<= "9") { c = (c - "0") + 31; } else if (c == " ") { c = 0; // space } else if (c == ".") { c = 27; // full stop } else if (c == "\"") { c = 28; // single quote mark } else if (c == ":") { c = 29; // clock_mode selector arrow } else if (c == ">") ( c = 30; // clock_mode selector arrow ) else if (c >= -80 && c<= -67) { c *= -1; } for (char col = 0; col < 5; col++) { dots = pgm_read_byte_near(&myfont[c]); for (char row = 0; row < 7; row++) { //check coords are on screen before trying to plot //if ((x >= 0) && (x<= 31) && (y >= 0) && (y<= 7)){ if (dots & (64 >> row)) ( // only 7 rows. plot(x + col, y + row, 1); ) else ( plot(x + col, y + row, 0); ) //) ) ) ) //small_mode //show the time in small 3x5 characters with seconds display void small_mode() ( char textchar; // the 16 characters on the display byte mins = 100; //mins byte secs = rtc; //seconds byte old_secs = secs; / /holds old seconds value - from last time seconds were updated o display - used to check if seconds have changed cls(); //run clock main loop as long as run_mode returns true while (run_mode()) ( get_time(); / /check for button press if (buttonA.uniquePress()) ( switch_mode(); return; ) if (buttonB.uniquePress()) ( display_date(); return; ) //if secs changed then update them on the display secs = rtc; if (secs != old_secs) ( //secs char buffer; itoa(secs, buffer, 10); //fix - as otherwise if num has leading zero, e.g. "03" secs, itoa covers this to chars with space "3".if(secs< 10) { buffer = buffer; buffer = "0"; } puttinychar(20, 1, ":"); //seconds colon puttinychar(24, 1, buffer); //seconds puttinychar(28, 1, buffer); //seconds old_secs = secs; } //if minute changes change time if (mins != rtc) { //reset these for comparison next time mins = rtc; byte hours = rtc; if (hours > < 1) { hours = hours + ampm * 12; } //byte dow = rtc; // the DS1307 outputs 0 - 6 where 0 = Sunday0 - 6 where 0 = Sunday. //byte date = rtc; //set characters char buffer; itoa(hours, buffer, 10); //fix - as otherwise if num has leading zero, e.g. "03" hours, itoa coverts this to chars with space "3 ". if (hours < 10) { buffer = buffer; //if we are in 12 hour mode blank the leading zero. if (ampm) { buffer = " "; } else { buffer = "0"; } } //set hours chars textchar = buffer; textchar = buffer; textchar = ":"; itoa (mins, buffer, 10); if (mins < 10) { buffer = buffer; buffer = "0"; } //set mins characters textchar = buffer; textchar = buffer; //do seconds textchar = ":"; buffer; secs = rtc; itoa(secs, buffer, 10); //fix - as otherwise if num has leading zero, e.g. "03" secs, itoa coverts this to chars with space "3 ". if (secs < 10) { buffer = buffer; buffer = "0"; } //set seconds textchar = buffer; textchar = buffer; byte x = 0; byte y = 0; //print each char for (byte x = 0; x < 6 ; x++) { puttinychar(x * 4, 1, textchar[x]); } } delay(50); } fade_down(); } // basic_mode() // show the time in 5x7 characters void basic_mode() { cls(); char buffer; //for int to char conversion to turn rtc values into chars we can print on screen byte offset = 0; //used to offset the x postition of the digits and centre the display when we are in 12 hour mode and the clock shows only 3 digits. e.g. 3:21 byte x, y; //used to draw a clear box over the left hand "1" of the display when we roll from 12:59 ->1:00am in 12 hour mode. //do 12/24 hour conversion if ampm set to 1 byte hours = rtc; if (hours > 12) ( hours = hours - ampm * 12; ) if (hours< 1) { hours = hours + ampm * 12; } //do offset conversion if (ampm && hours < 10) { offset = 2; } //set the next minute we show the date at //set_next_date(); // initially set mins to value 100 - so it wll never equal rtc on the first loop of the clock, meaning we draw the clock display when we enter the function byte secs = 100; byte mins = 100; int count = 0; //run clock main loop as long as run_mode returns true while (run_mode()) { //get the time from the clock chip get_time(); //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); return; } //check whether it"s time to automatically display the date //check_show_date(); //draw the flashing: as on if the secs have changed. if (secs != rtc) { //update secs with new value secs = rtc; //draw: plot (15 - offset, 2, 1); //top point plot (15 - offset, 5, 1); //bottom point count = 400; } //if count has run out, turn off the: if (count == 0) { plot (15 - offset, 2, 0); //top point plot (15 - offset, 5, 0); //bottom point } else { count--; } //re draw the display if button pressed or if mins != rtc i.e. if the time has changed from what we had stored in mins, (also trigggered on first entering function when mins is 100) if (mins != rtc) { //update mins and hours with the new values mins = rtc; hours = rtc; //adjust hours of ampm set to 12 hour mode if (hours >12) ( hours = hours - ampm * 12; ) if (hours< 1) { hours = hours + ampm * 12; } itoa(hours, buffer, 10); //if hours < 10 the num e.g. "3" hours, itoa coverts this to chars with space "3 " which we dont want if (hours < 10) { buffer = buffer; buffer = "0"; } //print hours //if we in 12 hour mode and hours < 10, then don"t print the leading zero, and set the offset so we centre the display with 3 digits. if (ampm && hours < 10) { offset = 2; //if the time is 1:00am clear the entire display as the offset changes at this time and we need to blank out the old 12:59 if ((hours == 1 && mins == 0)) { cls(); } } else { //else no offset and print hours tens digit offset = 0; //if the time is 10:00am clear the entire display as the offset changes at this time and we need to blank out the old 9:59 if (hours == 10 && mins == 0) { cls(); } putnormalchar(1, 0, buffer); } //print hours ones digit putnormalchar(7 - offset, 0, buffer); //print mins //add leading zero if mins < 10 itoa (mins, buffer, 10); if (mins < 10) { buffer = buffer; buffer = "0"; } //print mins tens and ones digits putnormalchar(19 - offset, 0, buffer); putnormalchar(25 - offset, 0, buffer); } } fade_down(); } //like basic_mode but with slide effect void slide() { byte digits_old = {99, 99, 99, 99}; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts byte digits_new; //new digits time will slide to reveal byte digits_x_pos = {25, 19, 7, 1}; //x pos for which to draw each digit at char old_char; //used when we use itoa to transpose the current digit (type byte) into a char to pass to the animation function char new_char; //used when we use itoa to transpose the new digit (type byte) into a char to pass to the animation function //old_chars - stores the 5 day and date suffix chars on the display. e.g. "mon" and "st". We feed these into the slide animation as the current char when these chars are updated. //We sent them as A initially, which are used when the clocl enters the mode and no last chars are stored. //char old_chars = "AAAAA"; //plot the clock colon on the display cls(); putnormalchar(13, 0, ":"); byte old_secs = rtc; //store seconds in old_secs. We compare secs and old secs. WHen they are different we redraw the display //run clock main loop as long as run_mode returns true while (run_mode()) { get_time(); //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); return; } //if secs have changed then update the display if (rtc != old_secs) { old_secs = rtc; //do 12/24 hour conversion if ampm set to 1 byte hours = rtc; if (hours >12) ( hours = hours - ampm * 12; ) if (hours< 1) { hours = hours + ampm * 12; } //split all date and time into individual digits - stick in digits_new array //rtc = secs //array pos and digit stored //digits_new = (rtc%10); //0 - secs ones //digits_new = ((rtc/10)%10); //1 - secs tens //rtc = mins digits_new = (rtc % 10); //2 - mins ones digits_new = ((rtc / 10) % 10); //3 - mins tens //rtc = hours digits_new = (hours % 10); //4 - hour ones digits_new = ((hours / 10) % 10); //5 - hour tens //rtc = date //digits_new = (rtc%10); //6 - date ones //digits_new = ((rtc/10)%10); //7 - date tens //draw initial screen of all chars. After this we just draw the changes. //compare digits 0 to 3 (mins and hours) for (byte i = 0; i <= 3; i++) { //see if digit has changed... if (digits_old[i] != digits_new[i]) { //run 9 step animation sequence for each in turn for (byte seq = 0; seq <= 8 ; seq++) { //convert digit to string itoa(digits_old[i], old_char, 10); itoa(digits_new[i], new_char, 10); //if set to 12 hour mode and we"re on digit 2 (hours tens mode) then check to see if this is a zero. If it is, blank it instead so we get 2.00pm not 02.00pm if (ampm && i == 3) { if (digits_new == 0) { new_char = " "; } if (digits_old == 0) { old_char = " "; } } //draw the animation frame for each digit slideanim(digits_x_pos[i], 0, seq, old_char, new_char); delay(SLIDE_DELAY); } } } /* //compare date digit 6 (ones) and (7) tens - if either of these change we need to update the date line. We compare date tens as say from Jan 31 ->Feb 01 then ones digit doesn"t change if ((digits_old != digits_new) || (digits_old != digits_new)) ( //change the day shown. Loop below goes through each of the 3 chars in turn e.g. "MON" for (byte day_char = 0; day_char<=2 ; day_char++){ //run the anim sequence for each char for (byte seq = 0; seq <=8 ; seq++){ //the day (0 - 6) Read this number into the days char array. the seconds number in the array 0-2 gets the 3 chars of the day name, e.g. m o n slideanim(6*day_char,8,seq,old_chars,days); //6 x day_char gives us the x pos for the char delay(SLIDE_DELAY); } //save the old day chars into the old_chars array at array pos 0-2. We use this next time we change the day and feed it to the animation as the current char. The updated char is fed in as the new char. old_chars = days; } //change the date tens digit (if needed) and ones digit. (the date ones digit wil alwaus change, but putting this in the "if" loop makes it a bit neater code wise.) for (byte i = 7; i >= 6; i--)( if (digits_old[i] != digits_new[i]) ( for (byte seq = 0; seq<=8 ; seq++){ itoa(digits_old[i],old_char,10); itoa(digits_new[i],new_char,10); slideanim(digits_x_pos[i],8,seq,old_char,new_char); delay(SLIDE_DELAY); } } } //print the day suffix "nd" "rd" "th" etc. First work out date 2 letter suffix - eg st, nd, rd, th byte s = 3; //the pos to read our suffix array from. byte date = rtc; if(date == 1 || date == 21 || date == 31) { s = 0; } else if (date == 2 || date == 22) { s = 1; } else if (date == 3 || date == 23) { s = 2; } for (byte suffix_char = 0; suffix_char <=1 ; suffix_char++){ for (byte seq = 0; seq <=8 ; seq++){ slideanim((suffix_char*6)+36,8,seq,old_chars,suffix[s]); // we pass in the old_char array char as the current char and the suffix array as the new char delay(SLIDE_DELAY); } //save the suffic char in the old chars array at array pos 3 and 5. We use these chars next time we change the suffix and feed it to the animation as the current char. The updated char is fed in as the new char. old_chars = suffix[s]; } }//end do date line */ //save digita array tol old for comparison next loop for (byte i = 0; i <= 3; i++) { digits_old[i] = digits_new[i]; } }//secs/oldsecs }//while loop fade_down(); } //called by slide //this draws the animation of one char sliding on and the other sliding off. There are 8 steps in the animation, we call the function to draw one of the steps from 0-7 //inputs are are char x and y, animation frame sequence (0-7) and the current and new chars being drawn. void slideanim(byte x, byte y, byte sequence, char current_c, char new_c) { // To slide one char off and another on we need 9 steps or frames in sequence... // seq# 0123456 <-rows of the display // | ||||||| // seq0 0123456 START - all rows of the display 0-6 show the current characters rows 0-6 // seq1 012345 current char moves down one row on the display. We only see it"s rows 0-5. There are at display positions 1-6 There is a blank row inserted at the top // seq2 6 01234 current char moves down 2 rows. we now only see rows 0-4 at display rows 2-6 on the display. Row 1 of the display is blank. Row 0 shows row 6 of the new char // seq3 56 0123 // seq4 456 012 half old / half new char // seq5 3456 01 // seq6 23456 0 // seq7 123456 // seq8 0123456 END - all rows show the new char //from above we can see... //currentchar runs 0-6 then 0-5 then 0-4 all the way to 0. starting Y position increases by 1 row each time. //new char runs 6 then 5-6 then 4-6 then 3-6. starting Y position increases by 1 row each time. //if sequence number is below 7, we need to draw the current char if (sequence < 7) { byte dots; // if (current_c >= "A" && || (current_c >= "a" && current_c<= "z")) { // current_c &= 0x1F; // A-Z maps to 1-26 // } if (current_c >= "A" && current_c<= "Z") { current_c &= 0x1F; // A-Z maps to 1-26 } else if (current_c >= "a" && current_c<= "z") { current_c = (current_c - "a") + 41; // A-Z maps to 41-67 } else if (current_c >= "0" && current_c<= "9") { current_c = (current_c - "0") + 31; } else if (current_c == " ") { current_c = 0; // space } else if (current_c == ".") { current_c = 27; // full stop } else if (current_c == "\"") { current_c = 28; // single quote mark } else if (current_c == ":") { current_c = 29; //colon } else if (current_c == ">") ( current_c = 30; // clock_mode selector arrow ) byte curr_char_row_max = 7 - sequence; //the maximum number of rows to draw is 6 - sequence number byte start_y = sequence; //y position to start at - is same as sequence number. We inc this each loop //plot each row up to row maximum (calculated from sequence number) for (byte curr_char_row = 0; curr_char_row<= curr_char_row_max; curr_char_row++) { for (byte col = 0; col < 5; col++) { dots = pgm_read_byte_near(&myfont); if (dots & (64 >> curr_char_row)) plot(x + col, y + start_y, 1); //plot led on else plot(x + col, y + start_y, 0); //else plot led off ) start_y++;//add one to y so we draw next row one down ) ) //draw a blank line between the characters if sequence is between 1 and 7. If we don"t do this we get the remnants of the current chars last position left on the display if (sequence >= 1 && sequence<= 8) { for (byte col = 0; col < 5; col++) { plot(x + col, y + (sequence - 1), 0); //the y position to draw the line is equivalent to the sequence number - 1 } } //if sequence is above 2, we also need to start drawing the new char if (sequence >= 2) ( //work out char byte dots; //if (new_c >= "A" && new_c<= "Z" || (new_c >= "a" && new_c<= "z")) { // new_c &= 0x1F; // A-Z maps to 1-26 //} if (new_c >= "A" && new_c<= "Z") { new_c &= 0x1F; // A-Z maps to 1-26 } else if (new_c >= "a" && new_c<= "z") { new_c = (new_c - "a") + 41; // A-Z maps to 41-67 } else if (new_c >= "0" && new_c<= "9") { new_c = (new_c - "0") + 31; } else if (new_c == " ") { new_c = 0; // space } else if (new_c == ".") { new_c = 27; // full stop } else if (new_c == "\"") { new_c = 28; // single quote mark } else if (new_c == ":") { new_c = 29; // clock_mode selector arrow } else if (new_c == ">") ( new_c = 30; // clock_mode selector arrow ) byte newcharrowmin = 6 - (sequence - 2); //minimumm row num to draw for new char - this generates an output of 6 to 0 when fed sequence numbers 2-8 . This is the minimum row to draw for the new char byte start_y = 0; //y position to start at - is same as sequence number. we inc it each row //plot each row up from row minimum (calculated by sequence number) ) up to 6 for (byte newcharrow = newcharrowmin; newcharrow<= 6; newcharrow++) { for (byte col = 0; col < 5; col++) { dots = pgm_read_byte_near(&myfont); if (dots & (64 >> newcharrow)) plot(x + col, y + start_y, 1); //plot led on else plot(x + col, y + start_y, 0); //else plot led off ) start_y++;//add one to y so we draw next row one down ) ) ) //print a clock using words rather than numbers void word_clock() ( cls(); char numbers = ( "one ", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" ); chartens numbers = ( "ten", "twenty", "thirty", "forty", "fifty" ); //potentially 3 lines to display char str_a; char str_c; //byte hours_y, mins_y; //hours and mins and positions for hours and mins lines hours = rtc; - ampm * 12; ) if (hours< 1) { hours = hours + ampm * 12; } get_time(); //get the time from the clock chip byte old_mins = 100; //store mins in old_mins. We compare mins and old mins & when they are different we redraw the display. Set this to 100 initially so display is drawn when mode starts. byte mins; //run clock main loop as long as run_mode returns true while (run_mode()) { //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); } get_time(); //get the time from the clock chip mins = rtc; //get mins //if mins is different from old_mins - redraw display if (mins != old_mins) { //update old_mins with current mins value old_mins = mins; //reset these for comparison next time mins = rtc; hours = rtc; //make hours into 12 hour format if (hours >12) ( hours = hours - 12; ) if (hours == 0) ( hours = 12; ) //split mins value up into two separate digits int minsdigit = rtc % 10; byte minsdigitten = (rtc / 10) % 10; //if mins<= 10 , then top line has to read "minsdigti past" and bottom line reads hours if (mins < 10) { strcpy (str_a, numbers); strcpy (str_b, "PAST"); strcpy (str_c, numbers); } //if mins = 10, cant use minsdigit as above, so soecial case to print 10 past /n hour. if (mins == 10) { strcpy (str_a, numbers); strcpy (str_b, " PAST"); strcpy (str_c, numbers); } //if time is not on the hour - i.e. both mins digits are not zero, //then make first line read "hours" and 2 & 3rd lines read "minstens" "mins" e.g. "three /n twenty /n one" else if (minsdigitten != 0 && minsdigit != 0) { strcpy (str_a, numbers); //if mins is in the teens, use teens from the numbers array for the 2nd line, e.g. "fifteen" //if (mins >= 11 && mins<= 19) { if (mins <= 19) { strcpy (str_b, numbers); } else { strcpy (str_b, numberstens); strcpy (str_c, numbers); } } // if mins digit is zero, don"t print it. read read "hours" "minstens" e.g. "three /n twenty" else if (minsdigitten != 0 && minsdigit == 0) { strcpy (str_a, numbers); strcpy (str_b, numberstens); strcpy (str_c, ""); } //if both mins are zero, i.e. it is on the hour, the top line reads "hours" and bottom line reads "o"clock" else if (minsdigitten == 0 && minsdigit == 0) { strcpy (str_a, numbers); strcpy (str_b, "O"CLOCK"); strcpy (str_c, ""); } }//end worknig out time //run in a loop //print line a "twelve" byte len = 0; while (str_a) { len++; }; //get length of message byte offset_top = (31 - ((len - 1) * 4)) / 2; // //plot hours line byte i = 0; while (str_a[i]) { puttinychar((i * 4) + offset_top, 1, str_a[i]); i++; } //hold display but check for button presses int counter = 1000; while (counter >0)( //check for button press if (buttonA.uniquePress()) ( switch_mode(); return; ) if (buttonB.uniquePress()) ( display_date(); ) delay(1); counter--; ) fade_down (); //print line b len = 0; while (str_b) ( len++; ); //get length of message offset_top = (31 - ((len - 1) * 4)) / 2; i = 0; while (str_b[i]) ( puttinychar((i * 4) + offset_top, 1, str_b[i]); i++; ) //hold display but check for button presses counter = 1000; while (counter > 0)( if (buttonA.uniquePress()) ( switch_mode(); return; ) if (buttonB.uniquePress()) ( display_date(); ) delay(1); counter--; ) fade_down() ; //print line c if there. len = 0; while (str_c) ( len++; ); //get length of message offset_top = (31 - ((len - 1) * 4)) / 2; i = 0; while (str_c[i]) ( puttinychar((i * 4) + offset_top, 1, str_c[i]); i++; ) counter = 1000; while (counter > 0)( //check for button press if (buttonA.uniquePress()) ( switch_mode(); return; ) if (buttonB.uniquePress()) ( display_date(); ) delay(1); counter- -; ) fade_down(); //hold display blank but check for button presses before starting again. counter = 1000; while (counter > 0)( //check for button press if (buttonA.uniquePress()) ( switch_mode(); return; ) if (buttonB.uniquePress()) ( display_date(); ) delay(1); counter- -; ) ) fade_down(); ) /// scroll message - not used at present - too slow. void scroll() ( char message = ("Hello There "); cls(); byte p = 6; //current pos in string byte chara = (0, 1, 2, 3, 4, 5); //chars from string int x = (0, 6, 12, 24, 30); //xpos for each char byte y = 0; //y pos // clear_buffer(); while (message[p] != "\ 0") ( //draw all 6 chars for (byte c = 0; c< 6; c++) { putnormalchar(x[c],y,message[ chara[c] ]); //draw a line of pixels turned off after each char,otherwise the gaps between the chars have pixels left in them from the previous char for (byte yy = 0 ; yy < 8; yy ++) { plot(x[c] + 5, yy, 0); } //take one off each chars position x[c] = x[c] - 1; } //reset a char if it"s gone off screen for (byte i = 0; i <= 5; i++) { if (x[i] < -5) { x[i] = 31; chara[i] = p; p++; } } } } //display_date - print the day of week, date and month with a flashing cursor effect void display_date() { cls(); //read the date from the DS1307 byte dow = rtc; // day of week 0 = Sunday byte date = rtc; byte month = rtc - 1; //array of month names to print on the display. Some are shortened as we only have 8 characters across to play with char monthnames = { "January", "February", "March", "April", "May", "June", "July", "August", "Sept", "October", "November", "December" }; //print the day name //get length of text in pixels, that way we can centre it on the display by divindin the remaining pixels b2 and using that as an offset byte len = 0; while(daysfull) { len++; }; byte offset = (31 - ((len-1)*4)) / 2; //our offset to centre up the text //print the name int i = 0; while(daysfull[i]) { puttinychar((i*4) + offset , 1, daysfull[i]); i++; } delay(1000); fade_down(); cls(); // print date numerals char buffer; itoa(date,buffer,10); offset = 10; //offset to centre text if 3 chars - e.g. 3rd // first work out date 2 letter suffix - eg st, nd, rd, th etc // char suffix={"st", "nd", "rd", "th" }; is defined at top of code byte s = 3; if(date == 1 || date == 21 || date == 31) { s = 0; } else if (date == 2 || date == 22) { s = 1; } else if (date == 3 || date == 23) { s = 2; } //print the 1st date number puttinychar(0+offset, 1, buffer); //if date is under 10 - then we only have 1 digit so set positions of sufix etc one character nearer byte suffixposx = 4; //if date over 9 then print second number and set xpos of suffix to be 1 char further away if (date >9)( suffixposx = 8; puttinychar(4+offset, 1, buffer); offset = 8; //offset to center text if 4 chars ) //print the 2 suffix characters puttinychar(suffixposx+offset, 1, suffix[s ]); puttinychar(suffixposx+4+offset, 1, suffix[s]); delay(1000); fade_down(); //print the month name //get length of text in pixels, that way we can center it on the display by divindin the remaining pixels b2 and using that as an offset len ​​= 0; while(monthnames) ( len++; ); offset = (31 - ((len-1)*4)) / 2; //our offset to center up the text i = 0; while(monthnames[i]) ( puttinychar((i*4) +offset, 1, monthnames[i]); i++; ) delay(1000); fade_down(); ) //dislpay menu to change the clock mode void switch_mode() ( //remember mode we are in. We use this value if we go into settings mode, so we can change back from settings mode (6) to whatever mode we were in. old_mode = clock_mode; char* modes = ("Basic", "Small", "Words", "Setup" ); byte firstrun = 1; //loop waiting for button (timeout after 35) loops to return to mode X) for (int count = 0; count< 35 ; count++) { //if user hits button, change the clock_mode if (buttonA.uniquePress() || firstrun == 1) { count = 0; cls(); if (firstrun == 0) { clock_mode++; } if (clock_mode >NUM_DISPLAY_MODES + 1) ( clock_mode = 0; ) //print arrown and current clock_mode name on line one and print next clock_mode name on line two char str_top; //strcpy (str_top, "-"); strcpy(str_top, modes); next_clock_mode = clock_mode + 1; if (next_clock_mode > NUM_DISPLAY_MODES + 1) ( next_clock_mode = 0; ) byte i = 0; while (str_top[i]) ( putnormalchar(i * 6, 0, str_top[i]); i++; ) firstrun = 0; ) delay(50); ) ) //run clock main loop as long as run_mode returns true byte run_mode() ( //if random mode is on... check the hour when we change mode. if (random_mode) ( //if hour value in change mode time = hours. then reurn false = i.e. exit mode. if (change_mode_time == rtc) ( //set the next random clock mode and time to change it set_next_random(); //exit the current mode. return 0; ) ) /else return 1 - keep running in this mode return 1; ) //set the next hour the clock will change mode when random mode is on void set_next_random() ( //set the next hour the clock mode will change - current time plus 1 - 4 hours get_time(); change_mode_time = rtc + random (1, 5); //if change_mode_time now happens to be over 23, then set it to between 1 and 3am if (change_mode_time > 23) ( change_mode_time = random (1 , 4); //set the new clock mode clock_mode = random(0, NUM_DISPLAY_MODES + 1); //pick new random clock mode ) //dislpay menu to change the clock settings void setup_menu() ( char* set_modes = ( "Rndom", "24 Hr","Set", "Brght", "Exit"); if (ampm == 0) ( set_modes = ("12 Hr"); ) byte setting_mode = 0; byte next_setting_mode; byte firstrun = 1; //loop waiting for button (timeout after 35 loops to return to mode X) for(int count=0; count< 35 ; count++) { //if user hits button, change the clock_mode if(buttonA.uniquePress() || firstrun == 1){ count = 0; cls(); if (firstrun == 0) { setting_mode++; } if (setting_mode >NUM_SETTINGS_MODES) ( setting_mode = 0; ) //print arrown and current clock_mode name on line one and print next clock_mode name on line two char str_top; strcpy(str_top, set_modes); next_setting_mode = setting_mode + 1; if (next_setting_mode > NUM_SETTINGS_MODES) ( next_setting_mode = 0; ) byte i = 0; while(str_top[i]) ( putnormalchar(i*6, 0, str_top[i]); i++; ) firstrun = 0; ) delay(50); ) //pick the mode switch(setting_mode)( case 0: set_random(); break; case 1: set_ampm(); break; case 2: set_time(); break; case 3: set_intensity(); break; case 4: //exit menu break; ) //change the clock from mode 6 (settings) back to the one it was in before clock_mode=old_mode; ) //toggle random mode - pick a different clock mode every few hours void set_random())( cls(); char text_a = "Off"; char text_b = "On"; byte i = 0; //if random mode is on , turn it off if (random_mode)( //turn random mode off random_mode = 0; //print a message on the display while(text_a[i]) ( putnormalchar((i*6), 0, text_a[i]) ; i++; ) ) else ( //turn randome mode on. random_mode = 1; //set hour mode will change set_next_random(); //print a message on the display while(text_b[i]) ( putnormalchar((i* 6), 0, text_b[i]); i++; ) ) delay(1500); //leave the message up for a second or so ) //set 12 or 24 hour clock void set_ampm() ( // AM/PM or 24 hour clock mode - flip the bit (makes 0 into 1, or 1 into 0 for ampm mode) ampm = (ampm ^ 1); //change screen intensityintensity void set_intensity() ( cls(); byte i = 0; char text = "Bright"; while(text[i]) ( puttinychar((i*4)+4, 0, text[i]); i++; ) //wait for button input while (! buttonA.uniquePress()) ( levelbar (0.6,(intensity*2)+2.2); //display the intensity level as a bar while (buttonB.isPressed()) ( if(intensity == 15) ( intensity = 0; cls (); ) else ( intensity++; ) //print the new value i = 0; while(text[i]) ( puttinychar((i*4)+4, 0, text[i]); i++; ) //display the intensity level as a bar levelbar (0,6,(intensity*2)+ 2,2); //change the brightness setting on the displays for (byte address = 0; address< 4; address++) { lc.setIntensity(address, intensity); } delay(150); } } } // display a horizontal bar on the screen at offset xposr by ypos with height and width of xbar, ybar void levelbar (byte xpos, byte ypos, byte xbar, byte ybar) { for (byte x = 0; x < xbar; x++) { for (byte y = 0; y <= ybar; y++) { plot(x+xpos, y+ypos, 1); } } } //set time and date routine void set_time() { cls(); //fill settings with current clock values read from clock get_time(); byte set_min = rtc; byte set_hr = rtc; byte set_date = rtc; byte set_mnth = rtc; int set_yr = rtc; //Set function - we pass in: which "set" message to show at top, current value, reset value, and rollover limit. set_date = set_value(2, set_date, 1, 31); set_mnth = set_value(3, set_mnth, 1, 12); set_yr = set_value(4, set_yr, 2013, 2099); set_hr = set_value(1, set_hr, 0, 23); set_min = set_value(0, set_min, 0, 59); ds1307.adjust(DateTime(set_yr, set_mnth, set_date, set_hr, set_min)); cls(); } //used to set min, hr, date, month, year values. pass //message = which "set" message to print, //current value = current value of property we are setting //reset_value = what to reset value to if to rolls over. E.g. mins roll from 60 to 0, months from 12 to 1 //rollover limit = when value rolls over int set_value(byte message, int current_value, int reset_value, int rollover_limit){ cls(); char messages = { "Set Mins", "Set Hour", "Set Day", "Set Mnth", "Set Year"}; //Print "set xyz" top line byte i = 0; while(messages[i]) { puttinychar(i*4 , 1, messages[i]); i++; } delay(2000); cls(); //print digits bottom line char buffer = " "; itoa(current_value,buffer,10); puttinychar(0 , 1, buffer); puttinychar(4 , 1, buffer); puttinychar(8 , 1, buffer); puttinychar(12, 1, buffer); delay(300); //wait for button input while (!buttonA.uniquePress()) { while (buttonB.isPressed()){ if(current_value < rollover_limit) { current_value++; } else { current_value = reset_value; } //print the new value itoa(current_value, buffer ,10); puttinychar(0 , 1, buffer); puttinychar(4 , 1, buffer); puttinychar(8 , 1, buffer); puttinychar(12, 1, buffer); delay(150); } } return current_value; } void get_time() { //get time DateTime now = ds1307.now(); //save time to array rtc = now.year(); rtc = now.month(); rtc = now.day(); rtc = now.dayOfWeek(); //returns 0-6 where 0 = Sunday rtc = now.hour(); rtc = now.minute(); rtc = now.second(); //flash arduino led on pin 13 every second //if ((rtc % 2) == 0) { // digitalWrite(13, HIGH); //} //else { // digitalWrite(13, LOW); //} //print the time to the serial port - useful for debuging RTC issues /* Serial.print(rtc); Serial.print(":"); Serial.print(rtc); Serial.print(":"); Serial.println(rtc); */ }

Now, to complete work on the device, you only need to perform a number of simple operations:


Compiling the program code and further loading it into the microcontroller’s memory will take some time, usually no more than one minute. The successful completion of the operation will be reported in the Arduino IDE console. After which all that remains is to reboot the Arduino using the Reset button on the device - a simple clock on LED matrices is ready!

Ready clock on Arduino

The clock is set using two buttons. The device supports 12- and 24-hour time formats, displays the date and day of the week, and displays the time with or without seconds. It is also possible to change the brightness of the LEDs.


You will probably want to add more features in the future (for example, a thermometer), or install the device in a body of your own design - good results can be achieved by manufacturing on laser cutting machines. But now you can safely say that you have assembled a full-fledged electronic watch with your own hands!

So, after a short technical break, we continue our acquaintance with the ARDUINO MK family. In this lesson we will try to make a clock that works from the internal generator of the MK (with an external generator there will be one of the following lessons) and displays information on an LCD indicator of type 1602 (which means 16 characters in 2 lines, there is also type 1604 - 16 characters in 4 lines, you already understand that the first 2 digits indicate the number of indicator characters and the second ones indicate the number of lines). Let's not delay the introduction, let's get to work.

For the project we will need:

  1. Arduino Uno
  2. LCD indicator 1602
  3. Development board
  4. Wires
  5. Trimmer resistor 10 kOhm

For those who are especially lazy, I advise you to go to the bottom of the page and download the finished sketch; for those who want to learn how to make sketches on their own, I will describe in more detail all the steps of the project. To work on a project correctly and quickly, a work algorithm is required. It’s better to sketch out almost any project on paper and then follow the algorithm step by step. We will do exactly the same. So let's create an algorithm. We have several conditions, let's write them in ascending order:

  1. Seconds work in the range from 0 to 59 in a cycle with a second interval (this is understandable).
  2. Minutes operate in the range from 0 to 59 in a cycle, switching occurs when the seconds value reaches 0.
  3. The clock operates in the range from 0 to 24 (here you can choose to display it in a foreign style from 0 to 12 with AM and PM values, whichever you prefer) in a cycle, switching occurs when the minute value reaches 0.
  4. Display all the necessary information on the display (for example, you may decide not to display seconds but just make a flashing dot between hours and minutes)

We assemble our watches according to this scheme:

Connecting LCD indicator 1602 to ARDUINO

Assembly tips. The 1602 indicator usually comes from China in a “naked” form, i.e. no pins are soldered, I advise you to use double-row computer sockets from motherboards for these purposes, one pin of the socket is inserted into 1602, the second pin is behind the edge of the board, solder both pins to one pin - this increases the mechanical and electrical strength. This diagram does not indicate the backlight connection diagram; these are the next 2 pins to the right of D7. You can connect them to the 3.3V power supply on ARDUINO, you can make a smooth switching on/fading if you connect the positive pin (it is labeled as A-anode) to the ARDUINO output and control the power through this pin, this is a secondary task, for now just connect the pin And on 1602 to 3.3V on ARDUINO, and pin K 1602 to GND ARDUINO.

Now let's start actually designing the watch. We launch the ARDUINO shell on the computer. Let's try to play with 1602 to check the correct connections of the circuit. Let's go File-Examples-LiqidCrystal and select any of the files. We upload the sketch to ARDUINO and watch what happens. If instead of symbols you see black squares, tighten the trimming resistor, this is a contrast regulator (do the same if nothing is displayed at all). Information must be displayed correctly and there simply should not be any “crazy stuff”. If they appear, check the connection diagram, where it was assembled incorrectly. You can immediately see in the sketches how to access the LCD indicator and be amazed at the ease of working with it! If everything works correctly for you, let’s move directly to programming.

Let's decide that our timer will work without the delay operator as written. Therefore, we enter the following code:




#include

// Variables will change:


void setup () {
lcd.begin(16, 2);

void loop ()
{

if (currentMillis - previousMillis >= interval) (


This code will already work but will not display anything. 1 will be added to the variable s every second. I.e. we already got the exact interval of 1 second! Now, following the algorithm, we need a limit of the variable between 0 and 59. Let's do it.

if(s>
{


}

Add this code to the program. According to the description, everything is clear - if the value of s is greater than 59, then we assign it 0 and add 1 minute to the variable m. At the moment we have a fully working second timer and an infinite (up to 32768 - the maximum value of the integer type) minute counter. Now you need to calculate the minutes in the same way. We write the following:

if (m>59) // if m values ​​are greater than 59
{


}

Add lines to the program. It should already look like this:

int h,m,s; // variables for hours, minutes, seconds
boolean z; // variable to display the point
// connect the indicator library
#include

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)

void setup () {
lcd.begin(16, 2);

void loop ()
{

Unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) (
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // add one, equivalent to writing s=s+1;

// seconds counting section

if (s>59) // if the value of s is greater than 59
{
s=0; // assign value 0 to variable s
m++; // add 1 to the variable m responsible for minutes
}

// minute counting section

if(m>
{
m=0; // assign value 0 to variable m
h++; // add 1 to the variable h responsible for the clock
}

In principle, everything is clear. All that remains is to process the clock. Let's do it. We add after the minute counting section:

if(h>
{

}

That's it, the watch is ready! Fill in the sketch and the clock will run as it should! I would like to draw your attention to the fact that they will count in 24-hour format. Try making a 12-hour format yourself. All that remains is to display the information on the LCD indicator. There are 2 ways to write code to display information.

  1. Calculate some data and immediately output it
  2. Calculate all the data and display it all at once.

At this point you will decide for yourself which path you will take. If you follow the first path, then you need to write the display of information immediately in the calculation sections, if you follow the second path, you write a block after all the calculations. Let's take the second path because... it is more preferable and more logical (although, to be honest, my test sketch was written along the first path). So, to transfer data to the 1602 indicator, only 2 commands are used:

lcd.setCursor(3, 0); // sets the cursor to the 3rd character of the 0th line (counting lines and characters starts from 0)
lcd.print(0); // print (print- print, learn English) 0

There is also the lcd.clear command; meaning clear screen but we don't have to use it here.

We begin to display information. Let's start with seconds (you can start with any value, do whatever is convenient for you). Set the cursor to line 0 in position 6 and display the seconds value. Why in 6th position you ask? Let's imagine the following: the clock display format is 2 characters (hours), a separator (let's say:), 2 characters (minutes), a separator (:) and, finally, seconds. We count from the zero position: 0+2+1+2+1=6. Since the counting starts from 0, we subtract one from the data (zero is also a number), we get 6-1=5. This is how long it takes to display hours and minutes with delimiters, the next position is the second position and it is equal to 5+1=6. A little confusing but I'll write the following hh:mm:ss and calculate the coordinates first starting from 0. This is how coordinates are calculated on indicators of the 16xx family. Under these conditions, the clock will be displayed in the upper left corner, you can change the location as you wish, you can even enter a variable and, by selecting it, select the display position you need. Okay, let's write these lines:

lcd.setCursor(6, 0); // sets the cursor to the 6th character of the 0th line (line counting starts from 0)

The program will look like this:

int h,m,s; // variables for hours, minutes, seconds
boolean z; // variable to display the point
// connect the indicator library
#include

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)

void setup () {
lcd.begin(16, 2);

void loop ()
{

Unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) (
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // add one, equivalent to writing s=s+1;

// seconds counting section

if (s>59) // if the value of s is greater than 59
{
s=0; // assign value 0 to variable s
m++; // add 1 to the variable m responsible for minutes
}

// minute counting section

if (m>59) // if the value of m is greater than 59
{
m=0; // assign value 0 to variable m
h++; // add 1 to the variable h responsible for the clock
}

// hour counting section

if (h>23) // if the value of h is greater than 23
{
h=0; // assign value 0 to variable h
}

// information output section

lcd.setCursor(6, 0);
lcd.print(s); // print data from variable s

Fill in the sketch and... seconds started to appear!!! Just pay attention, when counting from 0 to 59, everything is fine, but as soon as the next minute begins, tens of seconds begin to change instead of units of seconds, i.e. The time is displayed incorrectly. Let's figure this out. We have given the program a strict position 6,0 , and it displays the data exactly at this position without overwriting what is after this position. Exit 2. Apply lcd.clear or describe the display correctly, especially since with the first option it will be quite difficult to get used to the jumping digits of seconds (later minutes and hours). Let's write a correct display handler. What conditions will there be in this processing? Let's think about it. If the seconds are less than 10, then we write their value in the 7th position (6+1=7) and in the 6th position we write 0, if greater than or equal to 10, we write in the 6th position. It's quite simple. The condition will look like this:

if (s<10) //если секунд меньше 10
{

lcd.print(0); //print 0


}
else
{


}

Paste this code instead

lcd.setCursor(6, 0); // sets the cursor to the 7th character of the 0th line (line counting starts from 0)
lcd.print(s); // print data from variable s

and we are happy with the result already obtained! Everything is displayed correctly! In addition, a separator “:” appeared before the seconds! In the same way we write a handler for minutes and hours with the corresponding cursor coordinates. It might look like this for minutes:

If (m<10)
{
lcd.setCursor(3, 0);
lcd.print(0);
lcd.setCursor(4, 0);
lcd.print(m);
}
else
{
lcd.setCursor(3, 0);
lcd.print(m);
}

and so on for hours:

If (h<10)
{
lcd.setCursor(0, 0);
lcd.print(0);
lcd.setCursor(1, 0);
lcd.print(h);
}
else
{
lcd.setCursor(0, 0);
lcd.print(h);
}

Our program will take the following form:

int h,m,s; // variables for hours, minutes, seconds
boolean z; // variable to display the point
// connect the indicator library
#include

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Variables will change:
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 1000; // interval at which to blink (milliseconds)

void setup () {
lcd.begin(16, 2);

void loop ()
{

Unsigned long currentMillis = millis();

if (currentMillis - previousMillis >= interval) (
// save the last time you blinked the LED
previousMillis = currentMillis;
s++; // add one, equivalent to writing s=s+1;

// seconds counting section

if (s>59) // if the value of s is greater than 59
{
s=0; // assign value 0 to variable s
m++; // add 1 to the variable m responsible for minutes
}

// minute counting section

if (m>59) // if the value of m is greater than 59
{
m=0; // assign value 0 to variable m
h++; // add 1 to the variable h responsible for the clock
}

// hour counting section

if (h>23) // if the value of h is greater than 23
{
h=0; // assign value 0 to variable h
}

// information output section

// output seconds

if (s<10) //если секунд меньше 10
{
lcd.setCursor(6, 0); // cursor at position 6, line 0
lcd.print(0); //print 0
lcd.setCursor(7, 0); //cursor at position 7 of line 0
lcd.print(s); //print the value of the variable s
}
else
{
lcd.setCursor(6, 0); //otherwise the cursor is in the 6th position of the 0th line
lcd.print(s); // print the value of the variable s
}
lcd.setCursor(5, 0); // cursor at position 5, line 0
lcd.print(':'); // print separator between seconds and minutes

// output minutes

if (m<10)
{
lcd.setCursor(3, 0);
lcd.print(0);
lcd.setCursor(4, 0);
lcd.print(m);
}
else
{
lcd.setCursor(3, 0);
lcd.print(m);
}
lcd.setCursor(2, 0); // cursor at position 2 of line 0
lcd.print(':'); // print separator between minutes and hours

// output clock

if (h<10)
{
lcd.setCursor(0, 0);
lcd.print(0);
lcd.setCursor(1, 0);
lcd.print(h);
}
else
{
lcd.setCursor(0, 0);
lcd.print(h);
}

The entire code fit into just over 3 kilobytes! Most of them were consumed by the library for the LCD indicator. I would like to note right away that this code is only the body of the program; you also need to add a function for setting the time. In addition, you can add a photoresistor and brighten the display backlight. You can add an alarm input function and work with sound. You can also display the room temperature, date, etc... In general, this indicator with the appropriate sensors can turn this watch into a unique device! The only disadvantage of this device is that if there is a power outage, you will have to set the clock again. Therefore, in the near future I will describe how to work with the RTC module. When working with it, even if the power goes out, when voltage is applied, the clock will work as if nothing had happened. For a cheaper version of the clock, you can use arduino pro mini, this is the same controller only does not have a USB connector but costs several times less and is very small in size. You can also use arduino nano, the same pro but with a USB connector. Until the next lesson. Thank you all for your attention.

PS*. By the way, the procedure for displaying values ​​can be written as a separate function and passing the necessary values ​​to it. Try this and compare the amount of memory occupied. Always aim for the smallest volume possible.