Sammeln landwirtschaftlicher Daten über das The Things Network
Dieses Beispiel zeigt, wie die Datenerfassung von drei Sensoren eingerichtet wird, die mit einem LoRa®-Radio an eine Mikroprozessorplatine angeschlossen sind.
Diese Konfiguration ermöglicht die Erstellung eines Netzwerks verteilter Sensoren über ein großes Gebiet. Die Sensoren senden Daten an The Things Network, die dann zur Analyse und Visualisierung an ThingSpeak™ weitergeleitet werden. Im Beispiel erstellen Sie ein Prototypgerät, stellen eine Verbindung zum Things Network her und integrieren die Datenerfassung mit ThingSpeak. Das hier gezeigte Gerät erfasst Temperatur, Bodenfeuchtigkeit und GPS-Daten.

Überblick
Das Beispiel besteht aus drei Hauptschritten. Die Things Network-Integration ist weiter in mehrere Teilschritte unterteilt. Der aufwendigste Schritt ist der Bau des Geräts. Um das Beispiel abzuschließen, benötigen Sie ein ThingSpeak-Konto und ein Konto bei The Things Network. Auf ThingSpeak erstellen Sie einen neuen Kanal. Im Things Network erstellen Sie eine Anwendung und registrieren ein Gerät. Anschließend erstellen Sie einen Payload-Decoder und fügen die Integration hinzu, die die Daten an ThingSpeak weiterleitet.
1) Einrichten eines ThingSpeak-Kanal zum Sammeln von Daten ein
2) Einrichten des Things Network
Anwendung erstellen
Gerät registrieren
Nutzlastformat erstellen
Integration hinzufügen
3) Gerät erstellen
Zum Erstellen des Sensorknotens verwendete Hardware
Schema und Anschlüsse
4) Gerät programmieren
Programmier-Setup
Code
Richten Sie einen ThingSpeak-Kanal zum Sammeln von Daten ein
1) Erstellen Sie einen ThingSpeak-Kanal, wie unter Daten in einem neuen Kanal sammeln gezeigt. Notieren Sie den Write-API-Schlüssel und die Kanal-ID für Ihren neuen Kanal.
2) Navigieren Sie zur Seite Kanaleinstellungen. Legen Sie die Feldbezeichnungen wie folgt fest.
Feld 1—
CounterFeld 2—
Soil MoistureFeld 3—
Temperature F
3) Klicken Sie unten auf Save Channel, um Ihre Einstellungen zu speichern.
Konfigurieren Sie die Anwendung „The Things Network“
Erstellen Sie ein Konto bei The Things Network, und melden Sie sich dann bei der The Things Network Console an.
Anwendung erstellen
1) Wählen Sie Anwendungen.

2) Wählen Sie Anwendung hinzufügen.

3) Erstellen Sie eine Anwendungs-ID und fügen Sie dann eine Beschreibung hinzu. Wählen Sie die Handler-Registrierung basierend auf Ihrem Standort aus.

Gerät registrieren
1) Klicken Sie auf die Registerkarte Devices und registrieren Sie ein Gerät. Weitere Informationen finden Sie unter Geräteregistrierung.
2) Erstellen Sie eine Geräte-ID. Geben Sie die Geräte-EUI ein, falls Ihr Gerät über eine verfügt. Wenn nicht, wählen Sie die Schaltfläche links neben dem Feld Device EUI, um die EUI automatisch zu generieren.

3) Klicken Sie auf Registrieren. Der Browser führt Sie zurück zur Registerkarte Overview.
4) Wählen Sie die Registerkarte Settings.

5) Wählen Sie in den Einstellungen ABP als Aktivierungsmethode aus. Um das Debuggen zu erleichtern, können Sie optional die Frame Counter Checks unten auf der Seite deaktivieren.

6) Notieren Sie die Device Address (Geräteadresse), den Network Session Key (Netzwerksitzungsschlüssel) und den App Session Key (App-Sitzungsschlüssel). Diese Informationen sind in Ihrem Gerätecode erforderlich.
Payload-Formatierer erstellen
Der Payload-Formatierer verwendet die vom Gateway an die Anwendung gesendeten Bytes, um eine Nachricht zusammenzustellen. In diesem Beispiel ist die gewünschte Nutzlastnachricht ein JSON-codiertes Objekt, das an ThingSpeak gesendet wird.
1) Kehren Sie über das Navigationsmenü oben zur Anwendungsansicht zurück. Klicken Sie dann auf die Registerkarte Nutzlastformate.

2) Erstellen Sie in der decoder-Schnittstelle den Code zum Konvertieren der von Ihrem Gerät gesendeten Bytes in ein JSON-Objekt, das in ThingSpeak geschrieben werden soll. Der bedingte Code für lat und lon behandelt die Möglichkeit positiver oder negativer Werte.
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
}
}
Integration hinzufügen
Um Daten an ThingSpeak weiterzuleiten, müssen Sie über eine Anwendung im Things Network mit einem registrierten Gerät und einem Payload-Formatierer verfügen. Erstellen Sie eine ThingSpeak-Integration, um die Daten weiterzuleiten.
1) Melden Sie sich bei Ihrer The Things Network-Konsole an.
2) Wählen Sie Applications und wählen Sie die Anwendung aus, von der Sie Daten an ThingSpeak weiterleiten möchten.

3) Klicken Sie auf die Registerkarte Integrationen.

4) Wählen Sie ThingSpeak.

5) Geben Sie Ihrer Integration im Feld „Process ID“ einen Namen.
6) Geben Sie im Feld „Authorization“ den Schreib-API-Schlüssel für den Kanal ein, in dem Sie Ihre Daten speichern möchten. Der API-Schlüssel ist auf der Registerkarte API Keys Ihres ThingSpeak-Kanals verfügbar.
7) Geben Sie im Feld „Kanal-ID“ die Kanal-ID für den ThingSpeak-Kanal ein, an den Sie Daten weiterleiten möchten. Die Kanal-ID ist auf der Seite Ihres ThingSpeak-Kanals verfügbar.

Gerät erstellen
Zum Erstellen des Sensorknotens verwendete Hardware
Sie können verschiedene LoRa-Geräte verwenden, die LoRaWan-Protokolle für die Verbindung mit The Things Network unterstützen. Dieses Beispiel demonstriert das Verfahren anhand des folgenden Hardware-Setups.
Adafruit Feather M0
Adafruit Ultimate GPS FeatherWing
Bodenmonitor (zum Beispiel der Sparkfun Feuchtigkeitssensor SEN 13322)
DHT22
Stiftleisten und Kabel
GPS-Antenne W14Q5A-y
Kippschalter
LiPo-Akku 500 mAh
Schema und Anschlüsse
Schließen Sie die Sensoren wie im Schema gezeigt an. Das Foto zeigt eine mögliche Konfiguration der Sensoren in einer Projektbox. In dieser Konfiguration gibt der Temperatursensor im Inneren der Box möglicherweise nicht genau die Außentemperatur wieder. Sie müssen dem LoRa-Radio eine Antenne hinzufügen.
1) Schließen Sie die Strom- und Erdungsanschlüsse für die GPS- und Temperatursensoren an. Schließen Sie den Feuchtigkeitssensor nicht an die Stromversorgung an.
2) Verbinden Sie den Ausgang des Bodenfeuchtesensors mit dem analogen Eingang bei A0.
3) Richten Sie das System so ein, dass die Stromversorgung des Feuchtigkeitssensors abgeschaltet wird, wenn er nicht verwendet wird. Der Stromanschluss des Feuchtigkeitssensors ist mit dem GPIO-Anschluss 11 auf der Feder M0 verbunden. Das Ausschalten der Stromversorgung bei Nichtgebrauch verlängert die Lebensdauer des Sensors.
4) Verbinden Sie den Sensordatenpin DH-22 mit PA-15 auf dem Feather M0, was in der Arduino®-Skizze Pin 5 ist.
5) Verbinden Sie für die GPS-Platine TX mit RX auf dem Feather M0 und RX mit TX.
6) Aktivieren Sie das LoRa-Radio, indem Sie PA20 (Pin 29, GPIO 6) am Feather M0 mit Masse verbinden.
7) Erstellen Sie einen Netzschalter, indem Sie einen Schalter vom En-Pin mit der Erde verbinden.


Gerät programmieren
Programmier-Setup
1) Laden Sie die neueste Arduino IDE herunter.
2) Laden Sie die Adafruit GPS-Bibliothek herunter oder fügen Sie die Adafruit_GPS-Bibliothek im Bibliotheksmanager hinzu. Wählen Sie Sketch > Include Library > Manage Libraries. Suchen Sie nach Adafruit_GPS, um es zu Ihren installierten Bibliotheken hinzuzufügen.
3) Laden Sie die LoraWan-in-C-Bibliothek für die Arduino-Umgebung herunter oder fügen Sie die Bibliotheken lmic und hal/hal im Bibliotheksmanager hinzu. Wählen Sie Sketch > Include Library > Manage Libraries. Suchen Sie nach lmic und wählen Sie MCCI LoRaWAN LMIC library aus, um es zu Ihren installierten Bibliotheken hinzuzufügen. Fügen Sie auch MCCI Arduino LoRaWan Library to the library manager hinzu.
4) Erstellen Sie die Anwendung. Öffnen Sie ein neues Fenster in der Arduino IDE und speichern Sie die Datei. Fügen Sie den im Abschnitt „Code“ bereitgestellten Code hinzu.
Code
1) Beginnen Sie mit dem Einbinden der entsprechenden Bibliotheken und dem Initialisieren der Variablen.
#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) Verwenden Sie die Funktion setup, um den Temperatursensor, das GPS und das LoRa-Radio zu starten.
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) Verwenden Sie die Funktion loop, um den LoRa-Prozess zu starten und die GPS-Daten zu analysieren.
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) Die Funktion GetSensorData schaltet den Feuchtigkeitssensor ein, liest seine Daten und schaltet dann den Strom wieder aus. Außerdem liest es den Temperatursensor und prüft, ob Informationen vom GPS-Gerät vorliegen. Wenn ein GPS-Fix vorliegt, aktualisiert diese Funktion die Positionsinformationen.
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) Verwenden Sie die Funktion onEvent, um Ereignisse vom LoRa-Radio zu verarbeiten. Die Funktion aktualisiert den seriellen Monitor, plant die nächste Übertragung und empfängt Nachrichten.
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) Die Funktion processJob wandelt Sensordaten in Bits um, die über das LoRa-Radio gesendet werden sollen.
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); // Convert 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.
Informationen zum Erstellen eines detaillierten Visualisierungs-Dashboards in Ihrem ThingSpeak-Kanal finden Sie unter Benutzerdefinierte ThingSpeak-Kanalansicht erstellen.