234 lines
6.1 KiB
C++
234 lines
6.1 KiB
C++
/*
|
|
main.cpp - TinyLora / TinyTX Firmware
|
|
Copyright (c) 2019, Stefan Brand
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
3. Neither the name of the copyright holder nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <Arduino.h>
|
|
#include <avr/sleep.h>
|
|
#include <avr/wdt.h>
|
|
#include <tinySPI.h>
|
|
|
|
// secconfig.h Configures RF Module, TTN Keys / RF Networks and used Sensor
|
|
#include "secconfig.h"
|
|
|
|
#ifdef RF_LORA
|
|
// Include LoRaWAN
|
|
#include <LoRaWAN.h>
|
|
#define DIO0 PIN_B0
|
|
#define NSS PIN_B1
|
|
RFM95 rfm(DIO0,NSS);
|
|
LoRaWAN lora = LoRaWAN(rfm);
|
|
uint16_t Frame_Counter_Tx = 0x0000;
|
|
#endif
|
|
|
|
#ifdef RF_RFM69
|
|
// Include RFM69
|
|
#include <RFM69_f.h>
|
|
RFM69 radio;
|
|
#endif
|
|
|
|
// Sensorclass and deepsleep interval (for measurement about every 10Min)
|
|
#ifdef HAS_BME280
|
|
#include <BME280.h>
|
|
BME280 sensor;
|
|
#endif
|
|
|
|
#ifdef HAS_SHT21
|
|
#include <SHT21.h>
|
|
SHT21 sensor;
|
|
#endif
|
|
|
|
// Global Variable used for deep sleep
|
|
uint16_t sleep_interval;
|
|
|
|
#ifdef LED_PIN
|
|
void blink(uint8_t num) {
|
|
pinMode(LED_PIN, OUTPUT);
|
|
digitalWrite(LED_PIN, 0);
|
|
for (uint8_t i=0; i<num*2; i++) {
|
|
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
|
|
delay(100);
|
|
}
|
|
digitalWrite(LED_PIN, 0);
|
|
}
|
|
#endif
|
|
|
|
// Setup Wakeup Interrupt Timer
|
|
void init_wdt()
|
|
{
|
|
MCUSR &= ~(1<<WDRF);
|
|
// Start timed sequence
|
|
// Set Watchdog Change Enable bit
|
|
WDTCSR |= (1<<WDCE) | (1<<WDE);
|
|
// Set new prescaler (8 sec), unset reset enable
|
|
// enable WDT interrupt
|
|
WDTCSR = (1<<WDIE)|(1<<WDP3)|(1<<WDP0);
|
|
}
|
|
|
|
// Enter Sleepmode, Sleep for s Seconds
|
|
void sleep(uint16_t s)
|
|
{
|
|
s = s/8;
|
|
sleep_interval = 0;
|
|
while (sleep_interval < s) {
|
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
|
sleep_mode();
|
|
}
|
|
}
|
|
|
|
// Watchdog Callback for Sleep Timer
|
|
ISR(WATCHDOG_vect) {
|
|
sleep_interval++; // set global flag
|
|
// Start timed sequence
|
|
// Set Watchdog Change Enable bit
|
|
WDTCSR |= (1<<WDCE) | (1<<WDE);
|
|
// Set new prescaler (8 sec), unset reset enable
|
|
// enable WDT interrupt
|
|
WDTCSR = (1<<WDIE)|(1<<WDP3)|(1<<WDP0);
|
|
}
|
|
|
|
// Get Battery Voltage
|
|
int32_t readVcc() {
|
|
bitClear(PRR, PRADC);
|
|
ADCSRA |= bit(ADEN); // Enable the ADC
|
|
int32_t result;
|
|
ADMUX = _BV(MUX5) | _BV(MUX0); // For ATtiny84
|
|
delay(2); // Wait for Vref to settle
|
|
ADCSRA |= _BV(ADSC); // Convert
|
|
while (bit_is_set(ADCSRA,ADSC));
|
|
result = ADCL;
|
|
result |= ADCH<<8;
|
|
result = 1126400L / result; // Back-calculate Vcc in mV
|
|
ADCSRA &= ~ bit(ADEN);
|
|
bitSet(PRR, PRADC); // Disable the ADC to save power
|
|
return result;
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
// Initialize Sleep Timer
|
|
init_wdt();
|
|
PRR = bit(PRTIM1);
|
|
|
|
#ifdef RF_LORA
|
|
// Setup LoraWAN
|
|
rfm.init();
|
|
lora.setKeys(NwkSkey, AppSkey, DevAddr);
|
|
#endif
|
|
|
|
#ifdef RF_RFM69
|
|
radio.initialize(RF69_433MHZ,RFM69_NODEID,RFM69_NETWORKID);
|
|
#ifdef RFM69_ENCKEY
|
|
radio.encrypt(RFM69_ENCKEY);
|
|
#endif
|
|
radio.setPowerLevel(RFM69_TXPOWER);
|
|
radio.sleep();
|
|
#endif
|
|
// Setup LED if defined
|
|
#ifdef LED_PIN
|
|
pinMode(LED_PIN, OUTPUT);
|
|
blink(1);
|
|
#endif
|
|
}
|
|
|
|
|
|
void loop()
|
|
{
|
|
// Create Data Structure for Sensor Data
|
|
#ifdef HAS_NO_SENSOR
|
|
struct lora_data {
|
|
uint8_t bat;
|
|
} __attribute__ ((packed)) data;
|
|
#elif defined HAS_SHT21
|
|
struct lora_data {
|
|
uint8_t bat;
|
|
int32_t temperature;
|
|
int32_t humidity;
|
|
} __attribute__ ((packed)) data;
|
|
#elif defined HAS_BME280
|
|
struct lora_data {
|
|
uint8_t bat;
|
|
int32_t temperature;
|
|
int32_t humidity;
|
|
int32_t pressure;
|
|
} __attribute__ ((packed)) data;
|
|
#endif
|
|
|
|
// Get Sensor Data
|
|
#ifdef HAS_BME280
|
|
sensor.getData(&data.temperature, &data.pressure, &data.humidity);
|
|
#endif
|
|
|
|
#ifdef HAS_SHT21
|
|
data.temperature = (int32_t)(sensor.getTemperature()*100);
|
|
data.humidity = (int32_t)(sensor.getHumidity()*100);
|
|
#endif
|
|
|
|
// Add Battery Voltage, 20mv steps, encoded into 1 Byte
|
|
uint32_t batv = readVcc();
|
|
data.bat = (uint8_t)(batv/20);
|
|
if (batv % 20 > 9)
|
|
data.bat += 1;
|
|
|
|
// LED On before Sending
|
|
#ifdef LED_ON_SEND
|
|
digitalWrite(LED_PIN, 1);
|
|
#endif
|
|
|
|
#ifdef RF_LORA
|
|
#ifdef HAS_NO_SENSOR
|
|
// Send Packet in all 6 SFs for Beacon Mode
|
|
unsigned char Frame_Port =0x07;
|
|
for (int i = SF7BW125; i<=SF12BW125; i++) {
|
|
lora_data tdata = data;
|
|
lora.Send_Data((unsigned char *)&tdata, sizeof(tdata), Frame_Counter_Tx, static_cast<lora_dr>(i), Frame_Port);
|
|
Frame_Counter_Tx++;
|
|
Frame_Port++;
|
|
delay(25);
|
|
}
|
|
#else
|
|
// Send LoRa Packet, Increment Frame Counter
|
|
lora.Send_Data((unsigned char *)&data, sizeof(data), Frame_Counter_Tx, SF7BW125, 0x01);
|
|
Frame_Counter_Tx++;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef RF_RFM69
|
|
radio.sendWithRetry(RFM69_GATEWAY, &data, sizeof(data), 3, random(150,400));
|
|
radio.sleep();
|
|
#endif
|
|
|
|
// Led Off after Sending
|
|
#ifdef LED_ON_SEND
|
|
digitalWrite(LED_PIN, 0);
|
|
#endif
|
|
|
|
// Sleep until next Measurement
|
|
sleep(SLEEP_TIME);
|
|
}
|