Dct2000serial
Note: The correct title of this article is dct200serial. It appears incorrectly here due to technical restrictions.
| Author | Ian Forde |
| Description | Serial control of cable boxes in MythTV |
| Supports |
Serial control of cable boxes in MythTV
Or... "Want to get serial control working on your DCT-2000 cable box?"
Contents |
Version History
See changechannel.py
Credits
README Written by Ian Forde ( ian a duckland d org)
Mostly using code from Chris Griffiths ( dct2000 a y-fronts d com ) to do the actual work
Additional cable box functions written by Lonny Selinger
Inspired by Embeem's Tivo serial code
And various snippets of code from around the net. ;)
Contents
changechannel.py crcgen.py
Prerequisites
- You have to have a Motorola/GI DCT-2000 series box. DCT-2244, DCT-2224, and so on...
- You have to have firmware version 7.54 or higher on the box. It's somewhere off of the main menu when using the cable remote.
- Your serial port has to be enabled.
- Python2.2 on your myth backend
Note that if you meet prerequisite #1, but meet only one of 2 and 3, then you'll need to have your cable provider either a) Upgrade your firmware/enable your serial port or b) Swap out your cable box. Be creative in how you approach them. And you *don't* have to tell them why you want this... Especially the serial port bit...
Actually, regarding the serial port bit, I believe they might enable them if you tell them that you have or are planning to get a Tivo (note that I'm not
- endorsing* that approach...)
In any case, once you meet the prereqs, you can continue.
Installation
Okay - Basically, place the two scripts (crcgen.py, changechannel.py) into /usr/local/bin, make sure they are executable, then you're done.
cp crcgen.py changechannel.py /usr/local/bin cd /usr/local/bin chown 755 crcgen.py changechannel.py # Edit the SERIALPORT variable in changechannel.py to reflect which serial port you'll be using.
If you're using MythTV, you can point it to the external change channel script provided. (Here's a hint - it's the one called changechannel.py)
That's it!
The Code
#!/usr/bin/env python # Chris Griffiths, June 8th 2003 (dct2000@y-fronts.com) # # CRC calculator preset for CRC-CCIT suitable for a Motorola DCT2000 # Please see http://rcswww.urz.tu-dresden.de/~sr21/crctester.c # and http://rcswww.urz.tu-dresden.de/~sr21/crc.html for the # original c code to this, a web based crc generator, and # a description of what these settings do order = 16; polynom = 0x1021; direct = 1; crcinit = 0x0000; crcxor = 0x0000; refin = 1; refout = 1; def reflect (crc, bitnum): i = 1 << (bitnum-1) j = 1 crcout=0 while i: if (crc & i): crcout = crcout | j j = j << 1 i = i >> 1 return crcout def calcrc(data_bytes): crcmask = (((1<<(order-1))-1)<<1)|1; crchighbit = 1<<(order-1); # compute missing initial CRC value if direct: crcinit_direct = crcinit; crc = crcinit; for i in range (order): bit = crc & 1; if bit: crc = crc ^ polynom; crc >>= 1; if bit: crc = crc | crchighbit; crcinit_nondirect = crc; else: crcinit_nondirect = crcinit crc = crcinit for i in range (order): bit = crc & crchighbit crc = crc << 1; if bit: crc= crc ^ polynom; crc = crc & crcmask; crcinit_direct = crc; # now compute the crc crc = crcinit_direct for i in range( len(data_bytes) ): c = ord(data_bytes[i]) if refin: c = reflect(c, 8) j=0x80; while j: bit = crc & crchighbit crc = crc << 1 if (c & j): bit = bit ^ crchighbit if bit: crc = crc ^ polynom j = j >> 1 if refout: crc=reflect(crc, order) crc = crc ^ crcxor crc = crc & crcmask return crc
#!/usr/bin/env python
#
# Chris Griffiths, June 8th 2003 (dct2000@y-fronts.com)
#
# Simple program to send remote control sequences via serial port to a
# motorola dct2000
# It does not respect sequence numbers, nor check the returned codes for
# success or failure
#
# Modified by Ian Forde (ian@duckland.org) (Sometime in early 2003 - I don't
# remember when...)
#
# Modified by Lonny Selinger to support additional dct2000 functions
#
# Version history
# 0.87: Added an EXIT_KEY code to make the dct2000 osd go away
# as quickly as possible, since I don't know how to disable
# it yet!
# 0.86: Removed the debugging flag. That's my responsibility,
# not everyone else's!
# 0.85: Added a debugging flag
# Cleaned up the channel code determination by removing
# a whole bunch of 'if' statements and the corresponding
# variable names.
# Eliminated the need for a separate shell script by
# including channel number length normalizing into the
# script.
# Turned off flow control
# Created a variable for setting the serial port
# Fixed a bug in the alternate button parsing
# 0.81: Experimental code added by Lonny Selinger for additional
# functions such as last_channel, mute, favorite, chup,
# chdown. Not integrated into Myth yet!
# 0.8: it works!
import serial # pyserial (http://pyserial.sourceforge.net/)
import crcgen # CRC generator - preset to generate CCITT polynomial crc's
import time # std python lib
import sys
# define the serial port
SERIALPORT='/dev/ttyS1'
# define button codes:
ONOFF='\x0A'
CHAN_UP='\x0B'
CHAN_DOWN='\x0C'
VOLUME_UP='\x0D'
VOLUME_DOWN='\x0E'
MUTE='\x0F'
MUSIC='\x10'
SELECT_OK='\x11'
ENTER='\x11'
EXIT_KEY='\x12'
LAST_CHANNEL='\x13'
SWITCH_AB='\x14'
FAVORITE='\x15'
PPV_KEY='\x16'
A_KEY='\x17'
P_F='\x18'
MENU='\x19'
OUTPUT_CHAN='\x1C'
FAST_FORWARD='\x1D'
REVERSE='\x1E'
PAUSE='\x1F'
BROWSE='\x22'
CANCEL='\x23'
LOCK='\x24'
LIST='\x25'
THEME='\x26'
GUIDE='\x30'
RECORD='\x31'
HELP='\x32'
INFO='\x33'
UP='\x34'
DOWN='\x35'
LEFT='\x36'
RIGHT='\x37'
DAY_PLUS='\x38'
DAY_MINUS='\x39'
PAGE_PLUS='\x3A'
PAGE_MINUS='\x3B'
B_KEY='\x27'
C_KEY='\x28'
# This function below was blatantly lifted from:
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52660
def IsAllDigits( str ):
""" Is the given string composed entirely of digits? """
return all(letter.isdigit() for letter in str)
def debug(ctrl_str):
for hexChar in ctrl_str:
print "%#x" % ord(hexChar),
print '\n'
def word2str(val):
return chr((val & 0xFF00) / 256) + chr(val & 0x00FF)
def get_response(ser):
#response = ''
#byte = ser.read()
#while byte:
# response = response + byte
# byte = ser.read()
byte = ser.read()
response = byte
byte = ser.read()
while byte:
response += byte
byte = ser.read(7)
#print "length of response is ",len(response)
return response
def calc_code(payload=""):
#
# code is 10 78 ll ll ss 40 pp pp cc 10 03
# where ss = seq num, pp pp = payload (for a remote keypress first byte is 22), cc = crc
# It seems the sequence number can be ignored - so long as we send this double sequence before it -
# this is what embleem was using with the last sequence being the on/off keypress
#
# It also looks like we can only send one keypress at a time - although correct use of sequence number
# be get that working
#
code = '\x78' + word2str(len(payload)+2) + '\x10\x40' + payload
crc = crcgen.calcrc(code)
code = "\x10\x70\x00\x02\x03\x40\xc8\x27\x10\x03\x10\x78\x00\x03\x03\x40\x00\x68\x96\x10\x03" + '\x10' + code + word2str(crc) + '\x10\x03'
return code
def send_to_unit(ser, key_presses):
#
# Send each keypress, then get the returned status from the box (and subsequently ignore it)
# You have to read the return code, otherwise the next send with fail
#
for key in key_presses:
ser.write(calc_code('\x22' + key))
get_response(ser)
cmdarg = sys.argv[1]
# added an extra code block to handle a few more options that
# can be passed to remote.py.
# options: last_channel(), mute(), favorite(), chan_up(),
# chan_down() .... for now anyway
#
# Lonny Selinger
if not IsAllDigits(cmdarg):
if cmdarg in ("l", "m", "f", "u", "d"):
BUTNKEY=''
if cmdarg == "l" :
BUTNKEY=LAST_CHANNEL
elif cmdarg == "m" :
BUTNKEY=MUTE
elif cmdarg == "f" :
BUTNKEY=FAVORITE
elif cmdarg == "u" :
BUTNKEY=CHAN_UP
elif cmdarg == "d" :
BUTNKEY=CHAN_DOWN
serCon = serial.Serial(SERIALPORT,baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1, xonxoff=0, rtscts=0,)
send_to_unit(serCon, BUTNKEY)
else:
if len(cmdarg) > 3:
print "too many chars!"
else:
chanset=cmdarg.rjust(3, '0')
CHANCODE=''.join(chr(int(c)) for c in chanset)
serCon = serial.Serial(SERIALPORT,baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1, xonxoff=0, rtscts=0,)
send_to_unit(serCon, CHANCODE)
send_to_unit(serCon, EXIT_KEY)