looking for a simple relay controller

If you google for ‘usb relay module python’ you’ll find several which can be controlled from python over usb for <30 dollars.

For instance here are a few which seem to come with python example code

The simplest way to go in my opinion is an IP power switch. You give it a fixed ip address and with some simple udp string you can switch it off an on. Out of the box, no additional wiring etc needed. I guess the cheapest ones you’ll find are around 20-30€.

Check out Phidgets!

I’ve used their relay board before in the context of a pure python installation. Worked great.

To be more specific, check out:

phidgets.com/?tier=3&catid= … rodid=1020
4 SPDT relays 210W for DC or 1750VA for AC, they have one with 8 relays on board as well. Controlled from a simple API in C, C#, Java, JS, Python, Max/MSP, Obj-C, .NET, and Swift

thanks everyone.

hi drhandshake,
Are you saying ‘pure python’ as in you have only used outside of Touch?
We got the Phidget relay here you pointed to. I’ve managed to get it working in a 3.5.1 Idle environment and am now trying to weed through the one example script I found relevant to the relay (Digital_output.py).
it would be great if somebody has actually got this to work in Touch and slimmed down the example script to just the essentials it takes to actually control the relay in Touch.

If you used within Touch, was this 088 or 099? what version of Python ?

Hey yeah I meant the project I did with that relay was a python-only application …

I think probably the easiest way to deal with a device like that is to run the relay handling stuff in a standalone (python, javascript, whatever) process and communicate with it from TD over sockets or whatever suits you.

We use the Phidget Python module directly inside TouchDesigner and haven’t seen any issues. Relays a pretty easy to interface with as you are just setting values

The challenging part is using the devices that allow you to set a callback. They run on a separate thread and TD will complain or crash if you reference any TD objects. Best solution I’ve found is to open a socket to a tcpip dat from within the callback and send the callback args back via a json dict.

Yes - to clarify - the callback stuff is the use case I was recommending a separate thread for. IIRC the Phidgets handler keeps a connection open to the device and sends an ack when coms are established so if you don’t want to keep re-opening that for every change you could run that in a standalone context and communicate to it from your main TD process.

Recently used this. (with OSC)
shop.pimoroni.com/products/automation-hat + a Pi
The code is here (but not latest version PM me if you are interested)
Also had to hack the Automation Hat library a bit based on this post.
forums.pimoroni.com/t/automatio … issue/6692

I use ESP’s for everything now.

you can get pre-packaged chips that work just like arduino, I like the D1 Mini series.

amazon.com/IZOKEE-NodeMcu-I … ds=d1+mini

just like arduino they come with all sorts of shields


and with a little bit of code you can connect it to Touch in a few minutes.

I also use OSC for things like this.

[ below is arduino code ]

#include <WiFiUdp.h>
#include <ESP8266WiFi.h>
#include <OSCMessage.h>
#include <OSCBundle.h>

WiFiUDP Udp;                                // A UDP instance to let us send and receive packets over UDP

IPAddress outIp(10, 0, 1, 5);     // remote IP of your computer
const unsigned int outPort = 9900;          // remote port to receive OSC
const unsigned int localPort = 9901;        // local port to listen for OSC packets (actually not used for sending)

const char* ssid     = "[YOUR SSID OF WIFI]";
const char* password = "[THE WIFI PASSWORD]";

unsigned int ledState = LOW;              // LOW means led is *on*

void setup() {
  pinMode(D1, OUTPUT);
  digitalWrite(D1, ledState);


  Serial.print("Connecting to ");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {

  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println("Starting UDP");
  Serial.print("Local port: ");

void oscMessage(int input) {
  OSCMessage msg("/esp");
  Udp.beginPacket(outIp, outPort);

void relay(OSCMessage &msg, int addrOffset ) {
  ledState = msg.getInt(0);
  digitalWrite(D1, ledState);
  Serial.print("/led: ");

void loop() {
 OSCMessage msgIN;
  int size;
  if ( (size = Udp.parsePacket()) > 0)
    while (size--)
    if (!msgIN.hasError()) {
      msgIN.route("/relay", relay);
    outIp = Udp.remoteIP();

in touch, use an OSCOUT dat and run this script


or this one


Heres something I literally threw together in 10 minutes.


well my partner went with phidget relay so I’m tasked with figuring out, finding it to be a bit beyond me.

I’ve been hacking away at ‘DigitalOutput’ example script with intention of running in CHOPEXECUTE DAT, got standalone script below working with a hard-wired address in IDLE3.5.1, but within Touch099 its unreliable and slow. I’m not fully clear on open vs. attach channel requirements in script.

If any phidget users could point to a bare-minimum script for Touch I’d be much obliged. I cant seem to pare it down to what is essential with all the exceptions and support for usb hub/network stuff.

[code]#import sys
import time
#import traceback
from Phidget22.Devices.DigitalOutput import *
from Phidget22.PhidgetException import *
from Phidget22.Phidget import *
from Phidget22.Net import *

from PhidgetHelperFunctions import *
except ImportError:
sys.stderr.write("\nCould not find PhidgetHelperFunctions. Either add PhdiegtHelperFunctions.py to your project folder "
“or remove the import from your project.”)
sys.stderr.write("\nPress ENTER to end program.")
readin = sys.stdin.readline()


  • Displays info about the attached Phidget channel.

  • Fired when a Phidget channel with onAttachHandler registered attaches

  • @param self The Phidget channel that fired the attach event
    def onAttachHandler(self):

    ph = self
    #If you are unsure how to use more than one Phidget channel with this event, we recommend going to
    #www.phidgets.com/docs/Using_Multiple_Phidgets for information

      print("\nAttach Event:")
      * Get device information and display it.
      serialNumber = ph.getDeviceSerialNumber()
      channelClass = ph.getChannelClassName()
      channel = ph.getChannel()
      deviceClass = ph.getDeviceClass()
      if (deviceClass != DeviceClass.PHIDCLASS_VINT):
          print("\n\t-> Channel Class: " + channelClass + "\n\t-> Serial Number: " + str(serialNumber) +
                "\n\t-> Channel:  " + str(channel) + "\n")
          hubPort = ph.getHubPort()
          print("\n\t-> Channel Class: " + channelClass + "\n\t-> Serial Number: " + str(serialNumber) +
                "\n\t-> Hub Port: " + str(hubPort) + "\n\t-> Channel:  " + str(channel) + "\n")

    except PhidgetException as e:
    print("\nError in Attach Event:")

  • Displays info about the detached Phidget channel.

  • Fired when a Phidget channel with onDetachHandler registered detaches

  • @param self The Phidget channel that fired the attach event
    def onDetachHandler(self):

    ph = self
    #If you are unsure how to use more than one Phidget channel with this event, we recommend going to
    #www.phidgets.com/docs/Using_Multiple_Phidgets for information

      print("\nDetach Event:")
      * Get device information and display it.
      serialNumber = ph.getDeviceSerialNumber()
      channelClass = ph.getChannelClassName()
      channel = ph.getChannel()
      deviceClass = ph.getDeviceClass()
      if (deviceClass != DeviceClass.PHIDCLASS_VINT):
          print("\n\t-> Channel Class: " + channelClass + "\n\t-> Serial Number: " + str(serialNumber) +
                "\n\t-> Channel:  " + str(channel) + "\n")
          hubPort = ph.getHubPort()
          print("\n\t-> Channel Class: " + channelClass + "\n\t-> Serial Number: " + str(serialNumber) +
                "\n\t-> Hub Port: " + str(hubPort) + "\n\t-> Channel:  " + str(channel) + "\n")

    except PhidgetException as e:
    print("\nError in Detach Event:")


  • Writes Phidget error info to stderr.

  • Fired when a Phidget channel with onErrorHandler registered encounters an error in the library

  • @param self The Phidget channel that fired the attach event

  • @param errorCode the code associated with the error of enum type ph.ErrorEventCode

  • @param errorString string containing the description of the error fired
    def onErrorHandler(self, errorCode, errorString):

    sys.stderr.write("[Phidget Error Event] -> " + errorString + " (" + str(errorCode) + “)\n”)


  • Creates, configures, and opens a DigitalOutput channel.

  • Provides interface for setting the duty cycle of the DigitalOutput channel

  • Closes out DigitalOutput channel

  • @return 0 if the program exits successfully, 1 if it exits with errors.
    def main():

    ch = DigitalOutput()


    print("\nSetting OnAttachHandler…")

    print(“Setting OnDetachHandler…”)

    print(“Setting OnErrorHandler…”)




Can you send a .toe showing how you have this set up?

Looks like it’s attaching callbacks to a persistent connection to the device after some blocking calls (sleep), both of which are a bad fit for running within your main TD instance – this is why I was suggesting you might want to run this code in a freestanding Python process and communicate to it from your TD process with sockets or whatever suits.

thks Noah et al,

I guess I’m stubbornly not accepting the idea that most hardware that is Python controllable really is best controlled outside of touch’s internal python environment. Seems counter-intuitive and wasteful, complicating, but I guess any ‘performance hit’ ( if it exists) even in the most critical situations is probably largely unnoticeable. I need to just accept this and master JSON, OSC or UDP etc. communication to external Python ‘nodes’(if that is even the right term) :neutral_face: (Ive done this sort of communication before and it worked efficiently, but now Ive forgotten most of it and will have to re-familiarize myself)

also, explaining to somebody who is even a lesser programmer than me ’ yeah, you got to run this Python script outside of Touch simultaneously…’
( ‘First-World problems’, I know … )

It does seem like most of the suggested hardware solutions in this thread come to the same conclusion.
Maybe it is better this way, rather than trying to always adapt Python/hardware code to run in Touch … that way all I have to do is adapt whatever code to receive JSON or the like …

how to run a python file:

go to terminal/cmdprompt (outside of Touch)

type "python [path/name of file .py] "

press “return” or “enter”

it seems like you are making this a lot more complicated with sub-process python scripts connecting to serial lines. I would try to reduce the number of components adding complexity to this system you are making.

A simple wireless relay with a boolean message sent over OSC is my simplest recommendation.

heres one for 8 bucks.
amazon.com/IZOKEE-Wireless- … 8266+relay

thks Harvey I ordered one to check out . 8 bucks is a no-brainer.

the customer reviews seem to suggest there was hardware issue that has been resolved by manufacturer?

also is the cable in this vid just to provide power through USB to the ESP?

matthew could you/ would you be willing to share script ? I’ve not been able to make this work purely inside touch. im using 004 relay unit - hope that is what u are using.

So, the $8 one will probably take some more effort to program. you will need a usb adapter in order to connect to it over serial USB.

or, looks like amazon has that too.

amazon.com/diymore-ESP8266- … programmer

Then you will be able to re-program the esp with custom code that talks directly to touch.

I would recommend however, the first post I made. with the D1 Mini.
amazon.com/Makerfocus-Devel … 145&sr=8-3

It is a better chip because it comes with a USB port for easy programming
( and yes power also, the code uploads through the cable, but the code I use only transmits over wifi. In the video you can also see serial debugging messages that are sent over USB in a different program showing on my screen, this is just for debugging.

I usually use them attached to phone USB power chargers, but you can also just wire up a wall-wart)

you would plug this ESP into this relay, and you are off to the races.

amazon.com/HiLetgo-Relay-Sh … mini&psc=1

you can buy these chips and the relays for 1/8th of the price on Aliexpress


thank you @harveymoon for the post!
i just stumbled over it and having an ESP32s i tried your script. But i am new to the arduino world and it gave me the following errors (see below)
If i understand correctly it loads something called ESP8266WiFi.h whcih in my case should be
ESP32Wifi.h i guess :unamused:
what can i do to make the thing run ?!

Multiple libraries were found for “WiFiUdp.h”
osc01:2:25: error: ESP8266WiFi.h: No such file or directory
Used: /Users/bst/Library/Arduino15/packages/esp32/hardware/esp32/1.0.1/libraries/WiFi
compilation terminated.
Not used: /private/var/folders/d4/wt5z02692j79hhy5t05dk4_40000gr/T/AppTranslocation/BB700A48-4FC1-4DCA-B445-EC97B56BE04F/d/Arduino.app/Contents/Java/libraries/WiFi
Not used: /private/var/folders/d4/wt5z02692j79hhy5t05dk4_40000gr/T/AppTranslocation/BB700A48-4FC1-4DCA-B445-EC97B56BE04F/d/Arduino.app/Contents/Java/libraries/WiFi
Not used: /private/var/folders/d4/wt5z02692j79hhy5t05dk4_40000gr/T/AppTranslocation/BB700A48-4FC1-4DCA-B445-EC97B56BE04F/d/Arduino.app/Contents/Java/libraries/WiFi
Not used: /private/var/folders/d4/wt5z02692j79hhy5t05dk4_40000gr/T/AppTranslocation/BB700A48-4FC1-4DCA-B445-EC97B56BE04F/d/Arduino.app/Contents/Java/libraries/WiFi
exit status 1
ESP8266WiFi.h: No such file or directory

Sure! No problem.

You’ll need to install the phidget python module and point TD toward the 64bit python module path of course.

The parameters are pretty straight forward, you’ll just need to enter the serial number of the device and choose the correct Hub Port and Channel. If you have it connected to a usb VINT hub you’ll want to make sure the “Remote Device” toggle is off. That’s only for use with the SBC4. Once you have the parameters set correctly, use the “Open Channel” pulse to connect.

The HUB Port Device parameter should be off if you’re using a VINT relay module. That switch is for connecting a digital out directly to the hub port.

Just wanted to say a little more about this: We have used every option listed on this thread and have decided that Phidgets are by far the best choice. I’ve had plenty failures with ESPs, Teensys, Arduinos, Raspberry PIs, Pi Plates, and a few other serial relays. Never once have we had to replace a Phidget on a job. Some of our Phidgets have been running for +1year on permanent installations.

In addition to that, the setup time is non-existent. Their products are plug and play there is no soldering/coding/pulling-hair-out needed. Most products are even fitted with screw terminals. We have a tox file for almost every product of theirs and use them on virtually every job we do.

The product design is very well thought through, their software is wonderful, and their price points are very reasonable. We keep a fully stocked cabinet of their products and have pretty much given away most of the other devices we have used in the past.

For me and the rest of the crew at VT, Phidgets are the definitive answer.
digitalOutputPhidget.tox (1.89 KB)