-
Notifications
You must be signed in to change notification settings - Fork 73
bitty.py
The python program known as bitty.py is a serial port proxy. It runs on an Arduino-connected PC and makes the Arduino available over the network for connection via telnet, nc, or your favorite telnet client.
With Bitty you can make your Arduino available on the Internet with no extra hardware.
There are some nice features for Arduino users. You can unplug one Arduino and plug in another and bitty will find it. And the network port and baud rate are adjustable.
- python 2.4 or 2.5
- pySerial from SourceForge
To use: first, start the serial-to-net proxy and leave it running:
$ python bitty.py [options]
...and then, in another terminal window, connect to it with your favorite telnet or nc program:
$ nc localhost 8080
$ telnet localhost 8080
$ telnet arduino.bitlash.net 8080
Plug in Arduino via the USB-to-serial cable and you should connect up automatically.
Note: Type 'logout' when done for a clean disconnect.
Run Bitty with the "--help" option to see a list of options:
Arduino.app/.../library/bitlash/extras bill$ python bitty.py --help
Usage: bitty.py [options]
Options:
-h, --help show this help message and exit
-p PORT, --port=PORT network connection port [8080]
-u USBDEVICE, --usbdevice=USBDEVICE
name of USB serial port device for serial connection
[/dev/tty.usbserial*]
-b BAUD, --baud=BAUD baud rate for port specified by -u [57600]
-k, --keyboard-passthru
-d, --debug
Bitty normally listens for incoming connections on port 8080. If you want to use a different port, specify it like this:
$ python bitty.py -p 1234
To specify a particular usb device you can identify it like this:
$ python bitty.py -u TTYUSB0
Bitlash, by default, listens at 57600, so Bitty tries to connect at that rate. You can use a different rate by specifying it like this:
$ python bitty.py -b 115200
This enables local keyboard input from the console running Bitty (in addition to the normal input from a remote network-connected user). You can use this option to turn Bitty into a terminal emulator. You can also use it for a "two steering wheel" setup: the remote user can type, and so can you.
$ python bitty.py -k
#! /usr/bin/python
#
# bitty.py: serial-to-network multiplexer for Arduino
#
# This is a serial port proxy. It makes a usbserial port available over the network
# for connection via telnet, nc, or your favorite telnet client.
#
# There are some nice features for Arduino users. You can unplug one Arduino and
# plug in another and bitty will find it. And the network port and baud rate are
# adjustable.
#
# Requires:
# python 2.4 or 2.5
# pyserial from http://pyserial.wiki.sourceforge.net/pySerial
# tested with pyserial 2.4 on OS X and Fedora 4
#
# To use: first, start the serial-to-net proxy and leave it running:
# $ python bitty.py [options]
#
# and then, in another terminal window, connect to it with your favorite telnet or nc program:
# $ nc localhost 8080
# $ telnet localhost 8080
# $ telnet arduino.bitlash.net 8080
#
# plug in Arduino and you should connect up automatically.
# Type 'logout' when done for a clean disconnect.
#
#
# LICENSE
#
# Copyright 2010 by Bill Roy
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################
__version__ = '1.0'
__copyright__ = 'Copyright 2010 by Bill Roy'
import serial, time, socket, commands, traceback, os, sys
# serial port config
usbdevice = None
baud = 57600
# network config
port = 8080
# options
kbdpassthru = False
showflow = False
# change below here at your own risk
serialport = None
netsocket = None
netclient = None
clientaddress = None
# Net pump thread, and queued interface
import Queue
netq = Queue.Queue()
serialq = Queue.Queue()
def closeSerialPort():
global serialport
if serialport and serialport.isOpen():
print "Closing: ", serialport.portstr
try:
if netclient:
netclient.send("Closing ")
netclient.send(serialport.portstr)
netclient.send("\r\n");
except: pass
serialport.close()
def openSerialPort():
global serialport
closeSerialPort()
# serial port autoconfig
device = usbdevice # command line overrides auto select
if not device:
devicelist = commands.getoutput("ls /dev/tty.usbserial*")
#devicelist = commands.getoutput("ls /dev/ttyUSB*") # this works on the XO/Fedora
if devicelist[0] == '/': device = devicelist
if not device:
print "Waiting for device..."
return False
print "Connecting to", device, baud, "..."
if netclient:
netclient.send('Connecting to ');
netclient.send(device);
netclient.send('... ');
try:
# two stop bits helps paste-to-terminal not lose characters
serialport = serial.Serial(device, baud, timeout=0, stopbits=serial.STOPBITS_TWO)
print 'Opened port: ', serialport.portstr
if netclient:
netclient.send("connected.\r\n")
except:
print 'Failed to open port'
if netclient:
netclient.send("failed.\r\n")
return False
return True
# thread to read and queue serial input
# assumes opening the serial port is handled elsewhere
def serialPumpTask(usbdevice, baud):
while True:
if serialport and serialport.isOpen():
try:
data = serialport.read(1024);
if data:
if showflow: print "SER:",data
serialq.put(data)
else:
time.sleep(0.1)
except:
print "Exception reading serial port"
traceback.print_exc()
closeSerialPort()
else:
time.sleep(1.0)
def kbdPumpTask(d1,d2):
while True:
try:
netq.put(os.read(sys.stdin.fileno(), 1))
except:
print "Exception in keyboard handler"
traceback.print_exc()
# send network data to the serial port
def handleNetworkInput(data):
global serialport
try:
if serialport and serialport.isOpen():
#serialport.write(data);
for i in range(len(data)):
serialport.write(data[i])
time.sleep(0.05)
except:
print "Exception writing serial port"
traceback.print_exc()
closeSerialPort()
# send serial port data to the network socket
def handleSerialInput(data):
global netclient
try:
if netclient: netclient.send(data)
if kbdpassthru:
sys.stdout.write(data)
sys.stdout.flush()
except:
print "Exception writing network port"
traceback.print_exc()
closeSerialPort()
# thread to read and queue network input
def netPumpTask(port, dummy):
global netsocket, netclient, clientaddress
netsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
netsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#socket.setblocking(0)
netsocket.bind(('', port))
netsocket.listen(1) # allow no waiting connections
while True:
try:
print "Listening for network connection on", socket.getfqdn(), ':', port, '/', socket.gethostname()
netclient, clientaddress = netsocket.accept()
print "Connection from", clientaddress
netclient.send("g'day from bitty ")
netclient.send(__version__)
netclient.send(" -- type 'logout' to disconnect")
netclient.send("\r\n");
while True:
if not netclient: break # client went away: go get another one
while not openSerialPort():
netclient.send("Waiting for device...\r\n");
time.sleep(2);
while serialport.isOpen():
data = netclient.recv(10240)
if data:
if showflow: print "NET:", data
if data[0:6].find('logout') == 0:
if netclient: netclient.send("Disconnected.\r\n");
netclient.close()
netclient = None
closeSerialPort()
time.sleep(1.0) # pause to allow disconnect
else:
netq.put(data)
else: time.sleep(0.1)
except:
print "Exception in net pump"
traceback.print_exc()
closeSerialPort()
def parseOptions():
# parse command line options
from optparse import OptionParser
usage = "usage: %prog [options]"
parser = OptionParser()
parser.add_option("-p", "--port",
dest="port", type='int',
help="network connection port [8080]")
parser.add_option("-u", "--usbdevice",
dest="usbdevice",
help="name of USB serial port device for serial connection [/dev/tty.usbserial*]")
parser.add_option("-b", "--baud",
dest="baud", type='int',
help="baud rate for port specified by -u [57600]")
parser.add_option("-k", "--keyboard-passthru", action="store_true", dest="kbdpassthru")
parser.add_option("-d", "--debug", action="store_true", dest="debug")
(options, args) = parser.parse_args()
if options.port:
global port
port = options.port
print "Listening for connections on port:", port
if options.baud:
global baud
baud = options.baud
print "Serial port baud rate set to:", baud
if options.usbdevice:
global usbdevice
usbdevice = options.usbdevice
print "Using USB serial device: ", usbdevice
if options.debug:
global showflow
showflow = True
if options.kbdpassthru:
global kbdpassthru
kbdpassthru = True
#if options.password:
# import getpass
# password = getpass.getpass("Password:")
if __name__ == '__main__':
parseOptions()
import thread
net_pump_thread = thread.start_new_thread(netPumpTask, (port, 0))
serial_pump_thread = thread.start_new_thread(serialPumpTask, (usbdevice, baud))
if kbdpassthru:
kbd_pump_thread = thread.start_new_thread(kbdPumpTask, (0,0))
while True:
while not netq.empty():
handleNetworkInput(netq.get())
netq.task_done()
while not serialq.empty():
handleSerialInput(serialq.get())
serialq.task_done()
time.sleep(0.1)
Bitlash and this Bitlash documentation are Copyright 2012 by Bill Roy.