Introduction: Arduino & HC-05: Serial Menu Wizard

I have used the HC-05 bluetooth module many times in different control projects, but in fact I always used this with pre-defined configurations, where the module is a slave or a master with one defined slave.

lately, I had to the case where the master has take decisions based on surrounding devices, and this is when I realized that the use of HC-05 is not really well presented on the internet. At least, I could not find any program that utilizes HC-05 as what you expect in your mobile phone's bluetooth (e.g. switching on, searching for devices, selecting a device from the list...etc).

This is where I decided to do it, an interactive wizard for HC-05 bluetooth modules, that displays menus over serial to control the module functionalities as master/slave and that switches between data and AT modes with SW commands.

Step 1: The Hardware:

  1. HC-05 SMD module (I got mine from aliexpress)
  2. Arduino Nano (also from aliexpress)
  3. 3x7cm double-sided proto-board (again from aliexpress)
  4. Two LEDs
  5. Couple of single-core wires
  6. 2.54 cm headers

Step 2: The HC-05 Module:

In fact, the reference manual I used is about a module with different product number, but in fact it is the same module as the HC-05 (I found it well written and with nice graphs). The name of the module is EGBT-045MS, and you can find the manual here.

In particular, we are interested in the following 8 pins:

  1. pins 1,2 : TX,RX: the send/receive pins to be connected to the serial (or software serial) pins on Arduino.
  2. pins 12,13 : Vcc, GND: to power the module up.
  3. pins 31,32 : indication LEDs, cin particular a "status" LED and a "connected in data mode" LED
  4. pin 11 : Reset pin, used to reset the module to switch the operational mode (AT/DATA)
  5. pin 34 : Command mode pin, used to switch on/off AT(command) mode.

Step 3: Connections:

To connect with 5v host, you need to use voltage dividers (couple of resistors) on the HC-05 RX pin, not necessarily on the Vcc as it is 5v tolerant (this is what the manual says at least).

Now, to be honest, I did the connections for a 3.3v Arduino pro mini, but later I used a 5v Arduino Nano, and I DID NOT FOLLOW THE GUIDE adding resistors to the RX line. But guess what, IT IS WORKING PERFECTLY, so i'll keep it as is ;)

Note: I am not saying it will work with you or that it is correct this way, I think it is only by chance that it worked with me. Please follow the wiring diagram for the correct connection.

Now, properly place the HC-05 on your proto-board (pay attention, the HC-05 is 1.5 mm spacing, while the breadboard is 2.45 mm spacing. So, holes are not aligned, but with just few adjacent cables you can manage to pass it through)

Next, place the headers for the Arduino Nano and the HC-05 pins, two resistors for the LEDs, the LEDs themselves (connected to GND from the other end) and I have also placed a connection for I2C display (will be added and explained in another instructable in the near future.)

Note: I did not wire wire the HC-05 connections to particular Arduino pins, I am doing this through jumper wires for now to keep it modular with other components if any.

Step 4: AT Mode and HC-05 Reset.

In most references, you are asked to manually disconnect/reconnect the power supply to the HC-05 in order to put it into AT mode (with pin.34 put into HIGH) or back into data mode (with the same pin into LOW state). This does not make sense if your application requires switching back and forth in order from/to AT command mode.

The trick is to allow Arduino to control the Reset pin (pin no.11) which resets the module. The process is the following

To put in AT command mode:

digitalWrite(pinEn,HIGH);   // Set the Enable pin (no.34) to HIGH, and keep it during the AT command 
digitalWrite(pinRe,LOW);    // Set the Reset pin (no.11) to LOW, this postpones the module in preparation to reset
delay(100);                 // A short delay (should be more that 10 ms) to simulate a negative pulse on the reset pin
digitalWrite(pinRe,HIGH);   // Set the Reset pin back to HIGH, and with Enable pin set to HIGH, the module restarts in AT Command mode.

To put in data transmission mode:

digitalWrite(pinEn,LOW);    // Set the Enable pin (no.34) to LOW, and keep it during the data transmission mode
digitalWrite(pinRe,LOW); // Set the Reset pin (no.11) to LOW, this postpones the module in preparation to reset delay(100); // A short delay (should be more that 10 ms) to simulate a negative pulse on the reset pin digitalWrite(pinRe,HIGH); // Set the Reset pin back to HIGH, and with Enable pin set to LOW, the module restarts in Data transmission mode.

Step 5: Interaction With the User Input

In our program, the user can operate the module in one of three modes

  1. AT command mode: you enable it by typing '#start' in the serial terminal, where you need to type in each command and you read back the exact response from the module on Serial monitor.
  2. Data transmission mode:you enable it by typing '#end' in the serial terminal, where all over-the-air communication with the module will be listed in the Serial monitor, and no control is possible over the module parameters.
  3. Operational menu mode: you enable it by typing '#op' in the serial terminal, this puts the module into AT command mode, but instead of providing the commands directly by the user, the commands are issued with an interactive menu with predefined operations.

The operational menu allow the following operations:

  1. Display the module settings
    1. Name, Baudrate, Role, Conn. Mode, Password
  2. Modify current settings
    1. Modify the module name
    2. Modify UART settings
    3. Modify the pairing code/PW
    4. Switch the role (Master/Slave)
    5. Change connection mode
    6. Back...
  3. Reset to original settings
  4. Master-only operations
    1. Discover nearby devices
    2. Pair with a device
    3. Connect to a device
    4. Remove devices from pairing list
    5. Bind with a device
    6. Back...

Step 6: Code:

Below is the Arduino code (also you can download the file itself)

Pay attention to the intensive string usage in this program. I faced numerous problems because if this, and i could resolve couple of them with the following remarks:

  • Use the 'String.reserve()' function to pre-reserve dynamic memory for the string instance, this allows better handling on memory assignments in the heap. But still it is not enough as it does not guarantee anything.
  • Use of the 'F()' macro: which allows the storage of strings in flash memory rather than RAM. It works only with static strings within print/println operations. This is extremely useful but not enough (does not help with dynamic strings)
  • decrease the number of reserved strings: Do not ask to find up to 10 devices if this is not really what you want,. 3 might be enough for example.

The problem that the program will execute and upload in any case, but it will operate in unexpected way, responding to different queries than the one you typed, and not displaying some strings and stuff like that.

The way to resolve it is to use char arrays (or char*) instead of strings. This is my next step but now I have no time to do it ;)

/*
* AT_AT_mode.ino - a wizard for HC-05 bluetooth modules * Copyright (c) 2016 Rami Baddour. All right reserved. * http://ramibaddour.com/ https://ch.linkedin.com/in/ramibaddour * * A program to view, set, modify and use the HC-05 bluetooth with an interactive textural menus * over serial monitore * * You can include parts of the code that does specific functionality (e.g. search for devices) * to your own program * * Note that this code uses intensively the String class, which has some unpredictable behaviour * when virtual memory is not possible to researve. IT is tested to work perfectly when restricting * number of recorded searched devices to 3. above this can have some problems with displyed strings. * * Newer version will use char arrays instead, hopefully with better stability. * * */ #include SoftwareSerial mySerial(5, 4);// RX, TX where the HC-05 is connected #define pinRe 3 // the Reset pin, connected to HC-05 pin No. 11 #define pinEn 6 // the Enable pin, connected to HC-05 pin No. 34 #define INQ1 1 // Inquire Acces mode: 0-standard 1-RSSI(default) #define INQ2 100 // Maximum number of devices responce: 0-to-32000 #define INQ3 30 // Inquire timeout: 1-to-48 sec #define MAXDEV 1 // Maximum number of devices to record. #define PAIRTO "25" // Pairing timout in seconds #define MAXSTRLEN 50 // maximum buffer researved for string variables. #define ATSTART "#start" // The token string to start AT command input #define ATEND "#end" // The token string to end AT command or Operational menu input #define ATOP "#op" // The token string to start the operation menu. // column widths in character count for the discovred BT devices' list #define colW1 6 #define colW2 45 #define colW3 20 #define colW4 7 int8_t currIn = -1; // The current input, records the user selection and used within the program state machine int8_t currSt = -1; // the current state, defines the current status in regards to the menu selections int8_t deviceCount = 0; // the Reset pin, connected to HC-05 pin No. 13 int devCount = 0; // how many devices in total have been discovered in the last device inquiry int devLoc = 0; // the location of the selected device within the list of discovered devices. String serialStr, tmpName, tmpRes; //some string variables to be used later //define a data structure for each descovered device. It inlcudes basic information like the name, address, RRSI and Service class typedef struct inqDevices{ String Name; String Address; String RSSI; String Class; inqDevices(int defSize=MAXSTRLEN){ Name.reserve(defSize); Name = ""; Address.reserve(20); Address = ""; RSSI.reserve(20); RSSI = ""; Class.reserve(40); Class = ""; } }inqDevice; inqDevice lstDevices[MAXDEV];
// This function takes a string of any length 'strIn' and outputs a the same string but with fixed length 'strLen'
// which is a substring of the input if length(strIn)>strLen, is the same as 'strIn' with the remaining characters
// filled with 'strFillChar' character.
// if 'strFill' is set to true, the input string is appended to the end of the result string as well.
String strFixed (String strIn, int strLen, boolean strFill = false, String strFillChar = " "){
  String tmpStr1 = strIn.substring(0,strLen);
  int tmpLen = tmpStr1.length();
  for (int i=0; i< (strLen - tmpLen); i++)
    tmpStr1 = tmpStr1 + strFillChar;
  if (strFill)
    tmpStr1 = tmpStr1 + strIn;
  return (tmpStr1);
}
// A function to clear the contents of the Device List array strings.
void clearDeviceList(){
  for (int i = 0; i
// Use the 'discardOK' setting if you want the function to discard the 'OK' result on the AT command return.
// Use the 'charsToCutFrom' and 'charsToCutTo' if you want to get only a substring of the AT command return. e.g. if you want to discard 
// the '+INQ:' initial part in the 'AT+INQ' command responce and get directly the address and signal information of the descovered device.
String getFromMySerial(String strToWrite = "", bool discardOK=true, int charsToCutFrom = 0, int charsToCutTo = 150){
  if (strToWrite != "")
  {
    mySerial.println(strToWrite);
    delay(50);    // these delays may be not necessary, it just to accomodate slow responding HC-05 modules.
  }
    
     String tmpStr = (mySerial.readStringUntil('\n'));
     tmpStr.trim();
     delay(50);
     
     if (discardOK)
       mySerial.readStringUntil('\n');
     return tmpStr.substring(charsToCutFrom, charsToCutTo);
}
// This function is similar to the previous 'getFromMySerial'. But this one is used when the answer 
// is not guarenteed after a fixed delay. e.g. if you wait to get remote BT device address or name in
// discovery process. also, it is used if you are not expecting a return value at all.
String getFromMySerialSimple(String strToWrite = "", int charsToCutFrom = 0, int charsToCutTo = 150){
  if (strToWrite != "")
  {
    mySerial.println(strToWrite);
    delay(100);
    return "";
  }
  else 
  {
    while (!mySerial.available()){delay(10);}  
    String tmpStr = (mySerial.readStringUntil('\n'));
    return tmpStr.substring(charsToCutFrom, charsToCutTo);
  }
}
void setup() {
  Serial.begin(57600);    
  mySerial.begin(38400);        // default HC-05 baud rate at AT-mode
  Serial.print(F("to enter AT mode please type '"));
  Serial.print(ATSTART);
  Serial.print(F("', to exist type "));
  Serial.print(ATEND);
  Serial.print(F(", or type '"));
  Serial.print(ATOP);
  Serial.println(F(" for the menu"));
  pinMode(pinRe, OUTPUT);
  pinMode(pinEn, OUTPUT);
  serialStr = "";
  serialStr.reserve(MAXSTRLEN);
  tmpName.reserve(MAXSTRLEN);
  tmpRes.reserve(MAXSTRLEN);
}
void loop(){
  if (Serial.available()) {
    serialStr = (Serial.readStringUntil('\n'));
    serialStr.trim();
    // Entering AT mode, you set EN pin to HIGH, and generate a 100ms negative pulse on the reset pin
    if (serialStr.equalsIgnoreCase(ATSTART))    {
        Serial.println(F(ATSTART));
        Serial.println(F("You are now in AT mode, Enter AT commands:"));
        currIn = -1;
        currSt = -5;
        digitalWrite(pinEn,HIGH);   // Set the Enable pin (no.34) to HIGH, and keep it during the AT command operation
        digitalWrite(pinRe,LOW);    // Set the Reset pin (no.11) to LOW, this postpones the module in preparation to reset
        delay(100);                 // A short delay (should be more that 10 ms) to simulate a negative pulse on the reset pin
        digitalWrite(pinRe,HIGH);   // Set the Reset pin back to HIGH, and with Enable pin set to HIGH, the module restarts in AT Command mode.
        serialStr = "";
     }
    // Exiting AT mode into data transmission mode, you set EN pin to LOW, and generate a 100ms negative pulse on the reset pin
    else if (serialStr.equalsIgnoreCase(ATEND)){
        currIn = -1;
        currSt = -1;
        Serial.println(F(ATEND));
        Serial.println(F("You have quit AT mode."));
        digitalWrite(pinEn,LOW);    // Set the Enable pin (no.34) to LOW, and keep it during the data transmission mode
        digitalWrite(pinRe,LOW);    // Set the Reset pin (no.11) to LOW, this postpones the module in preparation to reset
        delay(100);                 // A short delay (should be more that 10 ms) to simulate a negative pulse on the reset pin
        digitalWrite(pinRe,HIGH);   // Set the Reset pin back to HIGH, and with Enable pin set to LOW, the module restarts in Data transmission mode.
    }
    // To enter the operational menu mode, you do the same as initiating AT mode, but with the state variablle state to 0 so you enable the menu state machine
    else if (serialStr.equalsIgnoreCase(ATOP)){
        currIn = 0;
        currSt = 0;
        Serial.println(F(ATOP));
        digitalWrite(pinEn,HIGH);
        digitalWrite(pinRe,LOW);
        delay(100);
        digitalWrite(pinRe,HIGH);
    }
    // For any other input, this is basically a selection of an item in the menu, or a command in AT mode.
    else{
      Serial.println(serialStr);
      currIn = serialStr.toInt();  //any non-numerical input results in '0'
    }
    // This is when we start the AT mode , all inputs are sent to the bluetooth module.
    if (currSt == -5){
      mySerial.println(serialStr);
    }
  }
  // In case of responce or input from the bluetooth module itself
  if (mySerial.available()){
    while (mySerial.available()){
     tmpName = (mySerial.readStringUntil('\n'));
     tmpName.trim();
     Serial.println(tmpName);
     delay(50);
    }
 }
  if (currIn >= 0){   // enter the state machine only if we get a valid input
    switch (currSt){  // the menu display selection
      case 0:{        // this is the main menu
        switch (currIn){  // based on the user input, selects the corresponding operation and therefore the next state
          case 0:
            Serial.println(F("----------------------------------------------------------"));
            Serial.println(F("Please select from the following operations:"));
            Serial.println(F("  1- Display the module settings"));
            Serial.println(F("  2- Modify current settings"));
            Serial.println(F("  3- Reset to original settings"));
            Serial.println(F("  4- Master-only operations"));
            Serial.println(F("  5 -"));
            Serial.print(F("(Option No.) "));
            currIn = -1;  // we set to -1 to avoid entering into the state machine indefinately.  
            break;
          case 1:
            currSt = 1;
            break;
          case 2:
            currSt = 2;
            currIn = 0;                
            break;
          case 3:
            currSt = 3;
            break;
          case 4:
            currSt = 4;
            currIn = 0;                
            break;
          default:
            Serial.println (F("not a valid input, please select from the list"));
            currSt = 0;
            currIn = 0;                
        }
        break;
      }
      case 1:{        // display the module main settings. for each, send the command and wait for the result and display it
        Serial.println(F("----------------------------------------------------------"));
        Serial.println(F("The current module settings are:"));
        Serial.print(F(" -- Name       : "));
        Serial.println(getFromMySerial("AT+NAME", true, 6));
        Serial.print(F(" -- Baudrate   : "));
        Serial.println(getFromMySerial("AT+UART", true, 6));
        Serial.print(F(" -- Role       : "));
        if (getFromMySerial("AT+ROLE", true, 6) == "1")
          Serial.println(F("Master"));
        else
          Serial.println(F("Slave"));
        Serial.print(F(" -- Conn. Mode : "));
        tmpRes = getFromMySerial("AT+CMODE", true, 6);
        if ( tmpRes == "0")
          Serial.println(F("Single remote connection"));
        else if ( tmpRes == "1")
          Serial.println(F("Any remote connection"));
        else 
          Serial.println(F("Test mode"));
        Serial.print(F(" -- Passowrd   : "));
        Serial.println(getFromMySerial("AT+PSWD", true, 6));
        currSt = 0;
        currIn = 0;          
        break;
      }
      case 2:{        // The menu to modify the BT module settings
        switch (currIn){
          case 0:
            Serial.println(F("----------------------------------------------------------"));    
            Serial.println(F("Please select from the following operations:"));
            Serial.println(F("  1- Modify the module name"));
            Serial.println(F("  2- Modify UART settings"));
            Serial.println(F("  3- Modify the pairing code/PW"));
            Serial.println(F("  4- Switch the role (Master/Slave)"));
            Serial.println(F("  5- Change connection mode"));
            Serial.println(F("  6- Back..."));
            Serial.print(F("(Option No.) "));
            currIn = -1;
            break;
          case 1:
            currSt = 21;
            currIn = 0;                
            break;
          case 2:
            currSt = 22;
            currIn = 0;                
            break;
          case 3:
            currSt = 23;
            currIn = 0;                
            break;
          case 4:
            currSt = 24;
            currIn = 0;                
            break;
          case 5:
            currSt = 25;
            currIn = 0;                
            break;
          case 6:
            currSt = 0;
            currIn = 0;                
            break;
          default:
            Serial.println (F("not a valid input, please select from the list"));
            currSt = 2;     // stay in the same menu if wrong input is entered
            currIn = 0;                
          break;
        }
      break;
      }
      case 21:{       // Modify the module's name
        Serial.println(F("----------------------------------------------------------"));
        Serial.print(F("The curent module name is : "));
        Serial.println(getFromMySerial("AT+NAME", true, 6));
        Serial.println(F("Please enter a new name, or leave blank to keep the current setting."));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        if (tmpName == "")
          Serial.println(F("Name is left unchanged"));
        else{
          tmpName = "AT+NAME=" + tmpName;
          tmpRes = getFromMySerial(tmpName, true);
          if (tmpRes == "OK")
            Serial.println (F("Name change successful"));
          else
            Serial.println (F("Faild to change the name, please check the name and try again"));
          }
        currSt = 2;
        currIn = 0;          
        break;
      }    
      case 22:{       // Modify the serial UART port settings (Baudrate, Stop bit and Parity bits)
        Serial.println(F("----------------------------------------------------------"));
        Serial.print(F("The curent UART (Baudrate) settings are : "));
        tmpName = getFromMySerial("AT+UART", true, 6);
        String tmpBR = tmpName.substring(0,tmpName.indexOf(','));                       // Baud rate
        String tmpSB = tmpName.substring(tmpName.indexOf(',')+1,tmpName.indexOf(',')+2);// Stop Bit 
        String tmpPB = tmpName.substring(tmpName.indexOf(',')+3,tmpName.indexOf(',')+4);// Parity bit
  
        Serial.println(tmpName);
        Serial.println(F("Choose new Baudrate, or leave it blank to keep unchanged. Possible options:"));
        Serial.println(F("(1) 4800      (2) 9600      (3) 19200     (4) 38400     (5) 57600"));
        Serial.println(F("(6) 115200    (7) 234000    (8) 460800    (9) 921600    (10) 1382400"));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        if (tmpName == "")
          {
            Serial.print(F("Baudrate is left unchanged: ")); 
            Serial.println(tmpBR);
          }
          else
          {
            int tmpInt = tmpName.toInt();   //Baudrate
            if ((tmpInt > 0)&&(tmpInt < 11))
              Serial.print(F("Baudrate channged successfully, the new baudrate is: "));
            switch (tmpInt) {
              case 1: case 2: case 3: case 4:
                tmpBR = String(2400 * pow(2,tmpInt),0);
                Serial.println(tmpBR);
                break;
              case 5: case 6: case 7: case 8: case 9:
                tmpBR = String(1800 * pow(2,tmpInt),0);
                Serial.println(tmpBR);
                break;
              case 10:
                tmpBR = "1382400";    
                Serial.println(tmpBR);
                break;          
              default: 
                Serial.println(F("Incorrect input, baudrate is left unchanged"));
             }
          }
        Serial.println();       // Stop Bits
        Serial.println(F("Choose the number of Stop bits, or leave it blank to keep unchanged. Possible options:"));
        Serial.println(F("(a) 0:1 bit                 (b) 1:2 bits"));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        if (tmpName == "")
          {
            Serial.print(F("Stop bit number is left unchanged: "));      
            Serial.println(tmpSB);
          }
          else if ((tmpName == "a")||(tmpName == "A")) 
          {
            tmpSB = "0";
            Serial.print(F("Stop bit number is channged successfully, the new setting is: "));      
            Serial.println(tmpSB);
          }
        else if ((tmpName == "b")||(tmpName == "B")) 
          {
            tmpSB = "1";
            Serial.print(F("Stop bit number is channged successfully, the new setting is: "));      
            Serial.println(tmpSB);
          }
        else
          Serial.println(F("Incorrect input, Stop bit number is left unchanged"));
        Serial.println();       //Parity bits
        Serial.println(F("Choose parity bits setting, or leave it blank to keep unchanged. Possible options:"));
        Serial.println(F("(1) no parity       (2) odd parity        (3) even parity"));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        int tmpInt = tmpName.toInt();
        if (tmpName == "")
          {
            Serial.print(F("Parity bits setting is left unchanged: "));      
            Serial.println(tmpPB);
          }
        else if ((tmpInt >0)&&(tmpInt <4))  
          {
            tmpPB = tmpName;
            Serial.print(F("Parity bits setting is channged successfully, the new setting is: "));      
            Serial.println(tmpPB);
          }
        else
          Serial.println(F("Incorrect input, Parity bits setting is left unchanged"));
        tmpName = "AT+UART=" + tmpBR + "," + tmpSB + "," + tmpPB;     // The overal UART settings command
        String tmpRes = getFromMySerial(tmpName, true);
        if (tmpRes == "OK")
          Serial.println (F("UART settings change successful"));
        else
          Serial.println (F("Faild to change the UART settings, please  try again"));
        currSt = 2;
        currIn = 0;          
        break;
      }     
      case 23:{       // Modify the password (Pairing code)
        Serial.println(F("----------------------------------------------------------"));
        Serial.print(F("The curent pairing code is : "));
        Serial.println(getFromMySerial("AT+PSWD", true, 6));
        Serial.println(F("Please enter a new pairing passkey, or leave blank to keep the current code."));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        if (tmpName == "")
          Serial.println(F("Pairing passkey is left unchanged"));
        else
          {
            tmpName = "AT+PSWD=" + tmpName;
            String tmpRes = getFromMySerial(tmpName, true);
            if (tmpRes == "OK")
              Serial.println (F("Pairing passkey  change successful"));
            else
              Serial.println (F("Faild to change the Pairing passkey , please check the code and try again"));
          }
        currSt = 2;
        currIn = 0;          
        break;
      }
      case 24:{       // Modify the role (Master/Slave/Slave-loop)
        Serial.println(F("----------------------------------------------------------"));
        Serial.print(F("The curent role setting is : "));
        tmpName = getFromMySerial("AT+ROLE", true, 6);
        tmpName.trim();
        Serial.print(tmpName);
        if (tmpName == "0")
          Serial.println(F(" [Slave]"));
        else if (tmpName == "1")
          Serial.println(F(" [Master]"));
        else if (tmpName == "2")
          Serial.println(F(" [Slave-loop]"));
          
        Serial.println(F("Choose a new role, or leave it blank to keep unchanged. Possible options:"));
        Serial.println(F("(0) 0:Slave        (1) 1:Master        (2) 2:Slave-loop"));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        if (tmpName == "")
          {
            Serial.print(F("Role is left unchanged: ")); 
          }
          else
          {
            int tmpInt = tmpName.toInt();
            switch (tmpInt)
            {
              case 0: case 1: case 2:
                tmpName = "AT+ROLE=" + tmpName;
                tmpRes = getFromMySerial(tmpName, true);
                if (tmpRes == "OK")
                  Serial.println (F("Role change successful"));
                else
                  Serial.println (F("Faild to change the role, please try again"));
                break;
              default: 
                Serial.println(F("Incorrect input, role is left unchanged"));
                break;
             }
            
          }
        currSt = 2;
        currIn = 0;          
        break; 
        }
      case 25:{       // Change the connection mode
        Serial.println(F("----------------------------------------------------------"));
        Serial.print(F("The curent connection mode is : "));
        tmpName = getFromMySerial("AT+CMODE", true, 6);
        tmpName.trim();
        Serial.print(tmpName);
        if (tmpName == "0")
          Serial.println(F(" [Single remote connection]"));
        else if (tmpName == "1")
          Serial.println(F(" [Any remote connection]"));
        else if (tmpName == "2")
          Serial.println(F(" [Test mode]"));
        Serial.println(F("Choose a new connection mode, or leave it blank to keep unchanged. Possible options:"));
        Serial.println(F("(0) 0:Single remote connection        (1) 1:Any remote connection        (2) 2:Test mode"));
        while (!Serial.available());
        tmpName = (Serial.readStringUntil('\n'));
        tmpName.trim();
        if (tmpName == "")
          {
            Serial.print(F("Connection mode is left unchanged: ")); 
          }
          else
          {
            int tmpInt = tmpName.toInt();
            switch (tmpInt)
            {
              case 0: case 1: case 2:
                tmpName = "AT+CMODE=" + tmpName;
                tmpRes = getFromMySerial(tmpName, true);
                if (tmpRes == "OK")
                  Serial.println (F("Connection mode change successful"));
                else
                  Serial.println (F("Faild to change the connection mode, please try again"));
                break;
              default: 
                Serial.println(F("Incorrect input, connection mode is left unchanged"));
                break;
             }
          }
        currSt = 2;
        currIn = 0;          
        break; 
        }
      case 3:{        // Reset the module to the original settings
        Serial.println(F("----------------------------------------------------------"));
        Serial.println(F("The module settings are reset to original"));
        getFromMySerial("AT+ORGL", false, 6);
        Serial.print(F(" -- Name     : "));
        Serial.println(getFromMySerial("AT+NAME", true, 6));
        Serial.print(F(" -- Baudrate : "));
        Serial.println(getFromMySerial("AT+UART", true, 6));
        Serial.print(F(" -- Role     : "));
        if (getFromMySerial("AT+ROLE", true, 6) == "1")
          Serial.println(F("Master"));
        else
          Serial.println(F("Slave"));
        Serial.print(F(" -- Passowrd : "));
        Serial.println(getFromMySerial("AT+PSWD", true, 6));
        currSt = 0;
        currIn = 0;          
        break;
      }
      case 4:{        // The menu to perform the Master module operations.
      if (getFromMySerial("AT+ROLE", true, 6) != "1") {      // Check if the module role=1 which mean the module is master.
        Serial.println(F("The module should be in Master role to perform these operations, please change the role."));
        currSt = 2;
        currIn = 0;          
      }
      else{
        switch (currIn){        
          case 0:
            Serial.println(F("----------------------------------------------------------"));    
            Serial.println(F("Please select from the following operations:"));
            Serial.println(F("  1- Discover nearby devices"));
            Serial.println(F("  2- Pair with a device"));
            Serial.println(F("  3- Connect to a device"));
            Serial.println(F("  4- Remove devices from pairing list"));
            Serial.println(F("  5- Bind with a device"));
            Serial.println(F("  6- Back..."));
            Serial.print(F("(Option No.) "));
            currIn = -1;
            break;
          case 1:
            currSt = 41;              currIn = 0;                          break;
          case 2: 
            currSt = 42;              currIn = 0;                          break;
          case 3:
            currSt = 43;              currIn = 0;                          break;
          case 4:
            currSt = 44;              currIn = 0;                          break;
          case 5:
            currSt = 45;              currIn = 0;                          break;
          case 6: 
            currSt = 0;               currIn = 0;                          break;
          default:
            Serial.println (F("not a valid input, please select from the list"));
            currSt = 4;               currIn = 0;                          break;
          }
        }
        break;
      }
      case 41:{       // Inquire nearby devices (addresses + names)
        Serial.println(F("----------------------------------------------------------"));
        Serial.println(F("List of nearby devices:"));
        getFromMySerial("AT+INIT", true);       // Discard the result, it may result in error if the SPP profile is already opened
        tmpName = String("AT+INQM=" + String(INQ1) + "," + String(INQ2) + "," + String(INQ3));
        tmpRes = getFromMySerial(tmpName, true);
        if (tmpRes != "OK")
          Serial.println (F("Inquire mode setting failed"));
        else
        {
          getFromMySerialSimple("AT+INQ");      // As multiple devices might be discovered, only execute the command then wait on serial for each new line until "OK" is received 
          clearDeviceList();
          tmpName = getFromMySerialSimple("", 5);   // at least, we have on result even if no devices were discovered, which is the "OK" output at the end of the inquire process
          while ((tmpName !=""))    // As "OK" is less than 5 characters long, the result will be empty string when OK is received.
          {
            int ind1st = tmpName.indexOf(',');                    // the index of the first comma
            int ind2nd = tmpName.indexOf(',',ind1st + 1);         // the index of the second comma
            String devAddr = tmpName.substring(0,ind1st);         // The device's address
            String devClas = tmpName.substring(ind1st+1,ind2nd);  // The device's service class
            String devRSSI = tmpName.substring(ind2nd+1);         // The device's RSSI
            devLoc = searchDeviceList(devAddr);                   // check ifthe device address is already discovered, As in most cases the same deie can be discovered multiple times with different RSSI and/or different service classes
            if ((devLoc < 0)&& devCount < MAXDEV)                 // a new device, not found in the list, but there is still empty slot in the list (did not exceed the maximum number of stored devices)
            {
              lstDevices[devCount].Address = devAddr;             // we store the new device data
              lstDevices[devCount].Class = devClas;
              lstDevices[devCount].RSSI = devRSSI;
              devCount++;
            }
            delay(10);
            tmpName = getFromMySerialSimple("", 5);               // get the next input from MySerial (i.e. BT device)
          }
          Serial.println (strFixed("+",colW1 + colW2 + colW3 + colW4 + 3,true,"-"));      // draw a table with the results
          Serial.println ("| " + strFixed("Ind.",colW1)+ strFixed("BT Device Name:",colW2) + strFixed("BT Device Address",colW3) + strFixed("Paired",colW4)+ " |");
          Serial.println ("| " + strFixed("-----",colW1)+ strFixed("----------------",colW2) + strFixed("------------------",colW3) + strFixed("-------",colW4)+ " |");
          for (int i=0; i devCount){      // if a numerical input exceeds the number of stored/discovered devices
          Serial.println(F("----------------------------------------------------------"));
          Serial.println(F("The selected index is out of bounds, please select within the list of available devices"));
          Serial.print(F("(Option No.) "));
          currIn = -3;    // stay in the same menu, until a correct index is entered or 'q' for exitting to previous menu
        }
        else{
          devLoc = currIn - 1;        // the index starts with 0
          tmpName = lstDevices[devLoc].Address;
          tmpName.replace(":",",");
          tmpName = String("AT+PAIR=" + tmpName+","+PAIRTO);
          tmpRes = getFromMySerialSimple(tmpName);
          tmpRes = getFromMySerialSimple("");       // we do this as the pairing process can take longer, so we cannot rely on the timeout in the readstringuntil()
          tmpRes.trim();
          if (tmpRes == "OK")
          {
            Serial.println (("You are now paired with the device " + lstDevices[devLoc].Name +" [" + lstDevices[devLoc].Address + "]"));
          }
          else
          {
            Serial.println (tmpName);
            Serial.println (tmpRes);
            Serial.println (F("Could not pair with the device, try again or initiat another search for devices"));
          }
          currSt = 4;
          currIn = 0;          
        }       
        break;
      }
      case 43:{       // Connect with one of the discovered devices
        if (currIn == 0){
          if (serialStr.equalsIgnoreCase("q")){     //'q' to quite to previous menu
            currSt = 4;
            currIn = 0;          
          }
          else{
            Serial.println(F("----------------------------------------------------------"));
            Serial.println(F("Select the device index that you want to connect to, q to abort "));
            Serial.print(F("(Option No.) "));
            currIn = -3;
          }
        }
        else if (currIn > devCount){      // if a numerical input exceeds the number of stored/discovered devices
          Serial.println(F("----------------------------------------------------------"));
          Serial.println(F("The selected index is out of bounds, please select within the list of available devices"));
          Serial.print(F("(Option No.) "));
          currIn = -3;
        }
        else{
          devLoc = currIn - 1;            // the index starts with 0
          tmpName = lstDevices[devLoc].Address;
          tmpName.replace(":",",");
          tmpName = String("AT+LINK=" + tmpName);
          tmpRes = getFromMySerialSimple(tmpName);
          tmpRes = getFromMySerialSimple("");       // we do this as the linking/connecting process can take longer, so we cannot rely on the timeout in the readstringuntil()
          tmpRes.trim();
          if (tmpRes == "OK"){
            Serial.println (("You are now connected with the device " + lstDevices[devLoc].Name +" [" + lstDevices[devLoc].Address + "], type '#end' to disconnect and reset."));
            currSt = 6;
            currIn = 0;   
          }
          else{
            Serial.println (tmpName);
            Serial.println (tmpRes);
            Serial.println (F("Could not connect with the device, try again or initiat another search for devices"));
          }
          currSt = 4;
          currIn = 0;          
        }       
        break;
      serialStr = "";
      }
      case 44:{
        if (currIn == 0)
        {
          if (serialStr.equalsIgnoreCase("q"))
          {
            currSt = 4;
            currIn = 0;          
          }
          else if (serialStr.equalsIgnoreCase("a"))
          {
            tmpRes = getFromMySerial(String("AT+ADCN"), true, 6);
            tmpRes.trim();
            int tmpCount = tmpRes.toInt();
            for (int i=0; i devCount)
        {
          Serial.println(F("----------------------------------------------------------"));
          Serial.println(F("The selected index is out of bounds, please select within the list of available devices"));
          Serial.print(F("(Option No.) "));
          currIn = -4;
        }
        else
        {
          devLoc = currIn - 1;
          tmpName = lstDevices[devLoc].Address;
          tmpName.replace(":",",");
          
           
          tmpRes = getFromMySerial(String("AT+FSAD=")+ tmpName, true);
          tmpRes.trim();
          if (tmpRes == "OK")
          {
            tmpRes = getFromMySerial(String("AT+RMSAD=")+ tmpName, true);
            tmpRes.trim();
            if (tmpRes == "OK")
            {
              Serial.println(("The device " + lstDevices[devLoc].Name +" [" + lstDevices[devLoc].Address + "] is removed from the paired devices list"));
            }
          }
          else
          {
            Serial.println (F("The device is not within the paired devices list."));
          }
        currIn = 0;          
        }
  
        break;
      }
      case 45:{
        if (currIn == 0)
        {
          if (serialStr.equalsIgnoreCase("q"))
          {
            currSt = 4;
            currIn = 0;          
          }
          else
          {
            Serial.println(F("----------------------------------------------------------"));
            Serial.println(F("Select the device index that you want to bind with, q to abort "));
            Serial.print(F("(Option No.) "));
            currIn = -3;
          }
        }
        else if (currIn > devCount)
        {
          Serial.println(F("----------------------------------------------------------"));
          Serial.println(F("The selected index is out of bounds, please select within the list of available devices"));
          Serial.print(F("(Option No.) "));
          currIn = -3;
        }
        else
        {
          devLoc = currIn - 1;
          tmpName = lstDevices[devLoc].Address;
          tmpName.replace(":",",");
          
           
          tmpName = String("AT+BIND=" + tmpName);
          tmpRes = getFromMySerialSimple(tmpName);
          tmpRes = getFromMySerialSimple("");
          tmpRes.trim();
          if (tmpRes == "OK")
          {
            Serial.println (("You are now binded with the device " + lstDevices[devLoc].Name +" [" + lstDevices[devLoc].Address + "], type '#end' to disconnect and reset."));
          }
          else
          {
            Serial.println (F("Could not bind with the device, try again or initiat another search for devices"));
          }
          currSt = 4;
          currIn = 0;          
        }       
        break;
      }
      case 6:{
        for (int i=0; i< serialStr.length(); i++)
        mySerial.write(serialStr[i]);
        break;
      }
    }
  } 
  serialStr = "";
}

Comments