Índice
AlternarOs computadores industriais ARM da série EC funcionam com uma versão otimizada do Ubuntu, proporcionando uma base estável para cenários industriais. Além disso, vêm com o seguinte pré-instalado: Nó-vermelho e NeuronEX-Lite, proporcionando uma clara vantagem em termos de diferenciação.
- Nó-vermelho: Uma ferramenta de programação visual de código aberto. Oferece uma interface de orquestração de fluxos baseada no navegador, na qual é possível ligar e configurar dispositivos e o processamento de dados através da função «arrastar e largar».
- NeuronEX-Lite: Um software leve de computação na periferia, concebido para a IoT industrial. É compatível com vários protocolos industriais, permitindo uma conectividade rápida dos dispositivos e o processamento de dados em tempo real na ordem dos milissegundos.
Esta arquitetura de software “Double N” permite aos programadores criar rapidamente lógica de negócio através da funcionalidade de arrastar e largar (Node-RED), ao mesmo tempo que oferece funcionalidades profissionais, tais como análise de protocolos, ligação de dispositivos e recolha de dados (NeuronEX-Lite). A série EC consegue uma combinação perfeita de desenvolvimento rápido de negócios e aquisição profissional de dados industriais, melhorando significativamente a eficiência do desenvolvimento e a fiabilidade operacional nas soluções de IoT industrial.
Para ajudar os programadores a compreender e a utilizar os gateways EC, lançámos anteriormente o Guia de Desenvolvimento do EC100. Apresentamos agora o seu guia complementar — Guia de Desenvolvimento do EC300. Antes de começar, se se tiver esquecido de informações básicas, como o URL do backend do gateway EC, consulte o Guia de Aprendizagem do Gateway da Série EC.
Preparação do desenvolvimento
Este guia aplica-se aos seguintes modelos da série EC300:
- EC300
- EC301
- EC300P
Ferramentas
| Categoria de ferramentas | Ferramenta recomendada | Descarregar |
|---|---|---|
| Ferramenta SSH | MobaXterm ou qualquer outra ferramenta SSH da sua preferência | MobaXterm_Installer_v20.5.zip |
| Ferramenta de depuração em série | O XCOM ou qualquer outra ferramenta de depuração em série da sua preferência | XCOM V2.6.zip |
| Ferramenta de hardware | Ferramentas auxiliares comuns de depuração: CAN para USB / RS485 para USB / RS232 para USB. Prepare-se | NENHUM |
| Cadeia de ferramentas de compilação cruzada | gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.zip |
Ambiente de software
| Software | Versão |
|---|---|
| SO | Linux incorporado |
| Kernel | Linux 5.10.209 |
| Nodejs | v22.17.0 |
| Python | python3.8 |
| Concha | bash |
| GLIBC | GLIBC_2.31 |
| Docker | V26.1.3 |
| Biblioteca QT | V5.15.10 |
| Ambiente de trabalho | Xfce4 |
Recursos do dispositivo
| Categoria | Detalhes | Notas |
|---|---|---|
| Armazenamento | 16G no total | Espaço livre predefinido de fábrica ≈ 9 GB; Ampliável através de cartão SD ou SSD M.2 NVMe |
| RAM | 2G |
Espaço livre predefinido de fábrica ≈ 1,4 GB |
| CPU | 4*Cortex-A53@1.8GHz | Espaço livre predefinido de fábrica ≈ 98% |
| NPU | 1 TPOS | Espaço livre predefinido de fábrica ≈ 100% |
Interfaces periféricas
1. Porta de depuração
A série EC300 permite a depuração do sistema através da porta Type-C.
- Início de sessão predefinido:
raiz - Palavra-passe predefinida: ver Documentação sobre permissões do Open System no Percurso de aprendizagem dos computadores industriais ARM da série EC.
2. Interfaces seriais
| Interface | Ficheiro do dispositivo | EC300 | EC300P | EC301 |
|---|---|---|---|---|
| RS485-1 | /dev/ttyS7 | ✔️ | ✔️ | ✔️ |
| RS485-2 | /dev/ttyS2 | ❌ | ❌ | ✔️ |
| RS232 | /dev/ttyS3 | ❌ | ❌ | ✔️ |
Nota: O RS485 gere automaticamente o envio e a receção; os utilizadores não precisam de alternar manualmente.
2.1 Teste rápido
Utilize o minicom ferramenta de teste. Para obter instruções detalhadas, pode pesquisar no Google ou consultar uma IA.
2.2 Exemplo em C
uart_example.c :
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 256
typedef struct {
int baud_rate; // taxa de transmissão
int data_bits; // bits de dados (5, 6, 7, 8)
int stop_bits; // bits de paragem (1,2)
char parity; // bit de paridade (N: Nenhuma, O: Ímpar, E: Par)
} SerialConfig;
int set_serial_attr(int fd, SerialConfig *config)
{
struct termios tty;
if (tcgetattr(fd, &tty) 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;
padrão:
fprintf(stderr, "Velocidade de transmissão não suportada, a utilizar 115200\n");
velocidade = B115200;
}
cfsetispeed(&tty, velocidade);
cfsetospeed(&tty, velocidade);
// definir os bits de dados
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, "Bits de dados não suportados, a utilizar 8\n");
tty.c_cflag |= CS8;
}
// definir os bits de paragem
if (config->stop_bits == 2) {
tty.c_cflag |= CSTOPB;
} else {
tty.c_cflag &= ~CSTOPB;
}
// definir o bit de paridade
switch (config->parity) {
case 'N': case 'n':
tty.c_cflag &= ~PARENB; // Paridade: Nenhuma
break;
case 'O': case 'o':
tty.c_cflag |= PARENB; // Paridade: ímpar
tty.c_cflag |= PARODD;
break;
case 'E': case 'e':
tty.c_cflag |= PARENB; // Paridade: Par
tty.c_cflag &= ~PARODD;
break;
default:
fprintf(stderr, "Paridade não suportada, a utilizar N\n");
tty.c_cflag &= ~PARENB;
}
// Outras configurações
tty.c_cflag |= (CLOCAL | CREAD); // Ativar os modos de receção e local
tty.c_cflag &= ~CRTSCTS; // Sem controlo de fluxo por hardware
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Modo de entrada bruta
tty.c_oflag &= ~OPOST; // Modo de saída bruta
tty.c_cc[VMIN] = 1; // Caracteres mínimos de leitura
tty.c_cc[VTIME] = 0; // Tempo limite de leitura (Unidade: 0,1 segundos)
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, "Utilização: %s \n", argv[0]);
exit(EXIT_FAILURE);
}
portname = argv[1];
// Configurar os parâmetros da porta série
SerialConfig config = {
.baud_rate = 115200, // taxa de transmissão
.data_bits = 8, // bits de dados
.stop_bits = 1, // bits de paragem
.parity = 'N' // bit de paridade (N: Nenhuma, O: Ímpar, E: Par)
};
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("Teste de eco da porta série a decorrer em %s\n", portname);
printf("Configuração: %d de baud, %d de bits de dados, %d de bit de paragem, %c de paridade\n",
config.baud_rate, config.data_bits, config.stop_bits, config.parity);
printf("Prima Ctrl+C para sair.\n");
fd_set readfds;
char buffer[BUFFER_SIZE];
int n;
while (1) {
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
// Aguarda indefinidamente por dados (tempo limite desativado)
if (select(fd + 1, &readfds, NULL, NULL, NULL) 0) {
buffer[n] = '\0';
printf("Recebidos %d bytes: %s\n", n, buffer);
// Repetir dados
write(fd, buffer, n);
} else if (n < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("read");
break;
}
}
}
}
close(fd);
return 0;
}
Compilar: gcc uart_example.c -o uart_example
Teste de funcionamento do RS485-1: ./uart_example /dev/ttyS7
2.3 Exemplo em Python
uart_example.py:
import serial
import select
import sys
import tty
class SerialConfig:
def __init__(self):
self.baud_rate = 115200 taxa de transmissão #
self.data_bits = 8 # bits de dados
self.stop_bits = 1 # bits de paragem
self.parity = 'N' # bit de paridade (N: Nenhuma, O: Ímpar, E: Par)
def set_serial_config(ser, config):
"""Configurar parâmetros da porta série"""
# definir a taxa de transmissão
ser.baudrate = config.baud_rate
# definir os bits de dados
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: # O valor por defeito é 8 bits
ser.bytesize = serial.EIGHTBITS
# definir os bits de paragem
if config.stop_bits == 2:
ser.stopbits = serial.STOPBITS_TWO
else: # O valor predefinido é 1 bit
ser.stopbits = serial.STOPBITS_ONE
# definir o bit de paridade
if config.parity.upper() == 'O':
ser.parity = serial.PARITY_ODD
elif config.parity.upper() == 'E':
ser.parity = serial.PARITY_EVEN
caso contrário: # O valor predefinido é None (sem paridade)
ser.parity = serial.PARITY_NONE
# Desativar o controlo de fluxo por hardware
ser.rtscts = False
# Desativar controlo de fluxo por software
ser.xonxoff = False
# Definir tempo de espera
ser.timeout = 0.1 # Tempo de espera de 100 ms
return ser
def main():
if len(sys.argv) < 2:
print(f"Utilização: {sys.argv[0]} ")
print(f"Exemplo: {sys.argv[0]} /dev/ttyS7")
sys.exit(1)
portname = sys.argv[1]
buff_size = 256
# Inicializar a configuração da porta série
config = SerialConfig()
try:
# Abrir a porta série
ser = serial.Serial()
ser.port = portname
# Aplicar a configuração e abrir a porta série
ser = set_serial_config(ser, config)
ser.open()
if not ser.is_open:
print("Não foi possível abrir a porta série")
sys.exit(1)
print(f"Porta série {portname} aberta com sucesso")
print(f"Configurado: velocidade de transmissão {config.baud_rate}, bits de dados {config.data_bits}, "
f"bits de paragem {config.stop_bits}, paridade {config.parity}")
print("Prima Ctrl+C para sair do programa")
# Utilizar o select para monitorizar os dados da porta série
while True:
# Aguardar até que os dados da porta série estejam legíveis
readable, _, _ = select.select([ser.fileno()], [], [], None)
if readable:
# Ler dados
data = ser.read(buff_size - 1)
if data:
# Tentar descodificar como uma cadeia de caracteres; se falhar, apresentar em hexadecimal
try:
text = data.decode('utf-8')
except UnicodeDecodeError:
text = f"[Dados binários] {data.hex()}"
print(f"Recebidos {len(data)} bytes: {text}")
# Enviar os dados de volta
ser.write(data)
except serial.SerialException as e:
print(f"Erro na porta série: {e}")
sys.exit(1)
exceto KeyboardInterrupt:
print("\nInterrupção do utilizador, a sair do programa")
finalmente:
se 'ser' estiver em locals() e ser.is_open:
ser.close()
print("Porta série fechada")
if __name__ == "__main__":
main()
Primeiro, instale as dependências necessárias:
pip install pyserial
Teste de funcionamento do RS485-1: python uart_example.py /dev/ttyS7
3. LED
A série EC300 tem, no total, 6 LEDs, 4 dos quais são programáveis pelo utilizador.
| Hardware | Índice IO | Número IO | Chip | Notas |
|---|---|---|---|---|
| POW | / | / | / | Indicador de alimentação |
| CORRER | 50 | GPIO1_C2 | gpiochip1 | Utilizado pelo sistema; pisca quando está em funcionamento |
| LED1 | 137 | GPIO4_B1 | gpiochip4 | Programável pelo utilizador |
| LED2 | 51 | GPIO1_C3 | gpiochip1 | Programável pelo utilizador |
| LED3 | 143 | GPIO4_B7 | gpiochip4 | Programável pelo utilizador |
| LED4 | 52 | GPIO1_C4 | gpiochip1 | Programável pelo utilizador |
Nota: O índice IO é calculado a partir do número IO.
3.1 Teste rápido
led_example.sh:
#!/bin/bash
# Verifica se foi fornecido um índice GPIO
if [ -z "$1" ]; then
echo "Utilização: $0 "
exit 1
fi
GPIO=$1
GPIO_PATH="/sys/class/gpio/gpio$GPIO"
EXPORT_PATH="/sys/class/gpio/export"
# Verificar se o GPIO já foi exportado
if [ ! -d "$GPIO_PATH" ]; then
echo "A exportar o GPIO $GPIO..."
echo "$GPIO" > "$EXPORT_PATH"
if [ $? -ne 0 ]; then
echo "Falha ao exportar o GPIO $GPIO. Verifique as permissões ou se está a ser utilizado."
exit 1
fi
else
echo "O GPIO $GPIO já está exportado."
fi
# Definir a direção do GPIO para saída
echo "out" > "$GPIO_PATH/direction"
# Definir o valor inicial para nível baixo
echo 0 > "$GPIO_PATH/value"
echo "A alternar o GPIO $GPIO a cada 1 segundo. Prima Ctrl+C para parar."
# Iniciar a alternância num ciclo
while true; do
echo 1 > "$GPIO_PATH/value"
sleep 1
echo 0 > "$GPIO_PATH/value"
sleep 1
done
Teste:
bash led_example.sh 137
3.2 Exemplo em C
led_example.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>
#include <errno.h>
#define GPIO_CHIP "/dev/gpiochip4" // Use gpiochip1
#define GPIO_LINE 137%32 // The offset of the GPIO to be controlled on chip1
#define PERIOD_US 500000 // Period (microseconds)
int main() {
struct gpiohandle_request req;
struct gpiohandle_data data;
int fd, ret;
// Open the GPIO character device
fd = open(GPIO_CHIP, O_RDWR);
if (fd < 0) {
perror("Unable to open the GPIO device");
return EXIT_FAILURE;
}
// Set up the GPIO request
memset(&req, 0, sizeof(req));
req.lineoffsets[0] = GPIO_LINE; // GPIO line number
req.flags = GPIOHANDLE_REQUEST_OUTPUT; // Set to output mode
req.default_values[0] = 0; // Set the initial value to low level
strcpy(req.consumer_label, "gpio-toggle"); // Consumer label
req.lines = 1; // Number of GPIO lines to control
// Request GPIO control
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (ret < 0) {
perror("Unable to acquire GPIO control");
close(fd);
return EXIT_FAILURE;
}
// Close the GPIO character device (the line handle has already been acquired)
close(fd);
printf("Controlling GPIO chip1 line %d with a period of %.1f seconds...\n",
GPIO_LINE, PERIOD_US/1000000.0);
// Toggle GPIO level periodically
while (1) {
data.values[0] = 1; // High level
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret < 0) {
perror("Failed to set GPIO to high level");
break;
}
usleep(PERIOD_US/2);
data.values[0] = 0; // Low level
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret < 0) {
perror("Failed to set GPIO to low level");
break;
}
usleep(PERIOD_US/2);
}
// Close the GPIO line handle
close(req.fd);
return EXIT_SUCCESS;
}
Compilar: gcc led_exemplo.c -o led_exemplo
Teste: ./exemplo_led
3.3 Exemplo em Python
led_example.py:
import os
import time
import sys
# Definir constantes
GPIO_CHIP = 4 # Número do chip GPIO
GPIO_LINE = 137 % 32 # Desvio do GPIO a controlar na linha
PERIOD_US = 500000 # Período (microssegundos)
def export_gpio(gpio_num):
"""Exportar GPIO"""
try:
with open('/sys/class/gpio/export', 'w') as f:
f.write(str(gpio_num))
time.sleep(0.1) # Aguardar até que a exportação esteja concluída
return True
except IOError as e:
if "já existe" in str(e):
return True # GPIO já exportado
print(f"Falha na exportação do GPIO: {e}")
return False
def set_gpio_direction(gpio_num, direction):
"""Definir a direção do GPIO"""
try:
with open(f'/sys/class/gpio/gpio{gpio_num}/direction', 'w') as f:
f.write(direction)
return True
except IOError as e:
print(f"Falha ao definir a direção do GPIO: {e}")
return False
def set_gpio_value(gpio_num, value):
"""Definir valor do GPIO"""
try:
with open(f'/sys/class/gpio/gpio{gpio_num}/value', 'w') as f:
f.write(str(value))
return True
except IOError as e:
print(f"Falha ao definir o valor do GPIO: {e}")
return False
def unexport_gpio(gpio_num):
"""Desativar a exportação do GPIO"""
try:
with open('/sys/class/gpio/unexport', 'w') as f:
f.write(str(gpio_num))
return True
except IOError as e:
print(f"Falha ao anular a exportação do GPIO: {e}")
return False
def cleanup_gpio(actual_gpio):
"""Limpar recursos do GPIO: primeiro desligar o LED, depois anular a exportação do GPIO"""
try:
print("Desligar o LED...")
# Definir o GPIO para o nível baixo primeiro para desligar o LED
if set_gpio_value(actual_gpio, 0):
print("LED desligado")
else:
print("Falha ao desligar o LED")
# Aguarda um breve período para garantir que o LED está desligado
time.sleep(0.05)
# Em seguida, retira o GPIO da exportação
print("Desativar GPIO...")
if unexport_gpio(actual_gpio):
print("GPIO desativado com sucesso")
else:
print("Falha ao desativar o GPIO")
except Exception as e:
print(f"Ocorreu um erro durante a limpeza dos recursos do GPIO: {e}")
def main():
# Calcular o número real do GPIO
actual_gpio = GPIO_CHIP * 32 + GPIO_LINE
print(f"Chip GPIO: {GPIO_CHIP}, linha GPIO: {GPIO_LINE}")
print(f"Número real do GPIO: {actual_gpio}")
print(f"Período de piscar: {PERIOD_US/1000000.0:.1f} segundos")
print("Prima Ctrl+C para parar o programa")
try:
# Exportar GPIO
if not export_gpio(actual_gpio):
return 1
# Definir o GPIO para o modo de saída
if not set_gpio_direction(actual_gpio, 'out'):
cleanup_gpio(actual_gpio)
return 1
print("Iniciar o controlo do piscar do LED...")
# Alternar o nível do GPIO periodicamente
enquanto True:
# Definir para nível alto
se não set_gpio_value(actual_gpio, 1):
break
time.sleep(PERIOD_US / 2000000.0) # Aguardar meio período
# Definir para nível baixo
se não set_gpio_value(actual_gpio, 0):
break
time.sleep(PERIOD_US / 2000000.0) # Aguardar meio período
except KeyboardInterrupt:
print("\nPrograma interrompido")
exceto Exception como e:
print(f"Ocorreu um erro: {e}")
import traceback
traceback.print_exc()
finalmente:
# Limpar recursos GPIO: primeiro desligar o LED, depois desativar a exportação do GPIO
print("Iniciar limpeza dos recursos GPIO...")
cleanup_gpio(actual_gpio)
print("Programa terminado")
if __name__ == "__main__":
sys.exit(main())
Teste:
python led_example.py
4. CAN
O EC301 possui duas interfaces CAN:
| Interface de hardware | Interface de rede |
|---|---|
| CAN1 [H1 L1] | can0 |
| CAN2 [H2 L2] | can1 |
4.1 Teste rápido
Ligue o CAN1 ao CAN2; teste através da linha de comandos:
can_example.sh:
#!/bin/bash
#Primeiro, inicialize o can0 e o can1 com uma taxa de transmissão de 500k e, em seguida, configure o can0 para receber dados e o can1 para enviar dados.
ip link set can0 down
ip link set can1 down
ip link set can0 type can bitrate 500000
ip link set can1 type can bitrate 500000
ip link set can0 up
ip link set can1 up
echo 4096 > /sys/class/net/can0/tx_queue_len
echo 4096 > /sys/class/net/can1/tx_queue_len
candump can0 &
cansend can1 001234EF#00.11.22.33.44.55.66.77
4.2 Exemplo em C
can_example.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <sys/wait.h>
#define SEND_CAN "can0" // CAN interface for sending data
#define RECV_CAN "can1" // CAN interface for receiving data
#define BITRATE 500000 // Baud rate 500k
#define SAMPLE_POINT 0.8
#define TX_QUEUE_LEN 4096
// Configure CAN interface
int setup_can_interface(const char *ifname) {
char cmd[512];
FILE *fp;
// Close interface
snprintf(cmd, sizeof(cmd), "ip link set %s down", ifname);
system(cmd);
// Set CAN parameters
snprintf(cmd, sizeof(cmd),
"ip link set %s type can bitrate %d sample-point %.1f",
ifname, BITRATE, SAMPLE_POINT);
if (system(cmd) != 0) {
fprintf(stderr, "Failed to configure CAN interface %s\n", ifname);
return -1;
}
// Set TX queue length
snprintf(cmd, sizeof(cmd), "/sys/class/net/%s/tx_queue_len", ifname);
fp = fopen(cmd, "w");
if (fp == NULL) {
perror("Failed to open tx_queue_len");
return -1;
}
fprintf(fp, "%d", TX_QUEUE_LEN);
fclose(fp);
// Enable interface
snprintf(cmd, sizeof(cmd), "ip link set %s up", ifname);
if (system(cmd) != 0) {
fprintf(stderr, "Failed to bring up CAN interface %s\n", ifname);
return -1;
}
printf("CAN interface %s configured:\n", ifname);
printf(" Bitrate: %d\n", BITRATE);
printf(" Sample point: %.1f\n", SAMPLE_POINT);
printf(" TX queue length: %d\n", TX_QUEUE_LEN);
return 0;
}
//Monitor CAN interface data in a child process
void receive_can_messages() {
int recv_sock;
struct sockaddr_can recv_addr;
struct ifreq recv_ifr;
struct can_frame frame;
int nbytes;
// Initialize receiver socket
if ((recv_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Receive socket creation failed");
exit(EXIT_FAILURE);
}
// Bind receiver interface
strcpy(recv_ifr.ifr_name, RECV_CAN);
if (ioctl(recv_sock, SIOCGIFINDEX, &recv_ifr) < 0) {
perror("IOCTL for receive interface failed");
close(recv_sock);
exit(EXIT_FAILURE);
}
recv_addr.can_family = AF_CAN;
recv_addr.can_ifindex = recv_ifr.ifr_ifindex;
if (bind(recv_sock, (struct sockaddr *)&recv_addr, sizeof(recv_addr)) < 0) {
perror("Receive socket bind failed");
close(recv_sock);
exit(EXIT_FAILURE);
}
printf("Receive process started (listening on %s)...\n", RECV_CAN);
while (1) {
nbytes = read(recv_sock, &frame, sizeof(frame));
if (nbytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
}
perror("CAN frame read failed");
break;
}
if (nbytes == CAN_MTU) {
printf("[%s] Received CAN frame: ID 0x%08X, DLC %d, data: ",
RECV_CAN, frame.can_id, frame.can_dlc);
for (int i = 0; i < frame.can_dlc; i++) {
printf("%02X ", frame.data[i]);
}
printf("\n");
} else {
printf("Received invalid frame (size: %d bytes)\n", nbytes);
}
}
close(recv_sock);
exit(EXIT_SUCCESS);
}
// Send CAN frame
int send_can_frame(int sock, uint32_t can_id, uint8_t *data, uint8_t len) {
struct can_frame frame;
if (len > CAN_MAX_DLEN) {
fprintf(stderr, "Data length too long (max %d bytes)\n", CAN_MAX_DLEN);
return -1;
}
memset(&frame, 0, sizeof(frame));
frame.can_id = can_id; // Frame ID
frame.can_dlc = len; // Data length
memcpy(frame.data, data, len); // Data content
// Transmit frame
int nbytes = write(sock, &frame, sizeof(frame));
if (nbytes != sizeof(frame)) {
perror("CAN frame send failed");
return -1;
}
printf("[%s] Sent CAN frame: ID 0x%08X, data: ", SEND_CAN, can_id);
for (int i = 0; i < len; i++) {
printf("%02X ", data[i]);
}
printf("\n");
return 0;
}
int main() {
int send_sock;
struct sockaddr_can send_addr;
struct ifreq send_ifr;
pid_t recv_pid;
// Configure CAN interfaces for sender and receiver
if (setup_can_interface(SEND_CAN) < 0) {
return 1;
}
if (setup_can_interface(RECV_CAN) < 0) {
return 1;
}
// Create child process for receiving data
recv_pid = fork();
if (recv_pid < 0) {
perror("Failed to create receive process");
return 1;
} else if (recv_pid == 0) {
receive_can_messages();
}
// Ensure the receiving child process is started and ready
usleep(100000); // 100ms
// Initialize sender socket
if ((send_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Send socket creation failed");
kill(recv_pid, SIGTERM);
return 1;
}
// Specify CAN interface
strcpy(send_ifr.ifr_name, SEND_CAN);
if (ioctl(send_sock, SIOCGIFINDEX, &send_ifr) < 0) {
perror("IOCTL for send interface failed");
close(send_sock);
kill(recv_pid, SIGTERM);
return 1;
}
send_addr.can_family = AF_CAN;
send_addr.can_ifindex = send_ifr.ifr_ifindex;
if (bind(send_sock, (struct sockaddr *)&send_addr, sizeof(send_addr)) < 0) {
perror("Send socket bind failed");
close(send_sock);
kill(recv_pid, SIGTERM);
return 1;
}
// Send test CAN frame (ID 0x123, data 00.11.22.33.44.55.66.77)
uint8_t test_data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
if (send_can_frame(send_sock, 0x12345678, test_data, sizeof(test_data)) < 0) {
close(send_sock);
kill(recv_pid, SIGTERM);
return 1;
}
sleep(1);
// Close sender socket and terminate receiving child process
close(send_sock);
kill(recv_pid, SIGTERM);
waitpid(recv_pid, NULL, 0);
printf("Program exited\n");
return 0;
}
Compilar: gcc can_exemplo.c -o can_exemplo
Teste: ./exemplo_de_can
4.3 Exemplo em Python
can_example.py:
import time
import threading
import subprocess
import can
# Configure constants
SEND_CAN = "can0" # CAN interface for sending data
RECV_CAN = "can1" # CAN interface for receiving data
BITRATE = 500000 # Baud rate 500k
SAMPLE_POINT = 0.8 # Sample point
TX_QUEUE_LEN = 4096 # Transmit queue length
class CANExample:
def __init__(self):
self.send_bus = None
self.recv_bus = None
self.receive_thread = None
self.running = False
def setup_can_interface(self, ifname):
"""Configure CAN interface"""
print(f"Configuring CAN interface {ifname}...")
try:
# Close interface
cmd = f"sudo ip link set {ifname} down"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
# Set CAN parameters
cmd = f"sudo ip link set {ifname} type can bitrate {BITRATE} sample-point {SAMPLE_POINT}"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"Failed to configure CAN interface {ifname}: {result.stderr}")
return -1
# Set TX queue length
queue_file = f"/sys/class/net/{ifname}/tx_queue_len"
try:
with open(queue_file, 'w') as fp:
fp.write(str(TX_QUEUE_LEN))
except Exception as e:
print(f"Fail to set TX queue length: {e}")
return -1
# Enable interface
cmd = f"sudo ip link set {ifname} up"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"Failed to enable CAN interface {ifname}: {result.stderr}")
return -1
print(f"CAN interface {ifname} configured successfully:")
print(f" Baud rate: {BITRATE}")
print(f" Sample point: {SAMPLE_POINT}")
print(f" Transmit queue length: {TX_QUEUE_LEN}")
return 0
except Exception as e:
print(f"An error occurred while configuring CAN interface {ifname}: {e}")
return -1
def setup_can_interfaces(self):
"""Configure sending and receiving CAN interfaces"""
# Configure CAN interfaces for sender and receiver
if self.setup_can_interface(SEND_CAN) < 0:
return False
if self.setup_can_interface(RECV_CAN) < 0:
return False
print("CAN interface configured successfully")
return True
def receive_can_messages(self):
"""Thread function for receiving CAN messages"""
try:
# Create receiving bus
self.recv_bus = can.Bus(interface='socketcan', channel=RECV_CAN, bitrate=BITRATE)
print(f"Receiving process started (listening on {RECV_CAN})...")
while self.running:
# Set timeout to avoid blocking
msg = self.recv_bus.recv(timeout=1.0)
if msg is not None:
print(f"[{RECV_CAN}] Received CAN frame: ID 0x{msg.arbitration_id:08X}, "
f"DLC {msg.dlc}, data: ", end="")
for i, byte in enumerate(msg.data):
if i < msg.dlc: print(f"{byte:02X} ", end="")
print()
except Exception as e:
print(f"Receive error: {e}")
finally:
if self.recv_bus:
self.recv_bus.shutdown()
def send_can_frame(self, can_id, data):
"""Send CAN frame"""
if not self.send_bus:
print("Error: sending bus not initialized")
return False
if len(data) > 8:
print(f"Error: data length too long (maximum 8 bytes)")
return False
try:
# Create CAN message
msg = can.Message(
arbitration_id=can_id,
data=data,
is_extended_id=True
)
# Send message
self.send_bus.send(msg)
print(f"[{SEND_CAN}] Send CAN frame: ID 0x{can_id:08X}, data: ", end="")
for byte in data:
print(f"{byte:02X} ", end="")
print()
return True
except Exception as e:
print(f"Fail to send: {e}")
return False
def run(self):
"""Main running function"""
print("CAN bus communication test program")
print("=" * 30)
# Configure CAN interface
if not self.setup_can_interfaces():
print("Failed to configure CAN interface, program exiting")
return 1
# Create receiving thread
self.running = True
self.receive_thread = threading.Thread(target=self.receive_can_messages)
self.receive_thread.daemon = True
self.receive_thread.start()
# Wait for receiving thread to start
time.sleep(0.1)
try:
# Initialize sending bus
self.send_bus = can.Bus(interface='socketcan', channel=SEND_CAN, bitrate=BITRATE)
print(f"Sending bus initialized (interface: {SEND_CAN})")
# Send test CAN frame
test_data = bytes([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])
if not self.send_can_frame(0x12345678, test_data):
return 1
# Wait for message transmission to complete
time.sleep(1)
except Exception as e:
print(f"Program error: {e}")
return 1
finally:
# Clean up resources
self.running = False
if self.receive_thread and self.receive_thread.is_alive():
self.receive_thread.join(timeout=2.0)
if self.send_bus:
self.send_bus.shutdown()
print("Program exiting")
return 0
def main():
"""Program entry point"""
can_example = CANExample()
return can_example.run()
if __name__ == "__main__":
main()
Primeiro, instale as dependências necessárias: pip install python-can
Teste: python can_example.py
5. DI
O EC301 possui duas interfaces DI; intervalo de deteção: 5–28 V
| Interface de hardware | Índice IO | Número IO | Grupo Chip |
|---|---|---|---|
| DI-1 | 19 | GPIO0_C3 | gpiochip0 |
| DI-2 | 21 | GPIO0_C5 | gpiochip0 |
5.1 Teste rápido
di_example.sh:
#!/bin/bash
# Verifica se foi fornecido um índice GPIO
if [ -z "$1" ]; then
echo "Utilização: $0 "
exit 1
fi
GPIO=$1
GPIO_PATH="/sys/class/gpio/gpio$GPIO"
EXPORT_PATH="/sys/class/gpio/export"
# Verificar se o GPIO já foi exportado
if [ ! -d "$GPIO_PATH" ]; then
echo "A exportar o GPIO $GPIO..."
echo "$GPIO" > "$EXPORT_PATH"
if [ $? -ne 0 ]; then
echo "Falha ao exportar o GPIO $GPIO. Verifique as permissões ou se está a ser utilizado."
exit 1
fi
else
echo "O GPIO $GPIO já está exportado."
fi
# Definir a direção do GPIO para entrada
echo "in" > "$GPIO_PATH/direction"
echo "Ler o valor do GPIO $GPIO. Prima Ctrl+C para parar."
# Iniciar a leitura num ciclo
while true; do
cat "$GPIO_PATH/value"
sleep 1
done
Teste: bash di_example.sh 19
Faça o teste ligando o terminal positivo de uma fonte de alimentação de 12 V ao DI1 e o terminal negativo ao COM1; quando recebe 12 V, imprime 0; quando não recebe alimentação, imprime 1. Para obter detalhes sobre a ligação, consulte o especificações de cada produto da série EC300.
5,2 °C Exemplo
di_example.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIO_CHIP "/dev/gpiochip0" // Dispositivo controlador GPIO
#define GPIO_LINE 19 // Número da linha GPIO a testar (interface DI)
#define SAMPLE_INTERVAL_1S 1 // Intervalo de amostragem (segundos)
#define MAX_SAMPLES 100 // Número máximo de amostras
int main() {
struct gpiohandle_request req;
struct gpiohandle_data data;
int fd, ret;
int sample_count = 0;
struct timespec start_time, current_time;
// Abrir dispositivo de caracteres GPIO
fd = open(GPIO_CHIP, O_RDWR);
if (fd < 0) {
perror("Não foi possível abrir o dispositivo GPIO");
return EXIT_FAILURE;
}
// Definir pedido GPIO
memset(&req, 0, sizeof(req));
req.lineoffsets[0] = GPIO_LINE; // Número da linha GPIO
req.flags = GPIOHANDLE_REQUEST_INPUT; // Definir para o modo de entrada
strcpy(req.consumer_label, "di-monitor"); // Etiqueta do consumidor
req.lines = 1; // Número de linhas GPIO a controlar
// Solicitar controlo do GPIO
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (ret < 0) {
perror("Não foi possível obter o controlo do GPIO");
close(fd);
return EXIT_FAILURE;
}
// Fechar o dispositivo de caracteres GPIO (identificador da linha já obtido)
close(fd);
printf("A monitorizar a linha %d do chip GPIO 0 (interface DI), intervalo de amostragem de %.1f segundos...\n",
GPIO_LINE, SAMPLE_INTERVAL_1S/1000000.0);
printf("Prima Ctrl+C para parar a monitorização\n\n");
// Obter a hora de início
clock_gettime(CLOCK_MONOTONIC, &start_time);
// Monitorizar continuamente o estado de entrada do GPIO
while (sample_count < MAX_SAMPLES) {
// Ler o estado atual do GPIO
ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
if (ret < 0) {
perror("Falha ao ler o estado do GPIO");
break;
}
// Obter a hora atual
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
double elapsed = (current_time.tv_sec - start_time.tv_sec) +
(current_time.tv_nsec - start_time.tv_nsec) / 1e9;
// Imprimir o estado e o carimbo de data/hora
printf("Hora: %.3fs - Estado DI: %d\n", elapsed, data.values[0]);
sample_count++;
sleep(SAMPLE_INTERVAL_1S);
}
// Fechar o identificador da linha GPIO
close(req.fd);
printf("Monitorização concluída, %d amostras recolhidas\n", sample_count);
return EXIT_SUCCESS;
}
Compilar: gcc di_example.c -o di_example
Teste: ./di_example
5.3 Python Exemplo
di_example.py:
import time
import sys
from typing import Optional
# Constant definitions
GPIO_LINE: int = 19
SAMPLE_INTERVAL: float = 1.0
MAX_SAMPLES: int = 100
def gpio_read(gpio_line: int) -> Optional[int]:
"""Read the state value of the specified GPIO line
Args:
gpio_line: GPIO line number
Returns:
GPIO state value (0 or 1), returns None on error
"""
try:
# Ensure GPIO has been exported and set to input
try:
with open('/sys/class/gpio/export', 'w') as export_file:
export_file.write(str(gpio_line))
time.sleep(0.1)
except IOError:
pass # GPIO may have been exported
# Set direction to input
with open(f'/sys/class/gpio/gpio{gpio_line}/direction', 'w') as direction_file:
direction_file.write('in')
# Read GPIO value
with open(f'/sys/class/gpio/gpio{gpio_line}/value', 'r') as value_file:
return int(value_file.read().strip())
except (IOError, ValueError, FileNotFoundError) as error:
print(f"GPIO read error: {error}")
return None
def main() -> int:
"""Main function, execute GPIO monitoring task"""
print(f"Monitoring GPIO line {GPIO_LINE}, interval {SAMPLE_INTERVAL} seconds...")
print("Press Ctrl+C to stop\n")
start_time: float = time.monotonic()
sample_count: int = 0
try:
while sample_count < MAX_SAMPLES:
target_time: float = start_time + sample_count * SAMPLE_INTERVAL
# Precise wait
current_time: float = time.monotonic()
if current_time < target_time:
time.sleep(target_time - current_time)
# Read GPIO state
gpio_value: Optional[int] = gpio_read(GPIO_LINE)
if gpio_value is None:
print("Failed to read GPIO state")
break
elapsed: float = time.monotonic() - start_time
print(f"Time: {elapsed:.3f}s - DI state: {gpio_value}")
sample_count += 1
except KeyboardInterrupt:
print("\nUser interrupted")
finally:
# Clean up GPIO
try:
with open('/sys/class/gpio/unexport', 'w') as unexport_file:
unexport_file.write(str(GPIO_LINE))
except (IOError, FileNotFoundError):
pass
print(f"\nMonitoring complete, {sample_count} samples collected")
return 0
if __name__ == "__main__":
sys.exit(main())
Teste: python di_exemplo.py
6. FAZER
O EC301 possui duas interfaces DO, com saída de 5 A/12~24 V CC
| Interface de hardware | Índice IO | Número IO | Grupo Chip |
|---|---|---|---|
| DO-1 | 20 | GPIO0_C4 | gpiochip0 |
| DO-2 | 18 | GPIO0_C2 | gpiochip0 |
6.1 Teste rápido
do_example.sh:
#!/bin/bash
# Verificar se foi fornecido um índice GPIO
if [ -z "$1" ]; then
echo "Utilização: $0 "
exit 1
fi
GPIO=$1
GPIO_PATH="/sys/class/gpio/gpio$GPIO"
EXPORT_PATH="/sys/class/gpio/export"
# Verificar se o GPIO já foi exportado
if [ ! -d "$GPIO_PATH" ]; then
echo "A exportar o GPIO $GPIO..."
echo "$GPIO" > "$EXPORT_PATH"
if [ $? -ne 0 ]; then
echo "Falha ao exportar o GPIO $GPIO. Verifique as permissões ou se está a ser utilizado."
exit 1
fi
else
echo "O GPIO $GPIO já está exportado."
fi
# Definir a direção do GPIO para entrada
echo "out" > "$GPIO_PATH/direction"
# Definir o valor inicial para nível baixo
echo 0 > "$GPIO_PATH/value"
echo "A alternar o GPIO $GPIO a cada 1 segundo. Prima Ctrl+C para parar."
# Iniciar a alternância num ciclo
while true; do
echo 1 > "$GPIO_PATH/value"
sleep 1
echo 0 > "$GPIO_PATH/value"
sleep 1
done
Teste: bash do_example.sh 20
Efetue o teste ligando o terminal positivo de uma carga externa (por exemplo, um LED) ao COM1 e o terminal negativo ao DO1; após executar o programa de teste, o LED irá piscar a intervalos regulares. Para obter detalhes sobre a ligação, consulte o especificações de cada produto da série EC300.
Exemplo em C 6.2
do_example.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIO_CHIP "/dev/gpiochip0" // Dispositivo controlador GPIO
#define GPIO_LINE 20 // Número da linha GPIO a testar (interface DO)
#define INTERVAL_SEC 1 // Intervalo de tempo de ligar/desligar (segundos)
#define MAX_CYCLES 100 // Número máximo de ciclos
volatile sig_atomic_t stop_flag = 0;
void handle_signal(int sig) {
stop_flag = 1;
}
int main() {
struct gpiohandle_request req;
struct gpiohandle_data data;
int fd, ret;
int cycle_count = 0;
struct timespec start_time, current_time;
// Registar o manipulador de sinais
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
// Abrir dispositivo de caracteres GPIO
fd = open(GPIO_CHIP, O_RDWR);
if (fd < 0) {
perror("Não foi possível abrir o dispositivo GPIO");
return EXIT_FAILURE;
}
// Definir pedido GPIO
memset(&req, 0, sizeof(req));
req.lineoffsets[0] = GPIO_LINE; // Número da linha GPIO
req.flags = GPIOHANDLE_REQUEST_OUTPUT; // Definir para o modo de entrada
req.default_values[0] = 0; // Definir o valor inicial para o nível baixo
strcpy(req.consumer_label, "do-control"); // Etiqueta do consumidor
req.lines = 1; // Número de linhas GPIO a controlar
// Solicitar controlo do GPIO
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (ret < 0) {
perror("Não foi possível adquirir o controlo do GPIO");
close(fd);
return EXIT_FAILURE;
}
// Fechar dispositivo de caracteres GPIO (identificador da linha já adquirido)
close(fd);
printf("A monitorizar a linha %d do chip GPIO 0 (interface DO), intervalo de amostragem de %.1f segundos...\n\n",
GPIO_LINE, INTERVAL_SEC);
printf("Prima Ctrl+C para parar a monitorização\n\n");
// Obter a hora de início
clock_gettime(CLOCK_MONOTONIC, &start_time);
// Controlo em loop da ativação/desativação da interface DO
while (!stop_flag && cycle_count < MAX_CYCLES) {
// Obter a hora atual
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
double elapsed = (current_time.tv_sec - start_time.tv_sec) +
(current_time.tv_nsec - start_time.tv_nsec) / 1e9;
// Alternar o estado
int state = cycle_count % 2;
data.values[0] = state;
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret < 0) {
perror("Falha ao definir o estado do GPIO");
break;
}
printf("Tempo: %.3fs - Definir estado do DO: %s\n",
elapsed, state ? "Alto (ON)" : "Baixo (OFF)");
cycle_count++;
sleep(INTERVAL_SEC);
}
// Garantir que o estado final é de nível baixo
data.values[0] = 0;
ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
// Fechar o identificador da linha GPIO
close(req.fd);
printf("\nControlo concluído, ciclos de ligar/desligar %d executados\n", cycle_count);
return EXIT_SUCCESS;
}
Compilar: gcc do_example.c -o do_example
Teste: ./do_example
6.3 Exemplo em Python
do_example.py:
import os
import time
import signal
import sys
from typing import NoReturn, Optional
# Constant definitions
GPIO_LINE: int = 20
INTERVAL_SEC: float = 1.0
MAX_CYCLES: int = 100
stop_flag: bool = False
def handle_signal(sig: int, frame) -> None:
"""Signal handler function, set stop flag"""
global stop_flag
stop_flag = True
def gpio_control(gpio_line: int, value: bool) -> bool:
"""Control the output state of the specified GPIO line
Args:
gpio_line: GPIO line number
value: Output value (True for high level, False for low level)
Returns:
Returns True on success, False on failure
"""
try:
# Ensure GPIO has been exported
try:
with open('/sys/class/gpio/export', 'w') as export_file:
export_file.write(str(gpio_line))
time.sleep(0.1)
except IOError:
pass # GPIO may have been exported
# Set direction to output
with open(f'/sys/class/gpio/gpio{gpio_line}/direction', 'w') as direction_file:
direction_file.write('out')
# Set GPIO value
with open(f'/sys/class/gpio/gpio{gpio_line}/value', 'w') as value_file:
value_file.write('1' if value else '0')
return True
except (IOError, FileNotFoundError) as error:
print(f"GPIO control error: {error}")
return False
def main() -> int:
"""Main function, execute GPIO control task"""
global stop_flag
# Register signal handler
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
print(f"Control GPIO line {GPIO_LINE},interval {INTERVAL_SEC} seconds...")
print("Press Ctrl+C to stop\n")
start_time: float = time.monotonic()
cycle_count: int = 0
try:
while not stop_flag and cycle_count < MAX_CYCLES:
target_time: float = start_time + cycle_count * INTERVAL_SEC
# Precise wait
current_time: float = time.monotonic()
if current_time < target_time:
time.sleep(target_time - current_time)
state: bool = bool(cycle_count % 2)
if not gpio_control(GPIO_LINE, state):
break
elapsed: float = time.monotonic() - start_time
status: str = "High (ON)" if state else "Low (OFF)"
print(f"Time: {elapsed:.3f}s - DO state: {status}")
cycle_count += 1
except KeyboardInterrupt:
print("\n User interrupted")
finally:
# Ensure final state is low level
gpio_control(GPIO_LINE, False)
print(f"\n Control completed, {cycle_count} on/off cycles executed")
return 0
if __name__ == "__main__":
sys.exit(main())
Teste: python do_example.py
7. Botões
Série de produtos EC300 – Botões: Geridos por programas internos, não programáveis pelo utilizador, utilizados para reiniciar ou reinicializar o dispositivo através da configuração na página web.
| Interface de hardware | Índice IO | Número IO | Grupo Chip | Observações |
|---|---|---|---|---|
| Botão | 0 | GPIO0_A0 | gpiochip0 | Botão de reinicialização/reinício do sistema por predefinição |
- Reiniciar: prima o 1 e solte-o
- Reinicializar + Reiniciar: manter premido >5s
8. Watchdog de hardware
Todos os produtos EC300 têm o watchdog de hardware ativado por predefinição, sendo gerido por programas internos.
| Interface de hardware | Índice IO | Número IO | Grupo Chip | Observações |
|---|---|---|---|---|
| Ativar controlo | 113 | GPIO3_C1 | gpiochip3 | Botão de reinicialização/reinício do sistema por predefinição |
| Watchdog IO | 8 |
GPIO0_B0 |
gpiochip0 |
Período recomendado para a alimentação do watchdog: <30 s
Nota: Se os programas internos forem interrompidos, terá de assumir você mesmo o serviço de alimentação do watchdog; caso contrário, o dispositivo irá reiniciar-se periodicamente.
9. Portas de rede
As portas de rede estão divididas em WAN + LAN e são geridas por programas de rede internos; é necessário efetuar a configuração através da interface web. Para mais informações, consulte o Guia de Início Rápido da CE.
10. Comunicações móveis sem fios
A série de produtos EC300 suporta comunicação 4G/5G (o 5G requer a aquisição da versão 5G; para mais informações, contacte o apoio ao cliente). O reconhecimento do módulo e a marcação são geridos por programas internos, pelo que os utilizadores não precisam de se preocupar. Consulte o Guia de Início Rápido da CE.
11. Wi-Fi
A série de produtos EC300 suporta os modos de ponto de acesso Wi-Fi e de cliente Wi-Fi; suporta as bandas de 2,4 GHz e 5 GHz. O Wi-Fi é gerido por programas internos, pelo que os utilizadores não precisam de se preocupar. Para mais informações, consulte: Guia de Início Rápido da CE.
12. Bluetooth
A série de produtos EC300 suporta a funcionalidade Bluetooth.
12.1 Teste rápido
# Entre no modo interativo do shell Bluetooth
bluetoothctl
# No modo interativo, execute os seguintes comandos:
# Verifique o estado do controlador (adaptador) Bluetooth
# Se o adaptador não estiver ativado, ative-o
power on
# Ative o modo de detecção (permita que outros dispositivos encontrem este dispositivo)
discoverable on
# Ative o modo de emparelhamento
pairable on
# Inicie a pesquisa (pesquisa contínua; prima Ctrl+C para parar)
scan on
# Para emparelhar um dispositivo Bluetooth (por exemplo, auscultadores, endereço MAC AA:BB:CC:DD:EE:FF), a título de exemplo:
# Executar no bluetoothctl
# Emparelhar o dispositivo
pair AA:BB:CC:DD:EE:FF
# Considerar o dispositivo de confiança (opcional, para evitar verificações repetidas mais tarde)
trust AA:BB:CC:DD:EE:FF
# Ligue o dispositivo
connect AA:BB:CC:DD:EE:FF
# Se o emparelhamento exigir um código PIN, introduza-o conforme solicitado (normalmente 0000 ou 1234, ou o código indicado no manual do dispositivo)
# Após a ligação bem-sucedida, pode visualizar os detalhes do dispositivo com info AA:BB:CC:DD:EE:FF
info AA:BB:CC:DD:EE:FF
13. Áudio
A série de produtos EC300 é compatível com uma tomada de 3,5 mm para auscultadores.
13.1 Teste rápido
Ligue uns auscultadores de 3,5 mm ou um altifalante à porta HP do dispositivo; crie um ficheiro de script da seguinte forma:
hp_example.sh:
#!/bin/bash
# Definir o dispositivo de reprodução como HP (SAÍDA DE AURICULARES)
amixer -c 0 cset name='Caminho de reprodução' 'HP'
# Definir o volume de reprodução para 200, intervalo 0–255
amixer -c 0 cset name='Playback Volume' 200
# Reproduzir um ficheiro de áudio de exemplo
aplay /run/media/mmcblk0p9/iotrouter.wav
Teste: bash hp_example.sh
14. Ecrã
| Interface de hardware | Resolução | EC300 | EC300P | EC301 |
|---|---|---|---|---|
| HDMI | 1920×1080 | ✔️ | ❌ | ✔️ |
| LVDS | 1280×800 | ❌ | ✔️ | ❌ |
14.1 Ecrã LVDS
Suporta o ajuste do brilho da retroiluminação, intervalo de ajuste [0–255]:
echo 128 > /sys/class/backlight/backlight/brightness
echo 255 > /sys/class/backlight/backlight/brightness
Exibição do ecrã de controlo:
echo off > /sys/class/drm/card0-LVDS-1/status
echo on > /sys/class/drm/card0-LVDS-1/status
Interface API do sistema
1. Obter informações básicas sobre o dispositivo
- URL: GET http://{IP do dispositivo}/rpc-api/data/devinfo
- Método: OBTER
- Parâmetros da solicitação: Nenhum
Descrição dos dados de resposta:
| Campo | Nome do campo | Tipo de dados | Descrição |
|---|---|---|---|
| código | Código de estado da resposta | int | Devolve sempre o código 200, o que indica que o pedido foi bem-sucedido |
| data.model | Modelo do dispositivo | cadeia de caracteres | Modelo do dispositivo |
| data.sn | Número de série do dispositivo | cadeia de caracteres | Número de série do dispositivo |
| data.versão | Versão do dispositivo | cadeia de caracteres | Versão do software do dispositivo |
Exemplo de resposta:
{
"code": 200,
"data": {
"model": "EC100",
"sn": "4310AA01C8AA2B6B",
"version": "3802"
}
}
2. Obter informações sobre a rede WWAN do dispositivo
- URL: GET http://{IP do dispositivo}/rpc-api/data/wwaninfo
- Método: OBTER
- Parâmetros da solicitação: Nenhum
Descrição dos dados de resposta:
| Campo | Nome do campo | Tipo de dados | Descrição |
|---|---|---|---|
| código | Código de estado da resposta | int | Devolve sempre o código 200, o que indica que o pedido foi bem-sucedido |
| data.ip | 4G IP | cadeia de caracteres | Endereço IP obtido através de 4G |
| data.mask | Máscara de sub-rede | cadeia de caracteres | Máscara de sub-rede |
| data.dns | IP do servidor DNS | cadeia de caracteres | Endereço do servidor de resolução DNS |
| data.signal | Intensidade do sinal | int | Intensidade do sinal 4G |
| data.ccid | Número do cartão SIM | cadeia de caracteres | Número do cartão SIM |
| data.imei | IMEI | cadeia de caracteres | Módulo 4G do dispositivo IMEI |
| data.netInfo | Tipo de rede | cadeia de caracteres | Tipo de rede atual |
| data.apn.addr | Endereço APN | cadeia de caracteres | Endereço do Nome do Ponto de Acesso (APN) |
| data.apn.username | Nome de utilizador APN | cadeia de caracteres | Nome de utilizador para autenticação APN |
| data.apn.password | Palavra-passe APN | cadeia de caracteres | Palavra-passe de autenticação APN |
Exemplo de resposta:
{
"code": 200,
"data": {
"ip": "37.64.21.99",
"máscara": "255.255.255.255",
"dns": "224.134.125.33",
"sinal": 90,
"ccid": "89860480192470076526",
"imei": "862701080346307",
"netInfo": "LTE",
"apn": {
"addr": "",
"username": "",
"password": ""
}
}
}
Aplicações de software
1. Programa de gestão interna
Para simplificar a utilização, a série de produtos EC300 inclui um programa de gestão integrado, o iotrouter, que é iniciado automaticamente no arranque. O programa de gestão inclui, entre outras, as seguintes funções:
- Inicialização do dispositivo
- Gestão de redes
- Cão de guarda
- Monitorização de teclas/botões
- Firewall
- Serviço de configuração do dispositivo (porta predefinida 80, a porta de escuta pode ser alterada; ficheiro: /usr/local/src/iotrouter/web/user-config.js)
- Função de navegação pelos ficheiros do utilizador (caminho predefinido: /home; o caminho pode ser alterado; ficheiro: /usr/local/src/iotrouter/web/user-config.js)
Nota: O programa interno deve permanecer em execução; caso contrário, o dispositivo poderá não funcionar normalmente no arranque. Se o utilizador tiver de desativar o programa de gestão interno, deverá assumir ele próprio os serviços de gestão acima referidos.
2. NeuronEX
A série de produtos EC300 vem com o NeuronEX-Lite pré-instalado. O serviço inicia automaticamente ao arrancar o sistema e fica à escuta na porta 8085. Para mais detalhes, consulte: Percurso de Aprendizagem
- Verificar o estado do serviço: systemctl status neuronex
- Reiniciar o serviço: systemctl restart neuronex
- Parar o serviço: systemctl stop neuronex
- Desativar o arranque automático do serviço: systemctl disable neuronex
3. Node-Red
A série de produtos EC300 vem com o Node-Red pré-instalado. O serviço inicia automaticamente no arranque e fica à escuta na porta 1880. Para mais detalhes, consulte: Percurso de Aprendizagem
- Verificar o estado do serviço: systemctl status node-red
- Reiniciar o serviço: systemctl restart node-red
- Parar o serviço: systemctl stop node-red
- Desativar o arranque automático do serviço: systemctl disable node-red
4. FUXA
5. Ambiente de trabalho Xfce4
O ambiente de trabalho predefinido é o Xfce4; isto é apenas um exemplo. Se os utilizadores precisarem de apresentar outras interfaces, como, por exemplo, uma interface QT, podem desenvolvê-las eles próprios.
O gestor de ecrã para o ambiente de trabalho é: lightdm
- Verificar o estado do serviço: systemctl status lightdm
- Reiniciar o serviço: systemctl restart lightdm
- Parar o serviço: systemctl stop lightdm
- Desativar o arranque automático do serviço: systemctl disable lightdm
6. Programas desenvolvidos pelos utilizadores
Os utilizadores têm total liberdade para desenvolver os seus próprios programas. Os programas dos utilizadores funcionam de forma independente, mas é necessário ter em conta a gestão da memória e do armazenamento para evitar anomalias no sistema. O software pode ser configurado para iniciar automaticamente através de:
- /etc/rc.local
- serviço systemd
- /etc/init.d sistema
Também prestamos serviços de desenvolvimento e personalização de software. Se necessário, contacte a nossa equipa de vendas.
7. LOGOTIPO de arranque
Os utilizadores podem substituir o logótipo de arranque do sistema por conta própria.
| Nome do logótipo | Palco de Exposição |
|---|---|
| logo.bmp | Exibido durante o arranque do U-Boot |
| logo_kernel.bmp | Exibido durante o arranque do kernel |
7.1 Requisitos de formatação
As imagens do LOGO devem estar no formato BMP de 24 bits, sendo recomendável que a resolução da imagem não exceda a resolução de ecrã do HDMI ou de outros monitores.
7.2 Substituição do LOGO
Quando o dispositivo ligar, copie o ficheiro preparado logo.bmp e logo_kernel.bmp ficheiros para o diretório “boot/” na raiz do sistema de ficheiros da placa de avaliação, substituindo os ficheiros existentes logo.bmp e logo_kernel.bmp ficheiros no diretório.
Nota: Os nomes dos ficheiros dos logótipos não devem ser alterados.
