Skip to main content

SenseCAP Indicator - Desarrollo de Aplicaciones LoRa

Introducción

LoRa® es una tecnología de comunicación inalámbrica de largo alcance optimizada para enviar pequeñas cantidades de datos a grandes distancias. Funciona modulando señales de radio en el espectro sub-GHz utilizando un método llamado Chirp Spread Spectrum (CSS).

El SenseCAP Indicator(versión D1L y D1Pro) de Seeed Studio incluye un módulo transceptor LoRa integrado(chip LoRa® Semtech SX1262), facilitando agregar conectividad inalámbrica de bajo consumo a tus proyectos. En esta publicación, explicaremos cómo configurar la comunicación LoRa entre dos placas SenseCAP Indicator.

Descripción General

Esta demostración muestra cómo establecer una comunicación LoRa básica entre el SenseCAP Indicator y la placa XIAO utilizando el Wio-E5 como intermediario. El SenseCAP Indicator obtiene datos de sensores del XIAO, que luego se transmiten a través del Wio-E5. La carga útil transmitida es posteriormente recibida por el SenseCAP Indicator, que descifra y produce el resultado y muestra los datos en su pantalla.

Sin Código LVGL: Código · GitHub

Hardware

SenseCAP Indicator

Desde la página, Dive_into_the_Hardware, podemos ver que el transceptor LoRa está conectado al MCU ESP32-S3 a través de la interfaz SPI.

Los componentes clave son:

  • Front-end de radio Semtech SX1262
  • MCU ESP32-S3

El transceptor LoRa maneja toda la modulación y demodulación de bajo nivel de las señales LoRa. Podemos comunicarnos con él utilizando la interfaz SPI desde el ESP32-S3.

XIAO

El XIAO en esta demostración es necesario para recopilar datos de sensores y transmitirlos al SenseCAP Indicator a través del Wio-E5. El XIAO está conectado al Wio-E5 a través de la interfaz UART.

  • XIAO
  • Wio-E5
  • SEN5x

Software

Como el SDK SenseCAP_Indicator_ESP32 ya ha proporcionado la biblioteca LoRa, podemos usarla directamente, puedes revisar rápidamente la página LoRa® para ver cómo usar la biblioteca LoRa.

Comenzando

esta demostración muestra cómo configurar un Hub LoRa® Local para Conectividad IoT.

Prerrequisitos

Por favor sigue las instrucciones proporcionadas para configurar el entorno de desarrollo.

Paso 1: Descargar el Código de Demostración

Clona o descarga el código de demostración desde este enlace. Este código servirá como punto de partida para tu aplicación LoRa.

Paso 2: Implementar el Codificador de Carga Útil (XIAO;Arduino)

Paso 2.1: Implementar tu Estructura de Carga Útil y Codificador

#ifndef _FRAME_H
#define _FRAME_H
#include <Arduino.h>
#include <vector>

/** payload format
* | topic | dataLength | Data Payload | CRC |
* | 1byte | 1byte | n byte | 2byte |
* example:
* | 0x01 | 0x0E | 14 bytes | 2byte | for SEN54
* | 0x01 | 0x10 | 16 bytes | 2byte | for SEN55
*/

#pragma pack(1)
enum topics {
TOPICS_MIN = 0x00,
TOPICS_SEN5x = 0x01,
TOPIC_MAX,
};

#pragma pack(1)
typedef struct
{
enum topics topic; /*msg type*/
uint8_t dataLength;
std::vector<uint8_t> data; /*actual data of payload*/
uint16_t crc;
} Frame_t;
String packFrame(Frame_t frame);
void deleteFrame(Frame_t *frame);
uint16_t crc16_ccitt(const uint8_t *data, size_t length);
#endif

Paso 2.2: Implementar la estructura de datos del sensor y adaptarla al Codificador de Carga Útil

#ifndef PAYLOAD_SEN5X_H
#define PAYLOAD_SEN5X_H
#include "Frame.h"
#include "SensorPayload.h"
#include <SensirionI2CSen5x.h>

#define DEVICE_SEN54

#if defined(DEVICE_SEN54)
#elif defined(DEVICE_SEN55)
#else
#error "Please define a device in the compiler options."
#endif

class PayloadSEN5x : public SensorPayload<SensirionI2CSen5x> {
public:
PayloadSEN5x(SensirionI2CSen5x handler);
uint16_t init() override;
String toPayloadString() override;

private:
uint16_t massConcentrationPm1p0;
uint16_t massConcentrationPm2p5;
uint16_t massConcentrationPm4p0;
uint16_t massConcentrationPm10p0;
int16_t ambientHumidity;
int16_t ambientTemperature;
int16_t vocIndex;
#ifdef DEVICE_SEN55
// int16_t noxIndex; // Sensor SEN54 does not support NOx
#endif
SensirionI2CSen5x _sen5x;
};
#endif // PAYLOAD_SEN5X_H

La función toPayloadString serializará los datos en una cadena, y la cadena será enviada al SenseCAP Indicator a través del Wio-E5.

Paso 2.3: Compilar y Subir el Código al XIAO

#include "sensor_sen5x.h"
#include "wio_e5_at.h"
#include <Arduino.h>
#include <SensirionI2CSen5x.h>
#include <Wire.h>
SoftwareSerial serial_lora( D2, D3 );
Radio radio( serial_lora, RF_FREQUENCY, LORA_SF12, LORA_BW_125, 15, 15, 14, LORA_CRC_ON, LORA_IQ_NORMAL, LORA_PUBLIC_OFF );

SensirionI2CSen5x sen5x;
PayloadSEN5x payloadSEN5x( sen5x );

void setup() {
delay( 2000 );
wait_serial();
Serial.println( "Starting..." );

radio.begin();

Wire.begin();
payloadSEN5x.init();

Serial.println( "APP begin" );
}

void loop() {
static int count = 0;
static unsigned long task_time = 0;
static String test_string;

if ( millis() - task_time > 10000 ) {
task_time = millis();

radio.sendPayload( payloadSEN5x.toPayloadString() );

Serial.printf( "Send data %d\r\n", count++ );
}
}

Completa el Payload, ahora nos sumergiremos en SenseCAP Indicator para programar el decodificador de payload.

Paso 3: Implementar el Decodificador de Payload (SenseCAP Indicator;ESP-IDF)

El decodificador de payload es una función que convierte el payload binario recibido del transceptor LoRa en un formato legible por humanos. El decodificador de payload es específico para tu aplicación y debe ser implementado por ti. El decodificador de payload para esta demostración se proporciona en el código de demostración.

Paso 3.1: Implementa Tu Decodificador de Payload

  #ifndef __SIMPLE_FRAME_H
#define __SIMPLE_FRAME_H
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/** payload format
* | topic | dataLength | Data Payload | CRC |
* | 1byte | 1byte | n byte | 2byte |
* example:
* | 0x01 | 0x0E | 14 bytes | 2byte | for SEN54
* | 0x01 | 0x10 | 16 bytes | 2byte | for SEN55
*/

#pragma pack(1)
enum topics {
TOPICS_MIN = 0x00,
TOPICS_SEN5x = 0x01,
TOPIC_MAX,
};
typedef struct
{
enum topics topic; /*msg type or DataId*/
uint8_t dataLength;
uint8_t *data; /*actual data of payload*/
uint16_t crc;
} Frame_t;
Frame_t *parsePayload( uint8_t *payload, uint8_t length );
void deleteFrame( Frame_t *frame );
uint16_t crc16_ccitt( const uint8_t *data, size_t length );
#endif

Paso 3.2: Implementar la Estructura de Datos del Sensor

  #ifndef PAYLOAD_SEN5X_H
#define PAYLOAD_SEN5X_H
#include "SensorPayload.h"

#define DEVICE_SEN54

#if defined( DEVICE_SEN54 )
#elif defined( DEVICE_SEN55 )
#else
#error "Please define a device in the compiler options."
#endif
#pragma pack(push, 1)
typedef union {
struct
{
uint16_t massConcentrationPm1p0;
uint16_t massConcentrationPm2p5;
uint16_t massConcentrationPm4p0;
uint16_t massConcentrationPm10p0;
int16_t ambientHumidity;
int16_t ambientTemperature;
int16_t vocIndex;
#ifdef DEVICE_SEN55
int16_t noxIndex;
#endif
};

#ifdef DEVICE_SEN55
int16_t data[8];
#else
int16_t data[7];
#endif
} SEN5xData_t;
#pragma pack(pop)
void phraseSEN5xData( uint8_t *data_arry, SEN5xData_t *SEN5x );
void prinSEN5xData( const SEN5xData_t *SEN5x );
#endif // PAYLOAD_SEN5X_H

Paso 3.3: Configurar el LoRa

Configurar los Parámetros de LoRa

configura los parámetros de LoRa necesarios como frecuencia, factor de dispersión y ancho de banda. Estas configuraciones deben coincidir entre los dos canales LoRa para una comunicación exitosa.

#define RF_FREQUENCY               868000000 // Hz
#define LORA_BANDWIDTH 0 // [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
#define LORA_SPREADING_FACTOR 12 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
#define LORA_PREAMBLE_LENGTH 15 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 5 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
Configurar el Receptor del Transceptor LoRa
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
SEN5xData_t sen5x_data;
Frame_t *frame = parsePayload( payload, size );
if ( frame == NULL ) {
ESP_LOGE( TAG, "parsePayload error" );
return;
}
ESP_LOGI( TAG, "frame->type: %s", dataIDToString( frame->topic ) );

switch ( frame->topic ) {
case TOPICS_SEN5x:
phraseSEN5xData( frame->data, &sen5x_data );
break;
default:
break;
}
deleteFrame( frame );
}
Inicializar el Transceptor LoRa
RadioEvents.RxDone = OnRxDone;
Radio.Init( &RadioEvents );

Radio.SetChannel( RF_FREQUENCY );

Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
Radio.SetMaxPayloadLength( MODEM_LORA, 255 );

Radio.Rx( 0 ); // Continuous Rx

Paso 3.4: Compilar y Flashear el Código al SenseCAP Indicator

/**
* @source: https://github.com/Seeed-Solution/indicator_lora_commu/blob/29624d10643a41ae5e1e24124b81e93b5e3cd3bb/Indicator/main/main.c
*/
#include "bsp_board.h"
#include "esp_log.h"
#include "frame.h"
#include "radio.h"
#include "sen5x.h"

static const char *TAG = "app_main";

#define VERSION "v0.0.1"

#define SENSECAP "\n\
_____ _________ ____ \n\
/ ___/___ ____ ________ / ____/ | / __ \\ \n\
\\__ \\/ _ \\/ __ \\/ ___/ _ \\/ / / /| | / /_/ / \n\
___/ / __/ / / (__ ) __/ /___/ ___ |/ ____/ \n\
/____/\\___/_/ /_/____/\\___/\\____/_/ |_/_/ \n\
--------------------------------------------------------\n\
Version: %s %s %s\n\
--------------------------------------------------------\n\
"

#define RF_FREQUENCY 868000000 // Hz
#define LORA_BANDWIDTH 0 // [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
#define LORA_SPREADING_FACTOR 12 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
#define LORA_PREAMBLE_LENGTH 15 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 5 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false

static RadioEvents_t RadioEvents;

SEN5xData_t sen5x_data;

void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ) {
int i = 0;
ESP_LOGI( TAG, "rssi:%d dBm, snr:%d dB, len:%d, payload:", rssi, snr, size );
for ( i = 0; i < size; i++ ) {
printf( "0x%x ", payload[i] );
}
printf( "\n" );

Frame_t *frame = parsePayload( payload, size );
if ( frame == NULL ) {
ESP_LOGE( TAG, "parsePayload error" );
return;
}
ESP_LOGI( TAG, "frame->type: %s", dataIDToString( frame->topic ) );

switch ( frame->topic ) {
case TOPICS_SEN5x:
phraseSEN5xData( frame->data, &sen5x_data );
prinSEN5xData( &sen5x_data );
break;

default:
break;
}

deleteFrame( frame );
}

void app_main( void ) {
ESP_LOGI( "", SENSECAP, VERSION, __DATE__, __TIME__ );

ESP_ERROR_CHECK( bsp_board_init() );

ESP_LOGI( TAG, "APP MAIN START" );

RadioEvents.RxDone = OnRxDone;
Radio.Init( &RadioEvents );

Radio.SetChannel( RF_FREQUENCY );

Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
Radio.SetMaxPayloadLength( MODEM_LORA, 255 );

Radio.Rx( 0 ); // Continuous Rx

while ( 1 ) {
vTaskDelay( pdMS_TO_TICKS( 10000 ) );
}
}

Paso 4: Probar la Comunicación

Enciende ambas placas SenseCAP Indicator y abre el monitor serie. Deberías ver mensajes siendo enviados y recibidos entre las dos placas. ¡Felicitaciones! Has configurado exitosamente la comunicación LoRa usando el SenseCAP Indicator.

Serial Monitor of XIAO
String: 76,80,81,81,5389,5990,980
0 4C 0 50 0 51 0 51 15 D 17 66 3 D4
CRC: 629
<<<AT+TEST=TXLRPKT,"010E004C005000510051150D176603D40629"
>>>+TEST: TX DONE
+TEST: TXLRPKT

Send payload successfully
Send data 1
Serial Monitor of SenseCAP Indicator
I (95490) app_main: rssi:-22 dBm, snr:5 dB, len:18, payload:
0x1 0xe 0x0 0x4c 0x0 0x50 0x0 0x51 0x0 0x51 0x15 0xd 0x17 0x66 0x3 0xd4 0x6 0x29
W (95541) parsePayload: topic: 1
W (95541) parsePayload: dataLength: 14
W (95545) parsePayload: payload[0]: 00
W (95549) parsePayload: payload[1]: 4C
W (95554) parsePayload: payload[2]: 00
W (95558) parsePayload: payload[3]: 50
W (95563) parsePayload: payload[4]: 00
W (95567) parsePayload: payload[5]: 51
W (95572) parsePayload: payload[6]: 00
W (95576) parsePayload: payload[7]: 51
W (95580) parsePayload: payload[8]: 15
W (95585) parsePayload: payload[9]: 0D
W (95589) parsePayload: payload[10]: 17
W (95594) parsePayload: payload[11]: 66
W (95598) parsePayload: payload[12]: 03
W (95603) parsePayload: payload[13]: D4
I (95607) app_main: frame->type: SEN5X
I (95612) sen5x_: massConcentrationPm1p0: 76
I (95617) sen5x_: massConcentrationPm2p5: 80
I (95622) sen5x_: massConcentrationPm4p0: 81
I (95627) sen5x_: massConcentrationPm10p0: 81
I (95632) sen5x_: ambientHumidity: 5389
I (95636) sen5x_: ambientTemperature: 5990
I (95641) sen5x_: vocIndex: 980

Recursos

nombre Función
Control de Pitido Recibe la cadena "ON" o "OFF", puede ejecutar las funciones correspondientes
PingPong establece un patrón de comunicación ping-pong entre un dispositivo maestro y un esclavo.
Carga de Datos Multi-Sensor XIAOS3 recopila datos y utiliza Wio-E5 (con módulo LoRa y comandos AT) para cargar datos del sensor al Indicator.

Para más detalles, consulta el archivo README.

Soporte Técnico

¿Necesitas ayuda con tu SenseCAP Indicator? ¡Estamos aquí para asistirte!

Si encuentras algún problema o tienes preguntas mientras sigues este tutorial, no dudes en contactar a nuestro soporte técnico. ¡Siempre estamos aquí para ayudar!

Visita nuestro Canal Oficial de Discord de Seeed para hacer tus preguntas o las discusiones de GitHub para compartir todo lo que quieras!

Loading Comments...