Yes, sorry.
hive.ino
#include "hive.h"
void setup() {
while (!Serial); // wait for Serial to be initialized
Serial.begin(115200);
delay(100);
initSensors();
initAndSetupOS();
// Start job
do_read_critical(&readCriticalJob);
do_update(&updatejob);
do_send(&sendjob);
// The packet indicating that the node has been turned on/reset has already been sent (do_sent(...)), now we set it back to 0
isReset = 0;
}
void loop() {
os_runloop_once();
}
hive.h
#ifndef HIVE_H
#define HIVE_H
/******************************************************************************
*
* 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)!
*
* Do not forget to define the radio type correctly in
* arduino-lmic/project_config/lmic_project_config.h or from your BOARDS.txt.
*
*******************************************************************************/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
// Temp sensor MPU6050 ,accel gyro temp
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
// Temp humid sensor, SHT31
#include <Adafruit_SHT31.h>
// LoRaWAN NwkSKey, network session key
// This should be in big-endian (aka msb).
constexpr PROGMEM u1_t NWKSKEY[16] = {X};
// LoRaWAN AppSKey, application session key
// This should also be in big-endian (aka msb).
constexpr u1_t PROGMEM APPSKEY[16] = {X};
// LoRaWAN end-device address (DevAddr)
// See http://thethingsnetwork.org/wiki/AddressSpace
// The library converts the address to network byte order as needed, so this should be in big-endian (aka msb) too.
constexpr u4_t DEVADDR = X ; // <-- Change this address for every node!
// Pin mapping
extern const lmic_pinmap lmic_pins;
// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
constexpr unsigned TX_INTERVAL = 60; // seconds
constexpr unsigned CRIT_CHECK_INTERVAL = 500; // milliseconds
constexpr unsigned CRIT_CHECK_DURATION = 20; // milliseconds -- just a largely generous guess
constexpr unsigned UPDATE_INTERVAL = TX_INTERVAL;
constexpr unsigned UPDATE_DURATION = 150; // milliseconds -- I2C readings take time but it's a generous guess too
//Adafruit_MPU6050 mpu;
extern Adafruit_SHT31 sht31;
//Limit switch (fin de course)
constexpr int limitSwitchPin = 5;
extern uint16_t openCount;
extern uint16_t closeCount;
extern uint32_t lastOpen;
//Added to reduce the processing load (updating mydata with no eventual changes)
extern bool updateOpen; // true : means openCount and lastOpen have changed and we should update mydata[] with the new data
extern bool updateClose; // true : means closeCount have changed and we should update mydata[] with the new data
extern uint8_t mydata[]; // For some reason we need an extra unused byte (for 13 slots "0-12" used we need 14)
extern osjob_t sendjob;
extern osjob_t readCriticalJob;
extern osjob_t updatejob;
// Power/Reset flag
extern uint8_t isReset; // 1 : the device has just been turned ON or reset
void do_send(osjob_t*);
void do_update(osjob_t*);
void do_read_critical(osjob_t*);
void onEvent (ev_t);
void initSensors();
void initAndSetupOS();
void checkTimeCriticalSensors();
#endif
hive.cpp
#include "hive.h"
const lmic_pinmap lmic_pins = {
.nss = 10,
.rxtx = LMIC_UNUSED_PIN,
.rst = 9,
.dio = {2, 6, 7},
};
Adafruit_SHT31 sht31 = Adafruit_SHT31();
//Limit switch (fin de course)
//TO UPDATE : Reduce openCount, closeCount to uint8_t
uint16_t openCount = 0;
uint16_t closeCount = 0;
uint32_t lastOpen = 0;
bool updateOpen = true;
bool updateClose = true;
uint8_t mydata[15];
osjob_t sendjob;
osjob_t readCriticalJob;
osjob_t updatejob;
uint8_t isReset = 1;
void onEvent (ev_t ev) {
Serial.print("ticks : " + String(os_getTime()) + ", ms : " + String(osticks2ms(os_getTime())));
Serial.print(": ");
switch(ev) {
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
// Schedule next transmission
os_setTimedCallback(&updatejob, os_getTime()+ (sec2osticks(UPDATE_INTERVAL) - ms2osticks(UPDATE_DURATION)), do_update);
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
//if (!os_queryTimeCriticalJobs(ms2osticks(CRIT_CHECK_INTERVAL))) { // Had to make sure there is nothing
os_setTimedCallback(&readCriticalJob, os_getTime()+ms2osticks(CRIT_CHECK_INTERVAL - CRIT_CHECK_DURATION), do_read_critical);
// Serial.println(F("nthn dont worry"));}
break;
case EV_TXSTART:
Serial.println(F("EV_TXSTART"));
break;
default:
Serial.print(F("Unknown event: "));
Serial.println((unsigned) ev);
break;
}
}
//MPU6050
/*void updateMPU() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
int16_t tempInt = temp.temperature * 100; // Convert temperature to an integer to avoid floating point in payload
mydata[0] = tempInt >> 8;
mydata[1] = tempInt & 0xFF;
}*/
//SHT31, read and pack temperature and humidity data (mydata : 0-3)
void readAndPackTempHum(){
float temperature = sht31.readTemperature();
float humidity = sht31.readHumidity();
// Check if readings failed and if so, use a sentinel value
if (isnan(temperature) || isnan(humidity)) {
Serial.println("Failed to read from SHT31 sensor!");
return;
}
// Convvert temperature and humidity to integers for transmission
int16_t tempInt = temperature * 100; // e.g., 23.45 degrees -> 2345
int16_t humInt = humidity * 100; // e.g., 45.67% -> 4567
// Pack the temperature into first two bytes of mydata
mydata[0] = tempInt >> 8;
mydata[1] = tempInt & 0xFF;
// Pack the humidity into next two bytes of mydata
mydata[2] = humInt >> 8;
mydata[3] = humInt & 0xFF;
Serial.println("temp : " + String(temperature) +", humid : " + String (humidity));
}
//LimitSwitch, packs switch related data (mydata : 4-11)
//TO UPDATE : packaging of openCount/closeCount to hold one slot only
void packSwitch() {
if (updateOpen) {
mydata[4] = openCount >> 8;
mydata[5] = openCount & 0xFF;
mydata[6] = lastOpen >> 24;
mydata[7] = (lastOpen >> 16) & 0xFF;
mydata[8] = (lastOpen >> 8) & 0xFF;
mydata[9] = lastOpen & 0xFF;
updateOpen = false;
}
else if (updateClose) {
mydata[10] = closeCount >> 8;
mydata[11] = closeCount & 0xFF;
updateClose = false;
}
Serial.println("openCount : " + String(openCount) + ", lastOpen : " + String(lastOpen) + ", closeCount : " + String(closeCount));
}
// Updates switch related data
void checkSwitch(s4_t currTime) {
int switchState = digitalRead(limitSwitchPin);
if (openCount == closeCount && switchState == HIGH) { // openCount == closeCount : means last time we checked it was closed
openCount ++;
lastOpen = currTime / 1000;
updateOpen = true;
}
else if (openCount > closeCount && switchState == LOW) { // openCount > closeCount : means last time we checked it was open
closeCount ++;
updateClose = true;
}
}
//reads all data off of the time critical sensors, eventually limitswitch and accelerometer
void checkTimeCriticalSensors() {
s4_t currTime = osticks2ms(os_getTime()); // We dont use millis() cuz it's interrupt dependant and LMIC by default has interrupts disabled
checkSwitch(currTime);
}
void updateData() {
readAndPackTempHum(); // 0-3
packSwitch(); //4-11
//Pack the reset flag
mydata[12] = isReset;
Serial.println("reset : " + String(isReset));
}
/* Decoder for TTN :
//TO UPDATE : packaging of openCount/closeCount to hold one slot only
function Decoder(bytes, port) {
var decoded = {};
// Decode temperature
var tempRaw = (bytes[0] << 8) | bytes[1];
decoded.temperature = tempRaw / 100.0;
// Decode humidity
var humRaw = (bytes[2] << 8) | bytes[3];
decoded.humidity = humRaw / 100.0;
// Decode openCount
decoded.openCnt = (bytes[4] << 8) | bytes[5];
//Decode lastOpen
decoded.lastOpen = (bytes[6] << 24) | (bytes[7] << 16) | (bytes[8] << 8) | bytes[9];
//Decode closeCount
decoded.closeCnt = (bytes[10] << 8) | bytes[11];
//Decode resetFLag;
decoded.reset = bytes[12];
return decoded;
}
*/
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 do_update(osjob_t* j){
Serial.println(F("Start update!"));
updateData();
//If in (UPDATE_INTERVAL) milliseconds no jobs are scheduled, schedule the next check in (UPDATE_INTERVAL - UPDATE_DURATION) milliseconds.
//It'll (take UPDATE_DURATION) milliseconds for the function to complete running.
if (!os_queryTimeCriticalJobs(ms2osticks(UPDATE_INTERVAL)))
os_setTimedCallback(&updatejob, os_getTime()+ms2osticks(UPDATE_INTERVAL - UPDATE_DURATION), do_update);
Serial.println(F("Updated!"));
}
void do_read_critical(osjob_t* j){
Serial.println(F("Start critical!"));
checkTimeCriticalSensors();
//If in (CRIT_CHECK_INTERVAL) milliseconds no jobs are scheduled, schedule the next check in (CRIT_CHECK_INTERVAL - CRIT_CHECK_DURATION) milliseconds.
//It'll take (CRIT_CHECK_DURATION) milliseconds for the function to complete running.
if (!os_queryTimeCriticalJobs(ms2osticks(CRIT_CHECK_INTERVAL))) {
os_setTimedCallback(&readCriticalJob, os_getTime()+ms2osticks(CRIT_CHECK_INTERVAL - CRIT_CHECK_DURATION), do_read_critical);
}
Serial.println(F("Read critical!"));
}
void initSensors() {
/*if (!mpu.begin()) {
Serial.println(F("Failed to find MPU6050 chip"));
while (1) {
delay(10);
}
}
Serial.println(F("MPU6050 Found!"));*/
if (!sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr
Serial.println(F("Couldn't find SHT31"));
while (1) delay(1);
}
Serial.println(F("SHT31 Found!"));
pinMode(limitSwitchPin, INPUT_PULLUP);
}
void initAndSetupOS() {
Serial.println(F("Starting LMIC OS"));
// 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. The LMIC doesn't let you change
// the three basic settings, but we show them here.
for (int channel = 0; channel < 9; channel++) {
if (channel == 0) {
LMIC_setupChannel(channel, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI);
} else {
LMIC_disableChannel(channel);
}
}
// 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.
#else
# error Region not supported
#endif
// Disable link check validation
LMIC_setLinkCheckMode(0);
// Set data rate and transmit power for uplink
LMIC_setDrTxpow(DR_SF7,14);
}