Raspberry Pi AD/DA Board library for Window 10 IoT Core

A fully functional C# library (Window 10 IoT Core) for the WaveShare “Raspberry Pi High-Precision AD/DA Expansion Board”

I got myself a Raspberry Pi High-Precision AD/DA Expansion Board to be used in a Windows IoT Core C# application.

The Raspberry Pi High-Precision AD/DA Expansion Board

The board has one 2-channel digital-to-analog converter chip (DAC8552) and a 8-channel analog-to-digital converter chip (ADS1256).

The two converter chips

All documentation provided by WaveShare was referring to Raspberry Pi running Linux and the source code examples was written in C, so I had to write my own library.

The first challenge was to try to understand how the Linux code actually communicated with the board, and it turned out to be quite a detective work.

The Linux examples needed a BCM2835 library to work, so I started with its source code to get an understanding of it. It took some time to wrap my head around it, but in the end it turned out not to be especially complicated.

Here are my findings…

The basics of the board

The two converter chips both communicate with the Raspberry Pi over the SPI bus which uses three pins of the Pi: data in, data out and a clock signal.

The Raspberry Pi GPIO pins, showing the SPI bus

Since the two chips share the same communication lines, somehow they need to know when the Pi wants to speak to the one or the other.

This is achieved by using two GPIO pins, one for each chip, controlled by the Raspberry Pi (who serves as the master of the communication). When the Pi is pulling the signal LOW on one of these pin means that the corresponding chip is selected. After communication the signal is set back to HIGH. (This is a common technique called chip select.)

The Raspberry Pi GPIO pins, showing the “chip select” pins

Now, simply put, by sending different commands over the SPI bus to the two chips, the Raspberry Pi is able to both set the voltage (between 0 and 5 V) on two different output terminals (using the DAC8552 chip) and read the voltage (between -5 and 5 V) on eight different input terminals (using the ADS1256 chip).

The analog input (purple) and output (blue) terminals

In the picture above the input and output terminals are marked. The top green plastic bar consists of 13 screw terminals on which you can connect to both the analog input and output signals.

The yellow block of pins to the left is designed to fit Waveshares different analog sensors.

The eight input pins are named AD0 to AD7 and the two output pins are named DAC0 and DAC1.

The basics of my code library

Even though the two chips are on the same board I chose to put the code to handle them in different classes. This is mainly because they are dealing with totally different things.

They have however a common wrapper-class I named AdDaBoard (implementing the public IAdDaBoard interface). This class owns the two chip-specific classes Ads1256 and Dac8552, named after the two chips. These two classes implements the public interfaces IAnalogInput and IAnalogOutput, respectively.

To get an instance of the AdDaBoard you’ll have to call GetAdDaBoard() on the static class AdDaBoardFactory instead of just creating a new
instance. The reason is that the chip-communication requires the .NET class SpiDevice
that must be instantiated asynchronously – and a .NET constructor (in this case for the AdDaBoard) cannot be asynchronous.

Sharing the SPI bus

I wanted a clear and foolproof handling of the SPI bus. Codewise, I wanted to:

  • Make sure only one of the two chips could use the SPI at any given time
  • Automatically controlling the output level of the two chip select pins

In the end I constructed a SpiComm class, managed by a SpiCommController. There is one SpiComm instance for each chip.

The SpiComm implements the public ISpiComm interface which has two versions of an Operate method. The Operate methods takes a lambda expression that temporarily gives access to the actual .NET SpiDevice class. One of the methods returns a value (of any type) and the other one doesn’t.

Calling Operate will first enter a lock statement, locking on a shared object for both the SpiComm classes. This ensures that the two SpiComm‘s cannot operate the SPI bus simultaneously. The second thing that happens is that the chip select pin is given a low output signal. Now the SpiDevice is handed over to the calling code, and when it returns the chip select pin is changed back to high and the lock is released.

Both the IAnalogInput and IAnalogOutput has the ISpiComm as a public property– this way the end user (you!) can gain access to the “raw” SPI bus exactly as the library code does. The reason is that my library is not fully complete; there are a number of features of the ADS1256 and DAC8552 chips that I left out.

The analog output converter

The analog output converter was the easiest chip to get to work. To specify one of the output voltage levels all it took was to send three bytes to the chip.

The first byte is a set of control bits, determining which of the two outputs to affect – and if the voltage value should only be stored in the chip’s internal buffer or actually going out on the pin.

The last two bytes holds the output voltage as a 16-bit number. A value of 0x0000 means the lowest possible output voltage (which is the same as GND; normally 0 Volt) and 0xFFFF means the highest possible (which is the same as VREF, normally 3.3 or 5 Volt).

The VREF voltage can be easily switched between 3.3 Volt and 5 Volt using a jumper on the board:

The VREF jumper position

Placing the jumper covering the top two pins (of the three marked above) connects 5 Volt to the VREF connection, and placing it covering the bottom two pins connects 3.3 Volt.

(The middle pin of the three is the VREF, and I assume you can connect it to other reference voltages.)

In my library code I chose to have two ways of specifying the output voltage; one taking the wanted voltage and the currently used VREF – and another taking the wanted normalized voltage (between 0.0 and 1.0). Both methods are called SetOutput but have different parameters.

There are also methods to be called to set both output values (SetOutputs). Doing this you ensure that the two outputs are changed at exactly the same time (if you would need that).

Take a look in the datasheet of the DAC8552 chip to see all the details. (Hint: they call the two output pins A and B.)

The analog input converter

The input converter chip ADS1256 was a bit more complicated. You can find the datasheet here.

First of all, it required one more GPIO pin called Data Ready (or DRDY in the datasheet).

The Raspberry Pi GPIO pins, showing the Data Ready pin used by the ADS1256 chip

This is a signal the chip basically uses to tell the Pi if it’s ready or not to accept new commands on the SPI bus. If the Pi reads a low level on the pin it means the chip is ready.

The behavior of the chip is controlled via a set of internal registers (see page 30 in the datasheet). In my library I make use of the first four (although there are eleven in total).

The registers can be controlled via my library using the properties of the IAnalogInput.

At startup I read the registers and convert the current settings to the class properties.

Changing any of the properties does not have an immediate effect. It’s not until a reading of any of the analog input pins they are written to the registers (and they are only written if they have changed since the last reading).

Nothing will happen if any of the properties be changed during a reading (perhaps by another thread); the ongoing reading will use the property settings as they were when the reading begun – the changed properties will affect the next reading.

One property is called AutoSelfCalibrate. This will make the chip re-calibrate before the next reading if any of the affected registers has changed since the last calibration. There is also a method called PerformSelfCalibration that will perform a calibration on demand. But I think the auto calibration feature is the best.

The Gain property is an enum that can be used to magnify a smaller reading. Using a gain of 1 allows the input be in the full range of -5 V to +5 V. A gain of 2 allows only half of that range – but with twice the resolution. A gain of 4 allows an input in the range of ±1.25 V and the highest gain (64) can only read an input between ±78.125 mV (see page 16 in the datasheet; it’s called PGA there, short for Programmable Gain Amplifier).

The DataRate is an interesting property. It specify how fast the chip should sample the input levels. It also determines how much filtering should be applied, if I understand the datasheet correctly. A very fast data rate means less filtering and vice versa. More filtering means a more exact value. The highest rate is 30,000 samples per second, but in reality you cannot squeeze these many readings out of the board – not on the Raspberry Pi running Windows 10 IoT Core, which is not a real-time operating system in this sense. This means, for instance, that the analog input is not very suitable for sampling sound. (I guess you could, but you would get a very lo-fi result.)

The effects of the data rate appears all over the datasheet. Search for “30,000” in the datasheet!

There is a “open/short sensor detection” on the chip, and it is controlled via the DetectCurrentSources property. You can read more about this on page 15 in the datasheet.

The final property is the UseInputBuffer which controls whether to use the embedded input buffer or not. Read more about it on page 15 in the datasheet. It is a “low-drift chopper-stabilized”, which sounds very cool, although I have no idea of what it means… 😛

Reading an input value can be done in two ways; either simply reading one of the eight pins (GetInput), or get a differential reading between two pins (GetInputDifference).

Either way you need to specify the vRef parameter, but that is just to scale the returned value to the right level. It doesn’t necessarily have to be the actual VREF voltage; it just sets the range of the returned value. If you for instance say vRef is 1.0 you will get a reading between -1.0 and 1.0.

Thread safety

The code should be completely thread safe. You may call any method or change any properties from parallel threads without having to do your own locking.

Running the demos

The board comes with a couple of components that simplifies playing with both the inputs and outputs.

The embedded testing gadgets on the board

The blue marking shows two LEDs that can be connected to the output signals. To rewire the output signals to them you must place a jumper marked with the green 1 and 2. (Jumper 1 is for the output pin DAC1 to LED B and jumper 2 is for output pin DAC0 to LED A.)

The big red marking is a potentiometer that may be connected to the input pin AD0 using the marked jumper 4. Turning the knob anticlockwise lowers the voltage on the first analog input pin and turning it clockwise raises it.

The smaller red marking shows a photo resistor that may be connected to the input pin AD1 using the marked jumper 3. Exposing it to light will affect the voltage to the second input pin.

To run my demo application RPi.AdDaBoard.Demo all the four jumpers should be connected (to make use of both the LEDs and both the input resistors).

In the constructor of the MainPage you will find a simple way of choosing which demo to run. There is one simple output demo, one simple input demo and one that combines both input and output.

The output demo makes the LED lights alternately go from completely off to full brightness and back repeatedly.

The input demo reads the voltage levels of the potentiometer knob and photo resistor and writes the values to the Visual Studio debug output console.

The input/output demo is a blend of the two; it takes a reading of the potentiometer knob and puts the value to the LEDs.

The demo code should be easy enough to understand how to use the library, but don’t hesitate to ask questions!

The Source Code

Can be found on GitHub under emmellsoft / RPi.AdDaBoard.

Connector Mapping

 

A copy of this blog post can be found among my projects on hackster.io.

Arduino Controlled Artificial Candle Lights

A while ago I started working on a project to subtly light up our atrium room which gets a bit too gloomy during the autumn and winter time. I wanted something more natural looking than hard LED lights, preferably the live look of flickering candles.

This is how it turned out:

The final result

A Christmas version

The story

Creating a realistic artificial flame is not an easy task, so my idea was to place the lights in a way where you don’t see the “flame” directly, but merely the light it produces, dancing on the wall.

Since the room is mostly made of glass walls, the obvious choice was to let the light be projected on the white beams holding the windows. I decided to place the lights on the bottom of the horizontal beam, projecting the light downwards on the vertical beams.

Picture from last summer showing one of the two glass walls
Picture from last summer showing one of the two glass walls

Preparation and planning

Since I wanted to be able to control each “candle” individually, the choice of LED light was easy; it should obviously be a set of WS2812-based LED modules, so I ordered 100 individual LEDs.

The individual WS2812 controlled LEDs I got came in sections of 10, easily cuttable

As you can see in the picture above each LED has six connections – and a direction marked with an arrow on the backside.

Two of the connections are marked 5V, two are GND and then there is the DIN and the DOUT. The two 5V are connected and so are the two GNDs. So in practice there are four connections; 5V, GND, DIN and DOUT.

After some experimenting with different number of LEDs per “candle” I came up with a number of four. This number allowed me to make enough interesting light manipulations on each beam to make a realistic candle look without demanding too many LEDs. However, should I have to do it again, I would have ordered a set of 2-by-2-LED modules, to simplify the assembly.

Early prototype showing a four-segment LED module, simply taped to the beam

The controller didn’t need to be very advanced, and my assumption was that an Arduino Nano would suffice.

The placement of the Arduino wasn’t obvious. In the end I chose to put it on the horizontal beam itself, behind the thick pillar that is visible in the first photo above (far to the left). The pillar is placed in the corner of the room where the two glass walls meets (of which the “right” wall is shown in the photo). This way I could have the LEDs on two parallel strings – one for each wall – to minimize the physical length of each string. Now the total length of each string would be around 4 meters (13 ft) instead of having one twice of that.

The Arduino Nano. The two white cables coming in on the right are the two LED strings

The Adafruit NeoPixel Überguide was a really good read here; if you plan to play with WS2812 LED modules you should definitely read it through! For instance, it told me why it’s good to limit the physical lengths of the wires.

Hiding cables…

…wasn’t that hard in my case since the LEDs were to be placed on the bottom side of the horizontal beam. I looked around for a cheap cable channel and found one on IKEA (can’t tell whether it’s ironic or obvious, being from Sweden).

The LED modules themselves were simply placed with a strong double sided sticky tape.

Creating the LED modules

Since I didn’t want to solder the whole thing together on a single full-length string (I imagined the cable jam I would encounter trying to install the thing on the bottom side of the beam) I decided to build the LED modules with connectors for the cables.

The LED modules were to be placed just next to the vertical beams in order to get as much light as possible be reflected upon it. I also wanted to have the cable going close to the beam – and I needed to honor the direction of the LEDs. This led (no pun intended) me to have two versions of the module; one setup placed on the string going to the right from the Arduino and one going left.

The layout of the 4-LED module on the string going right

The layout of the 4-LED module on the string going left

The two module versions required two unique soldering layout, where the main difference was keeping the data flow going from the correct LED to the next.

Red lines = 5V, blue = GND and green = Data

Since the LEDs are pretty small, about 9 millimeters in diameter (3/8″), it wasn’t easy to solder them; and given my lack of soldering experience the result isn’t that nice and pretty. But it worked.

The soldering

Before soldering, I cut off a 2-by-2 LED module from the 2-by-5 original sections. Then I painted one end red and the other one black to mark the sides close to the 5V and GND (in order to prevent stupid mistakes).

Step 1 The first soldering exercise was to put a ‘drop’ of solder on the tiny metal island on each LED.

Each island has gotten a bit of solder

The next step was to connect the simple straight, close-by connections.

The first two connections. As you can see, I have already managed to burn it a little… 🙂

Then following cables needed to keep their insulation,    since they were going over each other.

Soldering one end of the cable before cutting it made it easier

The final cable on this module is about to be attached

The final module

All in all there were five beams in each direction – plus the corner beam – giving a grand total of eleven beams. Since each LED module had four LED, the number of individual LEDS are 44.

After a few modules I got the hang of it, and in the end I could solder a complete module in about 30 minutes.

The “short-cable-trick”

Many of the tiny cables needed their insulation, but it was hard to cut of just enough insulation on each end to expose the inner core when the total length of the cable was less than a centimeter.

Then I figured out this (obvious) trick:

1. Solder one end of the cable, then bend and cut it to the desired length. (In this example the non-soldered end should be connected to the Dout of the lower right LED in the picture below.)


Solder one end of the cable, then bend and cut it to the desired length

2. Slide down the insulation a bit.

The insulation is slid down (making a “hollow, plastic tube” at the free end)

3. Cut off the desired length from the freed insulation.

A tiny stub of insulation is cut off

4. Slide the insulation back, exposing the core on the free end. Tadaa!

The insulation is slid back

This way the exact length of exposed cable core could be created, and the result was pretty nice:

Examples of short (but still pretty nice) cables

Finalizing the LED modules

In the end the LED modules should be attached up-side-down on the beam, so the soldered, cable-filled back-side must be prepared to hold the sticky tape.

I came to use some random flat plastic that I simply cut into squares. These were simply attached to the LED modules with hot glue.

Plastic squares to cover the backside of the LED modules

Here are all the complete modules! Hooray!

The eleven completed LED modules

(At this point it was vital to have some marking on them, to separate the ones going left from the ones going right!)

The complete modules was easily attached using strong sticky tape on their now flat backsides.

Making the cables

Luckily I had a roll of old telephone extension cord laying around. This cable had four separate wires which was more than enough, since I needed three wires (5V, GND and data).

Mounting the female sockets without the special crimping tool wasn’t that easy, but totally doable.

“Crimping” the individual connectors using a regular pair of pliers

The final look was almost professional

The cable channel was easily mounted; just cut it up into suitable lengths and use the pre-attached sticky tape to attach it to the horizontal beam.

One end of the IKEA cable channel

This is what a finalized mounted module looked like:

A mounted LED module

Automatic activation and deactivation

Since I don’t want to turn on the lights manually when it gets dark and turn it off afterwards, I put in a photo resistor.

I didn’t want a simple on/off, but a gradually change of light intensity during the twilight time. For this reason I needed to know the analog value of my photo resistor at the point in the evening when I decided that “daylight” turned into “twilight” and the point when “twilight” turned into “darkness”.

In the graph below the red curve represent the analog reading from the photo resistor as it shifts during a day (not an actual reading below, just my free-hand drawing). The two faint horizontal lines marks the two levels; the top one is the limit where “daylight” turns to “twilight” and the bottom one where “twilight” turns to “darkness”. So, when the red curve is above the top horizontal line I consider it to be DAY, and when it is below the bottom one it is NIGHT.

The green straighter line is the the “cleaned” daylight measurement, i.e. minimum (0.0) during night time and maximum (1.0) during the day, and a linear slope during twilight.

The daylight graph

To know the actual analog readings I hooked up four photo resistors to an Arduino together with an LCD screen to show the current, minimum and maximum values of the four resistors. I used four because I didn’t know if I had a bad one, so if most of them would have approximately the same reading I knew they would be working. Obviously I placed the device in the same spot where I intended to have the Arduino driving the LEDs in the end. This is how it looked:

Four photo resistors (between the Arduino and the LCD)

Since the LCD is quite limited I showed the reading of one photo resistor at the time for about five seconds. Then during the day I went over from time to time and wrote down the numbers on a piece of paper. (Obviously I could have kept it hooked up to my laptop and had the numbers be sent on the serial connection, but I needed my laptop during the day and didn’t want to sit in the atrium the whole day).

In the end I decided that it is dark below “630” and light above “800”. But those numbers obviously just fit my photo resistor together with the 10 kΩ resister I used in series, so it’s not an absolute truth.

The Arduino Source Code

I wanted to be able to have different types of lighting effects, not only candle lights. For this reason I built the source code modular, trying to isolate the different mechanisms in different files for easier overview.

The main .ino-file is kept really small. I basically just starts up the whole thing and calls Update() on a couple of helper-classes (which in turns does the trick).

Currently the source code has support for two different effects; the candle light effect and a “Christmas” effect. At the moment the choice of effect is hard-coded which means that I need to recompile the code if I want to make a switch. In the end this should be controlled using a remote control – or, even better, a smart phone. We’ll see.

The complete source code can be found on GitHub under emmellsoft / AtriumLighting.

Schematics

A Fritzing file can be found together with the source. It’s a really simple layout:

 

A copy of this blog post can be found among my projects on hackster.io.