November 24, 2007
Reading for November 27th, are now posted. Enjoy!
October 2, 2007
To upload your thoughtless acts, create a new assignment page like any other lab. You'll see "Thoughtless Acts" listed as one of the assignment options.
May 24, 2008
This site has been archived and is no longer editable. Stay tuned for the next version, coming in the fall!
Explore motion as an output (in a form of display or tactile feedback).
Use your DC motor to create vibration or rotational motion (e.g., you
have seen Ambient Media that use pinwheels, dancing wires, etc as
display or notification). You can also combine the motion with other
output (e.g., sound, lights, etc.). Be creative!
Description
I wanted to create something simple, that I could interface with an outside programming library like Python. The simple part didn't work out, and it actually took me some time to implement my idea. What I ended up with was a livewire implementation for IRC chat. This involved running a supybot (Python based IRC bot), and a small script in python that listens to the channel logs, looking for my screenname "n8agrin". Anytime someone in the channel messages me, Arduino fires up the motor which vibrates a string. Not the most creative of interfaces, but it was frustrating getting the technologies to mash together. Now I know when someone sends me a message in my chatrooms!
Components Used
2 AA batteries
1 electric motor
1 diode
1 transistor
Various lengths of wire
1 breadboard
1 arduino board
Code
Arduino code
/* * Listen to a serial port and if a 1 character comes in, * turn on a motor for a little while. * author: n8agrin @ n8agrin.com */
int motorPin = 9; // select the pin for the Motor int incoming = 0; // set the default value for the incoming message
void setup() { Serial.begin(9600); } void loop() { incoming = Serial.read(); // read in from the Serial port if (incoming == 49) { // look for the character '1', which maps to 49 analogWrite(motorPin, 120); // analogWrite can be between 0-255 delay(1000); // delay for a bit analogWrite(motorPin, 0); // turn off the motor }
Python code
from Tailer import * import serial, re
# define some 'constants' ARDUINO_PORT = "/dev/tty.usbserial-A4001nvG" IRC_LOGFILE = "/opt/local/supybot/logs/ChannelLogger/freenode/#n8/#n8.log"
# setup a serial connection to talk to Arduino arduino = serial.Serial(ARDUINO_PORT, 9600)
# create a regex object to match my username findn8 = re.compile(r'n8agrin:')
def livewire(filename, s): if findn8.search(s): print "Message received, printing to ardunio." arduino.write('1')
# create a new Tailer object to tail the IRC bot's log file t = Tailer(IRC_LOGFILE) # Tailer takes a list of files
# call the livewire method to inform the user of an incoming message t.pollloop(livewire)
Python Tailer library
#!/usr/bin/env python __doc__ = """ tail -f for multiple files written natively in Python
Polls files for appended text. Can call a callback function with the changes if you'd like, or you can control when the pollings occur.
Example: # a callback function def printer(filename, s): if s: print "%s:\t%s" % (filename, s) t = Tailer(file1, file2) # Tailer takes a list of files t.pollloop(printer)
See pydoc for the other tailing modes (modes are pollloop, poll, multipoll) by doing 'pydoc Tailer' in this directory.
Known bugs: It doesn't handle the case when a file is alterred in the middle very well.
Please feel free to contact me at dmcc AT bigasterisk DOT com with questions, feature requests, patches, whatever. """
class TailedFile: """An object representing an object being tailed and it's current state""" def __init__(self, filename, initial=None): """Filename is the name of the file to watch. initial is the size where where should start watching from. Omitting initial will result in watching changes from the end of the file.""" self.file = open(filename, 'r') self.filename = filename self.size = os.path.getsize(filename) if initial is not None: self.offset = initial else: self.offset = self.size def __len__(self): 'Returns the size of the file' return self.size def __str__(self): 'Returns the filename of the file' return self.filename def poll(self): """Returns a string of the new text in the file if there is any. If there isn't, it returns None. If the file shrinks (for whatever reason), it will start watching from the new end of the file.""" self.size = os.path.getsize(self.filename) if self.size > self.offset: self.file.seek(self.offset) s = self.file.read() self.offset = self.size return s # if file shrinks, we will adjust to the new size if self.size < self.offset: self.offset = self.size return None
class TailInterface: """An interface for file watching.""" def __init__(self, *files, **kw): """Files is a list of files to watch. Keyword arguments include: interval (for the interval at which the file should be poll()ed for pollloop() and initial for the initial end of the file. Setting initial to zero will make it read the entire file on the first poll() and any subsequent additions for the poll()s after that.""" def poll(self): """If any files have grown, return a tuple containing the filename and new text. If no files have changed, we return None""" def pollloop(self, callback): """Continously watches the files for changes, sleeping for 'interval' amount of seconds in between (see __init__). If there are any changes, it will call the callback with two arguments: the filename and new text."""
class Tailer(TailInterface): """An object that watches one or more files for additions. You can either call it whenever you want with poll(), or use pollloop() to call it regularly.""" def __init__(self, *files, **kw): """Given a list of files and some keyword arguments, constructs an object which will watch the files for additions. The optional keyword 'interval' affects the frequency at which pollloop() will check the files. Keywords arguments will be passed along to the TailedFiles.""" self.interval = kw.get('interval', 0.1) self.files = [TailedFile(f, **kw) for f in files] def poll(self): """If any files have grown, return a tuple containing the filename and new text. If no files have changed, we return None. Note that this function is a generator so that it will (try to) give each file equal treatment in polling.""" while 1: for f in self.files: s = f.poll() if s: yield (f, s) else: yield None def multipoll(self): """Returns a list of changes in files. It returns a list of (filename, newtext) tuples. If there are no changes, it will return the empty list.""" changes = [] for f in self.files: s = f.poll() if s: changes.append((f, s)) return changes def pollloop(self, callback): """Continously watches the files for changes, sleeping for 'interval' amount of seconds in between (see __init__). If there are any changes, it will call the callback with two arguments: the filename and new text.""" while 1: changes = self.multipoll() for change in changes: callback(*change) time.sleep(self.interval)
if __name__ == '__main__': print "Tailing %s:" % (', '.join(sys.argv[1:])) def printer(filename, s): if s: print "%s:\t%s" % (filename, s) t = Tailer(*sys.argv[1:]) t.pollloop(printer)
Good job! Getting different technologies to talk to each other can be a pain -- serial communication is versatile but not always the most user friendly. So, nice work getting everything working together! It sounds like you've created something that's actually useful to you, too. It reminds me of the "Dangling String" project that used a dancing string to indicate network activity.
When you have a chance, please post a photo of your project!
Comments
Good job! Getting different
Good job! Getting different technologies to talk to each other can be a pain -- serial communication is versatile but not always the most user friendly. So, nice work getting everything working together! It sounds like you've created something that's actually useful to you, too. It reminds me of the "Dangling String" project that used a dancing string to indicate network activity.
When you have a chance, please post a photo of your project!