Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Hacksaar
Sphinx
Commits
c6979ec0
Commit
c6979ec0
authored
Feb 18, 2015
by
Ralf
Browse files
port to I²C
parent
ca45cafb
Changes
7
Hide whitespace changes
Inline
Side-by-side
actor.py
View file @
c6979ec0
from
libtuer
import
ThreadFunction
,
logger
import
RPi.GPIO
as
GPIO
import
time
class
Actor
:
...
...
@@ -8,13 +7,16 @@ class Actor:
CMD_LOCK
=
2
class
CMD
():
def
__init__
(
self
,
name
,
pin
,
tid
,
todo
,
verbose
=
True
):
def
__init__
(
self
,
name
,
pin
,
tid
,
todo
,
i2c
,
invert
,
verbose
=
True
):
self
.
name
=
name
self
.
pin
=
pin
self
.
tid
=
tid
self
.
todo
=
todo
self
.
i2c
=
i2c
self
.
translator
=
(
lambda
x
:
not
x
)
if
invert
else
bool
self
.
verbose
=
verbose
# don't do the GPIO setup here, the main init did not yet run
# prepare the pin
self
.
i2c
.
setPin
(
self
.
pin
,
self
.
translator
(
False
))
def
execute
(
self
):
if
self
.
verbose
:
...
...
@@ -24,9 +26,10 @@ class Actor:
for
(
value
,
delay
)
in
self
.
todo
:
if
value
is
not
None
:
logger
.
debug
(
"Actor: Setting pin %d to %d"
%
(
self
.
pin
,
value
))
GPIO
.
output
(
self
.
pin
,
value
)
self
.
i2c
.
setPin
(
self
.
pin
,
self
.
translator
(
value
)
)
if
delay
>
0
:
time
.
sleep
(
delay
)
''' todo list explanation
type:
...
...
@@ -41,30 +44,29 @@ class Actor:
Worker threads do work one thing at a time from their input queue.
=> different commands with same tid will never be executed simultaneously
'''
CMDs
=
{
CMD_UNLOCK
:
CMD
(
"unlock"
,
pin
=
12
,
tid
=
0
,
todo
=
[(
True
,
0.3
),
(
False
,
0.1
)]),
CMD_LOCK
:
CMD
(
"lock"
,
pin
=
16
,
tid
=
0
,
todo
=
[(
True
,
0.3
),
(
False
,
0.1
)]),
CMD_BUZZ
:
CMD
(
"buzz"
,
pin
=
22
,
tid
=
1
,
todo
=
[(
True
,
2.5
),
(
False
,
0.1
)]),
}
def
__init__
(
self
):
def
__init__
(
self
,
i2c
):
self
.
_CMDs
=
{
CMD_UNLOCK
:
CMD
(
"unlock"
,
pin
=
(
0x21
,
3
),
tid
=
0
,
todo
=
[(
True
,
0.3
),
(
False
,
0.1
)],
i2c
=
i2c
,
invert
=
True
),
CMD_LOCK
:
CMD
(
"lock"
,
pin
=
(
0x21
,
2
),
tid
=
0
,
todo
=
[(
True
,
0.3
),
(
False
,
0.1
)],
i2c
=
i2c
,
invert
=
True
),
CMD_BUZZ
:
CMD
(
"buzz"
,
pin
=
(
0x21
,
1
),
tid
=
1
,
todo
=
[(
True
,
2.5
),
(
False
,
0.1
)],
i2c
=
i2c
,
invert
=
False
),
}
# launch threads, all running the "_execute" method
self
.
threads
=
{}
for
cmd
in
Actor
.
CMDs
.
values
():
GPIO
.
setup
(
cmd
.
pin
,
GPIO
.
OUT
)
# initialize all output pins with False
GPIO
.
output
(
cmd
.
pin
,
False
)
for
cmd
in
self
.
_CMDs
.
values
():
# one thread for each tid
if
not
cmd
.
tid
in
self
.
threads
:
self
.
threads
[
cmd
.
tid
]
=
ThreadFunction
(
self
.
_execute
,
"Actor TID %d"
%
cmd
.
tid
)
def
_execute
(
self
,
cmd
):
Actor
.
CMDs
[
cmd
].
execute
()
self
.
_
CMDs
[
cmd
].
execute
()
def
act
(
self
,
cmd
):
# dispatch command to correct thread
self
.
threads
[
Actor
.
CMDs
[
cmd
].
tid
](
cmd
)
self
.
threads
[
self
.
_
CMDs
[
cmd
].
tid
](
cmd
)
def
stop
(
self
):
for
thread
in
self
.
threads
.
values
():
thread
.
stop
()
# kate: space-indent off; tab-width 4; indent-width 4;
\ No newline at end of file
busfahrer.py
0 → 100644
View file @
c6979ec0
from
smbus
import
SMBus
from
threading
import
RLock
class
I2C
:
'''This class manages the I²C bus.
It is thread-safe.
'''
def
__init__
(
self
,
bus
):
self
.
_bus
=
SMBus
(
bus
)
self
.
_bytes
=
{}
# this maps bytes to the last written value
self
.
_active
=
False
# for now, we are in initialization mode
self
.
_lock
=
RLock
()
def
activate
(
self
):
'''This function activates the bus. Only now, stuff is sent to the HW.
Call this after all relevant pings have default values.'''
with
self
.
_lock
:
self
.
_active
=
True
for
addr
,
byte
in
self
.
_bytes
.
items
():
self
.
setPins
(
addr
,
byte
)
def
setPins
(
self
,
addr
,
stateByte
):
'''Set all pins of the given address'''
with
self
.
_lock
:
print
(
"Setting {0:x} to {1:08b}"
.
format
(
addr
,
stateByte
))
if
self
.
_active
:
self
.
_bus
.
write_byte
(
addr
,
stateByte
)
self
.
_bytes
[
addr
]
=
stateByte
def
setPin
(
self
,
pin
,
state
):
'''Set the gien pin: An (address, bit) pair'''
with
self
.
_lock
:
(
addr
,
bit
)
=
pin
assert
0
<=
bit
and
bit
<
8
byte
=
self
.
_bytes
.
get
(
addr
,
0x00
)
# set the bit
if
state
:
byte
|=
(
1
<<
bit
)
else
:
byte
&=
~
(
1
<<
bit
)
# now we know what to set
self
.
setPins
(
addr
,
byte
)
def
getPin
(
self
,
pin
):
with
self
.
_lock
:
if
not
self
.
_active
:
return
None
# read the current value
(
addr
,
bit
)
=
pin
byte
=
self
.
bus
.
read_byte
(
addr
)
# now we need to get the bit
mask
=
(
1
<<
bit
)
return
bool
(
byte
&
mask
)
# kate: space-indent off; tab-width 4; indent-width 4;
\ No newline at end of file
ledactor.py
View file @
c6979ec0
from
libtuer
import
logger
import
RPi.GPIO
as
GPIO
import
time
import
threading
import
signal
class
Blinker
(
threading
.
Thread
):
def
__init__
(
self
,
name
,
pin
,
pattern
=
[(
False
,
60.0
)]):
GPIO
.
setup
(
pin
,
GPIO
.
OUT
)
def
__init__
(
self
,
name
,
pin
,
i2c
,
pattern
=
[(
False
,
60.0
)]):
super
().
__init__
()
self
.
_name
=
name
self
.
_pin
=
pin
self
.
_pattern
=
pattern
self
.
_i2c
=
i2c
# if set, pattern will be reloaded and iteration of the pattern will restart
self
.
_notifyer
=
threading
.
Event
()
# Do you want to terminate this thread? Because this is how you terminate this thread.
...
...
@@ -24,12 +23,12 @@ class Blinker(threading.Thread):
self
.
_notifyer
.
clear
()
# list is copied to avoid concurrent modification exceptions
for
(
state
,
delay
)
in
list
(
self
.
_pattern
):
GPIO
.
output
(
self
.
_pin
,
state
)
self
.
_i2c
.
setPin
(
self
.
_pin
,
not
state
)
self
.
_notifyer
.
wait
(
delay
)
if
self
.
_notifyer
.
is_set
()
or
self
.
_terminate
:
break
# LEDs off in the end
GPIO
.
output
(
self
.
_pin
,
Fals
e
)
self
.
_i2c
.
setPin
(
self
.
_pin
,
Tru
e
)
def
_getTid
(
self
):
return
self
.
_thread
.
ident
...
...
@@ -47,16 +46,19 @@ class Blinker(threading.Thread):
class
LedActor
:
ledPins
=
{
"red"
:
26
,
"green"
:
23
,
"red"
:
(
0x21
,
4
)
,
"green"
:
(
0x21
,
5
)
,
}
def
__init__
(
self
):
def
__init__
(
self
,
i2c
):
# launch threads, all running the "_execute" method
self
.
_threads
=
{}
for
(
name
,
pin
)
in
self
.
ledPins
.
items
():
logger
.
debug
(
"Setting pin %d to output as
\"
%s
\"
LED, initial state: False"
%
(
pin
,
name
))
self
.
_threads
[
name
]
=
Blinker
(
name
,
pin
)
# set LED off initially
i2c
.
setPin
(
pin
,
True
)
# yes, True means off
self
.
_threads
[
name
]
=
Blinker
(
name
,
pin
,
i2c
)
self
.
_threads
[
name
].
start
()
def
setPattern
(
self
,
ledName
,
pattern
):
...
...
@@ -75,4 +77,4 @@ class LedActor:
def
stop
(
self
):
for
blinker
in
self
.
_threads
.
values
():
blinker
.
stop
()
\ No newline at end of file
blinker
.
stop
()
pins.py
View file @
c6979ec0
import
RPi.GPIO
as
GPIO
from
collections
import
namedtuple
from
libtuer
import
ThreadRepeater
,
logger
from
statemachine
import
StateMachine
...
...
@@ -7,27 +6,33 @@ class PinsState():
pass
class
PinWatcher
():
def
__init__
(
self
,
pin
,
histlen
):
GPIO
.
setup
(
pin
,
GPIO
.
IN
)
def
__init__
(
self
,
pin
,
histlen
,
i2c
,
invert
):
assert
histlen
>
1
# otherwise our logic goes nuts...
self
.
pin
=
pin
self
.
i2c
=
i2c
self
.
_histlen
=
histlen
self
.
_translator
=
(
lambda
x
:
not
x
)
if
invert
else
bool
# state change detection
self
.
state
=
None
self
.
_
state
=
None
self
.
_newstate
=
None
# != None iff we are currently seeing a state change
self
.
_newstatelen
=
0
# only valid if newstate != None
# raise the pin so we can read it
i2c
.
setPin
(
pin
,
invert
)
# we need a 1 if the pin is inverted, a 0 otherwise
def
read
(
self
):
curstate
=
True
if
GPIO
.
input
(
self
.
pin
)
else
False
curstate
=
self
.
i2c
.
getPin
(
self
.
pin
)
if
curstate
is
None
:
# the i2c is not yet ready. Nothing to do.
return
False
assert
curstate
in
(
True
,
False
)
if
curstate
!=
self
.
state
:
if
curstate
!=
self
.
_
state
:
# the state is about to change
if
curstate
==
self
.
_newstate
:
# we already saw this new state
self
.
_newstatelen
+=
1
if
self
.
_newstatelen
>=
self
.
_histlen
:
# we saw it often enough to declare it the new state
self
.
state
=
curstate
self
.
_
state
=
curstate
self
.
_newstate
=
None
return
True
else
:
...
...
@@ -39,15 +44,19 @@ class PinWatcher():
self
.
_newstate
=
None
return
False
def
state
(
self
):
return
self
.
_translate
(
self
.
_state
)
class
PinsWatcher
():
def
__init__
(
self
,
state_machine
):
def
__init__
(
self
,
state_machine
,
i2c
):
self
.
_pins
=
{
'bell_ringing'
:
PinWatcher
(
18
,
2
),
'door_closed'
:
PinWatcher
(
8
,
4
),
'door_locked'
:
PinWatcher
(
10
,
4
),
'space_active'
:
PinWatcher
(
24
,
4
),
'bell_ringing'
:
PinWatcher
(
(
0x20
,
2
),
2
,
i2c
,
invert
=
True
),
'door_closed'
:
PinWatcher
(
(
0x20
,
1
),
4
,
i2c
,
invert
=
True
),
'door_locked'
:
PinWatcher
(
(
0x20
,
0
),
4
,
i2c
,
invert
=
True
),
'space_active'
:
PinWatcher
(
(
0x20
,
3
),
4
,
i2c
,
invert
=
True
),
}
self
.
_sm
=
state_machine
self
.
_i2c
=
i2c
# start a thread doing the work
self
.
_t
=
ThreadRepeater
(
self
.
_read
,
0.02
,
name
=
"PinsWatcher"
)
...
...
@@ -58,15 +67,17 @@ class PinsWatcher():
pin
=
self
.
_pins
[
name
]
if
pin
.
read
():
saw_change
=
True
logger
.
debug
(
"Pin %s changed to %d"
%
(
name
,
pin
.
state
))
logger
.
debug
(
"Pin %s changed to %d"
%
(
name
,
pin
.
state
()
))
if
not
saw_change
:
return
None
# create return object
pinsState
=
PinsState
()
for
name
in
self
.
_pins
.
keys
():
setattr
(
pinsState
,
name
,
self
.
_pins
[
name
].
state
)
setattr
(
pinsState
,
name
,
self
.
_pins
[
name
].
state
()
)
# send it to state machine
self
.
_sm
.
callback
(
StateMachine
.
CMD_PINS
,
pinsState
)
def
stop
(
self
):
self
.
_t
.
stop
()
# kate: space-indent off; tab-width 4; indent-width 4;
\ No newline at end of file
pins.txt
0 → 100644
View file @
c6979ec0
Device 0x21 (Output)
Bit0 - NC
Bit1 - Relays (Tueroeffner) (immer auf 0, auf 1 zum summen)
Bit2 - T2 Tuer zu (immer auf 1, impuls auf 0 für signal)
Bit3 - T1 Tuer auf
Bit4 - LED0 (1 ist an)
Bit5 - LED1
Bit6 - LED2
Bit7 - LED3
Device 0x20 (Input)
Bit0 - Tuer Schloss
Bit1 - Tuer Rahmen
Bit2 - In Klingel
Bit3 - In Switch
Bit4 - NC
Bit5 - NC
Bit6 - T1 Feedback
Bit7 - T2 Feedback
testblinker.py
0 → 100644
View file @
c6979ec0
from
busfahrer
import
I2C
import
time
b
=
I2C
(
1
)
b
.
activate
()
i
=
0
while
True
:
b
.
setPin
((
0x21
,
4
),
i
%
2
==
0
)
b
.
setPin
((
0x21
,
5
),
(
i
//
2
)
%
2
==
0
)
b
.
setPin
((
0x21
,
6
),
(
i
//
4
)
%
2
==
0
)
b
.
setPin
((
0x21
,
7
),
(
i
//
8
)
%
2
==
0
)
time
.
sleep
(
0.2
)
i
+=
1
tuerd
View file @
c6979ec0
#!/usr/bin/python3
import
RPi.GPIO
as
GPIO
from
busfahrer
import
I2C
import
statemachine
,
actor
,
ledactor
,
pins
,
tysock
,
waker
,
spaceapi
from
libtuer
import
logger
import
argparse
...
...
@@ -13,6 +13,7 @@ parser.add_argument("-f", "--fallback",
action
=
"store_true"
,
dest
=
"fallback"
,
help
=
"Fallback mode for unfunctional hardware: Depend on less sensor input"
)
args
=
parser
.
parse_args
()
if
args
.
debug
:
import
libtuer
libtuer
.
mailAddress
=
[]
...
...
@@ -25,17 +26,20 @@ else:
# Not let's go!
logger
.
info
(
"Starting up..."
)
# initialize
GPIO
stuff
GPIO
.
setmode
(
GPIO
.
BOARD
)
# initialize
i2c
stuff
i2c
=
I2C
(
1
)
# bring 'em all up
the_actor
=
actor
.
Actor
()
the_led_actor
=
ledactor
.
LedActor
()
the_actor
=
actor
.
Actor
(
i2c
)
the_led_actor
=
ledactor
.
LedActor
(
i2c
)
the_waker
=
waker
.
Waker
()
the_api
=
spaceapi
.
SpaceApi
(
the_waker
)
the_machine
=
statemachine
.
StateMachine
(
the_actor
,
the_led_actor
,
the_waker
,
the_api
,
args
.
fallback
)
the_socket
=
tysock
.
TySocket
(
the_machine
)
the_pins
=
pins
.
PinsWatcher
(
the_machine
)
the_pins
=
pins
.
PinsWatcher
(
the_machine
,
i2c
)
# tell the i2c that all initial values are set
i2c
.
activate
()
# we do the socket accept thing in the main thread
try
:
...
...
@@ -53,6 +57,3 @@ the_machine.stop()
the_api
.
stop
()
the_actor
.
stop
()
the_led_actor
.
stop
()
# shutdown GPIO stuff
GPIO
.
cleanup
()
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment