In parts one and two, I created Python code for reading the Lego Dacta Interface B connected to an iMac via a serial-to-USB adapter. I discovered an issue with this "early" code in that it was prone to random failure, resulting in a Buffer getting filled and not emptied. The new code I created from relearning Python was more stable and doesn't suffer from the buffer issues the old code had.
The code is tested using Thonny and Python Idle IDEs.
Important note: The code and Guide are part of my upcoming Unofficial LEGO Technic Repair and Refurbish book.
When first connected to a computer and powered up, the Interface will have a red LED next to the "Stop" button and a green LED next to the "On" port lit,
First, we start by defining the USB port that Interface B is connected to with the following code:
import serial
import time
ser = serial.Serial(
port='/dev/cu.PL2303G-USBtoUART2420',
baudrate=9600,
)
It is worth noting that when the USB adapter is connected to the iMac via a Satechi hub, and depending on which of the hub’s USB ports the adapter is connected to, it determines which of the ports is enumerated as UART2410, UART2420, or UART2430.
Then we open the port to start communications with the following code:
ser.write(b'p\0###Do you byte, when I knock?$$$')
print(b'###Do you byte, when I knock?$$$')
print(ser.read(31))
This sends the message - b'p\0###Do you byte, when I knock?$$$' as a stream of bytes and then waits for a reply, which we print out using:
print(ser.read(31))
Which displays the 31-byte reply:
b'###Just a bit off the block!$$$'And turns the red LED next to the stop button off.
When starting serial communications in Python using pyserial, normally Python uses:
ser.open()
When talking to the Interface B, we open the port with:
ser.write()
Once the message "b'###Just a bit off the block!$$$'" is received and the red LED turns off, we have two seconds to send another message before the Interface B resets and terminates communication.
In order to keep the serial connection active, we need to keep the connection running by creating a loop and constantly sending the "wake up" command:
while True:
ser.write(b'\x02') # Keep Awake
Reading the input ports.Now that we have the computer communicating with the Interface B, it is time to start reading the input ports. With the Connection open, Interface B sends a 19-byte string to the computer as follows:
b'\x00\x00\xff\xc8\x95L\xff\xc8\xff\xcc\xff\xc8\xff\xcc\xff\xc8\xff\xcc\xa1'
The nineteen bytes consist of the following values:
- 1 is for the stop button.
- 2 does not respond to anything.
- 3 and 4 are for Sensor port 4.
- 5 and 6 are for Sensor port 8.
- 7 and 8 are for Sensor port 3.
- 9 and 10 are for Sensor port 7.
- 11 and 12 are for Sensor port 2.
- 13 and 14 are for Sensor port 6.
- 15 and 16 are for Sensor port 1.
- 17 and 18 are for Sensor port 5.
- 19 is a checksum byte.
There isn't much that can be done with the raw byte data at this point; we need to pass it to a byte array so that it can be split up. In order to split up the bytes, we first declare a byte array:
input_bytes = bytearray(ser.read(19))
And then we define how the array is to be split up:
st = (input_bytes[0:2])
p4 = (input_bytes[2:4])
p8 = (input_bytes[4:6])
p3 = (input_bytes[6:8])
p7 = (input_bytes[8:10])
p2 = (input_bytes[10:12])
p6 = (input_bytes[12:14])
p1 = (input_bytes[14:16])
p5 = (input_bytes[16:18])
cks = (input_bytes[18])
The byte string is a bit muddled and needs to be split up in the manner shown above. With the exception of "cks", which is the checksum byte, the other lines are split into two-byte or 16-bit words and then assigned to the variables labeled st, p4, p8, p3, p7, p2, p6, p1, p5, and cks. Once the byte array is split up, we can print out the values in the correct order with:
print(f'STOP: {st}')
print(f'Port1: {p1}')
print(f'Port2: {p2}')
print(f'Port3: {p3}')
print(f'Port4: {p4}')
print(f'Port5: {p5}')
print(f'Port6: {p6}')
print(f'Port7: {p7}')
print(f'Port8: {p8}')
print(f'Checksum: {cks}')
Working with the valuesThe values (with the exception of the checksum) are still in byte values, but we need them as numbers to work with. To convert them, we use the code as follows:
stop = int.from_bytes(st, "big")
This code example takes the bytes from the variable "st" and converts them to a big-endian int value of 0 when the button is not pressed.
When the Stop button on the interface is pressed, while the code is running, the stop variable changes to an int value of 1.
With this information, we can then trigger an output port with the following code.
while True:
ser.write(keepalive)
input_bytes = bytearray(ser.read(19))
st = (input_bytes[0:2])
p4 = (input_bytes[2:4])
stop = int.from_bytes(st, "big")
print(stop)
print(f'Stop Button: {stop}')
port4 = int.from_bytes(p4, "big")
print(port4)
print(f'Port4: {port4}')
if stop > 0:
print('Stop button pressed.')
ser.write(b'\x91\x01') #Turns on Output Port A
A point of note here is that when the stop button is pressed for the first time while the code is running, nothing changes in the byte stream, but if the button is pressed a second time to reset the stop condition and then pressed again to trigger the stop condition, it will then work.
Well, that's all for now, as my brain has stopped braining. See you in the next episode.







Comments