Recopile datos agrícolas a través de The Things Network
Este ejemplo muestra cómo configurar la recopilación de datos de tres sensores conectados a una placa de microprocesador con una radio LoRa®.
Esta configuración permite la creación de una red de sensores distribuidos en un área grande. Los sensores envían datos a The Things Network, que luego se reenvían a ThingSpeak™ para su análisis y visualización. En el ejemplo, usted construye un prototipo de dispositivo, se conecta a The Things Network e integra la recopilación de datos con ThingSpeak. El dispositivo que se muestra aquí recopila datos de temperatura, humedad del suelo y GPS.
Descripción general
El ejemplo consta de tres pasos principales. La integración de Things Network se divide a su vez en varios subpasos. El paso más complicado es la construcción del dispositivo. Para completar el ejemplo, necesita una cuenta de ThingSpeak y una cuenta en The Things Network. En ThingSpeak, creas un nuevo canal. En Things Network, creas una aplicación y registras un dispositivo. Luego crea un decodificador de carga útil y agrega la integración que reenvía los datos a ThingSpeak.
1) Configure un canal ThingSpeak para recopilar datos
2) Configurar la red de cosas
Crear aplicación
Registrar dispositivo
Crear formato de carga útil
Agregar integración
3) Crear dispositivo
Hardware utilizado para crear el nodo sensor
Esquema y conexiones
4) Dispositivo de programa
Configuración de programación
Código
Configurar un canal ThingSpeak para recopilar datos
1) Cree un canal ThingSpeak, como se muestra en Collect Data in a New Channel. Registre la clave API de escritura y el ID del canal para su nuevo canal.
2) Navegue a la página Configuración del canal. Configure las etiquetas de campo de la siguiente manera.
Campo 1 :
Counter
Campo 2:
Soil Moisture
Campo 3:
Temperature F
3) Haga clic en Guardar canal en la parte inferior para guardar su configuración.
Configurar la aplicación de red Things
Crea una cuenta en The Things Network, y luego inicia sesión en The Things Network Console.
Crear aplicación
1) Seleccione Aplicaciones.
2) Seleccione Agregar aplicación.
3) Cree un ID de aplicación, luego agregue una Descripción. Seleccione el Registro de manejador según su ubicación.
Registrar un dispositivo
1) Haga clic en la pestaña Dispositivos y registre un dispositivo. Para obtener más información, consulte Device Registration.
2) Cree una identificación de dispositivo. Ingrese el EUI del dispositivo si su dispositivo tiene uno. De lo contrario, seleccione el botón a la izquierda del campo Device EUI para generar el EUI automáticamente.
3) Haga clic en Registrarse. El navegador le devuelve a la pestaña Descripción general.
4) Seleccione la pestaña Configuración.
5) En la configuración, seleccione ABP para el método de activación. Para ayudar a la depuración, opcionalmente puede desactivar las Comprobaciones del contador de fotogramas en la parte inferior de la página.
6) Registre la Dirección del dispositivo, la Clave de sesión de red y la Clave de sesión de la aplicación. Esta información es necesaria en el código de su dispositivo.
Crear formateador de carga útil
El formateador de carga útil utiliza los bytes enviados a la aplicación desde la puerta de enlace para ensamblar un mensaje. En este ejemplo, el mensaje de carga útil deseado es un objeto codificado en JSON que se envía a ThingSpeak.
1) Regrese a la vista de la aplicación usando el menú de navegación en la parte superior. Luego haga clic en la pestaña Formatos de carga útil.
2) En la interfaz decoder
, cree el código para convertir los bytes enviados desde su dispositivo en un objeto JSON para escribir en ThingSpeak. El código condicional para lat
y lon
maneja la posibilidad de valores positivos o negativos.
function Decoder(b, port) { var counter = b[0] << 8) | b[1]; var moisture = b[2] | b[3] << 8; var temp= ( b[4] | b[5] << 8 )/100; var lat = ( b[6] | b[7] << 8 | b[8] << 16 | (b[8] & 0x80 ? 0xFF << 24 : 0)) / 10000; var lon = ( b[9] | b[10] << 8 | b[11] << 16 | (b[11] & 0x80 ? 0xFF << 24 : 0)) / 10000; return { field1: counter, field2: moisture, field3: temp, latitude: lat, longitude: lon } }
Agregar integración
Para reenviar datos a ThingSpeak, debe tener una aplicación en Things Network con un dispositivo registrado y un formateador de carga útil. Cree una integración de ThingSpeak para reenviar los datos.
1) Inicie sesión en su The Things Network Console.
2) Seleccione Aplicaciones y seleccione la aplicación desde la que desea reenviar datos a ThingSpeak.
3) Haga clic en la pestaña Integraciones.
4) Seleccione Cosa Habla.
5) En el campo ID de proceso, asigne un nombre a su integración.
6) En el campo Autorización, ingrese la clave API de escritura para el canal en el que desea almacenar sus datos. La clave API está disponible en la pestaña Claves API de tu canal ThingSpeak.
7) En el campo ID de canal, ingrese el ID del canal de ThingSpeak al que desea reenviar datos. El ID del canal está disponible en la página de su canal ThingSpeak.
Crear dispositivo
Hardware utilizado para crear el nodo sensor
Puede utilizar varios dispositivos LoRa que admitan protocolos LoRaWan para conectarse a The Things Network. Este ejemplo demuestra el procedimiento utilizando la siguiente configuración de hardware.
Pluma de Adafruit M0
Adafruit Ultimate GPS FeatherWing
Monitor de suelo (por ejemplo, Sparkfun Moisture Sensor SEN 13322)
DHT22
Cabeceras y alambre
Antena GPS W14Q5A-y
Interruptor de palanca
Batería LiPo de 500 mAh.
Esquema y conexiones
Conecte los sensores como se muestra en el esquema. La fotografía muestra una posible configuración de los sensores en una caja de proyecto. En esta configuración, es posible que el sensor de temperatura dentro de la caja no refleje exactamente la temperatura exterior. Necesita add an antenna a la radio LoRa.
1) Conecte las conexiones de alimentación y tierra para el GPS y los sensores de temperatura. No conecte la alimentación al sensor de humedad.
2) Conecte la salida del sensor de humedad del suelo a la entrada analógica en A0.
3) Configure el sistema para que el sensor de humedad se apague cuando no esté en uso. El pin de alimentación del sensor de humedad está conectado al pin GPIO 11 en la pluma M0. Apagar la alimentación cuando no se utiliza prolonga la vida útil del sensor.
4) Conecte el pin de datos del sensor DH-22 al PA-15 en el Feather M0, que es el pin 5 en el boceto de Arduino.
5) Para la placa GPS, conecte TX a RX en el Feather M0 y RX a TX.
6) Habilite la radio LoRa conectando PA20 (pin 29, GPIO 6) en el Feather M0 a tierra.
7) Cree un interruptor de alimentación conectando un interruptor del pin En a tierra.
Dispositivo de programa
Configuración de programación
1) Descargue el último IDE de Arduino.
2) Descargue la Adafruit GPS library o agregue la biblioteca Adafruit_GPS
en el administrador de bibliotecas. Seleccione Sketch
> Include Library
> Manage Libraries
. Busque Adafruit_GPS
para agregarlo a sus bibliotecas instaladas.
3) Descargue el LoraWan-in-C library para el entorno Arduino o agregue las bibliotecas lmic
y hal/hal
en el administrador de bibliotecas. Seleccione Sketch
> Include Library
> Manage Libraries
. Busque lmic
y seleccione MCCI LoRaWAN LMIC library
para agregarlo a sus bibliotecas instaladas. Añade también el MCCI Arduino LoRaWan Library to the library manager.
4) Crea la aplicación. Abra una nueva ventana en el IDE de Arduino y guarde el archivo. Agregue el código proporcionado en la sección Código.
Código
1) Comience incluyendo las bibliotecas apropiadas e inicializando las variables.
#include <lmic.h> #include <hal/hal.h> #include <SPI.h> #include "DHT.h" #include <Adafruit_GPS.h> #define DHTPIN 5 #define GPSSerial Serial1 #define SOIL_PIN 14 #define SOIL_POWER_PIN 11 #define GPSECHO false // Set to 'true' if you want to debug and listen to the raw GPS sentences #define PAYLOAD_SIZE 13 // Number of bytes sent plus 2 // LoRaWAN NwkSKey, network session key static const PROGMEM u1_t NWKSKEY[16] = {0x98, 0xEB, 0x1A, 0xC5, 0xF9, 0x20, 0x15, 0xCD, 0x12, 0xE5, 0x72, 0xFF, 0xCD, 0xE2, 0x94, 0x46}; // LoRaWAN AppSKey, application session key static const u1_t PROGMEM APPSKEY[16] = {0x50, 0x28, 0x4D, 0xAE, 0xEA, 0x41, 0x53, 0x7E, 0xCA, 0x70, 0xD2, 0x26, 0xCC, 0x14, 0x66, 0x19}; // LoRaWAN end-device address (DevAddr) static const u4_t DEVADDR = 0x26021115; // Callbacks are only used in over-the-air activation. Leave these variables empty unless you use over the air activation. void os_getArtEui(u1_t *buf) {} void os_getDevEui(u1_t *buf) {} void os_getDevKey(u1_t *buf) {} // Payload to send to TTN gateway static uint8_t payload[PAYLOAD_SIZE]; static osjob_t sendjob; // Schedule TX at least this many seconds const unsigned TX_INTERVAL = 60; //was 30 // Pin mapping for Adafruit Feather M0 LoRa const lmic_pinmap lmic_pins = { .nss = 8, .rxtx = LMIC_UNUSED_PIN, .rst = 4, .dio = {3, 6, LMIC_UNUSED_PIN}, .rxtx_rx_active = 0, .rssi_cal = 8, // LBT cal for the Adafruit Feather M0 LoRa, in dB. .spi_freq = 8000000, }; Adafruit_GPS GPS(&GPSSerial); // Connect to the GPS on the hardware port. DHT dht(DHTPIN, DHT22); // Connect to the temperature sensor. uint16_t counter = 0; int32_t myLatitude = -12345; // Initialize for testing before GPS finds a lock. int32_t myLongitude = 54321; // Initialize for testing. int myMoisture = 0; // 10 bit ADC value. float temperatureF = 1111;
2) Utilice la función setup
para iniciar el sensor de temperatura, el GPS y la radio LoRa.
void setup() { Serial.begin(115200); dht.begin(); Serial.println("Start"); // Set the power pin for the moisture sensor pinMode(SOIL_POWER_PIN,OUTPUT); digitalWrite(SOIL_POWER_PIN, LOW); GPS.begin(9600); // 9600 NMEA is the default baud rate. GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // Set a 1 Hz update rate. delay(1000); // Wait for GPS to initialize. // Ask for firmware version GPSSerial.println(PMTK_Q_RELEASE); // Initialize the LMIC. os_init(); // Reset the MAC state. Resetting discards the session and pending data transfers. LMIC_reset(); // Set static session parameters. uint8_t appskey[sizeof(APPSKEY)]; uint8_t nwkskey[sizeof(NWKSKEY)]; memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); LMIC_setSession(0x13, DEVADDR, nwkskey, appskey); LMIC_selectSubBand(1); // Only use the correct The Things Network channels, disable the others. for (int c = 0; c < 72; c++) { if ((c < 8) || (c > 15)) { LMIC_disableChannel(c); } } LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); // Disable link check validation LMIC_setLinkCheckMode(0); // TTN uses SF9 for its RX2 window. LMIC.dn2Dr = DR_SF9; // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) LMIC_setDrTxpow(DR_SF7, 14); // Start job. processJob(&sendjob); }
3) Utilice la función loop
para iniciar el proceso LoRa y analizar los datos del GPS.
void loop() // Run over and over again { os_runloop_once(); char c = GPS.read(); if (GPSECHO) { if (c){ Serial.print(c); } } // If a sentence is received, parse it if (GPS.newNMEAreceived()) { if (!GPS.parse(GPS.lastNMEA())) // Also sets the newNMEAreceived() flag to false return; } }
4) La función GetSensorData
enciende el sensor de humedad y lee sus datos, luego lo apaga. También lee el sensor de temperatura y busca información del dispositivo GPS. Si hay una posición de GPS, esta función actualiza la información de posición.
void GetSensorData() { digitalWrite(SOIL_POWER_PIN, HIGH); delay(1000); myMoisture = analogRead(SOIL_PIN); digitalWrite(SOIL_POWER_PIN, LOW); temperatureF = dht.readTemperature( true ); Serial.println("moisture " + String( myMoisture ) + " temp " + String( temperatureF )); if (GPS.fix) { Serial.print( "Location: " ); Serial.print( GPS.latitudeDegrees * 100, 4 ); Serial.print( " break " ); Serial.print( GPS.lat ); Serial.print( ", " ); Serial.print( GPS.longitudeDegrees * 100 , 4 ); Serial.println( GPS.lon ); myLatitude = GPS.latitudeDegrees * 10000; myLongitude = GPS.longitudeDegrees * 10000; } }
5) Utilice la función onEvent
para procesar eventos de la radio LoRa. La función actualiza el monitor serie, programa la siguiente transmisión y recibe mensajes.
void onEvent(ev_t ev) { Serial.print(os_getTime()); Serial.print(": "); switch (ev) { case EV_SCAN_TIMEOUT: Serial.println(F("EV_SCAN_TIMEOUT")); break; case EV_BEACON_FOUND: Serial.println(F("EV_BEACON_FOUND")); break; case EV_BEACON_MISSED: Serial.println(F("EV_BEACON_MISSED")); break; case EV_BEACON_TRACKED: Serial.println(F("EV_BEACON_TRACKED")); break; case EV_JOINING: Serial.println(F("EV_JOINING")); break; case EV_JOINED: Serial.println(F("EV_JOINED")); break; case EV_JOIN_FAILED: Serial.println(F("EV_JOIN_FAILED")); break; case EV_REJOIN_FAILED: Serial.println(F("EV_REJOIN_FAILED")); break; case EV_TXCOMPLETE: Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); if (LMIC.dataLen) { Serial.println(F("Received ")); Serial.println(LMIC.dataLen); Serial.println(F(" bytes of payload")); } // Schedule next transmission os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(TX_INTERVAL), do_send); break; case EV_LOST_TSYNC: Serial.println(F("EV_LOST_TSYNC")); break; case EV_RESET: Serial.println(F("EV_RESET")); break; case EV_RXCOMPLETE: // data received in ping slot Serial.println(F("EV_RXCOMPLETE")); break; case EV_LINK_DEAD: Serial.println(F("EV_LINK_DEAD")); break; case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; case EV_TXSTART: Serial.println(F("EV_TXSTART")); break; default: Serial.print(F("Unknown event: ")); Serial.println((unsigned)ev); break; } }
6) La función processJob
convierte los datos del sensor en bits para enviarlos a través de la radio LoRa.
void processJob(osjob_t *j) { getSensorData(); if (LMIC.opmode & OP_TXRXPEND) // Check if there is a current TX/RX job running. { Serial.println(F("OP_TXRXPEND, not sending")); } else { payload[0] = byte(counter); payload[1] = counter >>8; payload[2] = byte(myMoisture); payload[3] = myMoisture >> 8; int shiftTemp = int(temperatureF * 100); // Convet temperature float to integer for sending and save two places. payload[4] = byte(shiftTemp); payload[5] = shiftTemp >> 8; payload[6] = byte(myLatitude); payload[7] = myLatitude >> 8; payload[8] = myLatitude >> 16; payload[9] = byte(myLongitude); payload[10] = myLongitude >> 8; payload[11] = myLongitude >> 16; LMIC_setTxData2(1, payload, sizeof(payload) - 1, 0); // Prepare upstream data transmission at the next possible time. counter++; Serial.println(String(counter)); } // Next TX is scheduled after TX_COMPLETE event.
Para aprender cómo crear un panel de visualización detallado en su canal ThingSpeak, consulte Create Customized ThingSpeak Channel View.