/
MovementGrapher.py
executable file
·177 lines (139 loc) · 6.61 KB
/
MovementGrapher.py
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Accelerometer Grapher and Fall Dector - Hugh O'Brien March 2009
#
#This is a script for PyS60 that opens a bluetooth serial connection
#to a pre-programmed SHIMMER sensor, The SHIMMER provides accelerometer
#data in the form "1111 1111 1111" where '1111' will be in the range
#of 0 -> 4400. The three values represent the data gathered
#from monitoring the three axis of the accelerometer.
#
#The script reduces the accuracy of these values in order to be able
#to graph them on a screen that is only 320x240px in size
#
#The script also monitors the difference between two subsequent
#readings in order to determine if a large movement has occured.
#This can be interpreted as a fall. A call is then placed to a
#pre-defined telephone number and the details of the victim are
#read out to the receiver.
import e32, appuifw, audio, telephone
#btsocket is the 'old' BT system, new version introduced in
#PyS60 1.9.1 is harder to work with.
import btsocket as socket
#a predefined BT MAC address can be set here to skip discovery process
target = ''
contact_name = "John Watson"
contact_number = "5550137"
victim_name = "Mr. Sherlock Holmes"
victim_address = "221 B. Baker Street. London"
sensitivity = 28
def fall():
global app_lock, contact_name, contact_number, victim_name,\
victim_address, data, prev
audio.say("Dialling %s now" % contact_name)
telephone.dial(contact_number)
e32.ao_sleep(7) #7 sec delay for someone to answer
for i in range(2, -1, -1):
audio.say("This is an automated message. A fall has been detected.\
Please assist %s at address %s. \
This message will repeat %d more times" \
% (victim_name, victim_address, i) )
telephone.hang_up()
data = ( 40, 40, 40 ) #reset values so as not to trigger again
prev = data
app_lock.signal() #unlock the main loop
def connect(): #this function sets up the BT socket connection
global btsocket, target
try:
#socket params passed to the OS
btsocket=socket.socket(socket.AF_BT,socket.SOCK_STREAM)
if target == '': #if no target defined, begin OS discovery routine
address,services = socket.bt_discover()
target = (address, services.values()[0])
btsocket.connect(target) #initiate connection and notify user
appuifw.note(u"Connected to " + str(address), "info")
except: #fail cleanly
appuifw.note(u"Error connecting to device")
btsocket.close()
def getData(): #this receives single characters over the bitstream
#until it encounters a newline and carraige return it then
#returns the characters it has buffered until that point
global btsocket #use the globally defined socket
buffer = "" #create an empty buffer
rxChar = btsocket.recv(1) #receive 1 char over BT and save in rxChar
#spin here until we get a 'real' char
while (rxChar == '\n') or (rxChar == '\r'):
rxChar = btsocket.recv(1)
#as long as we receive 'real' chars buffer them
while (rxChar != '\n') and (rxChar != '\r'):
buffer += rxChar
rxChar = btsocket.recv(1)
return buffer #return the buffer contents
def graph_data(input):
#this function produces the graphs on the screen. the screen is
#landscape oriented with a resolution of 240x320. The constants seen
#here are used to define where on the screen the graphs should be drawn
global count, canvas, prev, data
#take the input string formated like "1111 1111 1111" and parse it
#to acquire 3 sets of chars and then interpret them as digits saving
#them to a list in this format: ( '1111', '1111', '1111' )
#the values are then divided by 60 as they will be in the range
#0 -> x -> 4400 as the screen is only 240px high. furthermore as there
#are three graphs being drawn each is confined to (240 / 3 )px of
#height. The divisor of 60 accommodates this at the cost of accuracy.
try:
data = (\
int(input[0:4]) / 60, \
int(input[5:9]) / 60, \
int(input[10:14]) / 60\
)
#sane defaults if we receive a malformed reading
except ValueError:
data = ( 36, 36, 36 )
#redraw the screen if there are more than 280 samples displayed.
if count > 280:
reset()
#draw a line, with the X1 starting 10 points from the left and
#expanding right, Y1 being the previous value of Y2 (initially zero)
#plus a vertical offset so the graphs don't overlap each other, X2
#being one point right of X1 and Y2 one of the 3 XYZ readings plus
#the vertical offset. other options are purely aesthetic.
canvas.line(\
(count + 10, prev[0], count + 11, data[0] ), \
outline = 0xFF0000, width = 1)
canvas.line(\
(count + 10, prev[1] + 80, count + 11, data[1] + 80), \
outline = 0x00DD00, width = 1)
canvas.line(\
(count + 10, prev[2] + 160, count + 11, data[2] + 160), \
outline = 0x4444FF, width = 1)
#increment counter - data should also be pushed into prev here
#but this happens in the main loop for monitoring reasons
count = count + 1
def reset(): # this function redraws the screen when it becomes full
global count, canvas
#reset the count and redraw a blank canvas
count = 0
canvas.rectangle((0, 0, 320, 240), fill = 0x000000)
#Main
data = ( 0, 0, 0 )
prev = (40, 40, 40) #initial zero values for 'previous values' of the data
canvas = appuifw.Canvas() #create a new Canvas object
appuifw.app.body = canvas
appuifw.app.screen = "full" #go 'fullscreen'
appuifw.app.orientation = "landscape" # draw in landscape orientation
appuifw.app.title = u"Activity Monitor" #name the program
app_lock = e32.Ao_lock() #locking system
connect() #open the BT socket
e32.ao_sleep(1) # sleep for 1 second in case of graphical slowness
reset() # initially reset the screen to draw the canvas
while 1: #loop the following code infinitely
e32.reset_inactivity() #keep the screensaver away
graph_data( getData() ) # poll the BT data passing it to the grapher.
#test the movement level between the last two samples
if ( (abs(data[0] - prev[0]) > sensitivity ) \
or (abs(data[1] - prev[1]) > sensitivity ) \
or (abs(data[2] - prev[2]) > sensitivity ) ):
fall() #if too much, take action
app_lock.wait() #pause this loop until fall() finishes
e32.ao_sleep(1)
reset()
prev = data #move current data into previous data buffer