Initial project setup: Cybersyn Chair recreation

- Comprehensive README with historical background, specs, and manufacturing options
- Project directory structure for CAD, electronics, software, manufacturing
- Initial Raspberry Pi firmware for button control with MQTT integration
- References document with sources, archives, and CAD starting points
- Support for modular armrest expansion (OLED, NFC, haptic feedback)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-29 20:55:50 +01:00
commit 047800f18d
17 changed files with 1031 additions and 0 deletions

44
.gitignore vendored Normal file
View File

@ -0,0 +1,44 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
ENV/
.env
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# CAD temporary files
*.bak
*.tmp
*.dwl
*.dwl2
~$*
# Build artifacts
build/
dist/
*.egg-info/
# OS files
.DS_Store
Thumbs.db
# Large binary files (use Git LFS for these)
*.stl
*.step
*.stp
*.iges
*.igs
*.3mf
*.gcode
# Keep empty directories
!.gitkeep

387
README.md Normal file
View File

@ -0,0 +1,387 @@
# Cybersyn Chair Recreation Project
A modern recreation of the iconic Cybersyn Operations Room chairs from Chile's Project Cybersyn (1971-1973), with integrated Raspberry Pi electronics for smart home/office control.
![Cybersyn Opsroom](https://upload.wikimedia.org/wikipedia/commons/1/1e/Cybersyn_control_room.jpg)
## Project Goals
1. **Faithful Recreation**: Reproduce the aesthetic of Gui Bonsiepe's original fiberglass swivel chairs
2. **Modern Electronics**: Integrate Raspberry Pi with customizable armrest controls
3. **Modular Design**: Create swappable armrest modules for different use cases
4. **Open Source**: Publish all CAD files, electronics schematics, and software
---
## Historical Background
### The Original Cybersyn Project (1971-1973)
Project Cybersyn was Chile's pioneering experiment in real-time economic management under President Salvador Allende. The Operations Room (Opsroom) was the nerve center - a hexagonal 72m² space containing 7 fiberglass swivel chairs arranged in a circle.
**Design Team:**
- **Gui Bonsiepe** (Lead Designer, German industrial designer)
- Fernando Shultz
- Rodrigo Walker
- Pepa Foncea
- Industrial Design Area of INTEC (Chilean State Technology Institute)
### Original Chair Specifications
| Feature | Description |
|---------|-------------|
| **Material** | Fiberglass shell with orange upholstery |
| **Style** | Tulip/pedestal base (similar to Saarinen, but custom) |
| **Seating** | 7 chairs in inward-facing circle |
| **Swivel** | Full 360° rotation (considered optimal for creativity) |
| **Armrests** | Integrated control panels, ashtrays, drink holders |
### Armrest Control Layout
The original buttons were deliberately large ("big hand" design) for users without keyboard experience:
```
┌─────────────────────────────────────┐
│ ORIGINAL BUTTON LAYOUT │
├─────────────────────────────────────┤
│ │
│ TOP ROW (3 square buttons): │
│ [■] [■] [■] │
│ │ │ │ │
│ └───┴───┴── Select data screens │
│ │
│ MIDDLE ROW (5 buttons): │
│ [○] [○] [○] [○] [○] │
│ │ │
│ └── Navigate subdirectories │
│ (hypertext-like navigation) │
│ │
│ BOTTOM ROW (1 large rectangular): │
│ [████████████████████] │
│ │ │
│ └── Index/Home screen │
│ │
└─────────────────────────────────────┘
```
The buttons connected via wires through the floor to slide carousels that displayed pre-made data visualization slides.
### Design Philosophy
- **No tables**: Intentionally omitted to prevent paper shuffling and encourage democratic discussion
- **Odd number (7)**: Ensures tie-breaking votes
- **Swivel chairs**: Maximizes creative interaction
- **No keyboards**: Large buttons for accessibility
- **No Star Trek influence**: Despite visual similarities, designers claimed no sci-fi inspiration
---
## Existing Resources & References
### Reconstructions
| Location | Year | Notes |
|----------|------|-------|
| FabLab Santiago, Chile | 2016 | Recreation supervised by original designers |
| Disseny Hub Barcelona, Spain | 2023 | First *functional* reconstruction, 72m² hexagonal room |
### Primary Sources
- **Eden Medina, *Cybernetic Revolutionaries* (MIT Press, 2011)** - Definitive historical account with design details
- **Stafford Beer Collection** - Liverpool John Moores University archives
- **INTEC Publication**: "Diseño de una sala de operaciones," INTEC, no. 4 (1973), pp. 1928
- **Gui Bonsiepe Archive** - Original sketches
### Available 3D Models (Starting Points)
| Model | Source | Format | Notes |
|-------|--------|--------|-------|
| Tulip Chair | [GrabCAD](https://grabcad.com/library/tulip-chair-1) | Various | Saarinen-style, needs armrest mods |
| Saarinen Tulip | [Sketchfab](https://sketchfab.com/3d-models/saarinen-tulip-chair-bc48eb1d27794a7baa8b2009aff5590e) | Free | Good base geometry |
| Knoll Tulip | [FaceQuad](https://facequad.com/products/knoll-tulip-chair-and-armchair-3d-model) | OBJ, FBX | Armchair variant |
| Eames Shell | [Herman Miller](https://www.hermanmiller.com/resources/3d-models-and-planning-tools/product-models/individual/eames-molded-fiberglass-side-chair-dowel-base-nonupholstered/) | Revit, SketchUp, AutoCAD | Fiberglass shell reference |
### Reference Images
- [Google Arts & Culture - FabLab Recreation](https://artsandculture.google.com/asset/the-counterculture-room-cybersyn-chair-gui-bonsiepe-recreated-by-fablab-santiago-in-2016/vQHnhWYDvfxj6Q)
- [Artsy - Original Opsroom Photos](https://www.artsy.net/artwork/gui-bonsiepe-cybersyn-operations-room-datafeed-with-chairs)
- [99% Invisible Episode](https://99percentinvisible.org/episode/project-cybersyn/) - Includes photos and context
---
## Manufacturing Approaches
### Option 1: Fiberglass Hand Lay-Up (Recommended for Authenticity)
**Estimated Cost**: $800-2,000/chair + mold
**Tooling Cost**: $1,000-5,000 for plug and mold
**Process:**
1. Create positive plug from CNC-milled foam or MDF
2. Apply release agent and gel coat
3. Lay fiberglass mat + polyester/epoxy resin
4. Cure under controlled conditions
5. Demold and finish
**Resources:**
- [Instructables: Fiberglass Shell Chair](https://www.instructables.com/DEVELOPING-FORM-Fabricating-Organic-Fiberglass-For/)
- [Hand Lay-Up Process Guide](https://www.deloachindustries.com/blog/fiberglass-process-hand-lay-up-contact-molding-)
### Option 2: Rotational Molding (Best for Small Batches)
**Estimated Cost**: $50-150/chair at 50+ units
**Tooling Cost**: $3,000-10,000 for aluminum mold
**Advantages:**
- Tooling costs 1/5th of injection molding
- Creates seamless hollow shells (perfect for hiding electronics)
- Ideal for 50-500 unit production runs
**Resources:**
- [Rotomolding vs Injection Molding Comparison](https://rotodynamics.com/injection-molding-advantages-disadvantages-and-the-appeal-of-rotational-molding/)
### Option 3: Hybrid Approach
| Component | Method | Material |
|-----------|--------|----------|
| Shell/seat | Fiberglass hand lay-up OR rotomolding | Fiberglass or LLDPE |
| Armrests | 3D printed master → silicone mold → cast | Polyurethane resin |
| Base/pedestal | CNC machined or cast | Aluminum or steel |
| Electronics bay | 3D printed inserts | PETG/ABS |
---
## Electronics Integration
### Architecture Overview
```
┌─────────────────────────────────────────────────────────────┐
│ ARMREST MODULE │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ BUTTON PANEL (Top Surface) │ │
│ │ │ │
│ │ [DATA 1] [DATA 2] [DATA 3] ← Screen selection │ │
│ │ │ │
│ │ [NAV 1] [NAV 2] [NAV 3] [NAV 4] [NAV 5] │ │
│ │ ↑ │ │
│ │ Navigation/subdirectory buttons │ │
│ │ │ │
│ │ [═══════════ INDEX/HOME ═══════════] │ │
│ │ ↑ │ │
│ │ Large "big hand" button │ │
│ │ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────┴──────────────────────────────┐ │
│ │ ELECTRONICS CAVITY │ │
│ │ │ │
│ │ ┌─────────────┐ ┌────────────────────────────┐ │ │
│ │ │ Raspberry │ │ GPIO Breakout Board │ │ │
│ │ │ Pi Zero 2 W │────│ + Button Matrix Driver │ │ │
│ │ │ │ │ + Status LEDs │ │ │
│ │ └──────┬──────┘ └─────────────┬──────────────┘ │ │
│ │ │ │ │ │
│ │ ┌──────┴─────────────────────────┴──────────────┐ │ │
│ │ │ USB-C Power + Data (through pedestal) │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ MODULAR EXPANSION BAYS (snap-in): │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ NFC │ │ OLED │ │ Haptic │ │ USB │ │ │
│ │ │ Reader │ │ Display │ │ Motors │ │ Ports │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Recommended Hardware
| Component | Model | Dimensions | Cost | Notes |
|-----------|-------|------------|------|-------|
| **Main Board** | Raspberry Pi Zero 2 W | 65 × 30 mm | ~$15 | WiFi/BT, sufficient GPIO |
| **Alternative** | Raspberry Pi Pico W | 51 × 21 mm | ~$6 | Even smaller, pure button I/O |
| **Buttons** | Cherry MX mechanical | 15.6 × 15.6 mm | ~$1 ea | Satisfying tactile feel |
| **Alt Buttons** | Silicone dome pads | Custom | ~$20/set | Authentic "big hand" style |
| **Display** | 1.3" SH1106 OLED | 35 × 33 mm | ~$8 | Optional status display |
| **NFC** | RC522 RFID module | 40 × 60 mm | ~$5 | User identification |
| **Haptic** | DRV2605L + LRA motor | 10 × 10 mm | ~$8 | Button feedback |
### Software Stack
```
┌─────────────────────────────────────────────────────────┐
│ SOFTWARE LAYERS │
├─────────────────────────────────────────────────────────┤
│ │
│ APPLICATION LAYER │
│ ├── Home Assistant integration (MQTT) │
│ ├── Custom Cybersyn dashboard (web UI) │
│ └── Multi-chair coordination (WebSocket) │
│ │
│ MIDDLEWARE │
│ ├── Python asyncio event loop │
│ ├── Button debouncing & state machine │
│ └── Module hot-plug detection │
│ │
│ HARDWARE ABSTRACTION │
│ ├── gpiozero (button/LED control) │
│ ├── smbus2 (I2C modules) │
│ └── spidev (SPI displays) │
│ │
│ OS: Raspberry Pi OS Lite (headless) │
│ │
└─────────────────────────────────────────────────────────┘
```
### Communication Protocol
**MQTT Topics:**
```
cybersyn/chair/{chair_id}/button/{button_name} → pressed/released
cybersyn/chair/{chair_id}/module/{module_type} → module data
cybersyn/chair/{chair_id}/status → online/offline
cybersyn/display/{screen_id}/show → screen control
```
---
## Project Phases
### Phase 1: Research & Design (Current)
- [x] Historical research on original design
- [x] Identify existing CAD resources
- [x] Document manufacturing options
- [ ] Obtain high-resolution reference photos
- [ ] Contact FabLab Santiago / Disseny Hub Barcelona for specs
- [ ] Create initial CAD model
### Phase 2: Prototype Electronics
- [ ] Build button matrix on breadboard
- [ ] Write Pi Zero control software
- [ ] Test MQTT integration with Home Assistant
- [ ] Design modular bay connector system
- [ ] 3D print armrest electronics enclosure
### Phase 3: Physical Prototype
- [ ] Modify existing tulip chair CAD for armrest cavity
- [ ] 3D print 1:4 scale model for review
- [ ] Create foam/MDF plug for fiberglass testing
- [ ] Make test fiberglass section
- [ ] Integrate electronics into armrest
### Phase 4: Production
- [ ] Finalize CAD for manufacturing
- [ ] Create production mold (fiberglass or rotomold)
- [ ] Produce first complete chair
- [ ] Document build process
- [ ] Publish open-source files
### Phase 5: Multi-Chair System
- [ ] Build 7-chair Opsroom configuration
- [ ] Create central display system
- [ ] Implement cross-chair coordination
- [ ] Add data visualization dashboard
---
## Directory Structure
```
cybersyn-chair/
├── README.md # This file
├── docs/
│ ├── history.md # Detailed historical background
│ ├── references.md # Links to sources and archives
│ └── manufacturing.md # Detailed manufacturing guides
├── cad/
│ ├── chair-shell/ # Main chair body CAD files
│ ├── armrest/ # Armrest with electronics cavity
│ ├── pedestal-base/ # Swivel base design
│ └── modules/ # Snap-in module designs
├── electronics/
│ ├── schematics/ # KiCad circuit designs
│ ├── pcb/ # Custom PCB layouts
│ └── bom/ # Bill of materials
├── software/
│ ├── firmware/ # Pi Zero control code
│ ├── server/ # Central coordination server
│ └── dashboard/ # Web-based control panel
├── manufacturing/
│ ├── mold-designs/ # Fiberglass mold CAD
│ └── assembly-guides/ # Step-by-step build docs
└── media/
├── reference-photos/ # Historical images
└── renders/ # 3D renders of design
```
---
## Cost Estimates
### Single Chair (DIY/Maker)
| Item | Estimated Cost |
|------|----------------|
| Fiberglass materials + mold (amortized over 7) | $200-400 |
| Metal pedestal base (fabricated) | $200-400 |
| Upholstery (orange fabric + foam) | $100-200 |
| Raspberry Pi Zero 2 W | $15 |
| Electronics (buttons, wiring, modules) | $50-100 |
| 3D printed components | $20-50 |
| **Total per chair** | **$585-1,165** |
### 7-Chair Opsroom Setup
| Item | Estimated Cost |
|------|----------------|
| 7 chairs (at $800 avg) | $5,600 |
| Central server (Pi 4 + display) | $150 |
| Network infrastructure | $100 |
| Display screens (7× monitors) | $1,400 |
| Room setup (hexagonal layout, wiring) | $500 |
| **Total Opsroom** | **$7,750** |
---
## Contributing
This is an open-source project. Contributions welcome:
1. **CAD Design**: Help model the chair shell, armrest, and base
2. **Electronics**: Design PCBs, write firmware
3. **Manufacturing**: Share fabrication experience
4. **Historical Research**: Locate original specifications and drawings
5. **Documentation**: Improve guides and tutorials
---
## License
- **Hardware designs**: CERN Open Hardware License v2
- **Software**: MIT License
- **Documentation**: CC BY-SA 4.0
---
## Acknowledgments
- **Gui Bonsiepe** and the original INTEC design team
- **Stafford Beer** for the Cybersyn vision
- **Eden Medina** for preserving this history in *Cybernetic Revolutionaries*
- **FabLab Santiago** for the 2016 recreation
- **Disseny Hub Barcelona** for the functional reconstruction
---
## Contact
For questions, collaboration, or access to original specifications:
- Open an issue on this repository
- Email: [your-email]
- Disseny Hub Barcelona: [Documentation Center](https://www.dissenyhub.barcelona/en/centredoc/services/information-and-requests)

0
cad/armrest/.gitkeep Normal file
View File

0
cad/chair-shell/.gitkeep Normal file
View File

0
cad/modules/.gitkeep Normal file
View File

View File

163
docs/references.md Normal file
View File

@ -0,0 +1,163 @@
# Cybersyn Chair - References & Resources
## Primary Historical Sources
### Books & Academic Papers
| Source | Description | Access |
|--------|-------------|--------|
| **Eden Medina, *Cybernetic Revolutionaries* (MIT Press, 2011)** | Definitive academic history with design details, armrest button layouts, and archival photos | [MIT Press](https://mitpress.mit.edu/9780262525961/cybernetic-revolutionaries/) / [PDF](https://uberty.org/wp-content/uploads/2015/10/Eden_Medina_Cybernetic_Revolutionaries.pdf) |
| **"Diseño de una sala de operaciones," INTEC, no. 4 (1973), pp. 1928** | Original Chilean technical documentation of the Opsroom design | Archives (contact institutions below) |
### Archives
| Archive | Contents | Contact |
|---------|----------|---------|
| **Stafford Beer Collection** | Original correspondence, technical drawings, project documents | Liverpool John Moores University, Liverpool, UK |
| **Gui Bonsiepe Archive** | Sketches, design drawings, photographs | Contact via [guibonsiepe.com](http://www.guibonsiepe.com/) |
| **INTEC Archives** | Chilean State Technology Institute records | Chile National Archives |
### Key Interviews & Contacts
- **Gui Bonsiepe** - Lead designer, interviewed by Eden Medina (May 2008, La Plata, Argentina)
- **Hugo Palmarola** - Curator of DHub exhibition, Chilean design historian
- **Eden Medina** - MIT Professor, author, [edenmedina.mit.edu](https://edenmedina.mit.edu/)
---
## Reconstructions
### FabLab Santiago (2016)
- **Location**: Santiago, Chile
- **Supervised by**: Original designers including Gui Bonsiepe
- **Contact**: [FabLabs.io Profile](https://www.fablabs.io/labs/fablabscl)
- **Note**: Website fablabsantiago.org currently inactive
### Disseny Hub Barcelona (2023)
- **Location**: Barcelona, Spain
- **Exhibition**: "How to Design a Revolution: The Chilean Road to Design"
- **Details**: First *functional* reconstruction - 72m² hexagonal room with working controls
- **Curators**: Hugo Palmarola, Eden Medina, Pedro Ignacio Alonso
- **Contact**: [Documentation Center](https://www.dissenyhub.barcelona/en/centredoc/services/information-and-requests)
- **Exhibition Page**: [Cybersyn in Action](https://www.dissenyhub.barcelona/en/activity/cybersyn-action)
---
## Visual References
### High-Quality Photos
| Source | Description |
|--------|-------------|
| [Google Arts & Culture - FabLab Chair](https://artsandculture.google.com/asset/the-counterculture-room-cybersyn-chair-gui-bonsiepe-recreated-by-fablab-santiago-in-2016/vQHnhWYDvfxj6Q) | FabLab Santiago 2016 recreation, high-res |
| [Artsy - Original Opsroom](https://www.artsy.net/artwork/gui-bonsiepe-cybersyn-operations-room-datafeed-with-chairs) | Cantor Fitzgerald Gallery collection |
| [Wikipedia - Control Room](https://en.wikipedia.org/wiki/Project_Cybersyn#/media/File:Cybersyn_control_room.jpg) | Classic operations room photo |
### Video Resources
| Source | Description |
|--------|-------------|
| [99% Invisible Podcast](https://99percentinvisible.org/episode/project-cybersyn/) | Episode with photos and context |
| [MIT News - Designing a Revolution](https://news.mit.edu/2023/designing-revolution-1002) | Coverage of Barcelona exhibition |
---
## CAD Resources (Starting Points)
### Tulip/Pedestal Chair Models
| Name | Source | Formats | Notes |
|------|--------|---------|-------|
| Tulip Chair | [GrabCAD](https://grabcad.com/library/tulip-chair-1) | Various | Saarinen-style, modify for armrests |
| Saarinen Tulip | [Sketchfab](https://sketchfab.com/3d-models/saarinen-tulip-chair-bc48eb1d27794a7baa8b2009aff5590e) | Free download | Good base geometry |
| Knoll Tulip Armchair | [FaceQuad](https://facequad.com/products/knoll-tulip-chair-and-armchair-3d-model) | OBJ, FBX, 3DS | Armchair variant with armrests |
| Tulip Dining Set | [CadNav](https://www.cadnav.com/3d/tulip-chair.html) | Multiple | Several variations |
| Open3dModel Collection | [Open3dModel](https://open3dmodel.com/3d-models/tulip-chair) | Blend, Max, OBJ, etc. | 13 tulip chair models |
### Fiberglass Shell References
| Name | Source | Formats | Notes |
|------|--------|---------|-------|
| Eames Fiberglass Chair | [Herman Miller](https://www.hermanmiller.com/resources/3d-models-and-planning-tools/product-models/individual/eames-molded-fiberglass-side-chair-dowel-base-nonupholstered/) | Revit, SketchUp, AutoCAD | Official models for reference |
| Eames Collection | [GrabCAD](https://grabcad.com/library/tag/eames) | Various | Community-created models |
---
## Manufacturing Resources
### Fiberglass Fabrication
| Resource | Description |
|----------|-------------|
| [Instructables: Fiberglass Shell Chair](https://www.instructables.com/DEVELOPING-FORM-Fabricating-Organic-Fiberglass-For/) | Complete DIY guide with mold-making |
| [Hand Lay-Up Process](https://www.deloachindustries.com/blog/fiberglass-process-hand-lay-up-contact-molding-) | Industrial process overview |
| [Fiberglass Supply](https://www.fiberglasssupply.com/) | Materials supplier (US) |
| [US Composites](https://www.uscomposites.com/) | Resins, fabrics, tools |
### Rotational Molding
| Resource | Description |
|----------|-------------|
| [Roto Dynamics Comparison](https://rotodynamics.com/injection-molding-advantages-disadvantages-and-the-appeal-of-rotational-molding/) | Rotomolding vs injection molding |
| [Treatstock Guide](https://www.treatstock.com/guide/article/138-rotomolding-vs-injection-molding) | Cost comparison for small batches |
### Metal Fabrication (Pedestal Base)
| Resource | Description |
|----------|-------------|
| [SendCutSend](https://sendcutsend.com/) | Laser cutting, bending, welding |
| [Xometry](https://www.xometry.com/) | On-demand CNC and sheet metal |
| [Protolabs](https://www.protolabs.com/) | Rapid manufacturing services |
---
## Electronics & Software
### Hardware
| Component | Recommended Part | Datasheet/Guide |
|-----------|------------------|-----------------|
| Raspberry Pi Zero 2 W | Official | [RPi Docs](https://www.raspberrypi.com/documentation/) |
| Cherry MX Switches | Various colors | [Cherry MX Guide](https://www.cherrymx.de/en/cherry-mx.html) |
| OLED Display (SH1106) | 1.3" I2C | Common modules |
| RC522 NFC Reader | MFRC522 | [Datasheet](https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf) |
### Software Libraries
| Library | Purpose | Install |
|---------|---------|---------|
| gpiozero | GPIO control | `pip install gpiozero` |
| paho-mqtt | MQTT client | `pip install paho-mqtt` |
| luma.oled | OLED display | `pip install luma.oled` |
| mfrc522 | NFC reader | `pip install mfrc522` |
### Home Automation
| Platform | Integration |
|----------|-------------|
| [Home Assistant](https://www.home-assistant.io/) | MQTT integration for buttons/displays |
| [Node-RED](https://nodered.org/) | Visual workflow automation |
---
## Related Projects
| Project | Description | Link |
|---------|-------------|------|
| Cybersyn Poster | Boot Boyz Biz merchandise | [Shop](https://boot-boyz.biz/products/cybersyn) |
| Eames Institute | Shell chair history | [Collection](https://www.eamesinstitute.org/collection/form-follows-formulation/) |
| Open Desk | Open-source furniture designs | [opendesk.cc](https://www.opendesk.cc/) |
| WikiHouse | Open-source building system | [wikihouse.cc](https://www.wikihouse.cc/) |
---
## Action Items for Research
1. [ ] Contact Disseny Hub Barcelona documentation center for technical specs
2. [ ] Request access to Stafford Beer Collection at Liverpool John Moores
3. [ ] Attempt to reach FabLab Santiago via fablabs.io for 2016 reconstruction files
4. [ ] Search Internet Archive for fablabsantiago.org snapshots
5. [ ] Locate copy of INTEC no. 4 (1973) article on Opsroom design
6. [ ] Email Gui Bonsiepe for any available design documentation

0
electronics/bom/.gitkeep Normal file
View File

0
electronics/pcb/.gitkeep Normal file
View File

View File

View File

View File

View File

0
media/renders/.gitkeep Normal file
View File

View File

View File

@ -0,0 +1,437 @@
#!/usr/bin/env python3
"""
Cybersyn Chair Controller
=========================
Raspberry Pi Zero 2 W firmware for armrest button control.
Hardware:
- 9 buttons in original Cybersyn layout
- Optional: OLED display, NFC reader, haptic feedback
- MQTT for Home Assistant integration
"""
import asyncio
import json
from dataclasses import dataclass
from enum import Enum
from typing import Callable, Optional
# Hardware abstraction - use gpiozero on actual Pi
try:
from gpiozero import Button, LED
HARDWARE_AVAILABLE = True
except ImportError:
HARDWARE_AVAILABLE = False
print("Running in simulation mode (no GPIO)")
# MQTT client
try:
import paho.mqtt.client as mqtt
MQTT_AVAILABLE = True
except ImportError:
MQTT_AVAILABLE = False
print("MQTT not available - install paho-mqtt")
class ButtonName(Enum):
"""Original Cybersyn button layout."""
# Top row - data screen selection
DATA_1 = "data_1"
DATA_2 = "data_2"
DATA_3 = "data_3"
# Middle row - navigation
NAV_1 = "nav_1"
NAV_2 = "nav_2"
NAV_3 = "nav_3"
NAV_4 = "nav_4"
NAV_5 = "nav_5"
# Bottom row - index/home
INDEX = "index"
@dataclass
class ButtonConfig:
"""GPIO pin configuration for a button."""
name: ButtonName
gpio_pin: int
led_pin: Optional[int] = None
# Default GPIO pin mapping (adjust for your wiring)
DEFAULT_BUTTON_CONFIG = [
ButtonConfig(ButtonName.DATA_1, gpio_pin=17, led_pin=27),
ButtonConfig(ButtonName.DATA_2, gpio_pin=22, led_pin=10),
ButtonConfig(ButtonName.DATA_3, gpio_pin=23, led_pin=9),
ButtonConfig(ButtonName.NAV_1, gpio_pin=24),
ButtonConfig(ButtonName.NAV_2, gpio_pin=25),
ButtonConfig(ButtonName.NAV_3, gpio_pin=8),
ButtonConfig(ButtonName.NAV_4, gpio_pin=7),
ButtonConfig(ButtonName.NAV_5, gpio_pin=12),
ButtonConfig(ButtonName.INDEX, gpio_pin=16, led_pin=20),
]
class CybersynController:
"""Main controller for a Cybersyn chair."""
def __init__(
self,
chair_id: str = "chair_1",
mqtt_broker: str = "localhost",
mqtt_port: int = 1883,
button_config: list[ButtonConfig] = None,
):
self.chair_id = chair_id
self.mqtt_broker = mqtt_broker
self.mqtt_port = mqtt_port
self.button_config = button_config or DEFAULT_BUTTON_CONFIG
self.buttons: dict[ButtonName, Button] = {}
self.leds: dict[ButtonName, LED] = {}
self.mqtt_client: Optional[mqtt.Client] = None
self._callbacks: dict[ButtonName, list[Callable]] = {
btn.name: [] for btn in self.button_config
}
def setup_hardware(self):
"""Initialize GPIO buttons and LEDs."""
if not HARDWARE_AVAILABLE:
print(f"[{self.chair_id}] Simulating hardware setup")
return
for config in self.button_config:
# Setup button with pull-up resistor
self.buttons[config.name] = Button(
config.gpio_pin,
pull_up=True,
bounce_time=0.05 # 50ms debounce
)
self.buttons[config.name].when_pressed = (
lambda name=config.name: self._on_button_pressed(name)
)
self.buttons[config.name].when_released = (
lambda name=config.name: self._on_button_released(name)
)
# Setup LED if configured
if config.led_pin:
self.leds[config.name] = LED(config.led_pin)
print(f"[{self.chair_id}] Hardware initialized: {len(self.buttons)} buttons")
def setup_mqtt(self):
"""Connect to MQTT broker."""
if not MQTT_AVAILABLE:
print(f"[{self.chair_id}] MQTT not available")
return
self.mqtt_client = mqtt.Client(client_id=f"cybersyn_{self.chair_id}")
self.mqtt_client.on_connect = self._on_mqtt_connect
self.mqtt_client.on_message = self._on_mqtt_message
try:
self.mqtt_client.connect(self.mqtt_broker, self.mqtt_port)
self.mqtt_client.loop_start()
print(f"[{self.chair_id}] Connected to MQTT broker at {self.mqtt_broker}")
except Exception as e:
print(f"[{self.chair_id}] MQTT connection failed: {e}")
def _on_mqtt_connect(self, client, userdata, flags, rc):
"""Handle MQTT connection."""
# Subscribe to commands for this chair
topic = f"cybersyn/chair/{self.chair_id}/command/#"
client.subscribe(topic)
# Publish online status
self._publish_status("online")
def _on_mqtt_message(self, client, userdata, msg):
"""Handle incoming MQTT messages."""
try:
payload = json.loads(msg.payload.decode())
# Handle LED control commands
if "/led/" in msg.topic:
button_name = msg.topic.split("/")[-1]
self._set_led(ButtonName(button_name), payload.get("state", False))
except Exception as e:
print(f"[{self.chair_id}] Error processing message: {e}")
def _on_button_pressed(self, button_name: ButtonName):
"""Handle button press event."""
print(f"[{self.chair_id}] Button pressed: {button_name.value}")
# Publish to MQTT
self._publish_button_event(button_name, "pressed")
# Trigger registered callbacks
for callback in self._callbacks.get(button_name, []):
callback(button_name, "pressed")
# Visual feedback
if button_name in self.leds:
self.leds[button_name].on()
def _on_button_released(self, button_name: ButtonName):
"""Handle button release event."""
print(f"[{self.chair_id}] Button released: {button_name.value}")
# Publish to MQTT
self._publish_button_event(button_name, "released")
# Trigger registered callbacks
for callback in self._callbacks.get(button_name, []):
callback(button_name, "released")
# Turn off LED feedback
if button_name in self.leds:
self.leds[button_name].off()
def _publish_button_event(self, button_name: ButtonName, state: str):
"""Publish button event to MQTT."""
if not self.mqtt_client:
return
topic = f"cybersyn/chair/{self.chair_id}/button/{button_name.value}"
payload = json.dumps({
"state": state,
"chair_id": self.chair_id,
"button": button_name.value,
})
self.mqtt_client.publish(topic, payload)
def _publish_status(self, status: str):
"""Publish chair status to MQTT."""
if not self.mqtt_client:
return
topic = f"cybersyn/chair/{self.chair_id}/status"
payload = json.dumps({
"status": status,
"chair_id": self.chair_id,
})
self.mqtt_client.publish(topic, payload, retain=True)
def _set_led(self, button_name: ButtonName, state: bool):
"""Set LED state."""
if button_name in self.leds:
if state:
self.leds[button_name].on()
else:
self.leds[button_name].off()
def register_callback(
self,
button_name: ButtonName,
callback: Callable[[ButtonName, str], None]
):
"""Register a callback for button events."""
self._callbacks[button_name].append(callback)
def run(self):
"""Run the controller main loop."""
self.setup_hardware()
self.setup_mqtt()
print(f"[{self.chair_id}] Controller running. Press Ctrl+C to exit.")
try:
# Keep running
while True:
asyncio.get_event_loop().run_until_complete(asyncio.sleep(1))
except KeyboardInterrupt:
print(f"\n[{self.chair_id}] Shutting down...")
self._publish_status("offline")
if self.mqtt_client:
self.mqtt_client.loop_stop()
# =============================================================================
# Modular Expansion System
# =============================================================================
class ArmrestModule:
"""Base class for armrest expansion modules."""
MODULE_TYPE = "base"
def __init__(self, controller: CybersynController):
self.controller = controller
self.enabled = False
def setup(self):
"""Initialize the module hardware."""
raise NotImplementedError
def poll(self) -> dict:
"""Read current module state."""
raise NotImplementedError
def cleanup(self):
"""Cleanup module resources."""
pass
class OLEDDisplayModule(ArmrestModule):
"""1.3" OLED status display in armrest."""
MODULE_TYPE = "oled_display"
def __init__(self, controller: CybersynController, i2c_address: int = 0x3C):
super().__init__(controller)
self.i2c_address = i2c_address
self.display = None
def setup(self):
"""Initialize OLED display."""
try:
# Requires: pip install luma.oled
from luma.core.interface.serial import i2c
from luma.oled.device import sh1106
serial = i2c(port=1, address=self.i2c_address)
self.display = sh1106(serial)
self.enabled = True
print(f"[OLED] Display initialized at 0x{self.i2c_address:02X}")
except Exception as e:
print(f"[OLED] Failed to initialize: {e}")
def show_text(self, text: str, font_size: int = 16):
"""Display text on OLED."""
if not self.display:
return
from PIL import Image, ImageDraw, ImageFont
img = Image.new("1", (self.display.width, self.display.height), "black")
draw = ImageDraw.Draw(img)
draw.text((0, 0), text, fill="white")
self.display.display(img)
def poll(self) -> dict:
return {"enabled": self.enabled}
class NFCReaderModule(ArmrestModule):
"""RC522 NFC reader for user identification."""
MODULE_TYPE = "nfc_reader"
def __init__(self, controller: CybersynController):
super().__init__(controller)
self.reader = None
self.last_uid = None
def setup(self):
"""Initialize NFC reader."""
try:
# Requires: pip install mfrc522
from mfrc522 import SimpleMFRC522
self.reader = SimpleMFRC522()
self.enabled = True
print("[NFC] Reader initialized")
except Exception as e:
print(f"[NFC] Failed to initialize: {e}")
def poll(self) -> dict:
"""Check for NFC card."""
if not self.reader:
return {"uid": None}
try:
uid, _ = self.reader.read_no_block()
if uid and uid != self.last_uid:
self.last_uid = uid
# Publish to MQTT
if self.controller.mqtt_client:
topic = f"cybersyn/chair/{self.controller.chair_id}/nfc"
payload = json.dumps({"uid": str(uid)})
self.controller.mqtt_client.publish(topic, payload)
return {"uid": str(uid) if uid else None}
except Exception:
return {"uid": None}
class HapticFeedbackModule(ArmrestModule):
"""Vibration motor for button feedback."""
MODULE_TYPE = "haptic"
def __init__(self, controller: CybersynController, gpio_pin: int = 21):
super().__init__(controller)
self.gpio_pin = gpio_pin
self.motor = None
def setup(self):
"""Initialize haptic motor."""
if HARDWARE_AVAILABLE:
from gpiozero import PWMOutputDevice
self.motor = PWMOutputDevice(self.gpio_pin)
self.enabled = True
# Register for all button presses
for button_name in ButtonName:
self.controller.register_callback(
button_name,
self._on_button_event
)
print("[Haptic] Motor initialized")
def _on_button_event(self, button_name: ButtonName, state: str):
"""Vibrate on button press."""
if state == "pressed" and self.motor:
self.pulse(duration=0.05, intensity=0.7)
def pulse(self, duration: float = 0.1, intensity: float = 1.0):
"""Pulse the vibration motor."""
if self.motor:
self.motor.value = intensity
asyncio.get_event_loop().call_later(
duration,
lambda: setattr(self.motor, 'value', 0)
)
def poll(self) -> dict:
return {"enabled": self.enabled}
# =============================================================================
# Main Entry Point
# =============================================================================
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Cybersyn Chair Controller")
parser.add_argument(
"--chair-id",
default="chair_1",
help="Unique identifier for this chair (1-7)"
)
parser.add_argument(
"--mqtt-broker",
default="localhost",
help="MQTT broker address"
)
parser.add_argument(
"--mqtt-port",
type=int,
default=1883,
help="MQTT broker port"
)
args = parser.parse_args()
# Create and run controller
controller = CybersynController(
chair_id=args.chair_id,
mqtt_broker=args.mqtt_broker,
mqtt_port=args.mqtt_port,
)
# Optional: Add expansion modules
# oled = OLEDDisplayModule(controller)
# oled.setup()
# haptic = HapticFeedbackModule(controller)
# haptic.setup()
controller.run()

0
software/server/.gitkeep Normal file
View File