Intelligent Wearable Pager for Real-Time Production Notifications
The MeshWave Pager is the mobile companion to NGIS PTE LTD's professional Andon Mesh Vision platform. It empowers mobile workers—forklift operators, logistics personnel, maintenance technicians—to receive targeted notifications wherever they are in the facility. Built on the Seeed Studio XIAO ESP32S3 with high-contrast OLED display and intuitive rotary encoder, the MeshWave Pager delivers a robust, battery-powered solution for mission-critical factory communication.
PROPRIETARY SOFTWARE - ALL RIGHTS RESERVED
Copyright © 2024-2025 NGIS PTE LTD
Author: Christian Zeh (info@ng-is.com)
NOTICE: This software is proprietary and confidential.
Unauthorized use, reproduction, or distribution is strictly prohibited.
All rights reserved by NGIS PTE LTD.
For licensing inquiries, contact: info@ng-is.com
This project is licensed under a proprietary license. See the LICENSE file for complete terms.
Modern manufacturing facilities generate constant streams of events: material requests, maintenance calls, quality issues, safety alerts. The Andon Mesh Vision system captures these events centrally and orchestrates their resolution. But how do you reach the forklift operator in the warehouse? The technician in Maintenance Workshop B? How do you ensure they get the message before they reach the next station?
The MeshWave Pager answers this challenge. It is a wearable, battery-powered pager that receives targeted notifications directly from the central Andon Mesh Vision platform. When, for example, a production worker at Assembly Station 3 initiates a material request, the Andon Mesh Vision system can instantly page the assigned forklift operator. That operator receives an unmistakable acoustic alert through the characteristic "Wup-Wup" alarm pattern, sees the specific request on the display, and has the complete history of all incoming notifications available—timestamped, searchable, and ready for audit trails.
The result: faster response times, eliminated missed requests, better traceability, and operators who stay aware of production needs without being tethered to a fixed workstation.
The MeshWave Pager manages up to 50 messages in persistent flash memory, so the communication history survives power outages and battery changes. The rotary encoder uses an optimized Grey Code state machine for precise, debounce-free input detection. Users navigate messages with a simple twist of the dial, delete individual messages, or clear the entire queue—all accomplishable with one hand without taking eyes off the work.
The alarm system employs a carefully tuned "Wup-Wup" pattern across four phases: 200ms ON (first "wup"), 150ms OFF (short pause), 200ms ON (second "wup"), then 400ms OFF (breath pause). This cycle repeats for the configured duration. Crucially, the siren triggers only for new incoming messages, not on system startup, preventing unnecessary noise and confusion. A single button press temporarily mutes the siren when temporary silence is needed in high-noise environments.
The display automatically scales message text across three font sizes to accommodate everything from single-word alerts to detailed multi-line status updates. After 60 seconds of inactivity, a screensaver activates to protect display lifespan. Any encoder rotation or button press instantly awakens the display. The entire interface is designed around one-handed operation in a busy factory environment—no need for menus or complicated navigation.
The MeshWave Pager follows a modular software architecture where each hardware component and function has its own dedicated C++ module. This separation of concerns creates a system that is highly maintainable, testable, and extensible for future enhancements.
graph TB
A["main.cpp<br/>(Core Event Loop)"] --> B["config.cpp<br/>(NVS Persistence)"]
A --> C["display.cpp<br/>(OLED Rendering)"]
A --> D["encoder.cpp<br/>(Input Handling)"]
A --> E["serial.cpp<br/>(UART & JSON)"]
A --> F["buzzer.cpp<br/>(Audio Feedback)"]
A --> G["messages.cpp<br/>(Message Queue)"]
A --> H["modal.cpp<br/>(UI Dialogs)"]
A --> I["handlers.cpp<br/>(Config Commands)"]
E --> I
I --> H
C --> H
B --> A
style A fill:#ff6b6b,color:#fff
style B fill:#4ecdc4,color:#fff
style C fill:#45b7d1,color:#fff
style D fill:#96ceb4,color:#000
style E fill:#ffeaa7,color:#000
style F fill:#dfe6e9,color:#000
style G fill:#a29bfe,color:#fff
style H fill:#fd79a8,color:#fff
style I fill:#fdcb6e,color:#000
main.cpp serves as the orchestration hub, running the core initialization sequence and the main event loop. It initializes all hardware components in the correct order—display first for visual feedback, then buzzer, I2C bus for the expander, encoder, and finally the serial interface. The main loop continuously checks for buzzer alarms that need timing updates, encoder input from the user, and incoming serial data from the Andon Mesh Vision gateway. Each event is delegated to the appropriate handler module, and the display refresh cycle is maintained at a responsive rate.
config.cpp manages persistent configuration storage using the ESP32's NVS (Non-Volatile Storage) system, which survives power cycles. It maintains a configuration structure containing the buzzer duration (configurable from 100 to 20000ms), display contrast level (0-255), screensaver timeout (5-600 seconds), and the authentication "magic word" string (2-7 characters). On system startup, configuration is loaded from flash with sensible factory defaults. Any configuration command received via OTA immediately updates this in-memory structure and persists it back to flash, ensuring settings survive unexpected power loss.
display.cpp handles all OLED rendering, from the welcome screen through message display with intelligent font auto-scaling, screensaver management, confirmation dialogs, and integration with the modal system. When displaying a message, it counts how many lines would be needed with the largest font; if more than 3 lines are needed, it drops to the medium font; if more than 5 lines, it uses the smallest font. This ensures maximum readability while minimizing scrolling. The screensaver monitors inactivity time and powers down the display after 60 seconds to extend OLED lifespan—a significant benefit for battery-powered operation.
encoder.cpp implements the complete input handling system including a sophisticated Grey Code state machine for rotation detection (immune to contact bounce), click counting with timeout logic to distinguish between single-click, double-click, and long-press gestures. In confirmation dialogs, encoder rotation selects between Yes and No options. Every encoder action updates the global activity timestamp to prevent the screensaver from activating during active user interaction.
serial.cpp provides the communication layer, reading from both UART1 (SerialExt on GPIO 43/44) and USB debug Serial, buffering incoming characters, and triggering message processing when a newline or closing brace is detected. It implements sophisticated debug message filtering to ignore ESP32 system output (common during development and operation). It distinguishes between configuration commands (which bypass authentication to allow system setup) and regular messages (which require the magic word prefix). It handles timeout-based message completion, so if a message arrives fragmented across multiple UART packets, they are assembled and processed as one unit.
buzzer.cpp manages the alarm system with precise timing for the characteristic "Wup-Wup" pattern. Rather than simple on/off toggling, it implements a four-phase timing sequence: 200ms ON (first beep), 150ms OFF (short silence), 200ms ON (second beep), 400ms OFF (long silence for breath). This cycle repeats for a duration specified in the configuration. The buzzer can be temporarily muted with a single button press, which is crucial for factory environments where workers sometimes need temporary silence without losing awareness that an alert is still pending.
messages.cpp maintains the message queue array and handles all message lifecycle operations: adding new messages (with automatic oldest-message eviction when the 50-message buffer fills), retrieving messages by index, deleting individual messages, and clearing all messages at once. All operations immediately persist to flash via the Preferences system, ensuring robustness against power loss—a critical requirement for a battery-powered device.
modal.cpp provides a reusable modal dialog system with automatic fade-in/fade-out animations, auto-closing timers, and a rotating spinner animation to indicate ongoing activity. The modal system is used for all OTA configuration feedback, allowing users to see immediate visual confirmation of parameter changes with professional polish even in an industrial setting.
handlers.cpp contains the JSON command processing logic. It deserializes incoming JSON, validates command structure, executes SET operations (updating a single configuration parameter with range validation), GET operations (retrieving a single parameter), or STATUS operations (dumping all current configuration). It provides helpful error messages via modal dialogs when validation fails, ensuring safe and predictable configuration changes.
The MeshWave Pager is designed as a direct UART connection to the Andon Mesh Vision platform. A secondary controller or gateway at the central facility reads events from the Andon Mesh Vision database (when a production worker triggers a material request, a quality issue is detected, or time thresholds are exceeded) and transmits these as structured messages to the pager's UART1 interface.
The typical data flow works like this: A production worker at Assembly Station 3 presses the Material Request button on the Andon Mesh Vision panel. The central system recognizes this event, identifies the assigned forklift operator (based on shift assignment, current location, or default routing rules), and transmits a message to that operator's pager containing both the request details and the material SKU or location code. The pager's unique magic word (e.g., "MW01") acts as a simple addressing mechanism—multiple pagers can share the same communication channel, and each responds only to messages prefixed with its assigned magic word.
sequenceDiagram
participant Worker at Station
participant Andon Mesh Vision
participant Gateway/Controller
participant MeshWave Pager
Worker at Station->>Andon Mesh Vision: Triggers Material Request
Andon Mesh Vision->>Gateway/Controller: Event: Material needed, assigned to Operator#7
Gateway/Controller->>MeshWave Pager: MW01Material: 500x TypeA from Bin 34
MeshWave Pager->>MeshWave Pager: Receives message, plays Wup-Wup
MeshWave Pager-->>Operator: Alert! Check Display
Operator->>MeshWave Pager: Views message via rotary encoder
Operator->>Andon Mesh Vision: Acknowledges via separate channel
This architecture provides several advantages. First, the pager can be assigned a unique magic word when paired with the Andon system, enabling one-to-one targeting of specific workers or roles. Second, the message format is completely flexible—you can include SKU codes, location references, priority levels, or any other metadata your Andon Mesh Vision system generates. Third, the UART connection is simple and can be implemented on any gateway that runs your Andon software; no special hardware or protocols are required beyond standard serial communication.
The physical system integrates several carefully selected components. At the center is the XIAO ESP32S3 microcontroller, a thumb-sized powerhouse featuring a dual-core ESP32-S3R8 processor running at 240MHz with 8MB of PSRAM and 8MB of flash storage. This provides ample computing power and memory for message storage, state machines, and future AI/ML features.
The SSD1309 OLED display (128x64 pixels, 2.42 inches) provides a clear, high-contrast viewing surface visible in bright factory lighting. The display connects via I2C on GPIO pins 1 and 2, allowing software-based I2C communication without blocking the main processor.
The KY-040 rotary encoder connects through an MCP23017 I2C expander at address 0x20. The encoder pins map as follows: CLK to MCP23017 pin A2, DT to pin A1, and SW to pin A3. This I2C-based approach allows the main processor to read encoder state without dedicated GPIO pins, freeing up pins for future expansion.
The piezo buzzer on GPIO 3 provides direct acoustic feedback. The ESP32S3 GPIO can drive it directly without additional circuitry, though a transistor can be added if higher volume is needed. The drive capability is set to GPIO_DRIVE_CAP_3 for maximum output.
Communication with the Andon Mesh Vision gateway uses UART1 on GPIO pins 43 (RX) and 44 (TX), configured for 115200 baud with 8 data bits, no parity, and 1 stop bit. This is the primary message input channel from the central system.
Power comes either from the USB-C port (5V, suitable for desktop testing) or from an 18650 Li-Ion battery (3.7V nominal) connected to the BAT+ and BAT- pads on the board's underside. The XIAO ESP32S3 includes an onboard charging circuit, so when powered via USB while the battery is connected, the battery charges automatically. A red charging indicator LED provides visual feedback: solid red during charge, off when complete, and blinking red during active charging.
The entire assembly has a total power consumption of approximately 40-50mA when active with the display on, and can drop to under 100µA in deep sleep mode—allowing several days of operation on a single 18650 charge with typical alert frequency of 10-20 messages per day.
The Over-The-Air (OTA) configuration system is one of the most powerful features of the MeshWave Pager, because it allows you to deploy configuration changes instantly without any physical connection to the device. All configuration happens through simple JSON commands sent via the same UART interface that carries alert messages.
All configuration commands arrive as JSON objects on the UART1 line, terminated with a newline or closing brace. There are three command types:
SET Commands allow you to change a configuration parameter. The format is:
{"CMD":"SET","KEY":"buzzer_duration","VALUE":8000}This sets the buzzer alarm duration to 8000 milliseconds (8 seconds). The system immediately validates the new value against configured range constraints. If valid, it updates the parameter, saves it to NVS flash, and displays a confirmation modal on the pager screen. If invalid (outside the allowed range), an error modal appears showing the valid range.
GET Commands retrieve the current value of a parameter:
{"CMD":"GET","KEY":"display_contrast"}The pager responds by displaying a modal showing the current value (for example, "128"), allowing the operator or technician to verify settings without needing to read console output.
STATUS Commands dump all current configuration:
{"CMD":"STATUS"}The pager displays a comprehensive modal showing all current settings, and also prints them to the serial console for logging. This is useful during commissioning and troubleshooting.
buzzer_duration (100-20000 milliseconds): How long the "Wup-Wup" pattern repeats when an alert arrives. Default 5000ms. Use longer values for very noisy environments, shorter for quiet offices.
display_contrast (0-255): Controls the OLED brightness. Default 128 (approximately 50%). Higher values are brighter but consume slightly more power. Lower values are dimmer and save battery in low-light conditions.
screensaver_timeout (5000-600000 milliseconds): How long the pager waits without any user interaction before turning off the display. Default 60000ms (60 seconds). In high-vibration environments or dusty conditions, you might increase this to keep the display on longer.
magic_word (2-7 characters): The authentication prefix that incoming messages must have to be accepted and trigger an alarm. Default "MW01". Each pager should have a unique magic word—for example, a forklift operator might use "FLT01", a maintenance technician "MAINT02", etc. This allows the Andon Mesh Vision system to target specific workers or roles.
A typical configuration session might look like this during device commissioning. First, the technician sends a STATUS command to see the factory defaults:
{"CMD":"STATUS"}The pager displays a modal with all settings. The technician observes that the default magic word is "MW01" but this pager is assigned to a forklift operator and should use "FLT03". They send:
{"CMD":"SET","KEY":"magic_word","VALUE":"FLT03"}The pager validates that "FLT03" is 5 characters (within the 2-7 range), updates the setting, persists it to flash, and displays a confirmation modal. From this point forward, only messages starting with "FLT03" will be accepted by this pager.
Next, the technician notes that the factory floor is very noisy, so the 5-second default buzzer duration might not be enough. They increase it:
{"CMD":"SET","KEY":"buzzer_duration","VALUE":10000}Again, the pager confirms the change. Now when an alert arrives, the "Wup-Wup" pattern will continue for 10 seconds, making it harder to miss in the noisy environment.
All these changes are persistent. Even if the pager loses power tomorrow, these settings remain when it powers back on. This persistence is critical because you don't want to reconfigure every pager after a power loss.
An observant reader might wonder: if messages require a magic word prefix, why don't config commands? The answer is security through obscurity is not real security, but practicality: configuration commands are rare and controlled (typically issued only by technicians or supervisors during commissioning), whereas messages arrive constantly. Making config commands require a magic word would add complexity without meaningful security benefit in a factory environment where the serial link is typically point-to-point between controlled systems. The JSON command format itself acts as a filtering mechanism—random noise is extremely unlikely to parse as valid JSON. In high-security environments, you could certainly add additional authentication layers by modifying the handlers.cpp file.
Messages arrive on UART1 at 115200 baud. The buffer accumulates incoming characters until a newline (\n) or closing brace (}) is detected. Once complete, the message is processed. If the message contains a "{" character, it is treated as JSON and passed to the JSON deserializer; otherwise, it is treated as a text message and checked for the magic word prefix.
Debug output from the ESP32 system or from connected devices is automatically filtered. Any line starting with "[", "E", "W", "I", or containing certain keywords like "esp32", "file:", or "Wire.cpp" is silently dropped. This prevents debug noise from corrupting the message stream.
If a message arrives incomplete (e.g., the JSON is cut off mid-transmission), the system waits up to 5 seconds for the rest. If the timeout elapses without completion, the partial message is discarded and an error is logged. This handles slow or unreliable serial links gracefully.
From the Andon Mesh Vision side, the gateway or controller software that interfaces with MeshWave pagers simply needs to:
- Identify the target pager's magic word from the pager assignment database
- Format the alert message with the magic word prefix
- Transmit to the pager's UART1 at 115200 baud
For example, if the Andon system determines that a material request should go to the forklift operator with magic word "FLT03", and the request is for 200 units of SKU "ABC-123" from Bin 45, the gateway might send:
FLT03Material Request: 200x ABC-123 from Bin 45
The pager receives this, sees the "FLT03" prefix, accepts the message, triggers the alarm, and stores "Material Request: 200x ABC-123 from Bin 45" for display.
The pager operates across several distinct states, each with specific input handling and display rendering behavior.
stateDiagram-v2
[*] --> IDLE
IDLE --> WELCOME: Boot or all messages deleted
IDLE --> MESSAGE_VIEW: Message received
WELCOME --> MESSAGE_VIEW: User navigates
WELCOME --> SCREENSAVER: 60s inactivity
MESSAGE_VIEW --> DELETE_CONFIRM: Double click
MESSAGE_VIEW --> CLEAR_CONFIRM: Long press 3s
MESSAGE_VIEW --> MUTED: Single click
MESSAGE_VIEW --> SCREENSAVER: 60s inactivity
MUTED --> MESSAGE_VIEW: Encoder activity
DELETE_CONFIRM --> MESSAGE_VIEW: No selected or delete done
CLEAR_CONFIRM --> WELCOME: Yes selected (all cleared)
CLEAR_CONFIRM --> MESSAGE_VIEW: No selected
SCREENSAVER --> MESSAGE_VIEW: Encoder or button activity
SCREENSAVER --> MESSAGE_VIEW: New message arrives
IDLE/WELCOME State: When the pager boots or all messages are cleared, it displays a professional welcome screen with the Andon Mesh Vision branding and NGIS attribution. Any encoder rotation or button press immediately shows the first message (if any exist) or stays in welcome mode (if no messages).
MESSAGE_VIEW State: The main operational state. The display shows the current message with auto-scaled font, the page counter [X/Y] in the top left, the magic word in the top right, and navigation hints in the footer. Encoder rotation moves to the next or previous message (wrapping around). A single click mutes the buzzer if an alarm is active. A double click initiates delete confirmation. A long press (3 seconds) initiates clear-all confirmation. After 60 seconds with no user input, the screensaver activates.
MUTED State: When the user presses the button during an alarm, the buzzer goes silent but the alarm state persists. The display can show a "muted" indicator if desired. Any encoder movement returns to normal alarm state with sound.
DELETE_CONFIRM State: A confirmation dialog appears asking "Delete this message?" The user can rotate the encoder to select No (default) or Yes. Pressing the button confirms the selection. If Yes is selected, the current message is deleted from storage, and navigation returns to the message view.
CLEAR_CONFIRM State: Similar to delete, but asks "Delete all messages?" If Yes is selected, all messages are cleared and the welcome screen appears.
SCREENSAVER State: After 60 seconds without user action, the display powers off (set to power-save mode) to extend OLED lifespan. Any encoder rotation or button press immediately powers the display back on and returns to message view. An incoming message also wakes the display.
Main Controller: Seeed Studio XIAO ESP32S3 with integrated USB-C, built-in Li-Ion charger, dual-core Xtensa processor at 240MHz, 8MB PSRAM, 8MB flash. Available form: bare PCB, or with pre-soldered headers.
Display: SSD1309 OLED 128x64 pixels (2.42 inch diagonal), I2C interface, typical power consumption 20-40mA when on.
Input: KY-040 rotary encoder module with push button, connected via MCP23017 I2C GPIO expander (Adafruit MCP23017 Arduino Library compatible).
Audio: Piezo buzzer rated for 5V operation, typical current draw 20-30mA at peak. Can be direct-driven from ESP32S3 GPIO 3.
Communication: UART1 connection on GPIO 43 (RX) and 44 (TX) @ 115200 baud, 3.3V logic level. Can connect to any UART gateway or level-shifter for RS232.
Power: USB-C for 5V input and charging, or 18650 Li-Ion (3.7V nominal, ~2600-3500 mAh typical). Device draws ~40-50mA when active, ~1mA in light sleep, ~0.1mA in deep sleep.
- Arduino IDE 2.0+ with ESP32 board package (v2.0.14 or later)
- PlatformIO (optional, but recommended)
- USB-C cable for programming
- 18650 Li-Ion battery and charger (optional, for portable use)
If using Arduino IDE:
- Install ESP32 board support: Tools → Boards Manager → search "esp32" → install "esp32 by Espressif Systems"
- Select board: Tools → Board → ESP32 → "XIAO_ESP32S3"
- Install required libraries: Sketch → Include Library → Manage Libraries
- "Adafruit MCP23017 Arduino Library" by Adafruit
- "ArduinoJson" by Benoit Blanchon
- "U8g2" by Oliver Kraus
- Clone/download this repository
- Open main.cpp in Arduino IDE
- Connect pager via USB-C
- Press Boot button, then press Reset button (or use auto-reset if your USB cable supports it)
- Click Upload
If using PlatformIO:
- Install PlatformIO extension in VS Code
- Open this folder as a PlatformIO project
- Press Ctrl+Alt+U to upload
The pager will boot, display "Initializing..." for 2 seconds, then show the welcome screen.
Once booted, send a STATUS command to see the current configuration:
{"CMD":"STATUS"}Typically, you'll want to set the magic word to something unique for this operator or device:
{"CMD":"SET","KEY":"magic_word","VALUE":"FLT01"}Adjust buzzer duration if needed:
{"CMD":"SET","KEY":"buzzer_duration","VALUE":8000}All commands are sent via UART1 (GPIO 43/44 @ 115200 baud) in JSON format. No magic word is required for configuration commands.
Retrieve all current configuration parameters:
{"CMD":"STATUS"}Response: Serial output showing all current settings
📋 Configuration Status:
buzzer_duration: 5000 ms
display_contrast: 128
screensaver_timeout: 60000 ms
magic_word: MW01
Range: 100 - 20000 ms
Default: 5000 ms
{"CMD":"SET","KEY":"buzzer_duration","VALUE":8000}Examples:
{"CMD":"SET","KEY":"buzzer_duration","VALUE":3000} // Short 3 second alarm
{"CMD":"SET","KEY":"buzzer_duration","VALUE":10000} // Long 10 second alarm
{"CMD":"SET","KEY":"buzzer_duration","VALUE":5000} // DefaultFeedback: Modal dialog shows "OK | Buzzer Duration | 8000 ms"
Range: 0 (darkest) - 255 (brightest)
Default: 128 (medium)
{"CMD":"SET","KEY":"display_contrast","VALUE":200}Examples:
{"CMD":"SET","KEY":"display_contrast","VALUE":100} // Dim for dark environments
{"CMD":"SET","KEY":"display_contrast","VALUE":200} // Bright for high-light areas
{"CMD":"SET","KEY":"display_contrast","VALUE":255} // Maximum brightness
{"CMD":"SET","KEY":"display_contrast","VALUE":128} // DefaultFeedback: Modal dialog shows "OK | Contrast | 200 (78%)"
Note: Contrast persists immediately. Display updates in real-time.
Range: 5000 - 600000 ms (5 seconds - 10 minutes)
Default: 60000 ms (60 seconds)
{"CMD":"SET","KEY":"screensaver_timeout","VALUE":120000}Examples:
{"CMD":"SET","KEY":"screensaver_timeout","VALUE":30000} // 30 seconds (very active environment)
{"CMD":"SET","KEY":"screensaver_timeout","VALUE":60000} // 60 seconds (default)
{"CMD":"SET","KEY":"screensaver_timeout","VALUE":300000} // 5 minutes (less active use)
{"CMD":"SET","KEY":"screensaver_timeout","VALUE":5000} // 5 seconds (very sensitive)Feedback: Modal dialog shows "OK | Screensaver | 120 sec"
Note: Setting to longer duration reduces battery life. Setting very low may cause frequent wake-ups.
Range: 2-7 alphanumeric characters
Default: "MW01"
Purpose: Addresses the pager among multiple devices on shared UART
{"CMD":"SET","KEY":"magic_word","VALUE":"FLT01"}Examples:
{"CMD":"SET","KEY":"magic_word","VALUE":"MW01"} // Default multi-pager setup
{"CMD":"SET","KEY":"magic_word","VALUE":"FORKLIFT1"} // 8 chars - ERROR! Max 7
{"CMD":"SET","KEY":"magic_word","VALUE":"OP7"} // Short form (3 chars)
{"CMD":"SET","KEY":"magic_word","VALUE":"MAINT"} // Maintenance pager
{"CMD":"SET","KEY":"magic_word","VALUE":"QC"} // Quality control (too short - ERROR! Min 2)
{"CMD":"SET","KEY":"magic_word","VALUE":"SUPPLY01"} // Supply chain pager (8 chars - ERROR!)
{"CMD":"SET","KEY":"magic_word","VALUE":"LG02"} // Logistics pager (valid)Feedback: Modal dialog shows "OK | Magic Word | FLT01"
Important: Messages received without this prefix are rejected silently
Retrieve a single configuration parameter:
{"CMD":"GET","KEY":"magic_word"}Response: Modal dialog displays the current value
Examples:
{"CMD":"GET","KEY":"buzzer_duration"} // Shows current alarm duration
{"CMD":"GET","KEY":"display_contrast"} // Shows current contrast level
{"CMD":"GET","KEY":"screensaver_timeout"} // Shows current timeout
{"CMD":"GET","KEY":"magic_word"} // Shows current magic wordMessages are received via UART1 and must be prefixed with the magic word. The format is:
<MAGIC_WORD><MESSAGE_TEXT>
Examples (assuming magic_word = "MW01"):
MW01Material request: Bin 34, Type A, Qty 500
MW01Quality Alert: Station 3 - Defect detected
MW01Maintenance: Filter replacement due
What happens:
- Message is received and parsed
- Magic word is verified (message rejected if missing)
- Message is added to 50-message buffer
- Display wakes if in screensaver
- Buzzer alarm starts immediately (characteristic "Wup-Wup" pattern)
- Operator sees new message on display
{"CMD":"STATUS"} // Check defaults
{"CMD":"SET","KEY":"magic_word","VALUE":"FLT01"} // Set to Forklift 01
{"CMD":"SET","KEY":"buzzer_duration","VALUE":6000} // 6 second alarm
{"CMD":"GET","KEY":"magic_word"} // Verify it was set{"CMD":"SET","KEY":"buzzer_duration","VALUE":12000} // Longer alarm (12 sec)
{"CMD":"SET","KEY":"display_contrast","VALUE":255} // Max brightness
{"CMD":"SET","KEY":"screensaver_timeout","VALUE":120000} // 2 minute timeout{"CMD":"STATUS"} // See current config
{"CMD":"SET","KEY":"magic_word","VALUE":"MAINT02"} // Move to maintenance team
{"CMD":"SET","KEY":"display_contrast","VALUE":150} // Adjust brightness| Command | Key | Value | Range | Example |
|---|---|---|---|---|
| SET | buzzer_duration |
milliseconds | 100-20000 | {"CMD":"SET","KEY":"buzzer_duration","VALUE":8000} |
| SET | display_contrast |
0-255 | 0-255 | {"CMD":"SET","KEY":"display_contrast","VALUE":200} |
| SET | screensaver_timeout |
milliseconds | 5000-600000 | {"CMD":"SET","KEY":"screensaver_timeout","VALUE":120000} |
| SET | magic_word |
string | 2-7 chars | {"CMD":"SET","KEY":"magic_word","VALUE":"FLT01"} |
| GET | buzzer_duration |
(ignored) | - | {"CMD":"GET","KEY":"buzzer_duration"} |
| GET | display_contrast |
(ignored) | - | {"CMD":"GET","KEY":"display_contrast"} |
| GET | screensaver_timeout |
(ignored) | - | {"CMD":"GET","KEY":"screensaver_timeout"} |
| GET | magic_word |
(ignored) | - | {"CMD":"GET","KEY":"magic_word"} |
| STATUS | (N/A) | (N/A) | - | {"CMD":"STATUS"} |
If a command fails, a modal error dialog appears:
| Error | Cause | Solution |
|---|---|---|
| "ERROR | Range: | 100-20000ms" | buzzer_duration out of range |
Use value between 100 and 20000 |
| "ERROR | Range: | 0-255" | display_contrast out of range |
Use value between 0 and 255 |
| "ERROR | Range: | 5s-10min" | screensaver_timeout out of range |
Use value between 5000 and 600000 ms |
| "ERROR | Length: | 2-7 chars" | magic_word wrong length |
Magic word must be 2-7 characters |
| "ERROR | JSON | Invalid" | JSON parsing failed | Check JSON syntax (quotes, braces, commas) |
| "ERROR | Unknown | [KEY_NAME]" | Invalid key name | Use one of: buzzer_duration, display_contrast, screensaver_timeout, magic_word |
Viewing Messages: Use the rotary encoder to rotate left/right to navigate between messages. Messages wrap around (after the last message, you cycle back to the first).
Muting Alert: If an alert is currently sounding, press the encoder button once to mute it. The alarm continues in the background, but the buzzer is silent. Rotate the encoder to unmute.
Deleting a Message: Double-click the encoder button. A confirmation dialog appears. Rotate to select "Yes" or "No", then click to confirm.
Clearing All Messages: Press and hold the encoder button for 3 seconds. A confirmation dialog appears. Rotate to select, then click.
Checking Message History: Simply rotate the encoder to browse all stored messages. Each message shows its position in the queue ([X/Y] in the top left corner).
Configuration: Send JSON commands via the serial port connected to the Andon Mesh Vision gateway (see OTA Configuration System section above).
The modular architecture makes it straightforward to extend the pager with new features. Some potential enhancements:
- Acknowledged Status: Add an ACK button that sends a confirmation back to the Andon Mesh Vision system
- Priority Levels: Parse message priority from JSON and display with color or different alarm pattern
- GPS/Bluetooth Tracking: Integrate location services to track operator movements
- Local Wi-Fi Config: Add AP mode to allow configuration via web interface instead of serial commands
- Rotating Modes: Switch between different alarm patterns via configuration
Each of these would require changes primarily in one or two modules, minimizing the risk of introducing bugs elsewhere.
For questions, feature requests, or integration support with Andon Mesh Vision, contact:
NGIS PTE LTD
Author: Christian Zeh
Email: info@ng-is.com
This project is proprietary software owned by NGIS PTE LTD. Unauthorized use, reproduction, or distribution is strictly prohibited. All rights reserved. See the LICENSE file for the complete legal terms.
Built with ❤️ by NGIS PTE LTD for the future of smart manufacturing.