Simple communication with a TCP/IP device using Python

I had an interesting correspondence recently with Wim Bruyn from Holland who was using my SerialSend program to implement a remote-controlled relay system for switching appliances on and off automatically. The relays Wim is using (ETH002) are actually controlled via TCP/IP, so he ended up abandoning SerialSend in favour of a custom Python script. It struck me as a great example of using Python to do automation really easily and I think others will find it useful. Wim has kindly given me permission to reproduce it here. I made some tiny modifications, but Wim did all the work.

Python is available for Windows, Linux, Mac OS and various other platforms. It can be downloaded for free from python.org and only takes a couple of minutes to install. All modules used in the examples below (socket, smtplib, argparse, time) are part of the standard library, so they shouldn’t require separate installation.

I’ll present this example in two stages. The first is very pared back version which demonstrates the basic process of sending a short byte sequence via TCP/IP and then sending a short report via email.

#
# simple_rc.py - Written by Wim Bruyn (slightly modified by Ted Burke)
#
# This short Python program sends a three byte command to a remote
# controlled relay device via TCP/IP. It receives a response indicating
# whether the command was carried out. A report is then sent via email.
#

import socket    # used for TCP/IP communication 
import smtplib   # used to send email report
import time      # used to insert current date in email report

# Prepare 3-byte control message for transmission
TCP_IP = '192.168.2.115'
TCP_PORT = 17494
BUFFER_SIZE = 80
MESSAGE = '\x21\x01\x00' # Relays 1 permanent off

# Open socket, send message, close socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()

# Create report and send as email
sender = 'joe.bloggs@email.com'
receivers = ['joe.bloggs@email.com']

if data == '\x00':
    msg = 'Device powered off on date: ' + time.strftime(“%c”)
else:
    msg = 'Error: Device not powered off on date: ' + time.strftime(“%c”)

smtpObj = smtplib.SMTP('mailhost.email.com', 25)
smtpObj.sendmail(sender, receivers, msg)
smtpObj.quit()

The second example, shown below, is Wim’s final program. It parses command line arguments, allowing eight relay modules to be switched on or off individually. Wim uses this program in conjunction with the Windows Task Scheduler to automate on/off switching of appliances.

#
# SwitchETH002.py - Written by Wim Bruyn
#
# Platform: python33, Windows 7
#
# Purpose:
#   Switching ETH002 or ETH008 TCP/IP relays on and off and send an
#   e-mail when done. Windows task scheduler can be used to activate
#   the relays (optional).
#
# Command line arguments required for:
#   Relays number (1-8),
#   Mode (on/off),
#   e-mail report ID (free format text).
#
# Example usage:
#
#   c:\python33\python.exe SwitchETH002.py 2 off "External WD USB disk"
#

import socket
import time
import argparse
import smtplib

def SendCommandToRelays (MESSAGE): #Value of MESSAGE is command to be send to relays
    TCP_IP = '192.168.2.115' #IP address of the relays
    TCP_PORT = 17494 #Port number of the relays
    BUFFER_SIZE = 80

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((TCP_IP, TCP_PORT))
    s.send(MESSAGE)
    data = s.recv(BUFFER_SIZE) #Response from Relays
    s.close()

    if data == b'\x00':
        SendMailMessage ( (args.Device + ' is powered ' + args.Function + ', on date: ' + time.strftime("%c") ) )
    else:
        SendMailMessage ( ('Error:' + args.Device + ' is not powered ' + args.Function + ', on date: ' + time.strftime("%c")) )

def SendMailMessage (Mtext): #Value of Mtext is action report send to mail recipient
    sender = 'joe.bloggs@email.com' #mail address of the sender
    receivers = ['joe.bloggs@email.com'] #mail address of the receiver

    smtpObj = smtplib.SMTP(‘mailhost.email.com’, 25)
    smtpObj.sendmail(sender, receivers, Mtext)
    smtpObj.quit()

parser = argparse.ArgumentParser()
parser.add_argument ("Number", help = "Relays number 1 – 8", type=int, choices = [1, 2, 3, 4, 5, 6, 7, 8])
parser.add_argument ("Function", help = "on is relays on, off is relays off", choices = ["on", "off"])
parser.add_argument ("Device", help = "Device id for e-mail message")

args = parser.parse_args()

if args.Number == 1 :
    if args.Function == 'on' :
        print ('Relays 1 on'),
        SendCommandToRelays ( b'\x21\x01\x00' ) #Relays 1 permanent on
    elif args.Function == 'off' :
        print ('Relays 1 off'),
        SendCommandToRelays ( b'\x20\x01\x00' ) #Relays 1 permanent off

if args.Number == 2 :
    if args.Function == 'on' :
        print ('Relays 2 on'),
        SendCommandToRelays ( b'\x21\x02\x00' ) #Relays 2 permanent on
    elif args.Function == 'off' :
        print ('Relays 2 off'),
        SendCommandToRelays ( b'\x20\x02\x00' ) #Relays 2 permanent off

if args.Number == 3 :
    if args.Function == 'on' :
        print ('Relays 3 on'),
        SendCommandToRelays ( b'\x21\x03\x00' ) #Relays 3 permanent on
    elif args.Function == 'off' :
        print ('Relays 3 off'),
        SendCommandToRelays ( b'\x20\x03\x00' ) #Relays 3 permanent off

if args.Number == 4 :
    if args.Function == 'on' :
        print ('Relays 4 on'),
        SendCommandToRelays ( b'\x21\x04\x00' ) #Relays 4 permanent on
    elif args.Function == 'off' :
        print ('Relays 4 off'),
        SendCommandToRelays ( b'\x20\x04\x00' ) #Relays 4 permanent off

if args.Number == 5 :
    if args.Function == 'on' :
        print ('Relays 5 on'),
        SendCommandToRelays ( b'\x21\x05\x00' ) #Relays 5 permanent on
    elif args.Function == 'off' :
        print ('Relays 5 off'),
        SendCommandToRelays ( b'\x20\x05\x00' ) #Relays 5 permanent off

if args.Number == 6 :
    if args.Function == 'on' :
        print ('Relays 6 on'),
        SendCommandToRelays ( b'\x21\x06\x00' ) #Relays 6 permanent on
    elif args.Function == 'off' :
        print ('Relays 6 off'),
        SendCommandToRelays ( b'\x20\x06\x00' ) #Relays 6 permanent off

if args.Number == 7 :
    if args.Function == 'on' :
        print ('Relays 7 on'),
        SendCommandToRelays ( b'\x21\x07\x00' ) #Relays 7 permanent on
    elif args.Function == 'off' :
        print ('Relays 7 off'),
        SendCommandToRelays ( b'\x20\x07\x00' ) #Relays 7 permanent off

if args.Number == 8 :
    if args.Function == 'on' :
        print ('Relays 8 on'),
        SendCommandToRelays ( b'\x21\x08\x00' ) #Relays 8 permanent on
    elif args.Function == 'off' :
        print ('Relays 8 off'),
        SendCommandToRelays ( b'\x20\x08\x00' ) #Relays 8 permanent off
Advertisements
This entry was posted in Uncategorized and tagged , , , , , , , , , , , , , . Bookmark the permalink.

15 Responses to Simple communication with a TCP/IP device using Python

  1. Brad says:

    Thank You…I will try it.

    Brad

  2. Mercedes says:

    Thank you very much for uploading this information.
    I am trying to control a RelayPro board of 16 Realys via Ethernet. I found your code and try to apply it.but I haven’t been able to make it work.
    I truly belueve that the IP adress and the port number are properly assigned. I do not know if the buffer size it corrects because I do not know what it represents.
    I was wondering if you could get in contact with me and help me figure it out.

    Thank you in advance,

    Mercedes.

    • batchloaf says:

      Hi Mercedes,

      The buffer size is just the maximum number of characters that the device might send back over ethernet in response to each command you send. In the example above, it’s set to 80, but actually I think that’s much longer than required since only a few bytes are normally sent back.

      Is this the exact device that you’re using?

      http://www.relaypros.com/Relay/Relay/CAT_RELAY16_ETHERNET#.VSKLvOpb–8

      If not, please specify exactly what device you’re using.

      The exact device you’re using is critically important because devices from different manufacturers are controlled using completely different command sets and they respond in different ways too – i.e. the bytes that are sent back in response to each command will be different for different manufacturers’ devices.

      For example, “\x21\x01\x00” was the 3-byte command sequence to switch on relay 1 on the exact board that Wim was using, but that command could be totally different for different ethernet relay boards, so it would be necessary to study the manual for the exact device.

      Ted

      • batchloaf says:

        From what I can see here…

        http://www.relaypros.com/Relay/Device/A0010#.VSKOz-pb–8

        …it looks like the command to turn on Relay 0 in bank 0 consists of the following three bytes: 254, 108, 0. The command to turn the same relay off seems to be 254, 100, 0. Converting these commands into hexadecimal:

        Bank 0, Relay 0 ON : 0xFE 0x6C 0x00
        Bank 0, Relay 0 OFF: 0xFE 0x64 0x00
        

        In the first Python example above, you might change line 17 from this…

        MESSAGE = '\x21\x01\x00' # Relays 1 permanent off
        

        to one of the following:

        MESSAGE = '\xFE\x6c\x00' # Bank 0, Relay 0 on
        

        …or…

        MESSAGE = '\xFE\x64\x00' # Bank 0, Relay 0 off
        

        Ted

      • Mercedes Modet says:

        Hello Ted,

        Thank you very much for your help, I will change the uffer size to smaller number. About the Relay board, it was the one on the link.
        I am sorry I didnt update my comment by I already figure out that the problem was in the way I sent the message.
        It is required to use the struct package and convert the message to this type.
        MESSAGE = struct.pack(“BBB”, 254, 108, 1)

        Thanks again for your help and your time!

        Mercedes.

      • batchloaf says:

        Hi Mercedes,

        Ok, thanks for the update. Glad to hear you got it working.

        I think the result of…

        struct.pack("BBB",254,108,1)
        

        …is probably more or less the same as writing the equivalent hex byte values directly in a string literal…

        struct = '\xfe\x6c\x01'
        

        Anyway, the main thing is that you have it working. Best of luck with the rest of whatever you’re working on!

        Ted

  3. Ditte says:

    This is just what I needed to see after searching the internet all day! Thank you so much!

  4. Ed says:

    I just recently purchased this device and I am looking to implement it into my companies systems by: http://gridconnect.com/serial-ethernet-converter.html

    We have TV’s that are always located far from our PCs+switch (running Ubuntu). We would like to run CAT6 cabling to this device that will be located next to the TV’s. This would allow us to control the TV’s from our PC’s using TCP/IP, remotely. We would assign a specific IP to the device and then connect it to the TV. With my limited python knowledge, I am having trouble trying to figure out how to “translate” the serial command of: ka 01 01 (turns tv on) to a packet that could be sent through TCP/IP. This program is a great building block and I am now trying to transform is to meet my needs and I appreciate you sharing this. Would you have any recommendations or help you could provide me with?

    Thanks

    • batchloaf says:

      Hi Ed,

      You could try something like this:

      import socket                                         # Import network socket library
      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Open socket
      s.connect(('192.168.2.7', 23))                        # Connect to 192.168.2.7 port 23
      s.send('ka 01 01')                                    # Turn TV on
      s.close()                                             # Close socket
      

      That’s assuming you want to do it from Python?

      The above code also assumes that you’ve configured the ATC-1000 in TCP server mode, assigned it a fixed IP address of 192.168.2.7, and left its port number set to the default value of 23. You’ll find more about that in the user manual:

      http://gridconnect.com/media/documentation/atc/ATC-1000-UM.pdf

      TCP server mode is described (kind of) in Section 5.3.

      I’m also assuming that you’re running Windows at the PC end? If you happen to be running Linux instead, then you don’t even need to create anything like the above program. I think you can just type this in at the command line:

      echo 'ka 01 01' > /dev/tcp/192.168.2.7/23
      

      An even better option might be to use netcat, but you can read more about here if you’re interested:

      https://stackoverflow.com/questions/3010507/linux-tool-to-send-raw-data-to-a-tcp-server

      Hope that helps!

      Ted

      • batchloaf says:

        PS Of course, I should have mentioned that I’ve never used an ATC-1000, so my suggestion above is based on of a quick read of its user manual. I think this is how it works, but I could be wrong! You’d definitely need to try it out in practice to be sure. Also, it definitely won’t work until you’ve configured the ATC-1000 as described in its documentation. The Python code I wrote above assumes that the ATC-1000 will be waiting to receive an incoming TCP connection – hence it’s essential that it’s configured in “TCP server” mode. There are several other ways it can be configured and I’m not sure which is the default.

        Best of luck with it!

        Ted

      • Ed says:

        I really appreciate you taking the time to answer my question. This is a project I am working on that would greatly help the company I work for. Let me start by explaining a little bit more of what I am trying to do. Basically, we are trying to remotely control and monitor over 300 televisions in the field through their RS232 port. We have to use a Ethernet -> SerialRS232 device (ATC1000) because of the distance between our PC’s and our TV’s. Our PC’s in the field are all running Ubuntu and are all monitored remotely over here at our office. The goal is to have a python script that we could trigger remotely using our internal tools that would turn the TV on. The first step is to try to get the device to work using some command line or python script.

        I first tested the device on windows just to see if I had the correct ASCII code to actually turn the TV on. On windows, I used the VCOM software that they suggested to create a virtual COM port and then used Putty to type in ‘ka 01 01’. This worked and it even sent back commands for status that looked like:
        “a 01 OK01x” -> when the tv was turned on

        So I know that the device is compatible with the TV and I know that I have the correct codes to control the various settings of my TV.

        As for the ATC1000. I have it set to TCP Server mode with a data port number of 23. There is a setting under it for the control protocol. By default it is just set to “Port Number 6000”. Should I set it to RFC2217?
        Here is a screenshot of the TCP settings: http://imgur.com/a/dvsg3

        Now I have my test PC with Ubuntu 16.04 LTS set up and I am trying to figure out how to get it to work. I have tried the following command:
        echo ‘ka 01 01’ > /dev/tcp/192.168.2.7/23

        No success. However, I do see the “ACT” LED light up quickly on the device when I try to echo the command through so we are at least sending it something. I will try to connect to it through telnet but ultimately I would like to use python. Let me know if you think I should try or use any other method.

        Thanks!

      • batchloaf says:

        Hi Ed,

        The fact that the ACT light is blinking when you send the message is encouraging at least. I’m not sure about the RFC2217 thing – my (limited) understanding of that is that it’s to do with creating a virtual COM port on the PC that you can talk to, which indirectly talks to whatever device is plugged into the remote serial port. Do you think that could be what that dialog box is configuring?

        Did you try running the short python script I suggested in the first of my two posts above? If you did (and assuming it didn’t work), did you notice whether the ACT LED lit up or not?

        All in all, if you seeing that ACT light blink when you send the command using echo and you’ve already been able to turn on the TV using putty, then I’d say you’re within a couple of configuration changes of getting the whole thing working.

        Ted

      • batchloaf says:

        Hi Ed,

        Just checking – did you make any progress with your problem?

        Ted

      • Ed says:

        Hi Ted,

        Some other stuff came up at the office so I have been super busy with that. I have no been able to communicate with the TV in the same way that I did on Windows by creating a VCOM port and then through the Putty command line. I have read a little on opening serial ports and listening as well. I tried using the socket code you suggested and get a quick flashing light on the ACT on the ATC1000 but can not get it to turn on the TV.

        The way I would want the program to work is work it send the signal to the TV and to listen to the response and then to close the socket. I will be taking the device home with me this weekend to try to get it work and will post a more in depth explanation of what I am trying out. If you have any suggestions on things I might want to try this weekend please let me know!

        Once again, I appreciate the help.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s