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

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

10
archive/README.md Normal file
View 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/**.

View 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.

View 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.

View 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`

View 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

View File

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

View 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

View 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/`

View 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

View 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

View 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

View 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

View 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.

View 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

View 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>

View 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)

View 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)

View File

@@ -0,0 +1,4 @@
Flask==2.3.0
flask-httpauth==4.8.0
flask-limiter==3.0.0
flask-cors==4.0.0

View 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"

View 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!"

View 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)

View 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)"

View 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)"

View 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) {
}