Our team was led to rub shoulders with beekeepers and after few exchanges some issues appeared. Indeed, beekeepers do not always have the time to check their beehives or the weather might nt be suitable (heavy rain, strong wind) for checking.
Thus, they asked us if there is a way to remotely monitor their beehives. They gave us the technical specifications we would have to meet and we started to design a solution for them.
So, our solution lie in a monitored beehive with an Arduino Nano. We linked to this board several sensors mesuring :
- the temperature inside and outside the beehive
- the beehive's weight
- the battery of the system
- the humidity inside and outside the beehive
These data are then uploaded to Ubidots, a cloud interface allowing beekeepers to consult the data and the state of the beehive.
When near the beehive, they can also connect to the board thanks to a bluetooth android application.
First step : data acquisitionOf course when it comes to environment monitoring, the first thing that comes to mind is data acquisition. Which data do we plan to measure ? How do we measure it ? How precisely ?
So based on the specifications we started to make a component list with everything we will need. We made sure to select well known components with great community support for easier and faster problem solving.
For starters, for the brain of the operation we turned to an Arduino Nano 33 BLE microcontroller which provides the best software support, with easy programming and a lot of libraries, as well as great features like Bluetooth Low Energy and a low battery consumption.
Then for the actual sensors we went with a DHT22 for temperature and humidity reading outside the beehive, but also a DHT11 for temperature and humidity reading inside the beehive. Both are very similar, the DHT22 just being an upgraded version of the DHT11 with a wider range and precision. In our case we initially planned to use two DHT22 but we could only get one due to it being out of stock so we substituted the missing DHT22 with a DHT11.
We also choose two DS18B20 as our temperature probes, simply because they fit our needs and are really convenient to put in a beehive.
Next, for the weight we were provided with a load cell so we simply had to pick a load cell amplifier and ADC, like the HX711 Grove form Seeed.
Finally, for light sensing, we choose a simple and affordable solution. We used a photoresistor along with a voltage divider. For the battery, we just used another voltage divider, as it's voltage can go up to 4.1V whereas the Arduino can only take inputs up to 3.3V.
Breadboard testingWe started testing every sensor individually. We downloaded libraries made by the Arduino community from each sensors and simply used them to print the sensor output on the serial console.This part was relatively easy due to most libraries being very user friendly. The most complicated part was working with the weight sensor as it requires some calibration.
Bluetooth Low Energy : a poorly documented mazeThen we began working on the BLE, which would allow us to monitor and test the sensors wirelessly from a phone. Working with BLE using the Arduino BLE library was fairly easy, as BLE is not so complicated to understand. You've got services which regroups characteristics and each characteristics can have one or more descriptors. If you want to send a data, you create a characteristic, which will hold the actual value for that data, and you assign a descriptor to the characteristic which will tell the remote device how the data is supposed to be interpreted. Then you assign all your characteristics to a service.
That looks easy on paper but when you want to do things correctly, it get more complex. The thing is that if you know how you send the data and use a custom app to retrieve it, you don't have to worry about which format you're sending the data. But if you actually want to follow the BLE standard, you will have to find how UUIDs works, every UUID has a meaning.
Services have an UUID to tell which type of service it is and characteristics have an UUID to tell which type of data it is (temperature, humidity, voltage, power, etc). All the information on which UUID to use can be found here, with Gatt Service listing all the standard services UUIDs and Gatt Characteristics listing all the standard characteristics UUIDs.
Finally, the descriptor's value is used to tell the device receiving the data, how it should interpret the characteristic's data (what is the unit ? is there an exponent ? where is the sensor located ?). Every characteristic has it's own descriptor value and the documentation for this value is not available for every characteristic. We have to search through archives and Github pages to find old documentation about the value of rarely used characteristics like light sensing.
To save you some time, you can find all the standard services and characteristics UUIDs in the BLE documentation here and there. You can also find most of the descriptors value in this Github repository or by search for there name "org.bluetooth.chracteristic.XXX" on Google (in fact the official documentation XMLs are still hosted on the Bluetooth official website, just not linked anywhere).
BLE is finally hereAfter a lot of trouble, we finally made it happen and had a service with a correct name, showing up characteristics with correct name and units ! Let's see how me did it using the Arduino BLE library. We will not go through every single step, like enabling BLE or starting to advertise but only the most complex part, creating the service, characteristics and descriptors.
First, we start by making a service :
BLEService BLEServiceUBeeZ("181A");
Here, the UUID 181A means that it is an "Environmental Sensing" service (as seen here on page 21).
Then we create our characteristics with their descriptor, let's take for example a temperature characteristic.
We create the characteristic :
BLEShortCharacteristic BLECharacteristicTemperature = BLEShortCharacteristic("00002A6E-0000-1000-8000-00805F9B34FB", BLERead | BLENotify)
It's a Short characteristic meaning that the data will be on 2 bytes, we will see why we choose that later.
Then, the UUID can be looked at like the combinaison of a "base" 128bit UUID : 0000xxxx-0000-1000-8000-00805F9B34FB, with the xxxx being a standard 16 bit UUID.Here the 2A6E refers to a temperature characteristic (as seen here on page 14).Finally, we have the BLERead | BLENotify which indicates that the data can only be read by a remote device and that this device will be notified when the data value changes.
Now, we have to create the descriptor for the temperature, with its value.
uint8_t descriptorTemperature[7] = {0x0E, (uint8_t)(int8_t)-2, 0x2F, 0x27, 0x01, 0x01, 0x00};
BLEDescriptor BLEDescriptorTemperature("2904", descriptorTemperature, 7);
The descriptor value is always 7 bytes so we create a 7 bytes array that we fill according to the documentation. We will also need the standard temperature descriptor values, which we can find here.
According to the second link, we know that the temperature should be sent as a 16 bit signed integer (that why we choose the BLEShortCharacteristic, because its 16 bits), in degrees Celsius and with a -2 exponent (meaning that if the temperature is 12.34 °C, we send 1234).
So for our 7 byte array, we have :
- 0x0E : signed 16-bit integer
- (uint8_t)(int8_t)-2 : decimal exponent -2
- 0x2F 0x27 : when we invert them (as the bytes are not sent in the same endian as the documentation was written in), we get 0x272F which corresponds to degrees Celsius (as seen in the documentation, page 23)
- 0x01 : means that we use the Bluetooth SIG Assigned Numbers standard
- 0x01 0x00 : when we invert them (for the same reason as before), we get 0x0001 which corresponds to "first", indicating that this is the first temperature sensor (as seen in the other documentation, page 3)
Finally, we assign everything, starting with the descriptor to the characteristic :
BLECharacteristicTemperature.addDescriptor(BLEDescriptorTemperature);
Then we assign the characteristic to the service :
BLEServiceUBeeZ.addCharacteristic(BLECharacteristicTemperature);
Later, when we want to actually send the data, we simply call :
BLECharacteristicTemperature.writeValue(temperature*100);
Not forgetting the x100 as the data is sent with a -2 exponent.
We repeat this process for every sensor, each with its own characteristic and descriptor, and assign every characteristic to the same service.
Sensors testing over the airWhen the BLE part was done, we hocked up every sensor and made a program combining all our simple individual testing of the sensors to make one big program which regrouped all sensors and sent their data over BLE.
We downloaded on our phone an app called nRF Connect which allows us to connect to BLE devices and see all their services, characteristics and descriptors.
We used it to connect to the Arduino and check that everything was fine :
We had the result that we wanted with all sensors showing their data in the correct format and with the correct values.
Furthermore, we spent some time putting all our code in order, splitting it in different files for easier debugging and we also spent a lot a time putting all our Bluetooth code on a separated thread. By doing so, we made sure that the BLE part or the program would not interfere with the rest of the program in any way.
We used RTOS features, as the Arduino nano 33 BLE runs Mbed OS, like Thread or EventQueue, feel free to check out the examples.
Getting the data from your homeEnough with Bluetooth, as you know its range is only a few meters and the main goal of the project was to send the data to the internet so they can be accessible from anywhere in the world, like from your home.
To send the data to the internet, we were provided with a small LPWAN module. It allows to send 12 bytes of data every 10 minutes (at least) from almost anywhere in France and using little power. It communicates with the Sigfox network, which is a cellular like network, and thus you don't even need Wifi near the hive !
The module in question is a small board, the Sigfox Breakout board BRKWS01. It communicates using simple AT commands (documented here) over a serial connection with the Arduino.
This solution was great, the only major disadvantage being the length of the payload being quite limited. To overcome this problem, we had to think about how we would send the data, how we would reduce its size, as one simple integer already takes 4 bytes.
Encoding the data for transmissionTo encode the data, we wondered what range of values did we need for each sensor and at which precision. For example, we wanted the temperature to be sent with a 0.1 °C precision but we only wanted a precision of 2% for the humidity. We also need to be able to send a negative temperature (as it gets pretty cold in France). So we came up with an Excel sheet containing the whole payload format.
A little note about GPS : in the beginning of the project we intended to use a GPS module to locate where the hive is. It didn't quite make it into the final project as we ran out of time but when we create the payload encoding model we thought of it and realized that a single payload was not enough to fit all the data (latitude and longitude both takes 19 bits to be precise on a map).
So what we did, was set the last bit of the payload to be an indicator or what type of payload is it, sensors or GPS. As you can see in the first picture above, the sensor payload has its last bit set to 0. We also have a payload with the last bit set to 1 which represent the GPS payload :
Finally, we changed the Arduino program so that it can send data every 10 minutes, encoding it according to the tables above and sending them with the Sigfox module.
Every message of 12 bytes is received and can be seen on the Sigfox backend website. We can create callbacks when a message is received to pass along the data to another third party.
The data's tripWe wanted to display the data on a nice, user friendly dashboard and we found that Ubidots would be a great fit for out project. The thing is that our data is still encoded and Ubidots only accept data in a raw format, moreover we need to decode the data differently depend on the last bit of the payload (depending if it's a sensors payload or a GPS payload) which is not possible to do directly on Ubidots. So we couldn't simply tell the Sigfox backend to send the data directly to Ubidots with a callback.
The solution we found was that, we can send the data from the Sigfox backend to an intermediate who will do the decoding, and then the intermediate will send the data to Ubidots. The intermediate we used was Microsoft Azure, we would have a diagram like this :
We choose Microsoft Azure for it's Azure Functions feature and because it was free for students.
Decoding the data on AzureFirst, we're going to assume that you have a few notions of how HTTP requests work. So to put it simple, a callback on the Sigfox backend is just making an HTTP request to an URL. You can pass data in the body of the request, for example in form of a JSON with a key containing the 12 bytes payload in hexadecimal.
Next, Azure Functions is a service that allows you to run code, in a few languages, when receiving an HTTP request on an endpoint. You can get the query parameters or the body content of the request as a function parameter in the code that will run. You can of course make HTTP request from this code, maybe you're starting to see the picture.
So we created a callback on the Sigfox backend which will make an HTTP request every time a payload is received from the Breakout module. This HTTP request will contain the message received by the Sigfox backend in its body and will trigger a code runing on Azure Functions. The code on Azure will then decode the payload bytes and put each sensors actual value in a JSON, with each sensor having its key. Finally, the JSON is sent as the body of an HTTP request to Ubidots.
Displaying the data on UbidotsOnce all the data was arriving correctly on Ubidots, we customized the dashboard and made it pretty and user friendly. All the data was displaying correctly so we were done with this part.
As we were getting done with our overall design, we started to focus on the energy effeciency of the system, as it was going to run on a LiPo battery and a solar panel.
We started with the battery being connected to the LiPo Rider Pro and the Arduino being connected to the 5V USB output of the LiPo Rider Pro. This was easy and convenient but really not energy effecient. Actually, a LiPo battery voltage is between 4.1V when charged, to 3V when discharged. This means that the LiPo Rider Pro has to step up the voltage to 5V. Then the Arduino, which runs on 3.3V, has to step down the voltage from 5V to 3.3V, losing even more power in the process.
This wasn't a viable option so we decided to keep the LiPo Rider Pro for its charging management feature with the solar panel, but decided to not use its output. Instead we decided that we would power everything with 3.3V.
We started by cuting a trace on the Arduino to disable its internal 5V to 3.3V regulator and to be able to power it directly with a 3.3V input, as mentioned in the documentation, page 2). All our other sensors were meant to used 3.3V so we didn't have to make any changes there.
Next, we connected the battery output to a 3.3V regulator's input and connected our arduino and sensors to the regulator's output. Once we've done that, we measured a consumption 10 times lower that before.
Finally, once we were done with hardware power optimisations, we turned to software. A few sensors had sleep modes availables so we simply used them and got to a consumption in idle state as low as 1.23 mA. During data transfert, we still got around 28.5mA but this happens every 10 minutes for 12 seconds so we were quite happy with what we got as according to our calculations, the battery could operate the system for 23 days without being charged.
Of course the system will be charged by the solar panel, but having a low consumption means that the system will be able to operate flawlessly even during long cloudy periods or during the winter when there is not much sun.
We tested our system over a week and got provising results only dropping 0.22 volts (the voltage drops faster when the battery is fully charged or is fully discharged).
Once the project was done and completed on the breadboard, it was time to make it more robust, we made a PCB to connected everything.
We designed the PCB using KiCad, started by placing every component we needed. We even replaced our 3.3V regulator with a more compact and power effecient 3.3V SMD LDO. Every sensor will be connected with a Grove header, to make it more simple and more durable. We also used a jumper that will later be connected to a physical switch to turn on or off the system.
Once the components were placed, we connected them properly and started make the layout of the actual PCB. We placed every Grove header, every SMD components and made sure to have large margins. Once everything was placed we were ready to send the gerber files to print the PCB.
We received our PCB and soldered every header and components on it, then tested it. Everything was working just like on the breadboard, so we decided to go to the next step.
Packing up everything watertightlyNow that the system is ready, we needed to protect it agaist the difficult environnement it will be facing. We got outself a water resistant box where we will put the system.
We drilled a few holes in the box for the sensors and even used some water resistant connectors for the weight sensor and the solar block that allowed us to completely disconnect them from the box, for easier transportation. The holes were covered in hot glue to make it as much water resistant as prossible.
Once everything was done, we ended us with a very provising looking system :
Finally, our system was ready for prime time. So one afternoon, we went to an apiary and installed our system. It was a really great experience, we learned a lot talking with them and we sucessfully put our box under a hive with the weight sensor and temperatures and humidity sensors in the hive, without forgetting the solar block on top.
We let the system run for 2 weeks during winter and checked frequently the Ubidots dashboard to see how the bees where doing.
Final resultsAfter all this time, our system what still working perfectly fine. Every 10 minutes for 2 weeks the sensors data was sent and we got amazing results.
Every sensors has worked well, our battery was almost always fully charged, never dropping bellow 80%. We can now proubly say that this project was a sucess !
Bonus : Android AppEven if the data was visible from the nRF Connect app, it was not really user friendly, so that's why we decided to create an Android application. It allows the user to see if every sensors is connected and working properly and display their values in a simple and pretty way.
The Android app uses standard API to acess the BLE functionnality of the Android device. By following the simple examples, we were able to come up with an app that we are quite happy with.
Comments