A real-time web dashboard for monitoring and controlling ESP32-based coffee roaster systems. Built with Rust, Axum, and modern web technologies, this dashboard provides live telemetry visualization, historical data analysis, and roaster control capabilities.
- Live temperature charts with Bean Temperature, Environment Temperature, and Rate of Rise
- WebSocket-based updates for sub-second latency
- Real-time telemetry cards with visual trend indicators
- System status monitoring with service health checks
- Historical telemetry storage with 1000+ data points
- Chart time range controls (5min, 10min, 30min, 1hour)
- Data export functionality (CSV/JSON formats)
- Roast session analysis and profiling
- Manual heater control with PWM percentage
- Variable fan speed control (0-255 PWM)
- Control mode switching (Manual/Auto PID)
- Emergency stop functionality
- Keyboard shortcuts for quick control
- Responsive design for desktop and mobile
- Toast notifications for alerts and status updates
- Connection resilience with automatic reconnection
- Performance optimized for high-frequency data
- π Fixed: Refresh button functionality in status section - resolved JavaScript
showToastreference error - π Improved: Chart.js time axis display - fixed year range issue (1975-2025) with proper timestamp handling
- ποΈ Enhanced: Auto mode setpoint controls - added input field and set button for PID temperature control
- π± Responsive: Fixed "Latest Readings" section layout - improved grid width for better readability
- β Verified: End-to-end control system working - dashboard β bridge β ESP32 confirmed functional
- π§Ή Cleaned: Removed test files and fixed all compilation warnings for production readiness
- Primary: WebSocket connection to bridge for real-time data
- Secondary: REST API calls for historical data and status
- Fallback: HTTP polling when WebSocket unavailable
- Backend: Rust with Axum web framework
- Templates: Askama HTML templating
- WebSocket: Tokio-tungstenite for real-time communication
- Frontend: Vanilla JavaScript with Chart.js
- Styling: Custom CSS with responsive design
- HTTP Client: Reqwest for bridge API integration
- Rust 1.70+ with Cargo
- Coffee Roaster Bridge running and accessible
- Modern web browser with WebSocket support
git clone https://github.com/yourusername/coffee-roaster-dashboard
cd coffee-roaster-dashboard
# Build in release mode for best performance
cargo build --release# Run with default settings (connects to localhost:8080 bridge)
cargo run --release
# Or run with custom bridge connection
cargo run --release -- \
--bridge-api-url "http://192.168.1.100:8080" \
--bridge-websocket-url "ws://192.168.1.100:8081" \
--port 3000Open your web browser and navigate to:
- Dashboard:
http://localhost:3000 - System Status:
http://localhost:3000/api/status
| Option | Default | Description |
|---|---|---|
--port |
3000 |
Dashboard web server port |
--bridge-api-url |
http://localhost:8080 |
Bridge REST API base URL |
--bridge-websocket-url |
ws://localhost:8081 |
Bridge WebSocket URL |
--debug |
false |
Enable debug logging |
cargo run -- --debug --port 3000cargo run --release -- \
--port 80 \
--bridge-api-url "https://roaster-api.example.com" \
--bridge-websocket-url "wss://roaster-ws.example.com"# Roaster 1
cargo run --release -- --port 3001 --bridge-websocket-url "ws://roaster1:8081"
# Roaster 2
cargo run --release -- --port 3002 --bridge-websocket-url "ws://roaster2:8081"- Bean Temperature: Primary roasting temperature with trend indicator
- Environment Temperature: Chamber air temperature
- Setpoint: Target temperature for PID control
- Rate of Rise: Temperature change velocity with status indicator
- Temperature Profile: Real-time temperature curves with time range controls
- Rate of Rise Chart: ROR visualization with fill area
- Heater Control: Enable/disable and PWM percentage control
- Fan Control: Variable speed control (0-255 PWM)
- Control Mode: Manual vs Auto (PID) mode selection
- Emergency Stop: Immediate safety shutdown
- Bridge Status: Connection health and version info
- Service Status: MQTT, Modbus, WebSocket, Serial service states
- Performance Metrics: Message counts and system statistics
- Optimization Status: Performance feature indicators
| Key | Action |
|---|---|
M |
Switch to Manual mode |
A |
Switch to Auto (PID) mode |
H |
Toggle heater enable |
Space |
Pause/Resume data updates |
Esc (2x) |
Emergency stop (double-tap protection) |
- Real-time updates via WebSocket
- Historical data loading for context
- Interactive tooltips with precise values
- Time range controls for zoom levels
- Responsive scaling for different screen sizes
// Update chart time range (seconds)
dashboard.charts.updateTimeRange(1800); // 30 minutes
// Get chart statistics
const rorStats = dashboard.charts.getRORStatistics();
console.log('Current ROR:', rorStats.current);
console.log('Average ROR:', rorStats.average);- CSV format for spreadsheet analysis
- JSON format for programmatic processing
- Configurable data points (1-10,000 entries)
- Timestamp-based exports for specific time ranges
The dashboard connects to the bridge WebSocket for real-time data:
// Request current telemetry data
{ "type": "request_current_data" }
// Heartbeat ping
{ "type": "ping", "timestamp": 1699123456 }// Real-time telemetry update
{
"type": "telemetry",
"data": {
"beanTemp": 185.5,
"envTemp": 145.2,
"rateOfRise": 12.5,
// ... full telemetry object
},
"timestamp": 1699123456
}
// Heartbeat response
{ "type": "pong", "timestamp": 1699123456 }The dashboard uses the bridge REST API for:
- Initial data loading:
/api/data,/api/status - Historical data:
/api/history?limit=300 - System information:
/api/metrics,/api/registers
// Access dashboard components programmatically
const dashboard = window.dashboard;
// Add custom telemetry handler
dashboard.websocket.onmessage = (data) => {
if (data.type === 'telemetry') {
// Custom processing
console.log('Bean temp:', data.data.beanTemp);
}
};
// Send custom control commands
dashboard.sendWebSocketMessage({
type: 'custom_command',
parameter: 'setpoint',
value: 200
});coffee-roaster-dashboard/
βββ src/
β βββ main.rs # Main application and routing
β βββ models.rs # Data structures and types
β βββ bridge_client.rs # HTTP client for bridge API
β βββ websocket.rs # WebSocket client management
βββ templates/
β βββ dashboard.html # Main dashboard template
β βββ api_status.html # Status page template
βββ static/
β βββ css/dashboard.css # Dashboard styling
β βββ js/
β βββ dashboard.js # Main dashboard controller
β βββ charts.js # Chart management
β βββ controls.js # Control interface
β βββ websocket.js # WebSocket connection handling
βββ Cargo.toml # Rust dependencies
# Development build
cargo build
# Release build with optimizations
cargo build --release
# Run tests
cargo test
# Check code formatting
cargo fmt --check
# Run clippy linter
cargo clippy// Add new chart data structure
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CustomChartData {
pub timestamp: u64,
pub custom_metric: f64,
}
// Add route handler
async fn custom_chart_handler(State(state): State<AppState>) -> impl IntoResponse {
// Implementation
}// Handle custom message types
dashboard.handleWebSocketMessage = (data) => {
switch (data.type) {
case 'custom_data':
this.handleCustomData(data);
break;
// ... existing cases
}
};- Adaptive layouts for phones and tablets
- Touch-friendly controls with proper sizing
- Optimized charts for small screens
- Collapsible sections for better mobile UX
- Swipe gestures for chart navigation
- Haptic feedback for control interactions
- Landscape mode optimization
- PWA capabilities for app-like experience
# Build optimized release
cargo build --release
# Run on production port
./target/release/coffee-roaster-dashboard --port 80FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/coffee-roaster-dashboard /usr/local/bin/
COPY --from=builder /app/templates /app/templates
COPY --from=builder /app/static /app/static
WORKDIR /app
EXPOSE 3000
CMD ["coffee-roaster-dashboard"]server {
listen 80;
server_name roaster-dashboard.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket upgrade handling
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}
}# Enable debug logging
cargo run -- --debug
# View detailed WebSocket messages
RUST_LOG=debug cargo run// WebSocket connection info
wsDebug.getState()
// Force WebSocket reconnection
wsDebug.reconnect()
// Send test ping
wsDebug.ping()
// Get chart data
dashboard.charts.exportChartData('json')// Dashboard performance metrics
console.log('Chart update rate:', dashboard.charts.updateCount);
console.log('WebSocket latency:', dashboard.websocket.lastPongTime - dashboard.websocket.lastPingTime);# Check bridge connectivity
curl http://localhost:8080/api/status
# Verify WebSocket endpoint
wscat -c ws://localhost:8081
# Check dashboard logs
cargo run -- --debug- Check WebSocket connection status in browser console
- Verify bridge is publishing telemetry data
- Check for JavaScript errors in browser dev tools
- Confirm dashboard is not paused
- Reduce chart history points:
dashboard.charts.maxDataPoints = 100 - Clear chart data:
dashboard.charts.clearCharts() - Check for memory leaks in browser dev tools
- Restart dashboard application
- Reduce chart update frequency for slower devices
- Limit historical data points loaded
- Use chart animation:
nonefor better performance - Enable chart data decimation for large datasets
- Increase WebSocket buffer sizes
- Use release build for production
- Monitor memory usage with
htop - Consider connection pooling for multiple clients
// Create charts instance
const charts = new DashboardCharts();
// Add telemetry data point
charts.addTelemetryPoint(telemetryData);
// Load historical data
charts.loadHistoricalData(historyArray);
// Get chart statistics
const stats = charts.getRORStatistics();// Create controls instance
const controls = new DashboardControls();
// Set heater PWM (0-100%)
controls.setHeaterPWM(75);
// Set fan speed (0-255)
controls.setFanPWM(180);
// Change control mode (0=manual, 1=auto)
controls.setControlMode(1);{
"type": "request_current_data" // Request latest telemetry
}
{
"type": "ping", // Heartbeat ping
"timestamp": 1699123456
}{
"type": "telemetry", // Real-time data update
"data": { /* TelemetryData */ },
"timestamp": 1699123456
}
{
"type": "pong", // Heartbeat response
"timestamp": 1699123456
}- Clone the repository
- Install Rust 1.70+
- Run
cargo buildto install dependencies - Start development server:
cargo run -- --debug - Access dashboard at
http://localhost:3000
- Follow Rust standard formatting:
cargo fmt - Lint with Clippy:
cargo clippy - Write tests for new features:
cargo test - Document public APIs with rustdoc comments
- Real-time alerts and notifications
- Roast profile templates and automation
- Data analytics and machine learning integration
- Multi-roaster fleet management
- Mobile app companion
This project is licensed under MIT OR Apache-2.0. See LICENSE files for details.
- Issues: Report bugs and feature requests via GitHub issues
- Documentation: Check this README and inline code comments
- Performance: Use browser dev tools and Rust profiling tools
- Integration: Refer to bridge API documentation
Version: 1.0.0
Rust Version: 1.70+
Real-time Updates: β
Mobile Responsive: β
Production Ready: β