Push Button RF Data Transmitter

Background

So a friend of mine who works in, let’s say, the sciences, approached me about a simple application, but one that a hardware company was charging an inordinate amount of money for. In the physical sciences equipment business, this is very common: overcharging for a very simple feature, because it’s convenient and it just works. The fact of the matter is that scientists simply don’t have the time to homebrew solutions, they don’t get accolades for making simple instruments work well and cheaply, and taking the time to do so takes them away from their real jobs — doing science. This is where consultants and outsourcing really make sense, even when at first it might seem a bit expensive. Regardless, this doesn’t mean that buying a receipt printer attached to a button to print out data from a device should cost $1000, or a dead-simple temperature controller in a box with a couple relays should cost $2k. but this is the twisted norm.

We are moving away from a time where a tiny handful of people were able to design things that once seemed very complicated, like a microcontroller IO device, to an era where these things are available for dirt cheap and are everywhere. The markets are therefore just waiting to be taken over or at least challenged and/or adjusted by the talents and creativity of the makers around us.

The Project

The device I was approached about was a laboratory instrument that has an RS232 interface. You can send it a couple characters and it sends back the data. What we envisioned was a single button: you press the button, it acquires data and sends it off to a sensor gateway, where the data is databased and stored for later processing, addition of metadata, etc. It’s a pretty general thing. To summarize:

basicmodel

Now the idea of a single button is pretty romantic, in a way. It is the absolute reduction of input to the simplest unit of data transfer: the binary digit. The ultimate in simplicity. As it turns out, it’s not a unique one, either. The Staples big red button has been around forever, and if you do some googling you can find a couple companies that make, essentially, big red networked buttons that do one thing. There are legion applications where you just need one thing to happen. Call an elevator, a taxi, turn on a light, disable an appliance … so many user interfaces can be created with one or three binary elements. Anyway, the imagination runs wild. But let’s get onto the simple nuts and bolts of how to make our little button here.

The Hardware

We’ll construct this unit exactly like our Enhanced Remote, which we wrote about previously. We’ll use the same enclosure, and just attach a button to the top, and run two cables out of one side. This will negate the IP rating of the cable entry, but make it so all of our cables come out of one side. This is a key feature for presentation and accessibility. As a result, however, we need to size up our cable entry to PG9, which really doesn’t fit on the side of the enclosure, despite the appearance. Next time we do this, we’ll get a single cable with enough conductors so we can use a smaller PG7 entry as on the standard remote.

Components for our RF button remote.
Components for our RF button remote.

What we SHOULD have done here was document the button insertion process and what the bare button looks like. It’s one of these guys at right.

Our button's product picture before we seal it up on the enclosure lid.
Our button’s product picture before we seal it up on the enclosure lid.

Strangely, the top bezel on this thing is really narrow, so you need to get the hole size just right. Let’s just say our set of bits didn’t prove up to getting it close enough. So we ended up using a grommet in the hole opening, which had the added benefit of keeping the top sealed. It also gives the button a little spring, and a totally secure feeling. To get it in there just right, we had to cut the grommet in half and use one half on each side. Not a very interesting story.

Wiring

Aside from the step putting this thing together we covered previously, we need to do our wiring to the outside and connect our button. From the outside, everything will be on one USB cable: 5V power, GND, and two data lines. The two data lines will be passed out on the second USB, and our data source can use them as they see fit. For an RS232 source, for example, we’ll use them as RX/TX, after level conversion if necessary. This will make using a Y USB cable possible in the future. Here’s a simple layout:

Simple wiring diagram of our button and RF sender unit. All the external connections go on a single USB bus.
Simple wiring diagram of our button and RF sender unit. All the external connections go on a single USB bus.

The button is pretty straightforward – two pins for the contact and two for the LED. One button contact will go to ground, with the other connected to the IO1, which we’ll configure as INPUT_PULLUP on the Arduino. Not coincidentally, this pin has a hardware interrupt (pin 3) on the Moteino/Arduino. When our button is pressed, the input will change from it’s normal state of 1 to a state of 0. We can set an interrupt on RISING or FALLING and we’ll be good to go.

For the button LED, we’ll need a current-limiting resistor. 330 ohms turns out to be about right, using the 5V input from our USB power input. We attach this to the positive LED terminal, and connect the negative terminal to IO1, which we’ll use as an open-drain output. The IO4 terminal is connected to the drain of the MOSFET footprint, into which we’ve soldered a 2N7000 NPN. IO4 on our mote is io7 internally, and corresponds to pin A2. Once we enable the output, it will be set each cycle, so we don’t /need/ a pulldown, but we’ll configure one anyway.

Construction

Now we need to physically get everything into the box. Not to complicated. First, we solder our button wire. I am personally so tired of soldering crappy stranded wire, such as that in ribbon cable, that I went with some solid core hookup wire. It’s not as pretty and tidy, and makes closing up the box a bit of a pain, but it’s much more robust and cuts down on the short/bad connection sleuthing endemic to stranded wire, especially when you are moving connections around a lot. It also makes screw terminal insertion (especially difficult with the space constraints here) a breeze. Totally worth it in this application.

Getting everything into a box. Pulling cable in and wiring our button.
Getting everything into a box. Pulling cable in and wiring our button.

Once we do some wire coiling and screw terminal insertions, we’re ready to close ‘er up (and then open it up again to program it).

All done! Time to play.
All done! Time to play.

Micro Programming

Programming this thing is pretty basic. You can find the sketch here. We’ll mention a couple things we did that were perhaps noteworthy. The rest has all been covered in our UniMote programming. We decided to slightly modify our interrupt programming. We have noticed that there are issues with running any serial print debug on a function run by an interrupt. This can be quite annoying, especially if you are calling a function that is designed to provide output, such as debug accompanied by our RF send functions, for example. To mitigate this, we used our interrupt to do nothing except set a loop routine to run:

pinMode (3,INPUT_PULLUP);
attachInterrupt (1,setDoButton,RISING);

...

void setDoButton() {
  runDoButton = 1;
}

Then, inside our loop:

if (runDoButton) {
  doButton();
}

The only requirement here is that the loop time is short enough that feedback is fast. Because we’re not really doing much, as long as we make sure our LOOPPERIOD is sufficiently short, we’re good to go.

Finally, sending our value is as simple as wrapping up our standard SendWithSerialNotify function with something that retrieves our data, and sends a specially formatted message. In this case, we’re using the identifiers ‘scalepin’ and ‘scalevalue’. This will allow our gateway to interpret the data accordingly. This is shown below, with dummy data for testing. You can see here that we manually turn the LED output on and off again when the function enters and exits, respectively.

void doButton() {
  if (millis() - lastbutton > debounce && iovalue[7] == 0){
    Serial.println("Button Pressed");
    iovalue[7]=1;
    
    // manually turn on output
    digitalWrite(16,1);
    
    float scalevalue = 9999.9999;
    sendScaleMessage(iopins[7], scalevalue);
    lastbutton = millis();
  }
  runDoButton = 0;
}
void sendScaleMessage(byte pin, float value) {
  int wholePart = value;
  long fractPart = (value - wholePart) * 10000;
  int sendlength = 35; 
  char sendstring[sendlength];
  sprintf(sendstring, "scalepin:%02d,scalevalue:%05d.%05d", pin, wholePart, fractPart);
  sendWithSerialNotify(GATEWAYID, sendstring, sendlength); 
  Serial.println("Waiting, hang on");
  delay(1000); //  This is for visual feedback
  Serial.println("Done");
  iovalue[7]=0;
  digitalWrite(16,0); // turn off light manually
}

Gateway Programming

On the gateway, all we need to do is include a handler for our special message with data fields of ‘scalepin’ and ‘scalevalue’. Pretty easy stuff. We’ll fancy it up later, but for now it’s just as simple as an additional line or two in our serialhandler.py:

elif 'scalevalue' in datadict:
            querylist.append('create table if not exists scalevalues (value float, time string)')
            querylist.append(pilib.makesqliteinsert('scalevalues',[datadict['scalevalue'], pilib.gettimestring()],['value','time']))
            pilib.sqlitemultquery(pilib.logdatabase, querylist)

A couple presses of the button later, and sure enough, we have a table with some values!

Button press dummy data, as viewed in phpliteadmin. Yay data!
Button press dummy data, as viewed in phpliteadmin. Yay data!

We’ll spend some time getting this into a proper page for export and addition of metadata shortly. Oh yeah, and getting real data!

Reference

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

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: