Multiple Arduinos

We have a set of five Arduino Uno’s attached to a control PC via USB. The Arduinos are send out data to LED’s strips and receiving data from IR sensors. We are using the RS232 node as we have loaded up our own code to the Arduino.

I have created a patch with separate RS232 nodes setting the ComPort to control/listen to each Arduino. The software works fine when one RS232 node has the comport set, but as soon as I bring in a second or third, etc. vvvv starts to really slow down.

Are there any known issues with with virtual comports and serial data rates?

I’ve found improvement using the RS232 (Devices Spreadable) node, but something is still slowly down vvvv when I enable multiple comports.

knock together a dynamic plugin that comms / buffers in a separate thread?

// This is a new namespace in .NET 2.0
// that contains the SerialPort class
using System.IO.Ports;

private static void SendSampleData()
{
  // Instantiate the communications
  // port with some basic settings
  SerialPort port = new SerialPort(
    "COM1", 9600, Parity.None, 8, StopBits.One);
  
  // Open the port for communications
  port.Open();
  
  // Write a string
  port.Write("Hello World");
  
  // Write a set of bytes
  port.Write(new byte[]() {0x0A, 0xE2, 0xFF}, 0, 3);
  
  // Close the port
  port.Close();
}

such a plugin would be nice !

damn, i bet sugokuGenki could’ve helped here, but since he misteriously disappeared…

no, wait… :o

edit: damn, mrboni used voodoo tricks to steal that silly joke right out of my mind a mere 15 minutes before i posted… :|

In his last moments before doom sugokuGENKI wrote the below code shortly before disappearing in to thin air - the plugin code enables me to communicate with the Arduinos (I can see the TX RX lights flashing), but they do not respond to the messages I am sending them. Anyways, someone may find it useful =)


  • region usings
    using System;
    using System.ComponentModel.Composition;
    using System.IO.Ports;
    using System.Threading;

using VVVV.PluginInterfaces.V1;
using VVVV.PluginInterfaces.V2;
using VVVV.Utils.VColor;
using VVVV.Utils.VMath;

using VVVV.Core.Logging;

  • endregion usings

namespace VVVV.Nodes
{
class SerialInstance : IDisposable
{
bool FNeedsSend = true;
string FMessageTx = “”;

	bool FNewReceive = true;
	string FMessageRx = "";
	
	string FStatus = "";
	
	bool FRunning = false;
	Thread FThread = null;
	
	SerialPort FSerialPort = null;
	
	//settings
	string FPort = "";
	int FBaud = 0;
	
	public void Dispose()
	{
		Close();
	}
	
	public void Initialise(string port, int baud)
	{
		if (FPort == port && FBaud == baud)
			return;
		
		Close();
		
		FPort = port;
		FBaud = baud;
		
		Open();
	}
	
	public void Open()
	{
		try
		{
			FSerialPort = new SerialPort(FPort, FBaud, Parity.None, 8, StopBits.One);
			FSerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived);
			FSerialPort.Open();
			FStatus = "Port opened";
		}
		catch
		{
			Close();
			FStatus = "Port open failed";
			return;
		}
		
		FRunning = true;
		FThread = new Thread(fnThread);
		FThread.Start();
	}
	
	public void Send(string message)
	{
		FMessageTx = message;
		FNeedsSend = true;
	}
	
	public string Receive()
	{
		if (FNewReceive)
		{
			FNewReceive = false;
			return FMessageRx;
		} else
			return "";
	}
	
	public bool IsOpen
	{
		get
		{
			return FSerialPort.IsOpen && FRunning;
		}
	}
	
	public string Status
	{
		get
		{
			return FStatus;
		}
	}
	
	private void DataReceived(object sender, SerialDataReceivedEventArgs e)
	{
		FStatus = "Data received";
		
		SerialPort sp = (SerialPort)sender;
		if (!FNewReceive)
			FMessageRx = sp.ReadExisting();
		else
			FMessageRx = FMessageRx + sp.ReadExisting();
	}
	
	private void fnThread()
	{
		while (FRunning)
		{
			if (FNeedsSend)
			{
				FSerialPort.Write(FMessageTx);
				FNeedsSend = false;
			}
			
			Thread.Sleep(1);
		}
	}
	
	void Close()
	{
		if (!FRunning)
			return;
		
		FRunning = false;
		FThread.Join(100);
		FSerialPort.Close();
		FSerialPort.Dispose();
	}
}
#region PluginInfo
[PluginInfo(Name = "SerialPort", Category = "Devices", Version = "Threaded", Help = "Send and receive data over the serial port in a seperate thread per port", Tags = "")](PluginInfo(Name = "SerialPort", Category = "Devices", Version = "Threaded", Help = "Send and receive data over the serial port in a seperate thread per port", Tags = ""))
#endregion PluginInfo
public class ThreadedDevicesRS232Node : IPluginEvaluate, IDisposable
{
	#region fields & pins
	[Input("Input", DefaultString = "hello c#")](Input("Input", DefaultString = "hello c#"))
	ISpread<string> FInput;

	[Input("Port", DefaultString = "COM1")](Input("Port", DefaultString = "COM1"))
	ISpread<string> FPort;
	
	[Input("Baud", DefaultValue = 115200)](Input("Baud", DefaultValue = 115200))
	ISpread<int> FBaud;
	
	[Input("Send", IsBang = true)](Input("Send", IsBang = true))
	ISpread<bool> FSend;
	
	[Output("Output")](Output("Output"))
	ISpread<string> FOutput;
	
	[Output("Open")](Output("Open"))
	ISpread<bool> FOpen;
	
	[Output("Status")](Output("Status"))
	ISpread<string> FStatus;

	[Import()](Import())
	ILogger FLogger;
	
	Spread<SerialInstance> FInstances = new Spread<SerialInstance>(0);
	#endregion fields & pins
	
	public void Dispose()
	{
		FLogger.Log(LogType.Debug, "Disposing SerialPort node");
		for (int i=0; i<FInstances.SliceCount; i++)
		{
			FInstances[i](i).Dispose();
		}
	}
	
	//called when data for any output pin is requested
	public void Evaluate(int SpreadMax)
	{
		FOutput.SliceCount = SpreadMax;
		FOpen.SliceCount = SpreadMax;
		FStatus.SliceCount = SpreadMax;
		
		Resize(SpreadMax);
		
		for (int i=0; i<SpreadMax; i++)
		{
			if (FSend[i](i))
			{
				if (!FInstances[i](i).IsOpen)
					FInstances[i](i).Open();
				
				FInstances[i](i).Send(FInput[i](i));
			}
			FOutput[i](i) = FInstances[i](i).Receive();
			FOpen[i](i) = FInstances[i](i).IsOpen;
			FStatus[i](i) = FInstances[i](i).Status;
		}

		//FLogger.Log(LogType.Debug, "Logging to Renderer (TTY)");
	}
	
	private void Resize(int count)
	{
		FInstances.SliceCount = count;
		for (int i=0; i<count; i++)
		{
			if (FInstances[i](i) == null)
				FInstances[i](i) = new SerialInstance();
			
			FInstances[i](i).Initialise(FPort[i](i), FBaud[i](i));
		}
	}
}

}

When I realised I couldn’t come up with good jokes of my own I started building a m4dvoodoo doll to keep my shoutbox stats high

see, that’s just what i’m talkin about. damn, mrboni how many times do i have to tell you that it’s not fair to users who try to contribute original silly jokes??!?

since when was shoutbox fair m4d?

Touché, mrboni!

i spoke to sugokuGENKI in a séance.
he said it’s possibly because it’s sending ascii rather than binary in that code

he asked if i could help you out later on
he’s such an asshole

I just tested vvvv in multi-threaded mode following the instructions here:

https://discourse.vvvv.org/t/7634

And yes, Sugo^Ahhem^ Elliot’s idea of multi threading the RS232 node works really well! I have four processors and five Arduinos though, so a bit of jiggle should get it working from one machine.

Elliot if you have the time to develop or anyone else for that matter has the time to finish off this plugin I will happily pay for development then it can be released to the community =)

Ok I’ve found I don’t need to use setprocessaffinitymask-(windows) simply setting the args.txt to /allowmultiple then opening up a second instance of vvvv works the same if using setprocessaffinitymask-(windows) or not. Then again the vvvv patches I’m running are not heavy!

@gaz - by default vvvv is allowed to run across all threads

e.g. on my i7 quad, when using EmguCV i can have activity on all 8 virtual cores without any special setup of the vvvv executable

secondly
you can have more threads than processors
in fact, rs232 communication is exactly the type of application which threads were originally invented for (long before muilti-core processors).
if a task involves running something that the cpu has to wait for (e.g. a serial port), then it’s a good idea to put that in a seperate thread so that your main app can get on with what its doing whilst it’s waiting for the serial port.
in this instance, the serial port is not necessarilly taking up any CPU, but in that thread the CPU is being blocked waiting for something.
i.e. you’ve not used up 100% of that cpu, so you can put more threads on the same cpu without any performance hit

the only reason why you’d need to worry about needing more cpu is if task manager was reporting that all your cores had 100% activity

in fact, a screen shot of task manager when it’s running slow would be useful!

elliot

ok another note

if you’re sending the same signal to all arduino’s (which you’re probably not)
then you could use rs485, send once and receive on all

rs485 = one pc COM port to many arduinos
rs232 = one pc COM port to one arduino

you’d also need to check the baud rate of the rs485 hardware to make sure it matched your 115200(?)

to get rs485 working an easy way would be:

Then it’s a case of doing some trivial wiring
The rs485 device appears as a serial device (e.g. COM4)
Whenever you send on that, it’ll receive on all of your arduino’s Serial.Read()

Elliot,

thanks a lot for your help and easy to understand explanations! I am sending the same data to all Arduino’s and having them choose which part of the data to use. However, the input data coming from the Arduinos is different - so not sure if your RS485 method would work or not?

We’re running out of time on the project, and I’m getting good results by opening up multiple instances of vvvv to run the RS232 node in a separate thread and then sending the data to the main patch via UDP.