Go Down

Topic: DCDW = Dirt Cheap Dumb Wireless (Read 24988 times) previous topic - next topic

AltairLabs

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)
Quote
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!


AltairLabs

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.

AltairLabs

Demo sketch for DCDW Two Button Remote

Code: [Select]
// 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;
}  

AltairLabs

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.
Code: [Select]
// 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.

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.

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.





tkbyd

Code: [Select]
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.

AltairLabs

#7
Jan 22, 2011, 02:36 am Last Edit: Jan 22, 2011, 02:38 am by AltairLabs Reason: 1
Howdy @martin_bg

Quote
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.

Code: [Select]
// 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.

AltairLabs

Howdy, @OSGrill

Quote
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.

AltairLabs

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.

AltairLabs

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?

Wagner Sartori Junior

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:
http://www.techspot.com/vb/topic138655.html

CMOS corruption! LOL!

AltairLabs

#12
Jan 24, 2011, 04:25 am Last Edit: Jan 24, 2011, 05:23 am by AltairLabs Reason: 1
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.

AltairLabs

#13
Jan 24, 2011, 05:07 am Last Edit: Jan 24, 2011, 06:13 am by AltairLabs Reason: 1
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
[glow]
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!
[/glow]
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:

Code: [Select]
// 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
}

AltairLabs

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:
Code: [Select]

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);
}

       

Go Up