Data are beautiful. And far from "destroying the poetry of the rainbow" (Keats), a scientific approach can help us better appreciate the things we enjoy doing. Here, we've taken a kit of IoT goodies and assembled them into a device chock full of sensors to help quantify the discovery history of a foraging-related hobby. In this case: mushroom-hunting. It was going to be used to help us reproducibly locate our favorite fishing spots, but the weather has packed it in here in the Pacific Northwest, and while fishing is out for all but the hardcore, the nearby rainforests are profusely sprouting fungi of all shapes and sizes. However, whatever you like going hunting for, this device has some fun features. Building it and processing the acquired data taught us a ton about how to assemble an electronics project and use its output, and we hope our experience is instructive.

Step 1: Sensors

We got a lovely grab-bag of sensors (the Grove Starter Kit Plus) and an Intel Edison to play with as part of the Intel IoT challenge. We got the automotive kit (which included a GPS and various proximity detectors) but decided to repurpose it in another application in which we could maximize our utilization of the sensors. As such, we wanted it to be outside where the environment is highly variable, and we were totally geeked out about the GPS module in particular. We decided to deploy the LCD screen for feedback, the light sensor for depth-of-shade, the temperature sensor for climate, the GPS for geolocation including altitude, the sound module for environmental cues, a button to register an event, and a dial to specify the nature of that event. These were all plugged into the Grove board installed on the Edison and enclosed within or fastened to a small box. The idea was that while foraging, you would take the box with you and passively record position, light, sound and temperature, and actively record the things you collect. They could be anything: fish, birds, mushrooms, berries, animals, whatever. Given the time of year, we had some constraints on our activity, but the woods were full of 'shrooms, so we recorded them.

Step 2: Physical Design

We wanted the device to fit in a small box, but we had other constraints - some of the sensors had to be exposed and the box had to fit the electronic components, battery and the many wires inside. Luckily, the box that the Grove stuff came in fitted the bill, so we put the electronic gubbins inside and the sensors, switch, dial and LCD panel on the outside (or at least accessible to the outside). The GPS and buzzer were left on the inside. The push switch was triggered by a bolt. Rectangular holes were made with a small drill in each corner and an Exacto knife, round holes with a drill. As a prototype, it worked fine; for a more finished product, something made of plywood or 3D printed would be recommended.

Step 3: Coding

All coding in this instructable was done by my collaborator on this project: dennishore, python guru and electronics enthusiast. I'm going to turn things over to him for this step and Step 6: Report. He's agreed to answer questions and has posted all of the code at the end of this step and Step 6.

Sensors and connections

Grove temperature sensor - AI0
Grove rotary potentiometer - AI1
Grove light sensor - AI2
Grove sound sensor - AI3
Grove 16x2 LCD display - I2C bus
Grove LED - digital 2
Grove button - digital 3
Grove buzzer - digital 4
Grove GPS - UART

The GPS communicates as a serial device. The Grove GPS page (like the equivalent page for all the Grove sensors) has an Arduino and a Raspberry Pi section. For the other sensors, I had a good guess as to how to do it on the Edison based on the connectivity from the Arduino section, and the commands based on the Raspberry Pi python modules. But this differed for the GPS. For clarity, it is the updated SIM28 GPS module. In the end, it works connected to UART on the Grove shield, and is initialized with:

uart = mraa.Uart(0)
ser = serial.Serial(uart.getDevicePath(), 9600)

The SIM28 specs state that the start up time can be as short as one second (warm and with internet assistance), or as long as 32 s (cold and without internet). The most challenging (ongoing) part of this project has been to debug what is a (1) GPS error, (2) serial port read error, (3) GPS data parsing problem. Parsing is currently done with the pymnea2 module. The complete initialization routine is

temp = mraa.Aio(0)
pot = mraa.Aio(1)
light = mraa.Aio(2)
sound = mraa.Aio(3)
display = lcd.Jhd1313m1(0, 0x3E, 0x62)
display.setColor(100, 0, 0)
led = mraa.Gpio(2)
button = mraa.Gpio(3)
buzzer = mraa.Gpio(4)
B = 3975.
uart = mraa.Uart(0)
ser = serial.Serial(uart.getDevicePath(), 9600)
types = 'turdstool puffball logOysters deathcap fungweed minishrooms'.split()
discovery = {}
for t in types:
    discovery[t] = 0

This includes definition of the categories. Since we are using the Grove rotary potentiometer (and not a rotary encoder), we have limited angular resolution. Six categories are easily selectable on the LCD display by rotating the knob, but we could likely accommodate up to ~15 without having the selections be too close together. The code then attempts an initial GPS fix:

fix = False
attemps = 0
while fix == False:
        gpsTime, lat, lon, elev, nsats = getGpsData()
        test = '%8.4f' % float(lat) # gets the job done
        fix = True
        screen('natureTracker', 'ver 0.1')
        screen('initializing GPS', '%d sats ... %d' % (nsats, attemps))
    attemps += 1

The infinite loop is broken when data from more than two satellites is returned. Success is gauged by the parsing of the latitude data into a float. Failure to do this could indicate various types of GPS errors, including serial communication errors. Once a fix has been established, the current system time is used to initiate the log file:

initial = datetime.datetime.now()

The core algorithm is then:

# event polling
i = 0
while True:
    # scan for button press
    picked = pick(pot.read())
    if button.read():
        discovery[picked] += 1
    # update display
    screen(picked,'> %s' % discovery[picked])
    # update log
    if i > 10:
        data = getData()
        recordData(data, discovery)
        i = 0  

    # slight delay
    i += 1

The half-second delay provides some stability for registering the button press events, without a fancier means of rejecting multiple press events. This also serves as the time base for recording data, currently set to 10 times this interval (approximately every 5 seconds). Details of the pick, screen, getData, and recordData functions may be seen in the complete code attached at the end of this section.

At the moment, the LED turns off/blinks when it loses its GPS fix, or if the number of satellites drops below 2. During these times, it is still possible to record field data using all sensors except GPS, i.e. not latitude, longitude, or elevation for those data points. The summary table produced from the output displays means for the data, taking into account whether elevation was available or not. By observing the state of the LED before registering data, this provides the option of whether to wait (sometimes only a few seconds) in order to have GPS data available. Obviously, data points without GPS do not appear on the map.

The complete code is linked below.

Automatically start on boot (Headless)

Since we require the data collection program to start as soon as the power is connected (and restart if we need to change batteries in the field), we require that the python program start on when the Edison finishes its boot sequence.

cat /etc/init.d/go.sh
#!/usr/bin/env sh
python natureTracker.py

chmod +x go.sh
update-rc.d go.sh defaults

Step 4: Mushrooms

None of us on the excursion had any real mushrooming experience. As such, we named the things we saw descriptively rather than with any attempt at mycological accuracy.

Turdstool (anything that looked brown)
Puffball (anything white and round)
Log oysters (anything that grew out of a log and was shaped like an oyster)
Deathcap (anything that looked evil)
Fungweed (anything that was a weird shape but still a fungus)
Minishrooms (anything small)

We did not eat (or indeed even pick!) any of the mushrooms we found, we just counted them. Misidentifying a mushroom and eating it can have severe consequences and this instructable does not seek in any way to assist you with this task. There are an insane number of 'shrooms out there and most of them are NOT recommended for consumption - check out http://www.rogersmushrooms.com/ to find out more.

Step 5: Data Collection

These data came from a stroll around a local park. We live in the Pacific Northwest and the environment nearby is coastal rainforest. Pretty much as soon as the rain starts in October each year the forest floor explodes with fungi of all shapes and sizes, and going on a walk with an eye out for mushrooms will reveal hundreds of them. Collecting the data is a matter of turning the selector to the appropriate place on the dial and register the 'shrooms with a push of a button. The video above captures a few snippets from our trail; I was assisted by two sharp-eyed boys and hindered by an excited and thoroughly confused dog.

We found a pretty fantastic variety of fungi out there in the woods that we really hadn't paid much attention to before we actually started looking. Some we found in our lawn and garden before even reaching the park.

Step 6: Report

The static report generation was in the form of a pdf file, with data points plotted on a map, color-coded according to category on the top, and a summary table (that acts a legend) on the bottom. Since the data recording interval is larger than the event polling, it is normal for entries to indicate multiple objects located. These are represented by proportionally larger circles on the map.

I was curious to play with matplotlib's basemap package, as I never had the occasion to plot geographic data. In the end, I was intrigued by all of the options for projections that basemap offered, but these came with some inflexibility for the plot aspect ratio, and ultimately wasn't worth it as the map was very zoomed in, covering a small range of latitude and longitude. Finally, I simply plotted the coordinates on a conventional rectangular grid. Basemap has some nifty features in drawing coastlines, etc, but these are not visible if the geographic area is too small.

Ultimately I would like to be able to overlay the data points on a map, such as from OpenStreetMap. The OSM web interfaces offers the possibility of downloading a map with specified latitude & longitude extents, but I couldn't figure out how to script such an operation, as the OSM website requires interacting with a script on its site. In the meanwhile, I grabbed a large enough region from Google Maps, and then had my script crop the image corresponding to the longitude & latitude extent of the data.

The side plots separate out the data for temperature, elevation, brightness and sound level, and plot them against position. For clarity, see the second figure, where they have been removed from the main plot and expanded. Temperature, elevation, number of satellites and a histogram of events are plotted against time in the lower graph.

The complete code for the graphic summary generation is linked below.

Step 7: Final Thoughts

Much of the functionality of this project could probably be reproduced using an app on a mobile phone - and you could set it up to trigger an event when you took a photo of the thing you're keeping track of. However, it's a lot of fun to put something like this together, to see which functions are useful and which are not, and to think about how to present the data gathered in a meaningful way. Thanks for reading and we hope this account inspires some of you to try an IoT project yourselves. Feedback welcome... including IDing some of those 'shrooms!

- makendo and dennishore

About This Instructable




Bio: Analog maker dabbling in digital manufacture
More by makendo:Laser-powered Light Saber Scott McIndoe Pier 9 Residency Solar analemma chandelier 
Add instructable to: