# Architecture Research: RTU Web Interface **Domain:** Embedded IoT/SCADA Web Interface for Rainfall Monitoring **Researched:** 2026-03-13 **Confidence:** HIGH ## System Overview Modern RTU (Remote Terminal Unit) web interfaces are embedded applications that bridge physical sensors with human operators. For a Raspberry Pi-based rainfall monitoring system, the architecture must balance real-time data visualization, configuration management, and remote accessibility while operating on constrained hardware. ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ PRESENTATION LAYER │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Dashboard UI │ │ Settings UI │ │ Calibration UI │ │ │ │ (Home Screen) │ │ (11 Submenus) │ │ (ADC/Levels) │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ └────────────────────┼────────────────────┘ │ │ ↓ │ │ ┌─────────────────────┐ │ │ │ Navigation/Router │ │ │ └─────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────────────┤ │ STATE MANAGEMENT LAYER │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Sensor Data Store │ │ │ │ (Rainfall, Voltage, ADC, Status) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Configuration Store │ │ │ │ (Settings, Calibration, Network, Station Info) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────────────┤ │ DATA ACCESS LAYER │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Sensor API │ │ Config API │ │ File Manager │ │ Network API │ │ │ │ (Real-time) │ │ (CRUD) │ │ (CSV/Flash) │ │ (FTP/SCP) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ │ ├───────────┴──────────────┴──────────────┴──────────────┴───────────────────┤ │ HARDWARE INTERFACE LAYER │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Rainfall │ │ ADC Inputs │ │ GPIO/Power │ │ Network │ │ │ │ Sensor │ │ (4-20mA) │ │ Monitoring │ │ Interface │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ## Component Responsibilities | Component | Responsibility | Implementation Notes | |-----------|----------------|---------------------| | **Dashboard UI** | Real-time sensor visualization, status indicators, quick actions | Optimized for 1024x600, refresh every 1-5s | | **Settings UI** | 11 configuration views (Utility, Calibration, Flash, Mobile, ADC, Rain, EVAP, GPRS, Level, Siren, Network) | Form-based, validation, persistence | | **Calibration UI** | ADC channel calibration, level sensor calibration | Wizard-style, step-by-step | | **Navigation** | Route management between views, breadcrumb trails | React Router, conditional rendering | | **Sensor Data Store** | Real-time sensor readings, historical data buffering | In-memory + localStorage for resilience | | **Config Store** | Application settings, station configuration, calibration values | localStorage/IndexedDB | | **API Layer** | Abstract hardware communication, RESTful interface to backend | Fetch API, error handling, retries | | **Hardware Interface** | GPIO access, serial communication, ADC reading | Python backend or Node.js native addons | ## Recommended Project Structure ``` src/ ├── components/ │ ├── ui/ # shadcn/ui base components │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── input.tsx │ │ ├── select.tsx │ │ └── ... │ ├── dashboard/ # Dashboard-specific components │ │ ├── SensorCard.tsx │ │ ├── RainfallDisplay.tsx │ │ ├── VoltageGauge.tsx │ │ └── StatusBar.tsx │ ├── settings/ # Settings view components │ │ ├── SettingsLayout.tsx │ │ ├── SettingsNav.tsx │ │ └── forms/ │ │ ├── AdcSettingsForm.tsx │ │ ├── NetworkSettingsForm.tsx │ │ └── ... │ └── layout/ # App shell components │ ├── AppLayout.tsx │ ├── Header.tsx │ ├── Sidebar.tsx │ └── KioskWrapper.tsx ├── hooks/ │ ├── useSensorData.ts # Real-time sensor data hook │ ├── useConfig.ts # Configuration management hook │ ├── useHardware.ts # Hardware status hook │ └── useNetwork.ts # Network status hook ├── stores/ │ ├── sensorStore.ts # Sensor data state management │ ├── configStore.ts # Configuration state management │ └── uiStore.ts # UI state (theme, layout mode) ├── services/ │ ├── api.ts # API client configuration │ ├── sensorService.ts # Sensor data API calls │ ├── configService.ts # Configuration API calls │ └── fileService.ts # File management API calls ├── types/ │ ├── sensor.ts # Sensor data types │ ├── config.ts # Configuration types │ └── api.ts # API response types ├── utils/ │ ├── formatters.ts # Data formatting utilities │ ├── validators.ts # Form validation │ └── constants.ts # App constants ├── views/ │ ├── Dashboard.tsx # Main dashboard view │ ├── Settings/ # Settings views │ │ ├── index.tsx │ │ ├── AdcSettings.tsx │ │ ├── NetworkSettings.tsx │ │ └── ... │ ├── Calibration.tsx # Calibration view │ └── FlashMemory.tsx # File manager view ├── App.tsx # Root component ├── main.tsx # Entry point └── index.css # Global styles + Tailwind ``` ### Structure Rationale - **components/ui/:** shadcn/ui components are copy-paste ready, open for modification - **components/dashboard/:** Dashboard-specific visualization components, optimized for 7" display - **components/settings/:** Modular settings forms, one per configuration category - **hooks/:** Custom React hooks for data fetching and hardware interaction - **stores/:** State management (Zustand recommended for minimal overhead) - **services/:** API abstraction layer for hardware communication - **views/:** Page-level components corresponding to routes ## Architectural Patterns ### Pattern 1: Dual-Mode Display Architecture **What:** Single codebase supporting two display modes: - **Kiosk Mode (1024x600):** Fixed layout, touch-optimized, no browser chrome - **Remote Mode (Full HD):** Responsive layout, desktop-friendly, expanded details **When to use:** When RTU has both local touchscreen display and remote web access requirements. **Trade-offs:** - **Pros:** Single codebase, consistent UX, easier maintenance - **Cons:** Conditional complexity, testing matrix doubles **Implementation:** ```typescript // Detect display mode based on port or viewport const useDisplayMode = () => { const [mode, setMode] = useState<'kiosk' | 'remote'>('kiosk'); useEffect(() => { // Detect from window location (port 8080 = kiosk, 9090 = remote) const port = window.location.port; setMode(port === '9090' ? 'remote' : 'kiosk'); }, []); return mode; }; // Usage in components const SensorCard = () => { const mode = useDisplayMode(); return mode === 'kiosk' ? : ; }; ``` ### Pattern 2: Polling-Based Real-Time Updates **What:** Periodic data fetching with optimistic UI updates for sensor readings. **When to use:** When WebSocket is not available or too resource-intensive for embedded hardware. **Trade-offs:** - **Pros:** Simple, works on constrained hardware, battery-friendly - **Cons:** Not truly real-time, potential for stale data **Implementation:** ```typescript const useSensorPolling = (interval = 5000) => { const [data, setData] = useState(null); const [isStale, setIsStale] = useState(false); useEffect(() => { const fetchData = async () => { try { const response = await sensorService.getCurrent(); setData(response); setIsStale(false); } catch (error) { setIsStale(true); } }; fetchData(); const intervalId = setInterval(fetchData, interval); return () => clearInterval(intervalId); }, [interval]); return { data, isStale }; }; ``` ### Pattern 3: Offline-First Configuration **What:** Configuration changes persisted locally first, synced to backend asynchronously. **When to use:** When network connectivity is intermittent or when critical settings must survive power cycles. **Trade-offs:** - **Pros:** Resilient to failures, fast UI response, works offline - **Cons:** Conflict resolution complexity, potential for desync **Implementation:** ```typescript interface ConfigStore { local: Config; remote: Config | null; syncStatus: 'synced' | 'pending' | 'error'; updateConfig: (update: Partial) => Promise; } const useConfigStore = create((set, get) => ({ local: loadFromLocalStorage(), remote: null, syncStatus: 'synced', updateConfig: async (update) => { // Update local immediately const newConfig = { ...get().local, ...update }; set({ local: newConfig, syncStatus: 'pending' }); saveToLocalStorage(newConfig); // Sync to backend try { await configService.save(newConfig); set({ remote: newConfig, syncStatus: 'synced' }); } catch (error) { set({ syncStatus: 'error' }); // Will retry on next connection } } })); ``` ## Data Flow ### Request Flow ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ USER ACTION │ │ (Touch/Button Click) │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ REACT COMPONENT │ │ (Event Handler Triggered) │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ CUSTOM HOOK │ │ (Business Logic / Validation) │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ SERVICE LAYER │ │ (API Call to Python Backend) │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ HARDWARE LAYER │ │ (GPIO / Serial / ADC Read/Write) │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ RESPONSE FLOW │ │ (Data → Store → Component Re-render) │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### State Management ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ ZUSTAND STORE │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Sensor Data │ │ Configuration │ │ UI State │ │ │ │ (Real-time) │ │ (Persistent) │ │ (Transient) │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ └────────────────────┼────────────────────┘ │ │ ↓ │ │ ┌─────────────────────┐ │ │ │ Selectors / Hooks │ │ │ └─────────────────────┘ │ │ ↓ │ │ ┌─────────────────────┐ │ │ │ React Components │ │ │ └─────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Key Data Flows 1. **Sensor Data Flow:** - Hardware (GPIO/ADC) → Python Backend → REST API → React Hook → Component - Update frequency: 1-5 seconds for display, configurable for logging 2. **Configuration Flow:** - User Input → Validation → Local Storage → API Sync → Hardware Apply - Two-phase commit: UI optimistic, backend async 3. **File Management Flow:** - CSV files stored on Flash/USB → Python File Service → Frontend Display - Upload/Download via HTTP multipart or WebDAV/FTP/SCP for remote ## Build Order Implications Based on component dependencies, suggested build order: ``` Phase 1: Foundation ├── Core UI components (shadcn/ui setup) ├── Type definitions ├── API client infrastructure └── Basic routing Phase 2: Dashboard ├── Sensor data hooks ├── Dashboard layout ├── Sensor card components └── Real-time data display Phase 3: Settings Framework ├── Settings navigation ├── Form components ├── Config store └── Settings persistence Phase 4: Individual Settings ├── Station Info ├── Date/Time ├── Network Setup ├── Mobile Settings └── ADC Settings Phase 5: Advanced Features ├── Calibration views ├── File manager ├── Network stack (FTP/SCP/SFTP/WebDAV) └── CSV processing Phase 6: Dual-Mode Support ├── Kiosk mode optimization (1024x600) ├── Remote mode layout (Full HD) ├── Responsive adaptations └── Performance tuning ``` **Critical Path:** - Dashboard requires sensor API → Build sensor service first - Settings require config API → Build config service before settings forms - File manager requires backend file service → Coordinate with Python backend team ## Scaling Considerations | Scale | Users | Architecture Adjustments | |-------|-------|-------------------------| | **Single RTU** | 1 local + few remote | Current architecture sufficient | | **Multi-RTU Fleet** | 10-100 devices | Add centralized management dashboard | | **Enterprise** | 1000+ devices | Separate management server, device fleet API | ### Scaling Priorities for Single RTU 1. **First bottleneck:** UI responsiveness on Pi Zero 2 W - **Fix:** Code splitting, lazy loading, virtual scrolling for large datasets 2. **Second bottleneck:** Storage for historical data - **Fix:** Implement data retention policies, CSV rotation, external storage 3. **Third bottleneck:** Network throughput for file transfers - **Fix:** Compression, chunked transfers, background sync ## Anti-Patterns to Avoid ### Anti-Pattern 1: Heavy Frameworks on Embedded **What people do:** Use Next.js, heavy state management (Redux), or large component libraries. **Why it's wrong:** Pi Zero 2 W has limited RAM (512MB) and single-core performance. Bundle size directly impacts load time and runtime performance. **Do this instead:** - Use Vite for minimal overhead build - Zustand or React Context for state (not Redux) - Copy-paste components (shadcn/ui) instead of importing entire libraries - Tree-shake aggressively, analyze bundle size ### Anti-Pattern 2: Real-Time Everything **What people do:** WebSocket connections for all data, constant polling at high frequency. **Why it's wrong:** Drains battery (if solar-powered), saturates network, unnecessary for rainfall data that changes slowly. **Do this instead:** - Adaptive polling: fast during events, slow during idle - Only subscribe to actively viewed sensors - Use caching and display stale data with indicators ### Anti-Pattern 3: Tight Hardware Coupling **What people do:** Direct GPIO access from frontend JavaScript (via unsafe methods). **Why it's wrong:** Security risk, platform lock-in, hard to test. **Do this instead:** - Clean API boundary between UI and hardware - Python backend service for hardware access - Mock API for development/testing ### Anti-Pattern 4: Ignoring Kiosk Constraints **What people do:** Design for desktop, shoehorn into kiosk mode. **Why it's wrong:** 1024x600 is cramped, touch targets must be large, no right-click context menus. **Do this instead:** - Design mobile-first, even for kiosk - Minimum 44px touch targets - Large fonts, high contrast - No hover-dependent interactions ## Integration Points ### External Services | Service | Integration Pattern | Notes | |---------|---------------------|-------| | **Sensor Hardware** | REST API via Python backend | GPIO, I2C, SPI abstraction | | **Network Stack** | Background service + API | FTP/SCP/SFTP/WebDAV daemons | | **CSV Export** | File system + HTTP download | Scheduled generation, on-demand download | | **Remote Server** | HTTP POST / MQTT | Data transmission, status heartbeat | ### Internal Boundaries | Boundary | Communication | Notes | |----------|---------------|-------| | **Frontend ↔ Backend** | HTTP REST API | JSON, stateless, retry logic | | **Backend ↔ Hardware** | Python libraries | GPIOZero, smbus2, spidev | | **Config ↔ Storage** | localStorage/IndexedDB | Offline resilience | | **UI ↔ State** | Zustand hooks | Minimal re-renders | ## Sources - Wikipedia: Remote Terminal Unit (RTU) - https://en.wikipedia.org/wiki/Remote_terminal_unit - React Documentation - https://react.dev/learn - Vite Documentation - https://vitejs.dev/guide/ - shadcn/ui Documentation - https://ui.shadcn.com/docs - MDN: Progressive Web Apps - https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps - Raspberry Pi GPIO Documentation (training data) - SCADA System Architecture Patterns (training data) --- *Architecture research for: TCKRTUIYO RTU Rainfall Monitoring System* *Researched: 2026-03-13*