A Jetson Nano Ambient Weather Station — Part 1

SmartCow
9 min readJul 7, 2021

In October of 2020, Nvidia released the latest member of the Jetson family of embedded computing boards, a 2GB version of their Jetson Nano kit for developers. At just $59, the aptly named Jetson Nano 2GB Developer Kit has an outstanding price-to-performance ratio, providing a GPU-accelerated processor coupled with a large array of GPIO pins and hardware interfaces easily accessible through the onboard 40-pin header.

The Jetson Nano 2GB Developer Kit

I’ve recently gotten my hands on one of these kits, and what could be a better kickstart my Jetson journey than by developing a project around it?

In this four part series, we’ll be constructing an ambient weather station based around the Jetson Nano 2GB. In later parts we’ll be adding internet connectivity and some AI to improve the station’s functionality.

For now let’s start off with a list of sensors and modules that we’ll be using to collect and display our weather data.

And with that, lets get started.

The Jetson Nano

Assuming that your Nano 2GB has been initialized and is running correctly as explained by other guides, we can begin the setup process for our demos by installing the necessary Linux packages and Python libraries needed to interface with our modules. To make the installation simpler, I’ve included an installer script in this project’s git repository to automate the process.

I’ll be running the Nano 2GB in headless mode for simplicity, but this guide applies just as well if you’ve connected the Nano to a monitor. After plugging in an Ethernet cable to the Nano and connecting to it via SSH. We’ll then clone the project’s git repository to a local folder, cd into the repository and execute the installer script;

git clone https://github.com/SmartCowAi/jetson-nano-weather-station.git
cd jetson-nano-weather-station
sudo chmod +x installer.sh
sudo ./library_installer.sh

The script will now automatically download, extract and install all the required packages. As some Python libraries have to be compiled from source, this process might take a while; a perfect opportunity to brew up a nice cup of coffee.

If everything has installed correctly we’re now ready to start getting some useful data from our sensors.

The BME680

The Grove BME680

SeeedStudio’s Grove BME680 module incorporates a temperature, humidity, pressure and gas sensor all in a single package, which we can communicate with using the I²C protocol. The Nano 2GB has two buses we can use, so we’ll wire the BME680 to Bus 0.

We can determine how to connect the sensor to the Nano by looking at the Pinout Diagram for the Nano’s 40-pin header. In this case, we’ll connect the sensor’s;

GND to Pin 20
VCC to Pin 17
SDA to Pin 27
SCL to Pin 28

To make wiring simpler, I prototyped a simple converter between the grove connector and the Nano’s pin header. SeeedStudio also provide handy pre-made converters if soldering isn’t your thing. As an aside, the sensor’s GND pin can be connected to any of the eight GND pins on the Nano, as can the sensor’s VCC pin to any of the two 3.3V pins.

We can then use Jetson’s pre-installed i2cdetect tool to scan for our BME680 sensor on the Nano’s I²C Bus 0;

i2cdetect -y -r 0

If everything is wired correctly, we should see the sensor’s hexadecimal address of 76 printed on the terminal.

Bingo, we detected our BME680

For our demo code, we’ll be using Adafruit’s CircuitPython library to create a wrapper around the BME680 so that we can easily obtain its readings. Let’s run our demo;

python3 demos/I2C/BME680/BME680_demo.py

And display the readings obtained from the sensor on the terminal.

Weather data obtained from the BME680

The HM3301

The Grove HM3301

So now that we can determine the current temperature, air pressure and humidity; how about air quality? We’ll use the Grove HM3301 laser dust sensor to measure the amount of fine and coarse particles in the air and give the user a heads up if these reach uncomfortable levels.

Like the BME680 this is also an I²C device which we will also attach to the Nano’s Bus 0, so we’ll be using the same wiring connections;

GND to Pin 20
VCC to Pin 17
SDA to Pin 27
SCL to Pin 28

If we run i2cdetect, we now get a new device address of 40

And there’s our dust sensor!

If we run our demo code, we now get measurements on the amount of fine (PM1.0 and PM2.5) and coarse (PM10) Particulate Matter present.

Data obtained from the HM3301

The Air530

The Grove Air530

For this project, I would also like to automatically determine the location of the weather station, as well as get the current date and time without relying on the user to enter these manually. For that, we’ll use the Grove Air530 GPS module.

Unlike the previous two sensors, the Air530 uses the UART protocol to transmit and receive data from the Nano. The connections are as follows;

GND to Pin 6
VCC to Pin 1
RX to Pin 8
TX to Pin 10

To communicate with the Air530, we’ll be using the pySerial library to read the TX line and obtain data from GPS module in the form of NMEA messages, which we will then decode to determine the module’s location as well as the UTC time. Let’s run the demo script and check the output on the terminal.

Decoded messages from the Air530 GPS module

And plugging those latitude and longitude values into Google Maps gives us the location of SmartCow’s office building, exactly where the Nano is located!

Greetings from SmartCow! ヾ(°∇°*)

OLED Display

A 128x64 OLED display

To make our weather station more user friendly, we’ll be displaying our sensor readings on an OLED display based around the SSD1306 driver chip. Like our first two sensors, we can communicate with the display using I²C. Here is where our connections will go;

GND to Pin 6
VCC to Pin 1
SDA to Pin 3
SCL to Pin 5

You’ll notice that I connected the display to I²C Bus 1 instead of Bus 0 on the Nano. This is because we’ll also want to increase the bus frequency from the default 100kHz to 1MHz to make our display update much smoother. As the BME680 and HM3301 are not rated to operate at such a high frequency, we’ll just use a separate bus for the display to ensure that our setup will function without any hiccups when we connect all the modules to the Nano.

We increase the bus frequency by running this command in the terminal.

sudo sh -c 'echo 1000000 > /sys/bus/i2c/devices/i2c-1/bus_clk_rate'

And we’ll run i2cdetect to check our connections. Notice that I now changed the bus identifier argument from 0 to 1, to reflect our change in connections.

Bingo! The display is detected by the Nano

We’ll use Adafruit’s SSD1306 library to display some sensor readings just to make sure they are properly displayed on the screen. Many implementations make use of the Python PIL library’s default font to display text on the screen, but I don’t particularly like using it as the bitmap font makes it difficult to change the size of the displayed text while retaining its sharpness.

Alternatively, TrueType fonts are perfectly suited for this application. I’ll be using Oswald Medium as I think it quite suits the style of a weather station, but please feel free to download any other fonts which you might like and try them out.

Again, let’s run the demo and see what gets displayed on the OLED module.

Sensor readings shown on the OLED display

Perfect! Now we have a way to easily display our sensor readings.

Rotary Encoder

The rotary encoder we’ll be using

With our current setup, the display will periodically cycle though the different readings obtained from our sensor. But what if the user wants to see what the current temperature is without having to wait around?

We can use a button to force the display to show the next weather data feature, but the user will have to cycle through all the options if they accidentally press the button again. We could also use a number of buttons equal to our weather features, but this requires a large number of GPIO pins and is not easily upgradeable should we add some extra sensors to the weather station.

Instead we’ll use a rotary encoder. This device is a particularly clever piece of hardware that makes it easy to tell if the shaft has been rotated clockwise or counter-clockwise, and is commonly used a simple and practical input device; think about how intuitive it is to use the volume knob on your car’s radio system, for example

The connections between the rotary encoder and the Nano are as follows;

CLK to Pin 29
DT to Pin 31
SW to Pin 33
+ to Pin 17
GND to Pin 20

The encoder has three outputs that we’ll want to monitor: the SW pin which is normally HIGH, but goes LOW when the encoder shaft is pressed, and the CLK and DT pins. These last two are what we will use to determine in which directions the knob has been turned in. We won’t go into much detail on how the rotary encoder works, so check out this excellent article for a more in-depth look.

So how do we know if the shaft has been turned? The DT and CLK pins are normally HIGH, but go LOW at an offset to each other as the shaft rotates. This offset is key to determining in which direction the shaft has been turned, as the CLK pin will go LOW before the DT pin if it was rotated counter-clockwise, and vice-versa if rotated clockwise

To see in which direction the shaft has been turned, we could check the state of the CLK and DT pins in a loop until one of them goes LOW, but this method is incredibly wasteful and resource-heavy. Instead, we will be using the Jetson GPIO library’s interrupt service routines to automatically call a function in our Python code when either pin goes from a HIGH state to a LOW state.

Let’s run our demo code and see what we can read from the encoder

Rotary encoder inputs being simultaneously printed on the terminal

The Nano immediately and reliably recognizes the inputs I make on the rotary encoder!

A little caveat which can be a nasty trap for newbies; Digital devices make use of pull-up and pull-down resistors to set input pins to a known digital state when the pin is not being driven by a source. The Jetson Nano 2GB does have these resistors, but the setup process to change their configurations is relatively complex, requiring the Nano to be re-flashed with a custom configuration file. Alternatively, we can just overpower the inbuilt 100kΩ resistors with external 1kΩ ones. Fortunately, this particular rotary encoder already has pads where I could solder the external 1kΩ pull-up resistors to the three output pins. Apart from a very minor increase in power consumption, this modification is completely safe and poses no risk to the Nano or the rotary encoder.

Conclusion

And that’s it! Now that we’ve learnt how to interface these five modules to our Jetson Nano 2GB, the next step is to combine them together and complete our ambient weather station. Check out Part 2 to see how we accomplish that.

About the Author

Ryan Agius is an AIoT Engineer working with SmartCow AI Technologies Ltd. at their Malta office. Before starting at his current position, Ryan worked as an Engineering intern with CERN as part of his thesis for a Masters in ICT (Signal Processing & Machine Learning) course.

--

--

SmartCow

SmartCow is an AI engineering company that specializes in advanced video analytics, applied artificial intelligence & electronics manufacturing.