I'm trying to make my own wakeup lamp. I have a lamp with a large metal base to hide all the components in. I also have a Velleman K8064 DC Controlled Dimmer Kit which I have put together and is working. I have a Raspberry pi to control it all (I also plan to use the Pi as an Android Dock)
I would like the base of the lamp to work as a touch switch to turn the lamp on and off. I've tried breadboarding a touch switch I found on www.talkingelectronics.com however when testing it the lamp is always on.
Can anyone point me in the direction of a wiring diagram of a simpler touch switch that will work with a raspberry pi?
4 Answers 4
If you have low level access to the raspy GPIO's then there's an easy way to do that, all you need is a reisistor. Yeah, you read that right.
Just connect a pin to the lamp base and tie the resistor between the base and \$V_{cc}\,ドル that's the 3.3V rail, remember that the pins are not 5 volt tolerant.
Here is in pseudocode what you need to do software side:
while(1) {
timer.reset()
gpiox.direction = GPIO.OUT
gpiox.write(GPIO.LOW)
while(gpiox.read != GPIO.LOW)
gpiox.direction = GPIO.IN
timer.start()
while(gpiox.read == GPIO.LOW)
timer.stop()
out = timer.read()
if (out > THRESHOLD)
pressed = true
else
pressed = false
}
What's happening? First of all you reset a timer. It needs to be fast, something like a cpu timer that is incremented at each clock cycle or so.
You set your pin as ouput, write a zero to it and then wait for it to really go to zero. That may be done inside the write depending on the driver/HAL you are using, but for this thing to work you'll need to be using none. After the pin is really zero you set it as an input and start the timer. The parasitic capacitance of the lamp base and the pin input and, if present, your finger starts to charge through the resistance. When the voltage across the capacitance is read as a logic high you stop the timer: the trick is that if your finger is present the capacitance is bigger so it takes a lot of time more to load to the logic one, so you can tell the finger presence by reading the timer.
What problems might you encounter?
Well maybe you just don't have such a low level access to the raspy GPIO pins... But that's something you need to find out. Maybe you don't have such a low level timer. The biggest problem is that maybe the capacitance of the lamp base is very big so that touching it won't make a big difference.
This method anyway is worth a try because it costs a few cents. About the resistor, you want something that charges the capacitance slow enough for your timer to actually measure the time it takes, but fast enough to be actually able to sample the "button" maybe 20 times per second. Lamp plus body plus pin may be around 1nF, you want a \$\tau=RC\$ of about 20ms so $$R=\frac{\tau}{C}=\frac{20\cdot10^{-3}s}{1\cdot10^{-9}F}=20M\Omega$$ Well that's too much. You will need a fast timer and go for something around \1ドルM\Omega\$
But how does it work?
Everything around us has a capacitance with respect to something else. It's common to refer to ground as "something else", so everything has a capacitance with respect to ground. That said, let's consider the following circuit:
schematic
simulate this circuit – Schematic created using CircuitLab
\$C_{in}\$ is the digital port input capacitance plus the lamp base capacitance towards ground, usually around some 100pF, while \$C_H\$ (as in 'human') is the capacity that we have towards ground. When you touch the lamp base you close the switch labeled 'touch', while the raspberry can only act on \$SW_{in}\$. You start with \$SW_{in}\$ closed, so \$V_{C_{in}} = 0\$. When the raspberry opens \$SW_{in}\$ the input capacitance starts to charge to \$V_{cc}\$ through \$R_p\$ with a time costant \$\tau=R_p\cdot C_{in}\$. The V(t) law is quite known: $$V_{C_{in}}(t) = V_{cc}(1-e^{-\frac{t}{\tau}})$$ The raspberry will detect a high input when voltage will reach something around \$\frac{2}{3}V_{cc}\,ドル so that will take:
$$t_{HIGH}=\tau\ln\Big(\frac{V_{cc}}{V_{cc}-\frac{2}{3}V_{cc}}\Big)=\tau\ln(3)$$
At this point it would stop the timer and check ho long did it take: if it's something around \1ドル.1\tau\$ no touch is occurring. The raspberry would close \$SW_{in}\$ and wait for the capacitor to discharge, i.e. waiting for the pin to read zero, to finally open the switch and start over.
But what if we close \$SW_h\$? Well, \$\tau\$ changes: \$\tau'=R_p\cdot (C_{in}+C_H)\$ now, and if \$C_{in}\$ is small enough, and hopefully it is, \$tau'\$ might even be ten times bigger than \$\tau\$. The raspberry will start its timer but now it will take considerably longer to read an high input:
$$t'_{HIGH}=...=\tau'\ln(3)\approx 10\cdot\tau\ln(3)=10\tau$$
The second to last relation is a sort of "we hope that's true" relation. When the raspberry will finally read an high input it will stop the timer and say well that take quite a lot of time, let's toggle the light for my programmer! And that's pretty much it.
And why does your solution work?
That's because your body is acting as an antenna. You are basically feeding the mains frequency in the raspberry input pin, and that can make the input read as one, but that would be an unreliable way to toggle your lamp. For what I can read your system is working quite randomly... Have a try at the proper way.
-
\$\begingroup\$ I do like your idea, however, do you think the Raspberry Pi will be powerful enough to be used as a touch switch and a pwm? I was also hoping to use the Pi as an Android Dock so I could play music as well. \$\endgroup\$TheLukeMcCarthy– TheLukeMcCarthy2014年04月29日 16:35:15 +00:00Commented Apr 29, 2014 at 16:35
-
\$\begingroup\$ It's powerful enough for sure, the question is: can you operate at such a low level? \$\endgroup\$Vladimir Cravero– Vladimir Cravero2014年04月29日 16:46:59 +00:00Commented Apr 29, 2014 at 16:46
-
\$\begingroup\$ I believe so, I will try it out and get back to you. \$\endgroup\$TheLukeMcCarthy– TheLukeMcCarthy2014年04月29日 16:50:49 +00:00Commented Apr 29, 2014 at 16:50
-
\$\begingroup\$ I'm just trying this out and I'm having a little trouble understanding the electronics side of things. Am I right in thinking that when I touch the lamp the voltage will drop? If so shouldn't the output start high and then the lamp is touched and the voltages goes low the Raspberry Pi should turn the lamp on? \$\endgroup\$TheLukeMcCarthy– TheLukeMcCarthy2014年05月03日 13:36:12 +00:00Commented May 3, 2014 at 13:36
-
\$\begingroup\$ @TheLukeMcCarthy you are not right. The voltage is oscillating, when you touch the base the time costant changes. If you are very interested in this I can include some graphs in the next days. \$\endgroup\$Vladimir Cravero– Vladimir Cravero2014年05月12日 09:50:28 +00:00Commented May 12, 2014 at 9:50
I wrote a C implementation (see below) based on the pseudocode in Vladimir's answer.
Works like a charm on my Pi Zero W, I'm using it to toggle the backlight of an LCD display by touching its bezel. I used a 1MΩ resistor, which takes ~20μs to charge when idle and 45-110μs when touching. It can even sense touch through the non-conductive coating on the bezel, so the Pi should be protected from static discharge.
So far I have not had any issues with repeated touches not being detected.
// Filename: touch_toggle.c
/* Compile with gcc -lwiringPi touch_toggle.c -o touch_toggle
* Run with sudo ./touch_toggle
* Do not touch while starting the program so it can initialize properly
*/
/* SCHEMATIC
*
* ,----------------------,
* | Raspberry Pi |
* | |
* | TOUCH_PIN VCC |
* `-----+-------------+--'
* | |
* +---[1MΩ]-----+
* |
* Touch surface
*
*/
#include <wiringPi.h>
#include <stdio.h>
// Note: Pin numbers are in BCM notation (pin number format is set by wiringPiSetupGpio)
// See pinout.xyz
#define TOUCH_PIN 20
#define OUTPUT_PIN 21
// How long to pull the touch pin low
// Controls loop speed and affects CPU usage
#define DELAY 15
int main(void) {
wiringPiSetupGpio();
unsigned int timer;
unsigned int threshold = 0;
unsigned char state = 0; // Currently being touched?
unsigned char out_state = 0; // State of output pin
signed char hysteresis = 0; // Counter for consecutive readings
pullUpDnControl(TOUCH_PIN, PUD_OFF); // Not sure if this would ever be set, just to be safe
pinMode(OUTPUT_PIN, OUTPUT);
digitalWrite(OUTPUT_PIN, out_state);
// Measure capacitance to calibrate touch sensitivity
for (char i=0; i < 10; i++) {
// Pull touch pin low to discharge
pinMode(TOUCH_PIN, OUTPUT);
digitalWrite(TOUCH_PIN, LOW);
// Wait a bit
delay(DELAY);
// Start timer
timer = micros();
pinMode(TOUCH_PIN, INPUT);
// Wait for pin to become high
while (!digitalRead(TOUCH_PIN));
// Get time elapsed
threshold += micros() - timer;
}
// Set threshold to twice the average capacitance
threshold /= 5; // This number might need to be increased if the touch is not sensitive enough
printf("threshold=%d\n",threshold);
while (1) {
pinMode(TOUCH_PIN, OUTPUT);
digitalWrite(TOUCH_PIN, LOW);
delay(DELAY);
timer = micros();
pinMode(TOUCH_PIN, INPUT);
while (!digitalRead(TOUCH_PIN));
timer = micros() - timer;
if (timer > threshold) {
if (hysteresis < 0) hysteresis = 0;
hysteresis++;
} else {
if (hysteresis > 0) hysteresis = 0;
hysteresis--;
}
// 3 consecutive readings are required to toggle touch state
if (hysteresis > 2) {
if (state == 0) {
out_state = !out_state;
digitalWrite(OUTPUT_PIN, out_state);
state = 1;
// Print when touch starts and the measured value
// Can be commented out
printf("START %d", timer);
fflush(stdout); // Display instantly (by default only flushed on newline)
}
hysteresis = 0;
} else if (hysteresis < -2) {
if (state == 1) {
state = 0;
printf(" END\n");
}
hysteresis = 0;
}
}
return 0;
}
Code also at: https://pastebin.com/FrsYCtXu
Thanks to Vladimir for his answer that allowed me to come up with this solution.
schematic
simulate this circuit – Schematic created using CircuitLab
The python code is as follows
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
touchSwitch = 23
outputPin = 24
GPIO.setup(touchSwitch, GPIO.IN)
GPIO.setup(outputPin, GPIO.OUT)
GPIO.output(outputPin, False)
while True:
switchTouched = GPIO.input(touchSwitch)
if switchTouched:
print "touch detected"
time.sleep(0.3) # sleep again here so not to toggle the lamp to quickly
else:
print "not touched"
time.sleep(0.15) # 0.10 seems to give the best results but 0.15 uses less CPU
The problem with this solution is, when holding the touch plate after somewhere between 5 to 12 detections of a touch, the touch will not be detected for a while and then will be detected again. Given I'm only using it for a touch lamp the solution is good enough for my needs.
-
\$\begingroup\$ You might want to touch ground (wall plate screw or similar) before touching the lamp. Just to prevent a static spark from ruining your GPIO23 input. Congratualtions, it is always fun to arrive at a Very simple solution. \$\endgroup\$Marla– Marla2014年05月10日 14:24:14 +00:00Commented May 10, 2014 at 14:24
-
\$\begingroup\$ @Marla do you think I should be using a resistor to protect the R-Pi? The only problem I would see is that the voltage only goes to ~0v5 when touching the lamp. Which is only just high enough to register a high voltage on the R-Pi. \$\endgroup\$TheLukeMcCarthy– TheLukeMcCarthy2014年05月12日 09:17:10 +00:00Commented May 12, 2014 at 9:17
This seems to work with no lag (using TheLukeMcCarthy's circuit but with GPIO 18 as input and GPIO 17 as output pin)
#include <wiringPi.h>
int main (void)
{
register unsigned char on = 0 ;
wiringPiSetup () ;
pinMode ( 1, INPUT) ;
pinMode ( 0, OUTPUT) ;
pinMode ( 4, OUTPUT) ;
digitalWrite ( 4, LOW) ;
digitalWrite ( 0, LOW) ;
while ( 1 )
{
if ( digitalRead (1) )
{
if ( on )
{
on=0;
digitalWrite ( 4, LOW) ;
}
else
{
on=1;
digitalWrite ( 4, HIGH) ;
}
delay ( 300 ) ;
}
delay ( 50 ) ;
}
return 0 ;
}
-
\$\begingroup\$ It might be worth adding a reference in this answer to the circuit above so that if the order of the answers move around it's clearer what it relates to. \$\endgroup\$PeterJ– PeterJ2014年08月03日 01:54:37 +00:00Commented Aug 3, 2014 at 1:54
Explore related questions
See similar questions with these tags.