Saltar al contenido principal

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...