Imagine you are a smart agriculture company that helps farmers optimize their crop yield using IoT sensors. You sell various sensors that can monitor soil moisture, temperature, humidity, and light levels in the farm fields.
To help farmers make the most of their resources, you want to build a centralized system. This system collects sensor data from all the fields and provides real-time insights into the health and growth of the crops. With this data, farmers can make informed decisions about when to irrigate, fertilize, and harvest their crops.
Using Neo4j, a graph database technology, can be the key to unlocking valuable insights from the collected data. It also enables the smart agriculture system to reach its full potential.
In this blog post, we explore how Neo4j can be used to build a powerful smart agriculture system. We start by discussing what Neo4j is and why it is ideal for storing and querying complex, interconnected data. Then, we dive into the specific use cases for Neo4j in smart agriculture, such as identifying crop patterns, predicting crop yields, and optimizing resource usage. Finally, we walk through a step-by-step guide to building a Neo4j-based smart agriculture system that can help farmers make better decisions and improve their crop yield.
To flash the Jetson Nano SD card image to an SD card, you can follow these steps:
The BME680 sensor from Seeed Studio is a compact environmental sensor designed for use in mobile applications and wearables. It can measure temperature, pressure, humidity, and indoor air quality with high accuracy, and is designed to consume very low power. The sensor is compatible with popular microcontroller platforms, such as Raspberry Pi and Arduino, making it easy to integrate into projects.
The Grove Base Hat is a 40-pin Grove add-on board designed to be compatible with Raspberry Pi boards. However, it can also be used with the NVIDIA Jetson Nano by connecting it to the appropriate GPIO pins. It has 15 Grove connectors including 6 digital, 4 analog, 3 I2C, 1 UART, and 1 PWM.
This hardware module provides a simple and convenient way to connect Grove sensors and actuators to Jetson Nano. It supports a wide range of Grove modules, such as sensors for temperature, humidity, light, and sound, as well as actuators like motors and displays.
To connect the Grove Base Hat to the Jetson Nano, you must connect the following pins:
Once you have connected the Grove Base Hat to the Jetson Nano, you can start using Grove sensors and actuators with your Jetson Nano projects. After wiring the sensors to the Grove, it is recommended to run I2C detection with the i2cdetect command to verify that you see the device: in our case, it shows 76. Please note that the sensor communicates with a microcontroller using I2C or SPI communication protocols.
$ i2cdetect -r -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- 76 --
Here is a Python script to fetch sensor values from BME680 sensor:
from bme680 import BME680 # Initialize BME680 sensor object sensor = BME680() # Check sensor is connected if not sensor.begin(): print("Failed to initialize BME680 sensor.") exit() # Read and print sensor values while True: if sensor.get_sensor_data(): temperature = round(sensor.data.temperature, 2) humidity = round(sensor.data.humidity, 2) pressure = round(sensor.data.pressure, 2) gas_resistance = round(sensor.data.gas_resistance, 2) print("Temperature: {} C".format(temperature)) print("Humidity: {} %".format(humidity)) print("Pressure: {} hPa".format(pressure)) print("Gas Resistance: {} Ohms".format(gas_resistance)) else: print("Error reading BME680 sensor data.") time.sleep(1)
The from bme680 import BME680 statement imports the BME680 sensor library, which provides an interface to read environmental data from the sensor. The sensor variable initializes the BME680 sensor object. The if not sensor.begin() : statement checks if the sensor is properly connected and initialized. If it fails to initialize, the code will exit and print an error message.
from bme680 import BME680
if not sensor.begin() :
The main loop of the code repeatedly reads sensor data from the BME680 sensor and prints the values to the console. The time.sleep (1) statement pauses the execution of the code for 1 second between each iteration of the loop.
time.sleep (1)
This script uses the BME680 library to read temperature, humidity, pressure, and gas resistance values from a BME680 sensor connected to the system running the script. The results are printed to the console in a loop with a delay of 1 second between each reading.
Assuming the BME680 sensor is connected and functioning properly, the output looks something like this:
Temperature: 26.68 C Humidity: 41.35 % Pressure: 1008.6 hPa Gas Resistance: 3110.63 Ohms
The temperature, humidity, and pressure values are in Celsius, percentage, and hectopascals respectively, rounded to two decimal places using the round() function. The gas resistance value is in ohms, also rounded to two decimal places.
round()
If there is an error reading the BME680 sensor data, the script will print the message "Error reading BME680 sensor data." to the console.
Error reading BME680 sensor data
It is time to write Python code to set up a connection to a Neo4j Graph database using the Neo4j driver and BME680 environmental sensor.
from neo4j import GraphDatabase from bme680 import BME680 import time # Set up the Neo4j driver uri = "neo4j+s://41275b2a.databases.neo4j.io" driver = GraphDatabase.driver(uri, auth=("neo4j", "3DXXXXXXXXXXXXXXXaM")) # Set up the BME680 sensor sensor = BME680() # Define a function to create a sensor reading node in Neo4j def create_sensor_reading(tx, temperature, humidity, pressure, gas): tx.run("CREATE (:SensorReading {temperature: $temperature, humidity: $humidity, pressure: $pressure, gas: $gas, timestamp: $timestamp})", temperature=temperature, humidity=humidity, pressure=pressure, gas=gas, timestamp=int(time.time())) # Generate and insert sensor readings into Neo4j every 5 seconds while True: if sensor.get_sensor_data(): temperature = round(sensor.data.temperature, 2) humidity = round(sensor.data.humidity, 2) pressure = round(sensor.data.pressure, 2) gas = round(sensor.data.gas_resistance, 2) with driver.session() as session: session.write_transaction(create_sensor_reading, temperature, humidity, pressure, gas) print(f"Inserted sensor reading - temperature: {temperature}, humidity: {humidity}, pressure: {pressure}, gas: {gas}") else: print("Error reading BME680 sensor data.") time.sleep(5)
This Python code sets up a connection to a Neo4j graph database using the Neo4j driver and a BME680 environmental sensor. It then defines a function to create a sensor reading node in the graph database with the current temperature, humidity, pressure, gas resistance, and timestamp. The main loop of the code repeatedly generates sensor readings every 5 seconds and inserts them into the graph database using the defined function.
The from neo4j import GraphDatabase statement imports the Neo4j driver, which allows Python code to interact with a Neo4j database. The from bme680 import BME680 statement imports the BME680 sensor library, which provides an interface to read environmental data from the sensor.
from neo4j import GraphDatabase
Neo4j driver
The uri variable specifies the location of the Neo4j database and the credentials to access it. The driver variable initializes the Neo4j driver using the specified URI and credentials.
uri
The sensor variable initializes the BME680 sensor object. The create_sensor_reading function takes in a Neo4j transaction object (tx) and the current sensor readings (temperature, humidity, pressure, gas, and timestamp). It then creates a new node in the graph database with the given properties.
create_sensor_reading
The main loop of the code repeatedly reads sensor data from the BME680 sensor and inserts the readings into the graph database using a Neo4j transaction. The time.sleep(5) statement pauses the execution of the code for 5 seconds between each iteration of the loop.
time.sleep(5)
git clone https://github.com/collabnix/bme680-jetson-neo4j cd bme680-jetson-neo4j
You can install the Neo4j driver for Python using pip:
pip install neo4j python3 sensorloader.py
Inserted sensor reading - temperature: 26.68, humidity: 41.35, pressure: 1008.6, gas: 3110.63 Inserted sensor reading - temperature: 12.42, humidity: 49.71, pressure: 1149.34, gas: 4815.11 Inserted sensor reading - temperature: 27.73, humidity: 77.2, pressure: 1081.24, gas: 4737.95 Inserted sensor reading - temperature: 19.22, humidity: 50.17, pressure: 958.73, gas: 516.57
Neo4j Aura is a fully managed cloud database service provided by Neo4j. It is a database as a service (DBaaS) offering that allows you to create, deploy, and manage their own graph databases in the cloud. This is without having to worry about the underlying infrastructure and maintenance tasks. Assuming that you already have Neo4j Aura DB connection UI in place and configured, the next step would be installing the Neo4j Docker Desktop.
Open Docker Dashboard > Extensions > Install Neo4j Docker Extension.
To allow Neo4 Docker Extension to connect to the remote Neo4j Aura DB, you must provide connection URL, authentication type, and credentials. Once successful, you should be able to start running cypher queries.
The BME680 sensor measures the concentration of several different gasses, including volatile organic compounds (VOCs), carbon monoxide (CO), and nitrogen dioxide (NO2). This is in addition to measuring temperature, humidity, and pressure. To fetch the concentration of these gases from the sensor, you can use the get_sensor_data() method of the BME680 class. This returns a BME680Data object that contains the latest sensor readings. You can then access the gas concentration values from the BME680Data object using the following attributes:
get_sensor_data()
from neo4j import GraphDatabase import time import bme680
# Set up the Neo4j driver uri = "neo4j+s://your-neo4j-instance-url-here" driver = GraphDatabase.driver(uri, auth=("neo4j", "your-neo4j-instance-password-here"))
# Set up the BME680 sensor sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
# Define a function to create a CO2 reading node in Neo4j def create_co2_reading(tx, co2_concentration): tx.run("CREATE (:CO2Reading {concentration: $concentration, timestamp: $timestamp})", concentration=co2_concentration, timestamp=int(time.time()))
# Wait for the sensor to warm up print("Warming up sensor...") sensor.set_gas_status(bme680.ENABLE_GAS_MEAS) time.sleep(300)
# Start retrieving CO2 concentration data and inserting it into Neo4j print("Starting CO2 data collection...") while True: if sensor.get_sensor_data(): co2_concentration = round(sensor.data.gas_resistance / 10, 2) with driver.session() as session: session.write_transaction(create_co2_reading, co2_concentration) print(f"Inserted CO2 reading - concentration: {co2_concentration}") else: print("Error retrieving sensor data") time.sleep(60)
python3 sensorloader_co2.py
Warming up sensor... (takes 4-5 minutes) Starting CO2 data collection... Inserted CO2 reading - concentration: 1294686.
The CO2 concentration of 1294686.06ppm (parts per million) is quite high compared to typical indoor CO2 levels. In a well-ventilated indoor environment, the CO2 concentration should be around 400-1000 ppm. CO2 levels above 1000ppm can cause drowsiness, headaches, and other symptoms, while levels above 5000ppm can cause serious health effects and even death in extreme cases.
However, the interpretation of CO2 levels depends on the context and the environment in which the measurements were taken. For example, in some industrial settings, such as breweries or greenhouses, CO2 levels may be intentionally high for specific purposes. It is also important to consider other factors that may affect indoor air quality, such as humidity, ventilation, and the presence of other pollutants.
Here is an example of how you might model a BME680 sensor and its readings in Neo4j. First, you would create a "Sensor" node to represent your BME680 sensor. This node might have properties like "name" and "manufacturer", and any other information you want to store about the sensor.
CREATE (:Sensor {name: 'BME680', manufacturer: 'Bosch'})
Next, you would create a "Timestamp" node to represent a particular point in time when a reading was taken. This node might have a "timestamp" property that stores the date and time the reading was taken.
CREATE (:Timestamp {timestamp: datetime()})
Then, you would create a "READS" relationship between the Sensor node and the Timestamp node. Properties like "temperature", "pressure", "humidity", etc., represent the values that were read from the sensor at that time. For example, to create a reading where the temperature is 25 degrees Celsius, the pressure is 1000hPa, and the humidity is 50 percent, you might use a query like this:
MATCH (s:Sensor {name: 'BME680'}), (t:Timestamp) CREATE (s)-[:READS {temperature: 37.0, pressure: 1168.83, humidity: 37.23}]->(t)
This query creates a "READS" relationship between the Sensor node and the Timestamp node. Properties for temperature, pressure, and humidity are set to the values 25, 1000, and 50, respectively. You can then use Cypher queries to:
Neo4j provides a data source plugin for Grafana that allows you to visualize and analyze data stored in a Neo4j graph database directly within the Grafana dashboard. To use the Neo4j data source plugin, you must install the plugin from the Grafana plugin repository. Once the plugin is installed, you can configure it to connect to your Neo4j database by specifying the database URL, username, and password. Once the data source is configured, you can create visualizations using Neo4j query language, Cypher. Cypher is a powerful graph query language that allows you to traverse and manipulate graph data stored in the Neo4j database.
You can use the Grafana Docker Extension by bringing up Grafana on Docker Desktop in a few seconds. Access the Grafana dashboard and login by using the default admin/admin as username/password.
Once you are logged in, click Settings > Data Sources > Plugins and search for Neo4j Datasource
Install the Grafana Datasource.
Supply the connection URL, username, and password of Neo4j Aura Instance and get connected.
The benefits of using the Neo4j data source plugin for Grafana include:
Once you get connected to the remote Neo4j Aura database, you should be able to run cypher queries against the sensor data.
MATCH (sr:SensorReading) WHERE sr.timestamp >= $timeFrom AND sr.timestamp <= $timeTo RETURN sr.timestamp as time, sr.temperature as temp, sr.humidity as hum, sr.pressure as press, sr.gas as gas_res ORDER BY sr.timestamp ASC
The query uses the MATCH clause to specify the node label and assigns a variable sr to represent nodes with that label. The WHERE clause specifies a range of timestamps using the variables $timeFrom and $timeTo. The RETURN clause specifies the properties to be returned for each sensor reading. The ORDER BY clause sorts the results by timestamp in ascending order.
MATCH
WHERE
RETURN
ORDER BY
Finally, you should be able to create all the four dashboards - temperature, pressure, humidity, and gas resistant as shown below:
Graph databases enable efficient querying of complex and interconnected data, making it an ideal choice for environmental monitoring applications where data is often interrelated. This is particularly relevant to the agricultural industry where this data is needed to improve crop yields.
In this blog, we discussed how to interface a BME680 environmental sensor with a Neo4j graph database using Python. The BME680 sensor is capable of measuring temperature, humidity, pressure, and gas resistance. This makes it an ideal sensor for environmental monitoring applications that can improve decision making within the agricultural industry.
Ajeet S Raina is a Developer Advocate at Docker and an Ambassador for the Arm Developer Program.
Join the Arm Developer Program
Very informative blog