Sending Commands over TCP with a node only for USB

Dear,

I use equipment from a company called: National Control Devices. They produces relais systems etc etc.
Also they are experiencing with NodeRED and have developed a node for controlling one of their favorite relais. The NCD MCP23008 node. Only one problem: the node can only be configured to talk to the devices through USB / COM port, or I2C port.

Now i was wondering if it is still possible to use this node, but talks to my device over TCP/IP?
It is only a relaisbord, no computer or anything.
Here is more information: https://ncd.io/using-node-red-mcp23008/
Is there a solution? Something like a virtual COM port or so?

Thanks in advance

1 Like

You could use a pizerow and have that talk to the board using USB and then talk to that over tcp from your main pi

this could be, but i have many boards over TCP/IP and all in different places.

Unless the board supports a virtual comm port then you cannot do it directly.

You could, however, use a Pi Zero running Node-RED. Still low-cost but the Zero with NR would create a TCP listener and forward the data on to an actual serial port that you connect to your board.

Dear All,

Mm okay, then i think it is better to write some python things to control the relays.
The boards has got Ethernet ports already delivered from factory.

Thanks anyway everybody!

I can't see any ethernet ports on that board you show... maybe it's a different board ?
If it is - is there spec for it that describes how the ethernet interface works (API). ?

Dear All,

yes on that particular board you don't see the Ethernet port.
Here is the board with IOT interface : https://store.ncd.io/product/8-channel-general-purpose-spdt-relay-shield-with-iot-interface/ and here is the Ethernet port attachment: https://store.ncd.io/product/ethernet-interface-adapter-for-ncd-iot-devices-lantronix-xport/

The ethernet devices are capable of using API commands. With the help of the program AnyI2C from ncd.io i can see what codes that program is sending to open or close the relais.

But due the fact that ncd.io designed an node for this kind of relayboard i was hoping to get that working true TCP. But unfortunatly it is only for USB or I2C direct communications.
The handyness of the node is that it can handle everything.
The other way what i was thinking of is with the use of a python scrip to control relays. But i don't know if it is possible to control 8 relais from one python interface.

Dear All,

I have another question: I have made a small Python3 script with al the commands for opening and closing a specific relay. Now i was thinking to connect buttons to the Python script in Node red and to control the relais like that way.

But how can i insert a payload message that python then uses as a variable for opening / closing the correct relay.
SO how is it possible to press a Dashboard button, witch sends out : R1_ON value, and python takes this value and execute the command after R1_ON by sending the API code. This way i can lead 4 buttons to one Python 3 module. Thank you.

The python value:

import sys
import socket
import datetime
import time

TCP_IP = '192.168.12.183'
TCP_PORT = 2101
BUFFER_SIZE = 1024
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))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send((R1_OFF))
s.close()

You may want to look at the existing Pi GPIO nodes as they so something similar and call a long running python process... https://github.com/node-red/node-red-nodes/tree/master/hardware/PiGpio

Dear Dceejay,

Thank you. But the boards can not be physicaly connected to the Rpi. So the GPIO i can not use.
The only way for me to communicate with the boards is over Ethernet.

Then you have the solution, use mqtt

Add mqtt to your python script. Then you can send specific "button presses" to your script, evaluate the message payload and control the desired relay. Very simple, fast, reliable

You might have to install mosquitto as broker and paho clients if you don't have it already

In your python script you then have to add code to handle subscription to the broker and message handling

If you need further help, just ask...done this thousand times

If you can talk to your board using TCP, then maybe this would work (not tested)

image

[{"id":"473bf70c.8cd408","type":"ui_button","z":"644bfc26.d04304","name":"","group":"f20856f.2d30aa8","order":0,"width":0,"height":0,"passthru":false,"label":"R1 On","color":"","bgcolor":"","icon":"","payload":"[170,3,254,108,1,24]","payloadType":"json","topic":"","x":710,"y":132,"wires":[["413758cf.ce8eb8"]]},{"id":"413758cf.ce8eb8","type":"tcp out","z":"644bfc26.d04304","host":"192.168.12.183","port":"2101","beserver":"client","base64":false,"end":true,"name":"","x":1012,"y":132,"wires":[]},{"id":"f20856f.2d30aa8","type":"ui_group","z":"","name":"Relay","tab":"e317a92c.8306f8","disp":true,"width":"6","collapse":false},{"id":"e317a92c.8306f8","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]
1 Like

If the suggestion from @cymplecy works it will be a very simple solution. If you decide moving forward with python and mqtt, below is a sample. It's a matter of taste what to select. Advantage in using mqtt could be that you would like to extend some functionality in the future. Let's say in future you would like to be able to control your relays from outside your home network in a safe way. With mqtt it is pretty easy using public mqtt servers

Install mosquitto broker in your RPi (there are many guides on the net):

Assuming we are using Python3, you eventually have to install some python libraries to make the script below work

sudo pip3 install paho-mqtt
sudo pip3 install setproctitle
sudo pip3 install subprocess

Copy the script below to your /home/pi directory in the RPi and start it from a command prompt

python3 relay_control.py

From Node-RED you just send very simple commands to control the relays. There is also a command to abort the script. Commands sent are strings like "R3_ON", "R3_OFF" and "abort"

image

# 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("commands", 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_IP = '192.168.12.183'
    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")
    print (event)

    if event == 'abort':
        abort = True
    
    if "_O" in event:
        print ("filtered:", event)
        print (cmds[event])
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((TCP_IP, TCP_PORT))
        s.send((cmds[event]))
        s.close()


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)

2 Likes

Dear Gentleman,

What a great help you both offer me!
To put some light on the situation: in the near future i will be controlling up to 4, 5 different relaisboards using an IP adress, so 5 IP adresses. About 40 relais in total.

Also i want to control and read sensor data etc etc. So i think that the Mosquitto broker is the best solution?

Dear All,

okey, i found my error in the Node-red configuration. I see that @krambriw named the topic "commands", and there was my error. So now if i click on R3_ON or R3_OFF, i see in the raspberry pi command terminal that commands are received:
For instance:
filtered: R3_ON
b'\xaa\x03\xfen\x01\x1a'

But the actually status of the relay does not change. When testing it with Jetbrains Pycharm and the same command ((170,3,254,110,1,26)), it works.

Any idea what could be the problem? The relays are controlled with API commands.

You are sending the "event" not the encoded version cmds[event] which I suspect is what you need to send to the device.

2 Likes

Thank you! Embarrassing...

@Observatory_Gromme
I don't know if you managed to make this work but I just thought about what you mentioned earlier about controlling multiple boards with various ip-addresses

The script I provided was made for one board. If you have multiple boards a modified solution is required. One could be that you have multiple scripts running with a unique mqtt topic and hardcoded ip per board. Another could be having a single script supporting multiple boards (what I personally would recommend)

Having a single script requires that the ip of the board somehow is unique to the command or is part of the message sent from Node-RED. The following example is based on that, i.e. the ip is part of the message sent


image

The updated script

# 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_ex((event[1], TCP_PORT))
        s.send((cmds[event[0]]))
        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)

Dear All,

Thank you very much! Indeed, i'm starting to let it work. Hadn't had much time yet but to give a full explanation. The idea is:
A raspberry pi now, in the future a MOXA linux computer with Node-red installed on. --> Connected to a local network.On that local network there are 5 relays boards connected to that are controller by IP address. So the Raspberry Pi controls those boards.
Single script? Yes, if you say it is the best, then i will proceed in that way.
A total of 40 relays will be controlled. That are a LOT of buttons..... :smiley:

Another question: about 30 to 35 relays will be controlled in ON / OFF situations.
But if possible: some relays must switch ON when pushing the button by mouse click, and when released ( Or by timer ), they must switch back off. Is this possible? Lets take the above script to test this?

The script will work for this as well. The key here is the buttons in your GUI. Assuming you are using the dashboard, there is no such "momentary button" available as standard but you can create this using the dashboard template node. The example below is one way of solving it. You can define one such button for each relay you want control (click send relay ON, release sends OFF)

If you want a timer controlled operation, you can use the trigger node

See below

The code in the template node:

<div class="momentary">
   <md-button> R1 ON/OFF</md-button>
</div>

<script>

(function($scope) {
    
$('.momentary').on('touchstart mousedown', function(e) {
    e.preventDefault(); //prevent default behavior
    $scope.send({"payload": "R1_ON,127.0.0.1"});
});

$('.momentary').on('touchend mouseup', function(e) {
    e.preventDefault(); //prevent default behavior
    $scope.send({"payload": "R1_OFF,127.0.0.1"});
});
    
})(scope);
</script>type or paste code here

The trigger node:

1 Like