
Arduino Core para
FIBOCOM L610
Manual completo de instalação, configuração e desenvolvimento
1O Módulo FIBOCOM L610
O FIBOCOM L610 é um módulo LTE Cat.1 baseado no chipset RDA/Unisoc ASR1601 com suporte a Open CPU — é possível executar código de aplicação diretamente no módulo, sem microcontrolador externo.
Especificações
| Característica | Valor |
|---|---|
| Chipset | RDA/Unisoc ASR1601 |
| CPU | ARM Cortex-A5 @ 312 MHz |
| Conectividade | LTE Cat.1, WiFi 802.11b/g/n, Bluetooth 4.2 + BLE |
| GNSS | GPS / GLONASS / BeiDou |
| Flash | 16 MB |
| RAM | 8 MB |
| Interfaces | UART, I2C, SPI, GPIO, ADC, PWM |
| Alimentação | 3.4V ~ 4.3V (VBAT) |
| Temperatura | −40°C ~ +85°C |
| Dimensões | 28.5 × 32.0 × 2.9 mm |
Kit de desenvolvimento
O kit ADP-L610-GL-33-00 inclui: conector SIM (NanoSIM), antenas LTE/GNSS/WiFi-BT, interface USB, conector LCD CON601 (27 pinos) e conector câmera CON600 (18 pinos).
2Arquitetura do BSP
├── boards.txt ← definição da placa e menus
├── platform.txt ← toolchain ARM e pipeline de build
├── package_index.json ← metadados do pacote
├── INSTALL_WINDOWS.md ← instruções de instalação
├── cores/l610/
│ ├── Arduino.h ← API Arduino (includes SDK + wrappers)
│ ├── fibo_sdk_fix.h ← fixes de compatibilidade C/C++
│ ├── main.c ← entrada RTOS (appimg_enter/exit)
│ ├── main_cpp_bridge.cpp ← bridge C/C++ para setup()/loop()
│ ├── wiring.c ← millis(), delay()
│ ├── wiring_digital.c ← pinMode(), digitalWrite()
│ ├── wiring_analog.c ← analogRead(), analogWrite()
│ └── HardwareSerial.h/.cpp ← Serial via UART
├── variants/l610/
│ ├── pins_arduino.h ← mapeamento de pinos
│ └── app_flashimg.ld ← linker script
└── libraries/ SPI/ Wire/
Pipeline de build
sketch.ino
│
▼ arm-none-eabi-g++
sketch.elf
│
▼ dtools.exe mkappimg
sketch.img
│
▼ python pacgen.py
sketch.pac ◄── este arquivo é gravado no L610Toolchain
arm-none-eabi-gcc -mcpu=cortex-a5 -mtune=generic-armv7-a -mthumb -mfpu=neon-vfpv4 -mfloat-abi=hard -mno-unaligned-access -DCHIP_8910 -DARDUINO_FIBOCOM_L610 -std=gnu++14 -Os -ffunction-sections -fdata-sections
3Instalação passo a passo
Instalar o Arduino IDE 1.8.19
Baixar em arduino.cc/en/software e instalar normalmente. Abrir e fechar uma vez para criar as pastas necessárias.
Copiar o BSP para o Arduino
Abrir o Explorador de Arquivos e navegar até:
C:\Users\<SEU_UTILIZADOR>\AppData\Local\Arduino15\packages\
ℹ️SeAppDatanão aparecer: Ver → Itens ocultos ✓Criar a seguinte estrutura de pastas e copiar o conteúdo do ZIP do BSP para a pasta
1.0.0\:Arduino15\packages\fibocom\hardware\l610\1.0.0\ ├── boards.txt ├── platform.txt ├── cores\l610\ ├── variants\l610\ └── libraries\
Instalar o SDK FIBOCOM
Extrair o SDK Open CPU do L610 para:
C:\fibocom\sdk\
Verificar que estes arquivos existem:
Arquivo Estado components\lib\core_stub.o✓ Obrigatório components\lib\fdl1.img✓ Obrigatório components\lib\fdl2.img✓ Obrigatório components\include\fibo_opencpu.h✓ Obrigatório prebuilts\win32\gcc-arm-none-eabi\bin\arm-none-eabi-gcc.exe✓ Obrigatório prebuilts\win32\python3\python.exe✓ Obrigatório tools\pacgen.py✓ Obrigatório Verificar endereços de memória
Abrir
C:\fibocom\sdk\cmake\core_config.cmakee confirmar que os endereços coincidem com os valores emboards.txt:l610.build.fdl1_addr = 0x00000000 l610.build.fdl2_addr = 0x00010000 l610.build.appimg_addr = 0x60334000 l610.build.appimg_size = 0x12C000
Se os valores forem diferentes, editar o
boards.txtdo BSP.Selecionar a placa no Arduino IDE
Abrir o Arduino IDE 1.8.19 e configurar:
- Tools → Board → FIBOCOM L610 (LTE Cat.1)
- Tools → Task Stack → 8 KB (recomendado)
- Tools → Debug Level → None
💡Se a placa não aparecer no menu, fechar e reabrir o Arduino IDE.Compilar e gravar
Clicar em Verify ✓ para compilar. O arquivo
.pacé gerado em:C:\Users\<USER>\AppData\Local\Temp\arduino_build_XXXXXX\sketch.ino.pac
Para gravar no L610:
- Conectar o L610 ao PC via USB
- AbrirFIBOCOM UpgradeDownload
- Carregar o arquivo
.pac - Clicar emStart
- Conectar/desconectar o módulo para iniciar o flash
4Primeiro Sketch — Blink
Todos os sketches Arduino para o L610 têm esta estrutura base:
#include <Arduino.h>
// OBRIGATORIO em todos os sketches — recebe eventos do sistema
void user_signal_process(GAPP_SIGNAL_ID_T sig, va_list arg)
{
(void)sig; (void)arg;
}
void setup()
{
Serial.begin(115200);
fibo_taskSleep(2000); // asalvar Serial inicializar
pinMode(LED_BUILTIN, OUTPUT); // GPIO5 = LPG (LED de hardware)
Serial.println("L610 Arduino ready!");
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED ON");
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED OFF");
delay(1000);
}Output esperado no Serial Monitor (115200 baud):
L610 Arduino ready! LED ON LED OFF LED ON ...
5API Arduino no L610
Digital I/O
pinMode(pin, mode) // INPUT, OUTPUT, INPUT_PULLUP, INPUT_PULLDOWN digitalWrite(pin, value) // HIGH, LOW int digitalRead(pin) attachInterrupt(pin, cb, mode) // RISING, FALLING, CHANGE detachInterrupt(pin)
Analógico
int analogRead(pin) // ADC pinos A0-A3 analogWrite(pin, value) // PWM via LPG hardware (0-255)
Tempo
unsigned long millis() // ms desde boot
void delay(ms) // ⚠️ APENAS em setup()/loop()
// Em threads: usar fibo_taskSleep(ms)Serial
Serial.begin(115200) Serial.print(...) Serial.println(...) Serial.printf(format, ...) Serial.available() Serial.read() Serial.write(buf, len)
SPI e Wire (I2C)
#include <SPI.h> → SPI.begin() / SPI.transfer() / SPI.end() #include <Wire.h> → Wire.begin() / Wire.beginTransmission() / Wire.write() / Wire.read()
Matemática
min(a,b) max(a,b) abs(x) constrain(x,lo,hi) map(x,in_min,in_max,out_min,out_max) bit(b) bitRead(v,b) bitSet(v,b) bitClear(v,b) lowByte(w) highByte(w)
6Pinout e Hardware
Constantes de pinos
| Constante | GPIO | Função |
|---|---|---|
LED_BUILTIN | 5 | LPG — LED de hardware / PWM |
PIN_SPI_SCK | 8 | SPI Clock |
PIN_SPI_MISO | 9 | SPI MISO |
PIN_SPI_MOSI | 10 | SPI MOSI |
PIN_SPI_SS | 12 | SPI Chip Select |
PIN_WIRE_SDA | 41 | I2C SDA |
PIN_WIRE_SCL | 42 | I2C SCL |
A0 ~ A3 | — | Canais ADC 0 a 3 |
LCD — Conector CON601 (ST7735 128×160)
| Pino CON601 | Sinal L610 | GPIO | Função LCD |
|---|---|---|---|
| 14 | LCD_SIO | GPIO0 | MOSI / SDA |
| 15 | LCD_SDC | GPIO1 | DC / A0 |
| 17 | LCD_CLK | GPIO2 | SCK / CLK |
| 12 | LCD_CS | GPIO3 | CS |
| 11 | LCD_RSTB | GPIO6 | RESET |
| 7 | IOVCC | — | 1.8V ou 3.3V |
| 8 | VDD | — | 2.8V |
| 9 | LED_A | — | Backlight + (3.3V) |
| 10 | LED_K | — | Backlight − (GND) |
Câmera — Conector CON600 (GC0308 VGA)
| Pino CON600 | Sinal | Função |
|---|---|---|
| 2 | VDDIO | 1.8V |
| 3 | SDA | I2C config |
| 4 | SCL | I2C config |
| 5 | CMRST | Reset câmera |
| 6 | CMPDN | Power Down |
| 8 | CMMCLK | Clock de referência |
| 10 | CMCSK | SPI Clock |
| 11 | CMCSD0 | SPI Data 0 |
| 13 | VCAMA | 2.8V alimentação |
| 7,9,19,20 | GND | Massa |
7Arquitetura de uma Aplicação
O L610 usa FreeRTOS. O código pesado deve correr em threads separadas, não no loop().
#include <Arduino.h>
// 1. OBRIGATORIO — eventos assíncronos
void user_signal_process(GAPP_SIGNAL_ID_T sig, va_list arg)
{
switch (sig) {
case GAPP_SIG_PDP_ACTIVE_ADDRESS: {
char *addr = (char*)va_arg(arg, char*);
va_end(arg);
Serial.printf("[LTE] IP: %s\n", addr);
break;
}
default: break;
}
}
// 2. Thread de trabalho
static void _my_thread(void *param)
{
Serial.println("Thread iniciada!");
while (true) {
// trabalho aqui...
fibo_taskSleep(1000); // NÃO usar delay() em threads!
}
fibo_thread_delete();
}
// 3. Criar thread em setup()
void setup()
{
Serial.begin(115200);
fibo_taskSleep(2000);
fibo_thread_create((void*)_my_thread, (INT8*)"mythread",
1024*4, // stack 4KB
NULL, OSI_PRIORITY_NORMAL);
}
void loop() { delay(60000); }Regras importantes
| Regra | Razão |
|---|---|
user_signal_process() obrigatório | SDK exige esta função — sem ela o link falha |
fibo_taskSleep() em threads | delay() não funciona correctamente em contexto RTOS |
fibo_getSysTick() em threads | millis() pode não funcionar em threads |
extern "C" para headers SDK | oc_ws.h, oc_coap.h são código C puro |
| Stack mínimo 4KB | Menos pode causar stack overflow |
fibo_taskSleep(2000) após Serial.begin | UART precisa de tempo para inicializar |
8Rede e PDP
Todos os exemplos de rede usam esta função padrão waitNetworkAndPDP():
static UINT8 _pdp_ip[50] = {0};
static bool waitNetworkAndPDP()
{
// Asalvar registo LTE — aceitar home (1) E roaming (5)
Serial.print("[LTE] A asalvar registo");
for (int i = 0; i < 120; i++) {
reg_info_t reg;
fibo_getRegInfo(®, (CFW_SIM_ID)0);
if (reg.nStatus == 1 || reg.nStatus == 5) {
Serial.printf(" OK (status=%d)\n", reg.nStatus);
break;
}
if (i % 10 == 0) Serial.printf("\n[LTE] status=%d...", reg.nStatus);
else Serial.print(".");
fibo_taskSleep(1000);
}
// Verificar se PDP já está ativo
UINT8 status = 0;
memset(_pdp_ip, 0, sizeof(_pdp_ip));
fibo_PDPStatus(1, _pdp_ip, &status, (CFW_SIM_ID)0);
if (status == 1 && _pdp_ip[0]) {
Serial.printf("[LTE] PDP ja ativo IP=%s\n", _pdp_ip);
return true;
}
// Ativar PDP com polling (mais robusto que semáforo)
Serial.println("[LTE] A ativar PDP...");
memset(_pdp_ip, 0, sizeof(_pdp_ip));
fibo_PDPActive(1, NULL, NULL, NULL, 0, (CFW_SIM_ID)0, NULL);
for (int i = 0; i < 30; i++) {
fibo_taskSleep(1000);
Serial.print(".");
fibo_PDPStatus(1, _pdp_ip, &status, (CFW_SIM_ID)0);
if (status == 1 && _pdp_ip[0]) break;
}
Serial.println();
if (status == 1 && _pdp_ip[0]) {
Serial.printf("[LTE] IP: %s\n", _pdp_ip);
return true;
}
Serial.println("[LTE] PDP falhou!");
return false;
}Output típico:
[LTE] A asalvar registo [LTE] status=0.... OK (status=1) [LTE] A ativar PDP... . [LTE] IP: 10.69.150.81,2804:389:C0C2:C3EF:E621:86CE:F33A:F31D
nStatus=5 — é importante aceitar este estado além de nStatus=1.9Os 58 Exemplos
01_Blink
LED GPIO pisca usando o LPG hardware (GPIO5). O "Hello World" do L610.
✓ Testado02_Serial
Echo UART — recebe bytes pelo Serial e retransmite. Testa a comunicação básica.
✓ Testado03_MQTT
Publish/Subscribe no broker público HiveMQ (broker.hivemq.com:1883). Inclui callbacks de RX, PUB e SUB.
✓ Testado04_HTTP
HTTP GET e POST para httpbin.org. Usa callback assíncrono para receber a resposta.
✓ Testado06_TCP
Socket TCP raw conectando a httpbin.org:80. Resolve DNS, liga, envia GET e recebe resposta.
✓ Testado14_FTP
Upload e download de arquivos via FTP. Servidor de teste: ftp.dlptest.com.
✓ Testado28_SSL
Socket TLS/HTTPS conectando a httpbin.org:443. Nota: requer 10s de espera após PDP.
✓ Testado32_WebSocket
WebSocket client com callbacks de recepção e fechamento. Servidor: echo.websocket.org.
✓ Testado44_CoAP
Protocolo CoAP (UDP) para IoT constrangido. GET /hello no servidor coap.me:5683.
✓ Testado26_Ping
ICMP ping via fibo_mping. Mostra RTT e estatísticas. Nota: alguns operadores bloqueiam ICMP.
✓ Testado27_NTP
Sincronização de tempo com pool NTP brasileiro (a.st1.ntp.br). Atualiza o RTC interno.
✓ Testado29_LBS
Localização por Cell ID. Mostra TAC e CellID LTE. Compatível com opencellid.org.
✓ Testado30_CellInfo
Info detalhada da célula LTE: TAC, CellID, RSSI em dBm, IMEI, IMSI, ICCID.
✓ Testado36_DNS
Resolução DNS de múltiplos hosts. Mostra os servidores DNS do operador e IPs resolvidos.
✓ Testado38_MultiPDP
Ativação de múltiplos contextos PDP (CID 1-7). Útil para separar tráfego IoT/dados.
✓ Testado56_SpeedTest
Teste de velocidade download e upload via TCP raw. Mostra Kbps e KB/s.
✓ Testado05_GPIO
Input, Output e Interrupções GPIO. Debounce e detecção de flancos.
✓ Testado09_I2C
Sensor de temperatura/humidade SHT40 via I2C. Lê valores e mostra no Serial.
✓ Testado10_SPI_Flash
Flash SPI externa GD25LQ64C — erase, write, read e verificação.
✓ Testado11_InnerFlash
Flash interna NVM do L610 — salvar e recuperar dados persistentes.
✓ Testado12_RTC
Relógio em tempo real — set/get hora, alarme e incremento periódico.
✓ Testado13_File
Filesystem /FFS/ — criar, escrever, ler, listar e excluir arquivos.
✓ Testado17_PWM_LPG
PWM via hardware LPG do L610. Controlo de intensidade luminosa.
✓ Testado18_SDCard
SD Card via interface SDIO. Leitura/escrita de arquivos.
⚠️ HW kit49_LCD_ST7735
Display SPI colorido 128×160. Preenchimento, linhas, pixels, gradiente e sleep/wake.
✓ Compilado50_Camera
Câmera SPI GC0308 VGA — preview contínuo no LCD e captura de foto.
✓ Compilado52_RS485
Interface RS485 half-duplex para Modbus RTU e sensores industriais.
✓ Compilado53_ExtFlash
Flash SPI externa com filesystem montado como /ext/. Erase, write, read.
✓ Compilado07_GNSS
GPS/GLONASS NMEA parser — latitude, longitude, altitude, velocidade e satélites.
✓ Testado08_SMS
Envio e recepção de SMS. Leitura de mensagens recebidas via sinal.
✓ Testado16_BLE_Client
BLE scan, connect, discover services e leitura de características.
✓ Testado21_BT_SPP
Bluetooth SPP (Serial Port Profile) — emula porta série over Bluetooth.
✓ Testado22_BLE_Server
BLE GATT Server com serviço personalizado, notificações e writes.
✓ Testado23_BT_Classic
Bluetooth Classic — scan, pair e troca de dados.
✓ Testado24_BT_A2DP
Bluetooth A2DP Audio Sink — receber áudio de smartphone.
⚠️ HW kit25_WiFiScan
Scan passivo de redes WiFi — lista SSIDs, BSSIDs, canais e RSSI.
✓ Testado39_DualSIM
Gestão dual SIM — sinal por SIM, troca ativa com cfun0/cfun1.
✓ Testado15_AT_Commands
Envio de comandos AT raw e recepção de respostas via arduino_at_response().
✓ Testado19_USB_Device
USB CDC Device — emula porta série USB no PC.
✓ Testado20_Debug
Debug logs via OSI_LOGI — visível no output do compilador e debug serial.
✓ Testado33_CPU_Usage
Monitor de CPU e RAM em tempo real. Útil para optimizar aplicações.
✓ Testado34_DeviceInfo
IMEI, IMSI, ICCID, número de série, versão de firmware e hardware.
✓ Testado40_XML
Parser XML SAX via Expat. Parsing de documentos XML com callbacks.
✓ Testado55_AppVersion
Gestão de versão da aplicação Arduino. Acessível via AT+APP.
✓ Compilado57_RTOS
FreeRTOS: criação de tasks, semáforos, event groups, secções críticas e gestão de memória.
✓ Compilado31_MP3_Player
Reprodutor MP3/AMR de arquivos no filesystem /FFS/. Controlo play/pause/stop.
✓ Compilado35_TTS
Text-to-Speech — converter texto em fala com voz em Português/Chinês.
✓ Compilado41_POC
Push-to-Talk (PoC) — streaming de áudio bidirecional em tempo real.
✓ Compilado42_AudioList
Playlist de arquivos MP3/AMR com fibo_audio_list_play. Útil para síntese de voz.
✓ Testado54_AudioStream
Audio stream PCM via buffer circular. Para VoIP e streaming em tempo real.
✓ Compilado58_QRCode
Leitura de QR Code e Barcodes via câmera SPI. Usa fibo_sweep_code().
✓ Compilado43_PSM
Power Saving Mode — consumo de ~5µA durante sono. T3412/T3324 configuráveis.
✓ Testado45_OTA
OTA update da aplicação Arduino via HTTP. Download + apply + reboot automático.
✓ Testado46_Charger
Monitor de bateria e carregador — tensão, nível %, corrente, estado de carga.
✓ Testado47_PowerKey
Botão de power com shutdown gracioso — executa código antes de desconectar.
✓ Testado48_FOTA
Firmware OTA — atualizar o firmware do modem via HTTP. fibo_firmware_ota().
✓ Testado37_Crypto
SHA256, HMAC-SHA1 e Base64. Para autenticação em serviços cloud IoT.
✓ Compilado51_Base64
Base64 encode/decode com 6 casos práticos: HTTP Basic Auth, JWT, dados binários.
✓ Compilado10Referência da API FIBOCOM
MQTT
fibo_mqtt_set((INT8*)user, (INT8*)pass) fibo_mqtt_connect(client_id, host, port, clean_session, keepalive, tls) fibo_mqtt_pub(topic, qos, retain, message, msglen) fibo_mqtt_sub(topic, qos) // Sinais: GAPP_SIG_CONNECT_RSP, GAPP_SIG_INCOMING_DATA_RSP, // GAPP_SIG_CLOSE_RSP, GAPP_SIG_PUB_RSP, GAPP_SIG_SUB_RSP
HTTP
oc_http_param_t *http = fibo_http_new() strncpy((char*)http->url, url, OC_HTTP_URL_LEN-1) http->enHttpReadMethod = OC_USER_CALLBACK http->cbReadBody = my_cb // void cb(UINT8 *buf, UINT32 len, INT32 flag) fibo_http_get(http, NULL) fibo_http_post_ex(http, data, header, len) fibo_http_delete(http)
SSL / TLS
fibo_set_ssl_chkmode(0) // 0 = sem verificar CA fibo_taskSleep(10000) // OBRIGATORIO após PDP! INT32 ssl = fibo_ssl_sock_create() fibo_ssl_sock_connect(ssl, host, port) fibo_ssl_sock_send / recv / close
TCP Socket
INT32 sock = fibo_sock_create(GAPP_IPPROTO_TCP) GAPP_TCPIP_ADDR_T addr; addr.sin_port = (uint16_t)(((port&0xFF)<<8)|((port>>8)&0xFF)); addr.sin_addr = server_ip; // de fibo_getHostByName fibo_sock_connect(sock, &addr) fibo_sock_send / recv / close
WebSocket
extern "C" { #include "oc_ws.h" }
fibo_ws_opt_t opt = {30000, 10000, true, NULL};
void *ws = fibo_ws_open(url, &opt, data_cb, close_cb, NULL)
fibo_ws_send_text(ws, (INT8*)msg)
fibo_ws_close(ws)CoAP
extern "C" { #include "oc_coap.h" }
void *h = fibo_coap_open(host, 5683, 0)
void *msg = fibo_coap_message_create(id, FIBOCOM_COAP_MSG_CON,
0, FIBOCOM_COAP_GET, payload, plen, token, tlen)
fibo_coap_message_add_uri(msg, "/path")
fibo_coap_send(h, msg) / fibo_coap_message_destroy(msg)
fibo_copa_wait_mesg(h, 1000)
void *r = fibo_coap_recv(h)
fibo_coap_close(h)Dispositivo
fibo_get_imei(imei, (CFW_SIM_ID)0) fibo_get_imsi(imsi) // sem argumento simID! fibo_get_ccid(iccid) fibo_get_csq(&rssi, &ber) // INT32* — sem argumento simID! fibo_getRegInfo(®, (CFW_SIM_ID)0) // reg.nStatus: 1=home, 5=roaming // reg.curr_rat: 7=LTE, 3=GSM // reg.lte_scell_info.tac / cell_id // reg.gsm_scell_info.lac / cell_id
RTOS
fibo_thread_create(fn, name, stack, param, OSI_PRIORITY_NORMAL) fibo_thread_delete() fibo_taskSleep(ms) fibo_getSysTick() // ms desde boot UINT32 sem = fibo_sem_new(0) fibo_sem_wait(sem) int ret = fibo_sem_try_wait(sem, timeout_ms) // 0=OK, !=0=timeout fibo_sem_signal(sem) / fibo_sem_free(sem) void *p = fibo_malloc(size) / fibo_free(p) UINT32 st = fibo_enterCS() / fibo_exitCS(st)
Energia
fibo_psm_mode(1, "01000001", "00000001") // 1h sleep, 2s active fibo_getVbatStaticVol() // tensão mV fibo_getVbatVolLevel() // nível % fibo_ischargering() // true=a carregar fibo_set_pwr_callback((key_callback)cb, 4000) fibo_softPowerOff() fibo_softReset()
OTA
fibo_ota_set_callback(cb) // void cb(UINT32 received, UINT32 total) fibo_open_ota(0, url, NULL, NULL, NULL) // APP OTA fibo_firmware_ota(0, url, NULL, NULL, NULL) // FW OTA fibo_ota_errcode() // código de erro