5
\$\begingroup\$

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?

asked Apr 29, 2014 at 14:08
\$\endgroup\$

4 Answers 4

8
\$\begingroup\$

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.

answered Apr 29, 2014 at 14:36
\$\endgroup\$
8
  • \$\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\$ Commented 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\$ Commented Apr 29, 2014 at 16:46
  • \$\begingroup\$ I believe so, I will try it out and get back to you. \$\endgroup\$ Commented 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\$ Commented 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\$ Commented May 12, 2014 at 9:50
4
\$\begingroup\$

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

SamGibson
18.5k5 gold badges42 silver badges65 bronze badges
answered Apr 6, 2021 at 18:25
\$\endgroup\$
1
\$\begingroup\$

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.

answered May 10, 2014 at 10:51
\$\endgroup\$
2
  • \$\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\$ Commented 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\$ Commented May 12, 2014 at 9:17
1
\$\begingroup\$

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 ;
}
answered Aug 2, 2014 at 22:30
\$\endgroup\$
1
  • \$\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\$ Commented Aug 3, 2014 at 1:54

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.