├── LICENSE ├── PayloadFormat.js ├── README.md └── code └── main.ino /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Luke Prior 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PayloadFormat.js: -------------------------------------------------------------------------------- 1 | function Decoder(bytes, port) { 2 | var temperature = bytes[0]<<24>>16 | bytes[1]; 3 | var humidity = (bytes[2] << 8) | bytes[3]; 4 | var pressure = ((bytes[4]) << 24) + ((bytes[5]) << 16) + ((bytes[6]) << 8) + ((bytes[7])); 5 | var battery = (bytes[8] << 8) | bytes[9]; 6 | var battery_level = bytes[11]; 7 | 8 | return { 9 | temperature: temperature / 100, 10 | humidity: humidity, 11 | pressure: pressure / 100, 12 | battery: battery / 1000, 13 | battery_level: battery_level 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TTN BME280 Weather Station - Heltec CubeCell HTCC-AB01 2 | 3 | This repository contains all the code and steps required to setup a BME280 with the Heltec CubeCell HTCC-AB01 on TTN Australia. 4 | 5 | # Getting Started 6 | 7 | This code is for a BME280 sensor attached to the Heltec CubeCell HTCC-AB01 via i2c. The code is set to send the temperature, humidity, pressure, battery voltage, and battery level values to The Things Network every 100 seconds. The TTN Development EUI, Application EUI, Application Key, and time period can all be configured in main.ino. 8 | 9 | Download this repository and open the main.ino file in the Arduino IDE, enter your TTN Dev EUI, App EUI, App Key, and upload the project to your CubeCell HTCC-AB01. The CubeCell HTCC-AB01 needs to be preconfigured in the Arduino board manager to set your LoRaWAN region and enable OTAA authentification. 10 | 11 | # DIYODE Magazine Article 12 | 13 | [Off-Grid LoRaWAN Weather Station](https://diyodemag.com/projects/long_range_report_off-grid_lorawan_weather_station) 14 | 15 | # License 16 | 17 | This project is licensed under the MIT License. This project contains code from [chrisys/mini-lora-weatherstation](https://github.com/chrisys/mini-lora-weatherstation). 18 | -------------------------------------------------------------------------------- /code/main.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* OTAA para*/ 7 | uint8_t devEui[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX }; //#Insert you Development EUI here as indivudal bytes replacing each XX pair 8 | uint8_t appEui[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX }; //#Insert you Application EUI here as indivudal bytes replacing each XX pair 9 | uint8_t appKey[] = { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX }; //#Insert you Application Key here as indivudal bytes replacing each XX pair 10 | 11 | bool ENABLE_SERIAL = true; // enable serial debug output here if required 12 | uint32_t appTxDutyCycle = 100000; // the frequency of readings, in milliseconds(set 100s) 13 | 14 | uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 }; 15 | LoRaMacRegion_t loraWanRegion = ACTIVE_REGION; 16 | DeviceClass_t loraWanClass = LORAWAN_CLASS; 17 | bool overTheAirActivation = LORAWAN_NETMODE; 18 | bool loraWanAdr = LORAWAN_ADR; 19 | bool keepNet = LORAWAN_NET_RESERVE; 20 | bool isTxConfirmed = LORAWAN_UPLINKMODE; 21 | uint8_t appPort = 2; 22 | uint8_t confirmedNbTrials = 4; 23 | 24 | int temperature, humidity, batteryVoltage, batteryLevel; 25 | long pressure; 26 | 27 | BME280 bme280; 28 | 29 | static void prepareTxFrame( uint8_t port ) 30 | { 31 | // This enables the output to power the sensor 32 | pinMode(Vext, OUTPUT); 33 | digitalWrite(Vext, LOW); 34 | delay(500); 35 | 36 | if(!bme280.init()){ 37 | if(ENABLE_SERIAL){ 38 | Serial.println("Device error!"); 39 | } 40 | } 41 | 42 | // This delay is required to allow the sensor time to init 43 | delay(500); 44 | 45 | temperature = bme280.getTemperature() * 100; 46 | humidity = bme280.getHumidity(); 47 | pressure = bme280.getPressure(); 48 | 49 | Wire.end(); 50 | 51 | // Turn the power to the sensor off again 52 | digitalWrite(Vext, HIGH); 53 | 54 | batteryVoltage = getBatteryVoltage(); 55 | batteryLevel = (BoardGetBatteryLevel() / 254) * 100; 56 | 57 | appDataSize = 12; 58 | appData[0] = highByte(temperature); 59 | appData[1] = lowByte(temperature); 60 | 61 | appData[2] = highByte(humidity); 62 | appData[3] = lowByte(humidity); 63 | 64 | appData[4] = (byte) ((pressure & 0xFF000000) >> 24 ); 65 | appData[5] = (byte) ((pressure & 0x00FF0000) >> 16 ); 66 | appData[6] = (byte) ((pressure & 0x0000FF00) >> 8 ); 67 | appData[7] = (byte) ((pressure & 0X000000FF) ); 68 | 69 | appData[8] = highByte(batteryVoltage); 70 | appData[9] = lowByte(batteryVoltage); 71 | 72 | appData[10] = highByte(batteryLevel); 73 | appData[11] = lowByte(batteryLevel); 74 | 75 | if(ENABLE_SERIAL){ 76 | Serial.print("Temperature: "); 77 | Serial.print(temperature / 100); 78 | Serial.print("C, Humidity: "); 79 | Serial.print(humidity); 80 | Serial.print("%, Pressure: "); 81 | Serial.print(pressure / 100); 82 | Serial.print(" mbar, Battery Voltage: "); 83 | Serial.print(batteryVoltage); 84 | Serial.print(" mV, Battery Level: "); 85 | Serial.print(batteryLevel); 86 | Serial.println(" %"); 87 | } 88 | } 89 | 90 | void setup() 91 | { 92 | 93 | boardInitMcu(); 94 | if(ENABLE_SERIAL){ 95 | Serial.begin(115200); 96 | } 97 | deviceState = DEVICE_STATE_INIT; 98 | LoRaWAN.ifskipjoin(); 99 | 100 | } 101 | 102 | void loop() 103 | { 104 | switch( deviceState ) 105 | { 106 | case DEVICE_STATE_INIT: 107 | { 108 | printDevParam(); 109 | LoRaWAN.init(loraWanClass,loraWanRegion); 110 | deviceState = DEVICE_STATE_JOIN; 111 | break; 112 | } 113 | case DEVICE_STATE_JOIN: 114 | { 115 | LoRaWAN.join(); 116 | break; 117 | } 118 | case DEVICE_STATE_SEND: 119 | { 120 | prepareTxFrame( appPort ); 121 | LoRaWAN.send(); 122 | deviceState = DEVICE_STATE_CYCLE; 123 | break; 124 | } 125 | case DEVICE_STATE_CYCLE: 126 | { 127 | // Schedule next packet transmission 128 | txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND ); 129 | LoRaWAN.cycle(txDutyCycleTime); 130 | deviceState = DEVICE_STATE_SLEEP; 131 | break; 132 | } 133 | case DEVICE_STATE_SLEEP: 134 | { 135 | LoRaWAN.sleep(); 136 | break; 137 | } 138 | default: 139 | { 140 | deviceState = DEVICE_STATE_INIT; 141 | break; 142 | } 143 | } 144 | } 145 | --------------------------------------------------------------------------------