Sending Commands over TCP with a node only for USB

Dear,

Getting back on track with this topic. Thank you already very much with the answers.
I concluded for myself that 35 of the 40 buttons or so will be used with the slide ON / OFF function.
This way i can better see which relays are switched on , and which are switched off.

How will this work with the different commands, and also eventually different IP addresses.

Thank you in advance.

How long is a wire???

What have you tried so far?

What proposed solution do you use? If you use and understand my latest proposal above, it will work the same with the switch node including support for different IP's, you just have to configure them accordingly

Dear,

i'm sorry, i was to quick with my answer / Question. I found it, and now i will be trying to compile the rest. Thank you very much for your help. It's really appreciated.

Dear All,

This weekend it turned out that the most of my relays boards have more different payloads then just the IP address. The first 2 boards are working like a charm with the different IP addresses and the solution with Python and MQTT.

The other boards are a bit more complicated ( at least for me ) to control.

We have different values:
The IP address is one, but then there is the I2C port, witch are 8 different numbers, and then there is the relay value. The relay boards are connected with I2C to the I2C hub, witch has got 8 ports. Is this also possible to make this work with a switch mode sending an ON/OFF payload with IP adress, the I2C port number, and the relay value?

It's a bit more complicated then i was thinking. The first payloads like before where straight forward as the products did know the status of the relays. The new boards is low level logic and i need to evaluate every time witch relays are on and witch are off.

In theory now when i want to put a relay ON i need to:
1: send command to get the relay status
2: the number that is received must be added to the requested relay code.
For instance: relay 1 and 3 are on ( value 1 Bit, and 4 bit ) = 5. I want to turn relay 4 on ( bit 8 ), this gives 5+8: value 13 that has to be shipped with the appropriate values.

see this chart:

The total command send to Open / close a relay is: 170 7 188 50 3 64 9 1 0 .
Whereas the value 50 and 1 are the port number and the relay bit value.

To close i also need to ask the relay status command, and then have to subtracted the wanted value of the relay that's needs to be closed, by the value that is received.
So relay 1 and 3 and 4 are on : this is bit value 13, and i want to close relay 4: -8, i need to send out value 5.

I'm afraid this is way above my head, but should not be impossible, right?

I could give much pre defined values for each port, but this means this would be a total of 32 pre defined values, or i could work with more values inside the command, but then it needs to be filtered out. What would be the best?

I hope you understand me what i'm saying here.

Thank you in advance! This help is much appreciated.

Do you mean you have two different types of boards, different products? And they have different protocols?

Yes.
I have one board witch is pretty straight forward, has it is smart of its own. This you already solved for me using the python script with mqtt broker.

The other boards are not so smart, as we do need to check every time witch relays from witch board are already on. See image below to explain it more i hope. :slight_smile:
The command line to put a relay on / off is like said before: it has a code, but is unique for the different I2C ports, and the actual relay. 170 7 188 X 3 64 9 X 0 and then the checksum.

Can you not return those boards and instead purchase the same type that already works?

Dear @krambriw unfortunatly not: these low level boards are a lot cheaper. The other boards total would be 3000ā‚¬ or more because of the complexity. So i really would like to use these ones. :slight_smile:

OK, understood

What I would need to start to be able to add support for this board is the exact commands & responses that you send out and receives back

  1. Command to get the relay statuses and the response back (I assume a typical command for this is 170,7,188,50,3,64,9,0,233)

Can you check and test this?

Try to run this Python script and check what we get back printed as response. You have to modify the IP and eventually the numbers in the command to fit with the settings of your boards

# coding: utf-8

import sys
import os
import socket


TCP_PORT = 2101
BUFFER_SIZE = 1024
IP = '192.168.0.241'

cmds = {
    "R_TEST" : bytes ((170,7,188,50,3,64,9,0,233))
}

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
p = s.connect_ex((IP, TCP_PORT))
res = s.send((cmds['R_TEST']))
print(res)
res = s.recv((1024))
print(res)
s.close()
del(s)

1 Like

I have been giving new information on how to sending commands easier to these new boards by the company. I will first investigate this, so i understand it myself better :). . thank you.
The I2C boards / hubs i'm referring to:

the relay boards:

PS: you script does work! But it should be easier probably. Let's hope. :smiley:
I will let you know further.

OK, let's see
Anyway, just an explanation of what the script did print for you

10
b'\xaa\x01\x13\xbe'

10 is actually the number of bytes sent. This is correct since you sent the message 170 7 188 50 3 64 9 1 0 236

We receive a python bytes object. If we convert those hex values to decimals we see that the received message is 170 1 19 190

Seems pretty correct

I think this should be good enough to use, to modify the python script to function with both types of boards, you should not need to purchase more interface cards

To control the relays, you would send a message from NR (via mqtt) that includes ip, board nbr, relay number and wanted state. The script will analyze the message and "understand" what type of board the message is targeting. It will first check the states of the relays on the board and make the necessary decoding/math to send the correct command to control the targeted relay. I think this will be rather simple to code in python, at least we should try before you purchase anything else

I think I have a solution that you can try. Use the modified python script below, it should work for both type of boards. Let's see how it works

When you configure the commands in NR for turning relays on/off you have to provide some more parameters for the other boards. You have to send commands as strings typically like "I2C,127.0.0.1,50,5,ON" where
I2C is the keyword telling the script the type of board
127.0.0.1 is the ip address
50 is the I2C port
5 is the relay nbr
ON or OFF is the state you want to set the relay in

Typical configuration in NR for buttons:


# coding: utf-8

import sys
import os
import socket
import datetime
import time
import subprocess
import signal
import paho.mqtt.client as mqtt
from setproctitle import setproctitle


def on_connect(client, obj, flags, rc):
    client.subscribe("cmnds", 0)


def on_subscribe(client, userdata, mid, granted_qos):
    print ('Subscribed:', userdata, mid, granted_qos)
    print ('We are here, waiting for button commands...')


def on_message(mosq, obj, msg):
    global abort
    TCP_PORT = 2101
    BUFFER_SIZE = 1024
    cmds = {
        "R1_ON" : bytes ((170,3,254,108,1,24)),
        "R1_OFF" : bytes ((170,3,254,100,1,16)),
        "R2_ON" : bytes ((170,3,254,109,1,25)),
        "R2_OFF" : bytes ((170,3,254,101,1,17)),
        "R3_ON" : bytes ((170,3,254,110,1,26)),
        "R3_OFF" : bytes((170,3,254,102,1,18)),
        "R4_ON" : bytes ((170,3,254,111,1,27)),
        "R4_OFF" : bytes ((170,3,254,103,1,19)),
        "R5_ON" : bytes ((170,3,254,112,1,28)),
        "R5_OFF" : bytes ((170,3,254,104,1,20)),
        "R6_ON" : bytes ((170,3,254,113,1,29)),
        "R6_OFF" : bytes((170,3,254,105,1,21)),
        "R7_ON" : bytes ((170,3,254,114,1,30)),
        "R7_OFF" : bytes ((170,3,254,106,1,22)),
        "R8_ON" : bytes ((170,3,254,115,1,31)),
        "R8_OFF" : bytes ((170,3,254,107,1,23))
    }

    event = msg.payload.decode("utf-8").split(',')
    print (event)

    if event[0] == 'abort':
        abort = True
      
    if "_O" in event[0]:
        print ("filtered:", event[0])
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        p = s.connect((event[1], TCP_PORT))
        s.sendall((cmds[event[0]]))
        s.close()
        del(s)

    if "I2C" in event[0]:
        Ip = event[1]
        iPort = int(event[2])
        iRelay = int(event[3])
        relayState = event[4]
        dValues = {"1":1, "2":2, "3":4, "4":8, "5":16, "6":32, "7":64, "8":128}
        rValues = [1,2,4,8,16,32,64,128]

        #Check relay states
        chkSum = (170+5+188+iPort+1+65+1) & 255
        #print (chkSum)
        cmd = bytes ((170,5,188,iPort,1,65,1,chkSum))
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        p = s.connect((Ip, TCP_PORT))
        s.sendall((cmd))
#        iSum = 255 # Just when testing
        res = s.recv((BUFFER_SIZE))
        #print (res)
        iSum = int(res[2])
        s.close()
        del(s)
        rStates = []
        for i in rValues[::-1]:
            if iSum-i >= 0:
                rStates.append(i)
                iSum = iSum-i
            else:
                rStates.append(0)

        #Reverse resulting list of current relay statuses
        rStates = rStates[::-1]
        print (rStates)
                
        if relayState == "ON":
            if rStates[iRelay-1]==0:
                rStates[iRelay-1]=rValues[iRelay-1] 
        if relayState == "OFF":
            if rStates[iRelay-1]!=0:
                rStates[iRelay-1]=0 
        print (rStates)

        #Send the new relay command
        iSum =  sum(rStates)
        chkSum = (170+7+188+iPort+3+64+9+iSum+0) & 255
        #print (chkSum)
        cmd = bytes ((170,7,188,iPort,3,64,9,iSum,0,chkSum))
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        p = s.connect((Ip, TCP_PORT))
        s.sendall((cmd))
        s.close()
        del(s)


p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE)
out, err = p.communicate()

for line in out.splitlines():
    #print (line)
    if b'relay_control' in line:
        pid = int(line.split(None, 1)[0])
        print ('Killing ', pid)
        os.kill(pid, signal.SIGKILL)

time.sleep(2)

nn = 'relay_control'
setproctitle (nn)

abort = False
client = mqtt.Mosquitto()
client.on_connect = on_connect
client.on_message = on_message
client.on_subscribe = on_subscribe
client.connect("127.0.0.1", 1883, 60)
client.loop_start()

#if not os.geteuid() == 0:
#    sys.exit("script only works as root")

while not abort:
    time.sleep(1)
    
client.loop_stop()
client.disconnect()
del client
exit(0)

I probably missed something in this thread and I appreciate that you may be more familiar with Python than JavaScript but I can't see anything in there that couldn't be done in Node-RED itself without having to call out to Python which is quite an overhead?

Well, I guess you are welcome to provide such alternative solution if you like. As you say, most likely possible, eventually you will need to use some tailored javascript to make things happen if you do not know nodes by hart that will make it. So some coding most likely required anyway, a matter of taste for python or javascript. Currently I'm also not fully sure about the total load and speed required, python will allow using threading and multiprocessing if it should become required

Keeping it outside of NR is also a choice of taste, it has anyway a standard mqtt interface so can be controlled/used by all type of applications having the same. And you are right, in this case it is not that much NR, just some buttons in the dashboard
:slightly_smiling_face:

Thank you for this great support!:
I changed the python script, and tested it with relay 1 of the I2C board.
See below what i get back ( first i sended ON, then OFF )

Subscribed: None 1 (0,)
We are here, waiting for button commands...
['I2C', '192.168.12.194', '50', '1', 'ON']
[1, 2, 4, 8, 16, 32, 64, 128]
[1, 2, 4, 8, 16, 32, 64, 128]
['I2C', '192.168.12.194', '50', '1', 'OFF']
[1, 0, 4, 0, 16, 0, 64, 0]
[0, 0, 4, 0, 16, 0, 64, 0]

Hmmm, doesn't look what I expected

I have made a new test script, check next post

Summary

Could you change the code where it says

        #print (res)
        iSum = int(res[2])

to

        print (res)
        iSum = int(res[2])
        print (iSum)

and run again and paste the result here?

I think I understand what went wrong here. As we received [1, 0, 4, 0, 16, 0, 64, 0 ] where the sum is 85 I realize that I instead of evaluting the relay status request command response, I have used the message response (85). That's why things goes wrong

But interesting to know is if the relay 1 still operated correctly or not? I mean if the message sent was correctly formatted?

I need to have more information about what is actually returned from the boards on the relay status
request command and to find away to discard the 85 response (problem here is that 85 is also a valid response to the relay status request so selecting 85, 86, 87 etc as a generic message response as they did was a bad choice when they designed the protocol)

Please run this python script and paste the result in your response. The script will just ask for relay status request 10 times with a 1 second delay and print the response

# coding: utf-8

import sys
import os
import socket
import time


TCP_PORT = 2101
BUFFER_SIZE = 1024
IP = '192.168.12.194'

cmds = {
    "R_TEST" : bytes ((170,5,188,50,1,65,1,224))
}

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
p = s.connect_ex((IP, TCP_PORT))

for i in range(0,10):
    res = s.send((cmds['R_TEST']))
    print(res)
    print(len(res))
    res = s.recv((1024))
    print(res)
    print(len(res))
    time.sleep(1)

s.close()
del(s)



Dear,

I have setup all the switches for ON and OFF with the correct values. When opening python and running the relay script this is what i get: I first did ON al switches, then OFF.

[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '1', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '2', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 2, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '3', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 4, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '4', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 8, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '5', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 16, 0, 0, 0]
['I2C', '192.168.12.194', '50', '6', 'ON']
b'\xaa\x01U\x00\xaa\x01\x10\xbb'
85
[1, 0, 4, 0, 16, 0, 64, 0]
[1, 0, 4, 0, 16, 32, 64, 0]
['I2C', '192.168.12.194', '50', '7', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 64, 0]
['I2C', '192.168.12.194', '50', '8', 'ON']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 128]
['I2C', '192.168.12.194', '50', '8', 'OFF']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '7', 'OFF']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '6', 'OFF']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '5', 'OFF ']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '4', 'OFF']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '3', 'OFF']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '2', 'OFF']
b'\xaa\x01\x00\xab'
0
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
['I2C', '192.168.12.194', '50', '1', 'OFF']
b'\xaa\x01\x10\xbb'
16
[0, 0, 0, 0, 16, 0, 0, 0]
[0, 0, 0, 0, 16, 0, 0, 0]

Not much seems to actually happen , only one time a relay did go on.

Then as you requested off you latest script, i get en error:

pi@raspberrypi:~ $ python3 relay_controlrec.py
8
Traceback (most recent call last):
File "relay_controlrec.py", line 23, in
print(len(res))
TypeError: object of type 'int' has no len()

PS: i have orderd the last boards that i needed to have to have my full setup. When receiving i will let you know, so maybe the code could be easier.

Thank you.

Oops, can you comment out line 23 and try again? I really need to see what is returned

for i in range(0,10):
    res = s.send((cmds['R_TEST']))
    print(res)
    #print(len(res))
    res = s.recv((1024))
    print(res)
    print(len(res))
    time.sleep(1)
1 Like

No problem, now it worked: result: ( all relays where really OFF )

8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4
8
b'\xaa\x01\x00\xab'
4