Add comprehensive ESP32 relay integration guide

This commit is contained in:
2025-12-15 16:24:07 +11:00
parent bc3ab2f52d
commit 8349443bf5

View File

@@ -0,0 +1,560 @@
# ESP32 Bluetooth Relay Integration Guide
Complete guide for integrating ESP32 8-channel Bluetooth relay controller with the Moonlight Drive-In Theatre system.
## 🎯 Overview
This integration allows the theatre software to automatically control:
- **House lights** - Main theatre lighting
- **Screen lights** - Illumination around the screen
- **Marquee lights** - External entrance/sign lighting
- **Speaker power** - Audio system power
- **Projector power** - Video projector control
- **Effect lights** - Special effect lighting
- **Concession lights** - Snack bar area
- **Parking lights** - Parking area illumination
## 📦 Prerequisites
### Hardware Required
1. **ESP32 Development Board** (with Bluetooth)
2. **8-Channel Relay Module** (5V)
3. **Wiring components** (jumper wires, power supply)
4. **Computer with Bluetooth** (for Python script communication)
### Software Required
```bash
pip install pyserial
```
The ESP32 firmware is in the separate `esp32-bluetooth-relay` repository.
## 🔧 Hardware Setup
### Step 1: Build ESP32 Relay Controller
Follow the complete guide in the `esp32-bluetooth-relay` repository:
- Wire ESP32 to relay module
- Upload firmware to ESP32
- Test basic relay operation
### Step 2: Connect Loads to Relays
Map your theatre equipment to relays (default configuration):
| Relay | Function | Theatre Equipment |
|-------|----------|-------------------|
| 1 | House Lights | Main overhead lights |
| 2 | Screen Lights | Screen border lighting |
| 3 | Marquee Lights | Entrance sign |
| 4 | Speaker Power | Audio amplifier |
| 5 | Projector Power | Video projector |
| 6 | Effect Lights | RGB/effect lighting |
| 7 | Concession Lights | Snack bar |
| 8 | Parking Lights | Outdoor parking |
**⚠️ SAFETY:** Only qualified electricians should wire AC-powered equipment. Start with low-voltage DC loads for testing.
### Step 3: Pair ESP32 with Computer
**Windows:**
1. Open Bluetooth settings
2. Search for "ESP32-Relay-8CH"
3. Pair the device
4. Note the COM port assigned (e.g., COM4)
**Linux:**
```bash
# Scan for ESP32
hcitool scan
# Pair with ESP32 (replace XX:XX... with actual MAC)
bluetoothctl
> pair XX:XX:XX:XX:XX:XX
> trust XX:XX:XX:XX:XX:XX
> connect XX:XX:XX:XX:XX:XX
# Bind to serial port
sudo rfcomm bind /dev/rfcomm0 XX:XX:XX:XX:XX:XX
```
## 🚀 Software Integration
### Step 1: Install Python Dependencies
```bash
cd moonlight-drive-in
pip install pyserial
```
### Step 2: Configure Theatre Automation
Edit `config.py` and add the relay configuration section:
```python
# ESP32 Relay Controller Configuration
RELAY_CONFIG = {
# Enable/disable features
'enabled': True,
'auto_theatre_lights': True, # Auto dim lights during movies
'auto_speaker_power': True, # Auto power speakers
'light_dim_delay': 5, # Seconds before dimming
# Serial port (None = auto-detect, or specify like 'COM4' or '/dev/rfcomm0')
'relay_port': None,
# Custom relay mapping (optional - override defaults)
'relay_mapping': {
'HOUSE_LIGHTS': 1,
'SCREEN_LIGHTS': 2,
'MARQUEE_LIGHTS': 3,
'SPEAKER_POWER': 4,
'PROJECTOR_POWER': 5,
'EFFECT_LIGHTS': 6,
'CONCESSION_LIGHTS': 7,
'PARKING_LIGHTS': 8,
}
}
```
### Step 3: Modify MPV Player Integration
Edit `mpv_seamless_player.py` to add automation hooks:
```python
# At top of file, add imports
from theatre_automation import TheatreAutomation, RelayFunction
import config
# In __init__ method, add:
self.theatre = None
if config.RELAY_CONFIG.get('enabled', False):
try:
self.theatre = TheatreAutomation(config.RELAY_CONFIG)
if self.theatre.connect():
logger.info("Theatre automation enabled")
else:
logger.warning("Failed to connect to theatre automation")
self.theatre = None
except Exception as e:
logger.error(f"Theatre automation error: {e}")
self.theatre = None
# Before playing trailer, add:
if self.theatre:
self.theatre.movie_starting(is_trailer=True)
# After trailer ends, add:
if self.theatre:
self.theatre.movie_ended(is_trailer=True)
# Before playing main movie, add:
if self.theatre:
self.theatre.movie_starting(is_trailer=False)
# After movie ends, add:
if self.theatre:
self.theatre.movie_ended(is_trailer=False)
# On shutdown/cleanup, add:
if self.theatre:
self.theatre.disconnect()
```
### Step 4: Add Web Interface Controls
Edit `web_interface.py` to add relay control routes:
```python
from theatre_automation import TheatreAutomation, RelayFunction
# Add global theatre variable
theatre_automation = None
# Initialize in app startup
if config.RELAY_CONFIG.get('enabled'):
theatre_automation = TheatreAutomation(config.RELAY_CONFIG)
theatre_automation.connect()
# Add API endpoints
@app.route('/api/relay/status')
def relay_status():
"""Get status of all relays"""
if theatre_automation and theatre_automation.connected:
return jsonify(theatre_automation.get_status())
return jsonify({'error': 'Theatre automation not connected'}), 503
@app.route('/api/relay/<function_name>/<state>')
def relay_control(function_name, state):
"""Control a specific relay"""
if not theatre_automation or not theatre_automation.connected:
return jsonify({'error': 'Not connected'}), 503
try:
function = RelayFunction[function_name.upper()]
state_bool = state.lower() == 'on'
if theatre_automation.set_relay(function, state_bool):
return jsonify({'success': True})
else:
return jsonify({'error': 'Command failed'}), 500
except KeyError:
return jsonify({'error': 'Invalid function'}), 400
@app.route('/api/relay/effect/<effect_name>')
def relay_effect(effect_name):
"""Run a lighting effect"""
if theatre_automation and theatre_automation.connected:
theatre_automation.effect_sequence(effect_name)
return jsonify({'success': True})
return jsonify({'error': 'Not connected'}), 503
```
## 🧪 Testing
### Test 1: Basic Connection
```bash
python esp32_relay_controller.py
```
This will:
- List available serial ports
- Connect to ESP32
- Run interactive test mode
### Test 2: Theatre Automation
```bash
python theatre_automation.py
```
This will:
- Initialize theatre automation
- Run a demo sequence (trailer → movie → closing)
- Test all automation functions
### Test 3: Integration Test
1. Start the theatre software normally
2. Watch for log messages: "Theatre automation enabled"
3. Scan an NFC tag to start a movie
4. Observe lights dimming after delay
5. Movie ends and lights come back up
## 🎮 Manual Control
### Command Line Control
```python
from theatre_automation import TheatreAutomation, RelayFunction
theatre = TheatreAutomation()
theatre.connect()
# Manual relay control
theatre.set_relay(RelayFunction.HOUSE_LIGHTS, False) # Lights off
theatre.set_relay(RelayFunction.SPEAKER_POWER, True) # Speakers on
# Run effects
theatre.effect_sequence("flash")
theatre.effect_sequence("pulse")
# Get status
status = theatre.get_status()
print(status)
theatre.disconnect()
```
### Web Dashboard Control
Add to `dashboard.html`:
```html
<!-- Theatre Automation Controls -->
<div class="theatre-automation">
<h2>Theatre Automation</h2>
<div class="relay-grid">
<div class="relay-control">
<span>House Lights</span>
<button onclick="controlRelay('HOUSE_LIGHTS', 'on')">ON</button>
<button onclick="controlRelay('HOUSE_LIGHTS', 'off')">OFF</button>
</div>
<div class="relay-control">
<span>Speaker Power</span>
<button onclick="controlRelay('SPEAKER_POWER', 'on')">ON</button>
<button onclick="controlRelay('SPEAKER_POWER', 'off')">OFF</button>
</div>
<!-- Add more controls for each relay... -->
</div>
<div class="effects">
<button onclick="runEffect('flash')">Flash Effect</button>
<button onclick="runEffect('pulse')">Pulse Effect</button>
<button onclick="runEffect('marquee')">Marquee</button>
</div>
</div>
<script>
function controlRelay(functionName, state) {
fetch(`/api/relay/${functionName}/${state}`)
.then(response => response.json())
.then(data => {
if (data.success) {
console.log(`${functionName} turned ${state}`);
updateStatus();
}
});
}
function runEffect(effectName) {
fetch(`/api/relay/effect/${effectName}`)
.then(response => response.json())
.then(data => console.log('Effect complete'));
}
function updateStatus() {
fetch('/api/relay/status')
.then(response => response.json())
.then(status => {
// Update UI with relay states
console.log('Theatre status:', status);
});
}
// Update status every 5 seconds
setInterval(updateStatus, 5000);
</script>
```
## 📋 Automation Sequences
### Movie Starting
1. Wait for light_dim_delay (default 5 seconds)
2. Turn off house lights
3. Turn off screen lights (main movie only)
4. Turn on speaker power
### Movie Ending
1. Turn on house lights
2. Turn on screen lights
3. Turn off speaker power (if no other content playing)
### Intermission
1. Turn on house lights
2. Turn on concession lights
3. Keep speakers powered
### Closing
1. Turn off speakers
2. Turn off screen lights
3. Turn off effect lights
4. Keep parking and marquee lights on
### Emergency
1. Turn ALL lights ON immediately
## ⚙️ Customization
### Change Relay Assignments
Edit `config.py`:
```python
'relay_mapping': {
'HOUSE_LIGHTS': 2, # Changed from 1 to 2
'SCREEN_LIGHTS': 1, # Swapped with house lights
# ... etc
}
```
### Adjust Timing
```python
'light_dim_delay': 10, # Wait 10 seconds before dimming
```
### Disable Auto-Features
```python
'auto_theatre_lights': False, # Manual light control only
'auto_speaker_power': False, # Manual speaker control
```
### Add Custom Functions
Edit `theatre_automation.py`:
```python
class RelayFunction(Enum):
# Add new function
FOG_MACHINE = 5 # Use relay 5 for fog machine
# Add method to control it
def activate_fog(self, duration=5):
self.set_relay(RelayFunction.FOG_MACHINE, True)
time.sleep(duration)
self.set_relay(RelayFunction.FOG_MACHINE, False)
```
## 🐛 Troubleshooting
### ESP32 Not Found
**Problem:** "No serial ports found"
**Solutions:**
1. Ensure ESP32 is powered on
2. Verify Bluetooth pairing
3. Check Bluetooth COM port in Device Manager (Windows)
4. Run `hcitool scan` to verify device (Linux)
### Connection Fails
**Problem:** "Failed to connect to relay controller"
**Solutions:**
1. Check COM port in config matches actual port
2. Verify no other program is using the port
3. Try manual port specification: `relay_port: 'COM4'`
4. Test with standalone script: `python esp32_relay_controller.py`
### Relays Don't Respond
**Problem:** Commands sent but relays don't switch
**Solutions:**
1. Test ESP32 with Serial Bluetooth Terminal app
2. Check ESP32 is running and Bluetooth is active
3. Verify relay module has power
4. Check wiring between ESP32 and relay module
### Lights Don't Dim
**Problem:** Automation enabled but lights stay on
**Solutions:**
1. Check `auto_theatre_lights: True` in config
2. Verify relay wiring to light switch/relay
3. Check relay polarity (active HIGH vs LOW)
4. Test manual control: `theatre.set_relay(RelayFunction.HOUSE_LIGHTS, False)`
## 📚 API Reference
### ESP32RelayController
```python
controller = ESP32RelayController()
# Connection
controller.connect(port='COM4')
controller.disconnect()
# Basic Control
controller.turn_on(1) # Turn on relay 1
controller.turn_off(1) # Turn off relay 1
controller.toggle(1) # Toggle relay 1
controller.all_on() # All relays on
controller.all_off() # All relays off
# Status
controller.get_status() # Request status update
controller.get_relay_state(1) # Get cached state of relay 1
controller.get_all_states() # Get all cached states
# Callbacks
controller.register_callback('connect', on_connect_func)
controller.register_callback('disconnect', on_disconnect_func)
controller.register_callback('status_update', on_status_func)
```
### TheatreAutomation
```python
theatre = TheatreAutomation(config)
# Connection
theatre.connect(port='COM4')
theatre.disconnect()
# Automation Sequences
theatre.movie_starting(is_trailer=True)
theatre.movie_ended(is_trailer=False)
theatre.intermission()
theatre.closing_sequence()
theatre.emergency_lights_on()
# Manual Control
theatre.set_relay(RelayFunction.HOUSE_LIGHTS, True)
theatre.toggle_relay(RelayFunction.EFFECT_LIGHTS)
theatre.effect_sequence("flash")
# Status
status = theatre.get_status()
```
## 🎬 Example Workflows
### Unattended Mode Integration
```python
# In unattend mode cycle handler
def cycle_content():
if theatre:
# Starting trailer
theatre.movie_starting(is_trailer=True)
play_trailer()
if theatre:
theatre.movie_ended(is_trailer=True)
time.sleep(2) # Brief pause
theatre.movie_starting(is_trailer=False)
play_movie()
if theatre:
theatre.movie_ended(is_trailer=False)
```
### NFC Tag Triggered Effects
```python
# Map NFC tags to lighting effects
NFC_EFFECTS = {
'AAAAAAAA': 'flash',
'BBBBBBBB': 'pulse',
'CCCCCCCC': 'marquee',
}
def handle_nfc(tag_id):
if tag_id in NFC_EFFECTS and theatre:
theatre.effect_sequence(NFC_EFFECTS[tag_id])
```
## 🔐 Safety Notes
1. **AC Power:** Only qualified electricians should work with mains voltage
2. **Fusing:** Use appropriate fuses for all loads
3. **Ratings:** Don't exceed relay current/voltage ratings
4. **Failsafe:** Ensure system fails to a safe state (lights ON)
5. **Testing:** Test thoroughly with low-voltage loads first
## 📖 Additional Resources
- **ESP32 Relay Repository:** `esp32-bluetooth-relay`
- **ESP32 Documentation:** Full wiring and setup guide
- **Python Serial:** https://pyserial.readthedocs.io/
- **Bluetooth Serial:** https://learn.adafruit.com/android-bluetooth-serial
---
**Version:** 1.0
**Last Updated:** December 2025
**Integration Status:** Ready for testing