Possible Serial Interaction? (SOLVED)

This is taking place on a Duemilanove with an ATMega328P.

My sketch polls a MaxSonar sensor to determine whether someone is standing in front of my aquarium and then adjusts the tank lights accordingly. Distance measurement is by way of pulse width detection (147 microseconds to the inch).

I'm playing around with the sketch and the hardware to eliminate spurious measurements. I've got a working solution...but only when the Arduino's USB port is plugged into the serial port of my laptop. When it's not plugged in, the sketch detects the presence of an observer and ramps the lights up, but it never detects the observer's departure so as to turn the lights down.

The effect of plugging in the USB connection is reproducible. Merely rebooting the Arduino, without the USB being connected, doesn't solve the problem.

Here's the portion of the code that interfaces to the MaxSonar. The entire sketch is pretty large (it compiles to around 20KB) so posting it probably wouldn't be too useful.

The header file:

// interface for sonar observation
#ifndef OBSERVATION_H
#define OBSERVATION_H

#include "Arduino.h"

#include <Time.h>

#define MICROSEC_PER_INCH 147.0
#define SONAR_READINGS 5
#define SENSE_WINDOW_MICROSEC 50000

class ObserverDetection;

typedef void (* DetectionCallback) ( ObserverDetection* ); 

class ObserverDetection
{
public:
	ObserverDetection( byte pin );
	int detect();
	int detect( const time_t t );

	byte freqSec;

private:
	byte _pin;
	unsigned long _pulseWidths[SONAR_READINGS];
	unsigned long _sortedWidths[SONAR_READINGS];
	int _lastDist;
	time_t _lastTime;

	void sortReadings();
};

#endif

The method definition file:

// implementation of sonar observation interface
#include "observation.h"

ObserverDetection::ObserverDetection( byte pin )
{
	_pin = pin;
	pinMode(_pin, INPUT);

	freqSec = 5;
	_lastTime = 0;	// force reading on first detect
	_lastDist = -1;
}

int ObserverDetection::detect()
{
	return detect(now());
}

int ObserverDetection::detect( const time_t t )
{
	if( (t - _lastTime) < freqSec ) return _lastDist;

	for( byte idx = 0; idx < SONAR_READINGS; idx++ )
	{
		_pulseWidths[idx] = pulseIn(_pin, HIGH, SENSE_WINDOW_MICROSEC);
	}

	sortReadings();
	_lastDist = (int) ((float) _sortedWidths[ SONAR_READINGS / 2 ] / MICROSEC_PER_INCH);

	if( _lastDist <= 0 ) _lastDist = -1;

	return _lastDist;
}

void ObserverDetection::sortReadings()
{
	for( byte i = 0; i < SONAR_READINGS; i++ )
	{
		_sortedWidths[i] = _pulseWidths[i];
	}

	for( byte i=0; i< SONAR_READINGS- 1; i++ ) 
	{
		byte m = i;

		for( byte j=i+1; j< SONAR_READINGS; j++ )
		{
			if (_sortedWidths[j] < _sortedWidths[m]) m = j;
		}

		if (m != i)
		{
			unsigned long t = _sortedWidths[m];
			_sortedWidths[m] = _sortedWidths[i];
			_sortedWidths[i] = t;
		}
	}
}

FYI, the basic algorithm is:

  • wait 5 seconds between observations (this is adjustable)
  • take 5 sonar readings (each one with a timeout window of 50000 microseconds)
  • sort the readings by distance
  • select the 3rd reading (median) as the observation

Elsewhere in the sketch the observed median distance value is used to determine whether the system is or is not under observation.

My question is this: are there side effects of the Arduino USB port being connected to a destination which alter the way the board works? Even to the extent of "just" inserting some timing delays in the looping?

Do you know when your constructor is called, relative to when init() gets done setting up the hardware? If you are not absolutely certain, then you should not be doing hardware stuff in the constructor.

Notice that the Serial instance doesn't. you must call Serial.begin(). Many other classes have a begin() method. Yours should, too.

My question is this: are there side effects of the Arduino USB port being connected to a destination which alter the way the board works? Even to the extent of "just" inserting some timing delays in the looping?

Not that I am aware of.

Good point, thanks. I rewrote all the classes where I was doing hardware interaction in the constructor to have a begin() method instead.

FWIW, that did not change the behavior I'm seeing. Having a "live" USB connection has the sketch behave the way I want it to. Having an unconnected USB connection results in the sonar sensor not being able to determine when there's an empty field of view.

Maybe I'll just have to leave my laptop plugged into the project all the time... :slight_smile:

How are you powering the Arduino when not using USB? Maybe what you have is an (under)power(ed) issue.

That's an interesting idea, thanx. The project is powered off of a 90W 15V wall wart. The 15V supply is connected directly to the buckpucks controlling the LED arrays, and connected to a 8V voltage regulator to produce power for the Arduino. There's another 5V regulator that is used to power various things including the MaxSonar sensor. I'd post a schematic but in the two years since I last worked on this project I seem to have cunningly deleted the schematic file from my computer. I have to pull some old backups and see if I can find it.

A quick update to record how I solved my problem.

Further testing showed that there was, in fact, no odd serial connection interaction (i.e., when I waited long enough, odd sonar triggering occurred whether or not the USB connection was established). I'm not sure why I had such a long run of observations implying there was a connection.

I finally did what I should have done earlier and slapped a scope on the variable pulse width signal coming out of the MaxSonar. What I found was that when there was no observer present I was getting spurious short distance readings. Most of the pulse widths would look normal (wide), but every second or so there would be a single narrower pulse. This was enough to falsely trip my detection algorithm, which was based on measuring the median distance of a set of 5 or 7 measurements.

These spurious narrow pulses may be a consequence of the physical placement of the MaxSonar sensor and the particular sensor model I'm using. There's a hardwood floor just "beyond" the detection horizon I've set for my sketch and it may have been creating the short distance readings when nothing is in the field of view. That's because the sensor model I picked has wide detection lobes. In retrospect I probably should have bought one with narrow lobes. Then again, I wanted to be able to detect anyone standing anywhere in front of the tank, which requires a wide field of view.

BTW, I did try putting a filter on the MaxSonar power supply. I didn't have the recommended 100uF capacitor, but with 47uF there was no change in the spurious short distance readings. That strengthens my belief that the hardwood floor is causing problems.

I've minimized the false triggers by doing two things. First, I've pointed the sensor up (i.e., away from the hardwood floor) as much as I can. Second, I changed the algorithm to look at the maximum distance detected within a series of 5 or 7 measurements. This ignores the spurious short measurements when nothing is in the field of view, but still works when an observer is in the target area.