Using a Qwiic UV Sensor with Zephyr

Mark Zachmann
Home Wireless
Published in
3 min readJun 4, 2020

The first sensor I connected to my BlueLora was a UV Sensor. It’s nice to know where high levels of UV are for killing Coronavirus, or for bleaching paintings and furniture, or maybe getting skin cancer.

BlueLora with a Qwiic UV Sensor attached

We have a lot of windows and suspected that some had UV coating and some did not. Do we need UV coating? Who knows…. time to find out.

Hardware

In the above picture, I’ve soldered a Grove connector right into the BlueLora board. The grove pins (at 2mm) are a bit closer together than the 0.1" pins on the board but a little bending works wonders. Then I used a Sparkfun Grove-Qwiic cable to connect the Sparkfun Qwiic UV Sensor board.

Showing the sensor working…

Since this is a Qwiic board, it uses the I2C protocol to communicate. The nrf52840 CPU supports two I2C ports. One is used here for the LED, the other port is used for the UV Sensor.

The below video shows starting up the UV Sensor application and then examining the effect of a good acrylic UV blocker.

Software

Sparkfun provides software for Arduino that uses the Arduino Wire (I2C) api. For simplest implementation I took and modified that code. You can find the new code at the Github Gist. The Gist starts with the original Arduino code and then has the changes to the new code, so you can do a diff there.

Here I just want to show the code changes. They fall into 3 groups.

Inclusions

Inclusion Changes

Here I get rid of the debugging stuff from Arduino, add the necessary includes, and use the Zephyr logging api.

Begin

Starting up the chip no longer saves the Arduino Wire instance but instead a pointer to the I2C Zephyr device. The Begin method also does a rather hacky reconfigure of the SDA/SCL pins to remove the pull-up because the Sparkfun board has its own (conflicting) pullups. This uses Nrf-specific Hal (hardware abstraction layer) code.

Note the very clever DT_PROP(DT_ALIAS… to get the sda and scl pin numbers from the devicetree :) .

Begin keeps the device pointer and turns off the I2c pullups

Here is the devicetree code for the i2c port that gets parsed:

spare_i2c: &i2c1 { /* User I2C */
compatible = "nordic,nrf-twi";
status = "okay";
clock-frequency = <I2C_BITRATE_STANDARD>;
sda-pin = <41>;
scl-pin = <40>;
};

I2c Flow

The Zephyr I2c interface is noticeably better suited to this application. Here we replace the clumsy debug methods with the builtin Zephyr LOG_ stuff and we replace the multiple i2c Wire request with a single burst read. The I2c write buffer method is changed exactly the same way.

The readI2CBuffer method redone for Zephyr

That’s all that’s required to support the Uv sensor. The rest of the arduino code just works (except for changing boolean to bool).

Postscript

Zephyr specifically supports sensor with an entire api (programming interface). So, there is a Zephyr-better way to do this but it’s out of the scope of this article.

--

--

Mark Zachmann
Home Wireless

Entrepreneur, software architect, electrical engineer. Ex-academic.