Battery-powered color e-ink photo frame driven by a Raspberry Pi and an Arduino power controller.
Warning, below has been written by an LLM only lightly edited by me and may not be 100% correct.
- Video and Build Image
- What This Project Does
- Hardware Architecture
- Component List and Purchase Links
- Software Components
- I2C Protocol (Pi master -> Arduino slave
0x12) - Pi App Behavior
- Image Server Behavior
- Setup Guide
- Known Issues and Practical Notes
- Safety and Power Notes
- Verification Checklist
- YouTube video: https://www.youtube.com/watch?v=AknCTQES6c4
PiFrame updates a 4:3 image on an e-ink screen, then shuts the Raspberry Pi fully off to save power. An Arduino Pro Mini stays alive, watches a button/RTC interrupt, and switches Pi power back on when needed.
High-level behavior:
- Arduino powers Pi on.
- Pi runs Python/imagesd.py, requests one image from a local CGI endpoint, and renders it to the e-ink display.
- Pi waits for user input for 45 seconds.
- If no cancel input is received, Pi sets an RTC alarm and requests shutdown.
- Arduino receives shutdown request over I2C, waits 10 seconds, then cuts Pi power.
- RTC alarm (or manual button press) wakes the system for the next update.
- Raspberry Pi (the transcript notes moving away from original Pi Zero due to boot time; Pi Zero 2 class is more practical).
- Arduino Pro Mini (3.3V variant expected by current voltage logic).
- Pimoroni Inky color display (transcript references 13.3-inch color e-ink panel).
- DS3231 RTC module (alarm interrupt wake).
- LiPo battery pack powering the whole frame.
- DC-DC converter to Pi 5V rail.
- High-side switching stage using one N-channel and one P-channel MOSFET.
- Voltage divider into Arduino A0 for battery telemetry.
- Button on frame side (plus optional duplicate button in parallel).
- I2C buffer/isolator (mentioned in transcript as part of bus-stability work).
From fullFrame/fullFrame.ino:
D7: Pi power switch control.D3: user button interrupt.D2: RTC interrupt input.A0: battery voltage ADC.- I2C slave address:
0x12.
From Python/imagesd.py:
- Pi local button: GPIO 5 via
gpiozero.Button. - I2C bus
1for Arduino (0x12) and DS3231 (0x68).
Where possible I've linked to the actual items I bought, but Pis seem to be expensive/hard to come by at the moment.
- Inky Impression 13" Colour e-Ink Screen: Product page
- Raspberry Pi Zero 2 W: Listing
- SanDisk Micro SD Card: Listing
- Arduino Pro Mini 3.3V: Listing
- USB Programming Board FT232RL FTDI Module 5V/3.3V: Listing
- RTC DS3231 Real Time Clock: Listing
- I forgot to mention that I removed the battery cell and contacts, and replaced the right-angle pins with straight.
- IRF9540N P-Channel MOSFET: Listing
- FQP30N06L N-Channel MOSFET: Listing
- Step Down Voltage Regulator 5v~24v to 1.8v/3.3v/12v 3A: Listing
- Note that adjustment is very sensitive and can be jogged; ended up soldering to fixed 5V rather than manually setting 5.2V.
- MCP1702-3302E/TO IC REGULATOR LINEAR 3.3V 250MA TO92-3: Listing
- A good option for higher voltages.
- MCP1700-3302E LDO Voltage Regulator 3.3V 250mA TO-92: Listing
- Another option for lower voltages, with slightly lower quiescent current.
- MT3608 DC-DC Voltage Step-Up Adjustable Converter Module 1A: Listing
- This was my original failed step-up option.
- Pack of resistors: Listing
- Solder: Listing
- Single Row Male Breakable Pin Header Connector Strip: Listing
- Very handy for various uses.
- On-On Mini PCB Slide Switch SPDT 5A: Listing
- A little too big for normal PCB/breadboard, and 5A is likely overkill.
- Nive Red Buttons: Listing
- These took a long time to ship (from China).
- USB Type A Right Angle Socket: Listing
- Solderless Prototype Breadboard: Listing
- ElectroCookie Solderable Breadboard: Listing
- Colored 9 Meter Spools 22 Gauge Jumper Wire: Listing
- Balsa Wood Bundle: Listing
- For mounting
- Ikea picture frame: Product range
- PCA9515A I2C Buffer / Repeater: Listing
- Texas 74HC4066 Quad SPST Analog Switch SOIC-14 (alternative option): Listing
- Mini LED Battery Meter: Listing
- I had real problems creating a voltage divider for this; likely better to use one matched to your battery (for example, 2-cell LiPo).
- LED 2S Battery Meter: Listing
- I have no direct experienceof this, but it's likely a better fit.
- fullFrame/fullFrame.ino: production Arduino firmware (power switching, sleep, I2C slave protocol, button/RTC interrupts).
- Python/imagesd.py: production Pi app (fetch image, display, low-battery warning, RTC alarm setup, controlled shutdown).
- Python/pics3.cgi: image server script (random selection, avoids immediate repeats, portrait handling, blur-fill to 4:3, JPEG response).
- Python/piframe.service: systemd unit that waits for network readiness, then runs git pull before launching the Python app.
- SleepTimer/SleepTimer.ino: RTC sleep/wake experiment sketch.
- Voltage/Voltage.ino: battery measurement test sketch.
Defined by fullFrame/fullFrame.ino and consumed in Python/imagesd.py:
0x01read: button event (1 once, then cleared).0x02read: battery voltage encoded as one byte wherevolts = raw / 25.0.0x10write: Pi is shutting down (Arduino enters delayed power-off state).0x11write: cancel shutdown (implemented in Arduino).
Arduino LED states:
- Off: Pi power off.
- Solid on: Pi power on.
- 2 Hz blink: shutdown pending.
Current defaults in Python/imagesd.py:
- Image endpoint:
http://192.168.1.4/py/pics3.cgi. - Wait before shutdown:
45seconds. - Sleep interval before next RTC wake:
24 * 60minutes (24 hours). - Low battery threshold:
6.75V. - Display saturation:
0.5.
Flow:
- Read voltage from Arduino over I2C.
- Fetch one image from CGI endpoint (
?v=<voltage>appended). - If low voltage, send local email via
s-nailand overlay warning text on the image. - Display image on Inky panel.
- During wait window:
- Pi GPIO button cancels shutdown.
- Arduino button triggers immediate script restart (new image).
- On timeout:
- Disable DS3231 32kHz output bit.
- Program DS3231 alarm.
- Send shutdown command to Arduino.
- Run
sudo shutdown -h now.
The CGI script Python/pics3.cgi:
- Reads source images from
/srv/http/192.168.1.4/resized/. - Picks a random image but avoids repeating the most recently logged filename.
- Logs timestamp, filename, and incoming voltage query value to
log.log. - Processing:
- Portrait image: center-crop to square first.
- If narrower than 4:3: generates blurred side fill.
- Else: center-crops to exact 4:3.
- Resizes output to
1600x1200JPEG.
- Open fullFrame/fullFrame.ino in Arduino IDE.
- Board settings from .vscode/arduino.json:
- Board:
arduino:avr:pro - CPU:
16MHzatmega328
- Board:
- Install required library:
LowPower(Rocket Scream variant).
- Upload firmware to Pro Mini using your USB-serial programmer.
Note: the transcript indicates power-saving hardware mods (removing board LEDs/regulator) were used; those are optional but materially affect standby current.
Install system and Python packages needed by Python/imagesd.py:
sudo apt update
sudo apt install -y python3 python3-pip python3-pil python3-smbus i2c-tools s-nail
pip3 install requests pillow gpiozero smbus2 adafruit-circuitpython-ds3231 inkyEnable I2C in raspi-config, then reboot.
- Copy repository to Pi (expected by service as
/home/jamie/PiFrame). - Ensure Python/imagesd.py is executable.
- Install service file Python/piframe.service to
/etc/systemd/system/piframe.service. - Edit service paths/user for your machine.
- Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable piframe.service
sudo systemctl start piframe.serviceService behavior in this repo:
- It blocks startup until internet is reachable (ping checks in
ExecStartPre). - On each run, it changes into the repo, runs
git pull, activates the virtualenv, then runs Python/imagesd.py. - Practical workflow: you can commit and push updates while the frame is off; at the next wake/update cycle, the service pulls latest changes automatically. You do not need to modify files with the frame powered on.
If using the included CGI script:
- Deploy Python/pics3.cgi to your web server CGI path.
- Update
IMAGE_FOLDERto your photo directory. - Ensure script has execute permission and PIL/Pillow available on host.
- Update
Config.IMAGE_URLin Python/imagesd.py.
- I2C lockups can still occur (explicitly described in the transcript); a hard power rail break/reset jumper is useful for recovery.
- E-ink color quality is image-dependent; photos with strong contrast and limited color palettes look best.
- Acrylic/gloss front layers can reduce apparent saturation; transcript notes improved perceived quality after removing acrylic.
- Boot time significantly impacts user experience and total energy per update cycle; faster Pi models improve both.
- Battery chemistry and charge/discharge safety are your responsibility.
- Validate converter thermal behavior and inrush margins during e-ink refresh.
- The project measures voltage but does not implement a full BMS in software.
After wiring and flashing:
- Arduino LED on solid after power-up.
i2cdetect -y 1on Pi shows0x12and0x68when powered.- Running Python/imagesd.py updates display and logs a voltage-tagged request at CGI host.
- After timeout, Pi halts and Arduino cuts power after delay.
- RTC alarm or side button wakes and repeats cycle.
