From edd9a26a3520e91b273caaedcee7ea6fe1fcc571 Mon Sep 17 00:00:00 2001 From: Kent Wiliams Date: Tue, 19 May 2020 10:27:53 -0700 Subject: [PATCH] updated for gnss --- .../longfi-us915-cayenne-gnss/README.md | 40 ++ .../longfi-us915-cayenne-gnss.ino | 507 ++++++++++++++++++ 2 files changed, 547 insertions(+) create mode 100644 ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/README.md create mode 100644 ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/longfi-us915-cayenne-gnss.ino diff --git a/ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/README.md b/ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/README.md new file mode 100644 index 0000000..208f4fc --- /dev/null +++ b/ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/README.md @@ -0,0 +1,40 @@ +# ST B-L072Z-LRWAN1 - GNSS Example + +This example demonstrates sending GNSS data in [CayenneLPP](https://developers.mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload) format, using a B-L072Z-LRWAN1 development board with a X-NUCLEO-GNSS1A1 expansion shield, to the myDevices Cayenne dashboard. For more information on adding your device to the Helium network, visit our quickstart guide [here](https://developer.helium.com/console/quickstart). For more information on adding your device to myDevices Cayenne, visit our guide [here](https://developer.helium.com/console/integrations/mydevices-cayenne-integration). + +## Required Arduino Libraries + +From the Arduino IDE, open the Library Manager (Sketch->Include Library->Manage Libraries). In the search box, type the library name below and install the latest version. + +[MCCI Arduino LoRaWAN Library](https://github.com/mcci-catena/arduino-lmic) +[CayenneLPP](https://github.com/ElectronicCats/CayenneLPP) +[X-NUCLEO-GNSS1A1](https://github.com/stm32duino/X-NUCLEO-GNSS1A1) + +## Required Arduino Board Support + +### B-L072Z-LRWAN1 - ST STM32L0 Discovery kit +Install board support package, find instructions [here](https://github.com/stm32duino/Arduino_Core_STM32#getting-started). + +Arduino IDE: +1. Select Tools -> Board: -> Discovery +2. Select Tools -> Board part number: -> Discovery L072Z-LRWAN1 + +## Required Hardware + +### B-L072Z-LRWAN1 - ST STM32L0 Discovery kit + +[B-L072Z-LRWAN1 Product Page](https://www.st.com/en/evaluation-tools/b-l072z-lrwan1.html) +[B-L072Z-LRWAN1 User Manual](https://www.st.com/content/ccc/resource/technical/document/user_manual/group0/ac/62/15/c7/60/ac/4e/9c/DM00329995/files/DM00329995.pdf/jcr:content/translations/en.DM00329995.pdf) + +### X-NUCLEO-IKS01A3 - ST Motion MEMS and Environmental Sensor Board + +[X-NUCLEO-IKS01A3 Product Page](https://www.st.com/en/ecosystems/x-nucleo-iks01a3.html) +[X-NUCLEO-IKS01A3 User Manual](https://www.st.com/resource/en/user_manual/dm00601501-getting-started-with-the-xnucleoiks01a3-motion-mems-and-environmental-sensor-expansion-board-for-stm32-nucleo-stmicroelectronics.pdf) +## Programming (Uploading Method): + +#### STM32CubeProgrammer(SWD) +Will use onboard ST-Link(Flasher/Debugger) to upload sketch. +Download and Install required utility from ST [here](https://www.st.com/en/development-tools/stm32cubeprog.html). + +Arduino IDE: +Select Tools -> Upload Method -> STM32CubeProgrammer(SWD) \ No newline at end of file diff --git a/ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/longfi-us915-cayenne-gnss.ino b/ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/longfi-us915-cayenne-gnss.ino new file mode 100644 index 0000000..2947088 --- /dev/null +++ b/ST-B-L072Z-LRWAN1/longfi-us915-cayenne-gnss/longfi-us915-cayenne-gnss.ino @@ -0,0 +1,507 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * Copyright (c) 2018 Terry Moore, MCCI + * + * 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. + * + * This example sends a valid LoRaWAN packet with payload "Hello, + * world!", using frequency and encryption settings matching those of + * the The Things Network. It's pre-configured for the Adafruit + * Feather M0 LoRa. + * + *******************************************************************************/ + +/******************************************************************************* + * + * For Helium developers, follow the Arduino Quickstart guide: + * https://developer.helium.com/device/arduino-quickstart + * TLDR: register your device on the Serial: + * https://Serial.helium.com/devices + * + * The App EUI (as lsb) and App Key (as msb) get inserted below. + * + *******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +//I2C communication parameters +#define DEFAULT_DEVICE_ADDRESS 0x3A +#define DEFAULT_DEVICE_PORT 0xFF +#define I2C_DELAY 1 + +#define RESET_PIN 7 + +#define DEV_I2C Wire + +// This is the "App EUI" in Helium. Make sure it is little-endian (lsb). +static const u1_t PROGMEM APPEUI[8] = {0}; +void os_getArtEui(u1_t *buf) { memcpy_P(buf, APPEUI, 8); } + +// This should also be in little endian format +// These are user configurable values and Helium Serial permits anything +static const u1_t PROGMEM DEVEUI[8] = {0}; +void os_getDevEui(u1_t *buf) { memcpy_P(buf, DEVEUI, 8); } + +// This is the "App Key" in Helium. It is big-endian (msb). +static const u1_t PROGMEM APPKEY[16] = {0}; +void os_getDevKey(u1_t *buf) { memcpy_P(buf, APPKEY, 16); } + +CayenneLPP lpp(51); +TwoWire& gps = DEV_I2C; +//I2C read data structures +char buff[32]; +int idx = 0; + +char nmeaBuffer[100]; +MicroNMEA nmea(nmeaBuffer, sizeof(nmeaBuffer)); + +bool ledState = LOW; +volatile bool ppsTriggered = false; + +void ppsHandler(void); +static osjob_t sendjob; +void do_send(osjob_t *j); + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +// +// Adafruit BSPs are not consistent -- m0 express defs ARDUINO_SAMD_FEATHER_M0, +// m0 defs ADAFRUIT_FEATHER_M0 +// +#if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) +// Pin mapping for Adafruit Feather M0 LoRa, etc. +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, +}; +#elif defined(ARDUINO_AVR_FEATHER32U4) +// Pin mapping for Adafruit Feather 32u4 LoRa, etc. +// Just like Feather M0 LoRa, but uses SPI at 1MHz; and that's only +// because MCCI doesn't have a test board; probably higher frequencies +// will work. +const lmic_pinmap lmic_pins = { + .nss = 8, + .rxtx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {7, 6, LMIC_UNUSED_PIN}, + .rxtx_rx_active = 0, + .rssi_cal = 8, // LBT cal for the Adafruit Feather 32U4 LoRa, in dB + .spi_freq = 1000000, +}; +#elif defined(ARDUINO_CATENA_4551) +// Pin mapping for Murata module / Catena 4551 +const lmic_pinmap lmic_pins = { + .nss = 7, + .rxtx = 29, + .rst = 8, + .dio = + { + 25, // DIO0 (IRQ) is D25 + 26, // DIO1 is D26 + 27, // DIO2 is D27 + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000 // 8MHz +}; +#elif defined(MCCI_CATENA_4610) +#include "arduino_lmic_hal_boards.h" +const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Catena4610(); +#elif defined(ARDUINO_DISCO_L072CZ_LRWAN1) +const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Disco_L072cz_Lrwan1(); +#else +#error "Unknown target" +#endif + +void ppsHandler(void) +{ + ppsTriggered = true; +} + +void gpsHardwareReset() +{ + //reset the device + digitalWrite(RESET_PIN, LOW); + delay(50); + digitalWrite(RESET_PIN, HIGH); + + //wait for reset to apply + delay(2000); +} + +//Read 32 bytes from I2C +void readI2C(char *inBuff) +{ + gps.beginTransmission(DEFAULT_DEVICE_ADDRESS); + gps.write((uint8_t) DEFAULT_DEVICE_PORT); + gps.endTransmission(false); + gps.requestFrom((uint8_t)DEFAULT_DEVICE_ADDRESS, (uint8_t) 32); + int i = 0; + while (gps.available()) + { + inBuff[i]= gps.read(); + i++; + } +} + +//Send a NMEA command via I2C +void sendCommand(char *cmd) +{ + gps.beginTransmission(DEFAULT_DEVICE_ADDRESS); + gps.write((uint8_t) DEFAULT_DEVICE_PORT); + MicroNMEA::sendSentence(gps, cmd); + gps.endTransmission(true); +} + +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_JOIN_TXCOMPLETE: + Serial.println(F("EV_JOIN_TXCOMPLETE")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + { + u4_t netid = 0; + devaddr_t devaddr = 0; + u1_t nwkKey[16]; + u1_t artKey[16]; + LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey); + Serial.print("netid: "); + Serial.println(netid, DEC); + Serial.print("devaddr: "); + Serial.println(devaddr, HEX); + Serial.print("artKey: "); + for (size_t i = 0; i < sizeof(artKey); ++i) { + if (i != 0) + Serial.print("-"); + Serial.print(artKey[i], HEX); + } + Serial.println(""); + Serial.print("nwkKey: "); + for (size_t i = 0; i < sizeof(nwkKey); ++i) { + if (i != 0) + Serial.print("-"); + Serial.print(nwkKey[i], HEX); + } + Serial.println(""); + } + // Disable link check validation (automatically enabled + // during join, but because slow data rates change max TX + // size, we don't use it in this example. + LMIC_setLinkCheckMode(0); + break; + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || 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; + 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; + /* + || This event is defined but not used in the code. No + || point in wasting codespace on it. + || + || case EV_SCAN_FOUND: + || Serial.println(F("EV_SCAN_FOUND")); + || break; + */ + case EV_TXSTART: + Serial.println(F("EV_TXSTART")); + break; + default: + Serial.print(F("Unknown event: ")); + Serial.println((unsigned)ev); + break; + } +} + +void readGPS() { + //If a message is recieved print all the informations + if (ppsTriggered) + { + ppsTriggered = false; + ledState = !ledState; + digitalWrite(LED_BUILTIN, ledState); + + // Output GPS information from previous second + Serial.print("Valid fix: "); + Serial.println(nmea.isValid() ? "yes" : "no"); + + Serial.print("Nav. system: "); + if (nmea.getNavSystem()) + Serial.println(nmea.getNavSystem()); + else + Serial.println("none"); + + Serial.print("Num. satellites: "); + Serial.println(nmea.getNumSatellites()); + + Serial.print("HDOP: "); + Serial.println(nmea.getHDOP()/10., 1); + + Serial.print("Date/time: "); + Serial.print(nmea.getYear()); + Serial.print('-'); + Serial.print(int(nmea.getMonth())); + Serial.print('-'); + Serial.print(int(nmea.getDay())); + Serial.print('T'); + Serial.print(int(nmea.getHour())); + Serial.print(':'); + Serial.print(int(nmea.getMinute())); + Serial.print(':'); + Serial.println(int(nmea.getSecond())); + + long latitude_mdeg = nmea.getLatitude(); + long longitude_mdeg = nmea.getLongitude(); + Serial.print("Latitude (deg): "); + Serial.println(latitude_mdeg / 1000000., 6); + + Serial.print("Longitude (deg): "); + Serial.println(longitude_mdeg / 1000000., 6); + + long alt; + Serial.print("Altitude (m): "); + if (nmea.getAltitude(alt)) + Serial.println(alt / 1000., 3); + else + Serial.println("not available"); + + Serial.print("Speed: "); + Serial.println(nmea.getSpeed() / 1000., 3); + Serial.print("Course: "); + Serial.println(nmea.getCourse() / 1000., 3); + Serial.println("-----------------------"); + nmea.clear(); + } + else + { + char c ; + if (idx == 0) + { + readI2C(buff); + delay(I2C_DELAY); + } + //Fetch the character one by one + c = buff[idx]; + idx++; + idx %= 32; + //If we have a valid character pass it to the library + if ((uint8_t) c != 0xFF) + { + Serial.print(c); + nmea.process(c); + } + } +} + +void do_send(osjob_t *j) { + // 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. + LMIC_setTxData2(1, lpp.getBuffer(), lpp.getSize(), 0); + Serial.println(F("Packet queued")); + } + // Next TX is scheduled after TX_COMPLETE event. +} + +void setup() { + delay(2000); + while (!Serial) + ; + Serial.begin(9600); + Serial.println(F("Starting")); + + // Start GPS Setup + gps.begin(); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, ledState); + + //Start the module + pinMode(RESET_PIN, OUTPUT); + digitalWrite(RESET_PIN, HIGH); + Serial.println("Resetting GPS module ..."); + gpsHardwareReset(); + Serial.println("... done"); + + // Change the echoing messages to the ones recognized by the MicroNMEA library + sendCommand((char *)"$PSTMSETPAR,1231,0x00000042"); + sendCommand((char *)"$PSTMSAVEPAR"); + + //Reset the device so that the changes could take plaace + sendCommand((char *)"$PSTMSRR"); + + delay(4000); + + //Reinitialize I2C after the reset + gps.begin(); + + //clear i2c buffer + char c; + idx = 0; + memset(buff, 0, 32); + do + { + if (idx == 0) + { + readI2C(buff); + delay(I2C_DELAY); + } + c = buff[idx]; + idx++; + idx %= 32; + } + while ((uint8_t) c != 0xFF); + + pinMode(2, INPUT); + attachInterrupt(digitalPinToInterrupt(2), ppsHandler, RISING); + // End GPS Setup + + SPI.setMOSI(RADIO_MOSI_PORT); + SPI.setMISO(RADIO_MISO_PORT); + SPI.setSCLK(RADIO_SCLK_PORT); + SPI.setSSEL(RADIO_NSS_PORT); + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // allow much more clock error than the X/1000 default. See: + // https://github.com/mcci-catena/arduino-lorawan/issues/74#issuecomment-462171974 + // https://github.com/mcci-catena/arduino-lmic/commit/42da75b56#diff-16d75524a9920f5d043fe731a27cf85aL633 + // the X/1000 means an error rate of 0.1%; the above issue discusses using + // values up to 10%. so, values from 10 (10% error, the most lax) to 1000 + // (0.1% error, the most strict) can be used. + LMIC_setClockError(1 * MAX_CLOCK_ERROR / 40); + + LMIC_setLinkCheckMode(0); + LMIC_setDrTxpow(DR_SF8, 20); + // Sub-band 2 - Helium Network + LMIC_selectSubBand(1); // zero indexed + + // Start job (sending automatically starts OTAA too) + do_send(&sendjob); +} + +void loop() { + os_runloop_once(); + readGPS(); +} + +namespace Arduino_LMIC { + +class HalConfiguration_Disco_L072cz_Lrwan1_t : public HalConfiguration_t { +public: + enum DIGITAL_PINS : uint8_t { + PIN_SX1276_NSS = 37, + PIN_SX1276_NRESET = 33, + PIN_SX1276_DIO0 = 38, + PIN_SX1276_DIO1 = 39, + PIN_SX1276_DIO2 = 40, + PIN_SX1276_RXTX = 21, + }; + + virtual bool queryUsingTcxo(void) override { return false; }; +}; +// save some typing by bringing the pin numbers into scope +static HalConfiguration_Disco_L072cz_Lrwan1_t myConfig; + +static const HalPinmap_t myPinmap = { + .nss = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NSS, + .rxtx = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_RXTX, + .rst = HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_NRESET, + + .dio = + { + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO0, + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO1, + HalConfiguration_Disco_L072cz_Lrwan1_t::PIN_SX1276_DIO2, + }, + .rxtx_rx_active = 1, + .rssi_cal = 10, + .spi_freq = 8000000, /* 8MHz */ + .pConfig = &myConfig}; + +}; // end namespace Arduino_LMIC