Implement automatic page scaling feature with viewport adjustments
This commit is contained in:
660
chromium-setup/FLASK-BUZZER-CONTROL.md
Normal file
660
chromium-setup/FLASK-BUZZER-CONTROL.md
Normal 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
|
||||
Reference in New Issue
Block a user