I live nestled in the heart of an agrarian country. Here, the soil holds tasteful tales of the past and the future of our economy. If you like numbers, here’s a bunch for you, Agriculture contributes approximately 33% of our GDP and employs more than 40% of our total population. You see, my journey into this project wasn’t fueled by a quest for innovation, but if I end up innovating something, I accept that. It was fueled by a need to improve, even just a tiny bit, the current state of affairs. USAID published an article saying that in recent years agricultural productivity has stagnated. Current methods of farming are pretty archaic and if we are to depend on these same methods to boost our productivity, we risk being obsolete as well.
One of the biggest challenges that farmers currently face is the gap that exists between them and the technology that could improve their output. This project aims to eliminate that gap. I have built an IoT sensor node that can be deployed to the farm and collect important parameters for a farmer which can be analyzed and used to maximize productivity. With this aim in mind, I have to solve several challenges including:
- It has to be powered by a battery and be solar rechargeable.
- The sensor nodes must be cost-effective because a number of them will have to be deployed.
- It has to be able to send information to the cloud using LPWAN.
- It can communicate with industry-grade sensors.
To solve these four main problems, I started by drafting a block diagram that provides an overview of the electronics.
Generally, the aim is to have a node that includes a high-performance, low-power, low-cost microcontroller at the heart of the board which can support industry-grade sensors using RS485 protocols, a low-power wide area networking module, a power management circuit that can supply power to both the MCU and any peripherals connected.
In this project, I will be describing how I accomplished that in the Green Dot Board. You can head to the Project and clone or fork it to have edit access.
One of the most important features of the Green Dot Board is power management. As described in the core requirements of the system, is the ability to work autonomously for years without any power concerns. I therefore decided to start by designing a power management circuit and making sure that works fine first to de-risk the whole system.
Most of the sensors that can be hooked onto this system require either 3.3V, 5V, or 12V. I therefore designed the power management circuit to be able to deliver all these powers. You can check out the Brave Power management circuit here. This board can be powered by either LiPo batteries, 4V - 6V solar power panels, or a Micro USB. From these inputs, there is a set of dedicated components to generate and regulate each of the mentioned outputs.
a)12V and 5V Rail
Starting with the 12V and 5V rail, I used the high-efficiency MT3608L 1.2MHz step-up converter. I used a voltage divider circuit to adjust the output of the component and used an SPDT switch to enable switching between the 12V and 5V output.
- Setting the output voltage can be calculated using the following equation.
Vout = Vref x (1 + R1/R2) where Vref = 0.6V as per the datasheet
- Recommended values for the inductor are 4.7uH - 22uH and the high switching frequency of the regulator enables me to select small components here.
- For the input and output capacitors, 22uF ceramic capacitors have either X5R or X7R type of dielectrics.
b) 3.3V Rail
For the 3.3V rail, I first use an RT8059 converter which is a high-efficiency Pulse Width Modulated (PWM) step-down DC/DC converter, capable of delivering 1A output current over a wide input voltage range from 2.8V to 5.5V. This was very ideal for my case since the LiPo batteries I will be using have a nominal voltage of 3.7V, a maximum of 4.2V when fully charged, and a minimum of about 3.0V when fully discharged.
- You can set the output voltage using a voltage divider circuit using the same equation we’ve seen.
- For the inductor selection, you can use the following equation to get an approximate value for the inductor.
where the ripple current can be approximated as 30% - 40% of the maximum output current
I then used an ultra-low loss power distribution switch with a programmable current limit to protect the power source from overcurrent and short circuit conditions. Current limiting is set by connecting a resistor Rset from ISET Pin to GND. To get the value of the Rset you want, you can use the following equation.
Rset = 6800/Current(A)
c)Charging circuit
For charging the batteries, I used the CN3063 which is a constant-current /constant-voltage linear charger for single-cell Li-ion and Li-Polymer rechargeable batteries. This device is ideally suited for solar-powered applications but it can also work with the USB specifications. It has a regulation voltage preset at 4.2V with 1% accuracy but can also be changed using a resistor. the device accepts input voltages of 4.4V to 6V meaning we would have to use a small solar panel delivering about 5W.
Some interesting features I haven’t mentioned yet of this device include the following.
- Programmable Charge Current: With an external resistor, the continuous charge current can be programmed up to 600mA, providing flexibility in charging requirements.
- Adaptive Charging: The on-chip 8-bit ADC adjusts the charging current based on the output capability of the input power supply, making it suitable for diverse scenarios.
- Status Indication: The open-drain output pin indicates the charger's status, providing visibility into the ongoing charge cycle.
The charge current is set by connecting a resistor Riset from the ISET pin to GND. The ISET pin’s voltage is regulated to 2V during constant charge current mode.
Ich = (Vset / Rset) x 900
I then used two diodes to direct current from both USB and Solar to the charging circuit.
Improvements for a V2 level, so reading the value with an MCU will prove invaluable.
- I also realized that there are scenarios where I would have both a solar panel and a USB connected to the brave board. Although nothing gothic happens to the board, I will have an automatic switch using a MOSFET to disconnect the Solar panel when both the solar panel and the USB are connected to the board.
For wireless connectivity, LoRaWAN was an obvious selection. I won’t go deep into the theory behind LoRaWAN, all I can say now is that LoRaWAN stands as a low-power, wide-area networking protocol, leveraging the LoRa radio modulation technique as its foundation. Its primary function lies in establishing wireless connectivity for devices to the internet. For example, enabling your motion sensor to have a heart-to-heart with your alarm system and notify you when unprecedented motion is recorded.
LoRaWAN boasts several features that position it as an ideal fit for our IoT applications:
- Long Range: As the name suggests, LoRaWAN provides long-range communication, making it a great choice if we are deploying a sensor node at the end of a large farm.
- Low Power Consumption: LoRaWAN operates with a low-power design, allowing our IoT devices to conserve energy and operate on battery power for extended periods.
- Low Data Rates: While not suitable for high-bandwidth applications, LoRaWAN's low data rates are perfect for transmitting small packets of data at regular intervals. This makes it suitable for applications like sensor networks and monitoring systems where periodic updates are sufficient.
I decided to choose the SEEED Studio’s LoRa-E5 module because it has built-in AT command firmware, making it easy to create prototypes or applications with just a few simple commands. However, soldering the module onto the board was probably the most challenging bit of the whole Green Dot development process. The module is small, and I don’t believe I had the right tools to handle that kind of hand soldering. It was only after I got soldering flux, and a better soldering iron tip that I was able to finish the job.
Designing around this module is quite simple. I connect UART and NRST to the host MCU to send AT commands. In addition, I connect Pin24 to a tactile push button because the grounding of the module will force the module to enter Boot upgrade mode. This module works on 3.3V so I also make sure to provide that.
a)Registering our LoRa Module
To register a device to a LoRaWAN platform, you can choose either of two options, Activation By Personalization (ABP) or Over The Air Activation (OTAA). One of the main differences between the two is that ABP offers simplicity and speed, while OTAA provides enhanced security through dynamic key exchange so for our case we will use OTAA to register our device.
When registering a device using OTAA (Over-The-Air Activation) in a LoRaWAN network, you typically need the following information:
- Device EUI (Extended Unique Identifier): A unique identifier for the device. This is often a hardware-specific identifier burned into the device. This can be generated and written to the device but I’m going to just read it directly.
# Generating the Device EUI
import secrets
dev_eui = secrets.token_hex(8).upper()
print("DevEui:", dev_eui)
# Reading it
dev_eui = read_id(b'DevEui')
print("DevEui:", dev_eui)
- Application EUI (AppEUI): A unique identifier for the application. This is required for OTAA and is assigned to the application by the network provider. It serves as a way to associate the device with a specific application. You may obtain it from your LoRaWAN network provider, or you can generate it yourself. It is a 64-bit (8-byte) value.
app_eui = secrets.token_hex(8).upper() # Formerly called JoinEUI
print("AppEUI:", app_eui)
- Application Key (AppKey): A secret key shared between the device and the network server. This key is used during the OTAA process to establish a secure connection. The AppKey is typically kept confidential and should be known only to the device and the network server. It is a 128-bit(16-byte) value. Similarly, you can use a random value as your AppKey
app_key = secrets.token_hex(16).upper()
print("AppKey:", app_key)
I’ll be using Loriot for this demonstration because that was readily available to me, but the same concepts apply to other similar platforms like The Things Network (TTN). Step 1 would be to enroll a device by providing the generated keys and a name for the device.
The next step would be to make sure those same keys are written to the LoRa module. This can be done as shown below.
from credentials import *
def configure_lora_module():
set_work_mode("LWOTAA")
# Set OTAA parameters
set_id("AppEui", app_eui.encode())
set_key("APPKEY", app_key.encode())
set_id("DevEui", DevEui.encode())
# Join the network using OTAA
status = join_network()
return status
Step 3: Selecting a MicrocontrollerChoosing the right microcontroller is pivotal for the success of an IoT sensor node. Considering the requirements of low cost, low power, efficiency, and an easy development environment. I decided to go with the Seeed Studio’s XIAO RP2040. This breakout features the RP2040 microcontroller from Raspberry Pi bringing with it high performance, low cost, and ease of use to the microcontroller space.
Some of the features outlined on Seeed Studio’s website include:
- High Performance: Powered by Raspberry Pi 2040 chip, dual-core operating up to 133 MHz, equipped 264KB of SRAM, and 2MB of onboard flash memory.
- Ultra-small Design: 21 x 17.5mm, Seeed Studio XIAO series classic form factor, suitable for wearable devices
- Multiple Development Interfaces: 2x buttons, 11x digital / 4x analog pins, 1x I2C interface, 1x UART port, 1x SPI port, and 1x SWD Bonding pad interface
- Multiple Developing Platforms: Support Arduino / MicroPython / CircuitPython development, friendly for beginners, satisfied for electronics enthusiasts.
This SEEED module has an onboard RGB LED which we will use to visually illustrate the state of the system.
Sensor Interface
When it comes to interacting with sensors, the green dot board can hook up UART, I2C, and RS485 sensors. The connection block also has a set of two pins which can be used as programmable IO pins using the RP2040.
For the RS485, I’m using the SP3485 a +3.3V low-power half-duplex RS485 transceiver with a 10Mbps data rate. The RS485 standard allows for multi-drop (multiple devices on the same bus) and long cabling lengths. In addition, it offers great noise immunity. Some of the sensors that can be connected include the following;
The supporting components around the RS3485 include a bypass capacitor, a pull-up resistor on the RO line, and a termination 120-ohm resistor at the end of the twisted pair A B cables. I used a 120 ohm because the twisted pair cable used in RS485 is defined to have a characteristic impedance of 120 ohms and so by adding a 120-ohm resistor at the end of an RS485 transmission line, the signal will be dampened by the resistor instead of reflected into the bus.
The last bit is to route to my microcontroller.
NOTE: During testing, I realized something that you might have already noticed from this schematic, I had forgotten to connect the ENABLE pins from the SP3485 to my microcontroller. This will be done in my second revision.
FirmwareI wrote a simple MicroPython code to that sends some data to the cloud as proof of concept. The code can be found on my GitHub Profile. I started by writing simple functions to interact with the LoRa module and then used those functions to join the network and send data to the cloud.
# This function sends an AT command to the LoRa module
def send_at_command(command, response_timeout=5000):
uart.write(command + b'\r\n')
time.sleep_ms(response_timeout)
return uart.read().decode('utf-8')
# This function sends hex data and blinks the onboard RGB LED when successfully sent
def send_msghex(hex_data):
command = b'AT+MSGHEX=' + hex_data
response = send_at_command(command)
if "ERROR" not in response:
blink_rgb_led(PURPLE)
if "FPENDING" in response:
print("Downlink Received!")
print(response)
In the main file, I start by importing the required libraries and defining the firmware parameters.
import time
import machine
from machine import Pin, I2C, deepsleep
import SHT31
from lora_lib import configure_lora_module, send_msghex
# Define firmware parameters
UPLINK_INTERVAL = 60000 # In milliseconds
FIRST_TIME = True
# Configure SHT31 Sensor
i2c = I2C(id=1, scl=Pin(7), sda=Pin(6), freq =400000)
sensor = SHT31.SHT31(i2c, addr=0x44)
I then enter an infinite loop that first checks whether It’s the first time we are running the code, if it is then we join the network, if not we continue with the rest of the code that first reads sensor values and then sends then converts the values into hexadecimal for transmission. After transmission, the system sleeps for the defined uplink interval period
# Loop after joined network
while True:
if FIRST_TIME:
# Configure LoRa Module
status = configure_lora_module()
FIRST_TIME = False
# Read sensor data
temp_humi = sensor.get_temp_humi()
humidity = temp_humi[1]
temperature = temp_humi[0]
# Convert into hex for transmission
sensor_data_hex = f'{int(humidity * 100):04x}{int(temperature * 100):04x}'
# Transmit data
send_msghex(sensor_data_hex)
# Sleep for UPLINK INTERVAL (adjust as needed)
print(f"Temperature: {temperature}")
print(f"Humidity: {humidity}")
time.sleep_ms(UPLINK_INTERVAL)
The final setup looks as shown below. I’m looking into getting a 3D-printed enclosure to just keep everything nice and tidy.
And there you have it folks, The Green Dot Board. This journey has been a total delight for me from the very beginning and I hope you can say the same thing. You can see below an illustration of how data is being received in the cloud. The next step from here would be to build either a custom dashboard to display all this information or to use a platform like Datacake which would integrate seamlessly with Loriot or TTN to do the data visualization.
I will be implementing some of the issues that arose along the way and updating the design in flux. You should be getting prompts of new updates as I do then and either choose to receive or deny them. Accepting updates is as simple as clicking the accept button.
Comments