EC700 Development Guide: Getting Started with the AI Edge Computing Industrial PC

This guide helps developers quickly get up and running on the EC700 — covering serial port communication, digital I/O control, REST API integration, pre-installed software management, and boot logo customization.

The EC700 is an AI edge computing industrial PC powered by the Rockchip RK3588J with a built-in 6 TOPS NPU, designed for on-device AI inference. It’s commonly deployed in smart manufacturing, industrial vision inspection, and other AI scenarios. See the full spec sheet on the EC700 product page.

Prerequisites and Tools

This guide applies to the EC700 gateway. Before diving in, make sure you have the following tools ready. Please download the following tools via the download page.

SSH Client

MobaXterm is recommended, or you may use any SSH client you’re familiar with.

Serial Debug Tool

XCOM is recommended, or you may use any serial terminal you prefer.

Hardware Accessories

Common auxiliary debugging tools include USB-to-RS485 adapters, Ethernet cables, etc. Please prepare these yourself.

Cross-Compilation Toolchain

If you’re compiling native C/C++ applications on a host PC and deploying to the EC700.

Software Environment

The EC700 ships with a full embedded Linux environment. Here’s what’s running under the hood:

Software Version
OS Embedded Linux
Kernel Linux 6.1.118
Node.js v22.17.0
Python Python 3.10.12
Shell bash
Docker V27.4.1
Qt V5.15.3
Desktop Environment Xfce4

Device Resources

Category Details Remarks
Storage 128 GB total ~108 GB free out of the box; expandable via SD card or M.2 NVMe SSD
Memory 8 GB ~6 GB free out of the box
CPU RK3588 ~98% idle out of the box
NPU 6 TOPS ~100% idle out of the box

Peripheral Interfaces

1. Debug Port

The EC700 exposes the system debug serial port via a Type-C interface. Use these settings in your serial terminal:

2. Serial Ports (RS485 / RS232)

The EC700 provides two RS485 channels and one RS232 channel:

Hardware Interface Device File
RS485-1 /dev/ttyS3 (corresponds to A1 / B1)
RS485-2 /dev/ttyS4 (corresponds to A2 / B2)
RS232 /dev/ttyS1 (corresponds to TXD / RTD)

Note: The RS485 ports use automatic direction control. You do not need to switch between transmit and receive modes manually — the hardware handles it automatically.

2.1 Quick Test

Use the minicom utility for testing. Refer to online resources or consult GPT/ Google for usage details.

2.2 C Code Example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/select.h>

#define BUFFER_SIZE 256

typedef struct {
  int baud_rate; // Baud rate
  int data_bits; // Data bits (5,6,7,8)
  int stop_bits; // Stop bits (1,2)
  char parity; // Parity bit (N: None, O: Odd, E: Even)
} SerialConfig;

int set_serial_attr(int fd, SerialConfig *config)
{
  struct termios tty;

  if (tcgetattr(fd, &tty) < 0) {
    perror("tcgetattr");
    return -1;
  }

  // Set Baud rate 
  speed_t speed;
  switch (config->baud_rate) {
    case 9600: speed = B9600; break;
    case 19200: speed = B19200; break;
    case 38400: speed = B38400; break;
    case 57600: speed = B57600; break;
    case 115200: speed = B115200; break;
    default:
        fprintf(stderr, "Unsupported baud rate, using 115200\n");
        speed = B115200;
  }
  cfsetispeed(&tty, speed);
  cfsetospeed(&tty, speed);

  // Set Data bits
  tty.c_cflag &= ~CSIZE;
  switch (config->data_bits) {
    case 5: tty.c_cflag |= CS5; break;
    case 6: tty.c_cflag |= CS6; break;
    case 7: tty.c_cflag |= CS7; break;
    case 8: tty.c_cflag |= CS8; break;
  default:
    fprintf(stderr, "Unsupported data bits, using 8\n");
    tty.c_cflag |= CS8;
  }

  // Set Stop bits
  if (config->stop_bits == 2) {
  tty.c_cflag |= CSTOPB;
  } else {
  tty.c_cflag &= ~CSTOPB;
  }

  // Set Parity bit
  switch (config->parity) {
    case 'N': case 'n':
      tty.c_cflag &= ~PARENB; // None
      break;
    case 'O': case 'o':
      tty.c_cflag |= PARENB; // Odd
      tty.c_cflag |= PARODD;
      break;
    case 'E': case 'e':
      tty.c_cflag |= PARENB; // Even
      tty.c_cflag &= ~PARODD;
      break;
    default:
      fprintf(stderr, "Unsupported parity, using N\n");
      tty.c_cflag &= ~PARENB;
  }

  // Other settings
  tty.c_cflag |= (CLOCAL | CREAD); // Enable receiver and local mode
  tty.c_cflag &= ~CRTSCTS; // Disable hardware flow control

  tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw input mode
  tty.c_oflag &= ~OPOST; // Raw output mode

  tty.c_cc[VMIN] = 1; // Minimum number of characters to read
  tty.c_cc[VTIME] = 0; // Read timeout (unit: 0.1 seconds)

  if (tcsetattr(fd, TCSANOW, &tty) < 0) {
    perror("tcsetattr");
    return -1;
  }

  return 0;
}

int main(int argc, char *argv[])
{
  int fd;
  char *portname;

  if (argc < 2) {
    fprintf(stderr, "Usage: %s <serial_port>\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  portname = argv[1];

  // Configure serial port parameters
  SerialConfig config = {
    .baud_rate = 115200, // Baud rate
    .data_bits = 8, // Data bits
    .stop_bits = 1, // Stop bits
    .parity = 'N' // Parity (N: None, O: Odd, E: Even)
  };

  fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
  if (fd < 0) {
    perror("open");
    exit(EXIT_FAILURE);
  }

  if (set_serial_attr(fd, &config)) {
    close(fd);
    exit(EXIT_FAILURE);
  }

  printf("Serial port echo test running on %s\n", portname);
  printf("Configuration: %d baud, %d data bits, %d stop bit, %c parity\n",
  config.baud_rate, config.data_bits, config.stop_bits, config.parity);
  printf("Press Ctrl+C to exit.\n");

  fd_set readfds;
  char buffer[BUFFER_SIZE];
  int n;

  while (1) {
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);

    // Wait indefinitely for data to arrive (timeout setting removed)
    if (select(fd + 1, &readfds, NULL, NULL, NULL) < 0) {
      perror("select");
      break;
    }

    if (FD_ISSET(fd, &readfds)) {
      n = read(fd, buffer, BUFFER_SIZE - 1);
      if (n > 0) {
        buffer[n] = '\0';
        printf("Received %d bytes: %s\n", n, buffer);

        // Echo data
        write(fd, buffer, n);
    } else if (n < 0) {
      if (errno != EAGAIN && errno != EWOULDBLOCK) {
        perror("read");
        break;
        }
      }
    }
  }

  close(fd);
  return 0;
}

Compile: gcc rs485_example.c -o rs485_example

Test RS485-1: ./rs485_example /dev/ttyS3

2.3 Python Code Example

import serial
import select
import sys
import tty

class SerialConfig:
  def __init__(self):
    self.baud_rate = 115200 # Baud rate
    self.data_bits = 8 # Data bits
    self.stop_bits = 1 # Stop bits
    self.parity = 'N' # Parity (N: None, O: Odd, E: Even)

def set_serial_config(ser, config):
  """Configure serial port parameters"""
  # Set baud rate
  ser.baudrate = config.baud_rate
  
  # Set data bits
  if config.data_bits == 5:
    ser.bytesize = serial.FIVEBITS
  elif config.data_bits == 6:
    ser.bytesize = serial.SIXBITS
  elif config.data_bits == 7:
    ser.bytesize = serial.SEVENBITS
  else: # Default to 8 bits
    ser.bytesize = serial.EIGHTBITS

  # Set stop bits
  if config.stop_bits == 2:
    ser.stopbits = serial.STOPBITS_TWO
  else: # Default to 1 bit
    ser.stopbits = serial.STOPBITS_ONE

  # Set parity
  if config.parity.upper() == 'O':
    ser.parity = serial.PARITY_ODD
  elif config.parity.upper() == 'E':
    ser.parity = serial.PARITY_EVEN
  else: # Default to none
    ser.parity = serial.PARITY_NONE
  
  # Disable hardware flow control
  ser.rtscts = False
  # Disable software flow control
  ser.xonxoff = False
  # Set timeout
  ser.timeout = 0.1 # 100ms timeout

  return ser

def main():
  if len(sys.argv) < 2:
    print(f"Usage: {sys.argv[0]} <serial_device>")
    print(f"Example: {sys.argv[0]} /dev/ttyS7")
    sys.exit(1)

  portname = sys.argv[1]
  buff_size = 256
  
  # Initialize serial configuration
  config = SerialConfig()
  
  try:
    # Open serial port
    ser = serial.Serial()
    ser.port = portname
    # Apply configuration and open serial port
    ser = set_serial_config(ser, config)
    ser.open()

    if not ser.is_open:
      print("Failed to open serial port")
      sys.exit(1)

    print(f"Serial port {portname} opened successfully")
    print(f"Config: Baud rate {config.baud_rate}, Data bits {config.data_bits}, "
f"Stop bits {config.stop_bits}, Parity {config.parity}")
    print("Press Ctrl+C to exit")
    
    # Use select to monitor serial port data
    while True:
      # Wait for data to arrive on serial port
      readable, _, _ = select.select([ser.fileno()], [], [], None)

      if readable:
        # Read data
        data = ser.read(buff_size - 1)
        if data:
          # Try decoding as string, show hex if failed
         try:
            text = data.decode('utf-8')
         except UnicodeDecodeError:
            text = f"[Binary data] {data.hex()}"

         print(f"Received {len(data)} bytes: {text}")

        # Echo data
        ser.write(data)

  except serial.SerialException as e:
    print(f"Serial error: {e}")
    sys.exit(1)
  except KeyboardInterrupt:
    print("\nUser interrupted, exiting")
  finally:
    if 'ser' in locals() and ser.is_open:
      ser.close()
      print("Serial port closed")
if __name__ == "__main__":
  main()

Install the dependency first: pip install pyserial

Test RS485-1: python uart_example.py /dev/ttyS3

3. DI

The EC700 provides one digital input channel for detecting dry-contact signals, corresponding to the DIN+ and DIN- terminals.

Hardware interface IO index
DI-1 130

3.1 Quick Test

#!/bin/bash
# EC700 DI (Digital Input) Test
# Usage: bash di_example.sh <GPIO_NUM>
# Example: bash di_example.sh 130

if [ $# -lt 1 ]; then
  echo "Usage: bash $0 <GPIO_NUM>"
  echo "Example: bash $0 130"
  exit 1
fi

GPIO=$1
GPIO_PATH=/sys/class/gpio/gpio${GPIO}

echo "EC700 DI Test - GPIO $GPIO"
echo "--------------------------"

# Export GPIO
if [ ! -d "$GPIO_PATH" ]; then
  echo "$GPIO" > /sys/class/gpio/export 2>/dev/null
  sleep 0.1
fi

if [ ! -d "$GPIO_PATH" ]; then
  echo "ERROR: Cannot export GPIO $GPIO"
  exit 1
fi

# Set direction to input
echo "in" > ${GPIO_PATH}/direction

echo ""
echo "Reading DI value..."
echo "Short DIN+ to 3.3V -> value=1"
echo "Short DIN+ to GND -> value=0"
echo "Press Ctrl+C to stop"
echo ""

# Read loop
while true; do
  VAL=$(cat ${GPIO_PATH}/value 2>/dev/null)
  if [ "$VAL" != "$LAST" ]; then
    echo "[$(date +%H:%M:%S)] DI = $VAL"
    LAST=$VAL
  fi
  sleep 0.2
done

Run: bash di_example.sh 130

After running the script, repeatedly short or open DIN+ and DIN- to observe the digital input state changes.

3.2 C Code Example

/*
* EC700 DI (Digital Input) Test
* Usage: ./di_example <GPIO_NUM>
* Example: ./di_example 130
*
* Connect DIN+ to 3.3V -> reads 1
* Connect DIN+ to GND -> reads 0
* Ctrl+C to stop
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>

static int keep_running = 1;

static void sig_handler(int sig)
{
  (void)sig;
  keep_running = 0;
}

/* return 0 on success, -1 on error */
static int gpio_write_file(const char *path, const char *value)
{
  int fd = open(path, O_WRONLY);
  if (fd < 0) {
    perror("open");
    return -1;
  }
  if (write(fd, value, strlen(value)) < 0) {
    perror("write");
    close(fd);
    return -1;
  }
  close(fd);
  return 0;
}

/* return value char on success, -1 on error */
static int gpio_read_value(const char *path)
{
  char buf[4] = {0};
  int fd = open(path, O_RDONLY);
  if (fd < 0) {
    perror("open");
    return -1;
  }
  if (read(fd, buf, sizeof(buf) - 1) < 0) {
    perror("read");
    close(fd);
    return -1;
  }
  close(fd);
  return buf[0];
}

int main(int argc, char *argv[])
  {
    int gpio;
    char path[128];
    char export_str[16];
    int last_val = -1;

    if (argc < 2) {
      fprintf(stderr, "Usage: %s <GPIO_NUM>\n", argv[0]);
      fprintf(stderr, "Example: %s 130\n", argv[0]);
      return 1;
  }

  gpio = atoi(argv[1]);
  if (gpio < 0 || gpio > 512) {
    fprintf(stderr, "Invalid GPIO number: %d\n", gpio);
    return 1;
  }

  signal(SIGINT, sig_handler);
  signal(SIGTERM, sig_handler);

  printf("EC700 DI Test - GPIO %d\n", gpio);
  printf("--------------------------\n");

  /* Export GPIO */
  snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", gpio);

  if (access(path, F_OK) != 0) {
    snprintf(export_str, sizeof(export_str), "%d", gpio);
  if (gpio_write_file("/sys/class/gpio/export", export_str) < 0) {
    fprintf(stderr, "ERROR: Cannot export GPIO %d\n", gpio);
    return 1;
  }
  usleep(100000); /* 100ms settle time */
  }

  /* Set direction to input */
  snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio);
  if (gpio_write_file(path, "in") < 0) {
    fprintf(stderr, "ERROR: Cannot set GPIO %d to input\n", gpio);
    return 1;
  }

  printf("\nReading DI value...\n");
  printf("Short DIN+ to 3.3V -> value=1\n");
  printf("Short DIN+ to GND -> value=0\n");
  printf("Press Ctrl+C to stop\n\n");

  /* Read loop */
  while (keep_running) {
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
    int val = gpio_read_value(path);
    if (val < 0) {
      fprintf(stderr, "ERROR: Cannot read GPIO %d value\n", gpio);
    break;
  }

  if (val != last_val) {
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    printf("[%02d:%02d:%02d] DI = %c\n",
    tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
val);
    fflush(stdout);
    last_val = val;
  }

  usleep(10000); /* 10ms */
}

  /* Unexport */
  printf("\nCleaning up...\n");
  snprintf(export_str, sizeof(export_str), "%d", gpio);
  gpio_write_file("/sys/class/gpio/unexport", export_str);

  printf("Done.\n");
  return 0;
}

Compile: gcc di_example.c -o di_example

Run: ./di_example 130

3.3 Python Code Example

#!/usr/bin/env python3
"""
EC700 DI (Digital Input) Test
Usage: python3 di_example.py <GPIO_NUM>
Example: python3 di_example.py 130

Connect DIN+ to 3.3V -> reads 1
Connect DIN+ to GND -> reads 0
Ctrl+C to stop
"""

import sys
import os
import time

GPIO_BASE = "/sys/class/gpio"

def gpio_export(num):
path = os.path.join(GPIO_BASE, "export")
with open(path, "w") as f:
f.write(str(num))

def gpio_unexport(num):
  path = os.path.join(GPIO_BASE, "unexport")
  with open(path, "w") as f:
    f.write(str(num))

def gpio_set_direction(num, direction):
  path = os.path.join(GPIO_BASE, f"gpio{num}", "direction")
  with open(path, "w") as f:
    f.write(direction)

def gpio_read_value(num):
  path = os.path.join(GPIO_BASE, f"gpio{num}", "value")
  with open(path, "r") as f:
    return f.read().strip()

def main():
  if len(sys.argv) < 2:
    print(f"Usage: python3 {sys.argv[0]} <GPIO_NUM>")
    print(f"Example: python3 {sys.argv[0]} 130")
    sys.exit(1)

gpio = int(sys.argv[1])

gpio_path = os.path.join(GPIO_BASE, f"gpio{gpio}")

print(f"EC700 DI Test - GPIO {gpio}")
print("-" * 26)

# Export GPIO
if not os.path.exists(gpio_path):
  try:
    gpio_export(gpio)
    time.sleep(0.1)
  except Exception as e:
    print(f"ERROR: Cannot export GPIO {gpio}: {e}")
    sys.exit(1)

if not os.path.exists(gpio_path):
  print(f"ERROR: GPIO {gpio} export failed")
  sys.exit(1)

# Set direction
try:
  gpio_set_direction(gpio, "in")
except Exception as e:
  print(f"ERROR: Cannot set GPIO {gpio} to input: {e}")
  gpio_unexport(gpio)
  sys.exit(1)

print()
print("Reading DI value...")
print("Short DIN+ to 3.3V -> value=1")
print("Short DIN+ to GND -> value=0")
print("Press Ctrl+C to stop")
print()

last_val = None

try:
  while True:
    val = gpio_read_value(gpio)
    if val != last_val:
      now = time.strftime("%H:%M:%S", time.localtime())
      print(f"[{now}] DI = {val}")
      last_val = val
    time.sleep(0.01)
except KeyboardInterrupt:
  pass
except Exception as e:
  print(f"ERROR: {e}")
finally:
  print("\nCleaning up...")
  try:
    gpio_unexport(gpio)
  except Exception:
    pass
  print("Done.")

if __name__ == "__main__":
  main()

Run: python di_example.py 130

4. DO

The EC700 provides two relay output channels supporting normally-open dry-contact output for industrial relays, used to control external circuit on/off states. Hardware Interface:

IO IO Index IO Number Chip Group
DO-1 129 GPIO4_A gpiochip1
DO-2 128 GPIO4_A  gpiochip0

4.1 Quick Test

#!/bin/bash
# EC700 DO (Digital Output) Test
# Usage: bash do_example.sh <GPIO_NUM> <on|off>
# DOUT1: bash do_example.sh 129 on
# DOUT2: bash do_example.sh 128 off

if [ $# -lt 1 ]; then
  echo "Usage: bash $0 <GPIO_NUM> [on|off]"
  echo " GPIO: 129=DOUT1 128=DOUT2"
  echo " If on/off omitted: toggle"
  exit 1
fi

GPIO=$1
GPIO_PATH=/sys/class/gpio/gpio${GPIO}

# Export
if [ ! -d "$GPIO_PATH" ]; then
  echo "$GPIO" > /sys/class/gpio/export 2>/dev/null
  sleep 0.1
fi

# Set direction
echo "out" > ${GPIO_PATH}/direction 2>/dev/null

# Read current value
CUR=$(cat ${GPIO_PATH}/value 2>/dev/null)

if [ $# -ge 2 ]; then
  case "$2" in
    on|1) VAL=1 ;;
    off|0) VAL=0 ;;
    *) echo "Invalid: $2 (use on/off or 1/0)"; exit 1 ;;
  esac
else
  # Toggle
  if [ "$CUR" = "1" ]; then VAL=0; else VAL=1; fi
fi

echo "$VAL" > ${GPIO_PATH}/value
CUR=$(cat ${GPIO_PATH}/value)
[ "$CUR" = "1" ] && STATE="ON (Relay Closed)" || STATE="OFF (Relay Open)"
echo "DOUT GPIO=$1 -> $STATE"

# Cleanup
echo "$GPIO" > /sys/class/gpio/unexport 2>/dev/null

Compile: gcc do_example.c -o do_example

Run: ./do_example 129 on

4.2 C Code Example

/*
* EC700 DO (Digital Output) Test
* Usage: ./do_example <GPIO_NUM> [on|off]
* GPIO 129 = DOUT1 128 = DOUT2
* Without on/off: toggle
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

static int gpio_write_file(const char *path, const char *value)
{
  int fd = open(path, O_WRONLY);
  if (fd < 0) { perror("open"); return -1; }
  if (write(fd, value, strlen(value)) < 0) { perror("write"); close(fd); return -1; }
  close(fd);
  return 0;
}

static int gpio_read_value(const char *path)
{
  char buf[4] = {0};
  int fd = open(path, O_RDONLY);
  if (fd < 0) { perror("open"); return -1; }
  if (read(fd, buf, sizeof(buf) - 1) < 0) { perror("read"); close(fd); return -1; }
  close(fd);
  return buf[0];
}

int main(int argc, char *argv[])
{
  int gpio, val;
  char path[128], export_str[16], val_str[2];

  if (argc < 2) {
    fprintf(stderr, "Usage: %s <GPIO_NUM> [on|off]\n", argv[0]);
    fprintf(stderr, " GPIO 129=DOUT1 128=DOUT2\n");
    fprintf(stderr, " Without on/off: toggle\n");
    return 1;
}

gpio = atoi(argv[1]);
snprintf(export_str, sizeof(export_str), "%d", gpio);
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d", gpio);

/* Export */
if (access(path, F_OK) != 0) {
  gpio_write_file("/sys/class/gpio/export", export_str);
  usleep(100000);
}

/* Set direction */
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", gpio);
gpio_write_file(path, "out");

/* Read current */
snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", gpio);
int cur = gpio_read_value(path);

if (argc >= 3) {
  if (strcmp(argv[2], "on") == 0 || strcmp(argv[2], "1") == 0)
    val = 1;
  else if (strcmp(argv[2], "off") == 0 || strcmp(argv[2], "0") == 0)
    val = 0;
  else {
    fprintf(stderr, "Invalid value: %s (use on/off or 1/0)\n", argv[2]);
    gpio_write_file("/sys/class/gpio/unexport", export_str);
    return 1;
  }
} else {
    val = (cur == '1') ? 0 : 1; /* toggle */
}

snprintf(val_str, sizeof(val_str), "%d", val);
gpio_write_file(path, val_str);

cur = gpio_read_value(path);
printf("DOUT GPIO=%d -> %s (%s)\n", gpio,
(cur == '1') ? "ON (Relay Closed)" : "OFF (Relay Open)",
(cur == '1') ? "COM-NO Closed" : "COM-NO Open");

/* Unexport */
gpio_write_file("/sys/class/gpio/unexport", export_str);

return 0;
}

4.3 Python Code Example

 #!/usr/bin/env python3
"""
EC700 DO (Digital Output) Test
Usage: python3 do_example.py <GPIO_NUM> [on|off]
GPIO 129 = DOUT1 128 = DOUT2
Without on/off: toggle
"""

import sys
import os
import time

GPIO_BASE = "/sys/class/gpio"

def gpio_export(num):
  with open(os.path.join(GPIO_BASE, "export"), "w") as f:
    f.write(str(num))

def gpio_unexport(num):
  with open(os.path.join(GPIO_BASE, "unexport"), "w") as f:
    f.write(str(num))

def gpio_set_direction(num, direction):
  path = os.path.join(GPIO_BASE, f"gpio{num}", "direction")
  with open(path, "w") as f:
    f.write(direction)

def gpio_write_value(num, value):
  path = os.path.join(GPIO_BASE, f"gpio{num}", "value")
  with open(path, "w") as f:
    f.write(str(value))

def gpio_read_value(num):
  path = os.path.join(GPIO_BASE, f"gpio{num}", "value")
  with open(path, "r") as f:
    return f.read().strip()

def main():
  if len(sys.argv) < 2:
    print(f"Usage: python3 {sys.argv[0]} <GPIO_NUM> [on|off]")
    print(" GPIO 129=DOUT1 128=DOUT2")
    print(" Without on/off: toggle")
    sys.exit(1)

  gpio = int(sys.argv[1])
  gpio_path = os.path.join(GPIO_BASE, f"gpio{gpio}")

  # Export
  if not os.path.exists(gpio_path):
    try:
      gpio_export(gpio)
      time.sleep(0.1)
    except Exception as e:
      print(f"ERROR: export GPIO {gpio}: {e}")
      sys.exit(1)

  # Set direction
  gpio_set_direction(gpio, "out")

  # Read current
  cur = gpio_read_value(gpio)

  if len(sys.argv) >= 3:
    arg = sys.argv[2].lower()
    if arg in ("on", "1"):
      val = 1
    elif arg in ("off", "0"):
      val = 0
    else:
      print(f"Invalid value: {arg} (use on/off or 1/0)")
      gpio_unexport(gpio)
      sys.exit(1)
  else:
    val = 0 if cur == "1" else 1 # toggle

  gpio_write_value(gpio, val)
  cur = gpio_read_value(gpio)

  if cur == "1":
    state = "ON (Relay Closed) COM-NO Closed"
  else:
    state = "OFF (Relay Open) COM-NO Open"

  print(f"DOUT GPIO={gpio} -> {state}")

  gpio_unexport(gpio)

if __name__ == "__main__":
main()

Run: python do_example.py 129 on

5. Ethernet Ports

The Ethernet ports are divided into WAN and LAN, managed by the internal network service. Configuration is done via the web. See: [EC Series ARM Industrial Computer Quick Start / Preparation].

6. Cellular Wireless (Optional)

The EC700 product supports 4G/5G communication (please contact customer service to purchase 4G/5G communication modules). Module identification and dialing are managed internally, requiring no user intervention. For more details, please refer to: [EC Series ARM Industrial Computer Quick Start / Preparation]

Note: The SIM card does not support hot-swapping. Power off the device before inserting or removing the SIM card.

7. WiFi (Optional)

The EC700 supports both WiFi hotspot and WiFi client modes (consult customer service for WiFi module purchases). It supports 2.4 GHz and 5 GHz bands. WiFi is managed by the internal program — no user intervention required. Please refer to: [EC Series ARM Industrial Computer Quick Start / Preparation]

8. Bluetooth (Optional)

The EC300 product series supports Bluetooth functionality.

8.1 Quick Test

# 1. View local Bluetooth hardware status
hciconfig -a

# 2. Enter Bluetooth interactive console
bluetoothctl

# Bluetooth initialization, essential for pairing
# Power on the Bluetooth controller
power on
# Turn on the pairing agent to handle pairing pop-ups/verification codes
agent on
# Set as default agent
default-agent

# Turn on scanning, wait for [NEW] Device AA:BB:CC:DD:EE:FF target device to be printed before pairing
scan on

# Clear old device pairing cache (optional, resolves cache abnormalities)
remove AA:BB:CC:DD:EE:FF
# Initiate pairing, confirm by typing yes in the pop-up, PIN code is usually 0000/1234
pair AA:BB:CC:DD:EE:FF
# Trust the device to support automatic reconnection
trust AA:BB:CC:DD:EE:FF
# Establish Bluetooth connection
connect AA:BB:CC:DD:EE:FF

# Turn off scanning to stop log flooding
scan off

# Debug query commands
# View all scanned devices
devices
# View all paired devices
paired-devices
# View currently connected devices
connections
# View target device complete protocols and connection status
info AA:BB:CC:DD:EE:FF

# Disconnect device connection
disconnect AA:BB:CC:DD:EE:FF
# Turn off Bluetooth power (execute as needed)
power off
# Exit Bluetooth console
exit

9. Audio

The EC700 supports a 3.5mm headset jack for microphone recording and speaker playback.

9.1 Quick Test

#Recording
# Record 10 seconds of audio
arecord -D hw:1,0 -f cd -t wav test.wav -d 10

#Playback:
#Enter the command to open the sound debugging console
alsamixer

#Press F6 to select default:1 rockchip-es8388
#Press Enter to enter
#Ensure <Headphon><OUT1> is set to [00], press the M key to toggle the state
#Use the following command to play the audio in the current folder
aplay -D hw:1,0 test.wav

10. Display

10.1 HDMI Output

The EC700 supports up to 4K @ 60 fps video output.

Video output: Connect a monitor to the HDMI port. The device auto-adjusts the resolution on boot. If display issues occur, try switching the monitor resolution to 1080P.

Audio output: Connect an audio-capable monitor to the HDMI port and use the desktop media player to test:

/userdata/piano2-CoolEdit.mp3

10.2 HDMI Input

Supports 1080P60 video input.

Video Input:

# Connect the PC to the device via HDMI, and use the following command to check if the connection is successful
v4l2-ctl -d /dev/video0 -D
# Start capturing images
v4l2-ctl --verbose -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat='BGR3' --stream-mmap=4 --stream-skip=0 --stream-to=~/Desktop/1080P60.bgr --stream-count=50 --stream-poll
# Captures 50 frames by default. After capture is complete, it is saved as 1080P60.bgr at the path ~/Desktop/1080P60.bgr
# You can use an image viewer that supports BGR to open it and verify whether the output color and resolution are normal.
# It is recommended to use the 7YUV program for viewing.

Audio Input:

# Ensure the HDMI RX cable is connected between the PC and the device, ensure audio is playing on the PC, and specify the output sound card as RK xxx in Win10.
# Execute the following command on the board to capture HDMI RX audio input (the default HDMIRX input sound card is hw:0,0):
arecord -D hw:0,0 -f dat hdmi_rx.wav
# Stop capturing after a period of time by pressing Ctrl+C. The file will be saved in the current directory.
# Copy the audio file hdmi_rx.wav to the Win10 PC for playback. The sound should match.

System REST API

The EC700 exposes a REST API for querying device and network status programmatically. Both endpoints accept GET requests with no parameters.

1. Get Device Basic Information

    • URL: GET http://{device_IP}/rpc-api/data/devinfo
    • Method: GET
    • Request parameters: None

Response fields:

Field Description Type Remarks
code Response status code int Always 200 on success
data.model Device model string Device model
data.sn Device Serial number string Device Serial number
data.version Device Version string Software version number

Response example:

{
  "code": 200,
  "data": {
    "model": "EC700",
    "sn": "xxxxxxxxxxxx",
    "version": "x.x.x"
  }
}

 

2. Get Device WWAN (Cellular) Network Information

  • URL: GET http://{device_IP}/rpc-api/data/wwaninfo
  • Method: GET
  • Request parameters: None

Response fields:

Field Description Type Remarks
code Response status code int Always 200 on success
data.enable Cellular enabled boolean true = enabled, false = disabled
data.status Dial-up status number 0 = powered on, 1 = initializing, 2 = SIM detection, 3 = PDP activated, 4 = dialing, 5 = ready
data.ip WWAN IP address string IP assigned by cellular network
data.mask Subnet mask string Subnet mask
data.gateway Gateway address string Default gateway
data.metric Route metric number Lower value = higher priority
data.dns DNS server string DNS resolver address
data.version Module firmware version string 5G/4G module firmware version
data.imei IMEI string Module IMEI number
data.signal Signal strength number 0–100, higher = stronger signal
data.ccid SIM card ID string SIM ICCID
data.operator Carrier string Currently registered carrier name
data.sim.maxcall Max redial attempts number Retry limit on dial failure
data.sim.sim0.auth APN auth method number 0 = None, 1 = PAP, 2 = CHAP, 3 = PAP/CHAP
data.sim.sim0.addr APN address string Access point name
data.sim.sim0.name APN username string APN auth username
data.sim.sim0.passwd APN password string APN auth password
data.sim.sim0.pinCode PIN code string SIM card PIN code
data.ping.enable Network probe boolean true = enabled, false = disabled
data.ping.addr0 Probe host 0 string Network probe target address 1
data.ping.addr1 Probe host 1 string Network probe target address 2
data.ping.cyc Probe interval number Interval in seconds
data.ping.num Probe count number Failure threshold count
data.ping.state Probe status number 255 = not probed, 0 = failed, 1 = success

Response example:

{
  "code": 200,
  "data": {
  "enable": true,
  "status": 0,
  "ip": "",
  "mask": "",
  "gateway": "",
  "metric": 3,
  "dns": "223.5.5.5 8.8.8.8",
  "version": "",
  "imei": "",
  "signal": -1,
  "ccid": "",
  "operator": "",
  "sim": {
    "maxcall": 5,
    "sim0": {
      "auth": 3,
      "addr": "",
      "name": "",
      "passwd": "",
      "pinCode": ""
      },
   },
  "ping": {
    "enable": true,
    "addr0": "223.5.5.5",
    "addr1": "8.8.8.8",
    "cyc": 10,
    "num": 3,
    "state": 255
    }
  }
}

 

Pre-Installed Software

The EC700 comes with several industrial software packages pre-installed and configured for auto-start. Here’s how to manage each one.

1. Internal Management Program (iotrouter)

To simplify user experience, the EC700 series ships with the built-in iotrouter management program that starts on boot. The management program covers:

  • Device initialization
  • Network management
  • Cellular management
  • WiFi management
  • Firewall
  • Device configuration service (default port 80, configurable; file: /usr/local/src/iotrouter/web/user-config.js)
  • File browser (default path: /run/media/; configurable; file: /usr/local/src/iotrouter/web/user-config.js)

Note: It is recommended to keep the internal program running. If you must disable it, you will need to take over the above management services yourself.

2. NeuronEX

The EC700 comes with NeuronEX-Lite pre-installed. The service starts automatically on boot and listens on port 8085.

For an introduction, see: [EC Series ARM Industrial Computer Learning Path]

View service status: systemctl status neuronex

Restart service: systemctl restart neuronex

Stop service: systemctl stop neuronex

Disable auto-start: systemctl disable neuronex

3. Node-RED

The EC700 comes with Node-RED pre-installed. The service starts automatically on boot and listens on port 1880. For an introduction, see: [EC Series ARM Industrial Computer Learning Path]

View service status: systemctl status node-red

Restart service: systemctl restart node-red

Stop service: systemctl stop node-red

Disable auto-start: systemctl disable node-red

 

4. FUXA

All EC700 series products come with FUXA pre-installed. The service starts automatically on boot and listens on port 1881. For an introduction, see: [Advanced Development Guide for EC Series ARM Industrial Computers]

View service status: systemctl status fuxa

Restart service: systemctl restart fuxa

Stop service: systemctl stop fuxa

Disable auto-start: systemctl disable fuxa

5. Xfce4 Desktop

The default desktop environment is Xfce4. This is provided as a demonstration only. If you need to display other content, such as a Qt-based UI, you can develop it yourself.

The display manager is: lightdm

View service status: systemctl status lightdm

Restart service: systemctl restart lightdm

Stop service: systemctl stop lightdm

Disable auto-start: systemctl disable lightdm

 

6. Custom User Programs

User applications are developed independently by users and run as separate processes. However, careful attention must be paid to memory and storage management to prevent system exceptions. Software can be added to start automatically via the system using the following methods:

  • /etc/rc.local
  • systemd service
  • /etc/init.d system

We also offer custom software development services. For inquiries, please contact our sales team.

7. Boot Logo Customization

Users can replace the system boot logo themselves.

Logo File Display Stage
logo.bmp Displayed during the U-Boot phase
logo_kernel.bmp Displayed during the kernel boot phase

7.1 Format Requirements

The logo image must be a 24-bit BMP bitmap. The image resolution should not exceed the resolution of the connected HDMI display.

7.2 Logo Replacement

  1. Power on the device.
  2. Copy your custom logo.bmp and logo_kernel.bmp files to the SDK_PATH/kernel-6.1 directory, replacing the existing files of the same name.

image

Note: The logo filenames must not be changed.

Wrapping Up

The EC700 is built to get you from concept to deployed prototype fast. Between the open Linux environment, pre-installed industrial software stack (Node-RED, NeuronEX, FUXA), and straightforward hardware interfaces, most of the plumbing is already done — you just need to write your application logic and wire up your sensors and actuators.

A few things worth keeping in mind as you start developing:

  • Start with the debug serial port. Even if you plan to work over SSH, the Type-C debug port is your fallback when the network isn’t cooperating.
  • Leverage the REST API. The /rpc-api/data/ endpoints make it trivial to pull device info and cellular status into your own dashboards or monitoring scripts — no need to parse AT commands yourself.
  • Respect the iotrouter service. It handles network, WiFi, and cellular management behind the scenes. If you disable it, plan to reimplement those functions yourself.
  • Mind your storage budget. With 128 GB on board and NVMe expansion available, you have room to breathe — but Docker images and AI model weights add up quickly.

If you’re looking for the full hardware specifications, protocol support matrix, or OEM/ODM customization options, check out the EC700 product page. For questions about SDK access, custom development, or bulk ordering, the IOTRouter team is ready to help — just reach out through the contact form on the site.