-
Notifications
You must be signed in to change notification settings - Fork 0
/
KilroyTheRobot.py
324 lines (266 loc) · 9.49 KB
/
KilroyTheRobot.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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#-------------------------------------------------------------------------------
# KillroyTheRobot
#
# Author: Shawn Hymel @ SparkFun Electronics
# Date: April 14, 2014
# License: This code is beerware; if you see me (or any other SparkFun employee)
# at the local, and you've found our code helpful, please buy us a round!
# Distributed as-is; no warranty is given.
#
# Listens for Tweets sent to @KillroyTheRobot and parses the Tweet for commands.
# Special '&' tagged commands in the Tweet will cause Kilroy to perform certain
# actions. The command Tweet must be in the form:
#
# @KilroyTheRobot [commands separated by spaces]
#
# The accepted commands are:
#
# forward - Kilroy moves forward about 15 cm (6 in.)
# back - Kilroy moves backward about 15 cm (6 in.)
# left - Kilroy moves left about 30 deg.
# right - Kilroy moves right about 30 deg.
# pic - Kilroy will count down, snap a picture, and post it to Twitter
#
# For example, to make Kilroy move 30 cm forward, turn right 30 degrees and take
# a picture, send the Tweet:
#
# @KilroyTheRobot forward forward right pic
#
# When posting the picture to Twitter, Kilroy will attempt to tag the requesting
# user.
#
# IMPORTANT: USB needs resetting! Find camera and reset:
#
# $ lsusb
# $ sudo ./usbreset /dev/bus/usb/002/003
#
# To have Kilroy run on boot in Linux, add the following lines to /etc/rc.local (before 'exit 0')
#
#
# # Run Kilroy script
# cd /home/ubuntu/Projects/KilroyTheRobot/
# python KilroyTheRobot.py
#
# Source: http://askubuntu.com/questions/645/how-do-you-reset-a-usb-device-from-the-command-line
#
# TODO:
# - Fix keypress exit
# - Fix USB reset issues
#-------------------------------------------------------------------------------
import os
import sys
import termios
import time
import tty
import pygame
import pygame.camera
#-------------------------------------------------------------------------------
# Drive functions (callbacks from command parser)
#-------------------------------------------------------------------------------
# Drive Kilroy forward
def drive_forward(ds):
ds.drive_forward(DRIVE_TIME['fwd'])
# Drive Kilroy backward
def drive_backward(ds):
ds.drive_backward(DRIVE_TIME['bck'])
# Drive Kilroy left
def drive_left(ds):
ds.drive_left(DRIVE_TIME['lft'])
# Drive Kilroy right
def drive_right(ds):
ds.drive_right(DRIVE_TIME['rgt'])
# Take picture (placeholder)
def take_picture(ds):
return
#-------------------------------------------------------------------------------
# User parameters
#-------------------------------------------------------------------------------
# Debug level
# 0 - Run normally
# 1 - Error and runtime information printed to console
# 2 - Console output, motor drive off
DEBUG = 0
# Automatically shutdown on low battery?
AUTO_SHUTDOWN = True
# Camera USB location
USB_CAM = '/dev/bus/usb/003/004'
# Pin assignments
DIR_PIN = 8
DRIVE_PIN = 5
ADC_PIN = 0
# Battery levels
WARN_LEVEL = 51
SHUTOFF_LEVEL = 49
# Camera file
CAM_FILE = '/dev/video0'
# LED maps file (for eyes)
LEDMAP_FILE = 'ledmaps.txt'
# Alive number file
ALIVE_NUMBER_FILE = 'alive_number.txt'
# Alive number section and variable
ALIVE_NUMBER_SECTION = 'alive_number_section'
ALIVE_NUMBER = 'alive_number'
# ADC file
ADC_FILE = '/proc/adc0'
# Twitter authentication credentials
TWITTER_AUTH = { 'app_key': 'QP9zzvRZWgjDJkGgK8TZ6g',
'app_secret': 'wskPbXryJc1bHbESVmkYrfMHvsCVCty8LiEybvTAw',
'oauth_token': '2366092298-Wg9ZNFm16QvTBO7LXCx3wGKknGgZKCoU1GFnyH7',
'oauth_token_secret': 'wJgAcl4dYHGnDq8RcuUfV8fHzKLWlJ00XNR87Xg94qUXr' }
# The robot's Twitter handle. With an @ sign.
HANDLE = '@KilroyTheRobot'
# Accepted commands along with their appropriate function call
COMMANDS = {'forward':drive_forward,
'back':drive_backward,
'left':drive_left,
'right':drive_right,
'pic':take_picture}
# Drive time (in seconds) for [forward, backward, left, right]
DRIVE_TIME = {'fwd':2, 'bck':2, 'lft':0.5, 'rgt':0.5}
# Tweets
START_TWEET = "I'm Kilroy! Send me a tweet with the commands: forward back left right pic"
END_TWEET = "I'm tired. I think I'll take a nap."
PIC_TWEET = "Domo arigato, "
LOW_BATT_TWEET = "Help me, @ShawnHymel, you're my only hope."
#-------------------------------------------------------------------------------
# Import custom modules
#-------------------------------------------------------------------------------
# Add motor_driver module to path
path = os.path.join(os.path.dirname(__file__), 'py_apps/drive_system')
sys.path.append(path)
# Add tweet_feed module to path
path = os.path.join(os.path.dirname(__file__), 'py_apps/tweet_feed')
sys.path.append(path)
# Add led_driver module to path
path = os.path.join(os.path.dirname(__file__), 'py_apps/led_driver')
sys.path.append(path)
import drive_system
import tweet_feed
import led_driver
import ConfigParser
#-------------------------------------------------------------------------------
# Global Variables
#-------------------------------------------------------------------------------
g_alive_number = None
#-------------------------------------------------------------------------------
# Functions
#-------------------------------------------------------------------------------
def increment_alive_number():
global g_alive_number
# Open text file
config = ConfigParser.RawConfigParser()
config.read(ALIVE_NUMBER_FILE)
g_alive_number = int(config.get(ALIVE_NUMBER_SECTION, ALIVE_NUMBER))
# Increment alive number in param file for next time
config.set(ALIVE_NUMBER_SECTION, ALIVE_NUMBER, str(g_alive_number + 1))
param_file = open(ALIVE_NUMBER_FILE, 'w')
config.write(param_file)
param_file.close()
del config
# Returns the ADC pin value
def get_battery_level(pin):
# Open ADC file and read value
fd = open(ADC_FILE, 'r')
fd.seek(0)
val = fd.read(16)
return val
# Get a key that has been pressed (from Clark on raspberrypi.org/forums)
def get_key():
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = 1
new[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, new)
key = None
try:
key = os.read(fd, 3)
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, old)
return key
#-------------------------------------------------------------------------------
# Main
#-------------------------------------------------------------------------------
# Runs the main Kilroy loop. Waits for incoming Tweets and performs actions.
def run_kilroy():
# Initialize pygame and camera
pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera(CAM_FILE, (640, 480))
# Initialize user
user = ''
# Create an LEDDRiver object and draw dead eyes first
ld = led_driver.LEDDriver(LEDMAP_FILE, DEBUG)
ld.draw_eyes('dead left', 'dead right')
# Create a TweetFeed object
tf = tweet_feed.TweetFeed(TWITTER_AUTH, DEBUG)
# Create a DriveSystem object
ds = drive_system.DriveSystem(DIR_PIN, DRIVE_PIN, DEBUG)
# Get start time
start_time = time.time()
# Increment alive counter
increment_alive_number()
# Send hello tweet
tf.tweet(str(g_alive_number) + ': ' + START_TWEET)
# Start Twitter API streamer to look for Tweets at Kilroy
tf.start_streamer(HANDLE, COMMANDS)
# Main loop
warning_sent = False
ld.draw_eyes('open left', 'open right')
if DEBUG > 0:
print 'Here we go! Waiting for ' + HANDLE
while True:
# Look for keypresses and end game on quit
#if str(get_key()) == 'q':
# break
# Get commands and parse them
cmd_list = tf.get_commands()
for cmd in cmd_list:
if cmd[0] == '@':
user = cmd
elif cmd == 'pic':
ld.draw_eyes('closed left', 'closed right')
#***Cludgey USB restart
os.system('sudo ./usbreset ' + USB_CAM)
if DEBUG > 0:
print 'Taking picture'
pygame.camera.init()
cam = pygame.camera.Camera(CAM_FILE, (640, 480))
cam.start()
img = cam.get_image()
cam.stop()
ld.draw_eyes('open left', 'open right')
pygame.image.save(img, 'image.jpg')
time.sleep(1)
img = open('image.jpg')
tf.tweet_image(PIC_TWEET + user, img)
else:
COMMANDS[cmd](ds)
# Check battery voltage level
lvl = get_battery_level(ADC_PIN)
lvl = int(lvl[5:])
if DEBUG > 0:
#print 'Battery: ' + str(lvl)
pass
if (lvl > SHUTOFF_LEVEL) and (lvl <= WARN_LEVEL) and not warning_sent:
tf.tweet(str(g_alive_number) + ': ' + LOW_BATT_TWEET)
ld.draw_eyes('sleepy left', 'sleepy right')
warning_sent = True
elif (lvl <= SHUTOFF_LEVEL):
break
# Sleep
time.sleep(0.1)
# Send goodbye tweet and shut down
if DEBUG > 0:
print 'I\'m tired. I think I\'ll take a nap.'
tf.tweet(str(g_alive_number) + ': ' + END_TWEET)
tf.stop_streamer()
# If auto-shutdown is enabled, shutdown Linux
if AUTO_SHUTDOWN:
#***TODO: SHUTDOWN LINUX
pass
return
# Run main
if __name__ == "__main__":
run_kilroy()