Bits of Geekery

Building a Raspberry Pi weather station with Elixir/Nerves – Part 3

Part 2 ended without us being able to read the wind speed, let’s fix that!

Communicating via SPI bus

To communicate with the wind speed sensor, a binary payload must be sent to the MCP3008 chip over the SPI bus. The elixir_ale library will be used, as it supports SPI. The first step was to determine what channel the wind speed sensor is wired to. For my setup, that is channel 0, indicated by the top left most pin being used on the chip. Now that the channel is known, the binary payload can be constructed.

Binary to send to the SPI bus

Microchip provides the documentation necessary to figure out what should be sent to the MCP3008. The figure I needed was 6-2, it’s copied below for reference. Follow along when I explain how I came to my results.

For the correct binary payload, I needed to reference the MCU Transmitted Data section. There are 3 sections of bits, 8 bits per section. The first 7 bits are 0 with the 8th bit being a 1. This is the start bit, the following 4 bits are described using variables to be replaced based on the configuration of your pi. To find out what to set these variables to, I had to look at another table, 5-2, shown below.

The sensor is connected to channel 0, in a single-ended input configuration. If you map the values from that table to the variables we need to populate from figure 6-2, you will end up with the following:

Var Bit Value
D2 0
D1 0
D0 0

Replacing the variables results in the following binary (including the padding and start bit): 00000001 1000

When X is used in the MCU Transmitted Data section, the MCP3008 does not care what bits are used. I used 0 and ended up with: 00000001 10000000 00000000. Converted into hexadecimal gives us: 0x01 0x80 0x00, this is what will be sent to the chip via the SPI bus.

If you look at the elixir_ale example for SPI, it wasn’t clear to me that the second hexadecimal code is used for padding, this initially caused me to send the wrong data. Once I sat down with the MCP3002 docs, reversed the example, I figured out that the bit count sent to the SPI bus is the bit count received. It is equivalent to the trailing 0x00 in the hexadecimal codes shown above.

Sending the constructed binary

Elixir_ale makes sending the binary data a breeze. Call start_link with the device name to communicate with, to obtain the pid of the GenServer. Use SPI.transfer to send the hexadecimal codes, however you should use Elixir’s excellent binary pattern matching to capture the response from the chip. The example below only sends, but doesn’t capture, which will be shown in the next section.

alias ElixirALE.SPI

{:ok, pid} = SPI.start_link("spidev0.0")

# you should pattern match the results to read the sensor data
# shown in the next section
SPI.transfer(<<0x01, 0x80, 0x00>>)

Reading the binary results

I mentioned above that the bit count sent to the SPI bus will equal the bit count of the response. The MCU Received Data section of Table 5-2 defines the order of the bits for the response. Out of 24 bits, I only care about the last (low) 10 bits. Elixir’s binary pattern matching makes this insanely easy to capture.

# ignore the first 14 bits, bind `counts` to the remaining 10 bits
<<_::size(14), counts::size(10)>>

Next step is to convert counts into a much more usable voltage reading.

voltage = counts / 1023 * 3.3

I personally cannot (and don’t want to) convert from voltage to m/s in my head every time I want to know the wind speed. Also we unfortunately don’t use metric like the rest of the world, but that’s a rant for another day. The formula for the conversion from volts to m/s was taken off the spec sheet (written in Chinese) by my friend Dan. Thanks Dan!

# specific for the Anemometer Wind Speed Sensor w/Analog Voltage Output
wind_speed_in_ms = (voltage - 0.4) / 1.6 * 32.4

# close enough for government work
wind_speed_in_mph = wind_speed_in_ms * 2.2369

Complete example

alias ElixirALE.SPI

{:ok, pid} = SPI.start_link("spidev0.0")

<> = SPI.transfer(<<0x01, 0x80, 0x00>>)

voltage = counts / 1023 * 3.3

# specific for the Anemometer Wind Speed Sensor w/Analog Voltage Output
wind_speed_in_ms = (voltage - 0.4) / 1.6 * 32.4

# close enough for government work
wind_speed_in_mph = wind_speed_in_ms * 2.2369

Part #4 will involve adding the built functionality to the Elixir project, add a frequency for checking the sensors, and output to the screen. Then the Phoenix API can be started to persist results.

As always, if you have any questions, please reach out to me on twitter. I would love to hear what you think of the project so far!

Frank Kumro
Hi, my name is Frank Kumro. Writing Elixir and building hardware projects is a passion of mine. I enjoy sharing what I learn and hope it inspires others to do the same.