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
This entry was posted in Uncategorized and tagged , , , , , , , , , , , , , . Bookmark the permalink.

6 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

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