Tag Archives: featured

CuPID v2 RF HAT : Pi B+, Pi 2 & Moteino in Action

TL; DR

The Board:
Our CuPID HAT build, from beginning to end.
Our CuPID HAT build, from beginning to end.
The Final Product:
CuPID2_cased_smaller
Our CuPID, fully built. Also included here is our SPI LED indicator board, connected by SPI to the HAT SPI header.
CuPID v2 Features:
  • DS2483 1Wire bus master
  • 8P8C (RJ45-like) breakouts:
    • 8 Pi GPIO
    • 8 Analog Inputs from Moteino (328P), shared on pins with GPIO
    • Pi SPI, I2C1
    • 1Wire bus
  • I2C and SPI breakout headers (SPI compatible with CuPID indicator board)
  • Footprint for R4 or R5 Moteino RF Arduino clone
  • Serial interface connection for Pi/Moteino communication
  • 8 shared IO: Pi GPIO <–> Moteino (328P) Digital/Analog IO (state communication, mutual IO)
  • 4 Moteino DIO breakouts, two with pull-ups for I2c if desired
  • HAT EEPROM board and vendor information
Pinout
CuPID v2 Pinout
CuPID v2 Pinout

Background

What’s a CuPID? See here for a little background, or poke around the site here

Intro

The venerable Raspberry Pi, fueling frugal hackers’ dreams since its introduction, has been updated twice in the last year, with major improvements:

Rasperry Pi B+:

The B+ brought improved power management and increased power capacity, along with access to more of the broadcom 2835’s GPIO pins. Two USB ports were also added, along with a facility for on-board EEPROM to identify add-on boards via two dedicated i2c pins.  Physically, the 2×13 26-pin header was swapped out for a 2×20, two mounting holes were added, the existing were relocated for a square mounting grid.

Along with standard physical size standards, identification and configuration via EEPROM data, was built into a standard, called the HAT.  Today, we are going to build one.

Raspberry Pi 2

The RPi 2 swapped out the BCM2835 chip for a BCM2836, increasing the speed to 900Mhz with quad-core (from 700Mhz single-core), for a cited 6x improvement. This is obviously dependent on what you are doing … the memory was bumped up 2x to 1Gb from 512MB, which offer an inarguable performance improvement.

The most amazing part of this is that all of the above was added in a pin-compatible and (for the most part) completely backwards-compatible fashion. Awesome stuff.

The CuPID, v2

So how will we adapt? Technically, the v1 CuPID does fit onto the B+ and the RPi 2 without modification, but the location of the pins doesn’t exactly match compared with the main Pi board, so we don’t end up with a board that matches up perfectly vertically with the Pi. Besides, we want to break out all that yummy IO and set up EEPROM, so of course we had to make a new board. Hence, the CuPID v2.

RF Standard

Don’t get us wrong: we love the breakout on the base Pi for all the important functions of a Pi: GPIO, I2C, SPI, 1Wire, but we are super-invested in the idea of wireless sensing, everywhere. We have been documenting our affair with the Moteino line of arduino clone, RF-enabled devices in our series of remote monitors and controllers. An necessary piece to tie all of these together is the gateway: a Raspberry Pi with an RF receiver that talks to all of our nodes. For this reason, in the CuPID rev 2 board, we include a footprint for a Moteino. It does the following:

  • Powers the Moteino
  • Connects the Pi and Moteino serial interfaces
  • Attaches Moteino analog inputs to the COM2 and COM3 ports’ GPIO interfaces. This makes analog inputs available on the ports!
  • Breaks out four DIO 3-6 for external devices, such as I2C and SPI displays. These are all available DIO on Moteino. Two pull-ups are provided in case I2C is desired (on D3 and D4)
1Wire is universally compatible

We previously set our 1Wire in a way that made sense for our IO layout, but have modified it slightly in the newest revision. The new pinout is compatible with existing RJ45 pinouts, for example in use on the DS9490R USB, as well as iButtonlink adapters and devices. The new pinout makes our IO 100% compatible with all existing devices made by Dallas/Maxim and iButtonlink, using owfs.

EEPROM included

A new feature of the new Pi standard is an EEPROM chip that tell the Pi what hardware is in place and what default configurations should be to ensure compatibility with the hardware. The new CuPID HAT has an EEPROM that identifies the hardware. No defaults are modified.

What it looks like

Here’s a good picture of our board and everything needed to build it.

Our CuPID Hat, and everything needed to make it.
Our CuPID Hat, and everything needed to make it.

Compared to the original CuPID, v2 has fewer components, and most important is much, much easier to assemble. Gone is the micro USB surface mount connector, and all can be hand-soldered. The only surface mount components are the EEPROM and DS2483, which require careful installation, but the rest are through-hole and very manageable.

When we have it together, it looks like this, with and without the RF unit. You can see the standoffs we use to attach it to the Pi and also affix it to the bottom of the enclosure:

Built board, with and without RF unit.
Built board, with and without RF unit.

Finally, we mount the board into the enclosure. Nothing fancy here, except a handful of standoffs and some countersunk screws that we mount to the back of the enclosure. We mount the HAT to the Pi, and insert it into the enclosure. Another huge improvement, both with the B+/RPi 2 and the HAT, is how solid the mounts to the enclosure are. It’s really quite robust.

Hardware and board prior to mount and after insertion into our enclosure.
Hardware and board prior to mount and after insertion into our enclosure.

Now, it’s business as usual! Time to fire her up.

Until next time,

C

Reference

The above makes use of the open source libraries available on github, including sketches, python and all web libraries:

Explanation and installation here:

CuPID MEGA Remote: Networked Home Thermostat

Background

As we rambled on about previously, we’re pretty into the idea of a wireless, networked temperature controller. An industrial temperature controller with, you know, modern features like monitoring over the local network and/or the web. So we built a prototype, and recently an even better prototype. See here for construction:

http://www.cupidcontrols.com/2014/11/cupid-mega-remote-killer-controller/

Here, we take it for a spin in a pretty popular application: the home thermostat. Once again, the 10,000 foot view looks like this:

Basic CuPID Remote communications and control overview.
Basic CuPID Remote communications and control overview.

The Hardware

In the link above, we detail what goes into the box. Just to review, what we have is the following:

Available Inputs/Outputs:

We detailed all of the available IO on the Moteino mega here, see also here. On this module, we have five IO available on screw terminals:

  • IO0 : Isolated relay output, open-drain output, PWM output, digital output
  • IO1 -4 : Open-drain output, digital output, analog input, optional hardware pullup, optional hardware voltage divider to input

Internally, these IO map according to our MEGA map (full map here):

  • IO0 : io9, pin 14
  • IO1-4 : io21-24, pins 28-31
Used IO

Temperature input:

Here, we are going to use a waterproof 1Wire temperature sensor, the DS18B20. We’ll put it on IO4, and use a resistor pullup on the data line, as specified in the datasheet. The next board revision has available locations for though-hole resistor pullups, but for this guy we’ve just hand-soldered one into place.

Output contact:

Our furnace run signal is the closure of a contact. One side is a pretty typical 24VAC. Because we don’t want to mix signals, we use the contact of a mechanical relay installed on the board. This allows the controller to control more or less anything without worry for things like noise, overvoltage, etc.

Interface IO
  • 4-digit 7-segment with dot point LED display via MAX7221 (previously discussed with code here)
  • 12-bar bicolor display (red, green, red+green = yellow) display via 74HC595 shift registers) (previously discussed with code here)
  • Rotary encoder with button (right, left increment with no stop,  button knob press) (previously discussed with code here)

Controller Setup

First, we need to tell our controller what to read and what to do with it. We do this using the standard command set at controller setup, as detailed previously in our UniMote write-up. Configuration is as simple as issuing our standard command set over serial or RF. If, for example if we wanted to set the first pin as an analog input, which we might do for an RTD with one of the built-in voltage dividers:

Set mode to analog input:

~modparam;iomode;21;2

Set the read frequency:

~modparams;ioreadfreq;21;15

Enable input:

~modparam;ioenabled;21;1

Enable reporting (over RF):

~modparam;ioreportenabled;21;1

Set report frequency (default is 0, meaning report will happen each time input is read):

~modparams;ioreportfreq;21;30

For this controller, however, we are going to use a 1Wire DS18B20 temperature sensor, so our configuration for our input is as follows. Because 1Wire inputs are handled a bit differently (see here for details), they will automatically report each time they are read, using the tag ‘owrom’ instead of the ‘ioval’ used for general IO reporting. If IO reporting is enabled (as above for analog), it will also report using the ‘ioval’ message tag, as the 1Wire value is stored in the iovalues[] array on read, but we don’t need to double-report. This will probably change eventually, but for now it is what it is.

~modparams;iomode;24;4
~modparams;ioenabled;24;1
~modparams;ioreadfreq;24;15

Next we configure our output. We could enable reporting for this as well (to tell us when the furnace is on), but for now we’ll not.

~modparams;iomode;9;1
~modparams;ioenabled;9;1

And finally we configure and enable our control channel, setting the positive feedback to the output above.

~modparam;chanposfdbk;0;9
~modparam;chanenabled;0;1

We’ll configure the remainder of the channel parameters, such as deadband and delay time, from the interface.

Interface Operation

The way the interface works is pretty simple and described by the menu tree below under Controls and Menu Tree. By default, the display shows the current temperature on the LED numerical display and the bar graph shows where the temperature is relative to the setpoint.

Bar location: temperature relative to setpoint

In the bar display, the center is the setpoint. The current temperature is displayed relative to the setpoint on the bar display. Each bar is a unit, 1F in Fahrenheit mode or 1C in Celsius mode. If the temperature is within one temperature unit of the setpoint, two bars in the center will be displayed. If the temperature is two below the setpoint, the bar will be two to the left of center,  if it is three above, it will be three to the right of center … you get the picture. If it is beyond the numerical limit of the scale (6), it will show yellow at the end of the scale.

Bar color: controller action

If the controller is taking action, based on setpoint and deadband, the color will turn to red. If no action is being taken or pending (see below), the color will remain green.

Bar flashing or solid: controller action is active or pending

When the setpoint is outside of the currently measured value + deadband, the controller will attempt to take action. This will change the color of the bar display element. If the delay is > 0, however, the controller will wait until the delay time has expired before acting. During this time, the bar display will flash to indicate the action is pending.

Demonstration of several controller display modes. The bar display indicates process value relative to setpoint, as well as the action condition.
Demonstration of several controller display modes. The bar display indicates process value relative to setpoint, as well as the action condition.
Controls and Menu Tree

The menu is currently described by the tree below. Each movement vertically is controlled by a button push; horizontal is controlled by turning the rotary encoder knob.

CuPID Temperature Controller Menu Tree
CuPID Temperature Controller Menu Tree

 

Remote Data View/Display

As we detailed previously, the data is sent out on RF in json format, which our gateway RF Mote picks up and passes on to our gateway, a raspberry pi. The serial handler, which you can see here, handles a few different types of messages and puts them where they belong as inputs in our databases.

So magically, we open up our gateway and take a look and see our mote shows up on our motes table (this happens with no configuration required):

Our mote automagically shows up on our gateway once it receives its broadcast.
Our mote automagically shows up on our gateway once it receives its broadcast.

We add an interface from the UI for the mote, which has a NODEID of 15 (as you can see from the entry above). What this does is tell the updateio script that if there is a mote present in the motes table with a NODEID of 15, to insert it into our Inputs table.

Adding the interface for our mote ensures it will show up in our list of control inputs.
Adding the interface for our mote ensures it will show up in our list of control inputs.

And, as promised, our Mote temperature value shows up as an input:

Our list of inputs, now with our mote value inserted.
Our list of inputs, now with our mote value inserted.

We can edit it to give it a friendly name by expanding the entry and editing, which goes into a metadata table called ioinfo.

inputedit
Editing our Mote input entry to give it a friendly name.

We don’t have a direct UI edit page for the ioinfo table yet, but it’s on the way. In the meantime, we edit it using phpliteadmin, and also add to the options field the specification “formula:1.8*x+32”. This keyword will tell our IO updater to parse the input using the formula above (in python’s interpreter). In this case, we take the value in Celsius reported by the temperature controller and translate it to Fahrenheit for the UI. Easy peasy. Now let’s take a look at our data accumulating in our Dataviewer (this is some time later, temperature spikes from handling the temperature sensor):

Our dataviewer ... viewing mote data.
Our dataviewer … viewing mote data.

Set up alerts

Just for fun, let’s set up an alert so that when the temperature gets above or below a couple setpoints, we get an email alert. The current actions/notifications/indicators system is set up to handle arithmetic comparisons on Channel values. A Channel is the basic control structure for our CuPID Controller. It has an input, positive and negative feedback outputs, logging preferences, and an assortment of other control parameters. Although it’s only currently set up for simple on/off control (with deadband and delay), we’ve built in provisions for multiple feedback modes. It’s what we use for our reflow oven: set a TC input, set a GPIO output to a relay module, plug in a recipe, and it will control an action. If you don’t set any outputs (or disable them), it just acts as a data container, which in this case will allow us to set up alerts. In the future, it will allow remote setpoint and control parameter commands to be entered from the web UI (see Next Time below).

So let’s add one:

Adding a channel from the UI.
Adding a channel from the UI.

Then we specify that our Mote is the control input, and we’re done:

Editing our channel to specify our Mote temperature as Control Input
Editing our channel to specify our Mote temperature as Control Input

Next we create two alerts. One for Too Hot, and one for Too Cold. We could do other things like trigger on channel outputs active, which would take into account whatever control algorithm we’re using, or a boolean compare for if a channel is enabled (in case somebody accidentally disables it), or really do comparison on any value in the channels table (and eventually ANY table in the database).

Adding email alert actions.
Adding email alert actions.

For now, we configure the Actions, one will be less than, one greater than, both using the Control Value of Channel 1 (which we set above to be the reported Mote Temperature). There are a ton of other parameters you can set here, which are actually detailed in the Parameters expandable table item. Lots of flexibility.

Configuring email alerts. There are a ton of options here, and nearly anything can be set to trigger an action.
Configuring email alerts. There are a ton of options here, and nearly anything can be set to trigger an action.

To test our alert, we set the Too Cold Alert to have a temperature of 70, meaning that we should get an email if it drops below 70 for the specified OnDelay time. Sure enough, I get an email:

Test email alert.
Test email alert.

And after we change the temperature target back to a more reasonable 60F, we get an email letting us know our Alert has turned off. This email, called “Active Reset”, is optional.

Next Time

Remote setpoint by Web GUI:

We already have all the tools necessary to enable control from our gateway. We can send commands over RF in the same format we used above, and with this we can change setpoint, control parameters, you name it. Next time, we’ll add in the control library infrastructure to send remote commands in an object-oriented fashion. We’ll need to do some thinking about data concurrency and race conditions, especially considering we don’t have an RTC on our thermostat.

Finally, we’ll add some shiny widgets to our UI to control all of it. That will be the easy part.

Alert on All the Things

As shown above, we have only partially fleshed out the Actions feature. We need to be able to alert on everything. Why not? We also want to be able to add more complex logic. Like … if one of five rooms is cold, turn on the heat. This will likely entail creating some auxiliary variables. We’ll have a think on that.

Reference

The above makes use of the open source libraries available on github, including sketches, python and all web libraries:

Explanation and installation here:

CuPID MEGA Remote : Killer Controller

Last we met, we built a prototype temperature wireless RF temperature controller. We got some good ideas and a Beer Fridge monitor out of the deal. We also realized, however, that we were flying pretty close to the sun in terms of the available memory we had to work with. Considering we weren’t concerned about power consumption, wanted to make our IO as flexible as possible (for example more interrupts and UARTs), we decided that a more powerful micro is in order. Enter the Moteino MEGA. Take a Moteino R4 and swap out the ATMEGA328P for a 1284P, add a reset button, and you get the Moteino MEGA. Fantastic idea. It’s a slightly larger footprint, but all progress has a price, right?

Now since last time, when we realized soldering was going to be a chore with all of our itty-bitties on one board, we had a couple PCBs made. Since we wanted to use the same enclosure and still use all hand-solderable DIP components, we ended up needing two PCBs stacked. We left the headers for our Moteino exposed, however, so we can still get to them easily. Shown below are the boards with all of the components (some optional) that we can jam onto it.

 

Components for our MEGA controller
Components for our MEGA controller

The other great part about stacking boards is that it will put our displays high enough that we won’t need to attempt to fix them to the inside of the enclosure. This helps for taking it apart and service, and also eliminates the need for the messy silicone arrangement we were attempting on the last build. Here are a few photos after it’s all assembled. You’ll notice the switch mode power supply is not used here, as we’re controlling a thermostat, which has a 24VAC contact. We put this on the mechanical relay, an advantage for when you need to handle things like AC voltage.

Our board and components, assembled, with a demo fit into our box.
Our board and components, assembled, with a demo fit into our box.

The astute reader will note that the board has been modestly pruned in a few locations where my CAD skills and attention to detail were a bit, um, unfocused. This is why we prototype! Here are a few more pics that show the fit, including the height of the displays:

Profiles of our controller, all assembled.
Profiles of our controller, all assembled. The displays are nicely elevated for viewing

Finally, we need to attach our encoder for user input, as well as the remainder of our IO. Here, we’re going to use a waterproof 1Wire temperature sensor, and close a relay contact for 24VAC that runs the house furnace. We’ll power it on a USB power supply and feed that in. We’ll also need to drill holes for the cable glands. We won’t bore you with those details. Here are a few pictures from during assembly:

Getting all the IO into the box. In this case, we've got 5V USB power in, a 24VAC furnace run contact, and a 1Wire temperature sensor.
Getting all the IO into the box. In this case, we’ve got 5V USB power in, a 24VAC furnace run contact, and a 1Wire temperature sensor.

And finally, here is the end product:

All wired up! Everything sealed up and in its place.
All wired up! Everything sealed up and in its place.

Now on to the installation and programming. In our next post, we build this guy into a home thermostat using a Raspberry Pi in our CuPID base unit as our control/sensor gateway.

Reference

The above makes use of the open source libraries available on github, including sketches, python and all web libraries:

Explanation and installation here:

 

CuPID Remote Enhanced : Flexible IO Mote

Introduction

So recently, we’ve been down the road of creating a versatile harem of remotes. Of particular interest is our small form factor unit, which eschews any front-panel indicators or controls for low power and size.

We want this unit to be small, flexible, attractive, and powerful. We want to be able to have it battery or wall-powered. For inputs, we want 1Wire, digital, analog, and anything we can do on Arduino digital pins. For outputs, logic-level digital, as well as open-drain transistor outputs would be nice. We also want to monitor battery power, which we typically do with a voltage divider, as we did before.

Previously, we showed we can package this all up in a little box we like in our CuPID remote:

CuPID Remote

Since we’ve nailed down our design, we decided to get it onto a board, clean up the box, and make it pretty.

The Board

Let’s introduce the board:

Our CuPID Remote board: flexible and capable, and fits just right. Really, just barely.
Our CuPID Remote board: flexible and capable, and fits just right. Really, just barely.

The Fit

It’s tight. I mean, really tight. If we do nothing, we can wedge the board in there, and we wouldn’t even need to screw it down. The round corners above are gently filed, giving a free fit. Here’s how it fits:

It fits, but barely. But with so little room, we need to make it all count.
It fits, but barely. But with so little room, we need to make it all count.

We’ve left room at the top and bottom, where we’ll drill and fit cable entries. They need a bit of room for the sealing nut and for the cable bend.

Putting it together

So here, we’ll put together one configuration of this remote: a USB-powered, remote sensor node. This one will go into a system with a powered sensor, so our power output screw terminal will come in handy. Here’s our parts layout:

Our parts layout. We don't need to use the female headers, but it's nice to be able to pull the RF unit out if we want.
Our parts layout. We don’t need to use the female headers, but it’s nice to be able to pull the RF unit out if we want.

After some soldering, we get this:

Our assembled board, and how it fits. Looks good!
Our assembled board, and how it fits. Looks good!

Finally, we put our holes in our enclosure, and feed in some USB power into our terminal blocks. We’re ready for whatever sensor we decide to use.

Our completed USB mote, ready for sensor.
Our completed USB mote, ready for sensor.

Direct Raspberry Pi IO with Apache and WiringPi

So last time, we demonstrated how to control your Pi IO with an intermediate database, using either our CuPID Controls UI and interface or a barebones installation with IO setpoints. We like this arrangement, for many reasons, such as error-handling, safety interlocks, datalogging, race conditions, and many other bonus features. It’s what we use for all of our applications.

We have come to realize, however, that there are advantages and elegance to an even simpler approach. Specifically, if you want the fastest possible response and minimal infrastructure, you may want to manipulate the Raspberry Pi IO directly. You may not want to rely on an intermediate database and daemon script to enact your commands.  For each application there is a best (or better) approach.

For this reason, we decided to find an effective way to bypass the typical problem of manipulating IO from a web interface: scripts run by anybody other than root, www-data for example, cannot execute the typical API, RPi.GPIO, to enable or disable GPIO outputs. This is currently possible using specialty web services, such as WebIOPi, but we would like to do so on a standard web server, such as Apache. This offers the possibility of doing so over not just your local network, but from anywhere you wish, as it is over a universal web interface, using javascript and html.

WiringPi offers this possibility, using the IO access from /sys/class/GPIO, as documented here. We don’t do anything special here except set up a wsgi method to execute these commands and call it using some javascript/jQuery. These are detailed below.

 The Requirements

  • Apache with mod_wsgi enabled
  • WiringPi. Installation is covered here
  • wsgi script alias directed to your wsgi script, something like this in your site configuration file:
 WSGIScriptAlias /wsgigpioactions /usr/lib/iicontrollibs/wsgi/wsgigpioactions.wsgi
  • A wsgi script, at location above, outlined below
  • An html document sourcing jQuery, so we can use the ajax call

WiringPI

We won’t bore you with stuff better covered elsewhere, like all of the features of WiringPi, or how to install it. The main feature we exploit is listed here:

  • wiringPiSetupSys (void) ;

This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly. This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program. Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be aware of the differences between Rev 1 and Rev 2 boards.

The key feature, in bold, is that we can manipulate GPIO without root access. This is key, as our scripts executed from a web page via wsgi will be run as www-data.

Another nice feature of the package is that it comes with a handy command-line utility, gpio, that allows us to do our manipulation quite easily. So the code below will set up GPIO 18 for non-root access, and turn it on and then off:

gpio export 18 out
gpio -g write 18 1
gpio -g write 18 0

It doesn’t really get much easier than that. The export command automatically uses BCM numbering, and the ‘-g’ will let our gpio write command know to also use this scheme.

Although there is a python wrapper for this utility, we call it directly from our python scripts with a couple simple functions. More on that later.

The WSGI script

The server-side script is the special sauce. It is what will take our web page command and read or write on GPIO using WiringPi with the basic commands listed above. The mechanics of the wsgi call are explained here and here, but the gist is that with some leading headers and a return of data as requested, we can pass data to a wsgi script in json format, execute as we see fit in python, and return the data in a dictionary to do as we see fit in our web page. Take a look here for the complete wsgi script. We pass an object to the script in json defining an action, and act on our action as we see fit. Here, for example, is our action on an output toggle:

if d['action'] == 'wptoggleGPIOvalue':
    try:
        BCMpin = int(d['BCMpin'])
    except KeyError:
        data['message'] = 'No pin sent with command'
    else:
        from subprocess import check_output, call

        output = int(check_output(['gpio','-g','read','18']))
        # call(['gpio','export',BCMpin,'output'])
        if output == 0:
            call(['gpio','-g','write',BCMpin,'1'])
        else:
            call(['gpio','-g','write',BCMpin,'0'])

This is really basic stuff. The command assumes the gpio has already been exported as writable by /sys/class/,  reads the state, and writes the inverse. Piece of cake. We’ll add in some error-handling and export py-fu, but these are the nuts and bolts.

Polling data from the web page using jQuery

Using some basic ajax, we can execute calls to our wsgi script to get our data. The basic function is below:

function wsgiwpgpioactions (commands,callback) {
    // Get the data
    commands=commands || {action:'testaction'}
    callback = callback || logdone;
    $.ajax({
        url: "/wsgigpioactions",
        type: "post",
        datatype:"json",
        data: commands,
        success: function(response){
            callback(response)
        }
    });
}

We just need to pass it an action to carry out and a callback to execute on the data. It will execute asynchronously, execute the command we have issued, and return whatever data we have requested. Linking this to buttons, we can set up the jQuery action on a keypress:

$("testbutton").click(function)(){
    var command = {action:'wptoggleGPIOvalue',GPIOid:18}
    wsgigpioactions(command)
}

So in the above function, if we create a button with id of ‘testbutton’ and click it, it will execute a toggle of GPIO 18 as listed in the wsgi script above. Easy enough. If we want to get a little more complicated with it, we can apply the same key behavior to multiple button classes with different gpio IDs. The function below uses a helper function to extract a GPIO id from the DOM based on where the click came from. This way we don’t have to write  a button behavior for each:

$('.canpress').click(function(){
        var clickid = this.id
        var result = getGPIOfromid(clickid)
        if (result.mode == 'mode'){
            var action = 'wptoggleGPIOmode'
        }
        else if (result.mode == 'value') {
            var action = 'wptoggleGPIOvalue'
        }
        var command = {action:action,GPIOid:result.GPIOid}

        if (runqueries) {
//            alert('running command:')
//            alert('action: ' + action + ' , GPIOnum: ' + result.GPIOnum)
            wsgiwpgpioactions(command,updatestatusdata)
        }
        else {
            alert('queries not activated for command:')
            alert('action: ' + action + ' , GPIOnum: ' + result.GPIOnum)
        }
    });

Our helper function:

function getGPIOfromid(clickid) {
    if (clickid.search('mode') > 0){
            var mode = 'mode';
            // Chop up string
            // starts with GPIO
            // double-digit number
            if (clickid.search('mode') == 6) {
                var GPIOnum = clickid.slice(4,6);
                var GPIOid = clickid.slice(0,6);
            }
            // single digit number
            else {
                var GPIOnum = clickid.slice(4,5);
                var GPIOid  =clickid.slice(0,5);
            }
        }
        else if (clickid.search('value') > 0) {
            var mode = 'value';
            // Chop up string
            // starts with GPIO
            // double-digit number
            if (clickid.search('value') == 6) {
                var GPIOnum = clickid.slice(4,6);
                var GPIOid = clickid.slice(0,6);
            }
            // single digit number
            else {
                var GPIOnum = clickid.slice(4,5);
                var GPIOid  =clickid.slice(0,5);
            }
        }
    return {GPIOnum:GPIOnum, mode:mode, GPIOid:GPIOid}
}

 Putting it all together

Now, as we did before, we’ll set up a physical GPIO interface page, so we can monitor status and punch some buttons. First of all, we’ll need to get our gpio status regularly to update our buttons. So we wrap a text parser around the gpio readall function to return a dictionary array of current gpio statuses:

def getgpiostatus():

    from subprocess import check_output

    gpiolist=[]
    alloutput = check_output(['gpio','readall'])
    lines = alloutput.split('\n')[3:18]
    for line in lines:
        BCM1 = line[4:6].strip()
        wpi1 = line[10:12].strip()
        name1 = line[15:22].strip()
        mode1 = line[25:30].strip()
        val1 = line[32:34].strip()
        phys1 = line[36:39].strip()

        phys2 = line[42:44].strip()
        val2 = line[46:48].strip()
        mode2 = line[50:55].strip()
        name2 = line[57:65].strip()
        wpi2 = line[68:70].strip()
        BCM2 = line[74:76].strip()

        if BCM1 and BCM1 != '--':
            gpiolist.append({'BCM': BCM1, 'wpi': wpi1, 'name': name1, 'mode': mode1, 'value': val1, 'phys': phys1})
        if BCM2 and BCM2 != '--':
            gpiolist.append({'BCM': BCM2, 'wpi': wpi2, 'name': name2, 'mode': mode2, 'value': val2, 'phys': phys2})

    return gpiolist

We put that into our general-purpose library, pilib, and set up our wsgi script to call it (you can easily grab the function from our git blob and put it into your wsgi script if you don’t want pilib)

if d['action'] == 'wpgetgpiostatus':
    from pilib import getgpiostatus
    data = getgpiostatus()

And define a function in our web page to call the wsgi and return the data to the web page:

//// Update all GPIO data
function updategpioData() { wsgiwpgpioactions({action:'wpgetgpiostatus'},updatestatusdata)
}

This function runs our our ajax function wsgiwpgpioactions, with a callback of updatestatusdata, which will run once the ajax function is done. This function  takes the data as returned and inserts it into the DOM using some jQuery;

function updatestatusdata(statusdata){
    var text = '';
    $.each(statusdata, function(index,gpiodict){

        var value = gpiodict.value;
        var mode = gpiodict.mode;

        var valueelements =  $('.' + 'GPIO' + statusdata[index].BCM + 'value');
        valueelements.removeClass('on')
        valueelements.removeClass('off')
        if (value == '1'){
            text = 'On';
            valueelements.addClass('on')
        }
        else if (value == '0'){
            text = 'Off';
            valueelements.addClass('off')
        }
        else {
            text = 'Err';
        }
        valueelements.html(text);
        var modeelements = $('.' + 'GPIO' + statusdata[index].BCM + 'mode');

        modeelements.removeClass('input')
        modeelements.removeClass('output')
        if (mode == 'IN'){
            text = 'In';
            modeelements.addClass('input')
        }
        else if (mode == 'OUT'){
            text = 'Out';
            modeelements.addClass('output')
        }
        else if (mode == 'ALT0'){
            text = 'I2C';
            modeelements.addClass('I2C')
        }
        else {
            text = 'Err';
        }
        modeelements.html(text)
    })
}

So all we have to do is label our html elements properly with classes, and our update function will populate them nicely. For example:

<tr>
   <td>11</td>
   <td align="center"><div class="GPIO17value lightindicator canpress" id="GPIO17value">X</div></td>
   <td>&nbsp;</td>
   <td align="center"><div class="GPIO18value lightindicator canpress" id="GPIO18value">X</div></td>
   <td>12</td>
</tr>

You’ll notice above we added and removed ‘on’ and ‘off’ classes, and a few others, so that the buttons and elements look proper when they change values:

.lightindicator.on, .lightindicator.input {
        background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #77ff77), color-stop(1, #00cc00) );
        background:-moz-linear-gradient( center top, #77ff77 5%, #00cc00 100% );
        filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#77ff77', endColorstr='#00cc00');
        background-color:#ededed;
    }
    .lightindicator.off {
        background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #dddddd), color-stop(1, #777777) );
        background:-moz-linear-gradient( center top, #dddddd 5%, #777777 100% );
        filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dddddd', endColorstr='#777777');
        background-color:#ededed;
    }

Demo

So let’s do something a bit more interesting with the framework that demonstrates we can do some real-time operation. We stripped down our Pi GPIO page EVEN FURTHER to contain only four buttons. We define them as 18, 23, 24, 25, in this version. We create four super-big buttons on our page, like so:

Our basic four GPIO button screen. Press a button, turn on an output.
Our basic four GPIO button screen. Press a button, turn on an output.

Next, we hook up some china special laser diodes on a board with some mosfets. We use one of our power output boards because it nicely breaks out our GPIO and power on the CuPID COM1 or COM2 bus. This is really nothing fancier than directly connecting our GPIO to the transistor gates. We’re just using what we have available here … and using our COM power lets us put our lights wherever we want (or as far as we care to stretch ethernet cable. Anyhow, here are a few pictures:

Our test setup. A CuPID and a few laser diodes, hooked up to four GPIO.
Our test setup. A CuPID and a few laser diodes, hooked up to four GPIO.
A close-up of our output setup. We're using a CuPID output board as a breakout for the GPIO on ethernet and to grab 5V and ground.
A close-up of our output setup. We’re using a CuPID output board as a breakout for the GPIO on ethernet and to grab 5V and ground. We machined up a little holder for our laser diodes.
The backside of our output breadboard. Four NPN 2N7000 mosfets, four 47ohm current limiting resistors, and four 10k pull downs on our outputs.
The backside of our output breadboard. Four NPN 2N7000 mosfets, four 47ohm current limiting resistors, and four 10k pull downs on our outputs.

And finally, let’s show it in motion. Beware, poor quality video.

Adventures in Moteino: Remote Temperature Monitor

Introduction

<<Edit>>: See here for next post on enhanced model: http://www.cupidcontrols.com/2014/10/cupid-remote-enhanced-flexible-io-mote/

What every new system design has in common these days is wireless. Like bacon, it just makes everything better. Put a sensor wherever, read it from somewhere else. Put the power and control where you need it. For the CuPID/Pi, it is no different. We want to put our remote sense and control modules out into the wild and read and aggregate them as it makes sense.

Like bacon, wireless makes everything better.
Like bacon, wireless makes everything better.

Our basic system layout is as below. We’ve got multiple wireless nodes that broadcast data periodically, and a controller/aggregator that will log this data, acknowledge receipt, and do something useful with it. Eventually, we may have intermediate powered nodes that serve to mesh the grid out, but for now, our nodes just send data to the controller.

We’re currently using these awesome little RF units, called Moteinos. They are an Arduino clone that can use the standard IDE with their bootloader. They’ve got the ever-so-popular ATMega328P chip that is familiar to anybody working with an Arduino Nano or Uno. The Moteino comes with an RF board using the SPI bus of the microcontroller, available in various frequency flavors, and also in two different power varieties: one for maximum range, and one for maximum battery life. The whole thing comes nicely arranged on a PCB with a built-in voltage regulator, headers to connect our FTDI cable for loading and diagnostics, and an antenna. All for about $20. Really a fantastic deal .. and great customer support. Please patronize them.

Our basic remote unit layout and communication structure. Low power on the remote sensor end, and high functionality on the controller end.
Our basic remote unit layout and communication structure. Low power on the remote sensor end, and high functionality on the controller end.

Anyhow, so we jammed one of these into a box in another post, and now we’d like to complete the job of creating a general purpose remote wireless node, along with getting it talking and doing something useful.

As a post-note, we took the basics we hashed out here and put them into a modular Mote sketch. This post is still useful for a demonstration of how we put these pieces together.

We also went on to build up the backend data processing, and talk about that elsewhere as well. Check it out.

The Project

So let’s get started. Our goals for this project are the following:

  • Get our remote unit reading a 1Wire temperature sensor
  • Get the remote unit periodically broadcasting data in a standard message format
  • Configure the unit to request acknowledgement
  • Read data from the unit from a remote CuPID Control unit
  • Configure the unit to go into a low-power state between read operations after receiving acknowledgement or after fixed number of retries
  • Package the unit into a waterproof enclosure

The Prototype Setup

We set up our remote and gateway units, both connected by USB on virtual COM ports. At right, the remote unit is connected to a 1Wire DS18B20 temperature sensor, with a pull-up resistor on the data line.

Prototype setup for node/gateway test communication. Connection to two virtual COM ports allows reading activity on both Moteinos.
Prototype setup for node/gateway test communication. Connection to two virtual COM ports allows reading activity on both Moteinos.

The Remote Unit

Basic Communication

We’ve got our wireless unit (we’ll use the lower power RFM69 to start, for our remote unit), an FTDI/USB adapter, and a cable to plug her into the computer to do some programming. Let’s get talking. We grab the library from the git repo and put it somewhere our IDE can find it. We grab the SPIFlash repo as well so we can just jump into some example code.  We take the boilerplate ‘sender’ sketch and put it on our low power unit. Nothing surprising there – compiles and uploads fine!  We open up a PuTTY console on our windows box and are happily greeted:

Listening at 433 Mhz...
 SPI Flash Init OK!
Reading Temperature

Now let’s get a temperature sensor wired up and reading. We use a trusty DS18B20, the workhorse of our temperature-sensing toolbox. We hook up ground, power to our 3.3V rail, and data to one of our ATMega digital pins with a 4.7k pullup between it and 3.3V. We’ve got it connected to A0, pin 14 on the ATMega. We modify our sketch to spit out data in ASCII json on the serial port, using a main block like this, using OneWire library code. Not shown is the include line for the OneWire.h library, or a couple subroutines. You can find the complete sketches in the reference section at the end.

// Message Type 
Serial.print("msgtype:");
Serial.print(msgtypestr);
Serial.println(",");
    
// Message Source ID 
Serial.print("sourceid:");
Serial.print(SOURCEID);
Serial.println(",");
    
// Message Dest ID 
Serial.print("destid:");
Serial.print(DESTID);
Serial.println(",");

// Device identifier
byte dsaddr[8];
getfirstdsadd(dsaddr);
    
Serial.print("dsaddress:");
int i;
for (i=0;i<8;i++) {
  Serial.print(dsaddr[i], HEX);
}
Serial.println(',');

// Data
Serial.print("temperature:");
float temp = getdstemp(dsaddr);
Serial.print(temp);

Here are the subroutines for the OneWire stuff:

void getfirstdsadd(byte firstadd[]){
byte i;
byte present = 0;
byte addr[8];
float celsius, fahrenheit;
int length = 8;
//Serial.print("Looking for 1-Wire devices...\n\r");
while(ds.search(addr)) {
//Serial.print("\n\rFound \'1-Wire\' device with address:\n\r");
for( i = 0; i < 8; i++) {
firstadd[i]=addr[i];
//Serial.print("0x");
if (addr[i] < 16) {
//Serial.print('0');
}
//Serial.print(addr[i], HEX);
if (i < 7) {
//Serial.print(", ");
}
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
//Serial.print("CRC is not valid!\n");
return;
}
// the first ROM byte indicates which chip
//Serial.print("\n\raddress:");
//Serial.print(addr[0]);
return;
}
}
float getdstemp(byte addr[8]) {
byte present = 0;
int i;
byte data[12];
byte type_s;
float celsius;
float fahrenheit;
switch (addr[0]) {
case 0x10:
//Serial.println(" Chip = DS18S20"); // or old DS1820
type_s = 1;
break;
case 0x28:
//Serial.println(" Chip = DS18B20");
type_s = 0;
break;
case 0x22:
//Serial.println(" Chip = DS1822");
type_s = 0;
break;
default:
Serial.println("Device is not a DS18x20 family device.");
}
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
delay(1000); // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
//Serial.print(" Data = ");
//Serial.print(present,HEX);
//Serial.print(" ");
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
//Serial.print(data[i], HEX);
//Serial.print(" ");
}
//Serial.print(" CRC=");
//Serial.print(OneWire::crc8(data, 8), HEX);
//Serial.println();
// convert the data to actual temperature
unsigned int raw = (data[1] << 8) | data[0];
if (type_s) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
// count remain gives full 12 bit resolution
raw = (raw & 0xFFF0) + 12 - data[6];
} else {
byte cfg = (data[4] & 0x60);
if (cfg == 0x00) raw = raw << 3; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw << 2; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw << 1; // 11 bit res, 375 ms
// default is 12 bit resolution, 750 ms conversion time
}
}
celsius = (float)raw / 16.0;
fahrenheit = celsius * 1.8 + 32.0;
//Serial.print('Celsius:');
//Serial.println(celsius);
return celsius;
}

With this code, our output looks something like this (using PuTTY on our virtual USB COM port):

msgtype:1,
sourceid:87,
destid:1,
dsaddress:2811FDE230092,
temperature:22.00

Perfect.

Going On-Air with RF

Now we just need to format this into a radio-friendly format. This is not quite as easy as it seems. With the serial output, we can just continually send ASCII bytes with no regard to message beginning or end. With radio, this is not the case. We need to formulate our entire message before we send it. We broadcast it with a destination and source ID on a Network ID, put it all together with a CRC to ensure data integrity, and then encrypt the whole lot with shared keys. We are SO grateful that Felix over at LowPowerLab has already done this for us inside of the RFM69 library, as we were not looking forward to writing it all ourselves. It also has Acknowledge implementation, so we just saved days and days of coding. Thank you!

Alright, so on to putting our message into a string of bytes. Reading here, we see that the message can be a maximum of 61 bytes (to support AES encryption). The most efficient way to do encode our message is to definitively split our message up into identifiers and values. We can use either fixed-length fields or delimiters. We’ll use json-style delimiters, as we did in our serial output above. Our identifiers will tell whomever is listening on the other end how to interpret the data. So, for example, we’ll start with an identifier like “tempasc” that will indicate we are sending a temperature in ascii-encoded numeric characters. This is of course not the most efficient way to send the data. For example, we would send a value like 20.0625, taking seven bytes, where if we encoded it as a four-byte float value, we’d both save three bytes and allow for a very wide range of values indeed. We’ll get to that later. Baby steps.

So we initialize a character string of maximum length of 61 bytes and start filling it with our data, and then send it on over:

char sendstring[61];
int sendlength = 16;
int wholePart = temp;
int fractPart = (temp - wholePart) * 10000;
sprintf(sendstring, "tempasc: %d.%04d,", wholePart, fractPart);
    
Serial.println("SENDING");
Serial.println(sendstring);
radio.sendWithRetry(GATEWAYID, sendstring, sendlength);
Serial.println("SEND COMPLETE");

We check out our serial output and see what we see. Everything looks in order:

msgtype:1,
sourceid:87,
destid:1,
dsaddress:2811FDE2300,
temperature:22.25
SENDING
tempasc: 22.2500,
SEND COMPLETE

Since we’re here, we’ll go ahead and add a little metadata:

char sendstring[61];
int sendlength = 61;  // default
int wholePart = temp;
long fractPart = (temp - wholePart) * 10000;
sprintf(sendstring, "tempasc:%3d.%04d,tempdev:ds18x,temprom:%0xx%02x%02x%02x%02x%02x%02x%02x%02x", wholePart, fractPart, dsaddr[0],dsaddr[1],dsaddr[2],dsaddr[3],dsaddr[4],dsaddr[5],dsaddr[6],dsaddr[7]);
sendlength = 57; 
Serial.println("SENDING");
Serial.println(sendstring);
radio.sendWithRetry(GATEWAYID, sendstring, sendlength);
Serial.println("SEND COMPLETE");

This gives us the following on serial output:

SENDING
tempasc: 23.0625,tempdev:ds18x,temprom:0x2811fde203000092
SEND COMPLETE

This will work nicely. As we mentioned previously, we cleaned and wrapped the above code into a modular sketch that we now use for everything.

Sleep, Moteino, Sleep

One of the beautiful things about this setup is that the Moteino is tiny, the sensor is low-power, and the controller is also set up to draw very low power when sleeping. The LowPower library does just this, and quite easily. Also built into the RFM69 library is a simple radio.sleep() function that will put our RF to rest. We implement it at the end of our loop as here:

    Serial.flush();
    radio.sleep();

    for (i=0;i<numloops;i++) {
      LowPower.powerDown(sleepperiod, ADC_OFF, BOD_OFF); 
    // Sleeptime eludes millis()
    }
    looptime += LOOPPERIOD;

The serial flush function fixes some serial garble issues with power down (see discussion here). Also note the last bit where we add in the LOOPPERIOD. As it turns out, millis() stops when we power down the Mote. As a result, we need to manually add the time in to the looptime. We use this in the IO and channels processing, so we want to keep (relatively) accurate time. After these adds, we get the following for a sleep time of 3000:

Going to sleep for 
3000
numloops 12

Looks good! Again, see this post for a more exhaustive description of how the nuts and bolts of these routines work.

Now let’s get to receiving!

The Gateway

Basic Communication

Now let’s make sure we’re receiving what we want on the other end (at the gateway).  Rather than have much intelligence inside of our gateway, we’re going to configure it as a repeater of data over the serial port, which will be attached to our controller, in this case a Raspberry Pi/CuPID Controller. On the Pi, we’ll open a serial connection over TTYAMA0 (the built-in UART) and listen for data from our receiver. We’ll also set up basic commands to have it send commands to remote devices and set internal parameters, but we’ll cover that later. We set up a basic gateway sketch, as provided by our benefactor Felix (LowPowerLab). We make sure our network ID and encryption key match, throw in a RECEIVE begin and end notification, and begin listening. Piece of cake:

BEGIN RECEIVED
[2] tempasc: 23.0625,tempdev:ds18x,temprom:0x2811fde203000092 [RX_RSSI:-10]
END RECEIVED

Now let’s connect our receiver to our Pi and make sure we can get a listener up and reading. We take a high power unit and flash our gateway program to is, with a few mods to make it spit out everything in json-like format, and stick it down to the top of the board in our CuPID controller. We power from the aux pins on the top of the board, and connect our serial RX/TX to the exposed pins on the top of the header:

Putting our gateway RF unit in our CuPID Controller. TX/RX and power, and we’re done.

Now let’s fire up a shell and a serial monitor to make sure everything is coming through right. We use some pretty straightforward code in serialhandler.py to handle our serial input:

def monitor(port='/dev/ttyAMA0',baudrate=115200, timeout=1):
    import serial
    ser = serial.Serial(port=port, baudrate=baudrate, timeout=timeout)
    
    print "Monitoring serial port " + ser.name
    data = []
    while True:
        ch = ser.read(1)
        if len(ch) == 0:
            # rec'd nothing print all
            if len(data) > 0:
                s = ''
                for x in data:
                    s += '%s' % x # ord(x)
                
                # Here for diagnostics
                print '%s [len = %d]' % (s, len(data))

                # now process data
                processserialdata(s)

            data = []
        else:
            data.append(ch)

We process the serial input using a simple parser that we use elsewhere. It takes our data, parses using the beginning and ending messages that we’ve put into our arduino sketch, and converts whats in the middle into a python dictionary.

def processserialdata(data):
    try:
        message = data.strip().split('BEGIN RECEIVED')[1].split('END RECEIVED')[0]
    except:
        print('there was an error processing the message')
        return 
    else:
        from cupid.pilib import parseoptions
        datadict = parseoptions(message)
        return datadict

Present in pilib.py (imported here as cupid.pilib) is our parser:

def parseoptions(optionstring):
    list = optionstring.split(',')
    optionsdict={}
    for item in list:
        split = item.split(':')
        optionsdict[split[0].strip()] = split[1].strip()
    return optionsdict

So let’s try this out with our messages and see what we get:

>>> reload(serialhandler)
<module 'serialhandler' from 'serialhandler.py'>
>>> serialhandler.monitor()
Monitoring serial port /dev/ttyAMA0
{'tempdev': 'ds18x', 'temprom': '0x28f0d3a7040000cd', 'RX_RSSI': '-68', 'nodeid': '87', 'tempasc': '22.5625'}
{'tempdev': 'ds18x', 'temprom': '0x28f0d3a7040000cd', 'RX_RSSI': '-69', 'nodeid': '87', 'tempasc': '22.6250'}

Excellent! Now we can process as we see fit. What we’re going to do in this case is create a table in our CuPID database specifically for remote data. We do some message typing, data massaging, and scaling, which we ran through in another post. The moral of the story is that as messages are received, they’re logged nicely in a ‘remotes’ table with a timestamp and the messagetype, and as other IO are read and processed, the values are retrieved from the table and inserted into our IO. Our daemon ensure it runs continuously, so we don’t need to worry about it any more.

We’ve programmed our NodeID to 5, and when we open up our motes page, we see that we are in business:

Our Motes show up nicely in our table. We've got a temperature sensor and a battery monitor configured.
Our Motes show up nicely in our table. We’ve got a temperature sensor and a battery monitor configured.

Now, you notice that we’ve configured an analog input, which is attached to a voltage divider which we’re using to monitor battery voltage. We’ll need to scale the input, and while we’re at it we’ll give our inputs handy names as well. We add our scale option manually via phpliteadmin in the metadata table (we’ll add this to our io page later), and add the names directly in our IO table. Our scale shows up, and we see our battery monitor is at 4.995. We’ve got it on USB so far, but we’ll verify once we put it back on 9V. Our voltage divider (1M and 470k) puts 9V at 2.88V, so we’ll be able to cover the range just about right.

Our scaled and named motes in our IO table, alongside our local IO. Our Battery Monitor comes in where it should on USB at 5V.
Our scaled and named motes in our IO table, alongside our local IO. Our Battery Monitor comes in where it should on USB at 5V.

Now we pop on over to our log viewer to make sure we are logging:

After naming and scaling, our Mote temperature battery monitor show up nicely. We're ready to test!
After naming and scaling, our Mote temperature battery monitor show up nicely. We’re ready to test!

We’re golden. Now on to make the actual sealed up unit and do some testing.

Making the Mote

Now, we made the basic box we’ll show here in another post just to see how everything would fit. We really only need to add a resistor and a couple terminals for our 1Wire sensor, and a couple resistors for our voltage divider battery sensor. We buy loads of these waterproof 1Wire sensors, and typically wire them up to 8P8C connectors for use on Cat5 sensor networks, but today we’re going to put a few push on terminals to couple with headers for easy connections.

First, we insert our cord grip, for watertight installation of our wire entry point. A 1/2″ spade bit does the trick nicely.

Insertion of our cord grip. A simple hole and insertion of our screw in connection, and we have IP-rated insertion of our sensor cable ready to go.
Insertion of our cord grip. A simple hole and insertion of our screw in connection, and we have IP-rated insertion of our sensor cable ready to go.

Next, we put our Moteino to a protoplate, solder the pins, and make some connections. As we did on our breadboard, we add a pull-up to the 3.3V line, and connect the data pin to A0, which corresponds to pin 14 in our Arduino sketch:

Attaching our Moteino to a protoboard makes it much easier to connect sensors, connectors, and IO. It gives us power rails and an easy way to mount it too!
Attaching our Moteino to a protoboard makes it much easier to connect sensors, connectors, and IO. It gives us power rails and an easy way to mount it too!

We also connect three header pins to the board so that we can connect our 1Wire sensor or sensors semi-permanently. Finally, we put our battery into place, and seal it all up:

Putting everything together in our box, step by step.

Everything looks fine, and upon connection, our monitor happily reports that we’re reading as scheduled. To have a little fun, let’s make sure this thing does seal properly.

Bath Time

So these enclosures have an IP rating of 65. What this means is, from the definition of the standard, that:

  • The enclosure is dust tight
  • Water projected by a nozzle (6.3 mm) against enclosure from any direction shall have no harmful effects (15 minutes test duration)

We put this to the test. See the video below.

Reference

The above makes use of the open source libraries available on github here:

Explanation and installation here:

Making of a CuPID : Raspberry Pi, better.

What are we doing?

We’re making a CuPID control unit. A networked monitor, controller, and otherwise omnipotent member of our new IoT. You can read a blog post about it here or here, check out the details here, and some of the software and other hardware.

The parts

Here’s what we need to make our CuPID, numbered in the picture as below. Not pictured are power supply filter capacitor(s), which are typically installed as here.

All the parts necessary for a CuPID board
All the parts necessary for a CuPID board
  1. CuPID control board PCB
  2. Shielded RJ45 jack (3)
  3. DS2483 i2C 1Wire chip
  4. Surface mount fuse and fuse holder
  5. Through-hole PCB Mount Female USB A (2)
  6. SMT USB Micro
  7. 2-pin 3.5mm pitch male/female pair (2)
  8. Extrashort male USB A connector (2)
  9. Extra-extralong Raspberry Pi 2×13 stacking header
  10. WiFi dongle
  11. Short SD Card holder
  12. For SPI indicator board option : box header

The board

Let’s put this guy together. First, we put on the surface mount components and do a short reflow. The DS2483 and USB micro ports are pretty small.

Board with SMD parts. They are, well, not too big. We could probably hand-solder, but it wouldn't be fun.
Board with SMD parts. They are, well, not too big. We could probably hand-solder, but it wouldn’t be fun.

We could probably get away with doing them by hand, but we put on some paste and let another Pi run a reflow on them, as we did previously. These SMD USB micros are a pain to get right, but fortunately we didn’t connect the data lines on the board, so you can add on plenty of paste and not worry about having to go one-to-one, as long as we get our high and low power terminals. Before placing them, you’ll want to flip them over and take a quick blade to the short plastic posts on the underside. When we make a board again, we’ll create recesses for these. On the other hand, we may go with USB mini since we can get a through-hole version and it’s a bit more robust. Although USB micro is the ‘standard of the future’ on the roadmap, USB mini is just as easy to find and is rated for more connections.

Next, place the DS2483 on the board, taking care to orient the chip as shown in the figure, using the plus to indicate the proper direction. After placing,  we run a short reflow and go.

We test afterward with our voltmeter to make sure we got it right.

Next, we put on the surface-mount replaceable fuse. We’ve found these get a bit melty if we’re not super-careful on the reflow profile, so we do them by hand. It’s pretty easy with the large pads as long as we tin both sides.

Mounting of the replaceable fuse. Tin the pads, heat the pad to attach the fuse, and spot reflow if you're picky like me or it's not sitting quite right. I find the back of my tweezers helpful here.
Mounting of the replaceable fuse. Tin the pads, heat the pad to attach the fuse, and spot reflow if you’re picky like me or it’s not sitting quite right. I find the back of my tweezers helpful here.

Next we put in all of our through-hole components. I prefer to mount all of them and do some marathon soldering rather than doing them one by one. The shield/mount posts on the USB connectors need to be flattened prior to insertion:

The pins on these ports need straightening before insertion.
The pins on these USB ports need straightening before insertion.

We place our parts and do our soldering:

Through-hole component soldering. First, we mount them, shown top and bottom here. We do a mess of soldering, and we're ready to go.
Through-hole component soldering. First, we mount them, shown top and bottom here. We do a mess of soldering, and we’re ready to go.

Now that the board is done, it’s time to mount it to a Pi.

Marrying the Pi

Now that we’ve prepared our board, let’s get it ready for mounting on the Pi. We need to connect the USB ports and connect the header. First, we take our super-short USB connectors and solder a couple paired wires. We crimp female pins and mount into connectors:

We use these short USB connectors to pull data to the CuPID USB ports, so we can power them directly from our power supply. It also allows one port to be inside the enclosure and one outside.
We use these short USB connectors to pull data to the CuPID USB ports, so we can power them directly from our power supply. It also allows one port to be inside the enclosure and one outside.

Now we mount our superlong header. This is similar to the ones that Adafruit sell … except it’s much longer. In addition to allowing us to mount a board on top of the Pi, it extends far enough to allow us to attach a ribbon connector on top of that! This is great for anything you want to put on top, like a touch-screen. You can see how much bigger it is here:

Our extra-extralong headers, next to those from Adafruit. They're so long, we can mount the CuPID and still have room on top for.. more stuff.
Our extra-extralong headers, next to those from Adafruit. They’re so long, we can mount the CuPID and still have room on top for.. more stuff.

Before we mount it, we throw a couple pieces of thick double-sided tape to the ethernet and to the audio jack to give the board a bit of stick and cushion. We mount the CuPID board, solder our headers, and plug in our USB connectors:

Mounting the board to the Pi. A couple of pieces of thick double-sided tape give some stick and cushion to the combination. We solder the headers and connect our USB cable, and we're done.
Mounting the board to the Pi. A couple of pieces of thick double-sided tape give some stick and cushion to the combination. We solder the headers and connect our USB cable, and we’re done.

Put it in a box

Now that we’ve got the business end of our CuPID complete, let’s put it in a box to protect it. We really like the line of WC cases from Polycase. They’re water-tight (until we put holes in them), durable, and reasonably priced. They have flanges, panel mounts, and optional back panels. They’re fairly easy to mill, and when your design is complete you can have them CNC machine and label them for you, at pretty decent prices even at rather small quantities. Once we absolutely nail down our openings, we’ll definitely go this route. For the moment, we mill them by hand. For prototyping, we CAD up our openings as best we can estimate them, print them in real size, and compare them with the boards, components, and previously iterated enclosures. When we find one we like, we tape it to the enclosure, run a razor blade over the lines to score the plastic, remove the stencil, and then mill out the openings. We take a first pass with a 1/4″ or 3/8″ bit to remove material, and then go back with something small like a 1/32″ mill to reduce the radius on the corners. We sometimes go back with a file to sharpen them up.

Milling out the enclosure cutouts. For prototypes, patterns are transferred from a stencil using a razor blade, and milled out in two steps: material removal and finishing.
Milling out the enclosure cutouts. For prototypes, patterns are transferred from a stencil using a razor blade, and milled out in two steps: material removal and finishing.

After the cutouts are complete, we install standoffs on both the bottom of the board and also on the bottom of the enclosure. On the bottom of the board, we use male/female 2.5mm threaded 6mm long standoffs, which keep the board at a consistent height and solid footing. These are not in the parts list above, as we recently added them. Their installation with a couple nylon nuts are shown below.

Installation of standoffs on the board to keep the board level.
Installation of standoffs on the board to keep the board level.

To keep the boards in position, in particular to reinforce it against pushes on the inputs when cables and/or power is connected, we put some 2.5mm thread, 8mm length standoffs in the bottom of the enclosure. From the backside, we insert a couple brass 2.5mm screws. We countersink them to keep the bottom flush, and if needed, thin layer of silicone will keep this waterproof.

Installation of standoffs in enclosure to hold board in place.
Installation of standoffs in enclosure to hold board in place.

Once we clean out the enclosure, insert our SD Card in our low-profile connector, and plug in our WiFi, we slip it into the cutouts. Almost done.

Mounting of board into enclosure with standoffs.
Mounting of board into enclosure with standoffs.

Let it be seen!

At this point, we have a couple options for indicators. We can go with an indicator board we made specifically to fit in this enclosure, as we reflowed and demonstrated previously. It mounts to the internal panel mounts with standoffs and connects with an 6-conductor IDC and box connector, and has 4RGBs and 4 single-color (R,G,B,Y) LEDs. Best of all, it uses only one SPI port, employing a couple shift registers.

CuPID with indicator panel, including 4RGB and 4 single-color (R,G,B,Y) programmable indicator LEDs, all on a single SPI output.
CuPID with indicator panel, including 4RGB and 4 single-color (R,G,B,Y) programmable indicator LEDs, all on a single SPI output.

Alternatively, we can throw a nice little touchscreen on top. We’re working on that write-up currently, but the result is here:

CuPID with touchscreen. Runs on SPI outputs and two GPIO.
CuPID with touchscreen. Runs on SPI outputs and two GPIO.

Why CuPID? Because your Pi can do more.

So what are we doing? We’re making the most flexible general purpose controller. It’s a PLC. It’s a home automation controller. It’s a sensor gateway. It’s a home energy management system. It’s a homebrewer’s time and beer saver. It’s a hacker’s start at web-enabled IO. It’s many more.  What makes it able to do all of these things (and well) is a pretty typical combination of the hardware to do the job and the software to interface with the humans.

The Hardware

With all of the applications we’ve just described, there are a lot of things that need to be included. Below is what we consider the core of essential functionality, and how things stack up. What we’ve done is taken the beauty, simplicity, and raw potential of the Raspberry Pi, added a few goodies, and made everything simply accessible inside a pretty protective box. What we haven’t done is throw in a whole bunch of stuff you might or might not need, like relays or hardware switches. Nice and simple, so you can plug in whatever modules and functionality you may need.

Core Hardware

Raspberry Pi CuPID
Runs *nix X X
ModBus TCP/IP X X
Powered USB X
WiFi X
I2C X X
SPI X X
UART X X
1Wire X
Programmable Indicators X
IO on standard cabling X
Replaceable Fuse X
Durable Enclosure X
NEMA-rated Enclosure Option X
Remote Sensor Options X**
Camera Option X**

**In development

The software

The special sauce is the interface. It’s not always about doing things people can’t do. It’s about not reinventing the wheel. A lot of us could, for example, write static or dynamic html that produces the page you’re looking at. But we have bigger ideas to focus on. The CuPID is designed around this principle. Produce an API and interface for the IO that allows flexible control, logging, and databasing of what you want, when you want. Put it all in an HTML5-compliant, device-agnostic interface that allows the hacker and system developer alike to build powerful control systems.  Here are the basics, and you can see a little more detail here: http://interfaceinnovations.org/ccsoftware.html

OS raspbian wheezy
control languages Python 2.7, 3.0 compliant
databasing sqlite
interface html/js/jquery, jquerymobile
controller/model wsgi/python
web service apache