Thanks for the reminder.
Here is the library:
Code: Select all
/*
* Matthew Richardson
* matthewrichardson37<at>gmail.com
* http://mattallen37.wordpress.com/
* Jul. 7 / 2011
*
* You may use this code as you wish, provided you give credit where it's due.
*
* This is library for easy use of RS485 master/slave transactions.
*/
/*
My RS485 protocol data specs:
Sending byte array looks like this:
Senders address, Target address, Commands(flags), Data1, Data2, ..., Data n
Sending Ping looks like this:
Senders address, Target address, Ping Flag Value
Acknowledgement looks like this:
Senders address, Target address, Acknowledgement Flag Value
Returned values
-2 timeout
-1 something went wrong, probably not right address
0 not acknowledged
Command = success
*/
#define Master485Address 0x00 //The address of this NXT
#define Slave1485Address 0x01 //Slave number 1's address
//Master to slave flags
#define PingValue 0x08 //Return an Acknowledgement
#define WriteValue 0x10 //Write registers
#define ReadBackValue 0x20 //Return Values
//Slave to master flags
#define Acknowledge 0x01 //Result returned from a ping or write-only command
void SendRS485ByteArray (byte SenderAddress, byte TargetAddress, byte Command, byte SendData[]){
byte SendCompleteData[];
byte SendCompleteDataLength=ArrayLen(SendData)+3;
ArrayInit(SendCompleteData, 0, SendCompleteDataLength);
SendCompleteData[0]=SenderAddress;
SendCompleteData[1]=TargetAddress;
SendCompleteData[2]=Command;
byte i=0;
repeat(ArrayLen(SendData)){
SendCompleteData[i+3]=SendData[i];
i++;
}
string StringSendData = FlattenVar(SendCompleteData); //Flatten it into a string
SendRS485String(StringSendData); //Send the string
until(HSOutputBufferOutPtr() == StrLen(StringSendData)+1); //Wait until the string has been sent
}
int MasterReadRS485ByteArray (byte My485Address, byte BytesToReceive, int TimeOut, byte & InArray[]){
bool TimedOut=false;
byte TimePassed=0; //Reset the timeout counter
byte cnt;
string InString;
byte RawInArray[];
byte RawInArraySize=BytesToReceive+3;
ArrayInit(RawInArray, 0, RawInArraySize);
until (HSInputBufferInPtr() >= (BytesToReceive+4) || TimedOut){ //Until whole string is received
Wait(1); //Wait 1 ms
if (TimeOut==0){ //If TimeOut equals 0, then don't time out.
TimedOut=false;
}
else{
TimePassed++; //increase the timer
if (TimePassed>=TimeOut){
TimedOut=true;
}
}
}
if (TimedOut){ //If the timeout took place
return -2;
}
else{ //If the timeout did not take place
cnt=HSInputBufferInPtr();
GetHSInputBuffer(0, cnt, InString); //Read the buffer
SetHSInputBufferInPtr(0);
UnflattenVar(InString, RawInArray); //Unflatten the string into an array
byte SendersAddress=RawInArray[0];
byte TargetsAddress=RawInArray[1];
byte Command =RawInArray[2];
if (TargetsAddress==My485Address) //If the RdataC[0] contains this NXT's address, read the rest
{
ArraySubset(InArray, RawInArray, 3, NA);
return Command;
}
}
return -1;
}
void MasterSendPing(byte My485Address, byte TargetAddress){
byte PingData[3];
ArrayBuild(PingData, My485Address, TargetAddress, PingValue); //Build the array to send
string StringPingData = FlattenVar(PingData); //Flatten it into a string
SendRS485String(StringPingData); //Send the string
until(HSOutputBufferOutPtr() == StrLen(StringPingData)+1); //Wait until the string has been sent
}
int MasterCheckAcknowledgement(byte My485Address, int TimeOut){
bool TimedOut=false;
byte TimePassed=0; //Reset the timeout counter
string InString;
byte RawInArray[];
ArrayInit(RawInArray, 0, 3);
until (HSInputBufferInPtr() == 4 || TimedOut){ //Until whole string is received
Wait(1); //Wait 1 ms
if (TimeOut==0){ //If TimeOut equals 0, then don't time out.
TimedOut=false;
}
else{
TimePassed++; //increase the timer
if (TimePassed>=TimeOut){
TimedOut=true;
}
}
}
if (TimedOut){ //If the timeout took place
return -2;
}
else{ //If the timeout did not take place
GetHSInputBuffer(0, 4, InString); //Read the buffer
SetHSInputBufferInPtr(0);
UnflattenVar(InString, RawInArray); //Unflatten the string into an array
byte SendersAddress=RawInArray[0];
byte TargetsAddress=RawInArray[1];
byte Command =RawInArray[2];
if (TargetsAddress==My485Address) //If the TargetsAddress contains this NXT's address, read the rest
{
return Command;
}
}
return -1;
}
int MasterPing(byte My485Address, byte TargetAddress, int TimeOut){
MasterSendPing(My485Address, TargetAddress);
return MasterCheckAcknowledgement(My485Address, TimeOut);
}
void SlaveAcknowledge(byte My485Address, byte TargetAddress){
byte AcknowledgeData[3];
ArrayBuild(AcknowledgeData, My485Address, TargetAddress, Acknowledge); //Build the array to send
string StringAcknowledgeData = FlattenVar(AcknowledgeData); //Flatten it into a string
SendRS485String(StringAcknowledgeData); //Send the string
until(HSOutputBufferOutPtr() == StrLen(StringAcknowledgeData)+1); //Wait until the string has been sent
}
int SlaveReadAndRespondRS485ByteArray (byte My485Address, byte BytesToReceive, int TimeOut, byte SendValues[], byte & InArray[]){
byte GoOn=0;
int TimePassed=0; //Reset the timeout counter
byte cnt;
string InString;
byte RawInArray[];
until (HSInputBufferInPtr() >= (BytesToReceive+4) || GoOn!=0){ //Until whole string is received
Wait(1);
if(HSInputBufferInPtr()==4){ //If only a ping
Wait(3);
if(HSInputBufferInPtr()==4){ //Confirm only a ping
GoOn=1;
}
}
if (TimeOut!=0){ //If TimeOut equals 0, then don't time out.
TimePassed++; //increase the timer
if (TimePassed>=TimeOut){
GoOn=2;
}
}
}
if (GoOn==2){ //If TimedOut
return -2; // Return -2
}
else{ //If it didn't time out
if (GoOn==0){
byte RawInArraySize=BytesToReceive+3;
ArrayInit(RawInArray, 0, RawInArraySize);
cnt=HSInputBufferInPtr();
GetHSInputBuffer(0, cnt, InString); //Read the buffer
SetHSInputBufferInPtr(0);
UnflattenVar(InString, RawInArray); //Unflatten the string into an array
byte SendersAddress=RawInArray[0];
byte TargetsAddress=RawInArray[1];
byte Command =RawInArray[2];
if (TargetsAddress==My485Address)
{
if (ReadBackValue==(Command&ReadBackValue)){
SendRS485ByteArray (My485Address, SendersAddress, Command, SendValues);
}
if (WriteValue==(Command&WriteValue)){
ArraySubset(InArray, RawInArray, 3, NA);
}
return Command;
}
}
if (GoOn==1){
ArrayInit(RawInArray, 0, 3);
cnt=HSInputBufferInPtr();
GetHSInputBuffer(0, cnt, InString); //Read the buffer
SetHSInputBufferInPtr(0);
UnflattenVar(InString, RawInArray); //Unflatten the string into an array
byte SendersAddress=RawInArray[0];
byte TargetsAddress=RawInArray[1];
byte Command =RawInArray[2];
if (TargetsAddress==My485Address)
{
if (PingValue==(Command&PingValue)){
SlaveAcknowledge(My485Address, SendersAddress);
return Command;
}
}
}
}
return -1;
}
Here is an example master program:
Code: Select all
/*
* Matthew Richardson
* matthewrichardson37<at>gmail.com
* http://mattallen37.wordpress.com/
* Jun. 23 / 2011
*
* You may use this code as you wish, provided you give credit where it's due.
*
* This is an example master program for my RS485 library.
*/
/*
Returned values
-2 timeout
-1 something went wrong, probably not right address
0 not acknowledged
Command = success
*/
#include "MyRS485 lib.nxc" //Note, the main library
#define My485Address 0x00 //The address of this NXT
#define Master485Address 0x00 //The address of the master
#define Slave1485Address 0x01 //Slave number 1's address
//Master to slave flags
#define PingValue 0x08 //Return an Acknowledgement
#define WriteValue 0x10 //Write registers
#define ReadBackValue 0x20 //Return Values
//Slave to master flags
#define Acknowledge 0x01 //Result returned from a ping or write-only command
#define BytesToReceive 10
#define TimeOut 15
byte Sdata[]; //Array to send data
byte Rdata[BytesToReceive];
byte Svar0,Svar1,Svar2,Svar3,Svar4,Svar5,Svar6,Svar7,Svar8,Svar9;
int result;
task display() //Display the results
{
while(true)
{
ClearScreen();
NumOut(0, LCD_LINE1,Rdata[0]);
NumOut(0, LCD_LINE2,Rdata[1]);
NumOut(0, LCD_LINE3,Rdata[2]);
NumOut(0, LCD_LINE4,Rdata[3]);
NumOut(0, LCD_LINE5,Rdata[4]);
NumOut(50, LCD_LINE1,Rdata[5]);
NumOut(50, LCD_LINE2,Rdata[6]);
NumOut(50, LCD_LINE3,Rdata[7]);
NumOut(50, LCD_LINE4,Rdata[8]);
NumOut(50, LCD_LINE5,Rdata[9]);
NumOut(0, LCD_LINE8,result);
Wait(50);
}
}
task RS()
{
while (true)
{
Svar0=Random();
Svar1=Random();
Svar2=Random();
Svar3=Random();
Svar4=Random();
Svar5=Random();
Svar6=Random();
Svar7=Random();
Svar8=Random();
Svar9=Random();
ArrayBuild(Sdata, Svar0,Svar1,Svar2,Svar3,Svar4,Svar5,Svar6,Svar7,Svar8,Svar9);
SendRS485ByteArray (My485Address, Slave1485Address, (WriteValue|ReadBackValue), Sdata);
result=MasterReadRS485ByteArray (My485Address, BytesToReceive, TimeOut, Rdata);
//result=MasterPing(My485Address, Slave1485Address, TimeOut);
if (result==-1){
PlayToneEx(500,100,3,0);
}
if (result==-2){
PlayToneEx(1000,10,1,0);
}
Wait(500);
}
}
task main()
{
SetSensorType(IN_4, SENSOR_TYPE_HIGHSPEED); //Set up for RS485
SetHSState(HS_INITIALISE); // ''
SetHSFlags(HS_UPDATE); // ''
SetHSSpeed(HS_BAUD_921600);
SetHSInputBufferInPtr(0);
SetHSInputBufferOutPtr(0);
start display;
start RS;
}
And an example slave program:
Code: Select all
/*
* Matthew Richardson
* matthewrichardson37<at>gmail.com
* http://mattallen37.wordpress.com/
* Jun. 23 / 2011
*
* You may use this code as you wish, provided you give credit where it's due.
*
* This is an example slave program for my RS485 library.
*/
/*
My RS485 protocol data specs:
Sending byte array looks like this:
Senders address, Target address, Commands/flags, Data1, Data2, ..., Data n
Sending Ping looks like this:
Senders address, Target address, Ping Flag Value
Acknowledgement looks like this:
Senders address, Target address, Acknowledgement Flag Value
Returned values
-2 timeout
-1 something went wrong
0 not acknowledged
Command = success
*/
#include "MyRS485 lib.nxc" //Note, the main library
#define My485Address 0x01 //The address of this NXT
#define Master485Address 0x00 //The address of the master
#define Slave1485Address 0x01 //Slave number 1's address
//Master to slave flags
#define PingValue 0x08 //Return an Acknowledgement
#define WriteValue 0x10 //Write registers
#define ReadBackValue 0x20 //Return Values
//Slave to master flags
#define Acknowledge 0x01 //Result returned from a ping or write-only command
#define BytesToReceive 10
#define TimeOut 500
byte Sdata[];
byte Rdata[BytesToReceive];
byte Svar0,Svar1,Svar2,Svar3,Svar4,Svar5,Svar6,Svar7,Svar8,Svar9;
int result;
task display()
{
while(true)
{
ClearScreen();
NumOut(0, LCD_LINE1,Rdata[0]);
NumOut(0, LCD_LINE2,Rdata[1]);
NumOut(0, LCD_LINE3,Rdata[2]);
NumOut(0, LCD_LINE4,Rdata[3]);
NumOut(0, LCD_LINE5,Rdata[4]);
NumOut(50, LCD_LINE1,Rdata[5]);
NumOut(50, LCD_LINE2,Rdata[6]);
NumOut(50, LCD_LINE3,Rdata[7]);
NumOut(50, LCD_LINE4,Rdata[8]);
NumOut(50, LCD_LINE5,Rdata[9]);
NumOut(0, LCD_LINE8,result);
Wait(75);
}
}
task RS()
{
while (true)
{
Svar0=Random();
Svar1=Random();
Svar2=Random();
Svar3=Random();
Svar4=Random();
Svar5=Random();
Svar6=Random();
Svar7=Random();
Svar8=Random();
Svar9=Random();
ArrayBuild(Sdata, Svar0,Svar1,Svar2,Svar3,Svar4,Svar5,Svar6,Svar7,Svar8,Svar9);
result=SlaveReadAndRespondRS485ByteArray (My485Address, BytesToReceive, TimeOut, Sdata, Rdata);
if (result==-1){
PlayToneEx(500,100,3,0);
}
if (result==-2){
PlayToneEx(1000,10,1,0);
}
}
}
task main()
{
SetSensorType(IN_4, SENSOR_TYPE_HIGHSPEED);
SetHSState(HS_INITIALISE);
SetHSFlags(HS_UPDATE);
SetHSSpeed(HS_BAUD_921600);
SetHSInputBufferInPtr(0);
SetHSInputBufferOutPtr(0);
start display;
start RS;
}
I think this all worked fine the last time I tried it.
Basically, the master sends it's own address, the target address, a command (ping and or write values and or return values), and an array of user data using "SendRS485ByteArray". The master then awaits a response from the slave, in the receive function "MasterReadRS485ByteArray". If there was a timeout, the function will return -2. If it succeeded, it will return the value of the command byte that the slave sent in return (which is the command the master sent to the slave, requesting a response).
On the slave side of things, the program waits for a message to come in (using SlaveReadAndRespondRS485ByteArray). It will keep returning -2 indicating a timeout, until it gets a message from the master. I suggest you put it in a loop so that the master can access it at just about any time. You provide an array for it to send, assuming the master asks for it, and an array to store values coming from the master.
Make sure you set BytesToReceive to the number of bytes you want the NXT to be looking for. This is the number of user-bytes, not total bytes. According to the documentation of the EFW, it should support 60+ user-bytes (a 60+ element byte array of user data). In the examples above, I used 10.
Make sure you also set TimeOut to proper values. The slave should have a high timeout, and the master relatively short. I usually use 500 and 15 ms. You also need to be sure that the slave's control loop will be faster than the master's, so that the master can always access it on time.
I haven't used the library much yet, but all tests seemed really good IIRC. I used it for one of my larger rock-crawlers, and it worked perfectly every time.
EDITED for correction.