Well, I am sort of surprised...
Watched the linked YouTube video and thought, hmm, bet that's a) unreliable and b) running on mains pickup.
Anyhow, I built one.
For his application it's not bad, but it does rely on pickup (probably from mains electricity) because it stops working if you ground yourself. So it wouldn't work outdoors for example. It also depends a lot on the surface it is on. It only works when you press a finger on it - it has no range i.e. no effect from a few mm away. I also thought I'd damaged an analog input, which is a real possibility, but it seems ok now.
The real news is on a bit of an improvement - this is what I built:
The sensing strip is cut from copper foil tape, the central diagonal cut strips are about 10mm down to 1mm wide and about 300mm long, spaced by about 1mm. Around the outside is another strip roughly 5mm wide and spaced 3mm from the ones in the middle. Here's a closer view of each end:
(if the images don't appear inline, they are attached)
The foil was laid on some thin plastic sheet (maybe mylar, about 0.2mm thick), wires soldered on and then covered with a layer of clear packing tape. I've taped it to a length of PVC pipe (about 30mm dia.)
The idea is to drive the outer strip so there is something to couple to the inner strips.
The mashed-together code is:
#define DEBOUNCE_TIME 20 //20mS per debounce step
const int calPin = 2; // calibration button pin
// useful functions:
int debouncePin( uint8_t * pdebounce, int pin, int to_level );
void Sprint( char *fmt, ... );
//-----------------------------------
void setup() {
Serial.begin(9600);
pinMode(calPin, INPUT_PULLUP);// CAL button pin
pinMode( A0, INPUT );
pinMode( A1, INPUT );
pinMode( 4, OUTPUT );
digitalWrite( 4, LOW );
}
//-----------------------------------
static uint32_t time10mS=0;
static int count100mS= 0;
static int32_t a0,a1, total, b0, b1, cal0=0, cal1=0;
static uint8_t d_calPin= 0x7f;
int i;
void loop() {
// run 10mS and 100mS loops:
if( millis() > time10mS + 10 )
{
do_10mS_loop();
if( ++count100mS > 10 )
{
do_100mS_loop();
count100mS= 0;
}
}
}
// Executed every 10mS
//
void do_10mS_loop()
{
if ( debouncePin( &d_calPin, calPin, LOW ) )
{
// do something useful...
Serial.println("Calibrate!");
cal0= b0;
cal1= b1;
}
}
// Executed every 100mS
//
void do_100mS_loop()
{
a0= a1= 0;
for( i=0; i<500; i++ )
{
digitalWrite( 4, LOW );
digitalWrite( 4, HIGH );
a0+= analogRead( A0 );
digitalWrite( 4, LOW );
digitalWrite( 4, HIGH );
a1+= analogRead( A1 );
}
total= a0+a1;
b0= (a0 * 1000)/ (total/2) - cal0;
b1= (a1 * 1000)/ (total/2) - cal1;
Sprint("a0= %ld, a1= %ld, b0= %ld, b1= %ld, diff= %ld\n", a0, a1, b0, b1, b1-b0 );
}
//----------------------------------------------------------------------
// debouncePin
// - debounces the given pin input
// passed:
// debounce - pointer to a byte used to debounce this pin
// pin - no. of the pin to read and debounce
// to_level - the level at which the input is 'active'; LOW or HIGH
// returns:
// 1 when pin has just transitioned to the 'active' state
// otherwise 0
//
// usage:
// static uint8_t pin3debounce= 0x7f; // always initialise to 0x7f
// if( debouncePin( &pin3debounce, 3, LOW ) )
// \\ pin debounced LOW
//
int debouncePin( uint8_t * pdebounce, int pin, int to_level )
{
uint8_t levelFlip= (to_level == HIGH) ? 0 : 1;
*pdebounce &= 0x7f; // clear action bit
*pdebounce= (*pdebounce & 0xc0) | ((*pdebounce & 0x3f) << 1) | (digitalRead(pin) ^ levelFlip);
if( *pdebounce == 0x3f )
*pdebounce= 0xff; // is debounced '1', set state and action flags
if( (*pdebounce & 0x3f) == 0 )
*pdebounce= 0x00; // is debounced '0', set state= 0;
return (*pdebounce >> 7); // return 'action' bit
}
//-----------------------------
// Serial.print helper function
// - a real cut-down printf()
//
void Sprint( char *fmt, ... )
{
char c;
va_list args;
va_start( args, fmt );
while( (c=*fmt) != 0 ){
switch( c )
{
case '%':
c= *(++fmt);
if( c == 'l' ){ // lower case L for "%ld" or "%lu"
c= *(++fmt) & 0xdf; // convert 'd', 'u' to 'D', 'U'
}
switch( c )
{
case 'd': Serial.print( va_arg(args,int16_t) ); break;
case 'u': Serial.print( va_arg(args,uint16_t) ); break;
case 'D': Serial.print( va_arg(args,int32_t) ); break;
case 'U': Serial.print( va_arg(args,uint32_t) ); break;
case 'f': Serial.print( va_arg(args,double) ); break;
case 'h': Serial.print( va_arg(args,int), HEX ); break;
case 'c': Serial.print( (char)va_arg(args,int) ); break;
case 's': Serial.print( va_arg(args, char *) ); break;
default: break;
}
break;
case '\\':
c= *(++fmt);
if( c == 'n' )
Serial.println();
else
Serial.print( c );
break;
default:
Serial.print( c );
break;
}
++fmt;
}
va_end( args );
}
Analog inputs A0 and A1 are connected to the diagonal-cut strips and have 1Mohm resistors to ground.
Digital output D4 is connected to the outer strip. I've used D2 as an input for a 'Calibrate' button which removes offsets.
When calibrated it prints: (note the 'diff' value)
a0= 79048, a1= 69030, b0= 0, b1= 0, diff= 0
a0= 78969, a1= 69060, b0= -1, b1= 1, diff= 2
a0= 79093, a1= 68950, b0= 1, b1= -1, diff= -2
Hand gripping lower end:
a0= 111710, a1= 100892, b0= -17, b1= 17, diff= 34
a0= 111669, a1= 100791, b0= -16, b1= 16, diff= 32
a0= 111773, a1= 100860, b0= -16, b1= 16, diff= 32
Hand gripping upper end:
a0= 113945, a1= 128126, b0= -126, b1= 126, diff= 252
a0= 114930, a1= 129889, b0= -129, b1= 129, diff= 258
a0= 115349, a1= 130419, b0= -129, b1= 129, diff= 258
Surprisingly it seems to work ! However, you have to grip the tube for it to register well.
I may play with this some more and see if I can get anything out of a longer 'sensor'. It may help to have the 'driving strip' all the way around the tube so there's more area to couple into your hand.
Even this quick'n'nasty test piece lays nice and flat, so putting it around the outside of the tube and painting it may work.
Yours,
TonyWilk
P.S.
tip: cheap copper foil tape is sold as 'Slug Tape' - apparently garden slugs don't like sliming over copper, so you use it to protect plants.
CapStrip.ino (3.66 KB)