2014-03-04 Step 1: buy a lot of DS18x20 temperatur sensors

The other day I had the problem to check the states of several switches signalling the state of some doors and windows (closed, fully opend). Of course resources should not be wasted and it should be easy to build.

This could be done by several ways:

Solution #1 shows the obvious way: switches, wires and digital ports. Pullup resistors are build in, so it is straight forward to build.

One the other hand the problem is easy to see, too: One digital port and at least one wire for each switch.

Solution #2 replaces all those digital ports by one analogue port and a hand full of resistors. And with some smart wiring it could work with only two wires.

But the law of energy conservation (if it becomes easier somewhere it will be more difficult somewhere else) strikes back: it is quite difficult to detect the close state of multiple switches.

Then I came up with the idea of using DS18×20 1-wire temperature sensors. Not to actually measure the temperature but to detect whether a certain sensor is present. It is quite easy: every sensor has a unique ID (MAXIM ensures that)
and the switch adds the sensor to the bus. Voila: sensor present: switch closed. Sensor missing: switch open.

Law of energy conversation gives the additional costs for sensors and soldering them to the switch. But it is easy peasy, lemon squeezy. Take two wires and daisy chain all switch/sensors onto them.

Advantages:

  • easy to build
  • unlimited number of switches
  • low cost (switch + sensor)
  • two wires daisy chained structure

Disadvantages:

  • only polling
  • slow (compared to interrupts)
  • possible false open detects
  • not so easy to maintain

Lets discuss the problems: First two are polling and slow. If you are into detecting high speed things and need interrupts to react on something, this is not your solution. It won’t work. Don’t use it. Period.

The third one is more serious: Due to the bus nature and perhaps long wires there can be all sorts of interference between the different sensors and the (noisy) environment. Solution is not really simple: I choose to solve it with two approaches: multiple reads and defensive programming.

Multiple reads means that in case I don’t detect the sensor I do try to detect it again, again, again. But that does not really solve the problem because the switch could have been open and just closed… So I added a fixed sensor at the begin and end of the bus. Reading them should always work and gives a good monitor about the status of the bus. Lots of errors indicate a problem on the bus.

Defensive programming means to do it in a way to be on the safe side of live. Assume to control the filling level of a bucket by a float switch (turn pump on/off) and wanting to avoid overflow. Design it in a way that "enough water" means "switch open" equals to "no sensor" and "pump off".

A different concept could be to use a SPCO switch (single pole changeover) with two sensors. One of these sensors should always be detected. In case of detecting no sensor/switch enter program panic mode.

Last problem occurs on sensor fault: changing the sensors does not fix the problem. The software detects the presents of a certain sensor ID on the bus. Sensor dead: ID dead. New Sensor, new ID, old ID still dead. So either have a sensor set up or recompile.

Implementation has been done by adding the following code the http://www.pjrc.com/teensy/td_libs_OneWire.html library.

Select
// Verify the device with the ROM number in ROM_NO buffer is present.
// Return TRUE  : device verified present
//        FALSE : device not present
//
uint8_t OneWire::verify(uint8_t *searchAddr)
{
   unsigned char rom_backup[8], result[8];
   int i,rslt,ld_backup,ldf_backup,lfd_backup;

   // keep a backup copy of the current state
   for (i = 0; i < 8; i++) {
      rom_backup[i] = ROM_NO[i];
	  ROM_NO[i]=searchAddr[i];
   }	  
   ld_backup = LastDiscrepancy;
   ldf_backup = LastDeviceFlag;
   lfd_backup = LastFamilyDiscrepancy;

   // set search to find the same device
   LastDiscrepancy = 64;
   LastDeviceFlag = FALSE;

   if (search(result))
   {
      // check if same device found
      rslt = TRUE;
      for (i = 0; i < 8; i++)
      {
         if (searchAddr[i] != result[i])
         {
            rslt = FALSE;
            break;
         }
      }
   }
   else
     rslt = FALSE;

   // restore the search state 
   for (i = 0; i < 8; i++)
      ROM_NO[i] = rom_backup[i];
   LastDiscrepancy = ld_backup;
   LastDeviceFlag = ldf_backup;
   LastFamilyDiscrepancy = lfd_backup;

   // return the result of the verify
   return rslt;
}