DIY Grain bin monitoring system and fan Control

There are commercial systems available but I wonder if someone has already built this kind of system.
What kind of sensors are needed?
Especially for reading grain moisture.

1 Like

I use the DS18B20 1 wire sensor, it’s what they use in OPI commercial systems so I can read their sensors too. I read them with a 5v Arduino which reports to my own custom Perl server which uses RRD Tool to save the data to a RRD (round robin database). This has run for several years now.

About 1.5 years ago I started playing with Node-Red and MQTT. I use NodeMCU (ESP8266) wifi boards to connect via MQTT with Node-Red which handles all my automation. Another great benefit of MQTT is that I also have a nice control interface on my phone to control most aspects of the fan system. I have a personal weather station that reports outdoor temperature and humidity for my automation which can operate in 4 different modes (manual, warming, cooling or drying). Warming & cooling use temperature only and drying uses humidity & temperature to calculate how much more water the air could hold and it adjusts my fan speeds accordingly.

The two systems (bin temp & fan control) are for practical purposes, seperate from each other in that they don’t talk to each other or share data.

I’ve found my sensors are not quite as interference resistant as the OPI ones, I have not figured out the difference. I think I see a small SMT passive component on the OPI sensor ropes but I’m not sure what they’re doing there.

I designed the bin temp monitoring system before I knew about MQTT, otherwise I’d use that instead of my own custom protocol for sending data.

I can read the OPI moisture sensors but I have not figured out how to convert the readings into grain moisture values.

What else would you like to know?

Should add that Perl scripts also generate html and graphs for displaying the bin temps on a web server.


Yes I am thinking doing the same.
I want begin with a basic fan controller for running the fan only in some outdoor relative humidity range and something to read the grain moisture at different levels in the bins we put 14% to 17% grains and soybeans.
I have no need to read the grain temp only, I am looking for a cheep system to monitor the grain moisture as the bin often end to be 14% at the top and 10% or 8% at the bottom.
So with the readings, when I will see that all the bin is under 13.5% I will run the fan at 60-70rh to raise it all to 13.5%
And accurate reading would be nicer than the 5 ft probe we can take at the top and the small in the door.

So you can read temperature and relative humidity from these OPI cable with an arduino?

These moisture sensors will give to relative humidity I think. Can you convert the value to relative humidity?

For testing I want to find a good relative humidity sensor and temp sensor (the DS18B20 seem to be what I need)

For the grain moisture calculation there are charts available.
I think this is what is happening in the bin?
After reading this, I think I need a good relative humidity sensor, ±5% will not be acceptable.

I have been thinking about this. AM2302 ( DHT22) are quoted 2% accuracy on RH however I doubt that would hold long term. I think one major problem would be keeping dust, mites, insects etc. from the sensor which would affect the reading. As these would be embedded in the grain for long times, they would need be protected by a semi permeable barrier of some sort to let air and water vapour through but little else, which would come into equilibrium. Reaction time would be slower but that would not matter in this application. Not sure what sort of membrane would work which would need to enclose the sensor completely, and would need some testing to verify accuracy.

Actually the AM2305 might be just right for the job as i has a sintered microporous shield and otherwise sealed.

1 Like

Just a little bulky. The grain can pull down quite hard on even a smooth pipe. I’ve had a 1/4" bolt rip off from grain pulling down on a smooth 1/2" pipe.

1 Like

The sensor is 16 mm diameter without the flange fittings it would need to be in a tube for mechanical protection of the cable, or if mounted through a bin wall would need a cover above for mechanical protection. Another method for measuring MC in grains is the dielectric method ie measuring capacitance between two electrodes. A novel way to do this is the arduino single pin capacitive sensor method, comparing the internal S&H capacitor to the external capacitance. it may warrant some investigation and would need calibration and temperature compensation.

The ESP 32 also have single pin capacitive input sensors ( touch sensors, but they can also read values) The dielictric cell dimensions would have to be matched to the sensor range.

@m_elias If you find the Node red GUI antiquated try I think with just installing the Blynk tools in NR you can be up and running.


In a idle moment, and out of curiousity I measured the touchread value against a range of capacitors using the touch example in ESP32.
touchread vs capacitance ESP32
Its fairly linear against a log scale of capacitance. The resolution in the range 0-10 pF is between 1 to 2% this would reduce slightly by averaging although I was quite surprised by the readings being quite stable and reproducible.

Some reading from while ago.

I’m in the process of developing a long term automatic monitoring system stick a series of probes at regular intervals into a large ‘on the floor’ grain drier. My sensor of choice is the Sensirion SHT85, which is reasonably cheap, accurate and reliable. It works on the I2C bus and so you can put up to two onto one arduino with ease. There are a couple of different flavours in the family, with the cheaper ones having slightly less accurate results, but still good enough.

Putting aside the low power requirements (I am aiming for battery powered nodes with wireless transmission), the biggest challenge I’m finding is determining the correct isotherm equations. There is plenty of data for some categories of crop, but very little for the white wheat we predominantly grow in the UK, so I am going to have to do a bit of trial and error to see what comes out right.

1 Like

i did temp monitor only, using a nano and thermistors … allows up to eight sensors on each cable, and a seperate reader

Sorry this is an older thread. But has any consensus emerged about the best sensor for this setup? I implemented a 5 sensor DHT22 grain bin monitoring system using an ESP32, Wifi, NodeRed/Grafana/InfluxDB. It worked very well until the bin was drained and filled and the sensors separated from the cable.

Interestingly, the DHT22 will transmit reliably over 500ft of 22awg wire. All I had to do was increase the resistance between signal and the power IIRC.

Any suggestions to harden the sensors? I was going to try distributing the load more to the cable using cable glands and NPT PVC pipe for a mechanical ‘shield’

I’ve used DS18B20 as they scale better but then you only have temperature, no moisture. I’m not sure how you’d harden a DHT22 setup without either allowing too much dust in or not enough air circulation. I’m curious about your influxdb/grafana setup, can you share a tour video or a few pictures?

Sure. Its a pretty simple work flow actually. The system basics: A repurposed laptop running as a server. Server runs Openmediavault which hosts docker. Docker hosts the important programs: an MQTT broker, NodeRed, InFluxDB, Grafana. The ESP32 processes the sensors and broadcasts MQTT messages with each sensors data. (I wrote this in micropython. Its not super time sensitive with the 2s sensor update time so the ‘slowness’ of python is a non-issue). NodeRed processes incoming MQTT messages and sends them off to the InfluxDB. Super simple flow:

This is where future alerts will be process vs Grafana.
Once in the InfluxDB importing to Grafana follows anyone of many Youtube tutorials. Ultimately alllowing me to track visually all the temps and humidities at their corresponding levels:
[Temps from testing in home]

Was that helpful? Is there anything else that I poorly explained or skipped over?

As you can see, the humidity has a 20 point spread. These sensors were pulled from the grain bin that they were in when the signal s were lost. The soldered connections or wires themselves failed. These sensors are now sitting within inches of each other on a shelf in my house. My second generation system will probably stick with temp only data as the calibration seems to vary wildly for humidity in this use case

Sounds pretty straightforward. What I’m mostly curious about is how you visualize your data. Secondly, how reliable are your ESP32s? If you read my post (2nd one of this thread) you’ll see I started with my bin temp system long before MQTT & Node-Red become so popular. I’ve never been completely happy with my system. My DS18B20 sensors in parasite power mode are very sensitive to interference from our 3 phase fan inverters. I had tested an ESP8266 back then and found it was even worse (3.3V instead of 5V and onewire lib timing issues) so I stuck with a 5v Arduino and a wifi module. I’d love to switch to an ESP (especially one already built with solar input & batt charger). I’d also wouldn’t mind having moisture sensors. Now while my web server system is great for checking graphs from anywhere in the world, my graphs are very rigid. I’d love to have a system that allowed convenient/fast/customizable/adaptable viewing from either desktop or smartphone with an interface for alerts. I have just started getting into influx but have not had the time to expand to grafana. What I’m ultimately looking for is a purpose built bin temp fan control GUI with access control for different users.

Just to clarify, would you like to know how I generated the Grafana screen capture above?

Yeah, less awesome than Id like. I was getting between 1-3 months of data before the ESP32 would have to be power cycled. It would usually fault somewhere in the calculation and so would retain it wifi connection. I have yet to try sending reboots on signal losses with the NodeRed/MQTT functionality.

That’s interesting .I do not believe that I have seen any indication in the temp graphs that would indicate interference problems, but I have not looked for anything subtle. I do feed 5v to the DHT22 and use a necessary level shifter to pass the signals to the ESP32. Without the level shifter, nothing dies, but its super unstable.

As far as UI goes, the Grafana sounds like its exactly what you are looking for. Between the InfluxDB and Grafana, I have loved being able to quickly adjust timescales etc. I have yet to play with InfluxDB retention policies though. Something I have heard to keep in mind if you have lots of sensors. As far as alerts, I did watch a couple of YouTube videos that used Grafana to send alerts, but the NodeRed alerts were easier for me to execute.

All of my tinkering is actually for my father-in-laws farm. Unfortunately I haven’t quite gotten him to want to try any true automation (mostly turning on bin fans either auto or by web app) I see his point too, The fans are much higher voltage than an ESP32. I have seen some cool stuff using NodeRed from a UI standpoint, but I still haven’t gotten very good with it. I was thinking about looking in the HomeAssistant direction. My cursory glance over it looks like it do all that I use NodeRed for and provide a built in GUI. I think it also pushes Over-The-Air updates.

Do you have good resources on GUI design? My CSS/HTML is pretty weak, so much so that I only know of those letters. I mostly copy and paste from sites/authors that do know what they are doing :slight_smile:

I also wanted to follow up on the noise you were getting from the DS18B20. Were you using shield cable? Does the issue exist when they are powered? The system that I had setup thankfully has wall outlet power available, but we may go in the remote sensing direction in the future.

Thanks for your feedback.

I wanted to follow up with my current setup and changes made to my system. I also wanted to include some of the systems and code I used.

First, I setup up an OpenMediaVault server on an old computer I had. You could easily do this on a Raspberry Pi4. In fact below is a link to a several tutorial videos:

  1. OpenMediaVault5 which has Portainer ( a managment module) and Docker (container systemm see #2)
    Easy Script Install Openmediavault 5 Raspberry Pi 4 - YouTube
  2. Skip the OMV5, but remote backups
    #295 Raspberry Pi Server based on Docker, with VPN, Dropbox backup, Influx, Grafana, etc: IOTstack - YouTube

Once Docker is installed, I use Docker-Compose to standardize installations between my test server and the operational server on the farm.
Below is the core docker-compose.yml that I created. I nested these behind a firewall using Nginx Proxy Manager based on the below video.
Install NGINX Proxy Manager in OpenMediaVault and Docker - YouTube

version: '2.1' 
    container_name: mosquitto
    image: eclipse-mosquitto
    restart: unless-stopped
      - 1883:1883
      - 9001:9001
      - /srv/dev-disk-by-label-data/appdata/mosquitto/data:/mosquitto/data
      - /srv/dev-disk-by-label-data/appdata/mosquitto/log:/mosquitto/log
      - /srv/dev-disk-by-label-data/appdata/mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf
    container_name: influxdb
    image: "influxdb:latest"
    restart: unless-stopped
      - 8065:8086
      - 8066:8083
      - 2003:2003
    # env_file:
      # - ./services/influxdb/influxdb.env
      # - INFLUXDB_DB=mydb
      # - INFLUXDB_ADMIN_USER=myadminuser
      # - INFLUXDB_ADMIN_PASSWORD=myadminpassword
      - INFLUXDB_USER=nodered
      # - INFLUXDB_READ_USER=myreaduser
      # - INFLUXDB_READ_USER_PASSWORD=myreadpassword
      # - INFLUXDB_WRITE_USER=mywriteuser
      # - INFLUXDB_WRITE_USER_PASSWORD=mywritepassword
      - /srv/dev-disk-by-label-data/appdata/influxdb/data:/var/lib/influxdb
      - /srv/dev-disk-by-label-data/appdata/backups/influxdb/db:/var/lib/influxdb/backup
    container_name: nodered
    image: nodered/node-red:1.0.6
    restart: unless-stopped
    user: "0"
    privileged: true
    # env_file: ./services/nodered/nodered.env
      - TZ=America/Chicago
      - 1880:1880
      - /srv/dev-disk-by-label-data/appdata/nodered/data:/data
    container_name: grafana
    image: grafana/grafana:6.3.6
    restart: unless-stopped
    user: "0"
      - 3000:3000
    # env_file:
      # - ./services/grafana/grafana.env
      - TZ=America/Chicago
      - GF_PATHS_DATA=/var/lib/grafana
      - GF_PATHS_LOGS=/var/log/grafana
      # - GF_SERVER_ROOT_URL=http://localhost:3000/grafana
      # - GF_SECURITY_ADMIN_USER=admin
      - /srv/dev-disk-by-label-data/appdata/grafana/data:/var/lib/grafana
      - /srv/dev-disk-by-label-data/appdata/grafana/log:/var/log/
    image: 'jc21/nginx-proxy-manager:latest'
      - '80:80' #HTTP Traffic
      - '81:81' #Dashboard Port
      - '443:443' #HTTPS Traffic
      - ./config.json:/app/config/production.json
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    image: 'jc21/mariadb-aria:10.4'
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'npm'
      MYSQL_PASSWORD: 'npm'
      - ./data/mysql:/var/lib/mysql

I added the required Nginx Proxy portions to the bottom of the yml file, but have not validated this exact docker-compose file myself.

That covers the server side.
The ESP32 is also pretty straightforward, but has several steps.

  1. I prefer python as a programming language. I followed this tutorial to load Micropython on to the ESP32.
    Getting Started with MicroPython on ESP32 and ESP8266 | Random Nerd Tutorials
  2. the above is a great resource for all things ESP and Micropython. I use Thonny to write my code.

Below is the code created to run multiple DS18B20 sensors on a single pin on an ESP32 connected to the outdoor Wifi on the farm.

# Complete project details at
# µMail (MicroMail) for MicroPython
# Copyright (c) 2018 Shawwwn <>
# License: MIT

import network
from machine import Pin
import machine
import dht
import esp
import urequests
import ujson
from utime import sleep,localtime
import time
import usocket
from umqtt.simple import MQTTClient
import ubinascii
# import micropython
import webrepl
import onewire, ds18x20

import gc

#DHT22 Code
def read_sensor(every_n_mins,sec_update,num_sens):
    sensor = [dht.DHT22(Pin(27)),dht.DHT22(Pin(26)),dht.DHT22(Pin(25)),dht.DHT22(Pin(14))]
    for i in range(num_sens):#create list for each sensor
        humds[i] = []
        temps[i] = []
    for i in range(every_n_mins*60/sec_update):  
        for j in range(num_sens):
            temp = sensor[j].temperature()* (9/5) + 32.0
            humd = sensor[j].humidity()
#             print('Temp:%f'%temp)
#             print('Humd:%f'%humd)
    return temps,humds
def read_sensor(every_n_mins,sec_update,roms):
    for i in range(len(roms)):
        temps[i] = []
    for _ in range(every_n_mins*60/sec_update): 
        for rom in roms:
            temps[i] = []
            temp = ds_sensor.read_temp(rom)* (9/5) + 32.0
    #             print('Temp:%f'%temp)
    return temps

#DHT22 code
def record_grain_bin_01(every_n_mins,sec_update, num_sens):
    temps,humds = read_sensor(every_n_mins,sec_update,num_sens)##Read multiple sensors and return DICT
    form_data = []
    for i in range(num_sens):
        temp = list(filter(None, temps[i]))
        humd = list(filter(None, humds[i]))
        avg_temp = sum(temp)/len(temp)
        avg_humd = sum(humd)/len(humd)
        msg = '"Sensor":"Sensor_{:02d}", "Temp":{:.2f}, "Humidity":{:.2f}'.format(i,avg_temp,avg_humd)
        client.publish(topic_pub, '{'+msg+'}')
    counter = 0

def record_grain_bin_01(ds_pin,every_n_mins,sec_update):
    ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))
    roms = ds_sensor.scan()
    temps = read_sensor(every_n_mins,sec_update,roms)##Read multiple sensors and return DICT
    form_data = []
    for i in range(len(roms)):
        temp = list(filter(None, temps[i]))
        avg_temp = sum(temp)/len(temp)
        msg = '"Sensor":"Sensor_{:02d}", "Temp":{:.2f}'.format(i,avg_temp,avg_humd)
        client.publish(topic_pub, '{'+msg+'}')
    counter = 0

def sub_cb(topic, msg):
  print((topic, msg))
  if topic == b'notification' and msg == b'received':
    print('ESP received hello message')

def connect_and_subscribe():
  global client_id, mqtt_server, topic_sub
  client = MQTTClient(client_id, mqtt_server)
  print('Connected to %s MQTT broker, subscribed to %s topic' % (mqtt_server, topic_sub))
  return client

def restart_and_reconnect():
  print('Failed to connect to MQTT broker. Reconnecting...')
'''Setup and Connect to Wifi''' 
ssid = 'your_wireless_network'
password = 'your_password'

station = network.WLAN(network.STA_IF)
station.connect(ssid, password)
while station.isconnected() == False:
print('Connection successful')

###Setup MQTT Connections###
mqtt_server = ''#Specific to local network
client_id = ubinascii.hexlify(machine.unique_id())
# topic_sub = b'notification'
topic_pub = 'grainbin/sensor'

last_message = 0
message_interval = 5
counter = 0

  print('Client Connecting...')
  client = connect_and_subscribe()
except OSError as e:

print('Beginning Main Loop')
#Restart after 5 failed attempts
counter = 0    
led = machine.Pin(2, machine.Pin.OUT) 
ds_pin = Pin(4)  
while True:
        print('Running Main Loop')
        record_grain_bin_01(ds_pin, every_n_mins=1,sec_update=5)
        counter += 1
            client = connect_and_subscribe()
        except OSError as e:
        if counter > 5:

That pretty much wraps up my setup. The development for the DS18B20 is done and I will hopefuly get this in the grain bin shortly.

I’ll keep you posted.