Merge branch 'master' of git.starbeamrainbowlabs.com:sbrl/Msc-Summer-Project
This commit is contained in:
commit
1fbd864af8
28 changed files with 1206 additions and 130 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
config.custom.h
|
||||||
|
settings.custom.cpp
|
||||||
|
|
||||||
Reports/*.pdf
|
Reports/*.pdf
|
||||||
|
|
||||||
# Created by https://www.gitignore.io/api/libreoffice
|
# Created by https://www.gitignore.io/api/libreoffice
|
||||||
|
|
15
.gitmodules
vendored
15
.gitmodules
vendored
|
@ -4,9 +4,18 @@
|
||||||
[submodule "iot/libraries/Entropy"]
|
[submodule "iot/libraries/Entropy"]
|
||||||
path = iot/libraries/Entropy
|
path = iot/libraries/Entropy
|
||||||
url = https://github.com/taoyuan/Entropy.git
|
url = https://github.com/taoyuan/Entropy.git
|
||||||
[submodule "iot/libraries/arduino-lmic"]
|
|
||||||
path = iot/libraries/arduino-lmic
|
|
||||||
url = https://github.com/mcci-catena/arduino-lmic.git
|
|
||||||
[submodule "iot/libraries/TinyGPSPlus"]
|
[submodule "iot/libraries/TinyGPSPlus"]
|
||||||
path = iot/libraries/TinyGPSPlus
|
path = iot/libraries/TinyGPSPlus
|
||||||
url = https://github.com/mikalhart/TinyGPSPlus.git
|
url = https://github.com/mikalhart/TinyGPSPlus.git
|
||||||
|
[submodule "iot/libraries/arduino-lmic"]
|
||||||
|
path = iot/libraries/arduino-lmic
|
||||||
|
url = https://github.com/matthijskooijman/arduino-lmic.git
|
||||||
|
[submodule "iot/libraries/Arduino-MemoryFree"]
|
||||||
|
path = iot/libraries/Arduino-MemoryFree
|
||||||
|
url = https://github.com/mpflaga/Arduino-MemoryFree.git
|
||||||
|
[submodule "iot/libraries/SdFat"]
|
||||||
|
path = iot/libraries/SdFat
|
||||||
|
url = https://github.com/greiman/SdFat.git
|
||||||
|
[submodule "iot/libraries/TinyGPS"]
|
||||||
|
path = iot/libraries/TinyGPS
|
||||||
|
url = https://github.com/mikalhart/TinyGPS.git
|
||||||
|
|
|
@ -15,6 +15,13 @@
|
||||||
- [Project box](https://smile.amazon.co.uk/waterproof-plastic-Enclosure-Power-junction-Grey/dp/B01JHBHNMA/) - if we can't 3D print one
|
- [Project box](https://smile.amazon.co.uk/waterproof-plastic-Enclosure-Power-junction-Grey/dp/B01JHBHNMA/) - if we can't 3D print one
|
||||||
- [TPL-5111](https://www.adafruit.com/product/3573) - also available from Pimoroni
|
- [TPL-5111](https://www.adafruit.com/product/3573) - also available from Pimoroni
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
Run `bash ./build setup` from the command line at the root of this repository.
|
||||||
|
|
||||||
|
### IoT Device
|
||||||
|
- Copy `settings.custom.cpp.example` to `settings.custom.cpp` and fill in the fields there
|
||||||
|
- Review `settings.h` to make sure it matches your setup
|
||||||
|
- Copy the folders in `iot/libraries` to your Arduino IDE libraries folder
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
- Entropy extraction from the watchdog timer vs the internal clock: https://github.com/taoyuan/Entropy
|
- Entropy extraction from the watchdog timer vs the internal clock: https://github.com/taoyuan/Entropy
|
||||||
|
|
52
build
52
build
|
@ -43,18 +43,60 @@ fi
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
task_setup() {
|
# Toggles commenting and uncommenting lines in a file that contain a specific
|
||||||
task_begin "Setting up";
|
# substring. Checks for word boundaries either side of the substring.
|
||||||
|
# From https://stackoverflow.com/a/24901636/1460422
|
||||||
|
# $1 - Filename
|
||||||
|
# $2 - Search string
|
||||||
|
comment_toggle() {
|
||||||
|
filename="${1}";
|
||||||
|
search_string="${2}";
|
||||||
|
|
||||||
|
awk -v commentId='//' -v word="${search_string}" '
|
||||||
|
$0 ~ "(^|[[:punct:][:space:]])" word "($|[[:punct:][:space:]])" {
|
||||||
|
if (match($0, "^[[:space:]]*" commentId))
|
||||||
|
$0 = substr($0, RSTART + RLENGTH)
|
||||||
|
else
|
||||||
|
$0 = commentId $0
|
||||||
|
}
|
||||||
|
{ print }' "${filename}" > tmpfile.$$ && mv tmpfile.$$ "${filename}"
|
||||||
|
}
|
||||||
|
|
||||||
|
task_setup() {
|
||||||
|
stage_begin "Setting up";
|
||||||
|
|
||||||
|
task_begin "Checking Environment";
|
||||||
check_command git true;
|
check_command git true;
|
||||||
|
check_command awk true;
|
||||||
check_command pdflatex true;
|
check_command pdflatex true;
|
||||||
check_command bibtex true;
|
check_command bibtex true;
|
||||||
|
task_end $?;
|
||||||
|
|
||||||
subtask_begin "Initialising submodules";
|
task_begin "Initialising submodules";
|
||||||
git submodule update --init;
|
git submodule update --init;
|
||||||
subtask_end $?;
|
task_end $?;
|
||||||
|
|
||||||
task_end 0;
|
task_begin "Preconfiguring libraries";
|
||||||
|
config_file_directory="./iot/libraries/arduino-lmic/src/lmic/";
|
||||||
|
config_file_name="config.h";
|
||||||
|
cd "${config_file_directory}";
|
||||||
|
git reset --hard;
|
||||||
|
# Disable OTAA
|
||||||
|
comment_toggle "${config_file_name}" "#define DISABLE_JOIN";
|
||||||
|
# Disable class b stuff
|
||||||
|
comment_toggle "${config_file_name}" "#define DISABLE_PING";
|
||||||
|
comment_toggle "${config_file_name}" "#define DISABLE_BEACONS";
|
||||||
|
# Disable other misc. stuff we're not likely to use
|
||||||
|
comment_toggle "${config_file_name}" "#define DISABLE_MCMD_DCAP_REQ"; # Duty cycle cap - won't work anyway 'cause we're shutting down in between
|
||||||
|
comment_toggle "${config_file_name}" "#define DISABLE_MCMD_DN2P_SET"; # Receiving stuff
|
||||||
|
|
||||||
|
# echo "#define DISABLE_JOIN" >>"${config_file_name}";
|
||||||
|
# echo "#define DISABLE_PING" >>"${config_file_name}";
|
||||||
|
# echo "#define DISABLE_BEACONS" >>"${config_file_name}";
|
||||||
|
cd -;
|
||||||
|
task_end $?;
|
||||||
|
|
||||||
|
stage_end 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
task_render() {
|
task_render() {
|
||||||
|
|
195
iot/TTNTest/TTNTest.ino
Normal file
195
iot/TTNTest/TTNTest.ino
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This example sends a valid LoRaWAN packet with payload "Hello,
|
||||||
|
* world!", using frequency and encryption settings matching those of
|
||||||
|
* the (early prototype version of) The Things Network.
|
||||||
|
*
|
||||||
|
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1,
|
||||||
|
* 0.1% in g2).
|
||||||
|
*
|
||||||
|
* Change DEVADDR to a unique address!
|
||||||
|
* See http://thethingsnetwork.org/wiki/AddressSpace
|
||||||
|
*
|
||||||
|
* Do not forget to define the radio type correctly in config.h.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#include "config.custom.h"
|
||||||
|
|
||||||
|
// 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 uint8_t mydata[] = "Hello, world!";
|
||||||
|
static osjob_t sendjob;
|
||||||
|
|
||||||
|
// Schedule TX every this many seconds (might become longer due to duty
|
||||||
|
// cycle limitations).
|
||||||
|
const unsigned TX_INTERVAL = 60;
|
||||||
|
|
||||||
|
// Pin mapping
|
||||||
|
const lmic_pinmap lmic_pins = {
|
||||||
|
.nss = 10,
|
||||||
|
.rxtx = LMIC_UNUSED_PIN,
|
||||||
|
.rst = 9,
|
||||||
|
.dio = {2, 6, 7},
|
||||||
|
};
|
||||||
|
|
||||||
|
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_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.dataLen) {
|
||||||
|
// data received in rx slot after tx
|
||||||
|
Serial.print(F("Data Received: "));
|
||||||
|
Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
default:
|
||||||
|
Serial.println(F("Unknown event"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, mydata, sizeof(mydata)-1, 0);
|
||||||
|
Serial.println(F("Packet queued"));
|
||||||
|
}
|
||||||
|
// Next TX is scheduled after TX_COMPLETE event.
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("Starting"));
|
||||||
|
|
||||||
|
#ifdef VCC_ENABLE
|
||||||
|
// For Pinoccio Scout boards
|
||||||
|
pinMode(VCC_ENABLE, OUTPUT);
|
||||||
|
digitalWrite(VCC_ENABLE, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// LMIC init
|
||||||
|
os_init();
|
||||||
|
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||||
|
LMIC_reset();
|
||||||
|
|
||||||
|
// Set static session parameters. Instead of dynamically establishing a session
|
||||||
|
// by joining the network, precomputed session parameters are be provided.
|
||||||
|
#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
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Disable link check validation
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
|
||||||
|
// Set data rate and transmit power (note: txpow seems to be ignored by the library)
|
||||||
|
LMIC_setDrTxpow(DR_SF7,14);
|
||||||
|
|
||||||
|
// Start job
|
||||||
|
do_send(&sendjob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
os_runloop_once();
|
||||||
|
}
|
15
iot/TTNTest/config.custom.h.example
Normal file
15
iot/TTNTest/config.custom.h.example
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copy this file to have the name "config.custom.h", and fill in the 3 sections below
|
||||||
|
|
||||||
|
// LoRaWAN NwkSKey, network session key
|
||||||
|
// Big endian (i.e. msb)
|
||||||
|
static const PROGMEM u1_t NWKSKEY[16] = { .... };
|
||||||
|
|
||||||
|
// LoRaWAN AppSKey, application session key
|
||||||
|
// Big endian (i.e. msb)
|
||||||
|
static const u1_t PROGMEM APPSKEY[16] = { .... };
|
||||||
|
|
||||||
|
// LoRaWAN end-device address (DevAddr)
|
||||||
|
// See http://thethingsnetwork.org/wiki/AddressSpace
|
||||||
|
// The library converts the address to network byte order as needed.
|
||||||
|
// Big endian (i.e. msb)
|
||||||
|
static const u4_t DEVADDR = 0x....; // <-- Change this address for every node!
|
256
iot/TTNTestABPNew/TTNTestABPNew.ino
Normal file
256
iot/TTNTestABPNew/TTNTestABPNew.ino
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This uses ABP (Activation-by-personalisation), where a DevAddr and
|
||||||
|
* Session keys are preconfigured (unlike OTAA, where a DevEUI and
|
||||||
|
* application key is configured, while the DevAddr and session keys are
|
||||||
|
* assigned/generated in the over-the-air-activation procedure).
|
||||||
|
*
|
||||||
|
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
|
||||||
|
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
|
||||||
|
* violated by this sketch when left running for longer)!
|
||||||
|
*
|
||||||
|
* To use this sketch, first register your application and device with
|
||||||
|
* the things network, to set or generate a DevAddr, NwkSKey and
|
||||||
|
* AppSKey. Each device should have their own unique values for these
|
||||||
|
* fields.
|
||||||
|
*
|
||||||
|
* Do not forget to define the radio type correctly in
|
||||||
|
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
// References:
|
||||||
|
// [feather] adafruit-feather-m0-radio-with-lora-module.pdf
|
||||||
|
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#include "config.custom.h"
|
||||||
|
|
||||||
|
// 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 arduino-lmic/project_config/lmic_project_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 uint8_t mydata[] = "Hello, world!";
|
||||||
|
static osjob_t sendjob;
|
||||||
|
|
||||||
|
// Schedule TX every this many seconds (might become longer due to duty
|
||||||
|
// cycle limitations).
|
||||||
|
const unsigned TX_INTERVAL = 60;
|
||||||
|
|
||||||
|
// Pin mapping
|
||||||
|
const lmic_pinmap lmic_pins = {
|
||||||
|
.nss = 10,
|
||||||
|
.rxtx = LMIC_UNUSED_PIN,
|
||||||
|
.rst = 9,
|
||||||
|
.dio = {2, 6, 7},
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
/*
|
||||||
|
|| 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;
|
||||||
|
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 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, mydata, sizeof(mydata)-1, 0);
|
||||||
|
Serial.println(F("Packet queued"));
|
||||||
|
}
|
||||||
|
// Next TX is scheduled after TX_COMPLETE event.
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// pinMode(13, OUTPUT);
|
||||||
|
while (!Serial); // wait for Serial to be initialized
|
||||||
|
Serial.begin(115200);
|
||||||
|
delay(100); // per sample code on RF_95 test
|
||||||
|
Serial.println(F("Starting"));
|
||||||
|
|
||||||
|
// Activate the right SPI device
|
||||||
|
pinMode(10, OUTPUT);
|
||||||
|
pinMode(3, OUTPUT);
|
||||||
|
digitalWrite(10, LOW);
|
||||||
|
digitalWrite(3, HIGH);
|
||||||
|
|
||||||
|
#ifdef VCC_ENABLE
|
||||||
|
// For Pinoccio Scout boards
|
||||||
|
pinMode(VCC_ENABLE, OUTPUT);
|
||||||
|
digitalWrite(VCC_ENABLE, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// LMIC init
|
||||||
|
os_init();
|
||||||
|
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||||
|
LMIC_reset();
|
||||||
|
|
||||||
|
// Set static session parameters. Instead of dynamically establishing a session
|
||||||
|
// by joining the network, precomputed session parameters are be provided.
|
||||||
|
#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 (0x13, DEVADDR, nwkskey, appskey);
|
||||||
|
#else
|
||||||
|
// If not running an AVR with PROGMEM, just use the arrays directly
|
||||||
|
LMIC_setSession (0x13, DEVADDR, NWKSKEY, APPSKEY);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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.
|
||||||
|
#elif defined(CFG_us915)
|
||||||
|
// NA-US channels 0-71 are configured automatically
|
||||||
|
// but only one group of 8 should (a subband) should be active
|
||||||
|
// TTN recommends the second sub band, 1 in a zero based count.
|
||||||
|
// https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json
|
||||||
|
LMIC_selectSubBand(1);
|
||||||
|
#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
|
||||||
|
LMIC_setDrTxpow(DR_SF7,14);
|
||||||
|
|
||||||
|
// Start job
|
||||||
|
do_send(&sendjob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
unsigned long now;
|
||||||
|
now = millis();
|
||||||
|
if ((now & 512) != 0) {
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
os_runloop_once();
|
||||||
|
|
||||||
|
}
|
15
iot/TTNTestABPNew/config.custom.h.example
Normal file
15
iot/TTNTestABPNew/config.custom.h.example
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copy this file to have the name "config.custom.h", and fill in the 3 sections below
|
||||||
|
|
||||||
|
// LoRaWAN NwkSKey, network session key
|
||||||
|
// Big endian (i.e. msb)
|
||||||
|
static const PROGMEM u1_t NWKSKEY[16] = { .... };
|
||||||
|
|
||||||
|
// LoRaWAN AppSKey, application session key
|
||||||
|
// Big endian (i.e. msb)
|
||||||
|
static const u1_t PROGMEM APPSKEY[16] = { .... };
|
||||||
|
|
||||||
|
// LoRaWAN end-device address (DevAddr)
|
||||||
|
// See http://thethingsnetwork.org/wiki/AddressSpace
|
||||||
|
// The library converts the address to network byte order as needed.
|
||||||
|
// Big endian (i.e. msb)
|
||||||
|
static const u4_t DEVADDR = 0x....; // <-- Change this address for every node!
|
159
iot/TTNTestOTAA/TTNTestOTAA.ino
Normal file
159
iot/TTNTestOTAA/TTNTestOTAA.ino
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This example sends a valid LoRaWAN packet with payload "Hello,
|
||||||
|
* world!", using frequency and encryption settings matching those of
|
||||||
|
* the The Things Network.
|
||||||
|
*
|
||||||
|
* This uses OTAA (Over-the-air activation), where where a DevEUI and
|
||||||
|
* application key is configured, which are used in an over-the-air
|
||||||
|
* activation procedure where a DevAddr and session keys are
|
||||||
|
* assigned/generated for use with all further communication.
|
||||||
|
*
|
||||||
|
* Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in
|
||||||
|
* g1, 0.1% in g2), but not the TTN fair usage policy (which is probably
|
||||||
|
* violated by this sketch when left running for longer)!
|
||||||
|
|
||||||
|
* To use this sketch, first register your application and device with
|
||||||
|
* the things network, to set or generate an AppEUI, DevEUI and AppKey.
|
||||||
|
* Multiple devices can use the same AppEUI, but each device has its own
|
||||||
|
* DevEUI and AppKey.
|
||||||
|
*
|
||||||
|
* Do not forget to define the radio type correctly in config.h.
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
#define DISABLE_PING
|
||||||
|
#define DISABLE_BEACONS
|
||||||
|
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#include "config.custom.h"
|
||||||
|
|
||||||
|
static uint8_t mydata[] = "Hello, world!";
|
||||||
|
static osjob_t sendjob;
|
||||||
|
|
||||||
|
// Schedule TX every this many seconds (might become longer due to duty
|
||||||
|
// cycle limitations).
|
||||||
|
const unsigned TX_INTERVAL = 60;
|
||||||
|
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
// Disable link check validation (automatically enabled
|
||||||
|
// during join, but not supported by TTN at this time).
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
Serial.println(F("Unknown event"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, mydata, sizeof(mydata)-1, 0);
|
||||||
|
Serial.println(F("Packet queued"));
|
||||||
|
}
|
||||||
|
// Next TX is scheduled after TX_COMPLETE event.
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(F("Starting"));
|
||||||
|
|
||||||
|
// Activate the right SPI device
|
||||||
|
pinMode(10, OUTPUT);
|
||||||
|
pinMode(3, OUTPUT);
|
||||||
|
digitalWrite(10, LOW);
|
||||||
|
digitalWrite(3, HIGH);
|
||||||
|
|
||||||
|
#ifdef VCC_ENABLE
|
||||||
|
// For Pinoccio Scout boards
|
||||||
|
pinMode(VCC_ENABLE, OUTPUT);
|
||||||
|
digitalWrite(VCC_ENABLE, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// LMIC init
|
||||||
|
os_init();
|
||||||
|
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||||
|
LMIC_reset();
|
||||||
|
LMIC_setClockError(5 * MAX_CLOCK_ERROR / 100);
|
||||||
|
|
||||||
|
// Start job (sending automatically starts OTAA too)
|
||||||
|
do_send(&sendjob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
os_runloop_once();
|
||||||
|
}
|
26
iot/TTNTestOTAA/config.custom.h.example
Normal file
26
iot/TTNTestOTAA/config.custom.h.example
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
// This EUI must be in little-endian format, so least-significant-byte
|
||||||
|
// first. When copying an EUI from ttnctl output, this means to reverse
|
||||||
|
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
|
||||||
|
// 0x70.
|
||||||
|
static const u1_t PROGMEM APPEUI[8]={ ..... };
|
||||||
|
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
|
||||||
|
|
||||||
|
// This should also be in little endian format, see above.
|
||||||
|
static const u1_t PROGMEM DEVEUI[8]={ ...... };
|
||||||
|
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
|
||||||
|
|
||||||
|
// This key should be in big endian format (or, since it is not really a
|
||||||
|
// number but a block of memory, endianness does not really apply). In
|
||||||
|
// practice, a key taken from ttnctl can be copied as-is.
|
||||||
|
// The key shown here is the semtech default key.
|
||||||
|
static const u1_t PROGMEM APPKEY[16] = { ...... };
|
||||||
|
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
|
||||||
|
|
||||||
|
|
||||||
|
const lmic_pinmap lmic_pins = {
|
||||||
|
.nss = 10,
|
||||||
|
.rxtx = LMIC_UNUSED_PIN,
|
||||||
|
.rst = 9,
|
||||||
|
.dio = {2, 6, 7},
|
||||||
|
};
|
1
iot/libraries/Arduino-MemoryFree
Submodule
1
iot/libraries/Arduino-MemoryFree
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit aed5cc5e28f8f01afc41a625151c184a8e94e32e
|
1
iot/libraries/SdFat
Submodule
1
iot/libraries/SdFat
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3b79f38cb554809a45a0ccd6d9d6752b6ad948c2
|
1
iot/libraries/TinyGPS
Submodule
1
iot/libraries/TinyGPS
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit db4ef9c97af767e7345f5ccb277ac3bd1a2eb81f
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9f016150100afcde4d3def8abae234c6efc46e0c
|
Subproject commit ba1265d5d2f775177cdc7c82186724e4f0bdc3a8
|
|
@ -1,54 +1,93 @@
|
||||||
#include "gps.h"
|
#include "gps.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
// The GPS message decoder
|
#include <TinyGPS.h>
|
||||||
TinyGPSPlus gps;
|
|
||||||
// The serial connection to the GPS device
|
|
||||||
SoftwareSerial serial_gps(PIN_GPS_RX, PIN_GPS_TX);
|
|
||||||
|
|
||||||
void gps_begin() {
|
SoftwareSerial gps_begin() {
|
||||||
|
// FUTURE: If this doesn't work as expected, we may want to find a more reliable SoftwareSerial library
|
||||||
|
SoftwareSerial serial_gps(PIN_GPS_RX, PIN_GPS_TX);
|
||||||
// Initialise the software-based serial connection to the GPS device
|
// Initialise the software-based serial connection to the GPS device
|
||||||
serial_gps.begin(BAUD_GPS);
|
serial_gps.begin(BAUD_GPS);
|
||||||
|
return serial_gps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gps_fetch() {
|
GPSLocation gps_fetch() {
|
||||||
Serial.print("[gps] Getting location: ");
|
// The serial connection to the GPS module
|
||||||
uint32_t ms_last_update = millis();
|
SoftwareSerial serial_gps = gps_begin();
|
||||||
|
// The GPS message decoder
|
||||||
|
TinyGPS gps;
|
||||||
|
// The struct that will hold the result
|
||||||
|
GPSLocation result;
|
||||||
|
|
||||||
|
Serial.print(F("[gps] Working: "));
|
||||||
|
uint32_t start = millis(), ms_last_update = millis();
|
||||||
|
uint8_t ticks = 0;
|
||||||
|
unsigned long fix_age = TinyGPS::GPS_INVALID_AGE;
|
||||||
|
unsigned long chars;
|
||||||
|
unsigned short sentences, failed_checksum;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// We WILL discover our location - if it's the last thing we do!
|
// We WILL discover our location - if it's the last thing we do!
|
||||||
while(true) {
|
while(true) {
|
||||||
if(millis() > 5000 && gps.charsProcessed() < 10) {
|
gps.stats(&chars, &sentences, &failed_checksum);
|
||||||
Serial.println(F("\n[error] Failed to initialise GPS device."));
|
|
||||||
|
if(millis() - start > 5000 && chars < 10) {
|
||||||
|
Serial.println(F("\n[error] GPS device init failed"));
|
||||||
while(true) { delay(1); }
|
while(true) { delay(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there's something to read
|
// Make sure there's something to read
|
||||||
if(serial_gps.available() <= 0) {
|
if(serial_gps.available() <= 0)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// If it failed, go around again
|
// If it failed, go around again
|
||||||
if(!gps.encode(serial_gps.read()))
|
if(!gps.encode(serial_gps.read()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
gps.f_get_position(&(result.lat), &(result.lng), &fix_age);
|
||||||
|
|
||||||
// If we haven't acquired a lock yet, go around again
|
// If we haven't acquired a lock yet, go around again
|
||||||
// Also go around again if the location is the same hasn't yet been updated (GPS devices are strange)
|
if(fix_age == TinyGPS::GPS_INVALID_AGE) {
|
||||||
if(!gps.location.isValid()) {
|
// NOTE: It can take a rather long time to get a lock. Just wait patiently :-)
|
||||||
// NOTE: It can really take a rather long time to get a lock. Just wait patiently :-)
|
if(millis() - ms_last_update > 500) {
|
||||||
if(millis() - ms_last_update > 500) Serial.print(".");
|
ticks++;
|
||||||
|
Serial.print('.');
|
||||||
|
if(ticks > 80) {
|
||||||
|
Serial.println();
|
||||||
|
ticks = 0;
|
||||||
|
}
|
||||||
|
// Serial.println(gps.location.lat());
|
||||||
|
// Serial.println(gps.location.lng());
|
||||||
|
// Serial.println(gps.date.year());
|
||||||
|
// Serial.println(gps.time.hour());
|
||||||
|
// Serial.println(gps.time.minute());
|
||||||
|
}
|
||||||
ms_last_update = millis();
|
ms_last_update = millis();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooray!
|
// Hooray!
|
||||||
Serial.println("ok");
|
Serial.println(F("ok"));
|
||||||
return;
|
|
||||||
|
// gps.f_get_position pushes the values into result directly
|
||||||
|
|
||||||
|
#ifdef SD_DEBUG_ENABLED
|
||||||
|
int year;
|
||||||
|
byte month, day, hour, minute, second, _hundredths;
|
||||||
|
gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &_hundredths, &fix_age);
|
||||||
|
|
||||||
|
snprintf(result.time, 19, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
year, month, day,
|
||||||
|
hour, minute, second
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gps_end(serial_gps);
|
||||||
}
|
}
|
||||||
|
|
||||||
TinyGPSPlus gps_info() {
|
void gps_end(SoftwareSerial serial_gps) {
|
||||||
return gps;
|
serial_gps.end();
|
||||||
}
|
|
||||||
|
|
||||||
void gps_end() {
|
|
||||||
Serial.println(F("[warning] Putting the GPS device to sleep isn't implemented yet."));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,27 +2,29 @@
|
||||||
|
|
||||||
#include <SoftwareSerial.h>
|
#include <SoftwareSerial.h>
|
||||||
|
|
||||||
#include <TinyGPS++.h>
|
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
// A lightweight struct to hold location information.
|
||||||
|
// TinyGPS++ actually uses a *ton* of RAM so we can't keep the instance around as a global variable
|
||||||
|
// FUTURE: Rewrite this to use TinyGPS's integer system instead?
|
||||||
|
struct GPSLocation {
|
||||||
|
float lat;
|
||||||
|
float lng;
|
||||||
|
#ifdef SD_DEBUG_ENABLED
|
||||||
|
char time[20];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the connection to the GPS device.
|
* Initialises the connection to the GPS device.
|
||||||
*/
|
*/
|
||||||
void gps_begin();
|
SoftwareSerial gps_begin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches new data from the GPS module.
|
* Fetches new data from the GPS module.
|
||||||
* May take a moment, as the GPS device needs time to acquire a satellite fix.
|
* May take a moment, as the GPS device needs time to acquire a satellite fix.
|
||||||
*/
|
*/
|
||||||
void gps_fetch();
|
GPSLocation gps_fetch();
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the latest information from the GPS device.
|
|
||||||
* Call gps_fetch() first.
|
|
||||||
* @return TinyGPSLocation The current location.
|
|
||||||
*/
|
|
||||||
TinyGPSLocation gps_info();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends the connection to the GPS device and puts it to sleep in order to save
|
* Ends the connection to the GPS device and puts it to sleep in order to save
|
||||||
|
@ -31,5 +33,6 @@ TinyGPSLocation gps_info();
|
||||||
* system and so doesn't get turned off after each measurement, as it takes
|
* system and so doesn't get turned off after each measurement, as it takes
|
||||||
* ~30s to reacquire a lock when it first starts up - hence why we put it to
|
* ~30s to reacquire a lock when it first starts up - hence why we put it to
|
||||||
* sleep instead.
|
* sleep instead.
|
||||||
|
* TODO: Connect the gps module to the timed power rail instead, as its; got an onboard battery.
|
||||||
*/
|
*/
|
||||||
void gps_end();
|
void gps_end(SoftwareSerial gps);
|
||||||
|
|
|
@ -1,47 +1,78 @@
|
||||||
#include <Arduino.h>
|
|
||||||
#include <TinyGPS++.h>
|
|
||||||
#include <SD.h>
|
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#ifdef ENABLE_MEMORY_DIAGNOSTICS
|
||||||
|
// #include <MemoryFree.h>
|
||||||
|
#define DISPLAY_FREE_MEMORY() Serial.println(freeMemory(), DEC);
|
||||||
|
#else
|
||||||
|
#define DISPLAY_FREE_MEMORY() // noop
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
// BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better.
|
// BAD PRACTICE: For some extremely strange reason, the Arduino IDE doesn't pick up random.cpp like it does our other source files - so we've got to explicitly include it here. If we had control over the build process (which we don't), we've use a Makefile here that handled this better.
|
||||||
#include "random.cpp"
|
#include "random.cpp"
|
||||||
#include "gps.h"
|
#include "gps.h"
|
||||||
|
#include "peripheral.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "power.h"
|
||||||
|
#include "transmission.h"
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(BAUD_PC);
|
Serial.begin(BAUD_PC);
|
||||||
Serial.println("[main] Beginning collection sequence.");
|
Serial.println(F("[main] Starting"));
|
||||||
|
|
||||||
random_begin();
|
random_begin();
|
||||||
|
|
||||||
|
DISPLAY_FREE_MEMORY();
|
||||||
gps_begin();
|
GPSLocation gps_data = gps_fetch();
|
||||||
TinyGPSPlus gps_data = gps_location();
|
DISPLAY_FREE_MEMORY();
|
||||||
gps_end();
|
|
||||||
|
Serial.print(F("[main] Location ")); Serial.print(gps_data.lat); Serial.print(F(", ")); Serial.println(gps_data.lng);
|
||||||
Serial.print("[main] Location: ("); Serial.print(loc.lat()); Serial.print(", "); Serial.print(loc.lng()); Serial.println(")");
|
|
||||||
|
|
||||||
uint32_t id = random_get();
|
uint32_t id = random_get();
|
||||||
|
|
||||||
Serial.print("[main] id: ");
|
Serial.print(F("[main] Id "));
|
||||||
Serial.println(id);
|
Serial.println(id);
|
||||||
|
|
||||||
store_init();
|
// Activate microSD card breakout board on the SPI bus
|
||||||
store_reading(id, gps_data.location);
|
|
||||||
char debug_message[64];
|
peripheral_register(PIN_SPI_CS_RFM95);
|
||||||
int chars = snprintf(debug_message, 64, "%d-%d-%d %d:%d:%d | %f %f",
|
peripheral_register(PIN_SPI_CS_SD);
|
||||||
gps_data.date.year(),
|
|
||||||
gps_data.date.month(),
|
peripheral_unsilence(PIN_SPI_CS_SD);
|
||||||
gps_data.date.day(),
|
|
||||||
gps_data.time.hour(),
|
DISPLAY_FREE_MEMORY();
|
||||||
gps_data.time.minute(),
|
store_reading(id, gps_data);
|
||||||
gps_data.time.second(),
|
DISPLAY_FREE_MEMORY();
|
||||||
gps_data.location.lat(),
|
#ifdef SD_DEBUG_ENABLED
|
||||||
gps_data.location.lng(),
|
store_debug(gps_data.time, 19 - 1); // Don't print the null byte
|
||||||
);
|
DISPLAY_FREE_MEMORY();
|
||||||
store_debug(debug_message, chars);
|
#endif
|
||||||
store_close();
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Activate the RFM95
|
||||||
|
peripheral_switch(PIN_SPI_CS_SD, PIN_SPI_CS_RFM95);
|
||||||
|
|
||||||
|
const uint8_t message_size = sizeof(uint32_t) + sizeof(float)*2;
|
||||||
|
uint8_t message[message_size];
|
||||||
|
|
||||||
|
const uint8_t* bytes_id = reinterpret_cast<uint8_t const *>(&id);
|
||||||
|
const uint8_t* bytes_lat = reinterpret_cast<uint8_t const *>(&(gps_data.lat));
|
||||||
|
const uint8_t* bytes_lng = reinterpret_cast<uint8_t const *>(&(gps_data.lat));
|
||||||
|
|
||||||
|
for(int i = 0; i < sizeof(uint32_t); i++) {
|
||||||
|
message[i] = bytes_id[i];
|
||||||
|
}
|
||||||
|
for(int i = 0; i < sizeof(float); i++) {
|
||||||
|
message[sizeof(uint32_t) + i] = bytes_lat[i];
|
||||||
|
message[sizeof(uint32_t) + sizeof(float) + i] = bytes_lng[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_RADIO
|
||||||
|
Serial.println(F("[main] Transmitting"));
|
||||||
|
transmit_init();
|
||||||
|
transmit_send(message, message_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
power_off(); // Doesn't return
|
power_off(); // Doesn't return
|
||||||
}
|
}
|
||||||
|
|
20
iot/main/peripheral.cpp
Normal file
20
iot/main/peripheral.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void peripheral_register(uint8_t pin_number) {
|
||||||
|
pinMode(OUTPUT, pin_number);
|
||||||
|
// Disable the device by default to avoid issues
|
||||||
|
digitalWrite(pin_number, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void peripheral_unsilence(uint8_t pin_number) {
|
||||||
|
digitalWrite(pin_number, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void peripheral_silence(uint8_t pin_number) {
|
||||||
|
digitalWrite(pin_number, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void peripheral_switch(uint8_t pin_number_old, uint8_t pin_number_new) {
|
||||||
|
digitalWrite(pin_number_old, HIGH);
|
||||||
|
digitalWrite(pin_number_new, LOW);
|
||||||
|
}
|
24
iot/main/peripheral.h
Normal file
24
iot/main/peripheral.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new SPI peripheral.
|
||||||
|
* @param pin_number The pin number of the device's chip select pin.
|
||||||
|
*/
|
||||||
|
void peripheral_register(uint8_t pin_number);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the device with the given chip select pin to talk on the SPI bus.
|
||||||
|
* @param pin_number The pin number of the chip select pin of the device to allow to talk.
|
||||||
|
*/
|
||||||
|
void peripheral_unsilence(uint8_t pin_number);
|
||||||
|
/**
|
||||||
|
* Stops the device with the given chip select pin from talking on the SPI bus.
|
||||||
|
* @param pin_number The chip-select pin number of the device to stop.
|
||||||
|
*/
|
||||||
|
void peripheral_silence(uint8_t pin_number);
|
||||||
|
/**
|
||||||
|
* Switches the active device from one to another on the SPI bus.
|
||||||
|
* @param pin_number_old The chip-select pin number of the old device to switch out from.
|
||||||
|
* @param pin_number_new The chip-select pin number of the new device to switch in to.
|
||||||
|
*/
|
||||||
|
void peripheral_switch(uint8_t pin_number_old, uint8_t pin_number_new);
|
|
@ -2,19 +2,10 @@
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
|
||||||
void power_gps_wakeup() {
|
|
||||||
Serial.println("Warn: GPS wakeup isn't implemented yet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void power_gps_standby() {
|
|
||||||
Serial.println("Warn: GPS standby isn't implemented yet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void power_off() {
|
void power_off() {
|
||||||
Serial.println(F("[power] Beginning shutdown sequence"));
|
Serial.println(F("[power] Shutting down"));
|
||||||
Serial.println(F("[power] Switching GPS module to standby"));
|
// Serial.println(F("[power] Switching GPS module to standby"));
|
||||||
power_gps_standby();
|
// power_gps_standby();
|
||||||
|
|
||||||
Serial.println(F("[power] Signalling TPL5111"));
|
Serial.println(F("[power] Signalling TPL5111"));
|
||||||
pinMode(PIN_TPL_DONE, OUTPUT);
|
pinMode(PIN_TPL_DONE, OUTPUT);
|
||||||
|
|
|
@ -1,13 +1,4 @@
|
||||||
|
|
||||||
/**
|
|
||||||
* Wakes the GPS module up from standby.
|
|
||||||
*/
|
|
||||||
void power_gps_wakeup();
|
|
||||||
/**
|
|
||||||
* Puts the GPS module into standby.
|
|
||||||
*/
|
|
||||||
void power_gps_standby();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals that our work is complete and that we can turn off now to the
|
* Signals that our work is complete and that we can turn off now to the
|
||||||
* TPL5111.
|
* TPL5111.
|
||||||
|
|
26
iot/main/settings.custom.cpp.example
Normal file
26
iot/main/settings.custom.cpp.example
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
|
||||||
|
// LoRaWAN NwkSKey, network session key
|
||||||
|
// This is the default Semtech key, which is used by the early prototype TTN
|
||||||
|
// network.
|
||||||
|
static const PROGMEM u1_t NWKSKEY[16] = { .... };
|
||||||
|
|
||||||
|
// LoRaWAN AppSKey, application session key
|
||||||
|
// This is the default Semtech key, which is used by the early prototype TTN
|
||||||
|
// network.
|
||||||
|
static const u1_t PROGMEM APPSKEY[16] = { ..... };
|
||||||
|
|
||||||
|
// LoRaWAN end-device address (DevAddr)
|
||||||
|
static const u4_t DEVADDR = 0x....; // <-- Change this address for every node!
|
||||||
|
|
||||||
|
const lmic_pinmap lmic_pins = {
|
||||||
|
.nss = PIN_SPI_CS_RFM95,
|
||||||
|
.rxtx = LMIC_UNUSED_PIN,
|
||||||
|
.rst = 9,
|
||||||
|
.dio = {2, 6, 7},
|
||||||
|
};
|
14
iot/main/settings.custom.h
Normal file
14
iot/main/settings.custom.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't edit this file - you want to make a copy of settings.custom.cpp, call
|
||||||
|
* it settings.cpp, and edit that instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern const PROGMEM u1_t NWKSKEY[16];
|
||||||
|
extern const u1_t PROGMEM APPSKEY[16];
|
||||||
|
extern const u4_t DEVADDR;
|
||||||
|
|
||||||
|
extern const lmic_pinmap lmic_pins;
|
|
@ -1,5 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#include "settings.custom.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the main settings file - customise it to match your setup.
|
||||||
|
* Don't forget that you need to take a copy of settings.custom.cpp.example as
|
||||||
|
* settings.custom.cpp, and fill in the private keys the program needs to talk
|
||||||
|
* over LORaWAN.
|
||||||
|
*/
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
////////////// Main //////////////
|
////////////// Main //////////////
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
|
@ -7,23 +20,44 @@
|
||||||
// The speed at which we should talk over our main hardware serial connection.
|
// The speed at which we should talk over our main hardware serial connection.
|
||||||
#define BAUD_PC 115200
|
#define BAUD_PC 115200
|
||||||
|
|
||||||
// Multiple devices can use the same SPI data pin AFAIKT, but some libraries *cough* SD *cough* are too stupid to figure out which pin it is on their own.
|
// Multiple devices can use the same SPI data pin AFAIKT, but some libraries
|
||||||
|
// *cough* SD *cough* are too stupid to figure out which pin it is on their own.
|
||||||
#define PIN_SPI_DATA 9
|
#define PIN_SPI_DATA 9
|
||||||
|
|
||||||
|
// The 'done' pin to pulse to signal to the TPL5111
|
||||||
#define PIN_TPL_DONE 8
|
#define PIN_TPL_DONE 8
|
||||||
|
|
||||||
|
// Uncomment to print RAM diagnostics at regular intervals.
|
||||||
|
//#define ENABLE_MEMORY_DIAGNOSTICS
|
||||||
|
|
||||||
|
/////////////
|
||||||
|
/// RFM95 ///
|
||||||
|
/////////////
|
||||||
|
|
||||||
|
// Whether to actually enable LMIC-based transmission or not.
|
||||||
|
#define ENABLE_RADIO
|
||||||
|
|
||||||
|
// The SPI chip-select pin for the RFM 95
|
||||||
|
#define PIN_SPI_CS_RFM95 10
|
||||||
|
|
||||||
|
// Pin mapping - see settings.custom.cpp
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
////////////// GPS //////////////
|
////////////// GPS //////////////
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
|
||||||
// The *TX* gin of the GPS device.
|
// The *TX* gin of the GPS device.
|
||||||
// This is swapped because we receive the GPS device's message on our side on the RX pin, and the GPS device transmits messages on the TX.
|
// This is swapped because we receive the GPS device's message on our side on
|
||||||
|
// the RX pin, and the GPS device transmits messages on the TX.
|
||||||
#define PIN_GPS_RX 5
|
#define PIN_GPS_RX 5
|
||||||
// The *RX* pin on the GPS device.
|
// The *RX* pin on the GPS device.
|
||||||
// This is swapped because where the GPs device is receiving, we aresending and vice versa.
|
// This is swapped because where the GPs device is receiving, we aresending and
|
||||||
|
// vice versa.
|
||||||
// The TX / RX here are according to *our* side, not the GPS device's side.
|
// The TX / RX here are according to *our* side, not the GPS device's side.
|
||||||
#define PIN_GPS_TX 4
|
#define PIN_GPS_TX 4
|
||||||
// The speed at which we should talk to the GPS device. Some GPS devices require a certain speed in order to use certain commands, so it's important that you check the datasheets for the device you're using.
|
// The speed at which we should talk to the GPS device. Some GPS devices
|
||||||
|
// require a certain speed in order to use certain commands, so it's important
|
||||||
|
// that you check the datasheets for the device you're using.
|
||||||
// 9600 is the correct speed for a NEO-6M.
|
// 9600 is the correct speed for a NEO-6M.
|
||||||
#define BAUD_GPS 9600
|
#define BAUD_GPS 9600
|
||||||
|
|
||||||
|
@ -32,10 +66,13 @@
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
|
|
||||||
// The chip select pin that activates the connection to the microSD card over SPI.
|
// The chip select pin that activates the connection to the microSD card over SPI.
|
||||||
#define PIN_SD_SPI_CHIP_SELECT 3
|
#define PIN_SPI_CS_SD 3
|
||||||
|
|
||||||
// The filename on the microSD card to store data in.
|
// The filename on the microSD card to store data in.
|
||||||
#define SD_FILENAME "data.tsv"
|
#define SD_FILENAME F("data.tsv")
|
||||||
|
|
||||||
|
// Whether to write debug information to the filename below. This could compromise privacy, so it should be commented out for production.
|
||||||
|
// #define SD_DEBUG_ENABLED
|
||||||
|
|
||||||
// The filename on the microSD card to store debug information in
|
// The filename on the microSD card to store debug information in
|
||||||
#define SD_FILENAME_DEBUG "debug.log"
|
#define SD_FILENAME_DEBUG F("debug.log")
|
||||||
|
|
|
@ -1,41 +1,48 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <SD.h>
|
#include <SdFat.h>
|
||||||
#include <TinyGPS++.h>
|
// #include <ArduinoFiles.h>
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "gps.h"
|
||||||
|
|
||||||
File _debug_handle;
|
// FUTURE: We might be able to trim it down if we disable long filenames with #define
|
||||||
|
|
||||||
void store_init() {
|
SdFat store_init() {
|
||||||
// NOTE: If this doesn't work, then we need to use pinMode() & specify the data pin here instead.
|
SdFat card;
|
||||||
if(!SD.begin(PIN_SD_SPI_CHIP_SELECT)) {
|
if(!card.begin(PIN_SPI_CS_SD)) {
|
||||||
Serial.println(F("Error: Failed to initialise connection to microSD card"));
|
Serial.println("Error: MicroSD card init failed");
|
||||||
while(true) { delay(1); } // Pseudo-halt, but uses delay() to ensure we keep passing back control to allow easy re-programming
|
while(true) delay(100);
|
||||||
}
|
}
|
||||||
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_reading(uint32_t id, TinyGPSLocation location) {
|
void store_reading(uint32_t id, GPSLocation location) {
|
||||||
|
// Port the rest of this to SdFat from SD
|
||||||
|
SdFat card = store_init();
|
||||||
|
|
||||||
// Open the file to write the data to. If it doesn't exist it will be created.
|
// Open the file to write the data to. If it doesn't exist it will be created.
|
||||||
// Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know.
|
// Unlike elsewhere, FILE_WRITE opens the file with the cursor starting at the end, so it's basically actually equivalent to FILE_APPEND that we use in other environments. Confusing, I know.
|
||||||
File handle = SD.open(SD_FILENAME, FILE_WRITE);
|
File handle = card.open(SD_FILENAME, O_APPEND | O_CREAT | O_WRONLY);
|
||||||
|
uint8_t places = 6;
|
||||||
handle.print(id);
|
handle.printField(id, '\t');
|
||||||
handle.print("\t");
|
handle.printField(location.lat, '\t', places);
|
||||||
|
handle.printField(location.lng, '\n', places);
|
||||||
handle.print(location.lat());
|
handle.close(); // Syncs implicitly
|
||||||
handle.print("\t");
|
|
||||||
|
|
||||||
handle.print(location.lng());
|
|
||||||
handle.println();
|
|
||||||
|
|
||||||
handle.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_debug(char* buffer, int size) {
|
void store_debug(char* buffer, int size) {
|
||||||
File handle = SD.open(SD_FILENAME_DEBUG, FILE_WRITE);
|
SdFat card = store_init();
|
||||||
|
File handle = card.open(SD_FILENAME_DEBUG, O_APPEND | O_CREAT | O_WRONLY);
|
||||||
|
handle.write(buffer, size);
|
||||||
|
handle.write('\n');
|
||||||
|
handle.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void store_close() {
|
/*
|
||||||
SD.end();
|
void store_end() {
|
||||||
|
// Apparently we'ree fine so long as we don't have any open file handles - there's no end() method on the SdFat class
|
||||||
|
// card->end();
|
||||||
|
delete card;
|
||||||
|
card = nullptr;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <SD.h>
|
#include <SdFat.h>
|
||||||
|
|
||||||
#include <TinyGPS++.h>
|
#include "gps.h"
|
||||||
|
|
||||||
void store_init();
|
void store_init();
|
||||||
|
|
||||||
void store_reading(uint32_t id, TinyGPSLocation location);
|
void store_reading(uint32_t id, GPSLocation location);
|
||||||
|
|
||||||
void store_debug(char* buffer, int size);
|
void store_debug(char* buffer, int size);
|
||||||
|
|
||||||
void store_close();
|
// void store_end();
|
||||||
|
|
150
iot/main/transmission.cpp
Normal file
150
iot/main/transmission.cpp
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
// Global static variable that's used to detect when LMIC has finished doing it's thing
|
||||||
|
bool is_sending_complete = false;
|
||||||
|
|
||||||
|
void transmit_init() {
|
||||||
|
Serial.println(F("[LMIC] Init"));
|
||||||
|
// LMIC init
|
||||||
|
os_init();
|
||||||
|
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||||
|
LMIC_reset();
|
||||||
|
|
||||||
|
// Set static session parameters. Instead of dynamically establishing a session
|
||||||
|
// by joining the network, precomputed session parameters are be provided.
|
||||||
|
#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
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Disable link check validation
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
|
||||||
|
// Set data rate and transmit power (note: txpow seems to be ignored by the library)
|
||||||
|
LMIC_setDrTxpow(DR_SF7,14);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a specified message via LoRaWAN.
|
||||||
|
* @param data The message to send.
|
||||||
|
* @param length The length of the given message.
|
||||||
|
*/
|
||||||
|
bool transmit_send(uint8_t* data, int length) {
|
||||||
|
// Check if there is not a current TX/RX job running
|
||||||
|
if (LMIC.opmode & OP_TXRXPEND) {
|
||||||
|
Serial.println(F("OP_TXRXPEND: Job running, can't send"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Prepare upstream data transmission at the next possible time.
|
||||||
|
LMIC_setTxData2(1, data, length, 0);
|
||||||
|
Serial.println(F("Packet queued"));
|
||||||
|
|
||||||
|
// Run the LMIC loop, but only until it's finished sending the packet
|
||||||
|
while (!is_sending_complete) {
|
||||||
|
os_runloop_once();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset it for next time (just in case)
|
||||||
|
is_sending_complete = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEvent (ev_t ev) {
|
||||||
|
Serial.print(millis());
|
||||||
|
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_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_TXCOMPLETE:
|
||||||
|
Serial.println(F("EV_TXCOMPLETE (incl. RX window wait)"));
|
||||||
|
if(LMIC.dataLen) {
|
||||||
|
// data received in rx slot after tx
|
||||||
|
Serial.print(F("Received: "));
|
||||||
|
Serial.write(LMIC.frame+LMIC.dataBeg, LMIC.dataLen);
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
// We're done!
|
||||||
|
is_sending_complete = true;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
13
iot/main/transmission.h
Normal file
13
iot/main/transmission.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the RFM95 LoRa radio.
|
||||||
|
*/
|
||||||
|
void transmit_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a specified message via LoRaWAN.
|
||||||
|
* @param data The message to send.
|
||||||
|
* @param length The length of the given message.
|
||||||
|
*/
|
||||||
|
bool transmit_send(byte* data, int length);
|
Loading…
Reference in a new issue