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:
commit
047800f18d
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
## 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. 19–28
|
||||
- **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,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. 19–28** | 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,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()
|
||||
Loading…
Reference in New Issue