https://github.com/drewmcdonald/flipdot
Driver and UI for Alfa-Zeta xy5 flip-dot displays
https://github.com/drewmcdonald/flipdot
flipdiscs flipdot
Last synced: 4 months ago
JSON representation
Driver and UI for Alfa-Zeta xy5 flip-dot displays
- Host: GitHub
- URL: https://github.com/drewmcdonald/flipdot
- Owner: drewmcdonald
- License: mit
- Created: 2025-02-12T02:24:14.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-11-21T22:18:13.000Z (7 months ago)
- Last Synced: 2025-11-21T23:23:10.546Z (7 months ago)
- Topics: flipdiscs, flipdot
- Language: Python
- Homepage:
- Size: 480 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# FlipDot Driver v2.0
A lightweight driver for flipdot displays that fetches pre-rendered content from a remote server.
## Architecture Overview
The new architecture separates the **driver** (runs on Raspberry Pi) from the **content server** (runs anywhere). This separation allows:
- Faster startup times on the Pi (no NumPy, minimal dependencies)
- Heavy lifting (rendering, fonts, animations) happens on a powerful server
- Easier development and testing
- Better scalability
```
┌─────────────────────────────────┐
│ CONTENT SERVER (anywhere) │
│ - Render frames │
│ - Generate animations │
│ - Return structured JSON │
└────────────┬────────────────────┘
│ HTTP/JSON
▼
┌─────────────────────────────────┐
│ DRIVER (Raspberry Pi) │
│ - Poll for content │
│ - Accept push notifications │
│ - Manage frame queue │
│ - Send to hardware │
└────────────┬────────────────────┘
│ Serial
▼
┌─────────────────────────────────┐
│ FLIPDOT HARDWARE │
└─────────────────────────────────┘
```
## Quick Start
### 1. Install Dependencies
The driver has minimal dependencies:
```bash
pip install pydantic pyserial
```
### 2. Create Configuration
Copy the example configuration:
```bash
cp config.example.json config.json
```
Edit `config.json` with your settings:
```json
{
"poll_endpoint": "https://your-server.com/api/flipdot/content",
"auth": {
"type": "api_key",
"key": "your-secret-key"
},
"serial_device": "/dev/ttyUSB0",
"module_layout": [[1], [2]]
}
```
### 3. Run the Driver
```bash
python -m flipdot.driver.main --config config.json
```
For development (no hardware):
```bash
python -m flipdot.driver.main --config config.dev.json
```
## Data Structures
### Frame
A single image to display:
```json
{
"data_b64": "AQIDBAUGBwgJ...",
"width": 56,
"height": 14,
"duration_ms": 1000
}
```
- `data_b64`: Base64-encoded packed bits (little-endian)
- `width`, `height`: Dimensions in pixels
- `duration_ms`: How long to display (null = indefinite)
### Content
A sequence of frames with playback instructions:
```json
{
"content_id": "clock-12:00",
"frames": [
/* array of Frame objects */
],
"playback": {
"loop": true,
"loop_count": null,
"priority": 0,
"interruptible": true
}
}
```
- `content_id`: Unique identifier
- `frames`: Array of Frame objects
- `playback.loop`: Whether to loop frames
- `playback.loop_count`: How many times to loop (null = infinite)
- `playback.priority`: Priority level (0=normal, 10=notification, 99=urgent)
- `playback.interruptible`: Can be interrupted by higher priority?
### ContentResponse
What the server returns:
```json
{
"status": "updated",
"content": {
/* Content object */
},
"poll_interval_ms": 30000
}
```
- `status`: "updated", "no_change", or "clear"
- `content`: Content object (only if status="updated")
- `poll_interval_ms`: How long to wait before next poll
## Server API
The driver expects a server endpoint that returns `ContentResponse` JSON.
### Polling Endpoint
**GET** `/api/flipdot/content`
Returns the current content to display.
**Headers:**
- `X-API-Key: your-secret-key` (or `Authorization: Bearer token`)
**Response:**
```json
{
"status": "updated",
"content": {
"content_id": "clock-12:00",
"frames": [...],
"playback": {...}
},
"poll_interval_ms": 30000
}
```
### Push Notifications (Optional)
If `enable_push: true` in config, the driver runs a simple HTTP server:
**POST** `http://pi-address:8080/`
Push high-priority content immediately.
**Headers:**
- `X-API-Key: your-secret-key`
- `Content-Type: application/json`
**Body:**
```json
{
"content_id": "notification",
"frames": [...],
"playback": {
"priority": 10,
"interruptible": false
}
}
```
## Configuration Reference
```json
{
// Server settings
"poll_endpoint": "https://example.com/api/content",
"poll_interval_ms": 30000,
// Push server (optional)
"enable_push": false,
"push_port": 8080,
"push_host": "0.0.0.0",
// Authentication
"auth": {
"type": "api_key", // or "bearer"
"key": "secret-key",
"header_name": "X-API-Key"
},
// Hardware
"serial_device": "/dev/ttyUSB0",
"serial_baudrate": 57600,
"module_layout": [[1], [2]],
"module_width": 28,
"module_height": 7,
// Behavior
"error_fallback": "keep_last", // "keep_last", "blank", or "error_message"
"dev_mode": false,
"log_level": "INFO"
}
```
## Content Queue & Priorities
The driver maintains a priority queue:
- **Priority 0-9**: Normal content (clock, weather, etc.)
- **Priority 10-98**: Notifications
- **Priority 99**: Urgent alerts
Higher priority content **interrupts** lower priority if marked as `interruptible`.
Example flow:
1. Clock is displaying (priority 0)
2. Notification arrives (priority 10)
3. Clock pauses, notification plays
4. Notification completes
5. Clock resumes from where it left off
## Generating Content
See `examples/generate_content.py` for examples of creating frames:
```python
from flipdot.hardware import pack_bits_little_endian
import base64
# Create a 2x2 frame
bits = [1, 0, 1, 0] # Row-major order
packed = pack_bits_little_endian(bits)
b64 = base64.b64encode(packed).decode()
frame = {
"data_b64": b64,
"width": 2,
"height": 2,
"duration_ms": 1000
}
```
## Testing
Run tests:
```bash
pytest tests/test_driver.py
```
Generate example content:
```bash
cd examples
python generate_content.py
```
## Development Mode
Use `dev_mode: true` to test without hardware:
```bash
python -m flipdot.driver.main --config config.dev.json
```
The driver will print serial data to the console instead of sending to hardware.
## Migration from v1.0
**Key changes:**
1. **Driver is now minimal**: Only handles display logic, no rendering
2. **NumPy removed**: Faster startup on Pi
3. **Server provides frames**: All rendering happens server-side
4. **New data format**: Base64-encoded packed bits instead of live rendering
5. **Priority queue**: Better support for notifications
**What was removed from the Pi:**
- FastAPI web server
- React frontend
- Display modes (Clock, ScrollText, Weather, etc.)
- NumPy dependency
**What moved to the content server:**
- All rendering logic
- Font handling
- Animation generation
- Display mode implementations
## Troubleshooting
### Driver can't connect to server
Check:
- `poll_endpoint` is correct
- Authentication credentials match
- Server is running and accessible
### Serial device not found
Check:
- Device path is correct (`/dev/ttyUSB0`, `/dev/ttyACM0`, etc.)
- User has permission to access serial port
- Run: `sudo usermod -a -G dialout $USER`
### Frames not displaying
Check:
- Frame dimensions match display dimensions
- `data_b64` is valid base64
- Driver logs for errors (`log_level: "DEBUG"`)
## Next Steps
This is Phase 1 and 2 of the refactor. Still TODO:
- **Phase 3**: Build the content server
- Migrate existing mode logic
- Create API endpoints
- Handle rendering server-side
- **Phase 4**: Advanced features
- Content caching
- Compression
- Transition effects