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.

../_images/setup2.jpg

Sensor node in ABS Waterproof case.

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.

../_images/shield_wiring.jpg

Solar shield connections with the solar panel and a battery.

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.

../_images/short.jpg

Short R7 using a 0 ohm resistor for battery voltage measurement.

Final hardware setup looked as following:

../_images/hardware2.png

Final hardware wiring setup.

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.

Modify the keys in highlighted lines.
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:

Code for measuring the battery voltage
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

Arduino Sketch for Solar powered Seeeduino sensor node
  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)
}
Node red flow for Solar powered Seeeduino sensor node
  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"
    }
]