Controlling Router Peripherals
Digital Input/Output Interfaces
io Utility
The io utility allows controlling digital outputs and reading digital inputs and outputs from the command line.
Caution
Digital I/O often uses inverse logic. Active physical input (e.g., high voltage) might read as logical 0. Setting output to 1 might result in a low voltage physically. Always consult the router's Hardware Manual for I/O logic details.
Synopsis
io get <pin> or io set <pin> <value>
| Command | Description |
|---|---|
get <pin> | Reads the state of input <pin> (e.g., bin0, out0). |
set <pin> <value> | Sets the state of output <pin> to <value> (typically 0 or 1). |
io Utility Commands
Pin Names
Refer to the router's Hardware Manual for the available pins. Supported names are bin0–bin3 (alias DI0–DI3) for inputs and out0, out1 (alias DO0, DO1) for outputs, depending on the model. io get prints the state and also returns it as the exit code.
Examples
io set out0 1: Sets digital output OUT0 to state 1.io get bin0: Reads the state of digital input BIN0. Check exit code$?(0 or 1).
Activate Digital Output via SMS
Tips
See Handling Incoming SMS with a Custom Script for details on the custom SMS handling mechanism. Ensure this mechanism is enabled in the router's SMS configuration.
This example demonstrates the implementation of a new SMS command, "IMPULSE", which activates digital output OUT0 (a GPIO pin) for 5 seconds. It is triggered when an SMS containing the text "IMPULSE" is received by the router. The command is processed only if the sender is authorized.
Authorization Logic Options
The example script includes two common ways to authorize the sender:
- Check the flag passed by the system (
$1): If the sender's number is listed in the Authorized Number(s) field on the Configuration → Services → SMS page,$1is1. - Hardcode specific phone number(s) directly in the script and compare against the sender's number (
$2).
The example uses a combination (OR logic). Adjust the authorization check as needed for your requirements.
Startup Script
This script creates the /var/scripts/sms handler script in RAM at boot time.
#!/bin/sh
# Create the SMS handler script in RAM
cat > /var/scripts/sms << 'EOF'
#!/bin/sh
# Specify the authorized phone number
PHONE=+420123456789
if [ "$1" = "1" ] || [ "$2" = "$PHONE" ]; then
if [ "$3" = "IMPULSE" ]; then
io set out0 1
sleep 5
io set out0 0
fi
fi
EOFHow It Works
- The startup script creates the handler script
/var/scripts/sms. - Inside the handler script:
- It first checks for authorization:
[ "$1" = "1" ]: Is it an authorized sender?;||: Logical OR.[ "$2" = "$PHONE" ]: Is the sender's phone number hardcoded?
if [ "$3" = "IMPULSE" ]: Checks if the third parameter (the first word of the SMS text) is "IMPULSE". The router converts SMS words to uppercase before calling the script, so the sender can typeimpulseorIMPULSE.- If the command matches:
io set out0 1: Uses the Advantechioutility to set digital output OUT0 to state 1 (check the router manual for physical logic — often active-low).sleep 5: Pauses execution for 5 seconds.io set out0 0: Sets digital output OUT0 back to state 0 (inactive state).
- It first checks for authorization:
- This sequence effectively creates a 5-second pulse on OUT0 when an authorized SMS containing the word "IMPULSE" is received.
Send Email on Digital Input Activation
Tips
Make sure you have correctly configured the SMTP server in Configuration → Services → SMTP. Refer to Email Configuration Notes if applicable.
This script sends an informational email when the state of digital input BIN0 changes to active. It continuously monitors the input and sends a notification only when the input transitions from inactive (state 1) to active (state 0, assuming active-low logic).
Startup Script
#!/bin/sh
# Specify email address
EMAIL=john.doe@email.com
# Specify email subject
MESSAGE="BIN0 is active"
while true
do
io get bin0
VAL=$?
if [ "$VAL" != "$OLD" ]; then
[ "$VAL" = "0" ] && email -t $EMAIL -s "$MESSAGE"
OLD=$VAL
fi
sleep 1
doneHow It Works
- The script defines the destination email address (
EMAIL) and the email subject (MESSAGE). - It enters an infinite loop (
while true) to continuously monitor the input. - Inside the loop:
io get bin0: Reads the current state of digital input BIN0 using theioutility.VAL=$?: Captures the exit status ofio get. For digital inputs, this is typically 0 for the active state and 1 for the inactive state (verify with the router's Hardware Manual).if [ "$VAL" != "$OLD" ]: Checks if the current state (VAL) is different from the state stored from the previous iteration (OLD). This detects any change.[ "$VAL" = "0" ] && email -t $EMAIL -s "$MESSAGE": This is a short-circuit AND condition executed only if the state has changed. If the new state (VAL) is 0 (active), it executes theemailcommand to send the notification.OLD=$VAL: Updates the stored state (OLD) with the current state (VAL) for the next loop iteration.sleep 1: Pauses the script for 1 second before checking the input again.
- This logic ensures an email is sent only upon the transition from inactive (1) to active (0), avoiding repeated emails if the input remains active or becomes inactive.
Python Script
Here is an equivalent script in Python. Save the code below into a file named send_email.py.
#!/usr/bin/python3
import sys
import time
import subprocess
# Configuration
EMAIL_RECIPIENT = "john.doe@email.com"
EMAIL_SUBJECT = "BIN0 is active"
INPUT_PIN = "bin0" # The digital input pin to monitor
POLL_INTERVAL_SECONDS = 1
ACTIVE_STATE_EXIT_CODE = 0
def get_input_pin_state(pin_name):
try:
command = ["io", "get", pin_name]
process = subprocess.run(command, capture_output=True, text=True)
return process.returncode
except FileNotFoundError:
print(f"Error: The 'io' command was not found. Please ensure it's in the PATH.", file=sys.stderr)
return None
except Exception as e:
print(f"An unexpected error occurred while getting pin {pin_name} state: {e}", file=sys.stderr)
return None
def send_email_notification(recipient, subject):
try:
command = ["email", "-t", recipient, "-s", subject]
subprocess.run(command, check=True, capture_output=True, text=True)
print(f"Email notification sent to {recipient} with subject: {subject}", file=sys.stderr)
except FileNotFoundError:
print(f"Error: The 'email' command was not found. Please ensure it's in the PATH.", file=sys.stderr)
except subprocess.CalledProcessError as e:
print(f"Error executing 'email' command: {e.stderr.strip()}", file=sys.stderr)
except Exception as e:
print(f"An unexpected error occurred while sending email: {e}", file=sys.stderr)
def main():
old_state = None
print(f"Monitoring {INPUT_PIN} every {POLL_INTERVAL_SECONDS} second(s). Press Ctrl+C to stop.", file=sys.stderr)
try:
while True:
current_state = get_input_pin_state(INPUT_PIN)
if current_state is not None:
if current_state != old_state:
if current_state == ACTIVE_STATE_EXIT_CODE:
print(f"Input {INPUT_PIN} transitioned to ACTIVE. Sending email.", file=sys.stderr)
send_email_notification(EMAIL_RECIPIENT, EMAIL_SUBJECT)
old_state = current_state
time.sleep(POLL_INTERVAL_SECONDS)
except KeyboardInterrupt:
print("\nMonitoring stopped by user.", file=sys.stderr)
except Exception as e:
print(f"An unexpected error occurred in the main loop: {e}", file=sys.stderr)
if __name__ == "__main__":
main()How It Works
This Python script continuously monitors a specified digital input pin on the router. If the pin transitions to an active state, the script sends an email notification.
- Initialization and Configuration:
- The script starts with a shebang line (
#!/usr/bin/python3), ensuring it is executed by the Python 3 interpreter. - It imports standard Python modules:
sys(for system interaction, though minimally used here beyondsys.stderr),time(for pausing execution), andsubprocess(for running external router commands likeioandemail). - Essential configuration parameters are defined at the beginning:
EMAIL_RECIPIENT: The email address to which notifications will be sent.EMAIL_SUBJECT: The subject line for the notification email.INPUT_PIN: The name of the digital input pin to be monitored (e.g., "bin0").POLL_INTERVAL_SECONDS: The duration in seconds the script waits between checks of the input pin's state.ACTIVE_STATE_EXIT_CODE: Defines the exit code returned by theio get <pin_name>command that signifies the pin is in its active state (typically 0 for Advantech routers).
- The script starts with a shebang line (
- Reading Input Pin State (
get_input_pin_statefunction):- This function is responsible for querying the current state of the specified digital input pin.
- It constructs and executes the router's command
io get <pin_name>usingsubprocess.run(). - The function returns the exit code of the
io getcommand. For Advantech router digital inputs, an exit code of 0 usually indicates an active state, and 1 indicates an inactive state. - It includes error handling: if the
iocommand is not found or another error occurs during execution, it prints an error message to standard error (sys.stderr) and returnsNone.
- Sending Email Notification (
send_email_notificationfunction):- This function handles the sending of email alerts.
- It takes the recipient email address and the email subject as arguments.
- It forms and executes the router's command
email -t <recipient> -s <subject>usingsubprocess.run(). This relies on the router having a configuredemailutility. - Error handling is included to catch issues such as the
emailcommand not being found or the command failing (e.g., mail server misconfiguration), printing error messages tosys.stderr.
- Main Monitoring Loop (
mainfunction):- A variable
old_stateis initialized toNone, representing an unknown initial state of the input pin. - A startup message is printed to
sys.stderr, indicating which pin is being monitored and the polling interval. - The script enters an infinite loop (
while True:) to continuously monitor the input pin. This loop can be interrupted by pressing Ctrl+C (KeyboardInterrupt). - Inside the loop:
current_state = get_input_pin_state(INPUT_PIN): The current state of the input pin is read.if current_state is not None:: The script only proceeds if the pin state was successfully read (i.e.,io getdid not fail).if current_state != old_state:: This condition checks if the pin's state has changed since the last check. This is key to sending an email only upon a state transition.if current_state == ACTIVE_STATE_EXIT_CODE:: If the state has changed and the new state is the defined active state (e.g., exit code 0), an informational message is printed tosys.stderr, and thesend_email_notificationfunction is called.old_state = current_state: After processing any change,old_stateis updated to thecurrent_statefor the next iteration. This ensures that an email is sent only once when the pin transitions to active, and not repeatedly if it remains active.
time.sleep(POLL_INTERVAL_SECONDS): The script pauses for the specified interval before repeating the loop.
- The main loop is wrapped in a
try...except KeyboardInterruptblock to allow the user to stop the script gracefully. A generalexcept Exceptionblock is also present to catch other unexpected errors during the loop's execution.
- A variable
- Script Execution Entry Point:
- The standard
if __name__ == "__main__":construct ensures that themain()function is called only when the script is executed directly.
- The standard
Script Testing
Below is the console output, displaying the script's creation (using vi) and its testing.
/var/scripts # vi send_email.py
(...create and save the script with vi...)
/var/scripts # chmod +x send_email.py
/var/scripts # ./send_email.py
Monitoring bin0 every 1 second(s). Press Ctrl+C to stop.
Input bin0 transitioned to ACTIVE. Sending email.
Email notification sent to john.doe@email.com with subject: BIN0 is active
^C
Monitoring stopped by user.
/var/scripts #Send SNMP Trap on Digital Input State Change
Tips
Enable SNMP in Configuration → Services → SNMP. The snmptrap utility uses the community configured there; the SNMP manager's address is set in the script.
This script sends an SNMP trap to the SNMP manager whenever the state of digital input BIN0 (a GPIO pin) changes (either becoming active or inactive). It continuously monitors the input state.
Startup Script
#!/bin/sh
# Specify SNMP manager address
SNMP_MANAGER=192.168.1.2
while true
do
io get bin0
VAL=$?
if [ "$VAL" != "$OLD" ]; then
snmptrap $SNMP_MANAGER 1.3.6.1.4.1.30140.2.3.1.0 u $VAL
OLD=$VAL
fi
sleep 1
doneHow It Works
- The script defines the IP address of the SNMP manager (
SNMP_MANAGER). - It enters an infinite loop (
while true) for continuous monitoring. - Inside the loop:
io get bin0: Reads the current state of digital input BIN0.VAL=$?: Captures the exit status (state: 0=active, 1=inactive).if [ "$VAL" != "$OLD" ]: Checks if the state has changed since the last check.- If the state has changed:
snmptrap $SNMP_MANAGER 1.3.6.1.4.1.30140.2.3.1.0 u $VAL: Sends an SNMP trap.$SNMP_MANAGER: The destination IP address.1.3.6.1.4.1.30140.2.3.1.0: The specific OID (Object Identifier) being sent. This OID represents the state ofbin0.u: Specifies the data type of the value being sent as Unsigned32.$VAL: The current state (0 or 1) of the input pin.
OLD=$VAL: Updates the stored state for the next comparison.
sleep 1: Pauses for 1 second before the next check.
- This script sends an SNMP trap containing the specific OID and the new state (0 or 1) every time the digital input changes state.
Serial Interfaces
This chapter provides an overview of using serial interfaces on Advantech routers. It covers identifying available serial ports, command-line utilities for their configuration and use, and an example of interacting with a serial port using a Python script packaged as a Router App via the Advantech ModulesSDK.
Identifying Serial Interfaces
Advantech routers support various serial interface standards, with RS-232 and RS-485 being common. The specific interfaces available (e.g., physical DB9 ports, terminal blocks, or those provided via expansion modules like PORT1/PORT2) and their parameters (e.g., dedicated ttyS* device, configurable modes) vary by router model. Always consult the manual for your specific router model for detailed specifications.
To list available serial device nodes on a router, you can use the following console command. The presence of a device node in /dev/ (e.g., /dev/ttyS0, /dev/ttyUSB0) indicates that the kernel recognizes a serial interface. However, this does not always mean a physical port is directly accessible on the device exterior without additional configuration or hardware. Serial interfaces can also be provided by USB-to-UART converters, which typically appear as /dev/ttyUSB* devices.
~ # ls -l /dev/tty*
crw-rw-rw- 1 root root 5, 0 Jan 1 1970 /dev/tty
crw------- 1 root root 249, 0 Jan 1 1970 /dev/ttyS0
crw-rw---- 1 root daemons 249, 1 Jan 1 1970 /dev/ttyS1
crw-rw---- 1 root daemons 249, 5 Jan 1 1970 /dev/ttyS5
crw-rw---- 1 root daemons 188, 0 May 26 06:56 /dev/ttyUSB0
crw-rw---- 1 root daemons 188, 1 May 26 06:56 /dev/ttyUSB1
... (other ttyUSB* devices if present) ...Serial port configuration (baud rate, data bits, parity, stop bits) can be managed using command-line utilities or programmatically, as detailed in the following sections.
Command-Line Utilities for Serial Ports
Two common utilities for managing serial ports from the command line are stty and portd.
stty - Set and Print Terminal Line Settings
The stty program is used to change and print terminal line settings, including those for serial ports. It allows you to configure parameters like baud rate, character size, parity, stop bits, and flow control.
Synopsis:
stty [-a|g] [-F DEVICE] [SETTING]...
Common Options:
| Option | Description |
|---|---|
-F DEVICE | Open and use the specified DEVICE instead of standard input. |
-a | Print all current settings in human-readable form. |
-g | Print all current settings in a stty-readable form (can be used to save and restore settings). |
[SETTING]... | One or more settings to apply. Common settings include: • <N>: Set speed to N bits per second (e.g., 115200).• cs<N>: Set character size to N bits (cs7 or cs8).• cstopb: Use two stop bits (-cstopb for one).• parenb: Enable parity generation/detection.• -parodd: Use even parity (if parenb is set); parodd for odd parity.• -inpck: Disable input parity checking.• ignpar: Ignore characters with parity errors.• [-]raw: Enable (or disable) raw input.• [-]echo: Enable (or disable) echo of input characters.• [-]crtscts: Enable (or disable) RTS/CTS hardware flow control.• [-]ixon: Enable (or disable) XON/XOFF software flow control.For a full list, see the BusyBox stty documentation. |
Common stty Options and Settings.
Examples:
To display all current settings for the serial port /dev/ttyS0:
stty -F /dev/ttyS0 -aTo display only the current speed of /dev/ttyS1:
stty -F /dev/ttyS1 speedTo configure /dev/ttyS0 to 115200 bps, 8 data bits, no parity, 1 stop bit (8N1), and enable raw mode:
stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb rawportd - Serial Port to TCP/UDP Redirector
The portd daemon is a utility that provides transparent data transfer between a serial line and a TCP or UDP network connection. It can operate either as a server (listening for incoming network connections and forwarding data to/from the serial port) or as a client (connecting to a remote network host and forwarding data). This is often used for "Serial-to-Ethernet" or "Serial-over-IP" applications.
Synopsis:
portd -c <device> [-b <baudrate>] [-d <databits>] [-p <parity>] [-s <stopbits>]
[-l <split timeout>] [-4] [-h <hostname>] [-o <proto>] -t <port>
[-k <keepalive time>] [-i <keepalive interval>] [-j <inactivity timeout>]
[-n] [-r <keepalive probes>] [-u user] [-x] [-z] [-f]
Supported Options:
| Option | Description |
|---|---|
-c <device> | Required. Serial line device (e.g., /dev/ttyS0). |
-b <baudrate> | Baud rate (e.g., 115200). Default 9600. |
-d <databits> | Number of data bits (5–8; 5 and 6 are not supported by the internal UART on some platforms). Default 8. |
-p <parity> | Parity: none, even, odd. Default none. |
-s <stopbits> | Number of stop bits (1 or 2). Default 1. |
-l <split timeout> | Data split timeout in milliseconds. If no data arrives from the serial port for this timeout, buffered data is sent over the network. Default 20. |
-4 | Forced detection for RS-485 on an Expansion Port. (Advantech-specific feature). |
-h <hostname> | Remote hostname or IP address to connect to (client mode). If not specified, portd runs in server mode. |
-o <proto> | Network protocol: tcp or udp. |
-t <port> | Required. TCP or UDP port number. |
-k <keepalive time> | TCP Keepalive: Time (seconds) of inactivity before sending the first keepalive probe. Default: disabled. |
-i <keepalive intvl> | TCP Keepalive: Interval (seconds) between subsequent keepalive probes. Default 10. (Often named <keepalive interval> in help). |
-j <inactivity to> | Inactivity timeout in seconds. If no data is transferred on the network connection for this period, the connection might be closed. (Often named <inactivity timeout> in help). |
-n | Reject new incoming connections while a connection is active (flag without value). |
-r <keepalive probes> | TCP Keepalive: Number of unacknowledged probes before considering the connection dead. Default 5. |
-u <user> | Run portd as a specified user after starting (drops root privileges if started as root). |
-x | Use CD (Carrier Detect) line as an indicator of TCP connection status (server mode). |
-z | Use DTR (Data Terminal Ready) line to control/reflect TCP connection status (server mode). |
-f | Enable RTS/CTS hardware flow control (ignored in RS-485 mode). |
Supported portd options based on router's help output.
Examples:
To run portd as a TCP server on port 1000, redirecting data to/from /dev/ttyS0 configured at 115200 bps, 8 data bits, no parity, 1 stop bit, and with flow control enabled, running it in the background:
portd -c /dev/ttyS0 -b 115200 -d 8 -p none -s 1 -f -o tcp -t 1000 &To run portd as a TCP client, connecting to 192.168.1.100 on port 2000, forwarding data from /dev/ttyS1 (9600 bps, 8N1), and setting an inactivity timeout of 300 seconds:
portd -c /dev/ttyS1 -b 9600 -h 192.168.1.100 -o tcp -t 2000 -j 300 &Scripting Serial Communication with the um Python Module
This example demonstrates how to use the Advantech um Python module to communicate over a serial port (e.g., /dev/ttyS0) on the router. The Python script will be packaged as a Router App using the ModulesSDK.
Caution
To run Python scripts on the router, the Python 3 or Python 3 Lite Router App must be installed from the router's web interface (Customization → Router Apps) or be part of the firmware.
Step 1: Writing the Python Script
We will create a simple Python script named serial_um_example.py. This script will open /dev/ttyS0, send a command, attempt to read a response, and then print the response. For this example to fully work, a device capable of responding must be connected to /dev/ttyS0 and configured with matching serial parameters.
Source code for serial_um_example.py:
This file will be placed in ModulesSDK/modules/serial_um_example/source/serial_um_example.py.
#!/usr/bin/python3
import um
import sys # For exiting with error code
# Define serial port parameters
SERIAL_DEVICE = b"/dev/ttyS0"
BAUD_RATE = 115200
DATA_BITS = 8
PARITY = b"N" # None
STOP_BITS = 1
# Command to send and timeout for response
COMMAND_TO_SEND = b"ATI\r\n" # Example: AT command to request modem info
RESPONSE_TIMEOUT_SEC = 5
def main():
print(f"Attempting to open serial port: {SERIAL_DEVICE.decode()} at {BAUD_RATE} bps...")
fd = um.com_open(SERIAL_DEVICE, BAUD_RATE, DATA_BITS, PARITY, STOP_BITS)
if fd < 0:
print(f"Error: Failed to open serial port {SERIAL_DEVICE.decode()}. Error code: {fd}")
sys.exit(1)
print(f"Serial port opened successfully (fd: {fd}). Sending command...")
try:
received_data = um.com_xmit(fd, COMMAND_TO_SEND, RESPONSE_TIMEOUT_SEC)
print(f"Sent to {SERIAL_DEVICE.decode()}: {COMMAND_TO_SEND.decode(errors='replace').strip()}")
if received_data:
print(f"Received from {SERIAL_DEVICE.decode()}:")
# Print byte-by-byte hex and ASCII for detailed view if needed
# print("Hex: " + " ".join(f"{b:02x}" for b in received_data))
print(f"ASCII: {received_data.decode(errors='replace')}")
else:
print("No data received within the timeout period.")
except Exception as e:
print(f"An error occurred during serial communication: {e}")
finally:
print(f"Closing serial port (fd: {fd})...")
um.com_close(fd)
print("Serial port closed.")
if __name__ == "__main__":
main()How the Script Works
The script performs the following actions:
- Imports the necessary
ummodule (andsysfor exit codes). - Defines constants for serial port parameters, the command to send (an AT command
ATIwhich typically requests modem identification), and a timeout for waiting for a response. - The
main()function is the entry point. - It calls
um.com_open()to open and configure the specified serial port. - If opening fails (indicated by a negative file descriptor
fd), it prints an error and exits. - If successful, it calls
um.com_xmit()to send theCOMMAND_TO_SENDand wait up toRESPONSE_TIMEOUT_SECseconds for a response. - It then prints the sent command and the received data (if any). The received data is decoded from bytes to a string, replacing any non-decodable characters.
- A
try...finallyblock ensures thatum.com_close()is called to close the serial port, even if an error occurs during communication. - The standard
if __name__ == "__main__":idiom ensuresmain()is called when the script is executed directly.
Step 2: Creating the Router App Package Files (Simplified)
This step involves preparing the necessary files for the ModulesSDK to package the Python script as a Router App. For this simple command-line script, we do not need complex init, install, or uninstall scripts. The SDK's default packaging will usually place the script in a bin/ directory within the Router App's installation path (e.g., /opt/<app_name>/bin/). We will also omit metadata files like name and version for this basic example, though they are recommended for more complete Router Apps.
The primary file we need is our Python script itself (serial_um_example.py), which should be placed in the module's source/ directory within the ModulesSDK.
Step 3: Preparing Makefiles for ModulesSDK Integration
The Advantech ModulesSDK uses a Makefile-based build system. To integrate our serial_um_example Python application:
- Create the module directory: If it doesn't already exist, create a directory for your new module within the SDK. For example:
ModulesSDK/modules/serial_um_example/. - Place the Python script: Copy or move your
serial_um_example.pyscript intoModulesSDK/modules/serial_um_example/source/serial_um_example.py. - Copy the main Makefile template: Copy the generic module
MakefilefromModulesSDK/modules/template/Makefileto your new module's directory:ModulesSDK/modules/serial_um_example/Makefile. This main Makefile generally handles the overall process of building the module for different platforms and creating the.tgzpackage. It usually calls theMakefilewithin thesource/directory. - Create the source Makefile: Inside the
ModulesSDK/modules/serial_um_example/source/directory, create a newMakefilewith the following content. This Makefile defines how your Python script and any associatedummodule dependencies are installed into the Router App package. - Register the module: Add a line with the target platforms to
ModulesSDK/modules/Rules.mk, for example:serial_um_example = v2i v3 v4 v4i. The build loops only over the platforms listed there.
Content for ModulesSDK/modules/serial_um_example/source/Makefile:
include ../../../Rules.mk
all:
@true
clean:
@true
install:
@install -d $(DESTDIR)/bin
@install -m 644 $(SDKDIR)/library/$(OBJDIR)/*.so $(DESTDIR)/bin/
@install -m 644 $(SDKDIR)/library/*.py $(DESTDIR)/bin/
@install -m 755 *.py $(DESTDIR)/bin/Resulting Directory Structure for the serial_um_example Module within SDK:
After these steps, the directory structure for your serial_um_example module within the ModulesSDK should look like this:
ModulesSDK/
|-- Rules.mk (and other root SDK files)
|-- library/ (Example location for um.py and libum.so)
| |-- um.py
| |-- obj.v4/ (built per platform by the SDK)
| | `-- libum.so
| |-- obj.v4i/
| | `-- libum.so
| |-- ...
|-- modules/
| |-- serial_um_example/
| | |-- source/
| | | |-- serial_um_example.py (Your Python script)
| | | `-- Makefile (Source Makefile specific to this module)
| | `-- Makefile (Main module Makefile, copied from template)
| |-- template/
| | `-- Makefile (Original template main Makefile)
| |-- ... (Other example modules)
`-- ... (Other SDK directories)Step 4: Building the Router App Package
Once the Python script and Makefiles are correctly placed within the ModulesSDK structure:
- Navigate to the SDK's root directory:
user@machine:~$ cd /path/to/your/ModulesSDK/- Build for all modules:
Often, simply running make from the root of the SDK might build all modules for all default platforms if the main SDK Makefile is structured that way.
user@machine:/ModulesSDK$ makeThe SDK's build system will invoke the Makefiles you prepared. The install target in your source/Makefile will copy serial_um_example.py, um.py, and the appropriate libum.so into the staging area. The main module Makefile will then package these files into a *.tgz archive. The resulting packages are written to ModulesSDK/images/serial_um_example/.
Step 5: Uploading and Testing the Router App
Upload the generated *.tgz archive file for your target platform (e.g., serial_um_example.v4.tgz) to your Advantech router. This is typically done via the router's web interface, in the section Customization → Router Apps.
After the Router App is installed by the system:
- The Python script, along with
um.pyandlibum.so, should be installed into a directory under/opt/, typically/opt/serial_um_example/bin/. - Test from the router's CLI:
- Run script from any location using the full path:
/opt/serial_um_example/bin/serial_um_example.py- Observe the output. The script will attempt to communicate with
/dev/ttyS0. If a device is connected and responds to "ATI", you should see its response. Otherwise, you will see a "No data received" message or an error if the port cannot be opened.
This example provides a basic framework for packaging a Python script that uses the um module as a Router App. For more complex applications, you might need to include more sophisticated install/uninstall scripts, manage dependencies, or handle background services with an init script.
USB Interface
Storage Access — USB Flash and SD Card
Connecting a USB device or SD card works in the standard Linux way. When you connect a USB Flash drive to the router, its device node will appear in the /dev directory. You can view details about detected devices using the dmesg command.
- USB Flash drive partitions typically appear as
/dev/sda1. You can mount them using themountcommand (e.g.,mount -t vfat /dev/sda1 /mnt). - Some USB-to-Serial converters are supported and will show up as
/dev/ttyUSB0,/dev/ttyUSB1, etc. (see Supported USB Serial Converter Chips). - An SD Card inserted into the router's reader usually appears with partitions like
/dev/mmcblk0p1. You can mount it similarly (e.g.,mount -t vfat /dev/mmcblk0p1 /mnt).
Info
Firmware version 6.6.2 and later also supports the exFAT file system, so you can directly use USB flash drives and SD cards formatted in Microsoft Windows (e.g., mount -t exfat /dev/sda1 /mnt).
Mounting a USB Flash Drive Partition
To access files on a USB flash drive partition within the router's system, it must first be mounted. Follow these steps:
- Connect the USB Flash Drive: Plug the USB flash drive into the router's USB port.
- Identify the Device Partition: Run
dmesg | tailto display recent system messages. Look for lines indicating the new device name (e.g.,sda) and its partitions (e.g.,sda1). Note the partition identifier, such as/dev/sda1. - Create a Mount Point (Optional but Recommended): Create an empty directory where the filesystem will be mounted. Using
/mntis common practice.mkdir -p /mnt/usb - Mount the Partition: Use the
mountcommand to attach the partition to the mount point. The system often auto-detects the filesystem type.mount /dev/sda1 /mnt/usb - Verify Successful Mount: List mounted filesystems using
mount | grep /mnt/usbor check the contents of the mount point directory (ls /mnt/usb) to confirm access. - Unmount the Partition: Before physically removing the drive, unmount it using the mount point or device name to prevent data corruption.or
umount /mnt/usbumount /dev/sda1
Once unmounted, the USB flash drive can be safely removed. Ensure the correct device name and filesystem type (if specifying manually) are used.
Tips
If the mount command fails, double-check the device name (/dev/sda1) and try specifying the filesystem type with the -t option: mount -t vfat /dev/sda1 /mnt/usb.
Automount USB Flash Disk
Tips
This script provides a basic mechanism to automatically mount the first partition of a detected USB flash drive to /mnt/flash when inserted, and unmount it when removed. The monitoring script should be saved to a file (e.g., /root/automount.sh) and launched via the Startup Script.
This example demonstrates how to create a background script that monitors for the presence of a USB flash drive and automatically mounts/unmounts its first partition.
Monitoring Script (automount.sh)
Save the following code into a file, for example, /root/automount.sh.
#!/bin/sh
#
LAST=0
i=0
while true
do
flsh=`cat /proc/diskstats |awk '/8\x20\x20\x20\x20\x20\x20\x201/ {print $3}'`
if [ "$flsh" ]; then
i=1
else
i=0
fi
if [ $LAST != $i ]; then
LAST=$i
if [ $i = 1 ]; then
echo "Mount flash disk."
if [ -d /mnt/flash ]; then
mount /dev/$flsh /mnt/flash
else
mkdir /mnt/flash
mount /dev/$flsh /mnt/flash
fi
else
echo "UMOUNT flash disk."
umount /mnt/flash
rmdir /mnt/flash
fi
fi
sleep 2
doneStartup Script
Add the following line to your Startup Script to launch the monitoring script in the background when the router boots. Ensure the path (/root/automount.sh) matches where you saved the monitoring script.
#!/bin/sh
# Launch the automount script in the background
sh /root/automount.sh &How It Works
The Startup Script simply executes the saved
automount.shscript in the background usingsh ... &.The
automount.shscript runs an infinite loop (while true).Inside the loop, it reads
/proc/diskstats, a kernel interface providing disk I/O statistics.It uses
awkto search for a line matching the pattern/ 8 1 /. This pattern specifically looks for:- The major device number
8(commonly used for SCSI/SATA/USB block devices like/dev/sdX). - Exactly seven spaces (
\x20represents a space in the originalawkpattern). - The minor device number
1(commonly representing the first partition, e.g.,sda1).
If a matching line is found,
awkprints the third field ($3), which is the device name (e.g.,sda1).- The major device number
The device name is stored in the
flshvariable.if [ "$flsh" ]: This checks if theflshvariable is non-empty. If the device partition was found, the variable will contain its name (likesda1), and the condition is true, settingito 1 (detected). Otherwise,iis set to 0 (not detected). Quotes around"$flsh"prevent potential errors if the variable is empty.if [ $LAST != $i ]: The script compares the current detection state (i) with the state from the previous loop iteration (LAST). It only proceeds if the state has changed (device inserted or removed).LAST=$i: Updates the stored state for the next iteration.If state changed to 1 (Device Detected):
- Prints "Mount flash disk."
- Checks if the directory
/mnt/flashexists using[ -d ... ]and creates it withmkdirif it does not. - Executes
mount /dev/$flsh /mnt/flashto mount the detected partition (e.g.,/dev/sda1) onto the/mnt/flashdirectory. Filesystem type is typically auto-detected.
If state changed to 0 (Device Removed/Not Detected):
- Prints "UMOUNT flash disk."
- Executes
umount /mnt/flashto unmount the filesystem. - Executes
rmdir /mnt/flashto remove the mount point directory. Note thatrmdirwill fail if the directory is not empty (e.g., if the unmount failed or files were created outside the mount).
sleep 2: The script pauses for 2 seconds before repeating the loop.
Caution
The method used to detect the USB drive by parsing /proc/diskstats for the specific pattern / 8 1 / is very basic and potentially fragile. It will likely only work for the first partition (sda1) of the first detected USB drive. It may fail if the drive uses different major/minor numbers, has multiple partitions you wish to access, or if other block devices interfere. More robust solutions often involve using udev rules or dedicated automount daemons if available on the system.
Tips
After adding the automount.sh script (e.g., to /root/) and configuring the Startup Script to launch it, reboot the router. When you insert a compatible USB flash drive, its first partition should automatically become accessible under /mnt/flash.
Supported USB Serial Converter Chips
Advantech routers include built-in kernel support (drivers) for several common USB-to-serial converter chipsets. The drivers are built into the kernel, so an adapter using one of these chips appears as a serial device node in the /dev directory when connected.
Supported chip families generally include:
- FTDI: FT232R, FT232H, FT2232, FT4232, FT230X, etc. (Driver:
ftdi_sio) - Silicon Labs: CP210x series (e.g., CP2101, CP2102, CP2104) (Driver:
cp210x) - Prolific: PL2303 (various versions, support might vary) (Driver:
pl2303) - CDC-ACM: Standard USB class used by many modems and microcontroller boards (Driver:
cdc_acm). CH340/CH341 chips are not CDC-ACM devices and are not supported.
Adapters handled by ftdi_sio, cp210x, and pl2303 appear as /dev/ttyUSB0, /dev/ttyUSB1, etc.; CDC-ACM devices appear as /dev/ttyACM0, /dev/ttyACM1, etc.
You can verify this in console with: dmesg | grep -i ttyUSB
Using an Unsupported Serial Converter Chip
In some cases, you may need to use a USB-to-Serial converter whose specific Vendor ID (VID) and Product ID (PID) combination is not natively recognized by the pre-loaded kernel drivers (like ftdi_sio or pl2303), even if the underlying chip is technically supported by the driver. Every USB device is identified by these two hexadecimal numbers:
- Vendor ID (VID): Identifies the device manufacturer (e.g.,
0403for FTDI). - Product ID (PID): Identifies the specific product model (e.g.,
6001for FT232R).
Finding the VID and PID
You can discover the VID and PID of your USB device in several ways:
- On a Linux PC: Use the
lsusbcommand. The output lists connected devices with their IDs inVID:PIDformat. Note thatlsusbcommand is not available on the router itself. - On Windows: Open Device Manager, find the device (it might appear as an unknown device or under "Ports (COM & LPT)" or "Universal Serial Bus controllers"), right-click, select Properties, go to the Details tab, and choose "Hardware Ids" from the Property dropdown. Look for a string like
USB\VID_xxxx&PID_yyyy. - On the Router: Check kernel messages using
dmesgimmediately after plugging in the device. Look for lines likeusb 1-1: new full-speed USB device number X using ...and potentially lines showingidVendor=xxxx, idProduct=yyyy.
Dynamically Enabling the Device via sysfs
Once you have the VID and PID (as four-digit hexadecimal numbers without the 0x prefix) and know the appropriate kernel driver module name for the chip type (e.g., ftdi_sio, pl2303, cp210x), you can attempt to dynamically tell the driver to handle this specific VID/PID combination using the new_id interface within the sysfs filesystem:
Syntax:
echo <VID> <PID> > /sys/bus/usb-serial/drivers/ftdi_sio/new_idReplace <VID> and <PID> with the four-digit hex values (without 0x).
Example (for VID=0403, PID=d921):
echo 0403 d921 > /sys/bus/usb-serial/drivers/ftdi_sio/new_idAfter running this command, check dmesg again. If successful, you should see messages indicating the driver has claimed the device and created a serial port device node (e.g., /dev/ttyUSB0). If not, the driver might not support the underlying chip type.
Caution
This dynamic binding is temporary and will be lost on reboot. To make it persistent, this command must be executed from a Startup Script or a Router App's init script each time the router boots or the device is connected.
User LED
led Utility
The led utility provides basic command-line control over the user-controllable LED (often labeled "USR" or similar) typically found on the router's front panel.
Synopsis
led [on | off]
| Option | Description |
|---|---|
on | Turns the USR LED on (solid state). |
off | Turns the USR LED off. |
led Utility Options
Check IPsec Connection Status via LED
Tips
This script monitors the status of a specific IPsec tunnel and uses the USR LED to indicate whether the tunnel is established. It uses the swanctl command (part of strongSwan) and standard Linux utilities like awk and grep. The script should be saved to a file (e.g., /root/ipsec_stat.sh) and launched from the Startup Script.
This example provides a simple script to visually indicate the status of an IPsec connection using the router's USR LED. It checks the status every 5 seconds.
Monitoring Script (ipsec_stat.sh)
Save the following code into a file, for example, /root/ipsec_stat.sh. Adjust the num variable to match the IPsec connection number you want to monitor (typically 1, 2, 3, or 4, corresponding to the configuration order in the GUI).
#!/bin/sh
# ipsec_stat.sh - Monitors IPsec tunnel status and controls USR LED
num=1 # number of IPSec connection to monitor [1,2,3,4]
while true
do
# List Security Associations and filter for the desired IPsec connection
# then check if the line contains "INSTALLED"
/usr/libexec/ipsec/swanctl --list-sas | awk "/ipsec$num/" | grep INSTALLED
sts=$? # Capture the exit status of grep (0 if INSTALLED found, non-zero otherwise)
# Control USR LED based on the status
if [ "$sts" = "0" ]; then
led on # Turn LED ON if tunnel SA is INSTALLED
else
led off # Turn LED OFF otherwise
fi
# Wait before next check
sleep 5
doneStartup Script
Add the following line to your Startup Script (Configuration → Scripts → Startup Script) to launch the monitoring script in the background when the router boots. Ensure the path (/root/ipsec_stat.sh) is correct.
#!/bin/sh
# Launch the IPsec status monitoring script in the background
sh /root/ipsec_stat.sh &
exit 0How It Works
- The Startup Script executes the saved
ipsec_stat.shscript in the background usingsh ... &. - The
ipsec_stat.shscript first sets a variablenumto specify which IPsec connection instance (1-4) to monitor. - It enters an infinite loop (
while true). - Inside the loop:
/usr/libexec/ipsec/swanctl --list-sas: This command lists the current IPsec Security Associations (SAs), providing detailed status information for active tunnels.| awk "/ipsec$num/": The output ofswanctlis piped toawk. This filters the lines to only include those containing the specific connection name pattern (e.g.,ipsec1ifnum=1).| grep INSTALLED: The filtered output is then piped togrepto check if the line contains the word "INSTALLED". A successfully established SA typically shows this state.sts=$?: The exit status of thegrepcommand is captured in the variablests.grepreturns 0 if it finds a match ("INSTALLED" was found for the specified tunnel), and a non-zero value otherwise.if [ "$sts" = "0" ]: The script checks if the exit status is 0.- If
stsis 0 (tunnel is installed/established), it executesled onto turn the USR LED on. - If
stsis non-zero (tunnel is not installed or the line wasn't found), it executesled offto turn the USR LED off.
- If
sleep 5: The script pauses for 5 seconds before repeating the check.
- This provides a continuous visual indication of the specified IPsec tunnel's status via the USR LED.
Tips
After creating the ipsec_stat.sh script (e.g., in /root/), setting the correct IPsec connection number in the num variable, adding the launch command to the Startup Script, and rebooting the router, the USR LED should light up when the monitored IPsec tunnel is established.
Tips
As an alternative to polling swanctl every 5 seconds, enter led on in the User's Up Script field and led off in the User's Down Script field of the IPsec tunnel configuration (Configuration → IPsec, on the page of the corresponding tunnel). The router runs these scripts automatically when the tunnel comes up or goes down.
Indicate OpenVPN Status via LED
Tips
Advantech routers lack an OpenVPN "management" port, making it difficult to precisely determine if the VPN connection is fully established. However, the router can run user commands when the OpenVPN tunnel goes up and down. This example uses these commands to control the USR LED, providing a visual indication that the OpenVPN process is running, though not necessarily that the connection is fully established and passing traffic.
This example uses the commands triggered by OpenVPN's up and down events to control the router's USR LED.
Configuration
In Configuration → OpenVPN, open the page of the tunnel (e.g., 1st Tunnel), enter led on in the User's Up Script field and led off in the User's Down Script field, then click Apply.
How It Works
- The router runs the User's Up Script (
led on) when the OpenVPN tunnel comes up, turning the USR LED on. - The router runs the User's Down Script (
led off) when the OpenVPN tunnel goes down, turning the USR LED off. - This setup provides a basic visual cue: the LED is on when the OpenVPN process believes the tunnel is up, and off when it is stopped or down.
Caution
As noted, this method only indicates if the OpenVPN process has successfully executed its 'up' script. It does not guarantee that the VPN tunnel connection itself is successfully established, authenticated, or functional for passing traffic.