After another week of playing with this on and off things are coming along nicely. I picked up a USBSerial module from GHI and it has replaced my homemade setup. I also got a pulse counting module on the same order so I can try out some sort of encoder as an MPG later on.
After trying different ideas with getting data into and out of Mach 4 it was apparent I was making life hard on myself. There are different types of registers:
[ul]Input and Output are bool and can be mapped as switches, etc[/ul]
[ul]Encoder is a long int and meant to be mapped in as an actual motor/axis encoder or MPG[/ul]
[ul]General are ‘general purpose’ and can’t be automatically mapped[/ul]
By mapping I mean that the Mach 4 plug-in (calling it EZIO for now) takes the data from the Spider2 and writes it into the Input, Encoder registers. Then in Mach 4 you go into the configuration and say ‘I want the jog left signal to come from EZIO Input Register 1’ and then it ‘just works’. In a similar manner you can map Mach 4 outputs to the EZIO Output registers and then the plug-in sends them to the Spider2. Given this built in and easy way to do things in Mach I just created a fixed set of Inputs, Outputs, Encoder and General registers that for the plug-in.
One annoying thing I found yesterday is that the Gadgeteer serial port wrapper does not expose the ‘Error’ event. Typically I have seen buffer overflow type errors when entering a break point on the Spider2 while Mach 4 keeps sending packets, or vice versa. To combat this I now check the size of the TX and RX buffer and just purge them if they are too full.
The simple packet format is (from Mach 4 point of view) :
// Decode the received reply string
// packet format-> :STRING1:STRING2:STRING3:
// inputs <- ":I0=b:I1=b:I2=b...I7=b:"
// encoders <- ":E0=d:E1=d:E2=d:E3=d:"
// (4-7 input) general <- ":G4=b:G4=b:G4=b:G4=b:"
// if packet does not start/end with ':' error, throw away
So if a packet does not start and end with a ‘:’ it is pitched. The line delimiter is a ‘\n’ but it is stripped off before the decoder is called. The decode method from the Spider2 is below. Some basic checks are done to try and make sure that each ‘word’ in the packet, i.e. “I0=1” makes sense before it is processed. You can see I commented out my initial int.parse and made a separate function to catch errors and tell me if the conversion was OK. Last night I was wondering what happens is I do the String.Split on a word that has no ‘=’ in it? I’ll have to try it and see but perhaps a check before the split to see if an ‘=’ is there. It would still be possible for a device to send non-sense like ‘abc=qwe’ so I’m not sure the best way to check for word ‘sensibility’ in general. (also note that I just changed the Spider2 code yesterday so that the serial Rx thread pushes received packets into a Queue and a timer even in the main thread pops them off the Queue and decodes, updating the appropriate arrays. The code that does this “xPos = machAxisPos[0];” should not be in the decode method. )
I suspect in the end I’ll do a CRC on the packet to try to avoid processing corrupted data, that should be relatively fast and easy for folks to replicate even on different hardware.
/// <summary>
/// Deocde RXd data packet, update local values
/// We read everything as strings
/// </summary>
/// <param name="packet"></param>
/// <returns>True if good packet</returns>
// Decode Tx packet from Mach 4
// axis positions -> ":M0=f:M1=f:M2=f:M3=f:" -> machAxisPos
// outputs -> ":O0=b:O1=b;O2=b...O7=b:" -> machOutputs
// (0-3 output) general -> ":G0=b:G1=b:G2=b:G3=b:" -> machGeneral
private bool decodeRXpacket(string packet)
{
bool value = false;
// error if does not start/end with ':'
if (packet[0] == ':' && packet[packet.Length - 1] == ':')
{
value = true;
string[] data = packet.Split(':');
foreach (string word in data)
{
if (word.Length > 3) // a valid word has to have at least 4 characters
{
string[] nameValue = word.Split('=');
char nameType = nameValue[0][0];
int nameIndex = 0;//int.Parse(nameValue[0].Substring(1, nameValue[0].Length-1));
bool valOK = getInt(nameValue[0].Substring(1, nameValue[0].Length - 1), ref nameIndex);
if (valOK)
{
if (nameType == 'M' && (nameIndex >= 0 && nameIndex < 8))
{
// handles machAxisPos
machAxisPos[nameIndex] = nameValue[1];
}
else if (nameType == 'O' && (nameIndex >= 0 && nameIndex < 8))
{
// handles machOutputs
machOutputs[nameIndex] = nameValue[1];
}
else if (nameType == 'G' && (nameIndex >= 0 && nameIndex < 4))
{
// handles machGeneral
machGeneral[nameIndex] = nameValue[1];
}
xPos = machAxisPos[0];
yPos = machAxisPos[1];
zPos = machAxisPos[2];
}
}
}
}
return value;
}