Page 2 of 3

Re: NXC: BT send from slave to master

Posted: 24 Feb 2012, 21:11
by HaWe
pps
yes, I guess I know what makes the difference and why it works now ...

to do list:
- piece recognition and claw centering
- parallelizing the auto move generator on 4 NXTs
- 1 additional iteration depth.

Re: NXC: BT send from slave to master

Posted: 24 Feb 2012, 22:11
by afanofosc
On the slave side I would remove any use of BT in your naming conventions. As written, your slaves never send or receive bluetooth messages. They just read from a mailbox and write to a response mailbox. To keep this clear in your code I would recommend calling them MailboxSend and MailboxReceive or something like that.

On the master side, there is no doubt that the main cause of all your trouble is that you never make sure that the bluetooth system is idle before you try to send another message. That's the rule with NXC and Bluetooth (and the underlying firmware). If you call a function that sends a bluetooth message do not just throw in a Wait. Instead, do { Yield(); } while (BluetoothStatus(connection) != NO_ERR); This should be after every call to one of the SendRemote* function or one of the Remote* function (such as RemoteStopProgram).

John Hansen

Re: NXC: BT send from slave to master

Posted: 25 Feb 2012, 12:50
by HaWe
thank you for your efforts looking through my code!
I did so!
now it looked like this:

Code: Select all

  int ack=0;
  while (ack!=checksum(cmd))  {
    SendRemoteString(ID, OUTBOX, cmd);
    Yield(); while (BluetoothStatus(ID) != NO_ERR);
    ReceiveRemoteNumber(INBOX, true, ack);
    Wait(5);
  }
Trying this, no motor action was happening any more.
Pressing "start" on master and slave I could watch the slave's Sensor values on the master's display (reacting correctly), but no motors started running as they should.
re-changing the line
Yield(); while (BluetoothStatus(ID) != NO_ERR);
to
Wait(10)
all was fine again :?

Re: NXC: BT send from slave to master

Posted: 25 Feb 2012, 15:49
by afanofosc
Doc,

What you replaced Wait(10) with was not exactly what I recommended. But, gosh, I wish you would not just blindly try something I suggest and abandon it if it does not immediately work exactly right. What does SendRemoteString return? You should always check function return values in case of errors or other important status values. It probably returns STAT_COMM_PENDING. So maybe you could replace Wait(10) with "do { Yield(); } while (BluetoothStatus(ID) == STAT_COMM_PENDING);" or something like that. Or store the value of BluetoothStatus in a variable and check for other possible values that might indicate an error, i.e., something other than STAT_COMM_PENDING or NO_ERR.

Regardless, you should not ever use the Bluetooth subsystem without making sure that it is currently IDLE. Generally it is best to check before each function that might send a bluetooth message, i.e., all Remote* functions, all SendRemote* functions, and from a master NXT if the requested mailbox is empty the ReceiveRemote* functions. Also any call to SysCommExecuteFunction with parameters that cause bluetooth messages to be sent.

John Hansen

Re: NXC: BT send from slave to master

Posted: 25 Feb 2012, 17:34
by HaWe
hmm, and shall insert this
I just saw that you hace curled brackets round Yield
{ Yield(); } while (BluetoothStatus(ID) == STAT_COMM_PENDING);"

what is this for? is this sort of do...while?

and are you sure about inserting this line
{ Yield(); } while (BluetoothStatus(ID) == STAT_COMM_PENDING);"
only AFTER the SendRemoteString(ID, OUTBOX, cmd); line?
Why not before? (I was thinking that the BT line maybe should be free also before sending, either about what's after?

(I can't tell you how I actually really HATE this low level communication checking...!)

Re: NXC: BT send from slave to master

Posted: 25 Feb 2012, 17:45
by afanofosc

Code: Select all

do { Yield(); } while (BluetoothStatus(ID) == STAT_COMM_PENDING);
If you hate it then don't use Bluetooth. Cause using Bluetooth requires checking the status. Do it before any call that uses Bluetooth so that you never stomp on top of an in-process Bluetooth operation. Do it after any call that uses Bluetooth if you want to make sure it is finished before you move on to whatever else you are doing. Fiddle around until you get it right (i.e., change the loop as needed until it works flawlessly in your testing). Wrap it up in a nice package like BTWait(byte conn).

John Hansen

Re: NXC: BT send from slave to master

Posted: 25 Feb 2012, 21:46
by HaWe
John,
believe me or not, but I spent the whole afteroon trying EVERYTHING with all yields, NO_ERROR, STAT_COMM_PENDING and anything you can imagine.
Nothings worked reliably, most of the time the program hung up passing the long board array.
Now having added 1 more slave everything is completely muddled up.

As already once has been said (about rs485 before):
It's really enervating and frustrating that one has to spend more time with the implementation of COMM stuff than one could use rationally for one's actual project.

We urgently need save, quick, comfortable, and simple BT remote commands for connecting bricks to a network to control remote motors, poll remote sensors, and pass and read variables and long arrays!

And if the Lego fw functions are not able to provide this, we'll need a new firmware with functions which is able to do it.

Re: NXC: BT send from slave to master

Posted: 26 Feb 2012, 00:21
by afanofosc
This is why I tell you to put your comm logic on a single thread. Seriously, if you were trying to do what you are trying to do on your NXT with 2 or three computers talking to each other across multiple threads and you let each thread try to send and receive messages all willy nilly you would crash Windows for sure.

Remember that no functions are thread safe in NXC unless they are either inline or safecall. You can't have a small shared routine that you call within two separate threads unless that function is either inline or safecall.

What do you really think you are buying with all your threads other than heartache and grief?

Try making all your utility functions safecall routines and see if that fixes your problems.

John Hansen

Re: NXC: BT send from slave to master

Posted: 26 Feb 2012, 09:36
by HaWe
ok, although hard to explain the problem with my poor English, I try it nevertheless:
1st, my threads are already running by safecall or by mutexes. Nevertheless, the BT messages get mixed up if I'm sending, e.g., 3 or 4 strings of length >40 shortly one after the other to the 1st, then to the 2nd, then to 3rd slave. In that case the program often hangs up.

So because you asked or maybe didn't understand so far when reading through my program source code, this is roughly my program design (but it's whole lot of stuff).

I have 1 master and 3 slaves (currently 2, the 3rd is waiting for progress).
for each slave the master has 1 outbox and 2 inboxes => 3x3=9 mailboxes over all.
each slave has 1 corresponding inbox and 2 corresponding outboxes (3 mailboxes on each slave).

Code: Select all

#define BT_CONN_1 1 // Slave 1
#define OUTBOX_1  1 // out string, event-based, on demand
#define INBOX_11  2 // sensors+values string (continuously)
#define INBOX_12  3 // values (ack, requested)

#define BT_CONN_2 2 // Slave 2
#define OUTBOX_2  4 // out string, event-based, on demand
#define INBOX_21  5 // sensors+values string (continuously)
#define INBOX_22  6 // values (ack, requested)

#define BT_CONN_3 3 // Slave 3
#define OUTBOX_3  7 // out string, event-based, on demand
#define INBOX_31  8 // sensors+values string (continuously)
#define INBOX_32  9 // values (ack, requested)
The master has to know each and every of all 4 sensor states of every single slave at any time (which may change very quickly),
and additionally 6-10 different calculated values of char, int, or long which are changing their values rather slowly from time to time (not as quick as the sensors).

so each slave uptdates his series of values of interest rather quickly in an endless loop and packs it to a out string (strlen ~56-58). He provides his out string by putting it into a mailbox.

this string must be received by the master continuously from every slave within ~ 50 ms each
(or even if possible <20-30 ms, that means 100-200 ms at most for 1 receive loop to all slaves, better if <100 ms should be possible).
This variable polling is running in a single task, lets call it the VariableReadingTask.
This VariableReadingTask uses inboxes _11, _21, _31 (for Receive Slave String)

Now from time to time there are events happening at the master (sensor events, function calculation events, BT message events, or user interface/interrupt events).
The master now has to send a special command or requests or even rather long arrays (<= 140 chars) to a specfic slave, or maybe 2, or sometimes even to all 3 of them one after the other,
then for a long while (several minutes) maybe there is no such a event and nothing has to be done at all. But sometimes there might also be very frequent commands to a specific slave (e.g., quickly stopping a motor prematurely when a remote sensor signalizes a critical value). There is no way to plan these events and/or to daisy-chain them into the VariableReadingTask, because in these cases it's unpredictable when the master needs to send a motor command or not, or if he needs a special remote value which is not continuously sent by the VariableReadingTask.
In other cases, several commands are queued-up, with waiting periods in between (waiting for buttun-pressed, waiting for caclculating result ready, waiting for motor encoder reached) and so it keeps waiting most of the time and does nothing in between. Chaining this into the VariableReadingTask would cause that during this waiting periods also no other variables would be received as periodically requested. Nevertheless, it can be prematurely interrupted by "stop task" from other tasks in urgent cases (behaviour- or event-based).
Anyway, all requests and commands that the master may have to send on demand (within ~50 ms) are centralized in a, lets call it, MasterEventTask.
This MasterEventTask uses outbox _1, _2, _3 (for send BT msg) and inboxes _12, _22, _32 (for receive ack number).

So these 2 BT tasks have to run independently, 1 continuously, and 1 on demand, and that's exactly what I'm doing currently in my program.

Re: NXC: BT send from slave to master

Posted: 28 Feb 2012, 19:58
by HaWe
I changed my BT master code again and tried to real centralize all BT tasks into one using semaphores, safecall message_out handlers, and several global variables to force all commands in a row.
My BT-in-out task now looks like this:

Code: Select all

char   _NOS_;                        // Number of Slaves
string _OUT_;                        // BT out string to send
char   _ID_, _INBOX_, _OUTBOX_;      // COMM and Mailboxes

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
task MasterSlaveCOMMTask()    // NOS = Number of Slaves
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
{
   string slvmsg;
   int ack, ch;
   char ID;

   while(true)   {
     if (_NOS_>=1) {
       ID=1;
       until(ReceiveRemoteString(INBOX_11, true, slvmsg) == NO_ERR);
       ParseBTResponseMsg(ID-1, slvmsg) ;
       Wait(10);
     }

     if (_NOS_>=2) {
       ID=2;
       until(ReceiveRemoteString(INBOX_21, true, slvmsg) == NO_ERR);
       ParseBTResponseMsg(ID-1, slvmsg) ;
       Wait(10);
     }

     if (_NOS_>=3) {
       ID=3;
       until(ReceiveRemoteString(INBOX_31, true, slvmsg) == NO_ERR);
       ParseBTResponseMsg(ID-1, slvmsg) ;
       Wait(10);
     }
      
      ack=0;
      if (_OUT_ != "") {
        ch=checksum(_OUT_);
        while(ack!=ch)  {
            Wait(10);
            SendRemoteString(_ID_, _OUTBOX_, _OUT_);
            do {Wait(1);} while (BluetoothStatus(_ID_) == STAT_COMM_PENDING);
            Wait(10);
            ReceiveRemoteNumber(_INBOX_, true, ack);
            Wait(20);
        }
        _OUT_="";
      }
      
      Wait(5);
   }
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
safecall void SetBTSendParam(char ID, string cmd) {
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                           // called by all event handlers
  _ID_     = ID;                           // which generate a BT_OUT message
  _INBOX_  = 3*ID;
  _OUTBOX_ = _INBOX_ -2;
  _OUT_    = cmd;

}
Strangely, when sending 4 arrays[≈ 40] shortly one after the other to any slave, each slave seems to receive every "block-of-four" 3-4 times multiple, completely, redundantly, one block after the other, although the 4 arrays have been sent actually only once and then never, and this seems to muddle up the BT communication.
Moreover, I can clearly see that after that the slaves don't get all their response messages returned to the master...