ArduinoBT -- multiple connections

Hello !

I am developping a program to remotely turn ON/OFF differents electric devices throught Smartphones and ArduinoBT.

My code for the smartphone is developped for Windows Mobiles and I use the InTheHand.net library to communicate through bluetooth.
The program works perfectly when I have one smartphone. I keep the communication alive between smartphone and ArduinoBT to avoid reconnecting each time (takes about 1.7 sec).

Therefore, when I use 2+ smartphones, It seems to me that the ArduinoBT can't handle more than 1 simultanous connection/stream. Can someone confirm that ? I have tried to search for informations about that on internet, but haven't found anything.

Example:
When one smartphone is connected and that the second sends a message, the message never arrives to the ArduinoBT.
Note that the first smartphone is only connected, (a stream exists between them) and no message or information is sent.

Any help or clue or hint or link to some information would be greatly appreciated.

Hello !

As no one replies ... I am writing my findings here ...

The arduinoBT can only use one serial communication at a time ! So it can only talk to one device at a time.
You apparently can link multiple device if you use a different page mode (R2), see IWrap API for more details. FYI, the default page mode accepts only one.

It seems that what I want to do seems to be possible by using MUX (multiplexing) mode ... however, except messing up my ArduinoBT cards, this mode has not helped. I must admit I maybe haven't understood enough the API but the lack of examples of code does not help (IWrap API has no code examples for the MUX mode).

So for now, I am still stuck ... trying to find a working solution.

a few links:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1199569902

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1181825471/0

So actually, my program still has to stop/initiate a new connection each time. This stil takes 1.5 sec each time ... Using the 4 possible connections of the MUX mode would solve the problem ... but now I am afraid of messing up with the other ArduinoBT ...

See also http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1253105481 for more details about MUX ...


So I've come up with a code, that should let me understand a bit how the MUX works while being sure to reset parameters to default each time I reset the board ... (with the help of mikeT and different threads)

I have not tried it yet as I don't have any ArduinoBT working (should be ready tomorrow).

here is the code ! (If anyone sees a big mistake .. do tell me)

/*
 * Simon Ruffieux
 * Project Arduino Bluetooth Switch - Test MUX
 * Sept 2009
 * EIA-FR
 */

#include <stdlib.h>
#include <string.h>

#define LED_PIN 13
#define RESET 7


//Frame {0xBF, 0xFF,0x00, dataLength, data, link^0xFF }
byte disableMuxMode[] = {0xBF, 0xFF, 0x00, 0x11, 0x53, 0x45, 0x54, 0x20, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x20, 0x4d, 0x55, 0x58, 0x20, 0x30, 0x00};

void resetBTModule(){
  digitalWrite(RESET, HIGH);
  delay(10);
  digitalWrite(RESET, LOW);
  delay(2000);
}

void setup(){
  pinMode(LED_PIN,OUTPUT);
  pinMode(RESET, OUTPUT);
  Serial.begin(115200);
  
  // Reset the bluetooth interface
  resetBTModule();
  //Config Bluetooth module
  Serial.println("SET CONTROL ESCAPE 43 00 1"); // set escape character to + and 00 = DTR bitmask; 1 = return to command mode when DTR dropped
  //Serial.println("SET BT NAME BTSWITCH"); // just change name
  Serial.println("SET CONTROL BIND 0 20 RISE SET CONTROL MUX 0"); // in case the command below does not work, i can save the board with the GPIO5

  Serial.flush(); // empty serial
  int i;
  for(i=0;i<sizeof(disableMuxMode);i++){ //send the mux command to disable MUX MODE (note that I could also use the sendMuxcommand .. but as I am not sure the function works ...)
    Serial.print(disableMuxMode[i]);
  } 
}

byte buffer[50];
int bufCounter;
byte c;
int muxMode =0;  
  
void loop(){
     
  // If there is something to read in the serial
  if(Serial.available() > 0){
    bufCounter =0;
    while((c = Serial.read()) != -1){ // as long as there is something to read, put it in buffer
      delay(100); // delay a bit just to be sure not to miss something ...
      buffer[bufCounter] = c;
      bufCounter++;
    }
      if(!muxMode){
        Serial.print("Received: ");
        for(int i =0; i<bufCounter; i++){
          Serial.print((unsigned char)buffer[i]);
        }
        Serial.println();
      }else{ // If mux mode is o, try to send the received message in a frame to link 00 (link 00 should be the terminal, hopefully)
        sendMuxCommand(buffer, 0x00); //Maybe I should convert the buffer in char .. first check if the terminal will receive the response ...
      }      
      //processData();
  } 
}

/*
 * Send a command when MUX is in OFF:  Switch to COMMAND mode, send a command and switch back to DATA mode
 */
void sendCommand(char cmd){
  //Switch to COMMAND mode
  digitalWrite(LED_PIN, HIGH); // set led HIGH
  delay(2000);
  Serial.print("+++");
  delay(2000);
  //Send the command
  switch(cmd){
    case 'n':
    {
      Serial.println("SET BT NAME BTSWITCH");
      break;
    }
    case 'm':
    {
      Serial.println("SET CONTROL MUX 1");
      muxMode = 1;
      //Try to send a MuxMessage to link 0-8 to say mux mode is on  (= SET MUX DONE)
      byte cmd[] = {0x53, 0x45, 0x54, 0x20, 0x4d, 0x55, 0x58, 0x20, 0x44, 0x4f, 0x4e, 0x45};
      sendMuxCommand(cmd,0x00);     
      
      break;
    }
    case 'p':
    {
      Serial.println("SET BT PAGEMODE 3 2000 2");
      //Send a MuxMessage to link 0-8 to say mux mode is on with link
      break;
    }
     
    default:
    {
      break;
    }
  }
  //Switch back to DATA mode
  if(!muxMode){
    delay(2000);
    Serial.print("+++");
    delay(2000);
  }
  digitalWrite(LED_PIN, LOW); // set led HIGH  
}

/*
 *  When in MUX mode: Send a command (link = 0xFF) or data (link = 0x00-0x08) encapsulated in a frame
 */
void sendMuxCommand(byte cmd[], byte link){
  int cmdLen;
  for(cmdLen =0; cmd[cmdLen] != 0x00; cmdLen++){
    if(cmdLen > 100){
      cmdLen = 0;
      break;
    }
  }
  Serial.print(0xBF); //Start of Frame
  Serial.print(link); //Link id ( 0x00-0x08 pour les connecs (data), control = 0xFF)
  Serial.print(0x00); //Frame flags
  Serial.print((unsigned char) cmdLen); // Data length
  int i;
  for(i=0;i<cmdLen;i++){
    Serial.print(cmd[i]); // data
  }
  Serial.print((unsigned char) (link^0xFF)); //nLink
}

/*
 *  Not used now ... maybe later if MUX is working and I can receive messages in MUX mode and decode them
 */
void processData(char cmd){
  switch(c){
    case 'm':  //SET MUX MODE 0
    {
      //set control mux 0
      byte cmd[] = {0x53, 0x45, 0x54, 0x20, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x20, 0x4d, 0x55, 0x58, 0x20, 0x30};
      sendMuxCommand(cmd, 0xFF);
      sendMuxCommand(cmd, 0x00);
      break;
    }
    case 'n':
    {
      //set bt name test
      byte cmd[] = {0x53, 0x45, 0x54, 0x20,0x42,0x54,0x20,0x4e,0x41,0x4d,0x45,0x20,0x54,0x45,0x53,0x54};
      sendMuxCommand(cmd, 0xFF);
      sendMuxCommand(cmd, 0x00);
      break;
    }
    case 's':
    {
      //set link to be slave
      break;
    }
    case 'p':
    {
      //park a link
      break;
    }
    default:
    {
      //Send something to th serial link 00 to see what has been received !
      break;
    }
  }
}

So continuing my lonely post !

So just for your information I changed a bit the code and now it's working fine:

mux mode is always disabled during setup ...
and I can set it ON by sending the the command "SET CONTROL MUX 1" from a terminal.

Still trying now to be able to use the MUX mode and retrieve and display the frames received back to my terminal while in MUX mode.

Will keep you informed !

Just joined the group to say I'm reading!
Sorry I can't offer a bit of help, I am new to all this. I am very interested in how your project turns out so please keep posting.
All the best,
Kate

Hi nomis,

I also started some experiments using the MUX mode, but without success, yet.

MikeT

Hi nomis,

I make a sketch using yours as base. It echos back the frames it receives and disables MUX mode when it receives a frame starting with "q". What's missing is the code for the connecting handling.

/*
 * Simon Ruffieux and Michael Dreher
 * Project Arduino Bluetooth Switch - Test MUX
 * Sept 2009
 * EIA-FR
 */

#include <stdlib.h>
#include <string.h>

#define LED_PIN 13
#define RESET 7

void BTCommandMode()
{
  // the reset method will not work reliable because it terminates all actve bluetooth connections
#ifdef WT11_RESET_HARDWARE_PATCHED  
  // The hardware method. Requires a hardware patch: ATmega is not connected to WT-11 RESET but to
  // GPIO6 and the initialization command "SET CONTROL ESCAPE 43 40 1" 
  digitalWrite(RESET, HIGH);
  delay(10);
  digitalWrite(RESET, LOW);
#else
  // the escape command method (requires "SET CONTROL ESCAPE 43 00 1" or "SET CONTROL ESCAPE 43 40 1")
  delay(1100);
  Serial.print("+++");
  delay(1100);
#endif
}


void setup()
{
  digitalWrite(LED_PIN, HIGH);
  pinMode(LED_PIN,OUTPUT);
  digitalWrite(RESET, LOW);
  pinMode(RESET, OUTPUT);
  Serial.begin(115200);

  //Config Bluetooth module
  BTCommandMode();
  // disable the MUX mode at first, this gives us the chance to press reset at this point to upload a new sketch
  sendMuxCommand("SET CONTROL MUX 0", 0xFF);

  //Serial.println("SET CONTROL ESCAPE 43 40 1"); // set escape character to + and 00 = DTR bitmask; 1 = return to command mode
  // when DTR dropped
  //Serial.println("SET BT NAME BTSWITCH"); // just change name
  //Serial.println("SET CONTROL BIND 0 20 RISE SET CONTROL MUX 0"); // in case the command below does not work, i can save the
  //board with the GPIO5
  Serial.println("SET CONTROL INIT SET CONTROL MUX 0"); // when the WT-11 is reset it always disables MUX mode

  delay(5000);
  digitalWrite(LED_PIN, LOW);
  delay(3000);

  Serial.println("SET CONTROL ECHO 4"); // enables events like CONNECT / RING / NO CARRIER
  Serial.println("SET CONTROL MUX 1");
  Serial.flush();

  digitalWrite(LED_PIN, HIGH);
}


void loop()
{    
  int i;
  byte linkId = 0;
  int dataLen = peekFrame(&linkId);

  if(dataLen >= 0)
  {
    // copy data to buffer and read nLinkId
    char buffer[100];
    digitalWrite(LED_PIN, HIGH);
    if((dataLen + 1) < sizeof(buffer))
    {
      // for the payload + nLinkId
      for(i = 0; i <= dataLen; i++)
      {
        if(!Serial.available())
          delay(10); // it may take some time for the serial data to receive

        if(!Serial.available())
          break; // timeout => exit
        buffer[i] = Serial.read();
      }

      if(i == (dataLen + 1))
      {
        // received whole frame
        byte nlinkId = buffer[i - 1];
        buffer[i - 1] = 0; // terminate string and cut off nLinkId

        if(linkId == (nlinkId ^ 0xFF))
          handleFrame(linkId, dataLen, buffer);
      }
      digitalWrite(LED_PIN, LOW);
    }
  }
}

void handleFrame(byte linkId, int bufferLen, char* buffer)
{
  if(bufferLen > 0)
  {
    if(linkId != 0xFF)
    {
      if(buffer[0] == 'q')
      {
        sendMuxCommand("Disable MUX mode\r\n", linkId);
        sendMuxCommand("SET CONTROL MUX 0", 0xFF);
        // now the bluetooth connection is dead, I haven't found a way to reconnect it ("SELECT 0" doesn't work).
      }
      else //if(buffer[0] == 'a')
      {
        sendMuxCommand("Received: ", linkId);
        sendMuxCommand(buffer, linkId);
        sendMuxCommand("\r\n", linkId);
      }
    }
  }
}


/*
 *  When in MUX mode: Send a command (link = 0xFF) or data (link = 0x00-0x08) encapsulated in a frame
 *  Maximum command length is 800!
 */
void sendMuxCommand(const char* cmd, byte link)
{
  int cmdLen = strlen(cmd);

  if(cmdLen <= 800)
  {
    Serial.write((uint8_t)0xBF); //Start of Frame
    Serial.write((uint8_t)link); //Link id ( 0x00-0x08 pour les connecs (data), control = 0xFF)
    Serial.write((uint8_t)((cmdLen & 0x300) >> 2)); // Frame flags and upper two bits of length
    Serial.write((uint8_t)cmdLen); // Data length
    Serial.print(cmd);
    Serial.write((uint8_t)(link^0xFF)); //nLink
  }
}


/*
  Detect a frame and return its length and linkId. 'dataLen' will be -1 when there is not enough data
 in the serial receive buffer.
 After reading the frame payload data, you also have to read the negated linkId from the input buffer!
 
 returnvalue:
 <  0: error
 >= 0: payload data length of frame
 */
int peekFrame(byte* link)
{
  int dataLen = -1;

  // Syncronize to beginning of frame (skip anything which is not SOF (0xBF))
  while(Serial.available() >= 4)    // at least the header is in the receive buffer
  {
    if(!((serialPeek(0) == 0xBF) && ((serialPeek(2) & 0x3F) == 0)
      && (((*link = serialPeek(1)) <= 8) || (*link == 0xFF)) ))
    {
      // consume unwanted character, should only happen in non-MUX mode or when
      // stream syncronization is lost
      Serial.read();
      continue;
    }

    Serial.read(); // consume SOF (0xBF)
    *link =  Serial.read();
    dataLen = (Serial.read() & 0xC0) << 2;
    dataLen |= Serial.read();
    break;
  }

  return dataLen;
}

// originally defined in hardware\cores\arduino\HardwareSerial.cpp
#define RX_BUFFER_SIZE 128
struct ring_buffer {
  unsigned char buffer[RX_BUFFER_SIZE];
  int head;
  int tail;
};
extern ring_buffer rx_buffer;

// Allows safe access to the whole serial receive buffer without duplicating it.
// This is similar to Serial.read() but without removing the character from the buffer.
// index: the index in the receive buffer (0 to Serial.available() - 1)
// returnvalue:
//   < 0 : error: index out of bounds (not enough data in the buffer)
//   >=0 : character data from the buffer
int serialPeek(int index)
{
  // do we have enough characters?
  if (Serial.available() > index)
    return static_cast<unsigned char>(rx_buffer.buffer[(rx_buffer.tail + index) % RX_BUFFER_SIZE]);

  return -1;
}

MikeT

Hello MikeT !

Sweet, your code works perfectly and is very nice !

I had come to the same result but with a very uglier code ...
I'll start doing a more compelx program and make a few more tests out of your code !

But at least, now everyone can use MUX mode with the ArduinoBT, which was I think a big lack.

Maybe the code you (we) just produced should be added to the playground along with a few explanations.

Anyway thanks a lot !