My application involves sending a command to a Cerberus over the serial port and the Cerberus responding back with the results. Pretty simple, really. I don’t expect the Cerberus to do anything that it isn’t asked to do. It only takes commands and responds to them.
I poured over every “serial” post in these forums and tried to avoid data overruns and other issues. These things popped up a lot. So I did a lot of experimentation. The real problem seems to be writing to the port to send the data. One forum post pointed out that the transmit buffer was only 256 bytes, so that would make sense.
So I wrote my code (based on RoSchmi’s great contributions - thanks!) and it’s shown below.
I understand that it would be a good idea to have the receiving function of the serial port running in a separate thread. That thread would raise events when it had received complete command strings. The main thread would respond to those events by doing the requested processing. So far so good.
Then the main thread would raise an event and a 2nd thread, a sending thread, would deliver the results to the serial port.
I tried delegates, events and every other trick I’d seen or heard of to get that to work and I never could. I don’t have the C# skills/experience.
So what resulted is the code below. I ran 500,000 iterations through it with no data loss and ended up getting about 153 responses per second at 38400. Even adding back in the processing of the requests and it’ll be more than fast enough.
But I worry about the architecture. Is this the right approach and/or is this code going to cause me trouble? Every bone in my body says I’m not doing it in a “best practices” way, but it works.
Thanks in advance, guys. I’ve only gotten this far because of the generosity of forum posters.
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.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Serial = Gadgeteer.Interfaces.Serial;
using System.Text;
namespace CerberusThroughputTest2 {
public partial class Program {
Thread receivingThread;
int counter = 0;
int requestResponseCounter = 0;
void ProgramStarted()
{
Debug.Print("Program Started");
GT.Timer SendMessageTimer = new GT.Timer(1000, GT.Timer.BehaviorType.RunContinuously);
SendMessageTimer.Tick += SendMessage_Tick;
SetupSerialPort();
SetupReceivingThread();
SendMessageTimer.Start();
}
void SetupSerialPort()
{
try
{
this.rs232.Initialize(38400,
Serial.SerialParity.None,
Serial.SerialStopBits.One,
8,
Serial.HardwareFlowControl.NotRequired);
}
catch (Exception e)
{
Debug.Print(e.ToString());
}
if (!rs232.serialPort.IsOpen)
{
rs232.serialPort.Open();
}
}
void SetupReceivingThread()
{
SerialReader serialReaderTarget = new SerialReader(rs232);
serialReaderTarget.CommandReceived += CommandReceived;
ThreadStart serialReaderThreadStart = new ThreadStart(serialReaderTarget.Start);
receivingThread = new Thread(serialReaderThreadStart);
receivingThread.Start();
}
void CommandReceived(string data)
{
requestResponseCounter++;
ProcessingSingleCommand(data);
}
void SendMessage_Tick(GT.Timer timer)
{
counter++;
char_Display.SetCursor(0, 0);
char_Display.PrintString(counter.ToString());
}
void ProcessingSingleCommand(string command)
{
SendLine("OK12345678");
}
void SendLine(string response)
{
rs232.serialPort.WriteLine(response);
while (rs232.serialPort.BytesToWrite > 0)
{
Thread.Sleep(5);
}
Debug.Print("Sent this line: " + response);
}
}
}
And this is the serial class:
using System;
using Microsoft.SPOT;
using GTM = Gadgeteer.Modules;
using GT = Gadgeteer;
using Serial = Gadgeteer.Interfaces.Serial;
public delegate void SendLineHandler(string data);
namespace CerberusThroughputTest2 {
public class SerialReader {
static GTM.GHIElectronics.RS232 rs232;
SerialBuffer mySerialBuffer;
string dataLine = string.Empty;
public event SendLineHandler CommandReceived;
public SerialReader(GTM.GHIElectronics.RS232 rs232p)
{
rs232 = rs232p;
mySerialBuffer = new SerialBuffer(256);
}
public void Start()
{
rs232.serialPort.DataReceived += serialPort_DataReceived;
}
public void Stop()
{
rs232.serialPort.DataReceived -= serialPort_DataReceived;
}
void serialPort_DataReceived(GT.Interfaces.Serial sender, System.IO.Ports.SerialData data)
{
var toRead = sender.BytesToRead;
byte[] v = new byte[toRead];
sender.Read(v, 0, toRead);
mySerialBuffer.LoadSerial(v, 0, v.Length);
while ((dataLine = mySerialBuffer.ReadLine()) != null)
{
CommandReceived(dataLine);
}
}
}
}