Friday, August 9, 2013

Reading switches via GPIOs on Raspberry Pi in Lua (ArchLinux+Mihini)

My previous posts on using Eclipse M2M with Raspberry Pi:
  1. HelloWorld in Lua on Raspberry Pi running Mihini on ArchLinux with Koneki as IDE (link)
  2. LED Blinky in Lua on Raspberry Pi running Mihini on ArchLinux (link)
  3. Autostart Lua script on RasPi+Archlinux+Mihini at power up (link)
After having accomplished the above, I moved on to reading switches and controlling LEDs
My schematic looks like below:

This is how I connected 4 switches and 4 LEDs to the
GPIO pins of my Raspberry Pi. Black wire is Gnd.
I wanted to toggle an LED for each press of the corresponding switch. I paired up each switch to an LED:

Switch at GPIO 4 would control the LED at GPIO 22
Switch at GPIO 17 would control the LED at GPIO 23
Switch at GPIO 18 would control the LED at GPIO 24
Switch at GPIO 21 would control the LED at GPIO 25

I registered a hook function which would be called whenever the state changed of any of the four GPIOs to which the switches were connected. From within the hook functions, LED corresponding to that switch is toggled. A message is printed from inside the hook function with timestamp retrieved using the gettime() function of the 'socket' module. gettime() provides higher resolution (returns time in milliseconds) than the time() function of the 'os' module (returns time in seconds). We create an idle task using main() with an infinite loop in it. This keeps the script running albeit doing nothing while the hook listens for switch presses.

sw2leds.lua:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
-- Switches to LEDs

package.path = '/opt/mihini/lua/?.lua;/opt/mihini/lua/?/init.lua;' .. package.path
package.cpath = '/opt/mihini/lua/?.so;' .. package.cpath

local sched = require "sched"
local gpio = require "gpio"
local socket = require "socket"
---------------------------------------------------------------
local function toggle_led(n)
 local stat = gpio.read(n)
 if stat == 1 then
  gpio.write(n,0)
 elseif stat == 0 then
  gpio.write(n,1)
 end
end

local function main()
 while true do
 sched.wait(1)
 end
end

local function myhook(id, value)
 print(string.format("Time %15s | GPIO %2d changed | New value %s",socket.gettime(), id, value))
 if     id == 4 then toggle_led(22)
 elseif id == 17 then toggle_led(23)
 elseif id == 18 then toggle_led(24)
 elseif id == 21 then toggle_led(25)
 end
end
---------------------------------------------------------------
gpio.configure(22, {direction="out", edge="both", activelow="0"})
gpio.configure(23, {direction="out", edge="both", activelow="0"})
gpio.configure(24, {direction="out", edge="both", activelow="0"})
gpio.configure(25, {direction="out", edge="both", activelow="0"})
gpio.configure(4, {direction="in", edge="falling", activelow="1"})
gpio.configure(17, {direction="in", edge="falling", activelow="1"})
gpio.configure(18, {direction="in", edge="falling", activelow="1"})
gpio.configure(21, {direction="in", edge="falling", activelow="1"})

gpio.register(4, myhook)
gpio.register(17, myhook)
gpio.register(18, myhook)
gpio.register(21, myhook)


sched.run(main)
sched.loop()

As you will note, this script doesn't debounce the switches and on each press, you end up with flickering LEDs which end up in a random state post bouncing. You also get a lot of messages printed for each press:

Time 1376043551.5499 | GPIO  4 changed | New value 0
Time 1376043551.5523 | GPIO 17 changed | New value 0
Time  1376043551.554 | GPIO 18 changed | New value 0
Time  1376043551.556 | GPIO 21 changed | New value 0
Time 1376043552.9756 | GPIO  4 changed | New value 0
Time 1376043552.9786 | GPIO  4 changed | New value 1
Time 1376043552.9816 | GPIO  4 changed | New value 1
Time 1376043552.9844 | GPIO  4 changed | New value 1
Time 1376043552.9874 | GPIO  4 changed | New value 1
Time 1376043552.9901 | GPIO  4 changed | New value 1
Time  1376043552.993 | GPIO  4 changed | New value 1
Time 1376043552.9956 | GPIO  4 changed | New value 1
Time 1376043552.9989 | GPIO  4 changed | New value 1
Time 1376043553.0015 | GPIO  4 changed | New value 1
Time 1376043553.0044 | GPIO  4 changed | New value 1
Time  1376043553.007 | GPIO  4 changed | New value 1
Time 1376043553.0098 | GPIO  4 changed | New value 1
Time 1376043553.0124 | GPIO  4 changed | New value 1
Time  1376043553.161 | GPIO  4 changed | New value 0
Time 1376043554.0234 | GPIO  4 changed | New value 0
Time 1376043554.0333 | GPIO  4 changed | New value 1
Time 1376043554.0359 | GPIO  4 changed | New value 1
Time 1376043554.0387 | GPIO  4 changed | New value 1
Time 1376043554.1286 | GPIO  4 changed | New value 1
Time 1376043554.1311 | GPIO  4 changed | New value 1
Time 1376043570.8616 | GPIO 17 changed | New value 1
Time 1376043570.8643 | GPIO 17 changed | New value 1
Time 1376043570.8674 | GPIO 17 changed | New value 1
Time 1376043570.8704 | GPIO 17 changed | New value 1
Time 1376043570.8732 | GPIO 17 changed | New value 1
Time 1376043570.9058 | GPIO 17 changed | New value 0
Time 1376043571.5563 | GPIO 17 changed | New value 0
Time 1376043571.5589 | GPIO 17 changed | New value 1
Time  1376043571.681 | GPIO 17 changed | New value 0
Time 1376043571.7638 | GPIO 17 changed | New value 0

And so, I added some debouncing to each of the switches. We do this by ignoring the latter of two consecutive calls to the hook function for a particular switch if the time difference between them is less than quarter of a second. All message prints have been removed from this code.

sw2leds_debounce.lua:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
-- Switches to LEDs (with debouncing)

package.path = '/opt/mihini/lua/?.lua;/opt/mihini/lua/?/init.lua;' .. package.path
package.cpath = '/opt/mihini/lua/?.so;' .. package.cpath

local sched = require "sched"
local gpio = require "gpio"
local socket = require "socket"
---------------------------------------------------------------
local function toggle_led(n) -- Function to toggle the led
  local stat = gpio.read(n)
  if stat == 1 then
    gpio.write(n,0)
  elseif stat == 0 then
    gpio.write(n,1)
  end
end

do -- Hook function to debounce the switches
  -- First up are the variables to store the time when the switch was last pressed
  local gpio4time = socket.gettime()
  local gpio17time = socket.gettime()
  local gpio18time = socket.gettime()
  local gpio21time = socket.gettime()
  -- Second we have the variable to hold the debounce time in seconds
  -- If two consecutive pulses arrive within this interval, second pulse is ignored 
  local bounce_interval_secs = 0.25 
  -- Finally we have the function 
  function hook_switch_debounce(id, value) 
    if id == 4 then
      if socket.gettime() - gpio4time > bounce_interval_secs then
        toggle_led(22)
      end
      gpio4time = socket.gettime()
    elseif id == 17 then
      if socket.gettime() - gpio17time > bounce_interval_secs then
        toggle_led(23)
      end
      gpio17time = socket.gettime()
    elseif id == 18 then
      if socket.gettime() - gpio18time > bounce_interval_secs then
        toggle_led(24)
      end
      gpio18time = socket.gettime()
    elseif id == 21 then
      if socket.gettime() - gpio21time > bounce_interval_secs then
        toggle_led(25)
      end
      gpio21time = socket.gettime()
    end
  end
end

local function main() -- Idle task
  while true do
    sched.wait(1)
  end
end

---------------------------------------------------------------

-- Main Logic Begins
-- -- Configure the GPIOS for the 4 LEDs 
gpio.configure(22, {direction="out", edge="both", activelow="0"})
gpio.configure(23, {direction="out", edge="both", activelow="0"})
gpio.configure(24, {direction="out", edge="both", activelow="0"})
gpio.configure(25, {direction="out", edge="both", activelow="0"})

-- -- Configure the GPIOS for the 4 Switches
gpio.configure(4, {direction="in", edge="falling", activelow="1"})
gpio.configure(17, {direction="in", edge="falling", activelow="1"})
gpio.configure(18, {direction="in", edge="falling", activelow="1"})
gpio.configure(21, {direction="in", edge="falling", activelow="1"})

-- -- Register the hooks for the 4 Switches
gpio.register(4, hook_switch_debounce)
gpio.register(17, hook_switch_debounce)
gpio.register(18, hook_switch_debounce)
gpio.register(21, hook_switch_debounce)

-- -- Schedule the idle thread
sched.run(main)

-- -- Start Scheduler
sched.loop()

0 comments:

Post a Comment