About two years ago I decided to make something based on LED matrices. A bit later I bought a pretty cheap GPS module. That was the start of the GPS wall clock development. This clock does not have any buttons and does not require any settings. Time zone and automatic DST correction are should be defined just once, before the flashing. After that you just power it on and it works. In addition to the time you can see current temperature, humidity and pressure diagram. Display brightness automatically adjusts according to the information from the light sensor.
On the picture below you will find a brief clock schematic:Figure 1 — Brief schematic.
Clock is controlled by the STM32F103C8T6 microcontroller (ARM Cortex M3, 64kB flash, 20kB SRAM). I didn't have time to make my own processor board, so decided to buy it on eBay:Figure 2 — Processor board.
This board you can easily find, just search for "STM32F103C8T6 minimum system board", it costs less than 2$ per board.
The whole display has a 80x24 pixels resolution and is made of 8x8 LED matrices, which are controlled by drivers based on HT16K33 (it is the cheapest solution I could find). Each driver can control 8x16 LED matrix and has 16 brightness steps. According to the documentation it supports a simple keyboard, but I didn't use this feature. These drivers have I2C interface, up to 8 devices can be connected to one I2C bus. So, the display is controlled by 15 drivers, which are connected to 2 I2C buses. In order to make the display cheaper I ordered just 10 boards, the rest was soldered manually on a breadboards. Here is a picture of the assembled driver:Figure 3 — Driver module.
On the left bottom side of this picture you can see three resistors. These resistors are responsible for the 3 less significant bits of the I2C address. R1 is responsible for bit 2, R2 for bit 1 and R3 for bit 0. Soldered resistors means 1 in the appropriate bit.
In addition to the drivers, several other modules are also connected to the I2C buses. These are light sensor module, for the automatic brightness adjustment (based on BH1750), temperature/humidity sensor (based on Si7021) and pressure sensor (based on BMP180). All modules are freely available in the Internet. This is how they look like:Figure 4 — Sensor boards.
Please pay your attention to the Si7021. This chip is protected with a special sticker. Do not remove it unless you have another protection, because this sticker protects chip from the degradation and allows you to make humidity measurements.
In addition to sensors I decided to install FRAM chip. It stores the pressure log for the last 24 hours, so in case of the power interruption processor can recover this log. I do not publish detailed connection diagram for this chip because it is very simple (just look to the datasheet). GPS module is based on a EB-800 module (from TranSystem). It is connected via UART and works at the 9600 baudrate, 8n1 mode. This module does not require any additional settings and works out of the box. It seems that it is not available on eBay, but you can use almost any GPS module you want, in the following chapter I'll describe which messages are required for the correct operation.
Software is written in EmBitz (former EmBlocks), I wrote about it a bit ago. I decided to try a small operating system for the microcontroller, so I choose FreeRTOS. It is well documented, has a lot of examples and has a sertified SafeRTOS branch.
Now let's go deeper into the details. After the controller initialization two tasks become active. First task parses NMEA messages from the GPS module, second task once a second reads sensors and updates the display. This is the software structure:Figure 5 — Software structure.
GPS module is always on, once a second it sends a lot of messages using NMEA protocol. UART interrupt puts each received byte to the queue, which is parsed in a separate task. Received bytes are parsed with NMEA parser. If the difference between the local time and the received time is bigger than one minute then local time will be adjusted. NMEA parser takes information from the following messages:
- GPRMC (UTC time)
- GPGSA (active satellites list)
- GPGSV (active satellites info)
- GPZDA (it was the last message from EB-800 module, so I decided to use it to sort satellites by signal level. This feature is now not used now.)
Most of the time main task is in the blocked state, waiting for the RTC event. As soon as it occurs, main task unlocks, updates information from all sensors and updated the display. Once a second main task updates a pressure log in the FRAM. FRAM allows about 1010 cycles, so I don't have to think about wear levelling.
As you can see, SW structure is pretty simple. I use built-in watchdog just to be sure that the clock will never hang. This watchdog resets controller in case of any SW/HW fault, it is highly recommended to use watchdog in such a devices. When controllers resets from the watchdog, the picture on the display is not updated for about 3 seconds, then it continues to work again. I would like to mention that it occurs very rarely (once a month or so).
Source code and all information about this project is on my GitHub, I have only a small comment. By default SW uses english, cyrillic letters are also implemented. If you want to use other languages, I recommend to use a single-byte codepage. Font table is located in usr_font.c, all text messages are in usr_messages.c. These files should be saved in the same single-byte codepage before the compilation. In my case it is codepage 1251, which has both latin and cyrillic letters.
Assembly is pretty easy. All driver modules were glued together, each module has its own set of the address resistors. Other modules were fixed with a glue gun. After that I powered everything on and flashed the processor board. Front panel is made from the custom photoframe, the glass was covered with the dark transparent film. The rest of the case is done from the piece of wood and painted with the spay can. Small sieves, which allow air to flow through the case, are made from the small kitchen stainless steel sieves. In my case everything looks like this:Figure 6 — Assebly.
As you can see, it is pretty simple, just a bit of glue and several wires. :)
I understand that it can be done without any operating systems, synchronization primitives, tasks and so on. Some people would write everything in assembler or make everything without microcontroller, using just transistors. This clock was made in order to try FreeRTOS and I can report that it works pretty stable for more than one year. Our family is accustomed to check time with this clock. It is very useful, especially in winter, when it's dark early in the morning. After the wake you just look at the clock and know exactly, if you have some more time to sleep or it's already time to pull yourself out of a warm blanket.
Finally, I would like to show you a small video with a brief project overview: