serversdown a7983d2958 fix: correct DOD field parsing and stop measurement-time resets
Two device-data bugs surfaced while scoping the live-feed work:

1. DOD parser misalignment. DOD's response has no leading counter and
   includes LE + LN1-LN5, but the parser reused the DRD field map
   (parts[0]=counter). That shifted everything: Lp was stored as the
   counter, Leq as Lp, LE as Leq, and LN1 as Lpeak (visible because
   "Lpeak" came out below Lmax, which is impossible). Parse DOD with its
   own map: Lp=0, Leq=1, Lmax=3, Lmin=4, Lpeak=10 (channel 1 = main).

2. measurement_start_time reset on every live-stream open/close. The DOD
   path tags state "Start"; the DRD stream path tags "Measure". The
   transition detector treated only "Start" as measuring, so opening the
   stream ("Start"->"Measure") read as a stop (cleared start time) and
   closing it ("Measure"->"Start") read as a start (reset to now). Every
   viewer reset the elapsed measurement time. Treat {"Start","Measure"}
   both as measuring.

LN1/LN2 (L1/L10) parsing + model/serialization is the next step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:53:00 +00:00
2026-03-12 21:34:14 +00:00
2026-01-07 01:32:25 +00:00
2026-01-29 18:50:47 +00:00
2026-01-29 18:50:47 +00:00
2026-02-17 02:56:11 +00:00

SLMM - Sound Level Meter Manager

Version 0.3.0

Backend API service for controlling and monitoring Rion NL-43/NL-53 Sound Level Meters via TCP and FTP protocols.

Overview

SLMM is a standalone backend module that provides REST API routing and command translation for NL43/NL53 sound level meters. This service acts as a bridge between the hardware devices and frontend applications, handling all device communication, data persistence, and protocol management.

Note: This is a backend-only service. Actual user interfacing is done via customized front ends or cli.

Features

  • Persistent TCP Connections: Cached per-device connections with OS-level keepalive, tuned for cellular modem reliability
  • Background Polling: Continuous automatic polling of devices with configurable intervals
  • Offline Detection: Automatic device reachability tracking with failure counters
  • Device Management: Configure and manage multiple NL43/NL53 devices
  • Real-time Monitoring: Stream live measurement data via WebSocket
  • Measurement Control: Start, stop, pause, resume, and reset measurements
  • Data Retrieval: Access current and historical measurement snapshots
  • FTP Integration: Download measurement files directly from devices
  • Device Configuration: Manage frequency/time weighting, clock sync, and more
  • Rate Limiting: Automatic 1-second delay enforcement between device commands
  • Persistent Storage: SQLite database for device configs and measurement cache
  • Connection Diagnostics: Live UI and API endpoints for monitoring TCP connection pool status

Architecture

┌─────────────────┐         ┌──────────────────────────────┐         ┌─────────────────┐
│                 │◄───────►│  SLMM API                    │◄───────►│  NL43/NL53      │
│  (Frontend)     │  HTTP   │  • REST Endpoints            │  TCP    │  Sound Meters   │
└─────────────────┘         │  • WebSocket Streaming       │  (kept  │  (via cellular  │
                            │  • Background Poller         │  alive) │   modem)        │
                            │  • Connection Pool (v0.3)    │         └─────────────────┘
                            └──────────────────────────────┘
                                          │
                                          ▼
                                  ┌──────────────┐
                                  │  SQLite DB   │
                                  │  • Config    │
                                  │  • Status    │
                                  └──────────────┘

Persistent TCP Connection Pool (v0.3.0)

SLMM maintains persistent TCP connections to devices with OS-level keepalive, designed for reliable operation over cellular modems:

  • Connection Reuse: One cached TCP socket per device, reused across all commands (no repeated handshakes)
  • TCP Keepalive: Probes keep cellular NAT tables alive and detect dead connections early
  • Transparent Retry: Stale cached connections automatically retry with a fresh socket
  • Configurable: Idle TTL (300s), max age (1800s), and keepalive timing via environment variables
  • Diagnostics: Live UI on the roster page and API endpoints for monitoring pool status

Background Polling (v0.2.0)

Background polling service continuously queries devices and updates the status cache:

  • Automatic Updates: Devices are polled at configurable intervals (10-3600 seconds)
  • Offline Detection: Devices marked unreachable after 3 consecutive failures
  • Per-Device Configuration: Each device can have a custom polling interval
  • Resource Efficient: Dynamic sleep intervals and smart scheduling

Status requests return cached data instantly (<100ms) instead of waiting for device queries (1-2 seconds).

Quick Start

Prerequisites

  • Python 3.10+
  • pip package manager

Installation

  1. Clone the repository:
git clone <repository-url>
cd slmm
  1. Install dependencies:
pip install -r requirements.txt

Running the Server

# Development mode with auto-reload
uvicorn app.main:app --reload --port 8100

# Production mode
uvicorn app.main:app --host 0.0.0.0 --port 8100

The API will be available at http://localhost:8100

API Documentation

Once running, visit:

  • Swagger UI: http://localhost:8100/docs
  • ReDoc: http://localhost:8100/redoc
  • Health Check: http://localhost:8100/health

Configuration

Environment Variables

Server:

  • PORT: Server port (default: 8100)
  • CORS_ORIGINS: Comma-separated list of allowed origins (default: "*")

TCP Connection Pool:

  • TCP_PERSISTENT_ENABLED: Enable persistent connections (default: "true")
  • TCP_IDLE_TTL: Close idle connections after N seconds (default: 300)
  • TCP_MAX_AGE: Force reconnect after N seconds (default: 1800)
  • TCP_KEEPALIVE_IDLE: Seconds idle before keepalive probes (default: 15)
  • TCP_KEEPALIVE_INTERVAL: Seconds between keepalive probes (default: 10)
  • TCP_KEEPALIVE_COUNT: Failed probes before declaring dead (default: 3)

Database

The SQLite database is automatically created at data/slmm.db on first run.

Logging

Logs are written to:

API Endpoints

Device Configuration

Method Endpoint Description
GET /api/nl43/{unit_id}/config Get device configuration
PUT /api/nl43/{unit_id}/config Update device configuration

Device Status

Method Endpoint Description
GET /api/nl43/{unit_id}/status Get cached measurement snapshot (updated by background poller)
GET /api/nl43/{unit_id}/live Request fresh DOD data from device (bypasses cache)
WS /api/nl43/{unit_id}/stream WebSocket stream for real-time DRD data

Background Polling

Method Endpoint Description
GET /api/nl43/{unit_id}/polling/config Get device polling configuration
PUT /api/nl43/{unit_id}/polling/config Update polling interval and enable/disable polling
GET /api/nl43/_polling/status Get global polling status for all devices

Connection Pool

Method Endpoint Description
GET /api/nl43/_connections/status Get pool config, active connections, age/idle times
POST /api/nl43/_connections/flush Force-close all cached TCP connections

Measurement Control

Method Endpoint Description
POST /api/nl43/{unit_id}/start Start measurement
POST /api/nl43/{unit_id}/stop Stop measurement
POST /api/nl43/{unit_id}/pause Pause measurement
POST /api/nl43/{unit_id}/resume Resume paused measurement
POST /api/nl43/{unit_id}/reset Reset measurement data
POST /api/nl43/{unit_id}/store Manually store data to SD card

Device Information

Method Endpoint Description
GET /api/nl43/{unit_id}/battery Get battery level
GET /api/nl43/{unit_id}/clock Get device clock time
PUT /api/nl43/{unit_id}/clock Set device clock time
GET /api/nl43/{unit_id}/results Get final calculation results (DLC)

Measurement Settings

Method Endpoint Description
GET /api/nl43/{unit_id}/settings Get all current device settings for verification
GET /api/nl43/{unit_id}/frequency-weighting Get frequency weighting (A/C/Z)
PUT /api/nl43/{unit_id}/frequency-weighting Set frequency weighting
GET /api/nl43/{unit_id}/time-weighting Get time weighting (F/S/I)
PUT /api/nl43/{unit_id}/time-weighting Set time weighting

Sleep Mode

Method Endpoint Description
POST /api/nl43/{unit_id}/sleep Put device into sleep mode
POST /api/nl43/{unit_id}/wake Wake device from sleep
GET /api/nl43/{unit_id}/sleep/status Get sleep mode status

FTP File Management

Method Endpoint Description
POST /api/nl43/{unit_id}/ftp/enable Enable FTP server on device
POST /api/nl43/{unit_id}/ftp/disable Disable FTP server on device
GET /api/nl43/{unit_id}/ftp/status Get FTP server status
GET /api/nl43/{unit_id}/ftp/files List files on device
POST /api/nl43/{unit_id}/ftp/download Download file from device

For detailed API documentation and examples, see API.md.

Project Structure

slmm/
├── app/
│   ├── __init__.py          # Package initialization
│   ├── main.py              # FastAPI application and startup
│   ├── routers.py           # API route definitions
│   ├── models.py            # SQLAlchemy database models
│   ├── services.py          # NL43Client and business logic
│   ├── background_poller.py # Background polling service ⭐ NEW
│   └── database.py          # Database configuration
├── data/
│   ├── slmm.db              # SQLite database (auto-created)
│   ├── slmm.log             # Application logs
│   └── downloads/           # Downloaded files from devices
├── templates/
│   └── index.html           # Simple web interface (optional)
├── manuals/                 # Device documentation
├── migrate_add_polling_fields.py  # Database migration for v0.2.0 ⭐ NEW
├── test_polling.sh          # Polling feature test script ⭐ NEW
├── API.md                   # Detailed API documentation
├── COMMUNICATION_GUIDE.md   # NL43 protocol documentation
├── NL43_COMMANDS.md         # Command reference
├── CHANGELOG.md             # Version history ⭐ NEW
├── requirements.txt         # Python dependencies
└── README.md                # This file

Database Schema

NL43Config Table

Stores device connection configuration:

  • unit_id (PK): Unique device identifier
  • host: Device IP address or hostname
  • tcp_port: TCP control port (default: 80)
  • tcp_enabled: Enable/disable TCP communication
  • ftp_enabled: Enable/disable FTP functionality
  • ftp_username: FTP authentication username
  • ftp_password: FTP authentication password
  • web_enabled: Enable/disable web interface access
  • poll_interval_seconds: Polling interval in seconds (10-3600, default: 60) NEW
  • poll_enabled: Enable/disable background polling for this device NEW

NL43Status Table

Caches latest measurement snapshot:

  • unit_id (PK): Unique device identifier
  • last_seen: Timestamp of last update
  • measurement_state: Current state (Measure/Stop)
  • measurement_start_time: When measurement started (UTC)
  • counter: Measurement interval counter (1-600)
  • lp: Instantaneous sound pressure level
  • leq: Equivalent continuous sound level
  • lmax: Maximum sound level
  • lmin: Minimum sound level
  • lpeak: Peak sound level
  • battery_level: Battery percentage
  • power_source: Current power source
  • sd_remaining_mb: Free SD card space (MB)
  • sd_free_ratio: SD card free space ratio
  • raw_payload: Raw device response data
  • is_reachable: Device reachability status (Boolean) NEW
  • consecutive_failures: Count of consecutive poll failures NEW
  • last_poll_attempt: Last time background poller attempted to poll NEW
  • last_success: Last successful poll timestamp NEW
  • last_error: Last error message (truncated to 500 chars) NEW

Protocol Details

TCP Communication

  • Uses ASCII command protocol over TCP
  • Persistent connections with OS-level keepalive (tuned for cellular modems)
  • Connections cached per device and reused across commands
  • Transparent retry on stale connections
  • Enforces ≥1 second delay between commands to same device
  • Two-line response format:
    • Line 1: Result code (R+0000 for success)
    • Line 2: Data payload (for query commands)

FTP Communication

  • Uses active mode FTP (requires device to connect back)
  • TCP and FTP are mutually exclusive on the device
  • Credentials configurable per device
  • Default NL43 FTP Credentials: Username: USER, Password: 0000

Data Formats

DOD (Data Output Display): Snapshot of current display values DRD (Data Real-time Display): Continuous streaming data DLC (Data Last Calculation): Final stored measurement results

Example Usage

Configure a Device

curl -X PUT http://localhost:8100/api/nl43/meter-001/config \
  -H "Content-Type: application/json" \
  -d '{
    "host": "192.168.1.100",
    "tcp_port": 2255,
    "tcp_enabled": true,
    "ftp_enabled": true,
    "ftp_username": "USER",
    "ftp_password": "0000"
  }'

Start Measurement

curl -X POST http://localhost:8100/api/nl43/meter-001/start

Get Cached Status (Fast - from background poller)

curl http://localhost:8100/api/nl43/meter-001/status

Get Live Status (Bypasses cache)

curl http://localhost:8100/api/nl43/meter-001/live

Configure Background Polling NEW

# Set polling interval to 30 seconds
curl -X PUT http://localhost:8100/api/nl43/meter-001/polling/config \
  -H "Content-Type: application/json" \
  -d '{
    "poll_interval_seconds": 30,
    "poll_enabled": true
  }'

# Get polling configuration
curl http://localhost:8100/api/nl43/meter-001/polling/config

# Check global polling status
curl http://localhost:8100/api/nl43/_polling/status

Check Connection Pool Status

curl http://localhost:8100/api/nl43/_connections/status | jq '.'

Flush All Cached Connections

curl -X POST http://localhost:8100/api/nl43/_connections/flush

Verify Device Settings

curl http://localhost:8100/api/nl43/meter-001/settings

This returns all current device configuration:

{
  "status": "ok",
  "unit_id": "meter-001",
  "settings": {
    "measurement_state": "Stop",
    "frequency_weighting": "A",
    "time_weighting": "F",
    "measurement_time": "00:01:00",
    "leq_interval": "1s",
    "lp_interval": "125ms",
    "index_number": "0",
    "battery_level": "100%",
    "clock": "2025/12/24,20:45:30",
    "sleep_mode": "Off",
    "ftp_status": "On"
  }
}

Stream Real-time Data (JavaScript)

const ws = new WebSocket('ws://localhost:8100/api/nl43/meter-001/stream');

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Live measurement:', data);
};

Download Files via FTP

# Enable FTP
curl -X POST http://localhost:8100/api/nl43/meter-001/ftp/enable

# List files
curl http://localhost:8100/api/nl43/meter-001/ftp/files?path=/NL43_DATA

# Download file
curl -X POST http://localhost:8100/api/nl43/meter-001/ftp/download \
  -H "Content-Type: application/json" \
  -d '{"remote_path": "/NL43_DATA/measurement.wav"}' \
  --output measurement.wav

# Disable FTP
curl -X POST http://localhost:8100/api/nl43/meter-001/ftp/disable

Integration with Terra-View

This backend is designed to be consumed by the Terra-View frontend application. The frontend should:

  1. Use the config endpoints to register and configure devices
  2. Poll or stream live status for real-time monitoring
  3. Use control endpoints to manage measurements
  4. Download files via FTP endpoints for analysis

See API.md for detailed integration examples.

Troubleshooting

Connection Issues

  • Check connection pool status: curl http://localhost:8100/api/nl43/_connections/status
  • Flush stale connections: curl -X POST http://localhost:8100/api/nl43/_connections/flush
  • Verify device IP address and port in configuration
  • Ensure device is on the same network
  • Check firewall rules allow TCP/FTP connections
  • Verify RX55 network adapter is properly configured on device

Cellular Modem Issues

  • If modem wedges from too many handshakes, ensure TCP_PERSISTENT_ENABLED=true (default)
  • Increase TCP_IDLE_TTL if connections expire between poll cycles
  • Keepalive probes (default: every 15s) keep NAT tables alive — adjust TCP_KEEPALIVE_IDLE if needed
  • Set TCP_PERSISTENT_ENABLED=false to disable pooling for debugging

Rate Limiting

  • API automatically enforces 1-second delay between commands
  • If experiencing delays, this is normal device behavior
  • Multiple devices can be controlled in parallel

FTP Active Mode

  • Ensure server can accept incoming connections from device
  • FTP uses active mode (device connects back to server)
  • May require firewall configuration for data channel

WebSocket Disconnects

  • WebSocket streams maintain persistent connection
  • Limit concurrent streams to avoid device overload
  • Connection will auto-close if device stops responding

Development

Running Tests

# Add test commands when implemented
pytest

Database Migrations

# Migrate to v0.2.0 (add background polling fields)
python3 migrate_add_polling_fields.py

# Legacy: Migrate to add FTP credentials
python migrate_add_ftp_credentials.py

# Set FTP credentials for a device
python set_ftp_credentials.py <unit_id> <username> <password>

Testing Background Polling

# Run comprehensive polling tests
./test_polling.sh [unit_id]

# Test settings endpoint
python3 test_settings_endpoint.py <unit_id>

# Test sleep mode auto-disable
python3 test_sleep_mode_auto_disable.py <unit_id>

Legacy Scripts

Old migration scripts and manual polling tools have been moved to archive/ for reference. See archive/README.md for details.

Contributing

This is a standalone module kept separate from the SFM/Terra-View codebase. When contributing:

  1. Maintain separation from frontend code
  2. Follow existing API patterns and error handling
  3. Update API documentation for new endpoints
  4. Ensure rate limiting is enforced for device commands

License

[Specify license here]

Support

For issues and questions:

  • Backend API issues: This repository
  • Frontend/UI issues: Terra-View repository
  • Device protocol questions: See COMMUNICATION_GUIDE.md
S
Description
Sound Level Meter Manager
Readme 1.2 MiB
v0.4.0 Latest
2026-06-22 19:00:49 -04:00
Languages
Python 82.5%
HTML 16.1%
Shell 1.3%
Dockerfile 0.1%