Communicating via RS485
-
- Posts: 1818
- Joined: 02 Oct 2010, 02:19
- Location: Michigan USA
- Contact:
Re: Communicating via RS485
Actually yes. I started development of an RS485 library that supported NXT addresses, commands, and user elements. It worked/works fine, but it doesn't have any error checking (like checksum or crc). I think it tested out fine with three NXTs, but at the time I wasn't able to try more than that.
Maybe when I get back home I will post the library in it's latest state (could be a week or so).
Maybe when I get back home I will post the library in it's latest state (could be a week or so).
Matt
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
Re: Communicating via RS485
any news about advanced and simply accessable rs485 network functions?
-
- Posts: 1818
- Joined: 02 Oct 2010, 02:19
- Location: Michigan USA
- Contact:
Re: Communicating via RS485
Thanks for the reminder.
Here is the library:
Here is an example master program:
And an example slave program:
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.
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;
}
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;
}
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;
}
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.
Last edited by mattallen37 on 21 Nov 2012, 01:08, edited 1 time in total.
Matt
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
Re: Communicating via RS485
matt,
thanks, that looks great!
Ill try it as sonn I will have some days off in the next 1-2 weeks!
I'll post a link to your post also to the TO!
thanks again!
thanks, that looks great!
Ill try it as sonn I will have some days off in the next 1-2 weeks!
I'll post a link to your post also to the TO!
thanks again!
-
- Posts: 10
- Joined: 27 Dec 2011, 14:30
Re: Communicating via RS485
Hi Matt,
thank you for your code. I tried it with two NXTs and it worked fine.
I tried it also with 3 NXTs. So I defined a 2nd Slave Address in the master program and changed the "My485Address" in the slave program for one NXT also.
The Master should talk alternating to the NXTs.
But only the first slave is responding. If I use the Address of the 2nd Slave first, than only the 2nd Slave is responding? What did I wrong? Or can you post an extended version of your program for more than 2 NXTs?
Thank you very much!
Karl
thank you for your code. I tried it with two NXTs and it worked fine.
I tried it also with 3 NXTs. So I defined a 2nd Slave Address in the master program and changed the "My485Address" in the slave program for one NXT also.
The Master should talk alternating to the NXTs.
Code: Select all
if(i=1){
SendRS485ByteArray (My485Address, Slave1485Address, (WriteValue|ReadBackValue), Sdata);
result=MasterReadRS485ByteArray (My485Address, BytesToReceive, TimeOut, Rdata);
}
else if(i=2){
SendRS485ByteArray (My485Address, Slave2485Address, (WriteValue|ReadBackValue), Sdata);
result=MasterReadRS485ByteArray (My485Address, BytesToReceive, TimeOut, Rdata);
}
i++;
if(i>2)i=1;
Thank you very much!
Karl
Re: Communicating via RS485
did you already try
if (i==1)
...
if(i==2)
...
instad of i=1, i=2 ?
:)
if (i==1)
...
if(i==2)
...
instad of i=1, i=2 ?
:)
Re: Communicating via RS485
Good eye, Doc! These sort of errors can be hard to notice. It would be nice if the compiler warned you about such things.
John Hansen
John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
http://bricxcc.sourceforge.net/
-
- Posts: 1818
- Joined: 02 Oct 2010, 02:19
- Location: Michigan USA
- Contact:
Re: Communicating via RS485
As Helmut caught, try fixing it so it will only do one at a time (change it to ==). I didn't test it with more than 2 NXT's, because I only have two of the NXT battery packs, and it's hard to get ahold of rechargeable AA's in this house.
If fixing the conditionals doesn't help, let me know. It should work fine with up to about 32 NXT's, but it's possible I messed up in the code somewhere.
If fixing the conditionals doesn't help, let me know. It should work fine with up to about 32 NXT's, but it's possible I messed up in the code somewhere.
Matt
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
http://mattallen37.wordpress.com/
I'm all for gun control... that's why I use both hands when shooting
-
- Posts: 10
- Joined: 27 Dec 2011, 14:30
Re: Communicating via RS485
It works.
7 Posts. In the half of them I have disgraced myself. Better result than in real life.
Re: Communicating via RS485
don't bother, I'm happy that it works for you.
Maybe, would you like to publish your complete code?
I'm curious what data you are exchanging between all NXT's.
(is it a master-slave-architecture?)
Maybe, would you like to publish your complete code?
I'm curious what data you are exchanging between all NXT's.
(is it a master-slave-architecture?)
Who is online
Users browsing this forum: No registered users and 4 guests