Solar powered Seeeduino¶
This sensor node is made to showcase a use-case of LoRaWAN sensor node powered using a solar panel. For achieving this a Seeeduino LoRaWAN microcontroller was used along with a solar panel connected using a solar shield. To show a generic use-case we have used a temperature and humidity sensor in this case, but it can be easily replaced with some other sensor as well. The entire setup was carefully placed in the ABS Waterproof case which is an easy to install water-proof and dust-proof case for an indoor or outdoor sensor installations. However, this case has no provision for the ventilation unlike the TFA case and so the readings obtained by the sensor may not accurately represent the outdoor weather conditions. In this example, we measure parameters such as temperature, humidity, and battery voltage.
Hardware¶
To build this sensor node we have used following hardware components:
Wiring setup¶
First of all, the solar panel is connected with the SOLAR pin and a battery is connected with a BAT pin on the solar charger shield as shown in the figure below. A DHT-22 Sensor is connected to A2 pin on the Seeeduino board using a connector cable and then the solar charger shield prepared in the previous step is mounted on the board.
Apart from this, to measure the voltage of Lipo Battery we need to connect the VBAT pin to Analog pin A0, so that we can read the data from A0 pin. To achieve this, we need to Short R7 using a 0ohm resistor as shown in the figure here.
Final hardware setup looked as following:
Once all these connection were made, the board is connected with a computer using a USB cable. Further, steps of software part needs to be followed.
Software¶
To create this node, we use Arduino IDE for setting up the Seeeduino LoRaWAN device. First, install the Seeeduino LoRaWAN board to your Arduino IDE and select the correct port. Then following libraries needs to be installed before compiling the code:
- Wire.h to communicate with I2C devices.
- DHT.h for reading DHT-22 sensor.
- RTCZero.h for controlling internal clock for time.
- CayenneLPP.h for Cayenne Protocol.
Apart from this LoRaWan.h library is also used but it is bundled within Seeeduino Board and is not required to be separately installed.
Now download and run the Arduino Sketch for Solar powered Seeeduino sensor node file in the Arduino IDE. This code was created by merging the example code of both the sensors and the ttn-otaa example from the lmic library. Some required changes were made while merging the example codes. The user should change the network session key, app session key and device address in the code before compiling. These keys can be obtained from the TTN, SWM or other service providers.
1 2 3 4 5 6 7 8 9 | // The EUIs and the AppKey must be given in big-endian format, i.e. the
// most-significant-byte comes first (as displayed in the TTN console).
// For TTN issued AppEUIs the first bytes should be 0x70, 0xB3, 0xD5.
// void setId(char *DevAddr, char *DevEUI, char *AppEUI);
lora.setId(NULL, "00942FBXXXXXXXXX", "70B3D57XXXXXXXXX");
// setKey(char *NwkSKey, char *AppSKey, char *AppKey);
lora.setKey(NULL, NULL, "CB89A0AA43F6C5XXXXXXXXXXXXXXXXXX");
|
Following is the example code that can be used to measure the battery voltage of the Seeed solar charger shield:
1 2 3 4 5 6 7 8 9 | BatteryValue = analogRead(analogInPin);
// Calculate the battery voltage value
outputValue = (float(BatteryValue)*5)/1023*2;
// print the results to the serial monitor:
SerialUSB.print("Analog value = " );
SerialUSB.print(BatteryValue);
SerialUSB.print("\t voltage = ");
SerialUSB.println(outputValue);
SerialUSB.println("V \n");
|
Services¶
This node is connected using the TheThingsNetwork service. Further, a node-red work bench is used to forward this collected data from the TTN platform to the OGC Sensor Things API configured on the FROST Server. The node-red workbench that was used for forwarding the data is available at Node red flow for Solar powered Seeeduino sensor node for Solar powered Seeeduino. To use this node-red-workbench go to the node-red platform https://iot.gis.bgu.tum.de:1885/, login with the credentials, go to the options and select Import>Clipboard. Select the downloaded .json file with the given option and click on import. Make necessary changes and deploy the flow.
Datastreams setup for this sensor node on the FROST server can be seen at: http://iot.gis.bgu.tum.de:8081/FROST-Server-gi3/v1.0/Things(19)/Datastreams
The node-red workbench for this sensor node could be found at: https://iot.gis.bgu.tum.de:1885/#flow/58838bc1.4ce6a4
The GRAFANA dash-board for visualizing the collected data is available at: https://iot.gis.bgu.tum.de:3050/d/TfCVFRNWz/solar-powered-seeeduino-with-dht22?orgId=1&refresh=10s
Code files¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | #include <DHT.h>
#include <RTCZero.h>
#include <LoRaWan.h>
#include <Wire.h>
#include <CayenneLPP.h>
// Keep the following line, if the board is a Seeeduino LoRaWAN with GPS,
// otherwise comment the line out
// #define HAS_GPS 1
const int analogInPin = A0;
#define DHTPIN A2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
int BatteryValue = 0;
float outputValue = 0;
RTCZero rtc;
char buffer[256]; // buffer for text messages received from the LoRaWAN module for display
CayenneLPP lpp(51);
void setup(void)
{
digitalWrite(38, HIGH); // Provide power to the 4 Grove connectors of the board
for(int i = 0; i < 26; i ++) // Set all pins to HIGH to save power (reduces the
{ // current drawn during deep sleep by around 0.7mA).
if (i!=13) { // Don't switch on the onboard user LED (pin 13).
pinMode(i, OUTPUT);
digitalWrite(i, HIGH);
}
}
delay(5000); // Wait 5 secs after reset/booting to give time for potential upload
dht.begin(); // of a new sketch (sketches cannot be uploaded when in sleep mode)
SerialUSB.begin(115200); // Initialize USB/serial connection
delay(500);
// while(!SerialUSB);
SerialUSB.println("Seeeduino LoRaWAN board started!");
// nrgSave.begin(WAKE_RTC_ALARM);
// rtc.begin(TIME_H24);
#ifdef HAS_GPS
Serial.begin(9600); // Initialize serial connection to the GPS module
delay(500);
Serial.write("$PMTK161,0*28\r\n"); // Switch GPS module to standby mode as we don't use it in this sketch
#endif
lora.init(); // Initialize the LoRaWAN module
memset(buffer, 0, 256); // clear text buffer
lora.getVersion(buffer, 256, 1);
memset(buffer, 0, 256); // We call getVersion() two times, because after a reset the LoRaWAN module can be
lora.getVersion(buffer, 256, 1); // in sleep mode and then the first call only wakes it up and will not be performed.
SerialUSB.print(buffer);
memset(buffer, 0, 256);
lora.getId(buffer, 256, 1);
SerialUSB.print(buffer);
// The following three constants (AppEUI, DevEUI, AppKey) must be changed
// for every new sensor node. We are using the LoRaWAN OTAA mode (over the
// air activation). Each sensor node must be manually registered in the
// TTN console at https://console.thethingsnetwork.org before it can be
// started. In the TTN console create a new device with the DevEUI also
// being automatically generated. After the registration of the device the
// three values can be copied from the TTN console. A detailed explanation
// of these steps is given in
// https://learn.adafruit.com/the-things-network-for-feather?view=all
// The EUIs and the AppKey must be given in big-endian format, i.e. the
// most-significant-byte comes first (as displayed in the TTN console).
// For TTN issued AppEUIs the first bytes should be 0x70, 0xB3, 0xD5.
// void setId(char *DevAddr, char *DevEUI, char *AppEUI);
lora.setId(NULL, "00942FBXXXXXXXXX", "70B3D57XXXXXXXXX");
// setKey(char *NwkSKey, char *AppSKey, char *AppKey);
lora.setKey(NULL, NULL, "CB89A0AA43F6C5XXXXXXXXXXXXXXXXXX");
lora.setDeciveMode(LWOTAA); // select OTAA join mode (note that setDeciveMode is not a typo; it is misspelled in the library)
// lora.setDataRate(DR5, EU868); // SF7, 125 kbps (highest data rate)
lora.setDataRate(DR3, EU868); // SF9, 125 kbps (medium data rate and range)
// lora.setDataRate(DR0, EU868); // SF12, 125 kbps (lowest data rate, highest max. distance)
// lora.setAdaptiveDataRate(false);
lora.setAdaptiveDataRate(true); // automatically adapt the data rate
lora.setChannel(0, 868.1);
lora.setChannel(1, 868.3);
lora.setChannel(2, 868.5);
lora.setChannel(3, 867.1);
lora.setChannel(4, 867.3);
lora.setChannel(5, 867.5);
lora.setChannel(6, 867.7);
lora.setChannel(7, 867.9);
// The following two commands can be left commented out;
// TTN works with the default values. (It also works when
// uncommenting the commands, though.)
// lora.setReceiceWindowFirst(0, 868.1);
// lora.setReceiceWindowSecond(869.525, DR0);
lora.setDutyCycle(false); // for debugging purposes only - should normally be activated
lora.setJoinDutyCycle(false); // for debugging purposes only - should normally be activated
lora.setPower(14); // LoRa transceiver power (14 is the maximum for the 868 MHz band)
// while(!lora.setOTAAJoin(JOIN));
while(!lora.setOTAAJoin(JOIN,20)); // wait until the node has successfully joined TTN
lora.setPort(33); // all data packets are sent to LoRaWAN port 33
}
void loop(void)
{
bool result = false;
float temp_hum_val[2] = {0};
float temperature, humidity;
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
if(!dht.readTempAndHumidity(temp_hum_val)){
SerialUSB.print("Humidity: ");
SerialUSB.print(humidity = temp_hum_val[0]);
SerialUSB.print(" %\t");
SerialUSB.print("Temperature: ");
SerialUSB.print(temperature = temp_hum_val[1]);
SerialUSB.println(" *C");
}
else{
SerialUSB.println("Failed to get temprature and humidity value.");
}
BatteryValue = analogRead(analogInPin);
// Calculate the battery voltage value
outputValue = (float(BatteryValue)*5)/1023*2;
// print the results to the serial monitor:
SerialUSB.print("Analog value = " );
SerialUSB.print(BatteryValue);
SerialUSB.print("\t voltage = ");
SerialUSB.println(outputValue);
SerialUSB.println("V \n");
SerialUSB.println("-- LOOP");
lpp.reset();
lpp.addTemperature(1, temperature);
lpp.addRelativeHumidity(2, humidity);
lpp.addAnalogInput(3, outputValue);
result = lora.transferPacket(lpp.getBuffer(), lpp.getSize(), 5); // send the data packet (n byts) with a default timeout of 5 secs
if(result)
{
short length;
short rssi;
memset(buffer, 0, 256);
length = lora.receivePacket(buffer, 256, &rssi);
if(length)
{
SerialUSB.print("Length is: ");
SerialUSB.println(length);
SerialUSB.print("RSSI is: ");
SerialUSB.println(rssi);
SerialUSB.print("Data is: ");
for(unsigned char i = 0; i < length; i ++)
{
SerialUSB.print("0x");
SerialUSB.print(buffer[i], HEX);
SerialUSB.print(" ");
}
SerialUSB.println();
}
}
lora.setDeviceLowPower(); // bring the LoRaWAN module to sleep mode
doSleep((5*60-8)*1000); // deep sleep for 292 secs (+ 3 secs transmission time + 5 secs timeout = 300 secs period)
lora.setPort(33); // send some command to wake up the LoRaWAN module again
}
// The following function implements deep sleep waiting. When being called the
// CPU goes into deep sleep mode (for power saving). It is woken up again by
// the CPU-internal real time clock (RTC) after the configured time.
//
// A similar function would also be available in the standard "ArduinoLowPower" library.
// However, in order to be able to use that library with the Seeeduino LoRaWAN board,
// four files in the package "Seeed SAMD boards by Seeed Studio Version 1.3.0" that is
// installed using the Arduino IDE board manager need to be patched. The reason is that
// Seeed Studio have not updated their files to a recent Arduino SAMD version yet
// and the official "ArduinoLowPower" library provided by the Arduino foundation is
// referring to some missing functions. For further information see here:
// https://forum.arduino.cc/index.php?topic=603900.0 and here:
// https://github.com/arduino/ArduinoCore-samd/commit/b9ac48c782ca4b82ffd7e65bf2c956152386d82b
void doSleep(uint32_t millis) {
if (!rtc.isConfigured()) { // if called for the first time,
rtc.begin(false); // then initialize the real time clock (RTC)
}
uint32_t now = rtc.getEpoch();
rtc.setAlarmEpoch(now + millis/1000);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.standbyMode(); // bring CPU into deep sleep mode (until woken up by the RTC)
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | [
{
"id": "58838bc1.4ce6a4",
"type": "tab",
"label": "Device1",
"disabled": false,
"info": ""
},
{
"id": "daeb7602.698d18",
"type": "switch",
"z": "58838bc1.4ce6a4",
"name": "Separate",
"property": "key",
"propertyType": "msg",
"rules": [
{
"t": "cont",
"v": "temperature",
"vt": "str"
},
{
"t": "cont",
"v": "humidity",
"vt": "str"
},
{
"t": "cont",
"v": "analog",
"vt": "str"
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 4,
"x": 220,
"y": 180,
"wires": [
[
"a3a522a5.a81a9"
],
[
"367717e8.191318"
],
[
"466fd2c5.586efc"
],
[]
]
},
{
"id": "e2798231.c9314",
"type": "split",
"z": "58838bc1.4ce6a4",
"name": "",
"splt": "\\n",
"spltType": "str",
"arraySplt": 1,
"arraySpltType": "len",
"stream": false,
"addname": "key",
"x": 90,
"y": 180,
"wires": [
[
"daeb7602.698d18"
]
]
},
{
"id": "5c3e3ed9.0b4dd",
"type": "debug",
"z": "58838bc1.4ce6a4",
"name": "",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 810,
"y": 180,
"wires": []
},
{
"id": "367717e8.191318",
"type": "function",
"z": "58838bc1.4ce6a4",
"name": "Humidity",
"func": "var humValue = msg.payload.valueOf();\nvar newMessage = { payload: { \"result\": humValue, \"Datastream\": {\"@iot.id\": 102}} };\nnewMessage.headers = {\"Content-type\" : \"application/json\"}\nreturn newMessage;",
"outputs": 1,
"noerr": 0,
"x": 440,
"y": 200,
"wires": [
[
"c777922b.84784"
]
]
},
{
"id": "c777922b.84784",
"type": "http request",
"z": "58838bc1.4ce6a4",
"name": "POST Observation",
"method": "POST",
"ret": "obj",
"paytoqs": false,
"url": "http://iot.gis.bgu.tum.de:8081/FROST-Server-gi3/v1.0/Observations",
"tls": "",
"proxy": "",
"authType": "basic",
"x": 630,
"y": 180,
"wires": [
[
"5c3e3ed9.0b4dd"
]
]
},
{
"id": "a3a522a5.a81a9",
"type": "function",
"z": "58838bc1.4ce6a4",
"name": "Temperature",
"func": "var tempValue = msg.payload.valueOf();\nvar newMessage = { payload: { \"result\": tempValue, \"Datastream\": {\"@iot.id\": 101}} };\nnewMessage.headers = {\"Content-type\" : \"application/json\"}\nreturn newMessage;",
"outputs": 1,
"noerr": 0,
"x": 450,
"y": 160,
"wires": [
[
"c777922b.84784"
]
]
},
{
"id": "41ae6239.73f9bc",
"type": "ttn uplink",
"z": "58838bc1.4ce6a4",
"name": "TTN Input",
"app": "58ceff1f.8576a",
"dev_id": "tum-gis-device1",
"field": "",
"x": 80,
"y": 60,
"wires": [
[
"491bb4da.0eb58c"
]
]
},
{
"id": "491bb4da.0eb58c",
"type": "cayennelpp-decoder",
"z": "58838bc1.4ce6a4",
"name": "",
"x": 280,
"y": 60,
"wires": [
[
"e2798231.c9314",
"f2d3534b.0f44f"
]
]
},
{
"id": "466fd2c5.586efc",
"type": "function",
"z": "58838bc1.4ce6a4",
"name": "Battery Voltage",
"func": "var batteryvolt = msg.payload.valueOf();\nvar newMessage = { payload: { \"result\": batteryvolt, \"Datastream\": {\"@iot.id\": 104}} };\nnewMessage.headers = {\"Content-type\" : \"application/json\"}\nreturn newMessage;",
"outputs": 1,
"noerr": 0,
"x": 440,
"y": 240,
"wires": [
[
"c777922b.84784"
]
]
},
{
"id": "f2d3534b.0f44f",
"type": "debug",
"z": "58838bc1.4ce6a4",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"x": 490,
"y": 60,
"wires": []
},
{
"id": "58ceff1f.8576a",
"type": "ttn app",
"z": "",
"appId": "gis-tum-sensors",
"accessKey": "ttn-account-ACCESSKEY_HERE",
"discovery": "discovery.thethingsnetwork.org:1900"
}
]
|