Beehive Monitor (UDP, Sleep, Thermistor, WiFi antenna, ADC speed, RAM)

The aim of this project is to monitor the health of a bee hive (eventually several hives). To do this I want to know the size of the cluster of bees in the main brood body of the hive, to see how much flight activity there is and to listen to the buzzing – with a view to detecting preparation for swarming and other things demanding attention.

I have found two systems already available that perform at least some of this. There is a modification to the Open Energy Monitor that measures 4 temperatures and has a very neat display based on the emoncms. This would cost about £60 per hive pretty much pre-built. Temperature only,
There is also a commercial system that collects a richer set of data. This is quite pricey and has ongoing subscription charges. (£240 plus £70 p.a. for a single hive). It has a 3-4 month battery life. It communicates wirelessly from each hive to an apiary data collection point and then to the Arnia server via GRPS.

The system described here uses the Spark wifi which with a carefully positioned router reaches my wife’s apiary. For additional hives I can either use more Sparks or an 868MHz wireless Arduino.

This is work in progress. I have the basic hive data collection sensors and software up and running and have a simple python script that collects the data. I’m publishing now because I could use some advice on getting the most from the Spark RAM.

The hardware is very simple. I have a Sparkfun microphone feeding A7, a photoresistor in A0 and 47k thermistors in A1-A6.
A0-A6 are all pulled up to 3.3v with a 47k resistor network and have 10uF decoupling capacitors to ground. There is an led on D0.

The Spark software is …

// beehive monitor
// v0.01
// help yourself
// don't blame me
// don't get stung

#include "application.h"
#include <math.h>
//pin definitions
int led = D0;
int therm[2];         //two thermistor for demonstration expandable to 6
int micPin = A7;
int optPin = A0;
int dynamicData[128]; //we are very short of memory so reuse this for optical and audio
int packetSize;
char UDPin[16];
UDP udp;

int v[2];                    //thermistor ADC readings
const float Rinf = 0.06;     //thermistor constants for 57k thermistor
const float B = 3990.0;
const float Rup = 47000.0;   //pullup resistor value

float t[2];                  //temperature measurement

char output[48];             //for spark variable

void setup() {
    therm[0] = A1;
    therm[1] = A2;
    Spark.variable("temperature", &output, STRING); 
    pinMode(led, OUTPUT);

void loop() {
    if (udp.parsePacket()>1){
        digitalWrite(led, HIGH);   // Turn ON the LED to show packet received,16);
        if(UDPin[0]=='s'){         // If sn received, sleep for n mins
            int i = UDPin[1] - '0';
            if (i>0 && i<10){
                i*=60; //add a 0 for longer sleeps when debugged
                Spark.sleep(SLEEP_MODE_DEEP, i);
        if(UDPin[0]=='d'){          // If d received, send monitoring data
            udp.beginPacket(udp.remoteIP(), udp.remotePort());
            if (UDPin[1]=='t'){
                //update temperatures
                for (int i = 0; i<2;i++){
                    v[i] = analogRead(therm[i]);
                    t[i] = B / log(Rup * v[i]/(4096 - v[i])/Rinf) - 273;
                sprintf(output,"%d, %d, %.1f, %.1f",v[0],Network.RSSI(),t[0],t[1]);
                udp.write((unsigned char*)&output,sizeof(output));
            else if (UDPin[1]=='a'){
                //update sound
                for (int i=0;i<128;i++){
                udp.write((unsigned char*)&dynamicData[0],sizeof(dynamicData));
            else if (UDPin[1]=='o'){
                for (int i=0;i<128;i++){
                udp.write((unsigned char*)&dynamicData[0],sizeof(dynamicData));
            digitalWrite(led, LOW);    // Turn OFF the LED

And the python stub that shows that the data have been collected is …

# Beehive monitor

import socket               # Import socket module
import time
import struct

host = ""      # use name of Spark server
port = 5208                 # Reserve a port for your service.
micData = [0]*128
optData = [0]*128

while True:
    s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)   #Create a socket object
    while s: 
            s.connect((host, port))      # connect to server if awake
            s.sendall(b'da\0 ')          # da = send audio data
            print ('sent send data message')
        except socket.error:
            print('unable to connect')
        r='not read anything'
        try:                         #check for data
            r = s.recv(1024)
            if r == 0:          # if r is 0 then the sender has closed for good
                print('socket disconnected')
            #read audio data packed in a buffer

            s.sendall(b'do\0 ')          # da = send optical data
            print ('sent send data message')
        except socket.error:
            print('unable to connect')
        r='not read anything'
        try:                         #check for data
            r = s.recv(1024)
            if r == 0:          # if r is 0 then the sender has closed for good
                print('socket disconnected')
            #read optical data packed in a buffer

            s.sendall(b'dt\0 ')          # da = send optical data
            print ('sent send data message')
        except socket.error:
            print('unable to connect')
        r='not read anything'
        try:                         #check for data
            r = s.recv(1024)
            if r == 0:          # if r is 0 then the sender has closed for good
                print('socket disconnected')
            #read string with temperature data
                text = r.decode("utf-8")
                text = "Can't decode"

            #s.sendall(b's1\0 ')          # s = sleep n is time in 10s of min  (UDPstub does 1s not 10s of min
            #print ('sent go to sleep message')
        except socket.timeout:
print ("Finished...")

You will see that the python code collects a set of readings and then can (but presently it is commented out) send the spark to sleep. To stop the core going to sleep you justy stop the python code from running so that when the core wakes it stays awake for reprogramming. This is a good thing!

Data are collected each time the python code requests it.
Communication is by UDP. This seems the quickest way to get the data and handles the varying packet sizes. The python struct library looks after unpacking the raw integer data.


I have used the same array to store audio and optical data collected sequentially. I found that I get an out of heap SOS if I collect both audio and optical data simultaneously. I would really like to collect more data points but run out of space at around 256 samples. I’m not sure how the program gets through the 6k of user RAM so any advice on finding a bit more space would be appreciated.

I have 3 AA batteries that are trickle charged by a pair of 6v 90mA solar panels. As long as the spark sleeps most of the time these power it nicely. I had originally planned to power both a Ciseco RFu-328 and the Spark. The RF-328’s are cheap as chips and have built in 868MHz radio transcievers. The idea is to use one for each beehive in the apiary and have the Spark consolidate the data and forward it. Unfortunately the 1015 regulator I was using couldn’t handle the power requirements of bothe the RFu and the Spark. For the time being I’m going ahead with just the Spark.

I have experimented with antennae and I have found that I get better range with the chip antenna than a uFL connected wifi antenna. I will pursue this in more detail now that I have found the Network.RSSI() function. It is great that the Spark Team has added plenty of new functionality - I really must re-read the documentation to see what else is new.

What Next?

I need to get off the breadboard and into a hive - I thought I’d got there until the regulator wimped out. When I get a beefier regulator I’ll get both processors talking by I2C which will be a new learning experience.

I have found that the Spark ADC’s give a cleaner signal than the 328 unless I access the 328 using low level code that disables interrupts. The conversion rates are roughly similar but I know the Spark has stacks of headroom if I dig into the wiring library. I want to do that as I’d like to see whether there is any interesting audio information in the ultrasonic range- but that will wait till I have the basic system working.

Spark ADC output from Sparkfun microphone - 440Hz tone input

FRu-328 output for same tone high level code

FRu output - low level code

FRu output - low level and interrupts disabled

Signal processing

I was going to do most of this on the Spark so that I could send alerts using Spark publish. However while I’m struggling with RAM I’m doing the processing remotely. The volume of data isn’t a problem with the UDP communications.
The thermistors will measure the temperature on a line running through the middle of the bee brood nest. Where the bees cluster the temperature will be a steady 30C ish so this will give me the size of the brood cluster.
I will FFT the audio and optical signals.
The flicker on the optical signal will be a bit like the output of a Malvern Instruments particle counter and will give me information about the number and speed of bees passing through the hive entrance.
There have been several scholarly articles about bees buzzing. They mostly seem to use low audio frequencies and nothing more sophisticated than frequency-time traces and principal components analysis. The main thing is that there is a huge amount of data but mostly it is irrelevant or contains a lot of redundant information. I intend to distill out the key bits of information by using dimension reduction techniques. The basic idea is to transform each new set of data (2 spectrograms and a bunch of temperatures + time of day & weather or anything else that seems relevant) into a short vector that consolidates the useful data and rejects everything else. I’ve done this in the past for monitoring engineering systems using nonlinear principal components, K means and Kohonen mapping and cleverer people than me have used Gaussian mixtures and the wonderfully named Generative Topographical Mapping and Support Vector Machines. (This may seem to be drifting off topic but I want the information in the public domain so that someone doesn’t grab the IP from in front of all those hardworking beekeepers). The main thing is that once the current state of a hive is described by a short vector it is statistically meaningful to compare it with a library of known states either by calculating the nearest known vector or by using a neural network classifier. This way I can provide an alert when the bees are about to swarm, are running short of space, are under attack by animals or disease or have been disturbed. Furthermore if I can’t confidently classify the current state it means it is novel and that means someone needs to go take a look.

After I have identified the key features I may not need to collect such a lot of data. I hope that this is case because then I can get the processing back onto the Spark and make better use of the Cloud communications.

solar trickle charger - 5.5v output cells but with 3 AA cells connected gives 3.4 to 4.11v

breadboard Spark Beehive monitor

prototype board two processor beehive monitor with inadequate power supply :frowning: (for now).


@phec, what an amazing project! I was looking at the Spark code and there may be opportunities for optimizing your memory use.

The declaration int dynamicData[128] will create an array of 4x128 = 512 bytes. Since analogRead returns a value between 0 and 4095, you could use uint16_t instead, dropping the array size to 256 bytes. Also any constant values int micPin = A7 could be cast to flash using const int micPin = A7 (little things, I know, but every little bit counts!). Everything else looks good. If you compile locally, you can also reduce the UDP client buffer size to gain even more RAM. :smile:


That’s very helpful advice @peekay123 I’ll follow it now!

Update on WiFi communications:
Signal strength in apiary as reported by Network.RSSI - with chip antenna: -75 to -79dB
with 1/4 wave stick -69 to -73dB
with “25dBi” 15 element yagi -66 to -71dB
So the yagi is better than 1/4 wave which is better than the chip.
The main thing is that I have communication with the apiary.
Next an instrumented brood frame …
Also, thanks to @peekay123 I can collect 256 data points rather than 128. Unfortunately I can’t get 512.

1 Like

@phec, I am not sure why so I will do more investigation:)

1 Like

Stripboard for monitor

Finished board - just waiting for 6 thermistors from CPC. One for the board and 5 for the ribbon cable.
The board is pinned to the bottom bar of a brood frame with drawing pins (thumbtacks). It is 0.8" deep and I hope the space between the frame and the floor is 1".
Before I install it in a hive I will wrap some polythene round the electronics and close the ends where the power and antenna leads come out with foam rubber.


Thermistor Calibration
I notice that there is quite a bit of information posted where people use a polynomial to evaluate temperature using a thermistor. I have used the log formula - mainly because the B values are usually provided by the thermistor manufacturer but also because it is closer to the physics of a thermistor (which is nerdishly satisfying).

ln(R/Rinf) = B x 1/T

If you don’t have the calibration constants or want to tweek to match your circuit components, plot ln® vs 1/T.

The values in this graph are what I could find around my house. The thermistor R is as measured using the Spark ADC and a 47k resistor so any resistor inaccuracy is taken care of but not of course errors in the reference temperature. Excel conveniently does the curve fit and gives B (4573 in my case) and Rinf (0.0173) ready to be put in your code.

In your code:

const float Rinf = 0.0173;     // thermistor constants for 100k thermistor
const float B = 4573.5;
const float Rup = 47000.0;     // pullup resistor value

    v[i] = analogRead(therm[i]);
    t[i] = B / log(Rup * v[i]/(4096 - v[i])/Rinf) - 273;

Strictly I should use 4095 but the one part in 4096 error avoids a divide by zero error if I measure 4095 on the ADC.

1 Like

@phec, nice stuff on the thermistor! FYI, I am working on running your code locally so I can find out why you cannot increase your sample buffer size. :smile:


This looks like a great project @phec and just what I was planning as well with @rtbaker ! What kind of hives are you running this on as I would love to give it a try with my apiary as well. I have seen the other two systems you mention there and thought that they are quite highly priced for what they are offering. Would like to dig into the sensors you are using to see if I can do something similar. Have you ever thought about doing weight as well? Look forward to hearing more about the project!

Thanks @richardfreeman I’m using British Nationals which are quite a bit smaller than the Langstroth or Dadant that are used in the US. (Or at least my wife is - she is the beekeeper.) The best article I’ve found about the temperature distribution in hives is from the USDA - it is a bit gruesome but it has helped me to decide where to put the sensors - in a vertical line, slightly off centre so that the results are more sensitive to the cluster changing size.
The microphone is from Sparkfun (via a UK reseller). The light varying resistor is a generic one costing a few pence. The resistance in daylight is a few 10s of Ohms. In darkness it is over 100k. I hope that the resistance changes fast enough that I can pick up flickering as bees move through the entrance. The thermistors I have ordered are Vishay NTC 100k ones from CPC (The guys who will be stocking Spark Cores in Europe) so that I have 6 thermistors all from the same batch. I have already used 47k and 100k thermistors from various sources in other projects and they seem a very easy way to measure temperature unobtrusively. Our local electronics shop even has thermistors about 1mm across. They have a quick response but the leads are like cobweb - not really up to using unprotected in a beehive.
I have a frame all wired up and ready to go except for the eagerly awaited thermistors. I’ve made buzzing noises at the microphone and it seems to be sensitive enough. The light sensitive resistor picks up the flicker of my monitor.

Weight would be nice. Do you have any ideas? Strain gauges seem to need some expensive electronics and force sensors are pricey. I’ve seen pressure sensitive conductive plastic but I’m not sure how it would survive outdoors.

I’d be interested in knowing how accurate your temperature readings are over your given range after using just two points to perform your calibration. I would think your endpoints might be pretty close, but somewhat off in the middle.

This is funny how people use text based ADs these days… I’d never buy product from these guys because of it:

Also your thermistor (if it’s the same one you linked to) is noted as having a beta value of 4190K, whereas your calibration process came up with 4573.5K. Are these supposed to be the same thermistor?

I think we can only reasonably expect any calibration process to be only as accurate at the measurements and calculations we make. The more precise we are on the input, the more accurate the output will be. At the end of the day it’s probably easier to just buy calibrated probes (of whatever type)… although Makers always want it cheaper and more open. you can envision my two hands pretending to be a human scale right about now :wink:

What an irritating AD thing to find in an unlikely place. I wonder who thought that would do any good.

No - the thermistor I calibrated was from someone else. My calibrated B was several % different from the data sheet but not that different. I agree wholeheartedly with your comments about ‘only as good as …’ and my measurements are not very good.
I have just checked what my error would be if I calibrated using only two points with an Excel curve fit using a commercial thermistor calibration curve. If I used 0C and 100C as calibration points then the measurement made at 40C would be 1.25C too low which I guess is is why Steinhart and Hart added the extra term. I’m happy to stand corrected.

While I’m calibrating using a couple of temperature sensors that report temperatures 1C different I’ll stick with the simpler method for calibrating my thermistors but no longer be under the illusion that I 'm doing as well as I could.

p.s. for the bee monitor I’m measuring temperatures between 0C and 30C so if properly calibrated even with the simple formula I’ll have an error of well under 0.5C
p.p.s self heating will add to the error. In still air I may read 0.2C high and if well insulated by beeswax it could be higher still. Or there again inside the bee cluster they may provide some forced ventilation …

@Phec Thought about you when I came across this Bee Hive Monitor Article:


@RWB Neat! and Microsoft provides debugging tools (at a price?) which I miss with Arduino and Spark. But at $129 for just the processor board with WiFi I don’t think I’ll follow that route. It is good that more people are monitoring hives. Thanks.

@phec, I have been playing with the code to increase the sampling size. I can increase it to 512 by reducing the size of the UDP receive buffer (local build) without getting a heap error but for some reason, I could not get the python side to work. Strangely, at a size of 490, the python code seems fine but anything above that fails EVEN THOUGH the Spark side seems fine! Any thoughts?

Thank you for working on this @peekay123 I really appreciate it.
It is very odd about the python. Is there an error message?
If you have changed the length of the int to uint16_t in the Spark code as you recommended, have you matched this by changing the struct.unpack('128i',r) from my original python code to struct.unpack('512h',r)?
The other thing I can think of is that with the UDP header, 490 x 2 bytes + 8 bytes header is getting close to the 1024 input buffer size. If this is the problem I don’t think there is a practical limit on the PC buffer size you choose so does r = s.recv(2048) help?
Finally, I don’t know what data you are sending. If it is random bytes it can confuse python’s decode("utf-8"). I have replaced this with decode("utf-8","ignore") to avoid random crashes when an invalid character is received.
Finally +1 At the Spark end, I’ve accidentally written beyond variable boundaries with the Spark and it continues to run but with strange behaviour. Could this be happening with either of the variables UDPin[] or dynamicData[]?

The thermistors arrived this afternoon so as soon as the weather improves enough to open a beehive I’ll start monitoring.

@phec, yup, I changed the Spark code and added print statements to view the received command characters. At array size 512, everything works well until the upd.write() sending the dynamicData array. The sizeof is correct at 1024 but the value returned by udp.write() is 0xFFFFFFFF so something is failing in the write. The write take a long time and most likey times out. There is not supposed to be any length restrictions on the UDP.write but something is not happy. I have to dig some more. :smiley:

1 Like

No worries @phec thanks for coming back to me with some more details on this one. I strangely seem to have both Langstroth and National due to the local BBKA group preferring the latter and me preferring the former! The sensors by the guys over at the are going for National, mainly because of the larger number of those in the UK.

It looks like you have all the key sensors there. As I am sure your wife will tell you, really its all about trying to work out whether your bees are going to swarm at that point in the season, then trying to see if they have enough stores to get through the winter or have been robbed by other animals during that time. Being able to match environmental conditions with movement in the hive as well would be great to measure. The final element is the weight and I hadn’t heard of the pressure plastic you linked out to, great find there! It looks like the Bee Lab project are going load sensor - - with cradles here - - whereas the guys at Arnia have gone for a sort of scales arrangement here - I might experiment with the former to see how that goes.

Look forward to more news on the introduction this weekend anyway if the weather holds out!

All set to go, wrapped in plastic to keep the bees off the electronics and already logging data. I hope the rain stops for long enough to get into the hive. At the moment the stripboard is pinned to the frame. This way I know where the sensors are but I’ll probably eventually have it free standing on the floor which will be easier to set up.

I have one thermistor on the circuit board - if the Spark runs continuously it reads 5C warmer than the others but once I put the Spark to sleep for most of the time all the thermistors read within 0.2C of each other.

@richardfreeman Thank you for the links. My wife is visiting the Vet School apiary at Nottingham next month so she’ll look out for the Bee Lab and compare notes. Mind you I bet that the Horizon team never get down the road to the Vet School. (Sorry Tom Rodden if you come across this and have made it there!)

I’ll be interested to hear which weighing system you go for and how affordable it is.


Here is the temperature profile across the hive before and after the bees were introduced.
The cluster of bees maintains a remarkably constant temperature throughout the night. When it was coldest the cluster tightened up a bit.

And here is the outside view. Note the high tech. mount for the antenna and the solar power battery box.

View through the entrance - there is plenty of space for the electronics between the frame and the mesh floor.

The optical and audio data are showing plenty of activity with the bees settling for the night and starting to forage in the morning. I’ll need a lot more data before I can start to do some pattern recognition. The spectra are quite noisy so I collect 16 sets of data in quick succession. I can either average the spectra for a low noise result or plot out a sonagram:

Test sonagram - me whistling. 60Hz per bin 120-1600Hz frequency range 16 spectra samples per second.