How do I communicate with ASCII TCP using Python?

FAQs

This FAQ will show how a simple Python program can be used to communicate with ASCII TCP to toggle and monitor the state of the IO lines.

What is ASCII TCP?

The ASCII Transmission Control Protocol, or ASCII TCP is a query-response/question and answer communication protocol in which a host PC uses ASCII characters to send commands to a device, and receive responses back from the device. There are a range of different ASCII commands that can be sent to the BB-400 and ED devices, some of which will be demonstrated later on in this FAQ. The commands use a hexadecimal format. The 8 IO lines range from 00 (all IO lines are on) to the final hexadecimal number: ff (where all IO lines are off). For more information on ASCII TCP please refer to the: What is ASCII TCP FAQ and for a full list of commands: ASCII protocol and commands FAQ.

Requirements

If your version of Visual Studio Code does not have Python installed, then please refer to the following link: FAQ How do I set up visual studio code with Python.

Obtain the IP address of the BB-400/ED device and the port number of ASCII TCP

The default port number of the ASCII TCP server on the BB-400 is 9500, as shown below:

ASCII TCP default port number

You will also need the IP address of your BB-400 which can be determined in a number of ways, please refer to the following FAQ How do I find my BB-400 on the network.

To find the IP address of your ED device please refer to the following FAQ.

BB-400

ASCII commands

There are a number of useful ASCII commands that can be used to retrieve information about the state of the BB-400.
Some of the commands which will be used in the Python program are:

Device name
command: $01M
output: !01BB-400

Firmware version
command: $01F
output: !011.0.10

Switch all IO outputs on
command: @0100
output: >
All 8 IO lines will be on.

Switch all IO outputs off
command: @01FF
output: >
All 8 IO lines will be off.

Get the current state of the IO lines
command: @01
output: >****
where **** is a hexadecimal representation of the current IO line state. The first 2 numbers represent the inputs and the last 2 numbers represent the outputs.

You will notice that some of the outputs begin with “!01”. This is because “01” is the ID number of the device.

Sample Python Program for the BB-400

This section will provide an example of using a Python script to communicate with the IO lines using ASCII TCP. This program uses Python version 3.7.

Please ensure you have downloaded the Brainboxes Python module (provided in the Requirements section above) and that it is in the same directory as your Python program.

In VSCode create a new file and copy and paste the Python code into your file. Edit the code so that it uses the IP address of your device.
You can run the code by right clicking on the editor and selecting ‘Run Python File in Terminal’.
A terminal opens up at the bottom of the editor. The program is executed and the output is displayed in the terminal.


import brainboxes
import time
import signal
import itertools
from itertools import chain

''' The ASCII commands that will be used in this program:
b'@0100' = Set all outputs on.               IO line state = [1, 1, 1, 1, 1, 1, 1, 1]
b'@01FF' = Set all outputs off.              IO line state = [0, 0, 0, 0, 0, 0, 0, 0]
b'@0106' = Set IO1 and IO2 on, the rest off. IO line state = [0, 1, 1, 0, 0, 0, 0, 0]
b'@010F' = Set IO0-IO3 on and IO4-IO7 off.   IO line state = [0, 0, 0, 0, 1, 1, 1, 1]
b'@0155' = Set alternate IO lines on.        IO line state = [0, 1, 0, 1, 0, 1, 0, 1]
b'$01M'  = Read device name = b'!01BB-400'
Switch IO line 0 off, keeping the rest on, switch IO line 1 off, keeping the rest on, and repeat this through each IO line:
b'@0101', b'@0102', b'@0104', b'@0108', b'@0110', b'@0120', b'@0140', b'@0180'
'''

ascii_command_list = [b'@01FF', b'@0101', b'@0102', b'@0104', b'@0108', b'@0110', b'@0120',
                      b'@0140', b'@0180', b'@010F', b'@0155', b'@0106', b'@0100']

IOlines = [0,1,2,3,4,5,6,7]

def SetOutputs():
    bb400 = brainboxes.AsciiIo(ipaddr='192.168.0.60', port=9500, timeout=1.0)
    device_name = bb400.command_response(b'$01M')
    print("n*** Hello device %s ***n" % device_name[3:9])
    for txdata in ascii_command_list:
            print("Sending command %s" % txdata)
            time.sleep(1) # The sleep is optional. It has only been added to make the example readable.
            rxdata = bb400.command_response(txdata)
            print("Response: %s" % rxdata)

def ReadOutputs():
    try:
        with brainboxes.AsciiIo(ipaddr='192.168.0.60', port=9500, timeout=1.0) as bb400:
            # IO state in hexadecimal format
            read_io_status = bb400.command_response(b'@01')
            print('@01 response: ' +str(read_io_status))

            '''
            Converting ASCII hexadecimal into a binary string
            [2:] gets rid of "0b" at the start of the binary number 0b111 -> 111
            .zfill then adds 0's to the start of the number to make it an 8 bit number
            [::-1] reverses the number
            [3:7] is to read only 00FF and not b'>00FF'
            bin(int(read_io_status[3:7], 16)): 0b111
            bin(int(read_io_status[3:7], 16))[2:]: 111
            bin(int(read_io_status[3:7], 16))[2:].zfill(8): 00000111
            bin(int(read_io_status[3:7], 16))[2:].zfill(8)[::1]: 11100000
            '''
            # IO state in a binary string
            io_state = (bin(int(read_io_status[3:7], 16))[2:].zfill(8))[::-1]

            # Convert binary string to an array of 0s and 1s
            io_state_array=list(chain(*(io_state)))
            print("IO line status: " +str(io_state_array))

            # Convert the array of 0s/1s to an array of True or False. Then print out the IO lines that are on
            for i in range(len(io_state_array)):
                if io_state_array[i] == '1':
                    io_state_array[i] = True
                elif io_state_array[i] =='0':
                    io_state_array[i] = False

            # Use itertools.compress to loop through each IO line and then print out the IOlines that are True/on/1
            line_state_comparison = itertools.compress(IOlines, io_state_array)
            for line in line_state_comparison:
                print("IO line " +str(line)+ " is on")
    except:
        exit()

SetOutputs()

while True:
    try:
        ReadOutputs()
        time.sleep(0.2)
    except KeyboardInterrupt:
        exit()

The output of the program in the terminal will look like:

Terminal output

The program is setting different IO line states. The program will then update if you change an IO line:

Updated IO Line output

Initially the program changes the state of the IO lines, which you can see from the change on the webadmin page or the front of the device:

Web Admin UI

Next the program sends the “@01” command to get the current state of the IO lines.
If you change the IO line state webadmin IO page, and if the change matches any of the responses provided in the ReadOutputs() method the terminal will output the response, e.g. clicking on the button to switch IO line 0 off and keeping the other IO lines on, you will see the IO 0 is removed from the list of outputs that are on.

To terminate the program type ‘CTRL+C’ into the terminal.

ED device and ASCII TCP

The program can also be updated for ED devices. This example uses an ED-588 but the output will be similar over the range of ED devices. Just update the commands and input/output numbers to match that of your ED device. It should also be noted that “01” is being used as the ED device ID in this example

On the ED device’s web page click on Protocol and make sure Current Protocol is selected on ASCII:

ED device UI for changing protocol

For ED devices the ASCII commands to get the name of the device ($01M) and to get the current state of the IO lines (@01) are the same. The commands to adjust the IO lines differ and some examples will be provided below:

Outputs

Switch all outputs off
command: #010A00
@01 state: >00FF

Switch DOut0 on, all other outputs remain off
command: #010A01
@01 state: >01FF

Switch DOut3 on, all other outputs remain off
command: #010A08
@01 state: >08FF

Switch DOut2 on, all other outputs remain off
command: #010A04
@01 state: >04FF

Switch DOut0-DOut3 on, DOut4-DOut7 remain off
command: #010A0F
@01 state: >0FFF

Switch DOut0 and DOut1 on, all other outputs remain off
command: #010A03
@01 state: >03FF

Inputs

Toggling the inputs gives the following responses:
DIn4 low
@01 state: >00EFDIn5 low
@01 state: >00DFDIn6 low
@01 state: >00BFDIn7 low
@01 state: >007F

Sample Program for ED-588 devices

The python code to toggle the ED-588 is below. As with the BB-400 program, be sure to update the IP address to that of your device:

import brainboxes
import time
import signal
import itertools
from itertools import chain

#### ED-588 Device Output states
'''
b'#010A01' - Dout0 on -           response is >01FF
b'#010A08' - Dout3 on -           response is >08FF
b'#010A04' - Dout2 on -           response is >04FF
b'#010A0F' - Dout0 - Dout3 on -   response is >0FFF
b'#010A03' - Dout0 and Dout1 on - response is >03FF
b'#010A00' - all outputs off -    response is >00FF
'''

#### ED-588 Device Input states
'''
Din4 low - response is >00EF
Din5 low - response is >00DF
Din6 low - response is >00BF
Din7 low - response is >007F
'''

IOlines = [0,1,2,3,4,5,6,7]

ascii_command_list = [b'#010A01', b'#010A08', b'#010A04', b'#010A0F', b'#010A03', b'#010A00']

def SetOutputs():
    ed588 = brainboxes.AsciiIo(ipaddr='192.168.0.38', port=9500, timeout=1.0)
    device_name = ed588.command_response(b'$01M')
    print("n*** Hello device %s ***" % device_name[3:9])
    for txdata in ascii_command_list:
        print("Sending command %s " % txdata)
        time.sleep(2) # The sleep is optional. It has only been added to make the example readable.
        rxdata = ed588.command_response(txdata)
        print("Response %s" % rxdata)
        time.sleep(1)

def ReadInputs():
    try:
        with brainboxes.AsciiIo(ipaddr='192.168.0.38', port=9500, timeout=1.0) as ed588:
            # IO state in hexadecimal format
            read_io_status = ed588.command_response(b'@01')
            print('@01 response: ' +str(read_io_status))
            '''
            Converting ASCII hexadecimal into a binary 16 bit number, then flipping the array
            [2:] gets rid of "0b" at the start of the binary number 0b111 -> 111
            .zfill then adds 0 to start of number to make it an 8 bit number
            [::-1] reverses the number
            [3:7] is to read only 00FF and not b'[gt]00FF'
            bin(int(read_io_status[3:7], 16)): 0b111
            bin(int(read_io_status[3:7], 16))[2:]: 111
            bin(int(read_io_status[3:7], 16))[2:].zfill(8): 00000111
            bin(int(read_io_status[3:7], 16))[2:].zfill(8)[::1]: 11100000
            '''
            # IO state in a binary string
            io_state = (bin(int(read_io_status[3:7], 16))[2:].zfill(8))[::-1]

            # Convert binary string to an array of 0s and 1s
            io_state_array=list(chain(*(io_state)))
            print("IO line status: " +str(io_state_array))

            # Convert the array of 0s/1s to an array of True or False. Then print out the IO lines that are on
            for i in range(len(io_state_array)):
                if io_state_array[i] == '1':
                    io_state_array[i] = True
                elif io_state_array[i] =='0':
                    io_state_array[i] = False

            # Use itertools.compress to loop through each IO line and then print out the IOlines that are True/on/1
            line_state_comparison = itertools.compress(IOlines, io_state_array)
            for line in line_state_comparison:
                print("IO line " +str(line)+ " is high")
    except:
        exit()

SetOutputs()

while True:
    try:
        ReadInputs()
        time.sleep(2)
    except KeyboardInterrupt:
        exit()

The program initially sets the outputs, then it monitors the input. If you toggle the input and it matches the response provided in the ReadInputs() method you will see the output in the terminal.

The terminal output looks like:

Terminal output from inputs

The change in IO line states for the ED device is as follows:

ED Web Admin UI

This FAQ has demonstrated how the ASCII TCP protocol can be used to set the IO outputs and to monitor the state of the IO lines and send a response depending on the state. The script can be easily modified to extend the outputs to be set or monitored.

FAQs