Remove obsolete audio and buzzer control documentation files, including detailed guides and HTML interfaces, to streamline the repository and eliminate redundancy. This cleanup enhances maintainability and focuses on essential resources for the reTerminal DM4 audio and buzzer functionalities.

This commit is contained in:
nearxos
2026-02-20 15:39:39 +02:00
parent 9656771d5a
commit 58d9144752
101 changed files with 80 additions and 193 deletions

View File

@@ -0,0 +1,660 @@
# Flask Buzzer Control Documentation
## Overview
This guide explains how to control the reTerminal DM4 buzzer from a Python Flask web application. The buzzer is accessed via the Linux sysfs interface at `/sys/class/leds/usr-buzzer/brightness`.
## Prerequisites
- Python 3.x installed
- Flask installed (`pip install flask`)
- sudo permissions (or user in appropriate groups)
- reTerminal DM4 with buzzer accessible
## Basic Buzzer Control Functions
### Simple Python Buzzer Control
```python
import os
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
def buzzer_on():
"""Turn buzzer ON"""
with open(BUZZER_PATH, 'w') as f:
f.write('1')
def buzzer_off():
"""Turn buzzer OFF"""
with open(BUZZER_PATH, 'w') as f:
f.write('0')
def buzzer_beep(duration=0.2):
"""Play a beep for specified duration (in seconds)"""
import time
buzzer_on()
time.sleep(duration)
buzzer_off()
```
**Note**: These functions require root privileges. See "Permission Setup" section below.
## Flask Application Examples
### Example 1: Basic Flask App with Buzzer Control
```python
#!/usr/bin/env python3
from flask import Flask, jsonify, request
import os
import time
import subprocess
app = Flask(__name__)
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
def buzzer_on():
"""Turn buzzer ON"""
try:
subprocess.run(['sudo', 'tee', BUZZER_PATH],
input='1', text=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True)
return True
except subprocess.CalledProcessError:
return False
def buzzer_off():
"""Turn buzzer OFF"""
try:
subprocess.run(['sudo', 'tee', BUZZER_PATH],
input='0', text=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True)
return True
except subprocess.CalledProcessError:
return False
def buzzer_beep(duration=0.2):
"""Play a beep for specified duration"""
buzzer_on()
time.sleep(duration)
buzzer_off()
@app.route('/')
def index():
return jsonify({
'status': 'Buzzer Control API',
'endpoints': {
'/buzzer/on': 'Turn buzzer ON',
'/buzzer/off': 'Turn buzzer OFF',
'/buzzer/beep': 'Play a beep (duration parameter)',
'/buzzer/status': 'Get buzzer status'
}
})
@app.route('/buzzer/on', methods=['POST', 'GET'])
def turn_on():
if buzzer_on():
return jsonify({'status': 'success', 'message': 'Buzzer turned ON'})
return jsonify({'status': 'error', 'message': 'Failed to turn buzzer ON'}), 500
@app.route('/buzzer/off', methods=['POST', 'GET'])
def turn_off():
if buzzer_off():
return jsonify({'status': 'success', 'message': 'Buzzer turned OFF'})
return jsonify({'status': 'error', 'message': 'Failed to turn buzzer OFF'}), 500
@app.route('/buzzer/beep', methods=['POST', 'GET'])
def beep():
duration = float(request.args.get('duration', 0.2))
if duration < 0 or duration > 5:
return jsonify({'status': 'error', 'message': 'Duration must be between 0 and 5 seconds'}), 400
buzzer_beep(duration)
return jsonify({'status': 'success', 'message': f'Beeped for {duration} seconds'})
@app.route('/buzzer/status', methods=['GET'])
def status():
try:
with open(BUZZER_PATH, 'r') as f:
state = f.read().strip()
return jsonify({
'status': 'success',
'buzzer': 'ON' if state == '1' else 'OFF',
'state': state
})
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
```
### Example 2: Advanced Flask App with Pattern Support
```python
#!/usr/bin/env python3
from flask import Flask, jsonify, request
import subprocess
import time
import threading
app = Flask(__name__)
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
class BuzzerController:
def __init__(self):
self.is_playing = False
self.play_thread = None
def _write_buzzer(self, value):
"""Internal method to write to buzzer"""
try:
subprocess.run(['sudo', 'tee', BUZZER_PATH],
input=str(value), text=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=True)
return True
except subprocess.CalledProcessError:
return False
def on(self):
"""Turn buzzer ON"""
return self._write_buzzer(1)
def off(self):
"""Turn buzzer OFF"""
return self._write_buzzer(0)
def beep(self, duration=0.2):
"""Play a single beep"""
self.on()
time.sleep(duration)
self.off()
def play_pattern(self, pattern):
"""
Play a pattern
pattern: list of tuples [(duration_on, duration_off), ...]
Example: [(0.1, 0.1), (0.1, 0.1), (0.1, 0.3)] = two short beeps, pause, one beep
"""
if self.is_playing:
return False
def _play():
self.is_playing = True
for on_time, off_time in pattern:
self.on()
time.sleep(on_time)
self.off()
time.sleep(off_time)
self.is_playing = False
self.play_thread = threading.Thread(target=_play, daemon=True)
self.play_thread.start()
return True
def starwars_theme(self, duration=5):
"""Play Star Wars theme pattern"""
if self.is_playing:
return False
def _play():
self.is_playing = True
start_time = time.time()
# Opening sequence
self.beep(0.05)
time.sleep(0.05)
self.beep(0.05)
time.sleep(0.05)
self.beep(0.05)
time.sleep(0.1)
self.beep(0.1)
time.sleep(0.1)
self.beep(0.15)
time.sleep(0.15)
# Continue pattern until duration reached
while time.time() - start_time < duration:
self.beep(0.08)
time.sleep(0.05)
self.beep(0.08)
time.sleep(0.05)
self.beep(0.08)
time.sleep(0.1)
self.beep(0.12)
time.sleep(0.1)
self.off()
self.is_playing = False
self.play_thread = threading.Thread(target=_play, daemon=True)
self.play_thread.start()
return True
# Create global buzzer controller
buzzer = BuzzerController()
@app.route('/')
def index():
return jsonify({
'status': 'Advanced Buzzer Control API',
'endpoints': {
'/buzzer/on': 'Turn buzzer ON (POST/GET)',
'/buzzer/off': 'Turn buzzer OFF (POST/GET)',
'/buzzer/beep': 'Play a beep (GET: ?duration=0.2)',
'/buzzer/pattern': 'Play custom pattern (POST: JSON)',
'/buzzer/starwars': 'Play Star Wars theme (GET: ?duration=5)',
'/buzzer/status': 'Get buzzer status (GET)'
}
})
@app.route('/buzzer/on', methods=['POST', 'GET'])
def turn_on():
if buzzer.on():
return jsonify({'status': 'success', 'message': 'Buzzer turned ON'})
return jsonify({'status': 'error', 'message': 'Failed to turn buzzer ON'}), 500
@app.route('/buzzer/off', methods=['POST', 'GET'])
def turn_off():
if buzzer.off():
return jsonify({'status': 'success', 'message': 'Buzzer turned OFF'})
return jsonify({'status': 'error', 'message': 'Failed to turn buzzer OFF'}), 500
@app.route('/buzzer/beep', methods=['POST', 'GET'])
def beep():
duration = float(request.args.get('duration', 0.2))
if duration < 0 or duration > 5:
return jsonify({'status': 'error', 'message': 'Duration must be between 0 and 5 seconds'}), 400
buzzer.beep(duration)
return jsonify({'status': 'success', 'message': f'Beeped for {duration} seconds'})
@app.route('/buzzer/pattern', methods=['POST'])
def play_pattern():
data = request.get_json()
if not data or 'pattern' not in data:
return jsonify({'status': 'error', 'message': 'Pattern required in JSON body'}), 400
pattern = data['pattern']
if not isinstance(pattern, list):
return jsonify({'status': 'error', 'message': 'Pattern must be a list'}), 400
# Validate pattern format
try:
validated_pattern = []
for item in pattern:
if isinstance(item, list) and len(item) == 2:
validated_pattern.append((float(item[0]), float(item[1])))
else:
return jsonify({'status': 'error', 'message': 'Each pattern item must be [on_time, off_time]'}), 400
except (ValueError, TypeError):
return jsonify({'status': 'error', 'message': 'Invalid pattern format'}), 400
if buzzer.play_pattern(validated_pattern):
return jsonify({'status': 'success', 'message': 'Pattern playing'})
return jsonify({'status': 'error', 'message': 'Buzzer is already playing'}), 409
@app.route('/buzzer/starwars', methods=['GET'])
def starwars():
duration = float(request.args.get('duration', 5))
if duration < 0 or duration > 10:
return jsonify({'status': 'error', 'message': 'Duration must be between 0 and 10 seconds'}), 400
if buzzer.starwars_theme(duration):
return jsonify({'status': 'success', 'message': f'Playing Star Wars theme for {duration} seconds'})
return jsonify({'status': 'error', 'message': 'Buzzer is already playing'}), 409
@app.route('/buzzer/status', methods=['GET'])
def status():
try:
result = subprocess.run(['cat', BUZZER_PATH],
capture_output=True, text=True, check=True)
state = result.stdout.strip()
return jsonify({
'status': 'success',
'buzzer': 'ON' if state == '1' else 'OFF',
'state': state,
'is_playing': buzzer.is_playing
})
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
```
## Permission Setup
### Option 1: Run Flask with sudo (Not Recommended for Production)
```bash
sudo python3 app.py
```
### Option 2: Configure sudoers for passwordless access (Recommended)
Create a file `/etc/sudoers.d/buzzer-control`:
```
# Allow buzzer control without password
pi ALL=(ALL) NOPASSWD: /usr/bin/tee /sys/class/leds/usr-buzzer/brightness
```
Then your Flask app can use sudo without password prompts.
### Option 3: Use udev rules (Most Secure)
Create `/etc/udev/rules.d/99-buzzer.rules`:
```
SUBSYSTEM=="leds", KERNEL=="usr-buzzer", MODE="0666", GROUP="audio"
```
Add your user to the audio group:
```bash
sudo usermod -a -G audio pi
```
Then reload udev:
```bash
sudo udevadm control --reload-rules
sudo udevadm trigger
```
After this, you can write directly without sudo:
```python
def buzzer_on():
with open(BUZZER_PATH, 'w') as f:
f.write('1')
```
## API Usage Examples
### Using curl
```bash
# Turn buzzer ON
curl http://localhost:5000/buzzer/on
# Turn buzzer OFF
curl http://localhost:5000/buzzer/off
# Play a beep (0.5 seconds)
curl "http://localhost:5000/buzzer/beep?duration=0.5"
# Play Star Wars theme (5 seconds)
curl "http://localhost:5000/buzzer/starwars?duration=5"
# Play custom pattern
curl -X POST http://localhost:5000/buzzer/pattern \
-H "Content-Type: application/json" \
-d '{"pattern": [[0.1, 0.1], [0.1, 0.1], [0.1, 0.3], [0.2, 0.2]]}'
# Check status
curl http://localhost:5000/buzzer/status
```
### Using Python requests
```python
import requests
base_url = "http://localhost:5000"
# Turn on
response = requests.post(f"{base_url}/buzzer/on")
print(response.json())
# Play beep
response = requests.get(f"{base_url}/buzzer/beep", params={"duration": 0.3})
print(response.json())
# Play pattern
pattern = [[0.1, 0.1], [0.1, 0.1], [0.1, 0.3]]
response = requests.post(f"{base_url}/buzzer/pattern", json={"pattern": pattern})
print(response.json())
# Check status
response = requests.get(f"{base_url}/buzzer/status")
print(response.json())
```
### Using JavaScript/Fetch
```javascript
// Turn buzzer ON
fetch('http://localhost:5000/buzzer/on', {method: 'POST'})
.then(res => res.json())
.then(data => console.log(data));
// Play beep
fetch('http://localhost:5000/buzzer/beep?duration=0.5')
.then(res => res.json())
.then(data => console.log(data));
// Play Star Wars theme
fetch('http://localhost:5000/buzzer/starwars?duration=5')
.then(res => res.json())
.then(data => console.log(data));
// Play custom pattern
fetch('http://localhost:5000/buzzer/pattern', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
pattern: [[0.1, 0.1], [0.1, 0.1], [0.1, 0.3], [0.2, 0.2]]
})
})
.then(res => res.json())
.then(data => console.log(data));
```
## Web Interface Example
### HTML/JavaScript Frontend
```html
<!DOCTYPE html>
<html>
<head>
<title>Buzzer Control</title>
<style>
body { font-family: Arial; padding: 20px; }
button { padding: 10px 20px; margin: 5px; font-size: 16px; }
.status { margin-top: 20px; padding: 10px; background: #f0f0f0; }
</style>
</head>
<body>
<h1>reTerminal DM4 Buzzer Control</h1>
<div>
<button onclick="buzzerOn()">Buzzer ON</button>
<button onclick="buzzerOff()">Buzzer OFF</button>
<button onclick="beep(0.2)">Short Beep</button>
<button onclick="beep(0.5)">Long Beep</button>
<button onclick="starwars()">Star Wars Theme</button>
</div>
<div>
<h3>Custom Pattern</h3>
<input type="text" id="pattern" placeholder="0.1,0.1,0.1,0.1,0.1,0.3" style="width: 300px;">
<button onclick="playPattern()">Play Pattern</button>
</div>
<div class="status" id="status">Ready</div>
<script>
const API_URL = 'http://localhost:5000';
function updateStatus(message) {
document.getElementById('status').textContent = message;
}
async function buzzerOn() {
const response = await fetch(`${API_URL}/buzzer/on`, {method: 'POST'});
const data = await response.json();
updateStatus(data.message);
}
async function buzzerOff() {
const response = await fetch(`${API_URL}/buzzer/off`, {method: 'POST'});
const data = await response.json();
updateStatus(data.message);
}
async function beep(duration) {
const response = await fetch(`${API_URL}/buzzer/beep?duration=${duration}`);
const data = await response.json();
updateStatus(data.message);
}
async function starwars() {
const response = await fetch(`${API_URL}/buzzer/starwars?duration=5`);
const data = await response.json();
updateStatus(data.message);
}
async function playPattern() {
const patternStr = document.getElementById('pattern').value;
const parts = patternStr.split(',');
const pattern = [];
for (let i = 0; i < parts.length; i += 2) {
if (i + 1 < parts.length) {
pattern.push([parseFloat(parts[i]), parseFloat(parts[i+1])]);
}
}
const response = await fetch(`${API_URL}/buzzer/pattern`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({pattern: pattern})
});
const data = await response.json();
updateStatus(data.message);
}
</script>
</body>
</html>
```
## Security Considerations
1. **Authentication**: Add authentication for production use:
```python
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
@auth.verify_password
def verify_password(username, password):
return username == 'admin' and password == 'secret'
@app.route('/buzzer/on')
@auth.login_required
def turn_on():
# ...
```
2. **Rate Limiting**: Prevent abuse:
```python
from flask_limiter import Limiter
limiter = Limiter(app, key_func=get_remote_address)
@app.route('/buzzer/beep')
@limiter.limit("10 per minute")
def beep():
# ...
```
3. **CORS**: If accessing from web pages:
```python
from flask_cors import CORS
CORS(app)
```
## Installation and Setup
1. **Install Flask:**
```bash
pip3 install flask
# For advanced features:
pip3 install flask-httpauth flask-limiter flask-cors
```
2. **Set up permissions** (choose one method from above)
3. **Run the application:**
```bash
python3 app.py
```
4. **Run as a service** (systemd):
Create `/etc/systemd/system/buzzer-api.service`:
```ini
[Unit]
Description=Buzzer Control API
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/buzzer-api
ExecStart=/usr/bin/python3 /home/pi/buzzer-api/app.py
Restart=always
[Install]
WantedBy=multi-user.target
```
Enable and start:
```bash
sudo systemctl enable buzzer-api
sudo systemctl start buzzer-api
```
## Troubleshooting
### Permission Denied
- Check if user has sudo access
- Verify udev rules are applied
- Check file permissions: `ls -la /sys/class/leds/usr-buzzer/brightness`
### Buzzer Not Responding
- Verify buzzer device exists: `ls /sys/class/leds/usr-buzzer/`
- Test manually: `echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness`
- Check kernel messages: `dmesg | grep buzzer`
### Flask App Not Starting
- Check if port 5000 is in use: `sudo netstat -tulpn | grep 5000`
- Verify Python and Flask are installed: `python3 --version && pip3 list | grep Flask`
## Complete Example Project Structure
```
buzzer-api/
├── app.py # Main Flask application
├── buzzer.py # Buzzer control module
├── requirements.txt # Python dependencies
├── static/ # Static files (CSS, JS)
│ └── index.html # Web interface
└── README.md # Project documentation
```
**requirements.txt:**
```
Flask==2.3.0
flask-httpauth==4.8.0
flask-limiter==3.0.0
flask-cors==4.0.0
```
## Related Documentation
- See `BUZZER-TEST-GUIDE.md` for basic buzzer testing
- See `AUDIO-CONFIGURATION-REPORT.md` for complete audio system info