Learn

An Arduino desk clock with a DS3231 and a TM1637 display

  • 192parts on the bench
  • 25boards running now
  • 1.00xreal time, on every board
Arduino Uno: DS3231 desk clocklive0.000 s 0.00x
Click to open it in the editor
The Desk clock example on a simulated ATmega328P, with a logic analyzer decoding the I2C bus.

An Arduino has no idea what time it is. millis() counts from the moment it powered up, and it starts again from zero every time you unplug it. A real-time clock chip fixes that: it keeps the date and time on its own crystal, runs from a coin cell when the board is off, and answers over I2C when the sketch asks. The DS3231 is the one most people end up buying, because it corrects its crystal for temperature and stays accurate to about a minute a year.

The circuit above is an Uno reading a DS3231 once a second and showing the hours and minutes on a four-digit TM1637 display, with the colon blinking so you can see the clock is alive. A logic analyzer is clipped onto the I2C wires, so you can watch every byte of the conversation. It all runs in your browser, on the sketch below.

What you need

  • An Arduino Uno
  • A DS3231 real-time clock module
  • A 4-digit TM1637 display module (the kind with a colon in the middle)
  • A half-size breadboard and jumper wires
  • A coin cell for the clock module, on real hardware (the size depends on the module)

Wiring

Part and pin
Goes to
Note
DS3231 VCC
5 V
Most modules run from 3.3 to 5 volts
DS3231 GND
GND
Shared ground
DS3231 SCL
A5
The Uno’s I2C clock; the module has its own pull-up resistors
DS3231 SDA
A4
The Uno’s I2C data
TM1637 VCC
5 V
Display supply
TM1637 GND
GND
Shared ground
TM1637 CLK
Pin 4
Any digital pin works; this bus is not I2C
TM1637 DIO
Pin 5
Any digital pin works

Two chips, two different two-wire buses

The clock speaks real I2C at the fixed address 0x68. There is no address pin on a DS3231, so you can have only one per bus, and it shares 0x68 with the MPU6050 motion sensor, which is one of the few genuine address clashes in a starter kit.

The TM1637 display looks like I2C on a scope, with the same start and stop and an acknowledge on the ninth clock, but it is not. It has no address, so the first byte is a command, and it sends each byte least significant bit first where I2C sends the most significant first. That is why you cannot drive it with the Wire library, and why the sketch puts it on two ordinary pins.

Reading the time: BCD and one transfer

Every time register in the DS3231 is binary coded decimal: each byte holds two decimal digits, one per four bits. Twenty-five seconds is stored as 0x25, not 25. To turn a register into a normal number, take the top four bits times ten plus the bottom four bits. Forget that step and 37 minutes past the hour shows as 55.

The seven time registers are read in a single transfer, starting at register 0. Reading them one at a time lets the clock tick between reads, so 10:00:59 can come out as 10:01:59 once an hour. The driver does both of these for you, which is why nothing in the sketch below ever sees a BCD byte.

After power-up the chip also sets a flag, OSF in the status register, which means the time cannot be trusted. On a real bench that flag is how you find out the coin cell is flat. The sketch prints it once at startup.

The loop, from the Desk clock example
Ds3231Time now;
if (!rtc.now(now)) {
  Serial.println("read failed");
  delay(500);
  return;
}

if ((int)now.seconds != lastSecond) {
  lastSecond = (int)now.seconds;
  Tm1637::clock(now.hours, now.minutes, digits);
  // The colon blinks on the even second, which is how you can see it running.
  Tm1637::setColon(digits, (now.seconds % 2) == 0);
  panel.show(digits, 5);
}
delay(50);

The libraries on a real Uno

The example uses Mokxi’s own small headers (mokxi_ds3231.h, mokxi_i2c.h and mokxi_tm1637.h) so you can read every line of the protocol. On a real board most people use RTClib from Adafruit for the clock (rtc.begin(), rtc.now(), rtc.adjust() to set the time once, and rtc.lostPower() for the OSF flag) and the TM1637Display library for the display (setBrightness() and showNumberDecEx(), whose dots argument of 0b01000000 lights the colon).

On the display module the colon is wired to bit 7 of the second digit and nowhere else. That is the module’s wiring rather than the chip’s, which is why every TM1637 library has a special case for it.

Temperature, in °F and °C

The DS3231 has a thermometer inside, because it needs one to correct its crystal. It reports degrees Celsius in quarter-degree steps, and once a minute the sketch prints it after the time. To show Fahrenheit, multiply by 9, divide by 5 and add 32: 25 °C, the part’s default in Mokxi, is 77 °F. Keep it in integer math on an Uno, for example (quarters * 9) / 20 + 32, where quarters is the raw reading in quarter degrees.

In the editor the chip’s temperature is a property of the part, from -40 to 85 °C (-40 to 185 °F). Change it and the printed reading follows. Keep in mind it measures the chip, not the room: on a real board it reads a little warm.

Try it in the editor

Most US clocks show 12-hour time. Before the Tm1637::clock line, add int h = now.hours % 12; if (h == 0) h = 12; and pass h instead of now.hours. Midnight and noon now show 12, and 13:00 shows 1:00.

Open the logic analyzer and press Run. The trace shows the read as S 0x68W 0x00 Sr 0x68R, followed by the seven registers. Find the seconds byte and check that it is BCD.

Set an alarm for a different second with rtc.setAlarm1Seconds() and watch for the alarm line in the serial monitor.

Where to buy the parts in the US

Adafruit sells a DS3231 breakout with a coin cell holder (it takes a CR1220) and a STEMMA QT version that plugs in without soldering. The blue DS3231 modules and the TM1637 displays found in starter kits are sold under many generic brands by the online marketplaces. SparkFun and DigiKey carry real-time clock boards and chips; search their catalogs for DS3231.

One caution about the cheap blue modules: many include a charging circuit meant for a rechargeable LIR2032 cell. With an ordinary CR2032 in it, the common advice is to use the rechargeable cell instead or remove the charging resistor. Check the listing for your module.

Common mistakes

Forgetting BCD. If the minutes jump from 09 to 16, the sketch is printing raw register bytes.

Driving the TM1637 with Wire. It has no address and sends bits in the other order, so it needs its own library on any two pins.

Setting the time on every boot. A sketch that calls rtc.adjust() in setup() every time resets the clock to the compile time whenever the board restarts. Set it once, or only when lostPower() says so.

Reading registers one at a time, which lets the clock roll over between reads.

Questions

How do I connect a DS3231 to an Arduino Uno?

VCC to 5 V, GND to GND, SDA to A4 and SCL to A5. Most modules already carry the I2C pull-up resistors. The chip answers at address 0x68.

How accurate is a DS3231?

The DS3231 corrects its crystal for temperature, so it keeps time to within about a minute a year across normal room temperatures, where a DS1307 can drift that much in a month.

Does Mokxi’s DS3231 keep time on a battery?

No. The part has no coin cell, so it starts at the time in its start property each run, with the OSF flag set. Press S on the module to set it to your computer’s clock.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.