I was puzzled as well. I cleaned up the code a little and found the case where it behaved strangely. I’m pretty sure it is the other serial port (in Line mode) that is causing interference, things speed up after that test set finishes.
Here are a few sample results:
// 5 x messages, delay
Test 0: 1000/1000 messages from COM4 to COM1 with 0 errors, average latency=190 (rx:DataReceived)
Test 1: 1000/1000 messages from COM1 to COM4 with 0 errors, average latency=232 (rx:LineReceived)
Test 0: 1000/1000 messages from COM4 to COM1 with 0 errors, average latency=226 (rx:Polling)
Test 1: 1000/1000 messages from COM1 to COM4 with 0 errors, average latency=242 (rx:LineReceived)
// 5 x messages, sync
Test 0: 500/500 messages from COM4 to COM1 with 0 errors, average latency=99 (rx:Polling)
Test 1: 500/500 messages from COM1 to COM4 with 0 errors, average latency=255 (rx:LineReceived)
Test 0: 500/500 messages from COM4 to COM1 with 0 errors, average latency=406 (rx:DataReceived)
Test 1: 500/500 messages from COM1 to COM4 with 0 errors, average latency=205 (rx:LineReceived)
Here is the code set-up for the odd case: (the RS232 ports are named rs232_a and rs232_b)
using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Interfaces;
namespace CobraII_serial_test
{
public partial class Program
{
string port_a; // test 0 tx (test 1 rx)
string port_b; // test 1 tx (test 0 rx)
// This method is run when the mainboard is powered up or reset.
void ProgramStarted()
{
Debug.EnableGCMessages(false);
rs232_a.Initialize(57600, Serial.SerialParity.None, Serial.SerialStopBits.One, 8, Serial.HardwareFlowControl.NotRequired);
rs232_b.Initialize(57600, Serial.SerialParity.None, Serial.SerialStopBits.One, 8, Serial.HardwareFlowControl.NotRequired);
port_a = rs232_a.serialPort.PortName;
port_b = rs232_b.serialPort.PortName;
// setup rx for port a (line received)
rs232_a.serialPort.Close(); // need port closed when setting AutoReadLineEnabled - set_ReadTimeout gets called
rs232_a.serialPort.AutoReadLineEnabled = true;
rs232_a.serialPort.LineReceived += new Serial.LineReceivedEventHandler(Test_LineReceived);
rs232_a.serialPort.Open();
// setup rx for port b (data received)
// use this line for data received
rs232_b.serialPort.DataReceived += serialPort_DataReceived;
// use these lines for polling
//Thread pollThreadB = new Thread(() => serialPort_Poll(rs232_b.serialPort));
//pollThreadB.Start();
// use these lines for line received
//rs232_b.serialPort.Close(); // need port closed when setting AutoReadLineEnabled - set_ReadTimeout gets called
//rs232_b.serialPort.AutoReadLineEnabled = true;
//rs232_b.serialPort.LineReceived += new Serial.LineReceivedEventHandler(ReadLine1);
//rs232_b.serialPort.Open();
Thread.Sleep(1000); // make sure things are settled before starting the tests?
// these need to be on threads - timers or the main thread don't work because
// they can't happen in parallel with the EventHandlers
data[0] = new Data("rx:DataReceived");
Thread TestThread0 = new Thread(() => Test_Transmit(rs232_a.serialPort)); // tx:a rx:b
TestThread0.Start();
data[1] = new Data("rx:LineReceived");
Thread TestThread1 = new Thread(() => Test_Transmit(rs232_b.serialPort)); // tx:b rx:a
TestThread1.Start();
}
class Data
{
const int TEST_SIZE = 500;
public string[] message = new string[TEST_SIZE]; // message text (check for transmission errors)
public DateTime[] startTime = new DateTime[TEST_SIZE]; // rx time (to compute latency)
public long[] lantency = new long[TEST_SIZE]; // ms
public int rxCount = 0;
public int errCount = 0;
public long totalLatency = 0;
public Serial rxPort = null; // confirm rx port
public string info = "";
public Data(string info)
{
this.info = info;
}
}
Data[] data = new Data[2];
private void Test_Transmit(Serial sender)
{
int test = (sender.PortName == port_a) ? 0 : 1; // tx test
Thread.Sleep(1000);
Debug.Print("Starting test "+test);
for (int i = 0; i < data[test].message.Length; i++)
{
string msg = "#M" + i + " T=" + DateTime.Now.Ticks.ToString();
data[test].message[i] = msg;
sender.WriteLine(msg);
Mainboard.SetDebugLED(true); // blink lights for fun
data[test].startTime[i] = DateTime.Now;
// play with the traffic pattern
if ((i % 5) == 0) // send 5 messages and then wait for them to be received
while (data[test].rxCount < i + 1)
Thread.Sleep(20);
//if ((i % 5) == 0) Thread.Sleep(500); // burst of 5 messages and then delay
}
// wait for all messages (or no change for .5 seconds)
int lastCount = -1;
while ((data[test].rxCount < data[test].message.Length) && (data[test].rxCount != lastCount))
{
lastCount = data[0].rxCount;
Thread.Sleep(500);
}
long ms = (data[test].totalLatency / data[test].message.Length);
Debug.Print("Test "+test+": " + data[test].rxCount +"/"+ data[test].message.Length + " messages from " + sender.PortName + " to "
+ (data[test].rxPort != null ? data[test].rxPort.PortName : "?")
+ " with " + data[test].errCount + " errors, average latency=" + ms + " ("+data[test].info +")");
// print out latencies for both tests
if (test == 0)
{
while ((data[1].rxCount < data[1].message.Length))
Thread.Sleep(500);
Thread.Sleep(500); // let other message finish
for (int i = 0; i < data[test].message.Length; i++)
{
Debug.Print("M" + i + " " + data[0].lantency[i] + " \t" + data[1].lantency[i]);
}
}
}
private void Test_LineReceived(Serial sender, string line)
{
int test = (sender.PortName == port_a) ? 1 : 0; // rx test
int i = Convert.ToInt32(line.Substring(2, line.IndexOf(' ') - 2));
TimeSpan timediff = DateTime.Now - data[test].startTime[i];
data[test].lantency[i] = timediff.Ticks / TimeSpan.TicksPerMillisecond;
data[test].totalLatency += data[test].lantency[i];
Mainboard.SetDebugLED(false);
if (i == 0)
Debug.Print("Test" +test+" sample - " + line + " :" + data[test].lantency[i]+"ms");
if (data[test].message[i] != line)
{
Debug.Print("Data mismatch: " + data[test].message[i] + " => " + line);
data[test].errCount++;
}
data[test].rxCount++;
data[test].rxPort = sender;
}
void serialPort_Poll(Serial sender)
{
System.IO.Ports.SerialData data = new System.IO.Ports.SerialData(); // dummy for data call
while (true)
{
while (sender.BytesToRead > 0)
serialPort_DataReceived(sender, data);
Thread.Sleep(5);
}
}
bool[] dataReceivedRunning = {false, false};
string[] serialLine = {"",""};
void serialPort_DataReceived(Serial sender, System.IO.Ports.SerialData data)
{
int test = (sender.PortName == port_a) ? 1 : 0; // rx test
// only run one at a time
if (dataReceivedRunning[test]) return;
dataReceivedRunning[test] = true;
int total_bytes = 0;
// turn interrupt off for now
int read_bytes = sender.BytesToRead;
while (read_bytes > 0)
{
byte[] readBuf = new byte[read_bytes];
int read = sender.Read(readBuf, 0, read_bytes);
total_bytes += read;
for (int i = 0; i < read; i++)
{
if (readBuf[i] == 13) { } // chomp newline
else if (readBuf[i] == 10)
{ // split commands on /r
Test_LineReceived(sender, serialLine[test]);
serialLine[test] = "";
}
else
{
serialLine[test] += (char)readBuf[i];
}
}
read_bytes = sender.BytesToRead;
}
// hook back up
dataReceivedRunning[test] = false;
}
}
}