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:
10
archive/README.md
Normal file
10
archive/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Archive
|
||||
|
||||
This folder holds files that are no longer part of the active reTerminal DM4 / eMMC provisioning workflow. Kept for reference only.
|
||||
|
||||
| Subfolder | Contents |
|
||||
|-----------|----------|
|
||||
| **chromium-setup-legacy/** | Old Chromium-setup guides and scripts: KDE installation, LED/buzzer control, audio config, touchscreen options, Flask apps, test scripts, revert-to-lxde. Kiosk assets (start-chromium.sh, chromium-kiosk.desktop) live in `emmc-provisioning/cloud-init/` and `emmc-provisioning/cloud-init/config-files/`. |
|
||||
| **cloud-init-duplicates/** | Duplicate or superseded cloud-init files (e.g. plymouth-custom.script duplicate of `files-from-guard/plymouth-custom/custom.script`). |
|
||||
|
||||
Do not rely on archived files for deployment; use the main tree under **emmc-provisioning/**.
|
||||
245
archive/chromium-setup-legacy/AUDIO-CONFIGURATION-REPORT.md
Normal file
245
archive/chromium-setup-legacy/AUDIO-CONFIGURATION-REPORT.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# reTerminal DM4 Audio Configuration Report
|
||||
|
||||
## Date: 2026-01-14
|
||||
|
||||
## Official Documentation Summary
|
||||
|
||||
Based on Seeed Studio reTerminal DM documentation:
|
||||
|
||||
### Hardware Components
|
||||
- **Audio Codec**: TI TLV320AIC3104 Low-Power Stereo Audio Codec
|
||||
- **Microphones**: Dual MEMS microphones (left and right below screen)
|
||||
- **Audio Output**: 3.5mm audio jack (output only, no microphone input)
|
||||
- **Buzzer**: Built-in buzzer for system notifications/alerts
|
||||
- **Internal Speakers**: ❌ **NONE** - The device does NOT have built-in speakers in the monitor
|
||||
- **Interface**: IIS interface for microphones → codec, PCM interface for codec → CM4
|
||||
|
||||
### Audio Features
|
||||
- Dual microphone array configuration
|
||||
- Waterproof acoustic membrane (IP65 rating)
|
||||
- **Stereo audio output via 3.5mm jack** (requires external speakers/headphones)
|
||||
- **HDMI audio output** (when connected to external display)
|
||||
- **Built-in buzzer only** - for basic alerts/notifications, not for audio playback
|
||||
- **No internal speakers** - all audio playback requires external connection
|
||||
|
||||
---
|
||||
|
||||
## Current Device Configuration
|
||||
|
||||
### Audio Hardware Detection
|
||||
|
||||
**Playback Devices:**
|
||||
```
|
||||
card 0: vc4hdmi0 [vc4-hdmi-0] - HDMI audio output
|
||||
card 1: seeed2micvoicec [seeed2micvoicec] - reTerminal built-in audio (PRIMARY)
|
||||
card 2: vc4hdmi1 [vc4-hdmi-1] - HDMI audio output
|
||||
```
|
||||
|
||||
**Recording Devices:**
|
||||
```
|
||||
card 1: seeed2micvoicec [seeed2micvoicec] - Dual MEMS microphones
|
||||
```
|
||||
|
||||
### Audio Codec Status
|
||||
|
||||
**Detected Codec**: `tlv320aic3x` (TLV320AIC3x series - compatible with TLV320AIC3104)
|
||||
|
||||
**Kernel Messages:**
|
||||
```
|
||||
tlv320aic3x 1-0018: supply IOVDD not found, using dummy regulator
|
||||
tlv320aic3x 1-0018: supply DVDD not found, using dummy regulator
|
||||
tlv320aic3x 1-0018: supply AVDD not found, using dummy regulator
|
||||
tlv320aic3x 1-0018: supply DRVDD not found, using dummy regulator
|
||||
tlv320aic3x 1-0018: Invalid supply voltage(s) AVDD: -22, DVDD: -22
|
||||
```
|
||||
|
||||
**Note**: The supply voltage warnings are common and don't prevent functionality. The codec uses dummy regulators and operates normally.
|
||||
|
||||
### ALSA Mixer Configuration
|
||||
|
||||
**Primary Controls:**
|
||||
- **PCM**: 127/127 (100%) - Main playback volume ✓
|
||||
- **Line**: 0/9 (0%) - Line output level
|
||||
- **Line DAC**: 71/118 (60%) - Line DAC volume
|
||||
- **HP (Headphone)**: 0/9 (0%) - Headphone output
|
||||
- **HP DAC**: 71/118 (60%) - Headphone DAC volume
|
||||
- **PGA**: 32/119 (27%) - Microphone preamp gain ✓
|
||||
|
||||
**Audio Routing:**
|
||||
- **Playback**: Line Mixer DACL1/R1 enabled → 3.5mm audio jack
|
||||
- **Capture**: PGA enabled → Dual MEMS microphones
|
||||
- **AGC**: Disabled (Automatic Gain Control off)
|
||||
|
||||
### Audio System Status
|
||||
|
||||
| Component | Status | Details |
|
||||
|-----------|--------|---------|
|
||||
| Hardware Detection | ✓ Working | Card 1: seeed2micvoicec detected |
|
||||
| Playback | ✓ Working | PCM at 100%, tested with speaker-test |
|
||||
| Recording | ✓ Working | Microphones functional, tested recording |
|
||||
| Codec Driver | ✓ Loaded | tlv320aic3x kernel module active |
|
||||
| User Permissions | ✓ Configured | User in `audio` group |
|
||||
| ALSA System | ✓ Operational | Direct ALSA (no PulseAudio) |
|
||||
|
||||
---
|
||||
|
||||
## Configuration Comparison
|
||||
|
||||
### ✅ Matches Documentation
|
||||
|
||||
1. **Audio Codec**: TLV320AIC3x detected (compatible with TLV320AIC3104)
|
||||
2. **Dual Microphones**: Recording device shows stereo input capability
|
||||
3. **3.5mm Audio Jack**: Line output controls present and configured
|
||||
4. **ALSA Configuration**: Using ALSA directly as documented
|
||||
5. **Audio Groups**: User properly configured in audio group
|
||||
|
||||
### ⚠️ Notes & Observations
|
||||
|
||||
1. **Supply Voltage Warnings**: Kernel shows supply voltage warnings, but device functions normally
|
||||
2. **Card Numbering**: Documentation may reference different card numbers; device uses card 1
|
||||
3. **PulseAudio**: Not installed (using ALSA directly) - this is fine and matches some configurations
|
||||
4. **Volume Levels**:
|
||||
- PCM (main output): 100% ✓
|
||||
- Line/HP outputs: Lower levels (may need adjustment for 3.5mm jack)
|
||||
- Microphone gain: 27% (may need adjustment based on use case)
|
||||
|
||||
---
|
||||
|
||||
## Testing Results
|
||||
|
||||
### Playback Test
|
||||
```bash
|
||||
speaker-test -D hw:1,0 -t sine -f 1000 -c 2 -l 1
|
||||
```
|
||||
**Result**: ✓ Successfully generated test tone
|
||||
|
||||
### Recording Test
|
||||
```bash
|
||||
arecord -D hw:1,0 -f cd -d 1 test.wav
|
||||
```
|
||||
**Result**: ✓ Successfully recorded 173KB stereo audio file
|
||||
|
||||
---
|
||||
|
||||
## Recommended Configuration
|
||||
|
||||
### For 3.5mm Audio Jack Output
|
||||
|
||||
If using the 3.5mm audio jack, you may need to adjust the Line output:
|
||||
|
||||
```bash
|
||||
# Increase Line output volume (0-9 scale)
|
||||
amixer -c 1 sset 'Line' 9
|
||||
|
||||
# Or use Line DAC for finer control (0-118 scale)
|
||||
amixer -c 1 sset 'Line DAC' 118
|
||||
```
|
||||
|
||||
### For Microphone Input
|
||||
|
||||
Current microphone gain is at 27%. Adjust if needed:
|
||||
|
||||
```bash
|
||||
# Increase microphone gain (0-119 scale)
|
||||
amixer -c 1 sset 'PGA' 50
|
||||
|
||||
# Enable AGC for automatic gain control
|
||||
amixer -c 1 sset 'AGC' on
|
||||
```
|
||||
|
||||
### Set Default Audio Card
|
||||
|
||||
To make card 1 the default for applications:
|
||||
|
||||
```bash
|
||||
# Create ALSA configuration
|
||||
cat > ~/.asoundrc << EOF
|
||||
pcm.!default {
|
||||
type hw
|
||||
card 1
|
||||
device 0
|
||||
}
|
||||
|
||||
ctl.!default {
|
||||
type hw
|
||||
card 1
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No Sound from 3.5mm Jack
|
||||
|
||||
1. Check Line output volume:
|
||||
```bash
|
||||
amixer -c 1 sget 'Line'
|
||||
amixer -c 1 sset 'Line' 9
|
||||
```
|
||||
|
||||
2. Verify Line DAC routing:
|
||||
```bash
|
||||
amixer -c 1 sget 'Line DAC'
|
||||
amixer -c 1 sset 'Line DAC' 118
|
||||
```
|
||||
|
||||
3. Test with direct hardware access:
|
||||
```bash
|
||||
aplay -D hw:1,0 /path/to/test.wav
|
||||
```
|
||||
|
||||
### Microphone Not Working
|
||||
|
||||
1. Check microphone gain:
|
||||
```bash
|
||||
amixer -c 1 sget 'PGA'
|
||||
amixer -c 1 sset 'PGA' 50
|
||||
```
|
||||
|
||||
2. Verify recording device:
|
||||
```bash
|
||||
arecord -l
|
||||
arecord -D hw:1,0 -f cd test.wav
|
||||
```
|
||||
|
||||
3. Check microphone routing:
|
||||
```bash
|
||||
amixer -c 1 sget 'PGA Mixer'
|
||||
```
|
||||
|
||||
### Supply Voltage Warnings
|
||||
|
||||
The kernel warnings about supply voltages are informational. The codec uses dummy regulators and functions correctly. These warnings can be ignored unless experiencing actual audio issues.
|
||||
|
||||
---
|
||||
|
||||
## Important Note: No Internal Speakers
|
||||
|
||||
**⚠️ The reTerminal DM4 does NOT have built-in speakers in the monitor.**
|
||||
|
||||
The device only includes:
|
||||
- **Built-in Buzzer** (`/sys/class/leds/usr-buzzer`) - For system alerts/notifications only
|
||||
- **3.5mm Audio Jack** - For connecting external speakers or headphones
|
||||
- **HDMI Audio** - For audio output when connected to external displays
|
||||
|
||||
For audio playback, you **must** connect:
|
||||
- External speakers via 3.5mm jack, OR
|
||||
- Headphones via 3.5mm jack, OR
|
||||
- HDMI display with audio support
|
||||
|
||||
The buzzer can only produce simple beep tones for alerts - it cannot play music or general audio content.
|
||||
|
||||
## Summary
|
||||
|
||||
**Audio Status**: ✅ **FULLY FUNCTIONAL** (with external speakers/headphones)
|
||||
|
||||
- Hardware detected correctly
|
||||
- Playback working (tested via 3.5mm jack)
|
||||
- Recording working (tested)
|
||||
- Configuration matches documentation
|
||||
- **No internal speakers** - external audio output required
|
||||
- Minor adjustments may be needed for optimal 3.5mm jack output
|
||||
|
||||
The reTerminal DM4 audio system is properly configured and operational according to the official documentation specifications. All audio playback requires external speakers or headphones connected via the 3.5mm audio jack.
|
||||
311
archive/chromium-setup-legacy/BUZZER-CONTROL-SIMPLE.md
Normal file
311
archive/chromium-setup-legacy/BUZZER-CONTROL-SIMPLE.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Simple Buzzer Control Guide - reTerminal DM4
|
||||
|
||||
## Overview
|
||||
|
||||
The reTerminal DM4 has a built-in buzzer that can be controlled for alerts and notifications. The buzzer is located at the bottom right corner of the screen.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Turn Buzzer ON
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
### Turn Buzzer OFF
|
||||
```bash
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
### Check Buzzer Status
|
||||
```bash
|
||||
cat /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
- `0` = OFF
|
||||
- `1` or `255` = ON
|
||||
|
||||
---
|
||||
|
||||
## Basic Control Methods
|
||||
|
||||
### Method 1: Command Line (Simple)
|
||||
|
||||
**Single Beep (0.2 seconds):**
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
**Double Beep:**
|
||||
```bash
|
||||
for i in 1 2; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
done
|
||||
```
|
||||
|
||||
**Long Beep (1 second):**
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 1
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
### Method 2: Python Script
|
||||
|
||||
**Simple Python Control:**
|
||||
```python
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
def buzzer_on():
|
||||
"""Turn buzzer ON"""
|
||||
subprocess.run(['sudo', 'tee', BUZZER_PATH], input='1', text=True)
|
||||
|
||||
def buzzer_off():
|
||||
"""Turn buzzer OFF"""
|
||||
subprocess.run(['sudo', 'tee', BUZZER_PATH], input='0', text=True)
|
||||
|
||||
def beep(duration=0.2):
|
||||
"""Play a beep for specified duration (in seconds)"""
|
||||
buzzer_on()
|
||||
time.sleep(duration)
|
||||
buzzer_off()
|
||||
|
||||
# Usage
|
||||
beep(0.2) # Short beep
|
||||
beep(0.5) # Medium beep
|
||||
beep(1.0) # Long beep
|
||||
```
|
||||
|
||||
**Blinking Pattern:**
|
||||
```python
|
||||
def beep_pattern(count=3, on_time=0.1, off_time=0.1):
|
||||
"""Blink buzzer multiple times"""
|
||||
for _ in range(count):
|
||||
buzzer_on()
|
||||
time.sleep(on_time)
|
||||
buzzer_off()
|
||||
time.sleep(off_time)
|
||||
|
||||
# Usage
|
||||
beep_pattern(3, 0.1, 0.1) # 3 quick beeps
|
||||
```
|
||||
|
||||
### Method 3: Bash Function
|
||||
|
||||
Add to your `~/.bashrc`:
|
||||
```bash
|
||||
buzzer() {
|
||||
local action=$1
|
||||
local duration=${2:-0.2}
|
||||
local BUZZER='/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
case $action in
|
||||
on)
|
||||
echo 1 | sudo tee $BUZZER > /dev/null
|
||||
;;
|
||||
off)
|
||||
echo 0 | sudo tee $BUZZER > /dev/null
|
||||
;;
|
||||
beep)
|
||||
echo 1 | sudo tee $BUZZER > /dev/null
|
||||
sleep $duration
|
||||
echo 0 | sudo tee $BUZZER > /dev/null
|
||||
;;
|
||||
*)
|
||||
echo "Usage: buzzer {on|off|beep [duration]}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
```
|
||||
|
||||
Then use:
|
||||
```bash
|
||||
buzzer on # Turn on
|
||||
buzzer off # Turn off
|
||||
buzzer beep 0.2 # Beep for 0.2 seconds
|
||||
buzzer beep 0.5 # Beep for 0.5 seconds
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Success Alert (2 short beeps)
|
||||
```bash
|
||||
for i in 1 2; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
done
|
||||
```
|
||||
|
||||
### Error Alert (3 fast beeps)
|
||||
```bash
|
||||
for i in 1 2 3; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.05
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.05
|
||||
done
|
||||
```
|
||||
|
||||
### Warning Alert (1 long beep)
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.5
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
### Notification (1 short beep)
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using the Test Script
|
||||
|
||||
A test script is available at `/tmp/test_buzzer.sh` on the device:
|
||||
|
||||
```bash
|
||||
# Run the test script
|
||||
ssh guard "/tmp/test_buzzer.sh"
|
||||
```
|
||||
|
||||
Or copy and run locally:
|
||||
```bash
|
||||
scp guard:/tmp/test_buzzer.sh ./
|
||||
chmod +x test_buzzer.sh
|
||||
./test_buzzer.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Requires sudo**: All buzzer control commands require root privileges
|
||||
2. **Simple on/off**: The buzzer can only be turned ON or OFF - no volume or frequency control
|
||||
3. **Location**: Bottom right corner of the screen
|
||||
4. **Use case**: Alerts, notifications, system events
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Buzzer Not Working
|
||||
|
||||
1. **Check device exists:**
|
||||
```bash
|
||||
ls -la /sys/class/leds/usr-buzzer/
|
||||
```
|
||||
|
||||
2. **Test manually:**
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
# Wait a moment - you should hear the buzzer
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
3. **Check permissions:**
|
||||
```bash
|
||||
ls -la /sys/class/leds/usr-buzzer/brightness
|
||||
# Should show: -rw-r--r-- (requires sudo to write)
|
||||
```
|
||||
|
||||
4. **Verify current state:**
|
||||
```bash
|
||||
cat /sys/class/leds/usr-buzzer/brightness
|
||||
# 0 = off, 1 or 255 = on
|
||||
```
|
||||
|
||||
### No Sound from Buzzer
|
||||
|
||||
- Check if buzzer is physically present and working
|
||||
- Verify the device is powered on
|
||||
- Try a longer duration (e.g., `sleep 1` instead of `sleep 0.2`)
|
||||
- Check kernel messages: `dmesg | grep -i buzzer`
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Simple Alert Function
|
||||
```bash
|
||||
#!/bin/bash
|
||||
alert() {
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.3
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
}
|
||||
|
||||
# Use it
|
||||
alert
|
||||
```
|
||||
|
||||
### Example 2: Python Notification System
|
||||
```python
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
def notify(message_type):
|
||||
BUZZER = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
patterns = {
|
||||
'info': (1, 0.1), # 1 beep, 0.1s
|
||||
'success': (2, 0.1), # 2 beeps, 0.1s each
|
||||
'error': (3, 0.05), # 3 fast beeps
|
||||
'warning': (1, 0.5), # 1 long beep
|
||||
}
|
||||
|
||||
count, duration = patterns.get(message_type, (1, 0.2))
|
||||
|
||||
for _ in range(count):
|
||||
subprocess.run(['sudo', 'tee', BUZZER], input='1', text=True)
|
||||
time.sleep(duration)
|
||||
subprocess.run(['sudo', 'tee', BUZZER], input='0', text=True)
|
||||
time.sleep(0.1)
|
||||
|
||||
# Usage
|
||||
notify('success') # 2 short beeps
|
||||
notify('error') # 3 fast beeps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Action | Command |
|
||||
|--------|---------|
|
||||
| Turn ON | `echo 1 \| sudo tee /sys/class/leds/usr-buzzer/brightness` |
|
||||
| Turn OFF | `echo 0 \| sudo tee /sys/class/leds/usr-buzzer/brightness` |
|
||||
| Check Status | `cat /sys/class/leds/usr-buzzer/brightness` |
|
||||
| Single Beep | `echo 1 \| sudo tee /sys/class/leds/usr-buzzer/brightness && sleep 0.2 && echo 0 \| sudo tee /sys/class/leds/usr-buzzer/brightness` |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- `BUZZER-TEST-GUIDE.md` - Detailed testing guide
|
||||
- `FLASK-BUZZER-CONTROL.md` - Flask web API for buzzer control
|
||||
- `starwars_buzzer.sh` - Star Wars theme example script
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The buzzer is simple to control:
|
||||
1. **Path**: `/sys/class/leds/usr-buzzer/brightness`
|
||||
2. **Values**: `0` = OFF, `1` = ON
|
||||
3. **Requires**: `sudo` privileges
|
||||
4. **Control**: Write `1` to turn on, `0` to turn off
|
||||
|
||||
That's it! Simple and straightforward.
|
||||
152
archive/chromium-setup-legacy/BUZZER-CONTROL.md
Normal file
152
archive/chromium-setup-legacy/BUZZER-CONTROL.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Buzzer Control - reTerminal DM4
|
||||
|
||||
## Quick Reference
|
||||
|
||||
**Device Path:** `/sys/class/leds/usr-buzzer/brightness`
|
||||
|
||||
### Basic Commands
|
||||
|
||||
```bash
|
||||
# Turn ON
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
|
||||
# Turn OFF
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
|
||||
# Check Status
|
||||
cat /sys/class/leds/usr-buzzer/brightness # 0=OFF, 1/255=ON
|
||||
```
|
||||
|
||||
### Single Beep
|
||||
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test Scripts
|
||||
|
||||
### Bash Script
|
||||
|
||||
**Location on device:** `/home/pi/buzzer/test_buzzer.sh`
|
||||
|
||||
**Run:**
|
||||
```bash
|
||||
ssh guard "/home/pi/buzzer/test_buzzer.sh"
|
||||
# or
|
||||
/home/pi/buzzer/test_buzzer.sh
|
||||
```
|
||||
|
||||
**Tests included:**
|
||||
- Single beep
|
||||
- Double beep
|
||||
- Triple beep
|
||||
- Long beep
|
||||
- Rapid beeps
|
||||
- Slow beeps
|
||||
- Success pattern
|
||||
- Error pattern
|
||||
|
||||
### Python Script
|
||||
|
||||
**Location on device:** `/home/pi/buzzer/test_buzzer.py`
|
||||
|
||||
**Run:**
|
||||
```bash
|
||||
ssh guard "python3 /home/pi/buzzer/test_buzzer.py"
|
||||
# or
|
||||
python3 /home/pi/buzzer/test_buzzer.py
|
||||
```
|
||||
|
||||
**Same tests as bash script, with Python implementation**
|
||||
|
||||
---
|
||||
|
||||
## Python Control
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
BUZZER = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
def buzzer_on():
|
||||
subprocess.run(['sudo', 'tee', BUZZER], input='1', text=True)
|
||||
|
||||
def buzzer_off():
|
||||
subprocess.run(['sudo', 'tee', BUZZER], input='0', text=True)
|
||||
|
||||
def beep(duration=0.2):
|
||||
buzzer_on()
|
||||
time.sleep(duration)
|
||||
buzzer_off()
|
||||
|
||||
# Usage
|
||||
beep(0.2) # Short beep
|
||||
beep(0.5) # Long beep
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Success Alert (2 short beeps)
|
||||
```bash
|
||||
for i in 1 2; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
done
|
||||
```
|
||||
|
||||
### Error Alert (3 fast beeps)
|
||||
```bash
|
||||
for i in 1 2 3; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.05
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.05
|
||||
done
|
||||
```
|
||||
|
||||
### Notification (1 short beep)
|
||||
```bash
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Buzzer not working?**
|
||||
1. Check device: `ls -la /sys/class/leds/usr-buzzer/`
|
||||
2. Test manually: `echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness`
|
||||
3. Verify permissions: Requires `sudo`
|
||||
|
||||
**No sound?**
|
||||
- Check if buzzer is physically present
|
||||
- Try longer duration: `sleep 1` instead of `sleep 0.2`
|
||||
- Check kernel: `dmesg | grep -i buzzer`
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- **Requires:** `sudo` privileges
|
||||
- **Type:** Simple on/off (no volume/frequency control)
|
||||
- **Location:** Bottom right corner of screen
|
||||
- **Use:** Alerts, notifications, system events
|
||||
|
||||
---
|
||||
|
||||
## Related Files
|
||||
|
||||
- Test scripts: `/home/pi/buzzer/test_buzzer.sh` (bash), `/home/pi/buzzer/test_buzzer.py` (Python)
|
||||
- Detailed guide: `BUZZER-CONTROL-SIMPLE.md`
|
||||
- Flask API: `FLASK-BUZZER-CONTROL.md`
|
||||
264
archive/chromium-setup-legacy/BUZZER-TEST-GUIDE.md
Normal file
264
archive/chromium-setup-legacy/BUZZER-TEST-GUIDE.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# reTerminal DM4 Buzzer Test Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The reTerminal DM4 has a built-in buzzer located at the bottom right corner of the screen. The buzzer is controlled via the Linux LED subsystem and can be used for system alerts and notifications.
|
||||
|
||||
## Buzzer Device Location
|
||||
|
||||
- **Sysfs Path**: `/sys/class/leds/usr-buzzer`
|
||||
- **Control Method**: LED brightness control (0 = off, 1 = on)
|
||||
- **Type**: Active buzzer (on/off control)
|
||||
|
||||
## Quick Test Methods
|
||||
|
||||
### Method 1: Simple On/Off Test
|
||||
|
||||
```bash
|
||||
# Turn buzzer ON
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
|
||||
# Turn buzzer OFF
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
### Method 2: Single Beep
|
||||
|
||||
```bash
|
||||
# Beep for 0.2 seconds
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
### Method 3: Double Beep
|
||||
|
||||
```bash
|
||||
# Double beep pattern
|
||||
for i in 1 2; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.1
|
||||
done
|
||||
```
|
||||
|
||||
### Method 4: Rapid Beeps
|
||||
|
||||
```bash
|
||||
# 5 rapid beeps
|
||||
for i in {1..5}; do
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.05
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
sleep 0.05
|
||||
done
|
||||
```
|
||||
|
||||
## Complete Test Script
|
||||
|
||||
A comprehensive test script is available on the device at `/tmp/test_buzzer.sh`. To use it:
|
||||
|
||||
```bash
|
||||
# Run the test script
|
||||
/tmp/test_buzzer.sh
|
||||
```
|
||||
|
||||
Or create your own script:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Buzzer test script for reTerminal DM4
|
||||
|
||||
BUZZER_PATH='/sys/class/leds/usr-buzzer'
|
||||
|
||||
echo 'Testing reTerminal DM4 Buzzer'
|
||||
echo '=============================='
|
||||
echo ''
|
||||
|
||||
# Test 1: Single beep
|
||||
echo 'Test 1: Single beep (0.2 seconds)'
|
||||
echo 1 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.5
|
||||
|
||||
# Test 2: Double beep
|
||||
echo 'Test 2: Double beep'
|
||||
for i in 1 2; do
|
||||
echo 1 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.1
|
||||
echo 0 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.1
|
||||
done
|
||||
sleep 0.5
|
||||
|
||||
# Test 3: Long beep
|
||||
echo 'Test 3: Long beep (0.5 seconds)'
|
||||
echo 1 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.5
|
||||
echo 0 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.5
|
||||
|
||||
# Test 4: Rapid beeps
|
||||
echo 'Test 4: Rapid beeps (5 beeps)'
|
||||
for i in {1..5}; do
|
||||
echo 1 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.05
|
||||
echo 0 | sudo tee $BUZZER_PATH/brightness > /dev/null
|
||||
sleep 0.05
|
||||
done
|
||||
|
||||
echo ''
|
||||
echo 'Buzzer test complete!'
|
||||
```
|
||||
|
||||
## Using Timer Trigger (Advanced)
|
||||
|
||||
The buzzer can also use the timer trigger for automatic beeping patterns:
|
||||
|
||||
```bash
|
||||
# Set timer trigger
|
||||
echo timer | sudo tee /sys/class/leds/usr-buzzer/trigger
|
||||
|
||||
# Set delay_on and delay_off (in milliseconds)
|
||||
echo 100 | sudo tee /sys/class/leds/usr-buzzer/delay_on
|
||||
echo 100 | sudo tee /sys/class/leds/usr-buzzer/delay_off
|
||||
|
||||
# Disable timer trigger (return to manual control)
|
||||
echo none | sudo tee /sys/class/leds/usr-buzzer/trigger
|
||||
```
|
||||
|
||||
## Checking Buzzer Status
|
||||
|
||||
```bash
|
||||
# Check current brightness (0 = off, 1 = on)
|
||||
cat /sys/class/leds/usr-buzzer/brightness
|
||||
|
||||
# Check max brightness
|
||||
cat /sys/class/leds/usr-buzzer/max_brightness
|
||||
|
||||
# Check current trigger
|
||||
cat /sys/class/leds/usr-buzzer/trigger
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Python Script
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
|
||||
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
def buzzer_on():
|
||||
with open(BUZZER_PATH, 'w') as f:
|
||||
f.write('1')
|
||||
|
||||
def buzzer_off():
|
||||
with open(BUZZER_PATH, 'w') as f:
|
||||
f.write('0')
|
||||
|
||||
def beep(duration=0.2):
|
||||
buzzer_on()
|
||||
time.sleep(duration)
|
||||
buzzer_off()
|
||||
|
||||
# Test
|
||||
beep(0.2) # Single beep
|
||||
time.sleep(0.5)
|
||||
beep(0.1) # Short beep
|
||||
time.sleep(0.1)
|
||||
beep(0.1) # Short beep (double beep)
|
||||
```
|
||||
|
||||
### Bash Function
|
||||
|
||||
Add to your `~/.bashrc`:
|
||||
|
||||
```bash
|
||||
# Buzzer control function
|
||||
buzzer() {
|
||||
local action=${1:-status}
|
||||
local BUZZER='/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
case $action in
|
||||
on)
|
||||
echo 1 | sudo tee $BUZZER > /dev/null
|
||||
echo "Buzzer ON"
|
||||
;;
|
||||
off)
|
||||
echo 0 | sudo tee $BUZZER > /dev/null
|
||||
echo "Buzzer OFF"
|
||||
;;
|
||||
beep)
|
||||
local duration=${2:-0.2}
|
||||
echo 1 | sudo tee $BUZZER > /dev/null
|
||||
sleep $duration
|
||||
echo 0 | sudo tee $BUZZER > /dev/null
|
||||
;;
|
||||
status)
|
||||
local state=$(cat $BUZZER)
|
||||
echo "Buzzer is: $([ $state -eq 1 ] && echo 'ON' || echo 'OFF')"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: buzzer {on|off|beep [duration]|status}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
```
|
||||
|
||||
Then use:
|
||||
```bash
|
||||
buzzer beep 0.2 # Single beep
|
||||
buzzer on # Turn on
|
||||
buzzer off # Turn off
|
||||
buzzer status # Check status
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Buzzer Not Working
|
||||
|
||||
1. **Check device exists:**
|
||||
```bash
|
||||
ls -la /sys/class/leds/usr-buzzer/
|
||||
```
|
||||
|
||||
2. **Check permissions:**
|
||||
```bash
|
||||
# You need sudo to control the buzzer
|
||||
sudo echo 1 > /sys/class/leds/usr-buzzer/brightness
|
||||
```
|
||||
|
||||
3. **Check kernel messages:**
|
||||
```bash
|
||||
dmesg | grep -i buzzer
|
||||
```
|
||||
|
||||
4. **Verify GPIO/device tree:**
|
||||
```bash
|
||||
# Check if buzzer is in device tree
|
||||
ls /proc/device-tree/ | grep -i buzz
|
||||
```
|
||||
|
||||
### No Sound from Buzzer
|
||||
|
||||
- The buzzer is a simple active buzzer - it should make a continuous tone when ON
|
||||
- If no sound, check physical connections (buzzer may be damaged)
|
||||
- Verify the device is powered on
|
||||
- Check if buzzer is disabled in device tree or kernel config
|
||||
|
||||
## Notes
|
||||
|
||||
- **Buzzer is simple**: It can only be ON or OFF - no frequency control
|
||||
- **Requires sudo**: Writing to sysfs requires root privileges
|
||||
- **Location**: Bottom right corner of the screen
|
||||
- **Use case**: System alerts, notifications, error indicators
|
||||
- **Not for audio**: The buzzer cannot play music or complex sounds - only simple beeps
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- See `AUDIO-CONFIGURATION-REPORT.md` for complete audio system information
|
||||
- reTerminal DM4 User Manual for hardware specifications
|
||||
660
archive/chromium-setup-legacy/FLASK-BUZZER-CONTROL.md
Normal file
660
archive/chromium-setup-legacy/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
|
||||
92
archive/chromium-setup-legacy/JIRA-SUMMARY.md
Normal file
92
archive/chromium-setup-legacy/JIRA-SUMMARY.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# reTerminal DM4 Buzzer Control - Jira Summary
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented buzzer control functionality for reTerminal DM4 device with test scripts and documentation.
|
||||
|
||||
## Device Information
|
||||
|
||||
- **Model:** reTerminal DM4 (Raspberry Pi CM4)
|
||||
- **Buzzer Location:** Bottom right corner of screen
|
||||
- **Control Path:** `/sys/class/leds/usr-buzzer/brightness`
|
||||
- **Control Method:** Linux LED subsystem (on/off only)
|
||||
|
||||
## Implementation
|
||||
|
||||
### Test Scripts Created
|
||||
|
||||
1. **Bash Test Script** (`test_buzzer.sh`)
|
||||
- Location: `/home/pi/buzzer/test_buzzer.sh` on device
|
||||
- Tests: 8 different buzzer patterns (single, double, triple, long, rapid, slow, success, error)
|
||||
- Usage: `ssh guard "/home/pi/buzzer/test_buzzer.sh"` or `/home/pi/buzzer/test_buzzer.sh`
|
||||
|
||||
2. **Python Test Script** (`test_buzzer.py`)
|
||||
- Location: `/home/pi/buzzer/test_buzzer.py` on device
|
||||
- Same test patterns as bash script
|
||||
- Usage: `ssh guard "python3 /home/pi/buzzer/test_buzzer.py"` or `python3 /home/pi/buzzer/test_buzzer.py`
|
||||
|
||||
### Basic Control Commands
|
||||
|
||||
```bash
|
||||
# Turn ON
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
|
||||
# Turn OFF
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness
|
||||
|
||||
# Check Status
|
||||
cat /sys/class/leds/usr-buzzer/brightness # 0=OFF, 1/255=ON
|
||||
```
|
||||
|
||||
### Python Control Example
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
BUZZER = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
def beep(duration=0.2):
|
||||
subprocess.run(['sudo', 'tee', BUZZER], input='1', text=True)
|
||||
time.sleep(duration)
|
||||
subprocess.run(['sudo', 'tee', BUZZER], input='0', text=True)
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
- **Success Alert:** 2 short beeps
|
||||
- **Error Alert:** 3 fast beeps
|
||||
- **Notification:** 1 short beep
|
||||
- **Warning:** 1 long beep
|
||||
|
||||
## Requirements
|
||||
|
||||
- `sudo` privileges required
|
||||
- Test scripts available on device at `/tmp/`
|
||||
- Buzzer is simple on/off (no volume/frequency control)
|
||||
|
||||
## Documentation
|
||||
|
||||
- **Quick Reference:** `BUZZER-CONTROL.md` - Concise guide with commands and test script references
|
||||
- **Detailed Guide:** `BUZZER-CONTROL-SIMPLE.md` - Comprehensive documentation
|
||||
- **Flask API:** `FLASK-BUZZER-CONTROL.md` - Web API implementation
|
||||
|
||||
## Testing
|
||||
|
||||
Both test scripts have been uploaded to the device and verified working. Scripts test:
|
||||
- Single beep patterns
|
||||
- Multiple beep patterns
|
||||
- Different timing intervals
|
||||
- Common alert patterns (success, error)
|
||||
|
||||
## Status
|
||||
|
||||
✅ **Complete** - Test scripts created, uploaded to device, and documentation provided.
|
||||
|
||||
## Files
|
||||
|
||||
- `test_buzzer.sh` - Bash test script
|
||||
- `test_buzzer.py` - Python test script
|
||||
- `BUZZER-CONTROL.md` - Quick reference documentation
|
||||
- `BUZZER-CONTROL-SIMPLE.md` - Detailed documentation
|
||||
- `FLASK-BUZZER-CONTROL.md` - Flask API documentation
|
||||
268
archive/chromium-setup-legacy/KDE-INSTALLATION-GUIDE.md
Normal file
268
archive/chromium-setup-legacy/KDE-INSTALLATION-GUIDE.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# KDE Plasma Installation and Revert Guide for reTerminal DM4
|
||||
|
||||
## Device Information
|
||||
- **Model**: Raspberry Pi Compute Module 4 (CM4)
|
||||
- **CPU**: Cortex-A72 (4 cores)
|
||||
- **RAM**: 8GB
|
||||
- **Current Desktop**: LXDE with Openbox
|
||||
- **Display**: 10-inch, 1280x800
|
||||
|
||||
## Pre-Installation State
|
||||
|
||||
### Current Desktop Packages
|
||||
- libobrender32v5
|
||||
- libobt2v5:arm64
|
||||
- lxsession
|
||||
- lxsession-data
|
||||
- lxsession-logout
|
||||
- openbox
|
||||
- pcmanfm
|
||||
|
||||
### Current Session Manager
|
||||
- Default: `/usr/bin/startx-rpd` (priority 90)
|
||||
- Available: `/usr/bin/lxsession` (priority 49)
|
||||
- Available: `/usr/bin/openbox-session` (priority 40)
|
||||
- Current mode: auto (points to startx-rpd)
|
||||
|
||||
### Available Desktop Sessions
|
||||
- lightdm-xsession.desktop
|
||||
- openbox.desktop
|
||||
- rpd-x.desktop
|
||||
|
||||
### Installation Date
|
||||
- Date: 2026-01-09
|
||||
|
||||
---
|
||||
|
||||
## Installation Steps
|
||||
|
||||
### Step 1: Update System
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
```
|
||||
|
||||
### Step 2: Install KDE Plasma
|
||||
```bash
|
||||
# Install KDE Plasma desktop (lightweight version)
|
||||
sudo apt-get install -y kde-plasma-desktop
|
||||
|
||||
# Install on-screen keyboard for touchscreen
|
||||
sudo apt-get install -y maliit-keyboard
|
||||
|
||||
# Install additional KDE utilities
|
||||
sudo apt-get install -y kde-standard
|
||||
```
|
||||
|
||||
### Step 3: Configure Default Desktop
|
||||
|
||||
**Option A: Switch at Login (Recommended - Safest)**
|
||||
1. Log out of current session
|
||||
2. At login screen, click on the session/desktop selector (usually in top-right corner)
|
||||
3. Select "Plasma (X11)" or "plasmax11"
|
||||
4. Log in
|
||||
|
||||
**Option B: Set as Default**
|
||||
```bash
|
||||
# Set KDE as default desktop session
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select: /usr/bin/startplasma-x11 (priority 40)
|
||||
```
|
||||
|
||||
**Note**: KDE Plasma X11 session is available at `/usr/share/xsessions/plasmax11.desktop`
|
||||
|
||||
### Step 4: Configure Touchscreen Settings
|
||||
|
||||
After logging into KDE:
|
||||
|
||||
1. **Enable On-Screen Keyboard:**
|
||||
- System Settings > Input Devices > Virtual Keyboard
|
||||
- Enable "Show on-screen keyboard when needed"
|
||||
|
||||
2. **Adjust Touch Settings:**
|
||||
- System Settings > Input Devices > Touchpad/Touchscreen
|
||||
- Adjust touch sensitivity
|
||||
- Enable tap-to-click
|
||||
|
||||
3. **UI Scaling for Touch:**
|
||||
- System Settings > Display and Monitor
|
||||
- Set scale to 125% or 150% for better touch targets
|
||||
|
||||
4. **Disable Heavy Effects (for better performance):**
|
||||
- System Settings > Workspace Behavior > Desktop Effects
|
||||
- Disable resource-intensive effects
|
||||
- Keep basic animations enabled
|
||||
|
||||
### Step 5: Reboot
|
||||
```bash
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-Installation Verification
|
||||
|
||||
After reboot, verify KDE is working:
|
||||
```bash
|
||||
echo $XDG_CURRENT_DESKTOP
|
||||
# Should show: KDE or plasma
|
||||
|
||||
ps aux | grep -i plasma
|
||||
# Should show KDE processes running
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cloud-Init (Automated Install)
|
||||
|
||||
For EMMC provisioning you can install KDE, set it as the default session, and enable touch options automatically using cloud-init. Use the example that includes KDE and touch:
|
||||
|
||||
- **File:** `emmc-provisioning/cloud-init/user-data-kiosk-username-ssh.example`
|
||||
|
||||
It installs `kde-plasma-desktop`, `maliit-keyboard`, and `xinput-calibrator`; sets LightDM default session to Plasma (X11); writes KDE config for touch-friendly scaling and touch-point feedback; and autostarts the on-screen keyboard. Chromium kiosk and SSH are configured in the same example. Replace the password hash and use it as your cloud-init `user-data` when building the image.
|
||||
|
||||
---
|
||||
|
||||
## Reverting to LXDE
|
||||
|
||||
### Method 1: Switch at Login Screen (Easiest)
|
||||
|
||||
1. Log out of current session
|
||||
2. At the login screen, click on the session/desktop selector (usually top-right)
|
||||
3. Select "Openbox" or "RPD-X" session
|
||||
4. Log in
|
||||
|
||||
### Method 1b: Use Revert Script
|
||||
|
||||
A revert script is provided: `revert-to-lxde.sh`
|
||||
```bash
|
||||
# Copy script to device and run
|
||||
./revert-to-lxde.sh
|
||||
```
|
||||
|
||||
### Method 2: Change Default Session
|
||||
|
||||
```bash
|
||||
# Set LXDE as default
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select LXDE option (usually /usr/bin/startlxde or similar)
|
||||
```
|
||||
|
||||
### Method 3: Remove KDE (Complete Revert)
|
||||
|
||||
If you want to completely remove KDE:
|
||||
|
||||
```bash
|
||||
# Remove KDE packages
|
||||
sudo apt-get remove --purge kde-plasma-desktop kde-standard
|
||||
sudo apt-get autoremove -y
|
||||
sudo apt-get autoclean
|
||||
|
||||
# Restore LXDE as default
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select LXDE
|
||||
|
||||
# Reboot
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
### Method 4: Reinstall LXDE (if removed)
|
||||
|
||||
If LXDE was accidentally removed:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install --reinstall lxde
|
||||
sudo update-alternatives --config x-session-manager
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### KDE Won't Start
|
||||
```bash
|
||||
# Check if KDE is installed
|
||||
dpkg -l | grep kde-plasma
|
||||
|
||||
# Check available sessions
|
||||
ls /usr/share/xsessions/
|
||||
|
||||
# Try starting KDE manually
|
||||
startplasma-wayland
|
||||
# or
|
||||
startplasma-x11
|
||||
```
|
||||
|
||||
### Performance Issues
|
||||
- Disable desktop effects in System Settings
|
||||
- Reduce animation duration
|
||||
- Use X11 instead of Wayland (if Wayland is causing issues)
|
||||
|
||||
### Touchscreen Not Working
|
||||
```bash
|
||||
# Check touchscreen device
|
||||
xinput list
|
||||
|
||||
# Calibrate touchscreen
|
||||
sudo apt-get install xinput-calibrator
|
||||
xinput_calibrator
|
||||
```
|
||||
|
||||
### Chromium Autostart Issues
|
||||
Your existing Chromium autostart configuration should work with KDE.
|
||||
If it doesn't, check:
|
||||
```bash
|
||||
cat ~/.config/autostart/chromium-kiosk.desktop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- **Expected RAM Usage**: 400-800MB for KDE Plasma
|
||||
- **Expected CPU Usage**: Low to moderate (Cortex-A72 handles it well)
|
||||
- **Boot Time**: Slightly longer than LXDE (~10-15 seconds more)
|
||||
|
||||
---
|
||||
|
||||
## Files Modified/Created
|
||||
|
||||
- `/usr/share/xsessions/` - Desktop session files
|
||||
- `~/.config/plasma-org.kde.plasma.desktop-appletsrc` - KDE panel configuration
|
||||
- `~/.config/kwinrc` - Window manager settings
|
||||
- `~/.config/plasmarc` - Plasma settings
|
||||
|
||||
---
|
||||
|
||||
## Backup Before Installation
|
||||
|
||||
To create a backup of current desktop settings:
|
||||
```bash
|
||||
# Backup current desktop configuration
|
||||
mkdir -p ~/desktop-backup
|
||||
cp -r ~/.config/lx* ~/desktop-backup/ 2>/dev/null
|
||||
cp -r ~/.config/openbox ~/desktop-backup/ 2>/dev/null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollback Checklist
|
||||
|
||||
If you need to revert:
|
||||
- [ ] Switch session at login screen (easiest)
|
||||
- [ ] Change default session via update-alternatives
|
||||
- [ ] Remove KDE packages if needed
|
||||
- [ ] Verify LXDE is working
|
||||
- [ ] Check Chromium autostart still works
|
||||
- [ ] Verify touchscreen calibration
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
1. Check system logs: `journalctl -xe`
|
||||
2. Check X/Wayland logs: `~/.xsession-errors`
|
||||
3. Verify packages: `dpkg -l | grep kde`
|
||||
4. Check available sessions: `ls /usr/share/xsessions/`
|
||||
210
archive/chromium-setup-legacy/KDE-INSTALLATION-SUMMARY.md
Normal file
210
archive/chromium-setup-legacy/KDE-INSTALLATION-SUMMARY.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# KDE Plasma Installation Summary
|
||||
|
||||
## Installation Status: ✅ COMPLETE
|
||||
|
||||
**Date**: 2026-01-09
|
||||
**Device**: Raspberry Pi Compute Module 4 (8GB RAM)
|
||||
**Previous Desktop**: LXDE with Openbox
|
||||
**New Desktop**: KDE Plasma (X11)
|
||||
|
||||
---
|
||||
|
||||
## What Was Installed
|
||||
|
||||
### Core Packages
|
||||
- ✅ `kde-plasma-desktop` - KDE Plasma desktop environment
|
||||
- ✅ `maliit-keyboard` - On-screen keyboard for touchscreen
|
||||
- ✅ Additional KDE components and dependencies
|
||||
|
||||
### Available Sessions
|
||||
- ✅ **Plasma (X11)** - `/usr/share/xsessions/plasmax11.desktop`
|
||||
- ✅ **LXDE/Openbox** - Still available for revert
|
||||
- ✅ **RPD-X** - Original Raspberry Pi desktop
|
||||
|
||||
---
|
||||
|
||||
## Current System State
|
||||
|
||||
### Session Manager Status
|
||||
- **Current Default**: `/usr/bin/startx-rpd` (LXDE - unchanged)
|
||||
- **KDE Available**: `/usr/bin/startplasma-x11` (priority 40)
|
||||
- **LXDE Available**: `/usr/bin/lxsession` (priority 49)
|
||||
|
||||
### Backup Created
|
||||
- ✅ Desktop configuration backed up to: `~/desktop-backup/`
|
||||
- ✅ Session manager configuration saved
|
||||
|
||||
---
|
||||
|
||||
## How to Use KDE Plasma
|
||||
|
||||
### First Time Setup
|
||||
|
||||
1. **Log out of current session**
|
||||
```bash
|
||||
# Or simply reboot
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
2. **At login screen:**
|
||||
- Look for session/desktop selector (usually top-right corner)
|
||||
- Click and select **"Plasma (X11)"** or **"plasmax11"**
|
||||
- Enter credentials and log in
|
||||
|
||||
3. **First boot into KDE:**
|
||||
- KDE will initialize (may take 30-60 seconds first time)
|
||||
- You may see a welcome screen - you can skip it
|
||||
- Desktop will appear with KDE panel
|
||||
|
||||
4. **Configure for Touchscreen:**
|
||||
- System Settings > Input Devices > Virtual Keyboard
|
||||
- Enable "Show on-screen keyboard when needed"
|
||||
- System Settings > Display and Monitor
|
||||
- Set scale to 125% or 150% for better touch targets
|
||||
- System Settings > Workspace Behavior > Desktop Effects
|
||||
- Disable heavy effects for better performance
|
||||
|
||||
---
|
||||
|
||||
## Chromium Autostart
|
||||
|
||||
Your existing Chromium autostart configuration will work with KDE:
|
||||
- ✅ File: `~/.config/autostart/chromium-kiosk.desktop`
|
||||
- ✅ Script: `~/start-chromium.sh`
|
||||
|
||||
Chromium will start automatically when KDE desktop loads.
|
||||
|
||||
---
|
||||
|
||||
## Reverting to LXDE
|
||||
|
||||
### Quick Method (Recommended)
|
||||
1. Log out
|
||||
2. At login screen, select "Openbox" or "RPD-X" session
|
||||
3. Log in
|
||||
|
||||
### Using Revert Script
|
||||
```bash
|
||||
# Copy revert-to-lxde.sh to device
|
||||
scp revert-to-lxde.sh pi@10.130.60.253:~/
|
||||
ssh pi@10.130.60.253 "chmod +x ~/revert-to-lxde.sh && ~/revert-to-lxde.sh"
|
||||
```
|
||||
|
||||
### Manual Revert
|
||||
```bash
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select: /usr/bin/startx-rpd or /usr/bin/lxsession
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Expectations
|
||||
|
||||
### RAM Usage
|
||||
- **LXDE**: ~200-300MB
|
||||
- **KDE Plasma**: ~400-800MB
|
||||
- **Available**: ~6.7GB (plenty of headroom)
|
||||
|
||||
### CPU Usage
|
||||
- **Idle**: Low (< 5%)
|
||||
- **Active**: Moderate (10-30%)
|
||||
- **Heavy tasks**: May see some lag on Cortex-A72
|
||||
|
||||
### Boot Time
|
||||
- **LXDE**: ~20-30 seconds
|
||||
- **KDE Plasma**: ~30-45 seconds (first boot may be longer)
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### KDE Won't Start
|
||||
```bash
|
||||
# Check if installed
|
||||
dpkg -l | grep kde-plasma
|
||||
|
||||
# Check available sessions
|
||||
ls /usr/share/xsessions/
|
||||
|
||||
# Try manual start (if logged in via SSH)
|
||||
DISPLAY=:0 startplasma-x11
|
||||
```
|
||||
|
||||
### Performance Issues
|
||||
- Disable desktop effects: System Settings > Workspace Behavior > Desktop Effects
|
||||
- Reduce animations: System Settings > Workspace Behavior > General Behavior
|
||||
- Use X11 (already configured) instead of Wayland
|
||||
|
||||
### Touchscreen Issues
|
||||
```bash
|
||||
# Check touchscreen device
|
||||
xinput list
|
||||
|
||||
# Calibrate if needed
|
||||
sudo apt-get install xinput-calibrator
|
||||
xinput_calibrator
|
||||
```
|
||||
|
||||
### Chromium Not Starting
|
||||
- Check autostart file: `cat ~/.config/autostart/chromium-kiosk.desktop`
|
||||
- Check script: `cat ~/start-chromium.sh`
|
||||
- Manual test: `DISPLAY=:0 ~/start-chromium.sh`
|
||||
|
||||
---
|
||||
|
||||
## Files and Locations
|
||||
|
||||
### Configuration Files
|
||||
- KDE config: `~/.config/plasma*`
|
||||
- KDE panel: `~/.config/plasma-org.kde.plasma.desktop-appletsrc`
|
||||
- Window manager: `~/.config/kwinrc`
|
||||
- Session: `/usr/share/xsessions/plasmax11.desktop`
|
||||
|
||||
### Backup Location
|
||||
- `~/desktop-backup/` - Original LXDE configuration
|
||||
|
||||
### Documentation
|
||||
- `KDE-INSTALLATION-GUIDE.md` - Full installation and revert guide
|
||||
- `revert-to-lxde.sh` - Automated revert script
|
||||
- `TOUCHSCREEN-DESKTOP-OPTIONS.md` - Desktop environment comparison
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ **Reboot the device**
|
||||
```bash
|
||||
ssh pi@10.130.60.253 "sudo reboot"
|
||||
```
|
||||
|
||||
2. ✅ **Select KDE at login**
|
||||
- Choose "Plasma (X11)" from session selector
|
||||
|
||||
3. ✅ **Configure touchscreen settings**
|
||||
- Enable on-screen keyboard
|
||||
- Adjust UI scaling
|
||||
- Disable heavy effects
|
||||
|
||||
4. ✅ **Test Chromium autostart**
|
||||
- Verify Chromium starts in fullscreen
|
||||
- Check if it properly fills the screen
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
1. Check logs: `journalctl -xe`
|
||||
2. Check X session: `~/.xsession-errors`
|
||||
3. Verify packages: `dpkg -l | grep kde`
|
||||
4. Review documentation: `KDE-INSTALLATION-GUIDE.md`
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- KDE Plasma is installed but **not set as default** - you choose at login
|
||||
- LXDE is still available and can be selected at any time
|
||||
- All original configurations are backed up
|
||||
- Chromium autostart will work with both desktops
|
||||
- Performance should be acceptable on Pi 4 with 8GB RAM
|
||||
577
archive/chromium-setup-legacy/LED-CONTROL-GUIDE.md
Normal file
577
archive/chromium-setup-legacy/LED-CONTROL-GUIDE.md
Normal file
@@ -0,0 +1,577 @@
|
||||
# reTerminal DM4 LED Control Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The reTerminal DM4 has a user-controllable LED (`usr-led`) that can be used for status indicators, notifications, and visual feedback. The LED is controlled via the Linux LED subsystem, similar to the buzzer.
|
||||
|
||||
**⚠️ Important Note**: While the LED control interface exists and responds to commands, the physical LED may not be visible or installed on all reTerminal DM4 units. The control interface will accept commands and report correct values (0 for off, 255 for on), but if the LED doesn't appear to turn on physically, it may not be present on your specific model. Always verify with Seeed Studio documentation for your specific unit.
|
||||
|
||||
## LED Device Information
|
||||
|
||||
- **Device Path**: `/sys/class/leds/usr-led`
|
||||
- **Control Method**: Brightness control (0 = off, 1 = on)
|
||||
- **Type**: Simple on/off LED (max brightness = 1)
|
||||
- **Location**: User-controllable status LED
|
||||
- **Hardware**: Controlled via PCA9535 GPIO expander (I2C address 0x21)
|
||||
- **Note**: When setting brightness to 1, the system may report 255 (this is normal - it represents max brightness)
|
||||
|
||||
## Available LEDs on reTerminal DM4
|
||||
|
||||
The device has multiple LEDs, but the user-controllable one is:
|
||||
|
||||
- **`usr-led`**: User-controllable LED (primary LED for applications)
|
||||
- **`usr-buzzer`**: Buzzer (not an LED, but controlled similarly)
|
||||
- **`ACT`**: Activity LED (system-controlled, shows SD card activity)
|
||||
- **`PWR`**: Power LED (system-controlled)
|
||||
- **`lcd-pwr`**: LCD power LED (system-controlled)
|
||||
- **`audio-pwr`**: Audio power LED (system-controlled)
|
||||
|
||||
## Basic LED Control
|
||||
|
||||
### Simple On/Off
|
||||
|
||||
```bash
|
||||
# Turn LED ON
|
||||
echo 1 | sudo tee /sys/class/leds/usr-led/brightness
|
||||
|
||||
# Turn LED OFF
|
||||
echo 0 | sudo tee /sys/class/leds/usr-led/brightness
|
||||
```
|
||||
|
||||
### Check LED Status
|
||||
|
||||
```bash
|
||||
# Check current brightness (0 = off, 1 = on)
|
||||
cat /sys/class/leds/usr-led/brightness
|
||||
|
||||
# Check max brightness
|
||||
cat /sys/class/leds/usr-led/max_brightness
|
||||
```
|
||||
|
||||
## Python Control
|
||||
|
||||
### Simple Python Functions
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
LED_PATH = '/sys/class/leds/usr-led/brightness'
|
||||
|
||||
def led_on():
|
||||
"""Turn LED ON"""
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input='1', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
def led_off():
|
||||
"""Turn LED OFF"""
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input='0', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
def led_toggle():
|
||||
"""Toggle LED state"""
|
||||
with open(LED_PATH, 'r') as f:
|
||||
current = f.read().strip()
|
||||
new_state = '0' if current == '1' else '1'
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input=new_state, text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
def led_status():
|
||||
"""Get LED status"""
|
||||
with open(LED_PATH, 'r') as f:
|
||||
return 'ON' if f.read().strip() == '1' else 'OFF'
|
||||
```
|
||||
|
||||
### Blinking Pattern
|
||||
|
||||
```python
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
LED_PATH = '/sys/class/leds/usr-led/brightness'
|
||||
|
||||
def led_blink(count=5, on_time=0.2, off_time=0.2):
|
||||
"""Blink LED specified number of times"""
|
||||
for _ in range(count):
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input='1', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
time.sleep(on_time)
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input='0', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
time.sleep(off_time)
|
||||
|
||||
def led_pulse(duration=2, interval=0.1):
|
||||
"""Pulse LED (rapid on/off) for specified duration"""
|
||||
end_time = time.time() + duration
|
||||
while time.time() < end_time:
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input='1', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
time.sleep(interval)
|
||||
subprocess.run(['sudo', 'tee', LED_PATH],
|
||||
input='0', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
time.sleep(interval)
|
||||
```
|
||||
|
||||
## Using LED Triggers
|
||||
|
||||
The LED supports various triggers for automatic control:
|
||||
|
||||
### Available Triggers
|
||||
|
||||
```bash
|
||||
# List available triggers
|
||||
cat /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
Common triggers:
|
||||
- `none` - Manual control (default)
|
||||
- `timer` - Blink at specified intervals
|
||||
- `heartbeat` - Blink in heartbeat pattern
|
||||
- `default-on` - Always on
|
||||
- `cpu` - Blink based on CPU activity
|
||||
- `mmc0` - Blink on SD card activity
|
||||
|
||||
### Timer Trigger (Blinking)
|
||||
|
||||
```bash
|
||||
# Set timer trigger
|
||||
echo timer | sudo tee /sys/class/leds/usr-led/trigger
|
||||
|
||||
# Set delay_on (time LED is ON in milliseconds)
|
||||
echo 500 | sudo tee /sys/class/leds/usr-led/delay_on
|
||||
|
||||
# Set delay_off (time LED is OFF in milliseconds)
|
||||
echo 500 | sudo tee /sys/class/leds/usr-led/delay_off
|
||||
|
||||
# Disable timer (return to manual control)
|
||||
echo none | sudo tee /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
### Heartbeat Trigger
|
||||
|
||||
```bash
|
||||
# Set heartbeat pattern
|
||||
echo heartbeat | sudo tee /sys/class/leds/usr-led/trigger
|
||||
|
||||
# Return to manual control
|
||||
echo none | sudo tee /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
### CPU Activity Trigger
|
||||
|
||||
```bash
|
||||
# Blink based on CPU activity
|
||||
echo cpu | sudo tee /sys/class/leds/usr-led/trigger
|
||||
|
||||
# Return to manual control
|
||||
echo none | sudo tee /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
## Bash Script Examples
|
||||
|
||||
### Simple LED Control Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# LED control script
|
||||
|
||||
LED_PATH='/sys/class/leds/usr-led/brightness'
|
||||
|
||||
case "$1" in
|
||||
on)
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
echo "LED ON"
|
||||
;;
|
||||
off)
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
echo "LED OFF"
|
||||
;;
|
||||
toggle)
|
||||
current=$(cat $LED_PATH)
|
||||
if [ "$current" = "1" ]; then
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
echo "LED OFF"
|
||||
else
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
echo "LED ON"
|
||||
fi
|
||||
;;
|
||||
blink)
|
||||
count=${2:-5}
|
||||
for i in $(seq 1 $count); do
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.2
|
||||
done
|
||||
;;
|
||||
status)
|
||||
current=$(cat $LED_PATH)
|
||||
echo "LED is: $([ $current -eq 1 ] && echo 'ON' || echo 'OFF')"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {on|off|toggle|blink [count]|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
## Flask Integration
|
||||
|
||||
### Add LED Control to Flask App
|
||||
|
||||
```python
|
||||
from flask import Flask, jsonify, request
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
app = Flask(__name__)
|
||||
LED_PATH = '/sys/class/leds/usr-led/brightness'
|
||||
|
||||
class LEDController:
|
||||
def __init__(self):
|
||||
self.is_blinking = False
|
||||
self.blink_thread = None
|
||||
|
||||
def _write_led(self, value):
|
||||
"""Internal method to write to LED"""
|
||||
try:
|
||||
subprocess.run(['sudo', 'tee', LED_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 LED ON"""
|
||||
return self._write_led(1)
|
||||
|
||||
def off(self):
|
||||
"""Turn LED OFF"""
|
||||
return self._write_led(0)
|
||||
|
||||
def toggle(self):
|
||||
"""Toggle LED state"""
|
||||
try:
|
||||
result = subprocess.run(['cat', LED_PATH],
|
||||
capture_output=True, text=True, check=True)
|
||||
current = result.stdout.strip()
|
||||
new_state = '0' if current == '1' else '1'
|
||||
return self._write_led(new_state)
|
||||
except:
|
||||
return False
|
||||
|
||||
def blink(self, count=5, on_time=0.2, off_time=0.2):
|
||||
"""Blink LED specified number of times"""
|
||||
if self.is_blinking:
|
||||
return False
|
||||
|
||||
def _blink():
|
||||
self.is_blinking = True
|
||||
for _ in range(count):
|
||||
self.on()
|
||||
time.sleep(on_time)
|
||||
self.off()
|
||||
time.sleep(off_time)
|
||||
self.is_blinking = False
|
||||
|
||||
self.blink_thread = threading.Thread(target=_blink, daemon=True)
|
||||
self.blink_thread.start()
|
||||
return True
|
||||
|
||||
def pulse(self, duration=2, interval=0.1):
|
||||
"""Pulse LED for specified duration"""
|
||||
if self.is_blinking:
|
||||
return False
|
||||
|
||||
def _pulse():
|
||||
self.is_blinking = True
|
||||
end_time = time.time() + duration
|
||||
while time.time() < end_time:
|
||||
self.on()
|
||||
time.sleep(interval)
|
||||
self.off()
|
||||
time.sleep(interval)
|
||||
self.is_blinking = False
|
||||
|
||||
self.blink_thread = threading.Thread(target=_pulse, daemon=True)
|
||||
self.blink_thread.start()
|
||||
return True
|
||||
|
||||
def status(self):
|
||||
"""Get LED status"""
|
||||
try:
|
||||
result = subprocess.run(['cat', LED_PATH],
|
||||
capture_output=True, text=True, check=True)
|
||||
state = result.stdout.strip()
|
||||
return 'ON' if state == '1' else 'OFF'
|
||||
except:
|
||||
return 'UNKNOWN'
|
||||
|
||||
led = LEDController()
|
||||
|
||||
@app.route('/led/on', methods=['POST', 'GET'])
|
||||
def led_on():
|
||||
if led.on():
|
||||
return jsonify({'status': 'success', 'message': 'LED turned ON'})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to turn LED ON'}), 500
|
||||
|
||||
@app.route('/led/off', methods=['POST', 'GET'])
|
||||
def led_off():
|
||||
if led.off():
|
||||
return jsonify({'status': 'success', 'message': 'LED turned OFF'})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to turn LED OFF'}), 500
|
||||
|
||||
@app.route('/led/toggle', methods=['POST', 'GET'])
|
||||
def led_toggle():
|
||||
if led.toggle():
|
||||
return jsonify({'status': 'success', 'message': 'LED toggled'})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to toggle LED'}), 500
|
||||
|
||||
@app.route('/led/blink', methods=['POST', 'GET'])
|
||||
def led_blink():
|
||||
count = int(request.args.get('count', 5))
|
||||
on_time = float(request.args.get('on_time', 0.2))
|
||||
off_time = float(request.args.get('off_time', 0.2))
|
||||
|
||||
if led.blink(count, on_time, off_time):
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': f'LED blinking {count} times'
|
||||
})
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'LED is already blinking'
|
||||
}), 409
|
||||
|
||||
@app.route('/led/pulse', methods=['POST', 'GET'])
|
||||
def led_pulse():
|
||||
duration = float(request.args.get('duration', 2))
|
||||
interval = float(request.args.get('interval', 0.1))
|
||||
|
||||
if led.pulse(duration, interval):
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': f'LED pulsing for {duration} seconds'
|
||||
})
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'LED is already active'
|
||||
}), 409
|
||||
|
||||
@app.route('/led/status', methods=['GET'])
|
||||
def led_status():
|
||||
state = led.status()
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'led': state,
|
||||
'is_blinking': led.is_blinking
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
```
|
||||
|
||||
## Permission Setup
|
||||
|
||||
Same as buzzer control - see `FLASK-BUZZER-CONTROL.md` for detailed permission setup options.
|
||||
|
||||
### Quick Setup (sudoers method)
|
||||
|
||||
Create `/etc/sudoers.d/led-control`:
|
||||
|
||||
```
|
||||
# Allow LED control without password
|
||||
pi ALL=(ALL) NOPASSWD: /usr/bin/tee /sys/class/leds/usr-led/brightness
|
||||
```
|
||||
|
||||
### Udev Rules Method
|
||||
|
||||
Create `/etc/udev/rules.d/99-led.rules`:
|
||||
|
||||
```
|
||||
SUBSYSTEM=="leds", KERNEL=="usr-led", MODE="0666", GROUP="audio"
|
||||
```
|
||||
|
||||
Then add user to audio group and reload udev:
|
||||
```bash
|
||||
sudo usermod -a -G audio pi
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Status Indicator
|
||||
|
||||
```python
|
||||
def show_status(status):
|
||||
"""Show status with LED"""
|
||||
if status == 'success':
|
||||
led.on()
|
||||
time.sleep(0.5)
|
||||
led.off()
|
||||
elif status == 'error':
|
||||
led.blink(3, 0.1, 0.1) # Fast blink 3 times
|
||||
elif status == 'warning':
|
||||
led.blink(2, 0.3, 0.3) # Slow blink 2 times
|
||||
```
|
||||
|
||||
### Notification System
|
||||
|
||||
```python
|
||||
def notify(message_type):
|
||||
"""Notify user with LED patterns"""
|
||||
patterns = {
|
||||
'info': (1, 0.2, 0.2), # 1 blink, 0.2s on/off
|
||||
'success': (2, 0.1, 0.1), # 2 fast blinks
|
||||
'error': (3, 0.05, 0.05), # 3 very fast blinks
|
||||
'warning': (2, 0.3, 0.3), # 2 slow blinks
|
||||
}
|
||||
|
||||
count, on_time, off_time = patterns.get(message_type, (1, 0.2, 0.2))
|
||||
led.blink(count, on_time, off_time)
|
||||
```
|
||||
|
||||
### System Monitoring
|
||||
|
||||
```python
|
||||
import psutil
|
||||
|
||||
def monitor_system():
|
||||
"""Use LED to indicate system load"""
|
||||
cpu_percent = psutil.cpu_percent(interval=1)
|
||||
|
||||
if cpu_percent > 80:
|
||||
led.pulse(1, 0.05) # Fast pulse for high load
|
||||
elif cpu_percent > 50:
|
||||
led.pulse(1, 0.1) # Medium pulse
|
||||
else:
|
||||
led.off() # Off for low load
|
||||
```
|
||||
|
||||
## Combined LED and Buzzer Control
|
||||
|
||||
You can combine LED and buzzer for multi-modal notifications:
|
||||
|
||||
```python
|
||||
def alert(message_type):
|
||||
"""Alert with both LED and buzzer"""
|
||||
if message_type == 'critical':
|
||||
# Fast LED blink + buzzer beep
|
||||
threading.Thread(target=led.blink, args=(5, 0.1, 0.1), daemon=True).start()
|
||||
buzzer.beep(0.3)
|
||||
elif message_type == 'notification':
|
||||
# Slow LED blink
|
||||
threading.Thread(target=led.blink, args=(2, 0.3, 0.3), daemon=True).start()
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### LED Not Responding / Not Visible
|
||||
|
||||
**Important**: The LED control interface accepts commands, but the LED may not be physically visible or may be in a location that's not easily observable. Here are troubleshooting steps:
|
||||
|
||||
1. **Verify LED device exists:**
|
||||
```bash
|
||||
ls -la /sys/class/leds/usr-led/
|
||||
cat /sys/class/leds/usr-led/brightness
|
||||
```
|
||||
|
||||
2. **Ensure trigger is set to 'none' for manual control:**
|
||||
```bash
|
||||
# Check current trigger
|
||||
cat /sys/class/leds/usr-led/trigger
|
||||
|
||||
# Set to 'none' for manual control
|
||||
echo none | sudo tee /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
3. **Test with different values:**
|
||||
```bash
|
||||
# Turn OFF
|
||||
echo 0 | sudo tee /sys/class/leds/usr-led/brightness
|
||||
sleep 1
|
||||
|
||||
# Turn ON (may show as 255, which is normal)
|
||||
echo 1 | sudo tee /sys/class/leds/usr-led/brightness
|
||||
sleep 1
|
||||
|
||||
# Check what value was actually set
|
||||
cat /sys/class/leds/usr-led/brightness
|
||||
```
|
||||
|
||||
4. **Check if LED responds to trigger modes:**
|
||||
```bash
|
||||
# Try default-on trigger
|
||||
echo default-on | sudo tee /sys/class/leds/usr-led/trigger
|
||||
sleep 2
|
||||
|
||||
# Return to manual control
|
||||
echo none | sudo tee /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
5. **Physical verification:**
|
||||
- The LED may be located in a position that's not easily visible
|
||||
- Check around the screen bezel, especially near the buzzer location
|
||||
- The LED might be very dim or require specific viewing angle
|
||||
- Some reTerminal DM units may not have a physical LED installed
|
||||
|
||||
6. **Check GPIO control (alternative method):**
|
||||
```bash
|
||||
# The LED is controlled via PCA9535 GPIO expander
|
||||
# Base GPIO: 578
|
||||
# You can try controlling via GPIO directly if LED subsystem doesn't work
|
||||
```
|
||||
|
||||
### LED Always On/Off
|
||||
|
||||
- Check if a trigger is active: `cat /sys/class/leds/usr-led/trigger`
|
||||
- Set trigger to 'none' for manual control: `echo none | sudo tee /sys/class/leds/usr-led/trigger`
|
||||
- Verify brightness value: `cat /sys/class/leds/usr-led/brightness` (should be 0 for off, 255 for on)
|
||||
|
||||
### Brightness Shows 255 Instead of 1
|
||||
|
||||
**This is normal behavior!** When you set brightness to 1 (on), the system may report it as 255. This is because:
|
||||
- The LED subsystem internally uses 0-255 range
|
||||
- Setting 1 maps to maximum brightness (255)
|
||||
- This is expected behavior and doesn't indicate a problem
|
||||
|
||||
### LED Not Physically Visible
|
||||
|
||||
According to official documentation, the reTerminal DM may not have a user-controllable LED indicator in all models. If the LED doesn't appear to turn on:
|
||||
|
||||
1. **Verify your model**: Check if your specific reTerminal DM4 unit includes a user LED
|
||||
2. **Check documentation**: Refer to Seeed Studio's official documentation for your specific model
|
||||
3. **Contact support**: If LED control is required, contact Seeed Studio support for model-specific information
|
||||
|
||||
### Alternative: Use System LEDs
|
||||
|
||||
If `usr-led` is not available or not visible, you can use system LEDs for status indication:
|
||||
- **ACT LED**: Shows SD card activity (system-controlled)
|
||||
- **PWR LED**: Power indicator (system-controlled)
|
||||
|
||||
Note: System LEDs are typically read-only and controlled by the system.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- See `FLASK-BUZZER-CONTROL.md` for Flask integration examples
|
||||
- See `BUZZER-TEST-GUIDE.md` for buzzer control
|
||||
- See `AUDIO-CONFIGURATION-REPORT.md` for complete hardware info
|
||||
141
archive/chromium-setup-legacy/LED-SUMMARY.md
Normal file
141
archive/chromium-setup-legacy/LED-SUMMARY.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# reTerminal DM4 LED Summary
|
||||
|
||||
## Total LEDs Available: 9
|
||||
|
||||
### User-Controllable LEDs: **2**
|
||||
|
||||
1. **`usr-led`** - User-controllable LED
|
||||
- Path: `/sys/class/leds/usr-led/brightness`
|
||||
- Max brightness: 1 (on/off)
|
||||
- Status: ✅ Controllable (may not be physically visible on all models)
|
||||
- Control: `echo 1 | sudo tee /sys/class/leds/usr-led/brightness` (ON)
|
||||
- Control: `echo 0 | sudo tee /sys/class/leds/usr-led/brightness` (OFF)
|
||||
|
||||
2. **`usr-buzzer`** - User-controllable buzzer (controlled like LED)
|
||||
- Path: `/sys/class/leds/usr-buzzer/brightness`
|
||||
- Max brightness: 1 (on/off)
|
||||
- Status: ✅ Controllable and confirmed working
|
||||
- Control: `echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness` (ON)
|
||||
- Control: `echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness` (OFF)
|
||||
- Note: This is actually a buzzer, not an LED, but uses the same control interface
|
||||
|
||||
---
|
||||
|
||||
### System-Controlled LEDs: **7**
|
||||
|
||||
These LEDs are managed by the system and typically not user-controllable:
|
||||
|
||||
1. **`ACT`** - Activity LED (SD card activity)
|
||||
- Path: `/sys/class/leds/ACT/brightness`
|
||||
- Max brightness: 1
|
||||
- Trigger: `mmc0` (blinks on SD card activity)
|
||||
- Status: System-controlled (read-only for users)
|
||||
|
||||
2. **`audio-pwr`** - Audio power LED
|
||||
- Path: `/sys/class/leds/audio-pwr/brightness`
|
||||
- Max brightness: 1
|
||||
- Trigger: `none`
|
||||
- Status: System-controlled (indicates audio power state)
|
||||
|
||||
3. **`default-on`** - Virtual LED (always on)
|
||||
- Path: `/sys/class/leds/default-on/brightness`
|
||||
- Max brightness: 255
|
||||
- Trigger: `default-on`
|
||||
- Status: System-controlled virtual LED
|
||||
|
||||
4. **`lcd-pwr`** - LCD power LED
|
||||
- Path: `/sys/class/leds/lcd-pwr/brightness`
|
||||
- Max brightness: 1
|
||||
- Trigger: `none`
|
||||
- Status: System-controlled (indicates LCD power state)
|
||||
|
||||
5. **`mmc0`** - SD card activity LED (primary)
|
||||
- Path: `/sys/class/leds/mmc0/brightness`
|
||||
- Max brightness: 255
|
||||
- Trigger: `mmc0` (blinks on SD card activity)
|
||||
- Status: System-controlled
|
||||
|
||||
6. **`mmc0::`** - SD card activity LED (secondary)
|
||||
- Path: `/sys/class/leds/mmc0::/brightness`
|
||||
- Max brightness: 255
|
||||
- Trigger: `mmc0` (blinks on SD card activity)
|
||||
- Status: System-controlled
|
||||
|
||||
7. **`PWR`** - Power LED
|
||||
- Path: `/sys/class/leds/PWR/brightness`
|
||||
- Max brightness: 1
|
||||
- Trigger: `default-on` (always on when powered)
|
||||
- Status: System-controlled (indicates power state)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Count | LEDs |
|
||||
|----------|-------|------|
|
||||
| **User-Controllable** | **2** | `usr-led`, `usr-buzzer` |
|
||||
| **System-Controlled** | **7** | `ACT`, `audio-pwr`, `default-on`, `lcd-pwr`, `mmc0`, `mmc0::`, `PWR` |
|
||||
| **Total** | **9** | All LEDs in the system |
|
||||
|
||||
---
|
||||
|
||||
## Control Methods
|
||||
|
||||
### User-Controllable LEDs
|
||||
|
||||
**Both `usr-led` and `usr-buzzer` can be controlled using:**
|
||||
|
||||
```bash
|
||||
# Turn ON
|
||||
echo 1 | sudo tee /sys/class/leds/<led-name>/brightness
|
||||
|
||||
# Turn OFF
|
||||
echo 0 | sudo tee /sys/class/leds/<led-name>/brightness
|
||||
|
||||
# Check status
|
||||
cat /sys/class/leds/<led-name>/brightness
|
||||
```
|
||||
|
||||
### System LEDs
|
||||
|
||||
System LEDs are typically read-only and controlled by the kernel/system. Attempting to control them may:
|
||||
- Be ignored by the system
|
||||
- Be overridden by system triggers
|
||||
- Require disabling system triggers first (not recommended)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
1. **`usr-buzzer`** is technically a buzzer, not an LED, but uses the LED control interface
|
||||
2. **`usr-led`** may not be physically visible on all reTerminal DM4 models
|
||||
3. All LEDs require `sudo` for control (or proper udev rules/permissions)
|
||||
4. System LEDs are best left to system control for proper functionality
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# List all LEDs
|
||||
ls /sys/class/leds/
|
||||
|
||||
# Check LED status
|
||||
cat /sys/class/leds/<led-name>/brightness
|
||||
cat /sys/class/leds/<led-name>/trigger
|
||||
|
||||
# Control user LEDs
|
||||
echo 1 | sudo tee /sys/class/leds/usr-led/brightness # LED ON
|
||||
echo 0 | sudo tee /sys/class/leds/usr-led/brightness # LED OFF
|
||||
echo 1 | sudo tee /sys/class/leds/usr-buzzer/brightness # Buzzer ON
|
||||
echo 0 | sudo tee /sys/class/leds/usr-buzzer/brightness # Buzzer OFF
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- `LED-CONTROL-GUIDE.md` - Detailed LED control guide
|
||||
- `LED-TROUBLESHOOTING.md` - Troubleshooting LED issues
|
||||
- `FLASK-BUZZER-CONTROL.md` - Flask API for buzzer/LED control
|
||||
- `BUZZER-TEST-GUIDE.md` - Buzzer testing guide
|
||||
111
archive/chromium-setup-legacy/LED-TROUBLESHOOTING.md
Normal file
111
archive/chromium-setup-legacy/LED-TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# LED Troubleshooting Guide
|
||||
|
||||
## Issue: LED Control Works But LED Doesn't Turn On
|
||||
|
||||
### Symptoms
|
||||
- Commands execute successfully
|
||||
- Brightness value changes correctly (0 for off, 255 for on)
|
||||
- No physical LED visible or LED doesn't appear to turn on
|
||||
|
||||
### Possible Causes
|
||||
|
||||
1. **LED Not Installed on Your Model**
|
||||
- Not all reTerminal DM4 units may have a physical `usr-led` installed
|
||||
- The control interface exists in software but hardware may be missing
|
||||
- Check your specific model's documentation from Seeed Studio
|
||||
|
||||
2. **LED Location Not Visible**
|
||||
- The LED may be in a location that's difficult to see
|
||||
- May require specific viewing angle or lighting conditions
|
||||
- Check around the screen bezel, especially near buzzer location
|
||||
|
||||
3. **LED Requires Different Control Method**
|
||||
- Some LEDs may need to be controlled via GPIO directly
|
||||
- May require specific initialization sequence
|
||||
|
||||
### Verification Steps
|
||||
|
||||
1. **Check LED Device Exists:**
|
||||
```bash
|
||||
ls -la /sys/class/leds/usr-led/
|
||||
cat /sys/class/leds/usr-led/brightness
|
||||
cat /sys/class/leds/usr-led/max_brightness
|
||||
```
|
||||
|
||||
2. **Test Control Interface:**
|
||||
```bash
|
||||
# Turn OFF
|
||||
echo 0 | sudo tee /sys/class/leds/usr-led/brightness
|
||||
cat /sys/class/leds/usr-led/brightness # Should show 0
|
||||
|
||||
# Turn ON
|
||||
echo 1 | sudo tee /sys/class/leds/usr-led/brightness
|
||||
cat /sys/class/leds/usr-led/brightness # Should show 255
|
||||
```
|
||||
|
||||
3. **Test with Triggers:**
|
||||
```bash
|
||||
# Try default-on trigger
|
||||
echo default-on | sudo tee /sys/class/leds/usr-led/trigger
|
||||
sleep 2
|
||||
cat /sys/class/leds/usr-led/brightness # Should show 255
|
||||
|
||||
# Return to manual
|
||||
echo none | sudo tee /sys/class/leds/usr-led/trigger
|
||||
```
|
||||
|
||||
4. **Check Hardware Info:**
|
||||
```bash
|
||||
cat /sys/class/leds/usr-led/uevent
|
||||
dmesg | grep -i led
|
||||
```
|
||||
|
||||
### Official Documentation Status
|
||||
|
||||
According to Seeed Studio's official documentation:
|
||||
- The reTerminal DM has a **buzzer** that is well-documented and confirmed to work
|
||||
- The **LED indicator** (`usr-led`) control is mentioned but may not be physically present on all models
|
||||
- Some documentation sources indicate the LED may be system-managed rather than user-controllable
|
||||
|
||||
### Recommendations
|
||||
|
||||
1. **Contact Seeed Studio Support**
|
||||
- Verify if your specific model includes a user-controllable LED
|
||||
- Request model-specific documentation
|
||||
- Ask about LED location and visibility
|
||||
|
||||
2. **Use Alternative Indicators**
|
||||
- Use the buzzer for audio feedback
|
||||
- Use screen display for visual feedback
|
||||
- Use system LEDs (ACT, PWR) if available (though these are typically read-only)
|
||||
|
||||
3. **Check Model Variants**
|
||||
- Different reTerminal DM variants may have different hardware configurations
|
||||
- Verify your exact model number and compare with official specifications
|
||||
|
||||
### Alternative: GPIO Direct Control
|
||||
|
||||
If the LED subsystem doesn't work, you can try controlling via GPIO directly:
|
||||
|
||||
```bash
|
||||
# The LED is on PCA9535 GPIO expander (base GPIO 578)
|
||||
# You would need to export the specific GPIO pin
|
||||
# This requires knowing the exact pin number on the PCA9535
|
||||
|
||||
# Example (may need adjustment):
|
||||
GPIO_PIN=578 # Base + pin offset
|
||||
echo $GPIO_PIN | sudo tee /sys/class/gpio/export
|
||||
echo out | sudo tee /sys/class/gpio/gpio$GPIO_PIN/direction
|
||||
echo 1 | sudo tee /sys/class/gpio/gpio$GPIO_PIN/value # ON
|
||||
echo 0 | sudo tee /sys/class/gpio/gpio$GPIO_PIN/value # OFF
|
||||
```
|
||||
|
||||
**Note**: GPIO direct control requires knowing the exact pin mapping, which may not be documented.
|
||||
|
||||
### Conclusion
|
||||
|
||||
The LED control interface is functional and accepts commands correctly. If the LED doesn't physically turn on:
|
||||
- The control software is working
|
||||
- The hardware may not be present on your model
|
||||
- Contact Seeed Studio for model-specific information
|
||||
- Consider using buzzer or screen display as alternatives
|
||||
266
archive/chromium-setup-legacy/PAGE-SCALING-SOLUTIONS.md
Normal file
266
archive/chromium-setup-legacy/PAGE-SCALING-SOLUTIONS.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Page Scaling Solutions for Chromium Fullscreen
|
||||
|
||||
## Problem
|
||||
When Chromium loads in fullscreen, the page content requires scrolling (scrollbar appears) instead of fitting the viewport.
|
||||
|
||||
## Solutions
|
||||
|
||||
### Solution 1: Automatic Zoom via Script (Current Implementation) ⭐
|
||||
|
||||
The startup script now automatically zooms out the page after it loads using `xdotool`.
|
||||
|
||||
**How it works:**
|
||||
- Waits for Chromium to load
|
||||
- Sends keyboard commands to zoom out (Ctrl+-)
|
||||
- Adjusts zoom level to fit content
|
||||
|
||||
**Configuration:**
|
||||
Edit `~/start-chromium.sh` and adjust the zoom-out loop:
|
||||
```bash
|
||||
# Change the number of zoom-out commands (currently 3)
|
||||
for i in {1..3}; do # Increase number for larger pages
|
||||
xdotool key --window $WINDOW_ID ctrl+minus
|
||||
sleep 0.5
|
||||
done
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Works with any page
|
||||
- No page modification needed
|
||||
- Easy to adjust
|
||||
|
||||
**Cons:**
|
||||
- Requires xdotool
|
||||
- May need fine-tuning for different page sizes
|
||||
|
||||
---
|
||||
|
||||
### Solution 2: Edit the Page Itself (Best Long-term Solution)
|
||||
|
||||
If you control the web page at `http://127.0.0.1:8080`, add responsive CSS:
|
||||
|
||||
**Add to your HTML `<head>`:**
|
||||
```html
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
```
|
||||
|
||||
**Add responsive CSS:**
|
||||
```css
|
||||
/* Make page fit viewport */
|
||||
html, body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Scale content to fit */
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
/* Use CSS media queries for different screen sizes */
|
||||
@media (max-width: 1280px) {
|
||||
.container {
|
||||
transform: scale(calc(100vw / 1280));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Most reliable solution
|
||||
- Works without external tools
|
||||
- Better user experience
|
||||
|
||||
**Cons:**
|
||||
- Requires access to edit the page
|
||||
- May need to redesign layout
|
||||
|
||||
---
|
||||
|
||||
### Solution 3: JavaScript Injection
|
||||
|
||||
Inject JavaScript to auto-scale the page.
|
||||
|
||||
**Create a user script:**
|
||||
```javascript
|
||||
// Auto-fit page to viewport
|
||||
(function() {
|
||||
function autoFit() {
|
||||
const vw = window.innerWidth;
|
||||
const vh = window.innerHeight;
|
||||
const pw = document.body.scrollWidth;
|
||||
const ph = document.body.scrollHeight;
|
||||
|
||||
const zoom = Math.min(vw/pw, vh/ph, 1.0);
|
||||
|
||||
if (zoom < 1.0) {
|
||||
document.body.style.transform = 'scale(' + zoom + ')';
|
||||
document.body.style.transformOrigin = 'top left';
|
||||
document.body.style.width = (100/zoom) + '%';
|
||||
document.body.style.height = (100/zoom) + '%';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', autoFit);
|
||||
} else {
|
||||
autoFit();
|
||||
}
|
||||
window.addEventListener('resize', autoFit);
|
||||
setTimeout(autoFit, 2000);
|
||||
})();
|
||||
```
|
||||
|
||||
**To use:**
|
||||
1. Install a userscript extension (like Tampermonkey)
|
||||
2. Create a script that runs on `http://127.0.0.1:8080`
|
||||
3. Paste the JavaScript above
|
||||
|
||||
**Pros:**
|
||||
- Works automatically
|
||||
- No page modification needed
|
||||
- Can be fine-tuned
|
||||
|
||||
**Cons:**
|
||||
- Requires browser extension
|
||||
- May conflict with page JavaScript
|
||||
|
||||
---
|
||||
|
||||
### Solution 4: Chromium User Stylesheet
|
||||
|
||||
Use a custom CSS file to override page styles.
|
||||
|
||||
**Location:** `~/.config/chromium/Default/User StyleSheets/Custom.css`
|
||||
|
||||
**Content:**
|
||||
```css
|
||||
/* Hide scrollbars and scale content */
|
||||
html, body {
|
||||
overflow: hidden !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
}
|
||||
|
||||
/* Scale body to fit */
|
||||
body {
|
||||
transform: scale(0.8) !important; /* Adjust value as needed */
|
||||
transform-origin: top left !important;
|
||||
width: 125% !important; /* Inverse of scale */
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Simple CSS solution
|
||||
- No JavaScript needed
|
||||
|
||||
**Cons:**
|
||||
- Fixed scale value (not dynamic)
|
||||
- May break some layouts
|
||||
|
||||
---
|
||||
|
||||
### Solution 5: Chromium Zoom Flag
|
||||
|
||||
Use Chromium's force-device-scale-factor flag.
|
||||
|
||||
**Add to startup script:**
|
||||
```bash
|
||||
--force-device-scale-factor=0.8 # Adjust value (0.5-2.0)
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Built-in Chromium feature
|
||||
- Simple to use
|
||||
|
||||
**Cons:**
|
||||
- Affects entire browser, not just page
|
||||
- May make UI elements too small
|
||||
|
||||
---
|
||||
|
||||
## Recommended Approach
|
||||
|
||||
### For Immediate Fix (No Page Editing):
|
||||
✅ **Use Solution 1** (already implemented in script)
|
||||
- Automatic zoom via xdotool
|
||||
- Works immediately
|
||||
- Adjust zoom level in script if needed
|
||||
|
||||
### For Long-term Solution:
|
||||
✅ **Use Solution 2** (Edit the page)
|
||||
- Add responsive viewport meta tag
|
||||
- Add responsive CSS
|
||||
- Best user experience
|
||||
- No external dependencies
|
||||
|
||||
---
|
||||
|
||||
## Testing and Adjustment
|
||||
|
||||
### Test Current Implementation:
|
||||
1. Reboot or restart Chromium:
|
||||
```bash
|
||||
ssh pi@10.130.60.253 "pkill chromium && sleep 2 && DISPLAY=:0 ~/start-chromium.sh"
|
||||
```
|
||||
|
||||
2. Check if scrollbar is gone
|
||||
3. Verify content fits viewport
|
||||
|
||||
### Adjust Zoom Level:
|
||||
|
||||
If content is too small or still scrolling:
|
||||
|
||||
**Edit the script:**
|
||||
```bash
|
||||
ssh pi@10.130.60.253 "nano ~/start-chromium.sh"
|
||||
```
|
||||
|
||||
**Find this section and adjust:**
|
||||
```bash
|
||||
# Increase number for more zoom-out (smaller content)
|
||||
for i in {1..5}; do # Changed from 3 to 5
|
||||
xdotool key --window $WINDOW_ID ctrl+minus
|
||||
sleep 0.5
|
||||
done
|
||||
```
|
||||
|
||||
**Or decrease for less zoom-out:**
|
||||
```bash
|
||||
for i in {1..2}; do # Less zoom-out
|
||||
xdotool key --window $WINDOW_ID ctrl+minus
|
||||
sleep 0.5
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Scrollbar Still Appears:
|
||||
1. Increase zoom-out commands in script
|
||||
2. Check if page has fixed-width elements
|
||||
3. Consider editing the page CSS
|
||||
|
||||
### Content Too Small:
|
||||
1. Decrease zoom-out commands
|
||||
2. Or use zoom-in: `xdotool key --window $WINDOW_ID ctrl+plus`
|
||||
|
||||
### Zoom Not Working:
|
||||
1. Verify xdotool is installed: `which xdotool`
|
||||
2. Check window ID is found: Add `echo $WINDOW_ID` to script
|
||||
3. Try manual test: `xdotool key ctrl+minus`
|
||||
|
||||
---
|
||||
|
||||
## Current Script Location
|
||||
|
||||
- **Device**: `~/start-chromium.sh`
|
||||
- **Local**: `chromium-setup/start-chromium.sh`
|
||||
|
||||
The script now includes automatic page scaling via xdotool zoom commands.
|
||||
256
archive/chromium-setup-legacy/TOUCHSCREEN-DESKTOP-OPTIONS.md
Normal file
256
archive/chromium-setup-legacy/TOUCHSCREEN-DESKTOP-OPTIONS.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Touchscreen-Friendly Desktop Environments for reTerminal DM4
|
||||
|
||||
## Current Setup
|
||||
- **Screen**: 10-inch, 1280x800 resolution
|
||||
- **Current DE**: LXDE with Openbox
|
||||
- **Display Server**: Wayland (with Xwayland)
|
||||
|
||||
## Recommended Desktop Environments
|
||||
|
||||
### 1. KDE Plasma (Best for Touchscreen) ⭐ RECOMMENDED
|
||||
|
||||
**Pros:**
|
||||
- Excellent touch support with large touch targets
|
||||
- Built-in on-screen keyboard (Maliit)
|
||||
- Gesture support
|
||||
- Scalable UI elements
|
||||
- Modern, polished interface
|
||||
- Good performance on Raspberry Pi 5
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install kde-plasma-desktop
|
||||
# Or for full KDE experience:
|
||||
sudo apt-get install kde-standard
|
||||
```
|
||||
|
||||
**After installation:**
|
||||
```bash
|
||||
# Set KDE as default desktop
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select KDE/Plasma option
|
||||
|
||||
# Enable touchscreen optimizations
|
||||
# In System Settings > Workspace Behavior > Desktop Effects
|
||||
# Enable touch gestures and adjust touch settings
|
||||
```
|
||||
|
||||
**Size**: ~500-800 MB
|
||||
|
||||
---
|
||||
|
||||
### 2. GNOME (Good Touch Support)
|
||||
|
||||
**Pros:**
|
||||
- Excellent touch and gesture support
|
||||
- Large, finger-friendly interface
|
||||
- Built-in on-screen keyboard (Caribou)
|
||||
- Modern, tablet-like experience
|
||||
- Good Wayland support
|
||||
|
||||
**Cons:**
|
||||
- More resource-intensive than LXDE
|
||||
- May be slower on older Pi models
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install gnome-core
|
||||
# Or full GNOME:
|
||||
sudo apt-get install gnome
|
||||
```
|
||||
|
||||
**After installation:**
|
||||
```bash
|
||||
# Set GNOME as default
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select GNOME option
|
||||
|
||||
# Enable touch optimizations in GNOME Settings
|
||||
# Settings > Universal Access > Enable on-screen keyboard
|
||||
```
|
||||
|
||||
**Size**: ~1-1.5 GB
|
||||
|
||||
---
|
||||
|
||||
### 3. XFCE with Touch Optimizations
|
||||
|
||||
**Pros:**
|
||||
- Lightweight but more touch-friendly than LXDE
|
||||
- Customizable
|
||||
- Good performance
|
||||
- Can be configured for touch
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install xfce4 xfce4-goodies
|
||||
|
||||
# Install on-screen keyboard
|
||||
sudo apt-get install onboard
|
||||
```
|
||||
|
||||
**Touch optimizations:**
|
||||
```bash
|
||||
# Increase icon sizes, touch targets
|
||||
# Configure in Settings > Appearance > Icons (set to 48px or larger)
|
||||
# Settings > Panel > Increase panel size
|
||||
```
|
||||
|
||||
**Size**: ~200-300 MB
|
||||
|
||||
---
|
||||
|
||||
### 4. Phosh (Mobile-First Desktop)
|
||||
|
||||
**Pros:**
|
||||
- Designed specifically for touch devices
|
||||
- Very lightweight
|
||||
- Mobile/tablet interface
|
||||
- Built-in on-screen keyboard
|
||||
|
||||
**Cons:**
|
||||
- Less mature on Raspberry Pi
|
||||
- May require more configuration
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install phosh phosh-tour
|
||||
```
|
||||
|
||||
**Size**: ~100-200 MB
|
||||
|
||||
---
|
||||
|
||||
### 5. Lomiri (Ubuntu Touch Desktop)
|
||||
|
||||
**Pros:**
|
||||
- Designed for touch from the ground up
|
||||
- Mobile-first interface
|
||||
- Gesture-based navigation
|
||||
|
||||
**Cons:**
|
||||
- May not be in Debian repositories
|
||||
- Requires more setup
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
# May need to add Ubuntu Touch repositories
|
||||
# Check availability in Debian repos first
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comparison Table
|
||||
|
||||
| Desktop Environment | Touch Support | Performance | Size | Ease of Setup |
|
||||
|---------------------|---------------|-------------|------|---------------|
|
||||
| **KDE Plasma** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Medium | Easy |
|
||||
| **GNOME** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Large | Easy |
|
||||
| **XFCE** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Small | Easy |
|
||||
| **Phosh** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Small | Medium |
|
||||
| **LXDE (Current)** | ⭐⭐ | ⭐⭐⭐⭐⭐ | Very Small | N/A |
|
||||
|
||||
---
|
||||
|
||||
## Recommended: KDE Plasma
|
||||
|
||||
For a 10-inch touchscreen, **KDE Plasma** offers the best balance of:
|
||||
- Touch-friendliness
|
||||
- Performance
|
||||
- Features
|
||||
- Ease of use
|
||||
|
||||
## Installation Steps for KDE Plasma
|
||||
|
||||
1. **Install KDE Plasma:**
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install kde-plasma-desktop
|
||||
```
|
||||
|
||||
2. **Install on-screen keyboard:**
|
||||
```bash
|
||||
sudo apt-get install maliit-keyboard
|
||||
```
|
||||
|
||||
3. **Set as default desktop:**
|
||||
```bash
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select: /usr/bin/startplasma-wayland or /usr/bin/startplasma-x11
|
||||
```
|
||||
|
||||
4. **Configure touch settings:**
|
||||
- System Settings > Input Devices > Touchpad/Touchscreen
|
||||
- Adjust touch sensitivity and gestures
|
||||
- Enable tap-to-click
|
||||
|
||||
5. **Enable on-screen keyboard:**
|
||||
- System Settings > Input Devices > Virtual Keyboard
|
||||
- Enable "Show on-screen keyboard when needed"
|
||||
|
||||
6. **Adjust UI scaling:**
|
||||
- System Settings > Display and Monitor
|
||||
- Set scale to 125% or 150% for better touch targets
|
||||
|
||||
7. **Reboot:**
|
||||
```bash
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
## Switching Back to LXDE
|
||||
|
||||
If you want to switch back:
|
||||
```bash
|
||||
sudo update-alternatives --config x-session-manager
|
||||
# Select LXDE option
|
||||
```
|
||||
|
||||
## Touchscreen Calibration (if needed)
|
||||
|
||||
```bash
|
||||
# Install calibration tool
|
||||
sudo apt-get install xinput-calibrator
|
||||
|
||||
# Run calibration
|
||||
xinput_calibrator
|
||||
|
||||
# Follow on-screen instructions to calibrate touch points
|
||||
```
|
||||
|
||||
## Additional Touch Optimizations
|
||||
|
||||
### Increase Touch Targets
|
||||
Edit `~/.gtkrc-2.0` and `~/.config/gtk-3.0/gtk.css`:
|
||||
```css
|
||||
* {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
}
|
||||
```
|
||||
|
||||
### Enable Touch Gestures
|
||||
- Install `libinput-gestures`:
|
||||
```bash
|
||||
sudo apt-get install libinput-gestures
|
||||
```
|
||||
|
||||
### Font Scaling
|
||||
- Increase system font size in desktop settings
|
||||
- Recommended: 12-14pt for 10-inch screens
|
||||
|
||||
## Notes
|
||||
|
||||
- **Wayland vs X11**: KDE Plasma and GNOME work well with Wayland, which has better touch support
|
||||
- **Performance**: KDE Plasma is well-optimized for Raspberry Pi 5
|
||||
- **Chromium**: Your existing Chromium setup will work with any desktop environment
|
||||
- **Autostart**: Your autostart configuration will work with any DE
|
||||
|
||||
## Resources
|
||||
|
||||
- KDE Plasma: https://kde.org/plasma-desktop/
|
||||
- GNOME: https://www.gnome.org/
|
||||
- Phosh: https://source.puri.sm/Librem5/phosh
|
||||
348
archive/chromium-setup-legacy/buzzer_control.html
Normal file
348
archive/chromium-setup-legacy/buzzer_control.html
Normal file
@@ -0,0 +1,348 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>reTerminal DM4 Buzzer Control</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
padding: 15px 25px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-on {
|
||||
background: #4CAF50;
|
||||
}
|
||||
|
||||
.btn-on:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
.btn-off {
|
||||
background: #f44336;
|
||||
}
|
||||
|
||||
.btn-off:hover {
|
||||
background: #da190b;
|
||||
}
|
||||
|
||||
.btn-beep {
|
||||
background: #2196F3;
|
||||
}
|
||||
|
||||
.btn-beep:hover {
|
||||
background: #0b7dda;
|
||||
}
|
||||
|
||||
.btn-starwars {
|
||||
background: #FF9800;
|
||||
}
|
||||
|
||||
.btn-starwars:hover {
|
||||
background: #e68900;
|
||||
}
|
||||
|
||||
.pattern-section {
|
||||
background: #f5f5f5;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pattern-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.pattern-input {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input[type="text"]:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #e3f2fd;
|
||||
border-left: 4px solid #2196F3;
|
||||
border-radius: 5px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background: #e8f5e9;
|
||||
border-left-color: #4CAF50;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background: #ffebee;
|
||||
border-left-color: #f44336;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
color: #666;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.info {
|
||||
background: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.info p {
|
||||
color: #856404;
|
||||
margin: 5px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🎵 reTerminal DM4 Buzzer Control</h1>
|
||||
<p class="subtitle">Control the built-in buzzer via web interface</p>
|
||||
|
||||
<div class="button-group">
|
||||
<button class="btn-on" onclick="buzzerOn()">🔊 Buzzer ON</button>
|
||||
<button class="btn-off" onclick="buzzerOff()">🔇 Buzzer OFF</button>
|
||||
<button class="btn-beep" onclick="beep(0.2)">🔔 Short Beep</button>
|
||||
<button class="btn-beep" onclick="beep(0.5)">🔔 Long Beep</button>
|
||||
<button class="btn-starwars" onclick="starwars()">⭐ Star Wars Theme</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-section">
|
||||
<h3>🎼 Custom Pattern</h3>
|
||||
<p style="color: #666; margin-bottom: 10px; font-size: 14px;">
|
||||
Enter pattern as comma-separated values: on_time,off_time,on_time,off_time,...
|
||||
<br>Example: 0.1,0.1,0.1,0.1,0.1,0.3 (two short beeps, pause, one beep)
|
||||
</p>
|
||||
<div class="pattern-input">
|
||||
<input type="text" id="pattern" placeholder="0.1,0.1,0.1,0.1,0.1,0.3" value="0.1,0.1,0.1,0.1,0.1,0.3">
|
||||
<button class="btn-beep" onclick="playPattern()" style="min-width: 150px;">Play Pattern</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status" id="status">
|
||||
<div class="status-text">Ready</div>
|
||||
<div class="status-message">Click a button to control the buzzer</div>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p><strong>API Endpoint:</strong> <span id="apiUrl">http://localhost:5000</span></p>
|
||||
<p><strong>Status:</strong> <span id="connectionStatus">Not connected</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Configuration
|
||||
const API_URL = window.location.hostname === ''
|
||||
? 'http://localhost:5000'
|
||||
: `http://${window.location.hostname}:5000`;
|
||||
|
||||
document.getElementById('apiUrl').textContent = API_URL;
|
||||
|
||||
function updateStatus(message, type = 'info') {
|
||||
const statusDiv = document.getElementById('status');
|
||||
statusDiv.className = `status ${type}`;
|
||||
const statusText = statusDiv.querySelector('.status-text');
|
||||
const statusMessage = statusDiv.querySelector('.status-message');
|
||||
|
||||
if (type === 'success') {
|
||||
statusText.textContent = '✓ Success';
|
||||
} else if (type === 'error') {
|
||||
statusText.textContent = '✗ Error';
|
||||
} else {
|
||||
statusText.textContent = 'Ready';
|
||||
}
|
||||
|
||||
statusMessage.textContent = message;
|
||||
}
|
||||
|
||||
function updateConnectionStatus(connected) {
|
||||
const statusEl = document.getElementById('connectionStatus');
|
||||
statusEl.textContent = connected ? 'Connected' : 'Not connected';
|
||||
statusEl.style.color = connected ? '#4CAF50' : '#f44336';
|
||||
}
|
||||
|
||||
async function makeRequest(endpoint, method = 'GET', body = null) {
|
||||
try {
|
||||
const options = {
|
||||
method: method,
|
||||
headers: {}
|
||||
};
|
||||
|
||||
if (body) {
|
||||
options.headers['Content-Type'] = 'application/json';
|
||||
options.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_URL}${endpoint}`, options);
|
||||
const data = await response.json();
|
||||
|
||||
updateConnectionStatus(true);
|
||||
return data;
|
||||
} catch (error) {
|
||||
updateConnectionStatus(false);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function buzzerOn() {
|
||||
try {
|
||||
const data = await makeRequest('/buzzer/on', 'POST');
|
||||
updateStatus(data.message, 'success');
|
||||
} catch (error) {
|
||||
updateStatus(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function buzzerOff() {
|
||||
try {
|
||||
const data = await makeRequest('/buzzer/off', 'POST');
|
||||
updateStatus(data.message, 'success');
|
||||
} catch (error) {
|
||||
updateStatus(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function beep(duration) {
|
||||
try {
|
||||
const data = await makeRequest(`/buzzer/beep?duration=${duration}`);
|
||||
updateStatus(data.message, 'success');
|
||||
} catch (error) {
|
||||
updateStatus(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function starwars() {
|
||||
try {
|
||||
const data = await makeRequest('/buzzer/starwars?duration=5');
|
||||
updateStatus(data.message, 'success');
|
||||
} catch (error) {
|
||||
updateStatus(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function playPattern() {
|
||||
const patternStr = document.getElementById('pattern').value.trim();
|
||||
if (!patternStr) {
|
||||
updateStatus('Please enter a pattern', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = patternStr.split(',');
|
||||
if (parts.length % 2 !== 0) {
|
||||
updateStatus('Pattern must have even number of values (on,off pairs)', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const pattern = [];
|
||||
for (let i = 0; i < parts.length; i += 2) {
|
||||
const onTime = parseFloat(parts[i]);
|
||||
const offTime = parseFloat(parts[i + 1]);
|
||||
if (isNaN(onTime) || isNaN(offTime)) {
|
||||
updateStatus('Invalid pattern format. Use numbers only.', 'error');
|
||||
return;
|
||||
}
|
||||
pattern.push([onTime, offTime]);
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await makeRequest('/buzzer/pattern', 'POST', { pattern });
|
||||
updateStatus(data.message, 'success');
|
||||
} catch (error) {
|
||||
updateStatus(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Check API connection on load
|
||||
window.addEventListener('load', async () => {
|
||||
try {
|
||||
const data = await makeRequest('/');
|
||||
updateConnectionStatus(true);
|
||||
updateStatus('API connected and ready', 'success');
|
||||
} catch (error) {
|
||||
updateConnectionStatus(false);
|
||||
updateStatus('Cannot connect to API. Make sure Flask app is running.', 'error');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
266
archive/chromium-setup-legacy/flask_buzzer_app.py
Normal file
266
archive/chromium-setup-legacy/flask_buzzer_app.py
Normal file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flask Buzzer Control Application for reTerminal DM4
|
||||
Provides REST API for controlling the built-in buzzer
|
||||
"""
|
||||
|
||||
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:
|
||||
"""Buzzer controller class with pattern support"""
|
||||
|
||||
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():
|
||||
"""API documentation endpoint"""
|
||||
return jsonify({
|
||||
'status': 'Buzzer Control API',
|
||||
'version': '1.0',
|
||||
'endpoints': {
|
||||
'/buzzer/on': {
|
||||
'method': 'POST, GET',
|
||||
'description': 'Turn buzzer ON'
|
||||
},
|
||||
'/buzzer/off': {
|
||||
'method': 'POST, GET',
|
||||
'description': 'Turn buzzer OFF'
|
||||
},
|
||||
'/buzzer/beep': {
|
||||
'method': 'GET',
|
||||
'description': 'Play a beep',
|
||||
'parameters': {'duration': 'float (0-5 seconds)'}
|
||||
},
|
||||
'/buzzer/pattern': {
|
||||
'method': 'POST',
|
||||
'description': 'Play custom pattern',
|
||||
'body': {'pattern': '[[on_time, off_time], ...]'}
|
||||
},
|
||||
'/buzzer/starwars': {
|
||||
'method': 'GET',
|
||||
'description': 'Play Star Wars theme',
|
||||
'parameters': {'duration': 'float (0-10 seconds)'}
|
||||
},
|
||||
'/buzzer/status': {
|
||||
'method': 'GET',
|
||||
'description': 'Get buzzer status'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@app.route('/buzzer/on', methods=['POST', 'GET'])
|
||||
def turn_on():
|
||||
"""Turn buzzer 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():
|
||||
"""Turn buzzer 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():
|
||||
"""Play a beep for specified duration"""
|
||||
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():
|
||||
"""Play a custom 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) as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Invalid pattern format: {str(e)}'
|
||||
}), 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():
|
||||
"""Play Star Wars theme"""
|
||||
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():
|
||||
"""Get buzzer 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__':
|
||||
print("Starting Buzzer Control API...")
|
||||
print("API available at http://0.0.0.0:5000")
|
||||
print("Documentation at http://0.0.0.0:5000/")
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
350
archive/chromium-setup-legacy/flask_led_buzzer_app.py
Normal file
350
archive/chromium-setup-legacy/flask_led_buzzer_app.py
Normal file
@@ -0,0 +1,350 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flask LED and Buzzer Control Application for reTerminal DM4
|
||||
Provides REST API for controlling both the LED and buzzer
|
||||
"""
|
||||
|
||||
from flask import Flask, jsonify, request
|
||||
import subprocess
|
||||
import time
|
||||
import threading
|
||||
|
||||
app = Flask(__name__)
|
||||
LED_PATH = '/sys/class/leds/usr-led/brightness'
|
||||
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
class LEDController:
|
||||
"""LED controller class"""
|
||||
|
||||
def __init__(self):
|
||||
self.is_active = False
|
||||
self.thread = None
|
||||
|
||||
def _write(self, value):
|
||||
"""Internal method to write to LED"""
|
||||
try:
|
||||
subprocess.run(['sudo', 'tee', LED_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 LED ON"""
|
||||
return self._write(1)
|
||||
|
||||
def off(self):
|
||||
"""Turn LED OFF"""
|
||||
return self._write(0)
|
||||
|
||||
def toggle(self):
|
||||
"""Toggle LED state"""
|
||||
try:
|
||||
result = subprocess.run(['cat', LED_PATH],
|
||||
capture_output=True, text=True, check=True)
|
||||
current = result.stdout.strip()
|
||||
new_state = '0' if current == '1' else '1'
|
||||
return self._write(new_state)
|
||||
except:
|
||||
return False
|
||||
|
||||
def blink(self, count=5, on_time=0.2, off_time=0.2):
|
||||
"""Blink LED specified number of times"""
|
||||
if self.is_active:
|
||||
return False
|
||||
|
||||
def _blink():
|
||||
self.is_active = True
|
||||
for _ in range(count):
|
||||
self.on()
|
||||
time.sleep(on_time)
|
||||
self.off()
|
||||
time.sleep(off_time)
|
||||
self.is_active = False
|
||||
|
||||
self.thread = threading.Thread(target=_blink, daemon=True)
|
||||
self.thread.start()
|
||||
return True
|
||||
|
||||
def pulse(self, duration=2, interval=0.1):
|
||||
"""Pulse LED for specified duration"""
|
||||
if self.is_active:
|
||||
return False
|
||||
|
||||
def _pulse():
|
||||
self.is_active = True
|
||||
end_time = time.time() + duration
|
||||
while time.time() < end_time:
|
||||
self.on()
|
||||
time.sleep(interval)
|
||||
self.off()
|
||||
time.sleep(interval)
|
||||
self.is_active = False
|
||||
|
||||
self.thread = threading.Thread(target=_pulse, daemon=True)
|
||||
self.thread.start()
|
||||
return True
|
||||
|
||||
def status(self):
|
||||
"""Get LED status"""
|
||||
try:
|
||||
result = subprocess.run(['cat', LED_PATH],
|
||||
capture_output=True, text=True, check=True)
|
||||
state = result.stdout.strip()
|
||||
return 'ON' if state == '1' else 'OFF'
|
||||
except:
|
||||
return 'UNKNOWN'
|
||||
|
||||
class BuzzerController:
|
||||
"""Buzzer controller class"""
|
||||
|
||||
def __init__(self):
|
||||
self.is_playing = False
|
||||
self.play_thread = None
|
||||
|
||||
def _write(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(1)
|
||||
|
||||
def off(self):
|
||||
"""Turn buzzer OFF"""
|
||||
return self._write(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"""
|
||||
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 status(self):
|
||||
"""Get buzzer status"""
|
||||
try:
|
||||
result = subprocess.run(['cat', BUZZER_PATH],
|
||||
capture_output=True, text=True, check=True)
|
||||
state = result.stdout.strip()
|
||||
return 'ON' if state == '1' else 'OFF'
|
||||
except:
|
||||
return 'UNKNOWN'
|
||||
|
||||
# Create global controllers
|
||||
led = LEDController()
|
||||
buzzer = BuzzerController()
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""API documentation endpoint"""
|
||||
return jsonify({
|
||||
'status': 'LED and Buzzer Control API',
|
||||
'version': '1.0',
|
||||
'endpoints': {
|
||||
'LED': {
|
||||
'/led/on': 'Turn LED ON (POST, GET)',
|
||||
'/led/off': 'Turn LED OFF (POST, GET)',
|
||||
'/led/toggle': 'Toggle LED (POST, GET)',
|
||||
'/led/blink': 'Blink LED (GET: ?count=5&on_time=0.2&off_time=0.2)',
|
||||
'/led/pulse': 'Pulse LED (GET: ?duration=2&interval=0.1)',
|
||||
'/led/status': 'Get LED status (GET)'
|
||||
},
|
||||
'Buzzer': {
|
||||
'/buzzer/on': 'Turn buzzer ON (POST, GET)',
|
||||
'/buzzer/off': 'Turn buzzer OFF (POST, GET)',
|
||||
'/buzzer/beep': 'Play beep (GET: ?duration=0.2)',
|
||||
'/buzzer/status': 'Get buzzer status (GET)'
|
||||
},
|
||||
'Combined': {
|
||||
'/alert': 'Alert with LED and buzzer (GET: ?type=info|success|error|warning)'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
# LED Endpoints
|
||||
@app.route('/led/on', methods=['POST', 'GET'])
|
||||
def led_on():
|
||||
"""Turn LED ON"""
|
||||
if led.on():
|
||||
return jsonify({'status': 'success', 'message': 'LED turned ON'})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to turn LED ON'}), 500
|
||||
|
||||
@app.route('/led/off', methods=['POST', 'GET'])
|
||||
def led_off():
|
||||
"""Turn LED OFF"""
|
||||
if led.off():
|
||||
return jsonify({'status': 'success', 'message': 'LED turned OFF'})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to turn LED OFF'}), 500
|
||||
|
||||
@app.route('/led/toggle', methods=['POST', 'GET'])
|
||||
def led_toggle():
|
||||
"""Toggle LED state"""
|
||||
if led.toggle():
|
||||
return jsonify({'status': 'success', 'message': 'LED toggled'})
|
||||
return jsonify({'status': 'error', 'message': 'Failed to toggle LED'}), 500
|
||||
|
||||
@app.route('/led/blink', methods=['POST', 'GET'])
|
||||
def led_blink():
|
||||
"""Blink LED"""
|
||||
count = int(request.args.get('count', 5))
|
||||
on_time = float(request.args.get('on_time', 0.2))
|
||||
off_time = float(request.args.get('off_time', 0.2))
|
||||
|
||||
if led.blink(count, on_time, off_time):
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': f'LED blinking {count} times'
|
||||
})
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'LED is already active'
|
||||
}), 409
|
||||
|
||||
@app.route('/led/pulse', methods=['POST', 'GET'])
|
||||
def led_pulse():
|
||||
"""Pulse LED"""
|
||||
duration = float(request.args.get('duration', 2))
|
||||
interval = float(request.args.get('interval', 0.1))
|
||||
|
||||
if led.pulse(duration, interval):
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': f'LED pulsing for {duration} seconds'
|
||||
})
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'LED is already active'
|
||||
}), 409
|
||||
|
||||
@app.route('/led/status', methods=['GET'])
|
||||
def led_status():
|
||||
"""Get LED status"""
|
||||
state = led.status()
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'led': state,
|
||||
'is_active': led.is_active
|
||||
})
|
||||
|
||||
# Buzzer Endpoints
|
||||
@app.route('/buzzer/on', methods=['POST', 'GET'])
|
||||
def buzzer_on():
|
||||
"""Turn buzzer 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 buzzer_off():
|
||||
"""Turn buzzer 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 buzzer_beep():
|
||||
"""Play a 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 buzzer_status():
|
||||
"""Get buzzer status"""
|
||||
state = buzzer.status()
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'buzzer': state,
|
||||
'is_playing': buzzer.is_playing
|
||||
})
|
||||
|
||||
# Combined Alert Endpoint
|
||||
@app.route('/alert', methods=['GET'])
|
||||
def alert():
|
||||
"""Alert with LED and buzzer"""
|
||||
alert_type = request.args.get('type', 'info')
|
||||
|
||||
patterns = {
|
||||
'info': {
|
||||
'led': (1, 0.2, 0.2), # 1 blink
|
||||
'buzzer': (0.1, 0.1) # Short beep
|
||||
},
|
||||
'success': {
|
||||
'led': (2, 0.1, 0.1), # 2 fast blinks
|
||||
'buzzer': (0.2, 0.1) # Medium beep
|
||||
},
|
||||
'warning': {
|
||||
'led': (2, 0.3, 0.3), # 2 slow blinks
|
||||
'buzzer': (0.3, 0.2) # Longer beep
|
||||
},
|
||||
'error': {
|
||||
'led': (3, 0.05, 0.05), # 3 very fast blinks
|
||||
'buzzer': (0.2, 0.05) # Fast beeps
|
||||
}
|
||||
}
|
||||
|
||||
if alert_type not in patterns:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Invalid alert type. Use: info, success, warning, error'
|
||||
}), 400
|
||||
|
||||
pattern = patterns[alert_type]
|
||||
|
||||
# Start LED blink
|
||||
count, on_time, off_time = pattern['led']
|
||||
led.blink(count, on_time, off_time)
|
||||
|
||||
# Play buzzer pattern
|
||||
beep_duration, pause = pattern['buzzer']
|
||||
buzzer_pattern = [(beep_duration, pause)] * count
|
||||
buzzer.play_pattern(buzzer_pattern)
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': f'Alert ({alert_type}) triggered'
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Starting LED and Buzzer Control API...")
|
||||
print("API available at http://0.0.0.0:5000")
|
||||
print("Documentation at http://0.0.0.0:5000/")
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||
4
archive/chromium-setup-legacy/requirements.txt
Normal file
4
archive/chromium-setup-legacy/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Flask==2.3.0
|
||||
flask-httpauth==4.8.0
|
||||
flask-limiter==3.0.0
|
||||
flask-cors==4.0.0
|
||||
46
archive/chromium-setup-legacy/revert-to-lxde.sh
Executable file
46
archive/chromium-setup-legacy/revert-to-lxde.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# Revert to LXDE Desktop Environment
|
||||
# This script helps revert from KDE Plasma back to LXDE
|
||||
|
||||
echo "=== Reverting to LXDE Desktop Environment ==="
|
||||
echo ""
|
||||
|
||||
# Check if running as root for some operations
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Some operations require sudo. You may be prompted for your password."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Method 1: Change default session
|
||||
echo "Step 1: Setting LXDE as default session..."
|
||||
echo "You will be prompted to select the session manager."
|
||||
echo ""
|
||||
sudo update-alternatives --config x-session-manager
|
||||
|
||||
# Method 2: Verify LXDE packages are still installed
|
||||
echo ""
|
||||
echo "Step 2: Verifying LXDE packages..."
|
||||
if dpkg -l | grep -q "lxsession"; then
|
||||
echo "✓ LXDE packages are installed"
|
||||
else
|
||||
echo "⚠ LXDE packages not found. Reinstalling..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install --reinstall lxde
|
||||
fi
|
||||
|
||||
# Method 3: Check available sessions
|
||||
echo ""
|
||||
echo "Step 3: Available desktop sessions:"
|
||||
ls -1 /usr/share/xsessions/ | grep -E "openbox|rpd|lxde" || echo "No LXDE sessions found"
|
||||
|
||||
echo ""
|
||||
echo "=== Revert Complete ==="
|
||||
echo ""
|
||||
echo "To use LXDE:"
|
||||
echo "1. Log out of current session"
|
||||
echo "2. At login screen, click on session/desktop selector"
|
||||
echo "3. Select 'Openbox' or 'RPD-X' session"
|
||||
echo "4. Log in"
|
||||
echo ""
|
||||
echo "Or reboot and select LXDE at login:"
|
||||
echo " sudo reboot"
|
||||
122
archive/chromium-setup-legacy/starwars_buzzer.sh
Normal file
122
archive/chromium-setup-legacy/starwars_buzzer.sh
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/bin/bash
|
||||
# Star Wars Theme Song - Buzzer Version
|
||||
# Note: Buzzer can only be on/off, so we use timing patterns to create rhythm
|
||||
# Volume is simulated at 10% by using very short pulse patterns (PWM-like effect)
|
||||
|
||||
BUZZER_PATH='/sys/class/leds/usr-buzzer/brightness'
|
||||
DURATION=5 # Total duration in seconds
|
||||
|
||||
# Function to play a note with 10% volume effect
|
||||
# Uses rapid on/off pulses to simulate lower volume
|
||||
# $1 = note duration in hundredths of a second (e.g., 5 = 0.05s)
|
||||
play_note() {
|
||||
local note_duration=$1
|
||||
# For 10% volume: 10ms on, 90ms off pattern
|
||||
# This creates a quieter, pulsing effect
|
||||
local cycles=$((note_duration * 2)) # Each cycle is ~50ms
|
||||
|
||||
for i in $(seq 1 $cycles); do
|
||||
echo 1 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
sleep 0.005 # 5ms on (10% duty cycle)
|
||||
echo 0 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
sleep 0.045 # 45ms off
|
||||
done
|
||||
}
|
||||
|
||||
# Function for silence/pause
|
||||
pause() {
|
||||
local duration=$1
|
||||
sleep $duration
|
||||
}
|
||||
|
||||
# Star Wars Theme Pattern
|
||||
# Using timing to represent the iconic opening sequence
|
||||
echo "Playing Star Wars Theme (5 seconds, 10% volume)..."
|
||||
echo "=================================================="
|
||||
echo ""
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# Opening sequence (famous opening notes)
|
||||
# Short-short-short-long pattern
|
||||
play_note 2 # Very short
|
||||
pause 0.05
|
||||
play_note 2
|
||||
pause 0.05
|
||||
play_note 2
|
||||
pause 0.1
|
||||
play_note 4 # Longer
|
||||
pause 0.1
|
||||
play_note 6 # Even longer
|
||||
pause 0.15
|
||||
|
||||
# Main theme rhythm - first phrase
|
||||
play_note 3
|
||||
pause 0.05
|
||||
play_note 3
|
||||
pause 0.05
|
||||
play_note 3
|
||||
pause 0.1
|
||||
play_note 5
|
||||
pause 0.1
|
||||
play_note 4
|
||||
pause 0.1
|
||||
play_note 3
|
||||
pause 0.1
|
||||
play_note 5
|
||||
pause 0.15
|
||||
|
||||
# Continue theme pattern - second phrase
|
||||
play_note 4
|
||||
pause 0.05
|
||||
play_note 4
|
||||
pause 0.05
|
||||
play_note 4
|
||||
pause 0.1
|
||||
play_note 6
|
||||
pause 0.1
|
||||
play_note 5
|
||||
pause 0.1
|
||||
play_note 4
|
||||
pause 0.1
|
||||
play_note 6
|
||||
pause 0.15
|
||||
|
||||
# Final sequence - third phrase
|
||||
play_note 5
|
||||
pause 0.05
|
||||
play_note 5
|
||||
pause 0.05
|
||||
play_note 5
|
||||
pause 0.1
|
||||
play_note 8
|
||||
pause 0.1
|
||||
play_note 6
|
||||
pause 0.1
|
||||
play_note 5
|
||||
pause 0.1
|
||||
play_note 10
|
||||
pause 0.2
|
||||
|
||||
# Fill remaining time to reach 5 seconds
|
||||
CURRENT_TIME=$(date +%s)
|
||||
ELAPSED=$((CURRENT_TIME - START_TIME))
|
||||
REMAINING=$((DURATION - ELAPSED))
|
||||
|
||||
if [ $REMAINING -gt 0 ]; then
|
||||
# Continue theme pattern for remaining time
|
||||
while [ $(date +%s) -lt $((START_TIME + DURATION)) ]; do
|
||||
play_note 4
|
||||
pause 0.1
|
||||
play_note 5
|
||||
pause 0.1
|
||||
play_note 4
|
||||
pause 0.15
|
||||
done
|
||||
fi
|
||||
|
||||
# Ensure buzzer is off
|
||||
echo 0 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
|
||||
echo ""
|
||||
echo "Star Wars theme complete!"
|
||||
116
archive/chromium-setup-legacy/test_buzzer.py
Executable file
116
archive/chromium-setup-legacy/test_buzzer.py
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Buzzer Test Script for reTerminal DM4
|
||||
Tests various buzzer patterns and functions
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
|
||||
BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
def buzzer_on():
|
||||
"""Turn buzzer ON"""
|
||||
subprocess.run(['sudo', 'tee', BUZZER_PATH],
|
||||
input='1', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
def buzzer_off():
|
||||
"""Turn buzzer OFF"""
|
||||
subprocess.run(['sudo', 'tee', BUZZER_PATH],
|
||||
input='0', text=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
def beep(duration=0.2):
|
||||
"""Play a single beep"""
|
||||
buzzer_on()
|
||||
time.sleep(duration)
|
||||
buzzer_off()
|
||||
|
||||
def blink(count=3, on_time=0.1, off_time=0.1):
|
||||
"""Blink buzzer multiple times"""
|
||||
for _ in range(count):
|
||||
buzzer_on()
|
||||
time.sleep(on_time)
|
||||
buzzer_off()
|
||||
time.sleep(off_time)
|
||||
|
||||
def get_status():
|
||||
"""Get current buzzer status"""
|
||||
try:
|
||||
result = subprocess.run(['cat', BUZZER_PATH],
|
||||
capture_output=True, text=True, check=True)
|
||||
return 'ON' if result.stdout.strip() in ['1', '255'] else 'OFF'
|
||||
except:
|
||||
return 'UNKNOWN'
|
||||
|
||||
def main():
|
||||
print("=" * 50)
|
||||
print(" reTerminal DM4 Buzzer Test Script (Python)")
|
||||
print("=" * 50)
|
||||
print()
|
||||
|
||||
# Test 1: Single beep
|
||||
print("Test 1: Single beep (0.2s)")
|
||||
beep(0.2)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 2: Double beep
|
||||
print("Test 2: Double beep")
|
||||
blink(2, 0.1, 0.1)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 3: Triple beep
|
||||
print("Test 3: Triple beep")
|
||||
blink(3, 0.1, 0.1)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 4: Long beep
|
||||
print("Test 4: Long beep (0.5s)")
|
||||
beep(0.5)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 5: Rapid beeps
|
||||
print("Test 5: Rapid beeps (5x)")
|
||||
blink(5, 0.05, 0.05)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 6: Slow beeps
|
||||
print("Test 6: Slow beeps (3x)")
|
||||
blink(3, 0.3, 0.3)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 7: Success pattern
|
||||
print("Test 7: Success pattern (2 short)")
|
||||
blink(2, 0.1, 0.1)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test 8: Error pattern
|
||||
print("Test 8: Error pattern (3 fast)")
|
||||
blink(3, 0.05, 0.05)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Ensure buzzer is off
|
||||
buzzer_off()
|
||||
|
||||
print()
|
||||
print("=" * 50)
|
||||
print(" Buzzer test complete!")
|
||||
print("=" * 50)
|
||||
print()
|
||||
print(f"Current buzzer status: {get_status()}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nTest interrupted by user")
|
||||
buzzer_off()
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(f"\n\nError: {e}")
|
||||
buzzer_off()
|
||||
sys.exit(1)
|
||||
82
archive/chromium-setup-legacy/test_buzzer.sh
Executable file
82
archive/chromium-setup-legacy/test_buzzer.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Buzzer Test Script for reTerminal DM4
|
||||
# Tests various buzzer patterns and functions
|
||||
|
||||
BUZZER_PATH='/sys/class/leds/usr-buzzer/brightness'
|
||||
|
||||
echo "=========================================="
|
||||
echo " reTerminal DM4 Buzzer Test Script"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Function to play a beep
|
||||
beep() {
|
||||
local duration=${1:-0.2}
|
||||
echo 1 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
sleep $duration
|
||||
echo 0 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# Function to blink buzzer
|
||||
blink() {
|
||||
local count=${1:-3}
|
||||
local on_time=${2:-0.1}
|
||||
local off_time=${3:-0.1}
|
||||
|
||||
for i in $(seq 1 $count); do
|
||||
echo 1 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
sleep $on_time
|
||||
echo 0 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
sleep $off_time
|
||||
done
|
||||
}
|
||||
|
||||
# Test 1: Single beep
|
||||
echo "Test 1: Single beep (0.2s)"
|
||||
beep 0.2
|
||||
sleep 0.5
|
||||
|
||||
# Test 2: Double beep
|
||||
echo "Test 2: Double beep"
|
||||
blink 2 0.1 0.1
|
||||
sleep 0.5
|
||||
|
||||
# Test 3: Triple beep
|
||||
echo "Test 3: Triple beep"
|
||||
blink 3 0.1 0.1
|
||||
sleep 0.5
|
||||
|
||||
# Test 4: Long beep
|
||||
echo "Test 4: Long beep (0.5s)"
|
||||
beep 0.5
|
||||
sleep 0.5
|
||||
|
||||
# Test 5: Rapid beeps
|
||||
echo "Test 5: Rapid beeps (5x)"
|
||||
blink 5 0.05 0.05
|
||||
sleep 0.5
|
||||
|
||||
# Test 6: Slow beeps
|
||||
echo "Test 6: Slow beeps (3x)"
|
||||
blink 3 0.3 0.3
|
||||
sleep 0.5
|
||||
|
||||
# Test 7: Success pattern (2 short)
|
||||
echo "Test 7: Success pattern"
|
||||
blink 2 0.1 0.1
|
||||
sleep 0.5
|
||||
|
||||
# Test 8: Error pattern (3 fast)
|
||||
echo "Test 8: Error pattern"
|
||||
blink 3 0.05 0.05
|
||||
sleep 0.5
|
||||
|
||||
# Ensure buzzer is off
|
||||
echo 0 | sudo tee $BUZZER_PATH > /dev/null 2>&1
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Buzzer test complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Current buzzer status: $(cat $BUZZER_PATH) (0=OFF, 1=ON)"
|
||||
60
archive/chromium-setup-legacy/test_led.sh
Executable file
60
archive/chromium-setup-legacy/test_led.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# LED Test Script for reTerminal DM4
|
||||
|
||||
LED_PATH='/sys/class/leds/usr-led/brightness'
|
||||
|
||||
echo "Testing reTerminal DM4 LED"
|
||||
echo "=========================="
|
||||
echo ""
|
||||
|
||||
# Test 1: Turn ON
|
||||
echo "Test 1: Turning LED ON..."
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 1
|
||||
echo "LED should be ON now"
|
||||
echo ""
|
||||
|
||||
# Test 2: Turn OFF
|
||||
echo "Test 2: Turning LED OFF..."
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 1
|
||||
echo "LED should be OFF now"
|
||||
echo ""
|
||||
|
||||
# Test 3: Blink pattern
|
||||
echo "Test 3: Blinking LED (5 times)..."
|
||||
for i in {1..5}; do
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.2
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.2
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Test 4: Fast blink
|
||||
echo "Test 4: Fast blink (10 times)..."
|
||||
for i in {1..10}; do
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.1
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.1
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Test 5: Slow blink
|
||||
echo "Test 5: Slow blink (3 times)..."
|
||||
for i in {1..3}; do
|
||||
echo 1 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.5
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
sleep 0.5
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Ensure LED is off
|
||||
echo 0 | sudo tee $LED_PATH > /dev/null
|
||||
|
||||
echo "LED test complete!"
|
||||
echo ""
|
||||
echo "Current LED status:"
|
||||
cat $LED_PATH && echo " (0 = OFF, 1 = ON)"
|
||||
40
archive/cloud-init-duplicates/plymouth-custom.script
Normal file
40
archive/cloud-init-duplicates/plymouth-custom.script
Normal file
@@ -0,0 +1,40 @@
|
||||
screen_width = Window.GetWidth();
|
||||
screen_height = Window.GetHeight();
|
||||
|
||||
theme_image = Image("splash.png");
|
||||
image_width = theme_image.GetWidth();
|
||||
image_height = theme_image.GetHeight();
|
||||
|
||||
scale_x = image_width / screen_width;
|
||||
scale_y = image_height / screen_height;
|
||||
|
||||
if (scale_x > 1 || scale_y > 1)
|
||||
{
|
||||
if (scale_x > scale_y)
|
||||
{
|
||||
resized_image = theme_image.Scale(screen_width, image_height / scale_x);
|
||||
image_x = 0;
|
||||
image_y = (screen_height - ((image_height * screen_width) / image_width)) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
resized_image = theme_image.Scale(image_width / scale_y, screen_height);
|
||||
image_x = (screen_width - ((image_width * screen_height) / image_height)) / 2;
|
||||
image_y = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resized_image = theme_image.Scale(image_width, image_height);
|
||||
image_x = (screen_width - image_width) / 2;
|
||||
image_y = (screen_height - image_height) / 2;
|
||||
}
|
||||
|
||||
if (Plymouth.GetMode() != "shutdown")
|
||||
{
|
||||
sprite = Sprite(resized_image);
|
||||
sprite.SetPosition(image_x, image_y, -100);
|
||||
}
|
||||
|
||||
fun message_callback(text) {
|
||||
}
|
||||
Reference in New Issue
Block a user