DCDW = Dirt Cheap Dumb Wireless

A few Arduino Forum members are undertaking development; you can track our progress at http://www.dcdwireless.com/. Please note there is currently no product for sale and no support. It is of course Open Source so you hardcore hackers feel free. Schematics and board prints are on the site, until www collaboration is fully up just contact us for actual design files.

If interested but not hardcore hacker, tell us and we will email you back "when soup's ready".

The concept of Dirt Cheap Dumb Wireless was introduced to Arduino forum on http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1293839127

Circuit boards v0.1 and parts arrived, got 3 sensor nodes built except for the RF modules from SparkFun, which finally shipped. So right now the DCDW prototypes are hardwired to Arduino Uno to support code development.

The initial "frequency counter" code hooks interrupt 0 on digital pin 2 and sets a flag then returns. The freq measured by Arduino agrees pretty well with my scope.

This ISR "set flag and return" is due to attachInterrupt(interrupt, function, mode)

Inside the attached function, delay() won't work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.

The easy ways to count frequency depend on pulseIn() with millis() or micros(), so I elected to have the interrupt to just set flag "dataComingIn" (and a LED) and return, this minimizes the disruption of timers and serial, and gives other code access to all the time functions.

Right now the demo idles in the main Loop() until some pulses trigger the ISR. The ISR sets the flag, turns on a LED (for development) and returns. Code in Loop() calls countFrequency() when the flag is set, then afterward clears the flag and turns off the LED. countFrequency() sets a start time-hack in millis(), loops thru 100 pulses with pulseIn(), and then hacks the stop time and returns kHz as a float(). Floating math is costly, so today I'm gonna change to return an int() in Hz.

Precision is about 10 Hz, or rather the inverse number of millis() in 100 pulses. More pulses would be better, right now accuracy and precision are plenty good to just "drive on" with development, we can sharpen our pencils later.

Also my prototype code does not gracefully turn loose and return control to void Loop() after the first activation, but thats gonna get ironed out today. Will also post some prototype code here and however our Dirt Cheap Dumb Webmaster deems fit.

There are many ways to "skin a cat" and we are open to alternative approaches. By polling in Loop() any digital pin could be used vs the hardware interrupt approach. We may include both a "lazy" approach and a "serious" approach.

The ultimate goal is code Newbies can use to get top level functionality like "Node 3 Sensor 2 is 64 degrees F" or "Node 4 Switch 1 just opened" without having to delve into ISR and such. If this is you, we have nothing ready for you yet, just let us know who you are and be patient.

Hardcore hackers jump in, build a DCDW circuit, sling some code, have fun!

Received OOK radio links from SparkFun. The interrupt driven software is pointless for OOK links, bkoz in absence of a TX signal the RX chases its detector right down into the thermal noise and the data out pin dithers randomly. Main Loop() ends up going off every time to countFrequency(), might as well code it that way instead of wasting an interrupt. Also we can now use any Arduino data pin.

The good newz is the code reads the identical TX pulse rate thru the OOK radio link as it does hardwired. This is bkoz the function countFrequency() measures time for 100 (or your number) of complete cycles instead of just measuring pulse width.

Demo sketch for DCDW Two Button Remote

// Dirt Cheap Dumb Wireless  (DCDW)
// Deomo code for DCDW Two Button Remote 
// 2011 Jan 09   by AltairLabs <http://www.altair.org> 
// This example code is in the public domain.
//
// visit Dirt Cheap Dumb Wireless   www.DCDWireless.com 
// for more open source DCDW hardware and software
// Set up DCDW PCB for Two Button Remote
// Oscillator frequencies in this demo use 
// Osc A = 0.01 uF and 220k ohms
// Osc B = 0.01 uF and 100k ohms
// Osc C not used, no need to populate these parts
//
// visit Arduino   www.arduino.cc 
// for great open source microcontroller hardware and software
//
// declare stuff
int normLED = 13;      // the on-off LED is digital pin 13
int RXpin = 2;         // DCDW data link on digital pin 2
int pulses = 100;      // total number of pulses to time 
int pulse;             // pulse count
unsigned long timeout= 10000;   // microsecs for pulseIn() to wait before giving up
unsigned long t0;      // start time hack
unsigned long time;    // delay in millisec
unsigned long duration;  // length of one pulse 
long frequency;        // frequency of pulse train 
boolean debug = false;   // true = more verbose

void setup() 
{ 
  pinMode(normLED, OUTPUT);        // optional, used for demo only
  pinMode(RXpin, INPUT);           // set RX pin to input 
  Serial.begin(9600);              // start up the serial communications USART
  Serial.println(" ");             // test the comm port
  Serial.println("Good morning, Dr. Chandra.  This is HALduino.");    
} 
 
 
void loop() 
{ 
// DCDW code starts here
  digitalWrite(normLED, HIGH);  // debug: set normLED on before counting freq
  frequency = countFrequency(); 
  digitalWrite(normLED, LOW);   // debug: set normLED off when done counting
// end of DCDW receive, now display result
  if (debug) {
    Serial.print("Heard Freq = ");  Serial.println(frequency);                
    if (frequency < -1) {
      Serial.print(" (abandoned on pulse) ");
    }
  }
// Anything you want to do with "frequency" goes here
  if (frequency > 764 && frequency < 845) {
    Serial.print("Heard Button A ");  Serial.println(frequency);                
  }
  if (frequency > 1662 && frequency < 1842) {
    Serial.print("Heard Button B ");  Serial.println(frequency);                
  }

// Everything else you want to do goes here
  Serial.println(" . ");                
  delay(1000);
}
  
long countFrequency ()  
{
  frequency = 0;                               // nothin' yet
  t0 = millis();                               // start time
  for(pulse = 0; pulse < pulses; pulse += 1)   // count incoming pulses 
  {                                   
    duration = pulseIn(RXpin, LOW, timeout);   // wait for one complete pulse cycle
    if (duration == 0)                         // if pulse timout then give up
    {
      return -(long)pulse;                     // abort, return neg of bad pulse
    }
  } 
  time = millis()-t0;                          // got all pulses, so time = now - start
  frequency = 1000 * (unsigned long)pulses / time;  // frequency in Hz
  return (long)frequency; 
}

The function countFrequency() is now automatically immune to noise. It also autoscales the sample time to the incoming pulse frequency and desired precision. Code got fancier than planned but the use is simpler than before.

Heres a DCDW v0.1 set up as single channel temperature with identification:

Copied the Serial Monitor output to a spreadsheet.

Note in this TEST CODE the TIME IS NOT TO SCALE any valid samples are logged as they come in, when the SparkFun RX is just receiving noise, nothing is logged. Since function countFrequency() returns timely with error codes for noisy input you could plot whatever you want. Anyway this test code sampled between 1 and 3 seconds whenever valid data was present, no attempt yet to do real time, but thats where we are going. You can see sample time automatically "shift gears" when temperature rose afetr bringin DCDW back inside warm lab.

Heres code. Function countFrequency() is much more advanced now. It returns quickly if no valid signal is received, and automatically stays gone just long enough to get ~3 digit precision. The main code has some vestiges of 2-button remote version, and some declarations need to be hidden inside countFrequency() but its working well.

// Dirt Cheap Dumb Wireless  (DCDW)
// Deomo code for DCDW Single Channel Sensor with Ident 
// 2011 Jan 09   by AltairLabs <http://www.altair.org> 
// This example code is in the public domain.
//
// visit Dirt Cheap Dumb Wireless   www.DCDWireless.com 
// for more open source DCDW hardware and software
// Set up DCDW PCB for Single Channel Sensor with Ident
// Oscillator frequencies in this demo use 
// Osc A = 1000 pF 100k ohms thermistor (large resistor reduces battery drain)
// Osc B = 0.01 uF and 1k ohms
// Osc C = 22 uF Tantalum, R5, R6 = 10 Meg
//
// visit Arduino   www.arduino.cc 
// for great open source microcontroller hardware and software
//
// declare stuff
int normLED = 13;             // the on-off LED is digital pin 13
int RXpin = 8;                // DCDW data link on digital pin 2
int pulses = 10000;           // total number of pulses to time 
int pulse;                    // pulse count
unsigned long timeout= 10000; // microsecs for pulseIn() to wait before giving up
unsigned long t0;             // start time hack
unsigned long time;           // delay in millisec
unsigned long duration;       // length of one pulse 
unsigned long avgDuration;    // average length of first ten pulses 
unsigned long minDuration;    // minimum length of a valid pulse 
unsigned long maxDuration;    // maximum length of a valid pulse 
long frequency;               // frequency of pulse train 
boolean debug = false;         // true = more verbose

void setup() 
{ 
  pinMode(normLED, OUTPUT);        // optional, used for demo only
  pinMode(RXpin, INPUT);           // set RX pin to input 
  digitalWrite(RXpin, LOW);        // no pullup
  Serial.begin(9600);              // start up the serial communications USART
  Serial.println(" ");             // test the comm port
  Serial.println("Good morning, Dr. Chandra.  This is HALduino.");    
} 
 
void loop() 
{ 
// DCDW code starts here
  digitalWrite(normLED, HIGH);  // debug: set normLED on before counting freq
  frequency = countFrequency(); 
  digitalWrite(normLED, LOW);   // debug: set normLED off when done counting
// end of DCDW receive, now display result
  if (debug) {
    if (frequency > 0 ) {
      Serial.print("   Freq = ");               
    } else {
      Serial.print("  Error = ");
    }
  Serial.println(frequency); 
  }
// Anything you want to do with "frequency" goes here
  if (frequency > 0 || debug) {
    Serial.print(frequency);
    if (frequency > 764 && frequency < 845) {
      Serial.println(" Button A ");                  
    }
    else if (frequency > 1662 && frequency < 1842) {
      Serial.println(" Button B ");
    }
    else {
      Serial.println(" ");
    }
  }
// Everything else you want to do goes here
  delay(100);
}
  
long countFrequency ()  
{
  frequency = 0;                               // nothin' yet
  avgDuration = 0;
  for(pulse = 0; pulse < 10; pulse += 1)       // count 10 incoming pulses 
  {                                   
    duration = pulseIn(RXpin, LOW, timeout);   // wait for one complete pulse cycle
    if (duration == 0) {return -(long)pulse;}  // if pulse timout then abort, return neg of bad pulse
    avgDuration += duration;
  } 
  maxDuration = avgDuration / 9;
  minDuration = avgDuration / 11;
// now we have a range of average pulse durations
// so start counting "pulses"pulses, increasing "pulses by 10x each time
// until a string of pulses meets the minimum total time for accuracy
  for (pulses = 10; pulses < 100000; pulses = pulses * 10)
    {
    frequency = 0;                               // nothin' yet
    t0 = millis();                               // start time
    for(pulse = 0; pulse < pulses; pulse += 1)   // count incoming pulses 
      {                                   
        duration = pulseIn(RXpin, LOW, timeout);   // wait for one complete pulse cycle
      if (duration == 0) {return -(long)pulse;}    // pulse timout: abort, return neg of bad pulse
      if (duration > maxDuration)                  // pulse too long: abort, return error code
        {return -(long)duration;}
      if (duration < minDuration) 
        {return -(long)duration;}
      } 
    time = millis()-t0;                          // got all pulses, so time = now - start
    if (time > 100)                              // if time is enough for precision we are done
      {
      if (debug)
        {
        Serial.print(pulses);  Serial.print(" pulses = ");  
        Serial.print(time);  Serial.println(" millisecs ");  
        }
      frequency = 1000 * (unsigned long)pulses / time;  // frequency in Hz
      return (long)frequency; 
    }
  }
  return -9999;
}

Off to a great start, much more to do.

I'm very interested in seeing just how small a sensor can be made (thermo).

The goal is to create a probe that can be used in a wireless meat thermometer, so ultra-high precision and fast cycling/updating is not necessary. Long battery life, small probe size and a 2-4m transmit distance are the objectives.

would it be possible to have the code run on interrupts rather than a counting loop? that would make this whole thing easier / practical.

Would it be possible to have the code run on interrupts rather than a counting loop?

Here's the thing... As I understand it. AltairLabs may come in on this an correct me...

When no DCDW is transmitting, the receiver "listens" harder and harder, and before long starts "hearing things". At that point, the output of the receiver starts generating lots of spurious positive and negative edges... which would divert the Arduino excessively inside a fancier interrupt handler.

With the simple interrupt handler and more code (which taps into what the simple handler is picking up), selectively executed, a happy compromise can be had. The Arduino will do very little with the "noise", but will from time to time make a check to see if more than noise is coming in.

Howdy @martin_bg

would it be possible to have the code run on interrupts rather than a counting loop? that would make this whole thing easier / practical.

Thats just what we thought too. Interrupts was my very first approach, and it does work. But there turned out to be less advantage than I had hoped, and some unexpected drawbacks.

(1) the RX noise of an empty channel on these cheeep OOK radio links will consume significant CPU time servicing the interrupt, even to return a result code of "nothing coherent received".

(2) interrupts are only available for certain I/O pins, the new approach is possible for any I/O pin

(3) interrupts corrupt other Arduino functions such as the millis() counter.

So after trying both techniques a while, the interrupts seemed much less attractive. You can still do interrupts if you wish, it would probably work best for a continuous transmit or a quiet link like "real" radios with squelch. Maybe cheeep walkie talkies?

Heres some early code that was interrupt based.

// Dirt Cheap Dumb Wireless
// by AltairLabs <http://www.altair.org> 
// This example code is in the public domain.
//
// see www.DCDDumbWireless.com



int normLED = 13;      // the on-off LED is digital pin 13
int RXpin = 2;         // DCDW data link on digital pin 2
volatile int dataComingIn = 0;
int pulses = 1000;      // total number of pulses to time 
int pulse;             // pulse count
unsigned long timeout= 100000;   // microsecs to wait before giving up
unsigned long t0;      // start time hack
unsigned long time;    // delay in millisec
unsigned long duration;  // length of one pulse 
long frequency;        // frequency of pulse train 


void setup() 
{ 
  pinMode(normLED, OUTPUT);
  pinMode(RXpin, INPUT);           // set RX pin to input 
//  digitalWrite(RXpin, HIGH);       // and turn on pullup resistor
  Serial.begin(9600);              // start up the serial communications USART
  Serial.println(" ");             // test the comm port
  Serial.println("Good morning, Dr. Chandra.  This is HAL.");    

  attachInterrupt(0, blink, HIGH); // grab interrupt for digital pin 2

} 
 
 
void loop() 
{ 
  digitalWrite(normLED, LOW);   // set normLED off when in main
// pulse measurement goes here
  if (dataComingIn != 0) {
    frequency = countFrequency(); 
  }
  if (frequency > 0) {
    Serial.print("Heard Freq = ");  Serial.println(frequency);                
  }
  if (frequency < -1) {
    Serial.print("Freq count abandoned on pulse ");  Serial.println(frequency);                
  }
              
/*  while (dataComingIn) {
    digitalWrite(normLED, LOW);   // set normLED off when in main
    duration = pulseIn(RXpin, LOW, timeout); 
    if (duration == 0) {dataComingIn = 0;}  
  }
  */
  dataComingIn = 0;
  Serial.println("Main Loop Idle");                
  delay(1000);
  
}
  
long countFrequency ()  
{
  frequency = 0;                              // nothin' yet
//  Serial.print("counting  ");
  t0 = millis();                              // start time
  for(pulse = 0; pulse < pulses; pulse += 1)  // count pulses 
  {                                   
    duration = pulseIn(RXpin, LOW, timeout); 
    if (duration == 0) 
    {
//      Serial.println("abort countFrequency");                 // delta time
      dataComingIn = 0;  
      return -(long)pulse; 
    }
  } 
  time = millis()-t0;                                    // time = now - start
//  Serial.print(" "); Serial.println(time);               // delta time
  frequency = 1000 * (unsigned long)pulses / time;
//  Serial.println(frequency);                 // frequency in Hz
  return (long)frequency; 
}  

void blink()
{
  digitalWrite(normLED, HIGH);   // set normLED on when in interrupt
  dataComingIn = 1;              // set flag for incoming data pulse train
}

If you would like to run with the interrupt approach, our second set of PC boards may be ordered soon. At this early stage we cant give much support to "appliance operators" but if you are proficient with schematic and soldering iron youre welcome to join the development.

Howdy, @OSGrill

I'm very interested in seeing just how small a sensor can be made (thermo).
The goal is to create a probe that can be used in a wireless meat thermometer, so ultra-high precision and fast cycling/updating is not necessary. Long battery life, small probe size and a 2-4m transmit distance are the objectives.

Been thinkin 'bout your meat thermometer idea, the DCDW concept fits well for you.

The two approaches I see are
(1) the single dedicated sensor. You dont need the quad NAND at all., just use Osc C to send temp. Updates as often as you care to read it. It runs all the time and uses the least components. You can even snap off and discard one half of the circuit board.

(2) use Osc C to switch on Osc B every N seconds for an updated reading. This uses the full circuit board with both chips and conserves battery life.

We are not prepared to support newbies, we still need good explanations, diagrams and other documentation for the various modes, and a better introduction to the possible modes and how to choose. But if you are proficient with schematics and designing simple circuits, you are welcome to use these ideas. After all, that what Open Source is all about:)

The DCDW circuit board is a bit bigger than a 9V battery. You could use one to prototype your circuit and test your code, when you are satisfied with the design you could make your own dedicated PC board out of surface mount components, I reckon the size could be reduced to about an inch square.

The antenna wants to be nearly 20 inches, working against a ground at leeast the same size. nobody really wants a 40 inch meat thermometer so the antenna will be a compromise. Probably enclose the thermistor in a thin stainless steel tube, make that "ground". Then have a plastic head to hold battery and PCB and maybe a power switch. The antenna can be a wire coiled up inside the head. The RF modules claim a range of hundres of feet with "proper" antenna so you should still get enough to reach a few meters.

If you can glean enough from this post to run with the idea on your own, thats great we can put you down for a beta test board. If the details still seem fuzzy to you , please be patient while we develop better documentation and code examples. Either way hit the website and give us an email, we'll keep ya posted.

OKAY here is a major annoyance. The CD4093 from Texas Instruments is purportedly a Quad 2-Input NAND with Schmitt trigger inputs. DCDW depends on the "gated oscillator" or "astable multivibrator" touted by National Semiconductor and Texas Instruments, respectively.

OK fine, that exactly what we want, right down to those equations giving the transition times. The timing dont depend in ANY WAY on the voltage at the Control input, that just turns the oscillator on and off RIGHT? Thats the beauty of a Schmitt trigger input, RIGHT? Can I get an AMEN on that?

Well heres what REALLY happens when I use the Texas Instrument part and vary the control voltage.

The osc freq varies 10X as the control voltage drops, before it shuts off. WHERE is the Schmitt Trigger? I'll be in contact with TI about this, as well as ordering equivalent parts from the other 3 suppliers represented at Digi Key.

In the mean time we would greatly appreciate hearing from anyone about this.

OK need some help here.

We have 2 DCDW in "single sensor plus ident" and a simple data logger to demo them. Left it running to low inside and outside temperature for a day. Problem is Win7 keeps turning off the Acer Aspire netbook. Disabled WinDoze Update to prevent reboots, went to "power options" and set everything to "NEVER" (the netbook is on AC power adapter the whole time) but still I go down and find the DCDW happily chirping out temperature readins, the Arduino is receiving data (according to the LEDs) and trying to log to the stinkin' WinDoze that has turned itself off.

Where are all the OTHER power settings hidden?

First, have sure that your netbook are not overheating. If processor is heating too much it'll turn-off the netbook. Main cause: Burned fan(I don't know if your netbook have a fan)

Also, I found this:

CMOS corruption! LOL!

THANKS @ Wagner Sartori Junior !

Its almost time for me to go to sleep and see if the laptop can stay awake. I created a new power profile called "AlwaysON" but didnt find any settings I havent used before. If this doesnt work tonight then your link is what I will try next night.

BTW the netbook was sitting on a shop rag, the heater in lab keeps us between 20 C and 25 C. Tonight before going upstairs I will be sure to elevate the netbook where all vents are free and clear. Thanks again.

Tonight status is :

Working on SSID (Single Sensor plus IDent, but thats easily confused with the WiFi term). We currently have 2 problems.
(1) the CD 4093 Quad NAND with Schmitt Trigger Inputs produced by Texas Instruments is not acting like it has Schmitt trigger inputs. This makes the "gated oscillator" frequency chirp downward badly. Currently using slow packets of 10 sec every 3 minutes and the software can latch the data before the chirp becomes too bad.

Likely solution is avoiding this particular manufacturer for this chip

(2) when a collision of packets occurs, the software is sometimes does not recognize the error but rather mistakes the ID part of the second packet as the data part of the first one. Over night this happened about 10 times in 500 sensor readings, or in other terms about half the packet collisions were erroneously accepted as valid data. These are easily recognized as invalid readings but are still nuisances.

Solution is a more robust state machine, which is being tested overnight tonight.

Here is the master station


When DCDW development is finished, only the l33t beta testers will have the genuine DCDW master station official mounting string. Compatible with all genuine Arduino boards. Cost is just one carton of Samuel Adams for the development team! Supplies are limited, Sign up today!
Here is the indoor temperature sensor node

Here is the outdoor temperature sensor node

PCB layout v0.01 requires the ugly hack to scab on a diode, resistor and capacitor to put the DCDW in SSID mode. This will be cleanly accommodated on all PCB versions going forward.

Here is a Open Office import of what the DCDW master received from its nodes

Long straight lines are missing data when WinDoze7 ( >:(DONT get me STARTED >:( ) despite all power profile settings fell asleep and stopped listening to Arduino Uno.

Time is Arduino millis(), we have no Real Time Clock as you would use in a proper data logging project. We also have made no effort yet to convert frequency (Y axis) backto temperature. This is only a proof-of-concept for DCDW with one master node monitoring a swarm of sensors on a single channel.

The collisions are evident. Most falsely accepted collisions the outdoor node was sending its ID and was interrupted by (much stronger) indoor node ID which was erroneously accepted as the data for the outdoor temperature. Better spaghetti code software will fix this. You software mugs will get a laugh out of this:

// Dirt Cheap Dumb Wireless  (DCDW)
// Demo code for DCDW Single Channel Sensor with Ident 
// 2011 Jan 09   by AltairLabs <http://www.altair.org> 
// This example code is in the public domain.
//
// visit Dirt Cheap Dumb Wireless   www.DCDWireless.com 
// for more open source DCDW hardware and software
// Set up DCDW PCB for Single Channel Sensor with Ident
// Oscillator frequencies in this demo use 
// Osc A = 1000 pF 100k ohms thermistor (large resistor reduces battery drain)
// Osc B = 0.01 uF and 1 meg ohms
// Osc C = 22 uF Tantalum, R5 = 10 Meg, R6 = 330k
//
// visit Arduino   www.arduino.cc 
// for great open source microcontroller hardware and software
//
// declare stuff
#include "WProgram.h"
void setup();
void loop();
long countFrequency (int pin, int precision);

int normLED = 13;             // the on-off LED is digital pin 13
int RXpin = 8;                // DCDW data link on digital pin 2
long frequency;               // frequency of pulse train 
long freq1;                   // frequency recognized as tone 1
long freq2;                   // frequency recognized as tone 2 
unsigned long interval;       // millisecs interval between samples logged
unsigned long logTime;        // time to start log entry

boolean debug = false;         // true = more verbose

const int idle=0;
const int tone1=1;
const int tone2=2;
const int tGap=3;
const int silentGap= 2;
const int toneDurat = 1;
int rxtone;
int silent;
int state;
int lastState;

const int numSensors = 6;
int sensorNode;
long sensorTone;
const int IDtones[numSensors]={80,165,368,740};   // IDent tones (B)of each sensor
int freqT2[numSensors]={0,0,0,0,0,0};             // last Sensor Tone (A) from each sensor
unsigned long lastTime[numSensors]={0,0,0,0,0,0}; // last time each sensor was heard


void setup() 
{ 
  pinMode(normLED, OUTPUT);        // optional, used for demo only
  pinMode(RXpin, INPUT);           // set RX pin to input 
  digitalWrite(RXpin, LOW);        // no pullup
  Serial.begin(9600);              // start up the serial communications USART
  Serial.println(" ");             // test the comm port
  Serial.println("Good morning, Dr. Chandra.  This is HALduino.");    
  Serial.println("Ready to log Dirt Cheep Dumb Wireless sensor data");    
  interval = 1000;                 // log data every 1 second
  logTime = millis() + interval;   // starting next sample time
} 

void loop() 
{ 
  // DCDW code starts here
  digitalWrite(normLED, HIGH);  // debug: set normLED on before counting freq
  frequency = countFrequency(RXpin, 3); // count freq at RXpin with 3 digit precision
  digitalWrite(normLED, LOW);   // debug: set normLED off when done counting
  // end of DCDW receive, now display result
  if (debug) {
    if (frequency > 0 ) {
      Serial.print("   Freq = ");               
    } 
    else {
      Serial.print("  Error = ");
    }
    Serial.println(frequency); 
  }
  // Log the data sample
  if (frequency < 0) frequency =0;
  if (debug) {
    print_time(logTime);
    Serial.print(", "); 
    Serial.print(frequency); 
    Serial.print(", "); 
    Serial.println(" ");
  }

  //
  //  state machine for recognizing remote sensors
  //
  lastState = state;
  if (frequency > 0 ) {
    rxtone++;
    silent=0;  
  } 
  else {
    rxtone=0;
    silent++;    
  }
  if (silent > silentGap) {
    state = idle;
    freq1 = 0; 
    freq2 = 0;
  }
  if (rxtone >= toneDurat && state == idle) {
    state = tone1;
    freq1 = frequency;
  }
  if (rxtone >= toneDurat && state == tone1 && (abs(frequency-freq1) > 100)) {
    state = tone2;
    freq2 = frequency;
  }
  if (state == tone2 && state != lastState) { // we have a new SSID sensor reading
    float ratio = 999.0;
    float bestRatio = 999.0;
    for (int i = 1; i <= numSensors; i++) {   // find closest match to known ID tone
      if (freq1 > IDtones[i]) {               // make the ratio >1
        ratio = float(freq1) / float(IDtones[i]);
      } else {
        ratio = float(IDtones[i]) / float(freq1);
      }
      if (ratio < bestRatio) {                // if better match thsn before
        bestRatio = ratio;
        sensorNode = i;                       // sensorNode is index of best match
        sensorTone = freq2;
      }
    } 
      freqT2[6]=int(100.0*bestRatio);                // fake sensor to log match ratio
      freqT2[5]=sensorNode;                   // fake sensor to log snesorNode
      freqT2[sensorNode]=sensorTone;
      print_time(logTime);
      for (int i = 1; i <= numSensors; i++) {
        Serial.print(", ");  
        Serial.print(freqT2[i]);  
      }
      Serial.println(" ");  
  }

  // Set time for next sample
  logTime = logTime + interval;                    // usual time
  if (logTime > 4294967295)logTime -= 4294967295;  // need in case of rollover?
  while (logTime < millis()) logTime = logTime + interval;  // in case we miss a sked
  while (millis() < logTime) {
  };                   // now wait
}

Sorry some code got chopped off at post limit. Here is the workhorse of DCDW the function countFrequency() which is less spaghettified than the demo. Still a few of you could improve on it. To go with the last post, the remaining two functions are:

long countFrequency (int pin, int precision)
counts frequency on a pin, rejects noise and returns quickly as int precision allows.

void print_time(unsigned long t_milli)
a handy function from @davekw7x to print millis() in hh:mm:ss.sss form

here they are:

long countFrequency (int pin, int precision)  
{
  // counts frequency of an incoming pulse train
  //
  // pin              any digital input pin (int)
  // precision        number of significant digits, 2, 3 or 4 (int)
  // returns:         frequency in Hz (long)
  //
  // written for Dirt Cheep Dumb Wireless sensor telemetry boards
  // using OOK radio links (SparkFun etc) which output noise on no RF in
  // so this looks for consistent pulse width as criterion against noise
  // returns negative numbers for all errors such as noise
  //
  int pulses;                   // total number of pulses to time 
  int pulse;                    // pulse count
  unsigned long timeout= 10000; // microsecs for pulseIn() to wait before giving up
  unsigned long t0;             // start time hack
  unsigned long time;           // delay in millisec
  unsigned long needTime = 1;   // delay needed for precision in millisec
  unsigned long duration;       // length of one pulse 
  unsigned long totDuration;    // total duration of first ten pulses 
  unsigned long minDuration;    // minimum length of a valid pulse 
  unsigned long maxDuration;    // maximum length of a valid pulse 
  constrain (precision, 1, 5);                 // keepin it sane
  for (int i = 1; i < precision; i++) {
    needTime = needTime * 10;
  }  // millisecs needed
  long frequency = 0;                          // nothin' yet
  totDuration = 0;                             // clear this to start
  for(pulse = 0; pulse < 10; pulse += 1)       // count 10 incoming pulses 
  {                                   
    duration = pulseIn(RXpin, LOW, timeout);   // wait for one complete pulse cycle
    if (duration == 0) {
      return -(long)pulse;
    }  // if pulse timout then abort, return neg of bad pulse
    totDuration += duration;
  } 
  maxDuration = totDuration / 7;
  minDuration = totDuration / 14;
  // now we have a range of average pulse durations
  // so start counting "pulses"pulses, increasing "pulses by 10x each time
  // until a string of pulses meets the minimum total time for accuracy
  for (pulses = 10; pulses < 100000; pulses = pulses * 10)
  {
    frequency = 0;                               // nothin' yet
    t0 = millis();                               // start time
    for(pulse = 0; pulse < pulses; pulse += 1)   // count incoming pulses 
    {                                   
      duration = pulseIn(RXpin, LOW, timeout);   // wait for one complete pulse cycle
      if (duration == 0) {
        return -(long)pulse;
      }    // pulse timout: abort, return neg of bad pulse
      if (duration > maxDuration)                  // pulse too long: abort, return error code
      {
        return -(long)duration;
      }
      if (duration < minDuration) 
      {
        return -(long)duration;
      }
    } 
    time = millis()-t0;                          // got all pulses, so time = now - start

    if (time > needTime)                              // if time is enough for precision we are done
    {
      if (debug)
      {
        Serial.print(precision);  
        Serial.print(" digits ");  
        Serial.print(needTime);  
        Serial.println(" ms need:   ");  
        Serial.print(pulses);  
        Serial.print(" pulses = ");  
        Serial.print(time);  
        Serial.println(" millisecs ");  
      }
      frequency = 1000 * (unsigned long)pulses / time;  // frequency in Hz
      return (long)frequency; 
    }
  }
  return -9999;
}  

void print_time(unsigned long t_milli)
// inspired by a very handy function by @davekw7x
// argument is time in milliseconds
// prints days, hours, min, sec, fraction in millis as
// dd hh:mm:ss.fff
{
  char buffer[23];
  int days, hours, mins, secs;
  int fractime;
  unsigned long inttime;
  inttime  = t_milli / 1000;          // total number of number of seconds
  fractime = t_milli % 1000;          // thousandths of a second
  // number of days is total seconds divided by number of seconds per day
  days     = inttime / (86400);       // days
  inttime  = inttime % (86400);       // remainder in secs
  // hours is remainder of secs divided by secs per hour
  hours    = inttime / 3600;          // hours
  inttime  = inttime % 3600;          // remainder in secs
  // mins is remainder of secs divided by secs per min
  mins     = inttime / 60;            // mins
  inttime  = inttime % 60;            // remiander in secs
  // secs is the remaining number of seconds.
  secs = inttime;
  // Don't bother to print days
  sprintf(buffer, "%02d %02d:%02d:%02d.%03d", days, hours, mins, secs, fractime);
  Serial.print(buffer);
}

@AltairLabs I want to be a beta-tester :wink:

agreed, have ordered parts to assemble a prototype for coding, then miniaturization. I will be trying to have three temperatures per sensor. Ambient, circuit-battery, meat.