Heltec WiFi Kit 8 - LoRaWAN GPS Tracker

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • BosGnoom
  • Registratie: Februari 2002
  • Laatst online: 02-10 13:24
De laatste tijd ben ik aan het experimenteren met LoRaWan, via het TheThingsNetwork. Momenteel werk ik aan een GPS tracker. Daarbij maak ik gebruik van een Heltec Wifi kit 8 bordje, zie http://www.heltec.cn/project/wifi_kit_8/ .

Dit bordje is voorzien van een klein OLED. Deze wordt via I²C aangestuurd. De GPS module lees ik met SoftwareSerial (https://github.com/plerup/espsoftwareserial), omdat de Arduino versie niet geschikt is voor de esp8266. De hardware UART is bezet door de USB aansluiting. De GPS krijg ik helaas niet via deze poort aan het werk.

In de Arduino IDE (1.8.5) heb ik de ondersteuning voor dit bordje geladen via https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series. De ondersteuning voor LoRaWAN wordt verzorgd door de code van https://github.com/matthijskooijman/arduino-lmic .

Op zich krijg ik alles aan het werk: via de SoftSerial kan ik de GPS uitlezen. Met NeoGPS kan ik mijn positie vinden. Via de I²C kan ik deze vervolgens laten zien op het OLED.
De verbinding met het TheThingsNetwork werkt ook: de GPS positie kan ik versturen.

Gecombineerd werkt het ook: GPS+RFM95 = OK, GPS+OLED = OK, RFM95+GPS = OK, maar alles samen werkt niet. Op het moment dat ik de GPS module (SoftSerial), het OLED (I²C) en de RFM95 radio module (SPI) aansluit, dan valt het display uit.

De code werkt wel, als ik de TX van de GPS loshaal, dan kom ik in de "main" loop terecht met het display aan. Als ik dan pas de TX inprik, dan valt het display uit. Als ik alles verbonden laat, zie ik wel de "setup" lopen, maar op het moment dat ik in de "main" loop kom, dan valt het display uit.

Via een tweede module heb ik de GPS en de RFM95 gevoed (want misschien te weinig voeding), ook dit maakt niks uit.

Iemand een idee waar ik zou moeten zoeken? Volgens mij ligt het ergens in het gebied van de 3 (4) verschillende verbindingen die gebruikt worden, dat er teveel verschillende interrupts gebruikt worden om de I²C bus in de lucht te houden. Helaas heb ik geen logic analyser om dit te controleren...

Voor de liefhebbers de Arduino code: (geen idee hoe ik dit kan inklappen)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/*******************************************************************************
 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
 *
 * Permission is hereby granted, free of charge, to anyone
 * obtaining a copy of this document and accompanying files,
 * to do whatever they want with them without any restriction,
 * including, but not limited to, copying, modification and redistribution.
 * NO WARRANTY OF ANY KIND IS PROVIDED.
 *
  *******************************************************************************/

// Includes for LoRaWAN
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>

// Includes for OLED
#include <Wire.h>
#include "OLED.h"

// Includes for GPS
#include <SoftwareSerial.h>
#include <NMEAGPS.h>
#include <Streamers.h>


// LoRaWAN KEYS 
// LoRaWAN NwkSKey, network session key
static const PROGMEM u1_t NWKSKEY[16] = { xxx };
// LoRaWAN AppSKey, application session key
static const u1_t PROGMEM APPSKEY[16] = { xxx };
// LoRaWAN end-device address (DevAddr)
static const u4_t DEVADDR = 0x xxx; 


// Pin mapping
const lmic_pinmap lmic_pins = {
    .nss = 0,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = LMIC_UNUSED_PIN,
    .dio = {15, 15, LMIC_UNUSED_PIN},
};


// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 10;


// Set up OLED
#define RST_OLED 16
OLED display(4, 5);


// Set up GPS: Serial port and variables
#define GPS_BAUD_RATE 9600
SoftwareSerial gpsPort(2, SW_SERIAL_UNUSED_PIN, false, 256);
static NMEAGPS  gps;
static gps_fix  fix;


// Config ends here

// SETUP 
void setup() {
    Serial.begin(9600);
    Serial.println(F("Starting"));

    // Reset OLED
    digitalWrite(RST_OLED, LOW);   // turn D2 low to reset OLED
    delay(50);
    digitalWrite(RST_OLED, HIGH);    // while OLED is running, must set D2 in high

    // Initialize display
    display.begin();
    display.print("Starting...", 0);
    delay(1500);

    // LMIC init
    display.print("LMIC init...", 1);
    Serial.println("LMIC init...");
    os_init();
    delay(1500);
    // Reset the MAC state. Session and pending data transfers will be discarded.
    display.print("LMIC reset...", 2);
    Serial.println("LMIC reset...");
    LMIC_reset();
    delay(1500);
    
    // Set static session parameters. Instead of dynamically establishing a session
    // by joining the network, precomputed session parameters are be provided.
    display.print("Setup keys...", 3);
    Serial.println("Setup network keys...");
    #ifdef PROGMEM
    // On AVR, these values are stored in flash and only copied to RAM
    // once. Copy them to a temporary buffer here, LMIC_setSession will
    // copy them into a buffer of its own again.
    uint8_t appskey[sizeof(APPSKEY)];
    uint8_t nwkskey[sizeof(NWKSKEY)];
    memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
    memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
    LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);
    #else
    // If not running an AVR with PROGMEM, just use the arrays directly
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);
    #endif
    delay(1500);

    display.print(" Setup channels", 1);
    Serial.println("Set up channels");
    #if defined(CFG_eu868)
    // Set up the channels used by the Things Network, which corresponds
    // to the defaults of most gateways. Without this, only three base
    // channels from the LoRaWAN specification are used, which certainly
    // works, so it is good for debugging, but can overload those
    // frequencies, so be sure to configure the full frequency range of
    // your network here (unless your network autoconfigures them).
    // Setting up channels should happen after LMIC_setSession, as that
    // configures the minimal channel set.
    LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
    LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7),  BAND_CENTI);      // g-band
    LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK,  DR_FSK),  BAND_MILLI);      // g2-band
    // TTN defines an additional channel at 869.525Mhz using SF9 for class B
    // devices' ping slots. LMIC does not have an easy way to define set this
    // frequency and support for class B is spotty and untested, so this
    // frequency is not configured here.
    #endif

    // 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);
    delay(1500);
    
    // Start job
    display.print(" Start radio...", 2);
    Serial.println("Start radio...");
    delay(1500);
    do_send(&sendjob);

    // Start GPS port
    display.print(" Starting GPS...", 3);
    Serial.println("Starting GPS...");
    gpsPort.begin( GPS_BAUD_RATE );
    delay(2000);

    display.clear();

    display.print("Going into main", 0);
    Serial.println("Going into main loop");
}


// MAIN LOOP
void loop() {
    os_runloop_once();
    while (gps.available( gpsPort )) {
        fix = gps.read();
        doSomeWork();
    }
}


// LMIC EVENTS
void onEvent (ev_t ev) {
    String tx_info;
    Serial.print(os_getTime());
    Serial.print(": ");
    switch(ev) {
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            // tx_info = "TX:" + String(os_getTime());
            // display.print( (char*) tx_info.c_str(), 0);
            
            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"));
            }
            break;

        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_RFU1:
            Serial.println(F("EV_RFU1"));
            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_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;
         default:
            Serial.println(F("Unknown event"));
            break;
    }
}


// LMIC PREPARE TO SEND
void do_send(osjob_t* j){
    uint8_t mydata[10];
    
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        // Prepare upstream data transmission at the next possible time.

        if (fix.valid.location) {
            int32_t lat = fix.latitude() * 10000;
            int32_t lon = fix.longitude() * 10000;
            mydata[0] = lat;
            mydata[1] = lat >> 8;
            mydata[2] = lat >> 16;

            mydata[3] = lon;
            mydata[4] = lon >> 8;
            mydata[5] = lon >> 16;

            mydata[6] = fix.satellites;

            mydata[7] = fix.hdop;
            mydata[8] = fix.hdop >> 8;

            int32_t alt = fix.altitude();
            mydata[9] = alt;
            mydata[10] = alt >> 8;

            LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
            Serial.println(F("Packet queued"));
        } else {
            Serial.println(F("No fix, no data to send..."));
        }
    }
    // Next TX is scheduled after TX_COMPLETE event.
    // Schedule next transmission
    os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            
}


// UPDATE GPS to OLED
static void doSomeWork()
{
  // Print all the things to the Serial port
  trace_all( Serial, gps, fix );

  // Print selected information to OLED
//    if (fix.valid.location) {
      //display.on();
      String lat = "Lat:" + String(fix.latitude(), 6);
      String lon = "Lon:" + String(fix.longitude(), 6);
      String hoogte = "Alt:" + String(fix.altitude(), 3);
      String hdop = "Sats:" + String(fix.satellites) + " ";
      
      display.print( (char*) lat.c_str(), 0 );
      display.print( (char*) lon.c_str(), 1 );
      display.print( (char*) hoogte.c_str(), 2 );
      display.print( (char*) hdop.c_str(), 3);
      
    //} else {
      //display.print("No GPS fix...", 0);
  //  }

} // doSomeWork