Introduction
Ever wanted to add a real-time clock (RTC) to your project but didn’t have the hardware handy? With Velxio, you can simulate a complete I2C RTC readout using a Raspberry Pi Pico and a virtual DS1307—all in your browser. In this tutorial, we’ll walk through the circuit, the code, and the key concepts so you can confidently use I2C communication in your own projects.
Circuit Walkthrough
Let’s start by looking at the circuit. Open the example in Velxio and you’ll see the following components:

The circuit is simple: a Raspberry Pi Pico (nano-rp2040) is connected to two LEDs and a virtual DS1307 RTC (not shown as a separate component—the RTC is built into the simulation). The LEDs are used as visual indicators for I2C activity.
- LED (blue): Connected to pin D12 (SDA line) via a blue wire. Its anode (A) connects to D12, and its cathode (C) goes to GND.1 through a black wire.
- LED (yellow): Connected to pin D10 (SCL line) via an orange wire. Its anode (A) connects to D10, and its cathode (C) goes to GND.1 through a black wire.
Both LEDs share the same ground (GND.1). The I2C bus consists of the SDA (data) and SCL (clock) lines. The DS1307 RTC is internally connected to these lines at address 0x68. The LEDs are not strictly necessary for I2C communication, but they give a visual cue that the lines are toggling.
To build this circuit yourself, you can use the Velxio component picker:

Search for “Raspberry Pi Pico” and “LED”, then wire them as described. The canvas toolbar helps you zoom and arrange components:

Code Walkthrough
Now let’s dive into the code. The sketch uses the Wire library (built-in) to communicate with the DS1307 over I2C. Here’s the full code:
// Raspberry Pi Pico — I2C DS1307 RTC Read
// Reads time from virtual RTC at address 0x68
#include <Wire.h>
byte bcdToDec(byte val) {
return ((val >> 4) * 10) + (val & 0x0F);
}
void setup() {
Serial.begin(115200);
delay(500);
Wire.begin();
Serial.println("=== Pico I2C RTC Read ===");
Serial.println("Reading DS1307 at 0x68 (system time)");
Serial.println();
}
void loop() {
// Set register pointer to 0
Wire.beginTransmission(0x68);
Wire.write(0x00);
Wire.endTransmission();
// Read 7 bytes: sec, min, hr, dow, date, month, year
Wire.requestFrom(0x68, 7);
if (Wire.available() >= 7) {
byte sec = bcdToDec(Wire.read() & 0x7F);
byte min = bcdToDec(Wire.read());
byte hr = bcdToDec(Wire.read() & 0x3F);
byte dow = bcdToDec(Wire.read());
byte date = bcdToDec(Wire.read());
byte month = bcdToDec(Wire.read());
byte year = bcdToDec(Wire.read());
const char* days[] = {"", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
Serial.print("Time: ");
if (hr < 10) Serial.print('0'); Serial.print(hr); Serial.print(':');
if (min < 10) Serial.print('0'); Serial.print(min); Serial.print(':');
if (sec < 10) Serial.print('0'); Serial.print(sec);
Serial.print(" Date: ");
Serial.print(days[dow]); Serial.print(' ');
if (date < 10) Serial.print('0'); Serial.print(date); Serial.print('/');
if (month < 10) Serial.print('0'); Serial.print(month); Serial.print('/');
Serial.print("20"); if (year < 10) Serial.print('0'); Serial.println(year);
} else {
Serial.println("RTC not responding!");
}
delay(1000);
}
How It Works
-
Setup: We start serial communication at 115200 baud and initialize the I2C bus with
Wire.begin(). The DS1307 is at address 0x68. -
Loop: Every second, we:
- Send a
beginTransmissionto address 0x68, write 0x00 (the register pointer to the seconds register), andendTransmission. - Request 7 bytes from the RTC using
Wire.requestFrom(0x68, 7). - Read each byte and convert from BCD (Binary-Coded Decimal) to decimal using the helper function
bcdToDec. - Format and print the time and date to the Serial Monitor.
- Send a
-
BCD Conversion: The DS1307 stores time values in BCD format. For example, the byte
0x45represents 45 in BCD (4*10 + 5 = 45). ThebcdToDecfunction extracts the tens digit (high nibble) and ones digit (low nibble) and combines them. -
Masking: We apply masks to certain registers:
- Seconds:
& 0x7Fclears the CH (clock halt) bit. - Hours:
& 0x3Fclears the 12/24-hour mode bit (assuming 24-hour mode).
- Seconds:
Key Concepts
- I2C Protocol: A two-wire serial bus (SDA, SCL) for communication between microcontrollers and peripherals. Each device has a unique address.
- Wire Library: Arduino’s built-in I2C library. Use
Wire.begin()to join the bus,Wire.beginTransmission(addr)to start talking to a device,Wire.write()to send data,Wire.endTransmission()to finish, andWire.requestFrom(addr, count)to read bytes. - BCD Format: Many RTCs use BCD to simplify display. You must convert to decimal for human-readable output.
- Register Mapping: The DS1307 has a specific register map: seconds (0x00), minutes (0x01), hours (0x02), day-of-week (0x03), date (0x04), month (0x05), year (0x06).
Common Pitfalls & Debugging Tips
- Wrong I2C Address: Double-check the device address. The DS1307 is typically 0x68 (7-bit). If you get “RTC not responding!”, verify the address.
- Missing Pull-up Resistors: In real hardware, I2C lines need pull-up resistors. In Velxio, they are simulated internally, so you don’t need to add them.
- Baud Rate Mismatch: Ensure the Serial Monitor baud rate matches 115200.
- Register Pointer Not Set: Always set the register pointer before reading. The code writes 0x00 to point to the seconds register.
- Masking Errors: Forgetting to mask the CH bit (seconds) or the 12/24-hour bit (hours) can give incorrect values.
Suggested Extensions
- Set the Time: Add code to write the current time to the RTC. Use
Wire.write()to set each register. - Alarm Function: Implement an alarm that triggers an LED when a certain time is reached.
- Display on LCD: Use an I2C LCD to show the time instead of the Serial Monitor.
- Multiple RTCs: Try reading from two different I2C devices on the same bus.
Try It Yourself!
Ready to experiment? Open the live example in Velxio and run the simulation. You’ll see the time printed in the Serial Monitor every second. Modify the code to change the update interval or add new features.
Launch the Pico I2C RTC Read Example
Happy simulating!