Build a simple solar-powered weather station with LoRa & The Things Stack (part 1)

Please use the thread below to discuss the related blog post:

1 Like

Hello, can you tell me how to decode the code so I can use it in Cayenne?
I read there manual how to do that but I’m stuck on it now.
Hope someone can help me out.

Regards Reinier

Hello @reivis welcome to the balena forums! And happy to see you interested on trying this project!

I’m sure you can use it on Cayenne. I see several possibilities:

  1. Change the payload to Cayenne LLP. To this, you should update the Arduino sketch to prepare data into Cayenne format (e.g. CayenneLPP | The Things Network) . Maybe you can PR a new sketch for the project?
  2. Make an integration using webhooks or MQTT with Cayenne, once data is received and decoded.

Does it make sense to you? Let us know if we can help you more :slight_smile:

Hello, thank you for presenting this interesting project. Certainly a beginner’s question: Could additional sensors be integrated? For example one (or more) DS18b20, (or soil moisture sensor)? Can you even have 2 libraries for temperature? And if so, how?

1 Like

Hello @J_Paul welcome to the balena forums :slight_smile:

I’m not an expert on the Heltec Cubecell devices but i’m sure that you can find examples, such as this one here.

Let us know if that works!

Still stuck on how to convert the sketch to CayenneLPP. I have installed the CayenneLPP libary in the Arduino IDE and than…?

Hello @reivis did you adapt the code to something like this? CayenneLPP | The Things Network

let me know what errors do you get!

I don’t get any errors because I don’t know what steps I must take to convert the code in Arduino IDE.
I have installed the LPP libary and then …???

@reivis could you please share your Arduino code?

#include <LoRaWan_APP.h>
#include <Arduino.h>
#include <Seeed_BME280.h>
#include <Wire.h>
#include “ttnparams.h”

bool ENABLE_SERIAL = false; // enable serial debug output here if required
uint32_t appTxDutyCycle = 1200000; // the frequency of readings, in milliseconds(set 300s)

uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;
DeviceClass_t loraWanClass = LORAWAN_CLASS;
bool overTheAirActivation = LORAWAN_NETMODE;
bool loraWanAdr = LORAWAN_ADR;
bool keepNet = LORAWAN_NET_RESERVE;
bool isTxConfirmed = LORAWAN_UPLINKMODE;
uint8_t appPort = 2;
uint8_t confirmedNbTrials = 4;

int temperature, humidity, batteryVoltage, batteryLevel;
long pressure;

BME280 bme280;

static void prepareTxFrame( uint8_t port )
{
// This enables the output to power the sensor
pinMode(Vext, OUTPUT);
digitalWrite(Vext, LOW);
delay(500);

if(!bme280.init()){
if(ENABLE_SERIAL){
Serial.println(“Device error!”);
}
}

// This delay is required to allow the sensor time to init
delay(500);

temperature = bme280.getTemperature() * 100;
humidity = bme280.getHumidity();
pressure = bme280.getPressure();

Wire.end();

// Turn the power to the sensor off again
digitalWrite(Vext, HIGH);

batteryVoltage = getBatteryVoltage();
batteryLevel = (BoardGetBatteryLevel() / 254) * 100;

appDataSize = 12;
appData[0] = highByte(temperature);
appData[1] = lowByte(temperature);

appData[2] = highByte(humidity);
appData[3] = lowByte(humidity);

appData[4] = (byte) ((pressure & 0xFF000000) >> 24 );
appData[5] = (byte) ((pressure & 0x00FF0000) >> 16 );
appData[6] = (byte) ((pressure & 0x0000FF00) >> 8 );
appData[7] = (byte) ((pressure & 0X000000FF) );

appData[8] = highByte(batteryVoltage);
appData[9] = lowByte(batteryVoltage);

appData[10] = highByte(batteryLevel);
appData[11] = lowByte(batteryLevel);

if(ENABLE_SERIAL){
Serial.print("Temperature: ");
Serial.print(temperature / 100);
Serial.print(“C, Humidity: “);
Serial.print(humidity);
Serial.print(”%, Pressure: “);
Serial.print(pressure / 100);
Serial.print(” mbar, Battery Voltage: “);
Serial.print(batteryVoltage);
Serial.print(” mV, Battery Level: “);
Serial.print(batteryLevel);
Serial.println(” %”);
}
}

void setup()
{

boardInitMcu();
if(ENABLE_SERIAL){
Serial.begin(115200);
}
deviceState = DEVICE_STATE_INIT;
LoRaWAN.ifskipjoin();

}

void loop()
{
switch( deviceState )
{
case DEVICE_STATE_INIT:
{
printDevParam();
LoRaWAN.init(loraWanClass,loraWanRegion);
deviceState = DEVICE_STATE_JOIN;
break;
}
case DEVICE_STATE_JOIN:
{
LoRaWAN.join();
break;
}
case DEVICE_STATE_SEND:
{
prepareTxFrame( appPort );
LoRaWAN.send();
deviceState = DEVICE_STATE_CYCLE;
break;
}
case DEVICE_STATE_CYCLE:
{
// Schedule next packet transmission
txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );
LoRaWAN.cycle(txDutyCycleTime);
deviceState = DEVICE_STATE_SLEEP;
break;
}
case DEVICE_STATE_SLEEP:
{
LoRaWAN.sleep();
break;
}
default:
{
deviceState = DEVICE_STATE_INIT;
break;
}
}
}

Hello @reivis you should add the Cayenne Library (Cayenne LPP). More information here CayenneLPP | The Things Stack for LoRaWAN

Here you have an example Cayenne_encoding - #20 by naidu.hemanth9 - Helped - myDevices Cayenne Community

And here more information about the Cayenne Payload Cayenne Docs

Hi,

I have just a quick question. I have successfully built a weather station according to this guide. Working fine so far. I just stumbled over the battery level. It just shows 0 or 1 (when fully loaded) but is supposed to show a percentage …

I did not alter anything at the script nor the payload… Any hints?

1 Like

Hello @fhansi welcome to the balena.io community!

could you please confirm the value when the battery it’s not fully loaded? i think what is on Grafana is a visualization of the percentage of this value, but i’m not totally sure.

Let me know if this makes sense :slight_smile:

hi,

well in the payload I see this:

“decoded_payload”: {
“battery”: 4.16,
“battery_level”: 0,
“humidity”: 34,
“pressure”: 1005.04,
“temperature”: 12.87
},

I do not visualize via grafana…

Frank

@fhansi the source for this information is the firmware on the Heltec board. It’s returned as a value from the BoardGetBatteryLevel() function, then added to the LoRA packet along with all the other data.

You’d have to figure out where it’s getting lost, or even if it’s being reported correctly to begin with. I’d suggest starting with adding some debug lines to the Arduino to print the output to the log. You could also figure out the percentage yourself using the battery voltage levels based on a table like this one: Lipo Voltage Chart: Show the Relationship of Voltage and Capacity - Ampow Blog

1 Like

Hello Chris, great project that I would like to replicate. But I have one more question:
Is it also possible to transfer the CubeCell data via Helium and then display it in UBIDOTS in the Helium Console in the widgets?
What do I have to change for this?

Many thanks and best regards Ulli

1 Like

Hello @4711engel yes! that should be possible without any issue!

Just create the device on the Helium console that you preffer and create the flow to send data to Ubidots. You can find here an example → Connect a Seeed Soil and Temperature sensor with your balena Helium Data-Only Hotspot on Datacake

Hello, I would like to expand my small LoRawan helium weather station. Currently two sensors (LHT65 and an IKEA air quality sensor hack) send data via helium mesh and are displayed with UBIDOTS in widgets.
Now I would also like to measure and output the wind speed with an anemometer. But since I’m not a programmer, I’m looking for a sketch for the CubeCell and a data decoder. Who can help me?

Here is the sketch for the Arduino IDE, where I do the measurement and OLED output locally with an Arduino UNO:

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

const int RecordTime = 3; //Define Measuring Time (Seconds)
const int SensorPin = 3;  //Define Interrupt Pin (2 or 3 @ Arduino Uno)

int InterruptCounter;
float WindSpeed;

void setup()
{
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
}

void loop() 
{
  meassure();
  Serial.print("Wind Speed: ");
  Serial.print(WindSpeed);       //Speed in km/h
  Serial.print(" km/h - ");
  Serial.print(WindSpeed / 3.6); //Speed in m/s
  Serial.println(" m/s");

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 10);
  display.println("Wind Speed: ");
  display.setCursor(0, 20);
  display.println(WindSpeed);
  display.setCursor(50, 20);
  display.println(" km/h ");
  display.setCursor(0, 40);  
  display.println(WindSpeed / 3.6);
  display.setCursor(50, 40);  
  display.println(" m/s");
  display.display(); 
}

void meassure() {
  InterruptCounter = 0;
  attachInterrupt(digitalPinToInterrupt(SensorPin), countup, RISING);
  delay(1000 * RecordTime);
  detachInterrupt(digitalPinToInterrupt(SensorPin));
  WindSpeed = (float)InterruptCounter / (float)RecordTime * 2.4;
}

void countup() {
  InterruptCounter++;
}

Hello @4711engel some recomendation is to use the Cayenne LPP data format. Find here more information Cayenne Low Power Payload | myDevices Documentation

Having said that, if you use the Cubecell find here an example to send data using LoRa → CubeCell-Arduino/LoRaWan.ino at master · HelTecAutomation/CubeCell-Arduino · GitHub

if you use the Arduino UNO, get a hat with WiFi, cellular, LoRa connectivity and try to send data to your IoT platform.

Let me know if that works :slight_smile:

Hallo Chris, kannst du mir vielleicht helfen, wie ich den Payload Decoder in TTN erstellen muss, damit ich ĂĽber einen Webhook die Daten in Thingspeak darstellen kann.

Ich hatte einen Decoder gefunden:

function Decoder(b, port) {
var var1 = b[0];
var var2 = b[1];
var var3 = b[2];
var var4 = b[3];
var var5 = (b[4] << 8) | b[5];
var var6 = (b[6] << 8) | b[7];
var var7 = (b[8] << 8) | b[9];
var var8 = (b[10] << 8) | b[11];
var lat = (b[12] | b[13]<<8 | b[14]<<16 | (b[14] & 0x80 ? 0xFF<<24 : 0)) / 10000;
var lon = (b[15] | b[16]<<8 | b[17]<<16 | (b[17] & 0x80 ? 0xFF<<24 : 0)) / 10000;
var height = b[18];
var myStatus = b[19];

return {
field1: var1,
field2: var2,
field3: var3,
field4: var4,
field5: var5,
field6: var6,
field7: var7,
field8: var8,

latitude: lat,
longitude: lon,
elevation: height

}
}

Wie muss ich diesen anpassen?

In TTN sieht die Payload so aus:

payload: { Supply_Voltage: 3912, TH_Sensor_Humidity: “61”, TH_Sensor_Temperature: “23.4”,
TX_Reason: “App_Cycle_Event”, Temperature_Sensor: “22.5” }

Vielen Dank