diff options
Diffstat (limited to 'pyLogoCompiler/Communication.py')
-rw-r--r-- | pyLogoCompiler/Communication.py | 649 |
1 files changed, 649 insertions, 0 deletions
diff --git a/pyLogoCompiler/Communication.py b/pyLogoCompiler/Communication.py new file mode 100644 index 0000000..469b9ec --- /dev/null +++ b/pyLogoCompiler/Communication.py @@ -0,0 +1,649 @@ +# -*- coding: utf-8 -*- +#----------------------------BRGOGO---------------------------------------- +#Project site: http://br-gogo.sourceforge.net +# +# Name: pyLogo +# Origiginal from: Marcelo Barbosa +# About: Esta é uma versão da linguagem Logo em python para a placa Gogo board +# (This is a version of Logo in python for the card board Gogo) +# +# Modified by: Felipe Augusto Silva +# email: suportegogo@gmail.com +# compiler version: 0.23 +#obs.: version to work with pyGogoMonitor +# +# Modified by: Derek O'Connell +# email: doconnel@gmail.com +# +#----------------------------------------------------------------------------- + +import sys +#from time import sleep +import os +import serial +import glob + +from gettext import gettext as _ + +from pyLogoCompiler import Exceptions +from pyLogoCompiler import pyYacc + + + +# ============================================================ + +MODULE_DEBUG = False + +serialPort = None + + +# ============================================================ + +def printFunctionName(caller=""): + if caller <> "": + print ">>>", caller + print ">>> " + sys._getframe(1).f_code.co_name + "()" + +def callerName(): + return sys._getframe(2).f_code.co_name + "()" + + + +#================================================================================ +#================================================================================ +class SerialIF: + + # The coms port is set up when ComMgr is instantiated + def __init__(self, p=None, debug=False): + global serialPort + + serialPort = serial.Serial(p, 9600, bytesize = serial.EIGHTBITS, \ + parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, \ + timeout=0.1, xonxoff=0, rtscts=0 ) + self.debug = debug + + def rxBytes(self, size=0): + s = serialPort.read(size) + r = [0] * len(s) + for i in range(len(s)): + r[i] = ord(s[i]) + return tuple(r) + + def rxOneByte(self): + x = self.rxBytes(1) + if len(x)>0: + return x[0] + return False + + def rx16BitValueLSBFirst(self): + lowByte = serialPortIF.rxOneByte() & 0xff + highByte = serialPortIF.rxOneByte() #& 0x3 + return (highByte << 8) + lowByte + + def txByteRxEcho(self, byte): + serialPort.write(chr(byte)) + self.rxOneByte() # Rx hw echo byte + # Should check same byte returned here + return True + + + def txBytesNoEcho(self, bytes): + s="" + for i in bytes: + s=s+chr(i) + serialPort.write(s) + + + def txWordMSBFirst(self, word): + self.txByteRxEcho(word / 256) + self.txByteRxEcho(word % 256) + return True + + + def flush(self): + self.rxBytes(10) # Flush does not work properly + serialPort.flushInput() + serialPort.flushOutput() + + + def openPort(self): + try: + print "opening port..." + serialPort.open() + print "port opened" + return True + except serial.serialutil.SerialException: + print "serial.serialutil.SerialException" + return False + + def closePort(self): + if serialPort.isOpen(): + serialPort.read(100) # empty buffer serial + serialPort.close() + return True + + def checkConnection(self): + printFunctionName() + if not serialPort.isOpen(): + return False + self.flush() + serialPort.write(chr(135)) + i = serialPort.read(1); + if i == -1: + print "< Cannot find serial interface" + return False + serialPort.write(chr(0)) + j = serialPort.read(1); + k = serialPort.read(1) + if j == '' or k == '': + return False + if ord(i) == 135 and ord(j) == 0 and ord(k) == 55: + self.flush() + return True + return False + + def getPort(self): + return serialPort.port + + def scanUnix(self): + """scan for available ports (serial and USB). return a list""" + return glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*') + glob.glob('/dev/ttyS*') + + def scanNT(self): + """scan for available ports. return a list of tuples (num, name)""" + available = [] + for i in range(256): + try: + s = serial.Serial(i) + available.append(i) + s.close() + except serial.SerialException: + pass + return available + + + def autoConnect(self): + printFunctionName() + if os.name == 'nt': + available = self.scanNT() + else: + available = self.scanUnix() + available.remove('/dev/ttyS0') # Internal to XO + print "< Ports found:", available + for i in available: + print "< Trying port: ",i + serialPort.port = i + if self.openPort(): + if self.checkConnection(): + print "< Connected on port:", i + return True + else: + print "< Failed to connect to: ", i + + return False + + def isUSBVersion(self): + return str(self.getPort()).find("ACM") != -1 + + + +#================================================================================ +#================================================================================ +class CricketIF(): + + # Memory map: + ADDR_CODE_START = 0 # Code memory start (1280 bytes) + ADDR_RECDATA_START = 0x0500 # Recorded data start (2500 bytes) + ADDR_RECDATA_END = 0x0EC3 # Recorded data end + ADDR_CODE_END = 0x0FEF # Code memory end (4080 bytes. Note overlap!) + # Button Code Vector Addresses: Address of pointer to code to run when button pressed + ADDR_BUTTON1_VTR = 0x0FF0 # Address of pointer to code to run for button 1 + #ADDR_BUTTON2_VTR = 0x0FF2 # Address of pointer to code to run for button 2 + #ADDR_CRICKET_NAME = 0x0FF4 # 11 bytes (to 0xFFE) + #ADDR_AUTOSTART_FLAG = 0x0FFF # 1 byte + + # WIP: Preparation for preserving area of memory used for data recording + # (- check exactly what compiler spits out, ie, range of function addresses) + # (- using own variables for now...) + ADDR_CODE1_START = ADDR_CODE_START + ADDR_CODE1_SIZE = ADDR_RECDATA_START - ADDR_CODE1_START + ADDR_DATA_START = ADDR_RECDATA_START + ADDR_DATA_SIZE = ADDR_RECDATA_END - ADDR_RECDATA_START + ADDR_CODE2_START = ADDR_RECDATA_END + ADDR_CODE2_SIZE = ADDR_CODE_END - ADDR_RECDATA_END + + + CMD_SET_PTR_HI_BYTE = 0x80 # 128 + CMD_SET_PTR_LOW_BYTE = 0x81 # 129 + + CMD_READ_BYTES_COUNT_HI = 0x82 # 130 + CMD_READ_BYTES_COUNT_LOW = 0x83 # 131 + + CMD_WRITE_BYTES_COUNT_HI = 0x84 # 132 + CMD_WRITE_BYTES_COUNT_LOW = 0x85 # 133 + CMD_WRITE_BYTES_SENDING = 0x86 # 134 + + + CMD_RUN = 0x86 # 134 + CMD_CHECK = 0x87 # 135 + CMD_NAME = 0x87 # 135 + + RES_FOUND = 0x37 # 55, Response to CMD_CHECK if Cricket found + + + def __init__(self, debug=False): + pass + + #def sendMemoryPointer(self, ptr): + # self.cmdReadByteCount.(ptr) + + def cmdReadByteCount(self, byteCount): + serialPortIF.txByteRxEcho(CricketIF.CMD_READ_BYTES_COUNT_LOW) + serialPortIF.txWordMSBFirst(byteCount) + return True + + def cmdWriteByteCount(self, byteCount): + serialPortIF.txByteRxEcho(CricketIF.CMD_WRITE_BYTES_COUNT_LOW) + serialPortIF.txWordMSBFirst(byteCount) + return True + + def cmdRun(self): + #serialPort.write(chr(RUN)) + serialPortIF.txByteRxEcho(CricketIF.CMD_RUN) + return True + + def cmdCheckAllCrickets(self): + serialPortIF.txByteRxEcho(CricketIF.CMD_CHECK) + serialPortIF.txByteRxEcho(0) + return serialPortIF.rxOneByte() == CricketIF.RES_FOUND + + +#==================================== + + def writeBytesToCricketMemory(self, ptr, bytes): + printFunctionName() + print "< Bytes: ", bytes + try: + # Shouldn't this be "(ptr + len(bytes) < ADDR_CODE_END" ??? + if len(bytes) < CricketIF.ADDR_CODE_END: + self.cmdReadByteCount(ptr) # To set memory ptr? + self.cmdWriteByteCount(len(bytes)) + #serialPortIF.writeBytes(bytes) + for i in range(len(bytes)): + serialPortIF.txByteRxEcho(bytes[i] % 256) + serialPortIF.rxOneByte() # Extra echo byte! + except: + print "< Problem sending data" + raise Exceptions.CommunicationProblem + + + def cmdSetButton1Pointer(self, vector): + self.writeBytesToCricketMemory(CricketIF.ADDR_BUTTON1_VTR, [vector/256, vector%256]) + +#======================================== + + def txCompiledCodeEnd(self): + start = [128,0,0] # 0xF00000 ??? + end = [0] + + overhead = len(start) + len(end) + + if self.byteCount + overhead < CricketIF.ADDR_CODE_END: + self.writeBytesToCricketMemory(self.byteCount, start) + self.cmdSetButton1Pointer(self.byteCount) + self.byteCount = self.byteCount + overhead + else: + self.byteCount = self.byteCount + overhead + raise serial.serialutil.SerialException + return overhead + + + def txCompiledCode(self, byteCode, byteCodeCount): + printFunctionName() + #try: + self.byteCode = byteCode + self.byteCount = byteCodeCount + self.writeBytesToCricketMemory(CricketIF.ADDR_CODE_START, self.byteCode) + self.txCompiledCodeEnd() + #return "< Send success" + #except serial.serialutil.SerialException: + #return "< Problem sending, check communication" + + + def compile(self, logoCode, ErrMsgFunc=None): + printFunctionName() + self.byteCode = pyYacc.analisarCodigo(logoCode, ErrMsgFunc) + self.byteCodeCount = len(self.byteCode) + print "< code:", self.byteCode + + + def download(self): + printFunctionName() + if not serialPortIF.checkConnection(): + print "< Gogo disconnected" + #self.showInfo(_("Gogo disconnected")) + #return False + raise Exceptions.ConnectionProblem + + self.txCompiledCode(self.byteCode, self.byteCodeCount) + + + +#================================================================================ +#================================================================================ + +class GoGoIF: + + NUMBER_OF_SENSORS = 8 + + # COMMAND/RESPONSE HEADERS: + + HDR_SEND = 0x54,0xFE + HDR_ACK = 0X55,0xFF,0xAA + + + # SINGLE BYTE GOGO COMMANDS: + + CMD_PING = 0x00 # 00 Add Board ID + CMD_READ_SENSOR = 0x20 # 32 Add sensor number*4 + CMD_MOTOR_ON = 0x40, # 64, + CMD_MOTOR_OFF = 0x44, # 68, + CMD_MOTOR_REVERSE = 0x48, # 72, + CMD_MOTOR_THISWAY = 0x4C, # 78, !!! + CMD_MOTOR_THATWAY = 0x50, # 80, + CMD_MOTOR_COAST = 0x54, # 84, + CMD_SET_MOTOR_POWER = 0x60 # 96 Add motor power * 4 + + + # DOUBLE BYTE GOGO COMMANDS: + + BURST_MODE_NORMAL = 0 + BURST_MODE_SLOW = 1 + + CMD_TALK_TO_MOTOR = 0x80, # 128, # Byte 2: motors + CMD_SET_BURST = 0xA0 # 160 # Add burst mode + CMD_LED_ON = 0xC0,0 # 192,0 + CMD_LED_OFF = 0xC1,0 # 193,0 + CMD_BEEP = 0xC4,0 # 196,0 + CMD_SET_PWM_DUTY = 0xC8, # 200, # Byte 2: duty cycle + CMD_UPLOAD_EEPROM = 0xCC, # 204, # >>>>>>>> + # Add upper 2 bits of the number of bytes to upload. Byte 2: lower 8 bits + + + # UPLOADING RECORDED DATA + + CMD_AUTO_UPLOAD = 0xCC, 0x00 + HDR_AUTO_UPLOAD_LEN = 0xEE, 0x11 + + + + + def __init__(self, debug=False): + pass + + def sendCmd(self, code, flush=True): + printFunctionName(callerName()) + + #if self.checkConnection(): + # print "Gogo conectada" + #else: + # print "Gogo desconectada!" + # self.showInfo("Gogo desconectada") + # return False + + serialPortIF.flush() + + command = GoGoIF.HDR_SEND + code + serialPortIF.txBytesNoEcho(command) + + cmd_response = serialPortIF.rxBytes(len(command)) + if cmd_response == '': + print "< No response to command" + return False + ack_response = serialPortIF.rxBytes(3) + + print cmd_response, command + print ack_response, GoGoIF.HDR_ACK + + if flush: + serialPortIF.flush() + + #print cmd_response,ack_response + if cmd_response == command and ack_response == GoGoIF.HDR_ACK: + print "< Command successfully sent" + return True + + print "< Error: cmd_response or ack_response" + return False + + + def readSensor(self, sensorNumber=0): + if sensorNumber >= GoGoIF.NUMBER_OF_SENSORS: + print "readSensor(): Sensor does not exist:",sensorNumber + return -1 + + command = GoGoIF.HDR_SEND + tuple([GoGoIF.CMD_READ_SENSOR + (sensorNumber << 2)]) + serialPortIF.txBytesNoEcho(command) + + cmd_response = serialPortIF.rxBytes(3) + ack = serialPortIF.rxBytes(2) + + if not (cmd_response == command and ack == tuple(GoGoIF.HDR_ACK[:2])): + print "readSensor(): Error reading sensor:",sensorNumber + #print command,"!=",cmd_response + #print ack,"!=",tuple(GoGoIF.HDR_ACK[:2]) + serialPortIF.flush() + if serialPortIF.checkConnection(): + return -1 + else: + raise Exceptions.ConnectionProblem + + highByte = serialPortIF.rxOneByte() & 0x3 + lowByte = serialPortIF.rxOneByte() & 0xff + value = (highByte << 8) + lowByte + return value + + + def motorOn(self): + return self.sendCmd(GoGoIF.CMD_MOTOR_ON) + def motorOff(self): + return self.sendCmd(GoGoIF.CMD_MOTOR_OFF) + + #TODO + def motorBreak(self): + self.motorCoast() + def motorReverse(self): + return self.sendCmd(GoGoIF.CMD_MOTOR_REVERSE) + def motorThisway(self): + return self.sendCmd(GoGoIF.CMD_MOTOR_THISWAY) + def motorThatway(self): + return self.sendCmd(GoGoIF.CMD_MOTOR_THATWAY) + def motorCoast(self): + return self.sendCmd(GoGoIF.CMD_MOTOR_COAST) + + + def talkToMotor(self, motors=""): + byte = 0 + if 'a' in motors.lower(): + byte = byte + 1 + if 'b' in motors.lower(): + byte = byte + (1 << 1) + if 'c' in motors.lower(): + byte = byte + (1 << 2) + if 'd' in motors.lower(): + byte = byte + (1 << 3) + return self.sendCmd(GoGoIF.CMD_TALK_TO_MOTOR + (byte,)) + + def setMotorPower(self, power=0): + if power < 8: + command = (tuple([GoGoIF.CMD_SET_MOTOR_POWER + (power << 2)])) + else: + command = (tuple([GoGoIF.CMD_SET_MOTOR_POWER + (7 << 2)])) + return self.sendCmd(command) + + #TODO + def setBurstMode(self, burstBits=0, burstMode=0): + if burstMode < 0 or burstMode > 1: + return False + + command = tuple([GoGoIF.CMD_SET_BURST + burstMode, burstBits]) + return self.sendCmd(command) + + def ledOn(self): + return self.sendCmd(GoGoIF.CMD_LED_ON) + + def ledOff(self): + return self.sendCmd(GoGoIF.CMD_LED_OFF) + + def beep(self): + return self.sendCmd(GoGoIF.CMD_BEEP) + + #TODO + def setPwmDuty(self, duty=0): + if duty >= 0 and duty < 255: + return self.sendCmd(GoGoIF.CMD_SET_PWM_DUTY + (duty,)) + return False + + + def autoUpload(self, progress_cb=None): + printFunctionName() + if not serialPortIF.checkConnection(): + print "< Gogo disconnected" + #self.showInfo(_("Gogo disconnected")) + #return False + raise Exceptions.ConnectionProblem + + recData = [] + + if not self.sendCmd(GoGoIF.CMD_AUTO_UPLOAD, flush=False): + print "autoUpload: 1" + return recData + + # 3. The Gogo will send four bytes. 0xEE, 0x11, uploadLen (low byte), + # uploadLen (high byte). The first two bytes are just headers. The + # latter two are in bytes. + + # Rx upload-length header bytes (2) + hdr = serialPortIF.rxBytes(2) + print "hdr",hdr + dataBytes = serialPortIF.rx16BitValueLSBFirst() + dataWords = dataBytes / 2 + if dataWords > 0: + for i in range(dataWords): + recData.append(serialPortIF.rx16BitValueLSBFirst()) + if progress_cb: + progress_cb(i, dataWords) + if progress_cb: + progress_cb(dataWords, dataWords) + return recData + + + # Here's an example session: + # + # Computer->GoGo : 0x54, 0xFE, 0xCC, 0x00 // command + # GoGo->Computer : 0x54, 0xFE, 0xCC, 0x00 // echo + # GoGo->Computer : 0x55, 0xFF, 0xAA // ack + # GoGo->Computer : 0xEE, 0x11, 0x06, 0x00 // upload header + # GoGo->Computer : 0x00, 0x00, 0x01, 0x00, 0x02, 0x00 // upload data + # + # In this example the gogoboard uploads 3 16-bit values: 0, 1, 2 respectively. + # + # Note. You may find that the gogo seems to be excessively sending stuff + # back to the computer with all the echos and acks. This was done to + # provide compatibility with the Cricket download protocol. In practice, + # once you send the command bytes to the gogo you can just wait for the + # upload headers (0xEE, 0x11) while ignoring the others. + + + +#================================================================================ +#================================================================================ + +class GoGoComms: + + def __init__(self, portNum=None, debug=False): + global serialPortIF + + serialPortIF = SerialIF(portNum, debug) + + self.cricketIF = CricketIF(debug) + self.gogoIF = GoGoIF(debug) + + # Public Serial Interface: + + def autoConnect(self): + return serialPortIF.autoConnect() + + def getPort(self): + return serialPortIF.getPort() + + def closePort(self): + return serialPortIF.closePort() + + def isUSBVersion(self): + return serialPortIF.isUSBVersion() + + # Public Cricket Interface: + + def compile(self, text, ErrMsgFunc=None): + return self.cricketIF.compile(text, ErrMsgFunc) + + def download(self): + return self.cricketIF.download() + + + def autoUpload(self, progress_cb=None): + return self.gogoIF.autoUpload(progress_cb) + + + # Public GoGo Interface: + + def beep(self): + return self.gogoIF.beep() + + def ledOn(self): + return self.gogoIF.ledOn() + + def ledOff(self): + return self.gogoIF.ledOff() + + def talkToMotor(self, m): + return self.gogoIF.talkToMotor(m) + + def motorOn(self): + return self.gogoIF.motorOn() + + def motorOff(self): + return self.gogoIF.motorOff() + + def motorBreak(self): + return self.gogoIF.motorBreak() + + def motorCoast(self): + return self.gogoIF.motorCoast() + + def setMotorPower(self, power): + return self.gogoIF.setMotorPower(power) + + def motorThisway(self): + return self.gogoIF.motorThisway() + + def motorThatway(self): + return self.gogoIF.motorThatway() + + def motorReverse(self): + return self.gogoIF.motorReverse() + + def setPwmDuty(self, duty): + return self.gogoIF.setPwmDuty(duty) + + def readSensor(self, sensorNumber): + return self.gogoIF.readSensor(sensorNumber) + + +#========================================================================================================# + + +if __name__=='__main__': + GoGoComms(0, False) + |