How to build a Mechanical 7-Segment Display Clock Using Raspberry Pi Pico
Authors : Abraham Joseph, Adwaith K, Cristo Varghese, Fleming Joby, Mark Stephen, Soorya John (Students of Christ College of Engineering)
How to Build a Mechanical 7-Segment Display Clock Using Raspberry Pi Pico
Our Journey into Electromechanical Timekeeping
Mechanical displays have always fascinated us because they turn abstract data into physical motion. Unlike conventional digital clocks that silently update numbers on a screen, mechanical displays use movement to represent time, making every change visible and engaging.
This project documents the development of a mechanical 7-segment display clock built using a Raspberry Pi Pico microcontroller, PCA9685 PWM driver boards, servo motors, and a Real-Time Clock (RTC) module. Each digit is formed using seven servo-driven flaps that physically move to display the current time.

The primary challenge of this build was coordinating a large number of servo motors while maintaining accurate timekeeping and stable power delivery. By using hardware PWM drivers and an external RTC, the system achieves reliable operation and modular scalability.
In this guide, we walk through the design approach, hardware setup, and control logic behind building a practical and visually striking mechanical clock.
The Hardware: Whatโs Under the Hood
Building a large-scale mechanical display is as much a power distribution and motion-control challenge as it is a programming one. Since this project relies on 28 servo motors driving physical flaps, careful hardware selection was essential to ensure smooth motion, reliability, and long-term stability.
Unlike small servo projects where motors are driven directly from a microcontroller, this build uses dedicated PWM driver boards and a high-current external power supply to handle the electrical and timing demands of multiple servos operating simultaneously.
Core Components
| Component | Specification | Design Choice & Experience |
|---|---|---|
| Microcontroller | Raspberry Pi Pico (RP2040) | Dual-core architecture, deterministic timing, reliable IยฒC support. |
| PWM Driver | PCA9685 (16-Channel, IยฒC) ร2 | Stable servo control and clean scaling. |
| RTC Module | DS3231 / DS1307 | Battery-backed accurate timekeeping. |
| Servo Motors | MG996 (28 Units) | High torque for mechanical segments. |
| Power Supply | 5V, 36A DC | Prevents voltage sag during motion. |
| Capacitors | 4700ยตF Electrolytic | Reduces noise and absorbs spikes. |
| Mechanical Frame | Custom 7-Segment Structure | Stable servo mounting & smooth flap movement. |
Why This Hardware Configuration Matters
The combination of a Raspberry Pi Pico and PCA9685 PWM drivers ensures that servo control remains precise and jitter-free, even when many segments move at the same time. By delegating PWM generation to dedicated hardware, the Pico is free to focus on high-level control logic and timekeeping coordination.
Using an RTC module completely decouples timekeeping from the microcontrollerโs execution cycle, ensuring that the clock remains accurate even during resets or power interruptions.
Most importantly, the external high-current power supply eliminates one of the most common problems in servo-heavy projects: unstable behavior caused by insufficient current. By separating logic power from servo power and maintaining a common ground reference, the system achieves consistent and repeatable mechanical behavior.
This hardware architecture forms a robust foundation for a mechanical clock that is not only functional, but also scalable for future expansion. The components were fixed to metallic frame as shown below.



Our Setup: A Clean and Precise Connection Map Is Essential
When working with a system that controls dozens of servo motors simultaneously, a clean and well-planned wiring strategy becomes absolutely critical. Although this project does not rely on fast interrupt-driven control loops, it depends heavily on reliable IยฒC communication, stable power distribution, and clear channel mapping to ensure predictable mechanical motion.
By using hardware PWM driver boards and a dedicated RTC module, the Raspberry Pi Pico is relieved from time-critical signal generation and can instead operate as a high-level coordinator for time and motion.
Raspberry Pi Pico โ Peripheral Connections
IยฒC Connections
| Component | Signal | Pico Pin | Function |
|---|---|---|---|
| PCA9685 Board 1 | SDA | GPIO 4 | IยฒC Data |
| PCA9685 Board 1 | SCL | GPIO 5 | IยฒC Clock |
| PCA9685 Board 2 | SDA | GPIO 4 | Shared IยฒC Data |
| PCA9685 Board 2 | SCL | GPIO 5 | Shared IยฒC Clock |
| RTC Module | SDA | GPIO 4 | Time Data |
| RTC Module | SCL | GPIO 5 | Time Sync |
| RTC Module | VCC | 3.3V/5V | Logic Power |
| RTC Module | GND | GND | Common Ground |
Servo Channel Mapping
| Digit | PCA Board | Channels |
|---|---|---|
| Hour Tens | PCA #1 | 0โ6 |
| Hour Units | PCA #1 | 7โ13 |
| Minute Tens | PCA #2 | 0โ6 |
| Minute Units | PCA #2 | 7โ13 |
Motion Calibration and Timing Optimization
Achieving consistent and visually clean motion was one of the most important aspects of this build. Since the display relies on physical movement rather than electronic pixels, even small inaccuracies in servo positioning or timing are immediately noticeable.
Each servo was individually calibrated to define reliable ON and OFF positions for its corresponding segment. Mechanical orientation differences between segments were handled in software, allowing the overall structure to remain simple while maintaining uniform behavior across all digits.
To reduce unnecessary movement and mechanical wear, the system updates the display only when the time changes, ensuring that only the required segments are actuated. Combined with stable power delivery and proper PWM configuration, this calibration process results in smooth, repeatable, and long-term stable operation.
The Final Firmware (Source Code overview)
The firmware represents the final integration of all system components, combining RTC-based timekeeping, IยฒC communication, and servo control through PCA9685 hardware PWM drivers.
The Raspberry Pi Pico initializes the RTC and both PCA9685 boards, continuously reads the current time, and updates the mechanical display only when required. This approach minimizes unnecessary servo movement, reduces mechanical wear, and ensures stable long-term operation.
The firmware includes:
- RTC initialization and real-time data retrieval
- Digit extraction and 7-segment mapping logic
- Servo control using PCA9685 hardware PWM
- Calibration handling for mechanical orientation
- Update logic optimized for mechanical systems
Code
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <RTClib.h>
Adafruit_PWMServoDriver pcaHours(0x41);
Adafruit_PWMServoDriver pcaMinutes(0x40);
RTC_DS3231 rtc;
#define SERVOMIN 160
#define SERVOMAX 525
#define FLAP_DOWN 0
#define FLAP_UP 90
const bool digitMap[10][7] = {
{1,1,1,1,1,1,0}, // 0
{0,1,1,0,0,0,0}, // 1
{1,1,0,1,1,0,1}, // 2
{1,1,1,1,0,0,1}, // 3
{0,1,1,0,0,1,1}, // 4
{1,0,1,1,0,1,1}, // 5
{1,0,1,1,1,1,1}, // 6
{1,1,1,0,0,0,0}, // 7
{1,1,1,1,1,1,1}, // 8
{1,1,1,1,0,1,1} // 9
};
const uint8_t LEFT_DIGIT[7] = {8,7,6,5,4,3,2};
const uint8_t RIGHT_DIGIT[7] = {15,14,13,12,11,10,9};
uint16_t angleToPulse(int angle) {
angle = constrain(angle, 0, 180);
return map(angle, 0, 180, SERVOMIN, SERVOMAX);
}
void setFlap(Adafruit_PWMServoDriver &pca, uint8_t channel, bool state) {
int angle = state ? FLAP_UP : FLAP_DOWN;
if (channel == 2 || channel == 9) {
angle = FLAP_UP - angle;
}
pca.setPWM(channel, 0, angleToPulse(angle));
}
void displayOneDigit(
Adafruit_PWMServoDriver &pca,
const uint8_t seg[7],
int digit
) {
for (int i = 0; i < 7; i++) {
setFlap(pca, seg[i], digitMap[digit][i]);
}
}
void displayTwoDigits(
Adafruit_PWMServoDriver &pca,
int value
) {
displayOneDigit(pca, LEFT_DIGIT, value / 10);
displayOneDigit(pca, RIGHT_DIGIT, value % 10);
}
void setup() {
Serial.begin(115200);
Wire.begin();
if (!rtc.begin()) {
Serial.println("RTC NOT FOUND");
while (1);
}
rtc.adjust(DateTime(F(_DATE), F(TIME_)));
Serial.println("RTC synced to laptop time");
pcaHours.begin();
pcaMinutes.begin();
pcaHours.setPWMFreq(50);
pcaMinutes.setPWMFreq(50);
for (int ch = 0; ch < 16; ch++) {
pcaHours.setPWM(ch, 0, angleToPulse(FLAP_DOWN));
pcaMinutes.setPWM(ch, 0, angleToPulse(FLAP_DOWN));
delay(2);
}
Serial.println("HH:MM flip clock running");
}
void loop() {
static int lastMinute = -1;
static int lastHour = -1;
static int lastSecond = -1;
DateTime now = rtc.now();
if (now.hour() != lastHour) {
lastHour = now.hour();
displayTwoDigits(pcaHours, lastHour);
}
if (now.minute() != lastMinute) {
lastMinute = now.minute();
displayTwoDigits(pcaMinutes, lastMinute);
}
if (now.second() != lastSecond) {
lastSecond = now.second();
Serial.print("TIME โ ");
if (now.hour() < 10) Serial.print('0');
Serial.print(now.hour());
Serial.print(':');
if (now.minute() < 10) Serial.print('0');
Serial.print(now.minute());
Serial.print(':');
if (now.second() < 10) Serial.print('0');
Serial.println(now.second());
}
delay(50);
}