Go Down

Topic: Switching between Wifi and BLE (Read 1 time) previous topic - next topic

jerteach


So very happy with my 2 Nano 33 IoT boards.

I have got both BLE working to control a peripheral LED using the other board as Central, and Wifi working to make a web page showing the Analog pins. Very impressed with this as the web pages auto update the analog readings. Easily got 5 web pages all working at the same time. (Just the basic examples with perhaps a few changes)

Has anyone merged the two ideas. Have one Nano 33 IoT as a web page generator that shows and controls the other Nano 33 IoT LED using BLE?

I know BLE and Wifi can't work in parallel on this board but can you easily and quickly switch between the two.



pert

I know BLE and Wifi can't work in parallel on this board but can you easily and quickly switch between the two.
One of the Arduino developers has done some work towards allowing this. It requires changes to the NINA module's firmware:
https://github.com/agdl/nina-fw/tree/coexistance
the Arduino SAMD Boards package:
https://github.com/agdl/ArduinoCore-samd/tree/coexistance
and the ArduinoBLE library:
https://github.com/agdl/ArduinoBLE/tree/coexistance

jerteach

One of the Arduino developers has done some work towards allowing this. It requires changes to the NINA module's firmware:
That looks really interesting Pert for a more advanced Arduino user, I think I will just stick to the default Nano 33 IoT install.

pert

I think that's probably a good decision. My intent was more to make you aware that we are likely to see this functionality officially supported by Arduino at some point.

Klaus_K

I have created a project where I can switch between WiFi and BLE. I had some issues on the way and have not cleaned the code yet. Currently I need to reset the Nina and restart the WiFi driver.

The switch takes significant amount of time and therefore is only useful for use cases where you want to configure your device via Bluetooth and then switch to WiFi. You can also restart the Bluetooth mode from a webpage.

jerteach

#5
Jan 09, 2020, 07:01 pm Last Edit: Jan 09, 2020, 08:32 pm by jerteach
I have created a project where I can switch between WiFi and BLE. I had some issues on the way and have not cleaned the code yet. Currently I need to reset the Nina and restart the WiFi driver.

The switch takes significant amount of time and therefore is only useful for use cases where you want to configure your device via Bluetooth and then switch to WiFi. You can also restart the Bluetooth mode from a webpage.
Interesting Klaus_K I would like to have a look at your code. Curious about the time delay. I just want to read 3 BLE LED's on or off state from 3 different devices, then update the Nano 33 IoT generated webpage. So a 10 second time delay might not be a big deal.

Presently I am having some strange issues just getting my BLE LED Central control program working. Seemed to work fine 3 days ago but now does not connect to the peripheral LED.  The peripheral works fine since I can change the LED using nrf-connect for mobile, and my web page javascript BLE program.

My program on github is here  

The program scans for devices that contain the local name "LED" then when you send a "1" it connects to the last device found. Then when you send a "2" it goes back to scanning.  When it is connected you can connect 3V3 to pin 2 to turn on the LED on both devices (The central and the peripheral).


It was just a test program. It does seem to work, but my serial output is not getting me much useful information.




Update to the above. All three BLE programs work fine just getting strange data from these calls.





Code: [Select]
// Note the 2 GATT's are slightly different: service=19b10010,  LED=19b10011
    BLECharacteristic ledCharacteristic =   peripheral.service("19b10010-e8f2-537e-4f6c-d104768a1214").characteristic("19b10011-e8f2-537e-4f6c-d104768a1214");  //worked
  
    Serial.print("ledCharacteristic: ");
    Serial.println(ledCharacteristic);
    
    Serial.print("ledCharacteristic.descriptor(0): ");
    Serial.println(ledCharacteristic.descriptor(0));
    
    Serial.print("ledCharacteristic.descriptor(0).value(): ");
    Serial.println((int)ledCharacteristic.descriptor(0).value());

    
    Serial.print("ledCharacteristic.descriptor(1): ");
    Serial.println(ledCharacteristic.descriptor(1));
    
    Serial.print("ledCharacteristic.descriptor(1).value(): ");
    Serial.println((int)ledCharacteristic.descriptor(1).value());


)



What I want to see is proof about if the LED is on or off, but what I am getting is zero when the LED is on or when it is off.



ledCharacteristic: 1
ledCharacteristic.descriptor(0): 0
ledCharacteristic.descriptor(0).value(): 0
ledCharacteristic.descriptor(1): 0
ledCharacteristic.descriptor(1).value(): 0





... Sorry editing while working on things.


I think I understand what the problem is. Got it working now with some changes, but I do not like this function.




Code: [Select]

void printData(const unsigned char data[], int length) {
  for (int i = 0; i < length; i++) {
    unsigned char b = data[i];

    if (b < 16) {
      Serial.print("0");
    }

    Serial.print(b, HEX);
  }
}





Can someone explain it, or better yet show me a simpler way to tell if the LED is on or off.



The code I needed to get things working is

Code: [Select]
  if (ledCharacteristic.canRead()) {
    ledCharacteristic.read();
    if (ledCharacteristic.valueLength() > 0) {
      Serial.println();
      Serial.print(", value 0x");     
     printData(ledCharacteristic.value(), ledCharacteristic.valueLength());

    }
  }











Klaus_K

I have cleaned up my code and did some more experimenting. I do not need to reset the Nina module but reinitialize the WiFi driver when switching to WiFi. Here is my example. You will need to add a "arduino_secrets.h" file like other WiFi example for your Wifi network credentials.

Code: [Select]
/*
  BLE2WiFi

  This example creates a BLE peripheral with a service that contains a
  characteristic to switch to WiFi.

  The circuit:
  - Arduino Nano 33 IoT,

  You can use a generic BLE central app, like LightBlue (iOS and Android) or
  nRF Connect (Android), to interact with the services and characteristics
  created in this sketch.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include "utility/wifi_drv.h"
#include "arduino_secrets.h"


#define BLE_UUID_NETWORK_CONFIG_SERVICE              "343D2964-5ECF-2297-4463-609011571F24"
#define BLE_UUID_NETWORK_ENABLE_CHARACTERISTIC       "767B22E7-EA6C-B017-286A-55B68310FD9D"

BLEService networkConfigService( BLE_UUID_NETWORK_CONFIG_SERVICE );
BLEBoolCharacteristic networkEnableCharacteristic( BLE_UUID_NETWORK_ENABLE_CHARACTERISTIC , BLERead | BLEWrite );

///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssidName[] = SECRET_SSID;
char ssidPass[] = SECRET_PASS;

WiFiServer server( 80 );
int status = WL_IDLE_STATUS;
bool networkInitialized = false;
bool wifiModeFlag = false;

const int LED_PIN = LED_BUILTIN;

void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );

  pinMode( LED_PIN, OUTPUT );
}

void loop()
{
  if( !networkInitialized )
  {
    if( !wifiModeFlag )
    {
      Serial.print( "Switch to BLE: " );
      if( !switch2BleMode() )
      {
        Serial.println( "failed" );
      }
      else
      {
        networkInitialized = true;
        Serial.println( "success" );
      }
    }
    else
    {
      Serial.print( "Switch to WiFi: " );
      if( !switch2WiFiMode() )
      {
        Serial.println( "failed" );
      }
      else
      {
        networkInitialized = true;
        Serial.println( "success" );
      }
    }
  }
  else
  {
    if( !wifiModeFlag )
    {
      bleMode();
    }
    else
    {
      wifiMode();
    }
  }
}


void bleMode()
{
  BLEDevice central = BLE.central();

  if ( central )
  {
    Serial.print( "Connected to central: " );
    Serial.println( central.address() );

    while ( central.connected() )
    {
      if( networkEnableCharacteristic.written() )
      {
        networkInitialized = false;
        wifiModeFlag = true;
        return;
      }
    }
  }
}


void wifiMode()
{
  int connectCount = 0;

  if ( status != WL_CONNECTED )
  {
    while ( status != WL_CONNECTED )
    {
      connectCount++;
      Serial.print( "WiFi attempt: " );
      Serial.println( connectCount );

      if( connectCount > 10 )
      {
        networkInitialized = false;
        wifiModeFlag = false;
        Serial.println( "WiFi connection failed" );
        return;
      }
      Serial.print( "Attempting to connect to SSID: " );
      Serial.println( ssidName );

      status = WiFi.begin( ssidName, ssidPass );

      if( status != WL_CONNECTED )
      {
        // wait 10 seconds for connection:
        delay( 10000 );
      }
    }
    printWiFiStatus();

    server.begin();
  }
  else
  {
    WiFiClient client = server.available();

    if ( client )
    {
      String currentLine = "";
      while ( client.connected() )
      {
        if ( client.available() )
        {
          char c = client.read();
          if ( c == '\n' )
          {
            // if the current line is blank, you got two newline characters in a row.
            // that's the end of the client HTTP request, so send a response:
            if ( currentLine.length() == 0 )
            {
              // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
              // and a content-type so the client knows what's coming, then a blank line:
              client.println( "HTTP/1.1 200 OK" );
              client.println( "Content-type:text/html" );
              client.println();

              // the content of the HTTP response follows the header:
              client.print( "Click <a href=\"/H\">here</a> turn the LED on<br>" );
              client.print( "Click <a href=\"/L\">here</a> turn the LED off<br><br>" );
              client.print( "Click <a href=\"/B\">here</a> disconnect WiFi and start Bluetooth<br>" );

              // The HTTP response ends with another blank line:
              client.println();
              break;
            }
            else        // if you got a newline, then clear currentLine:
            {
              if ( currentLine.startsWith( "GET /H" ) )
              {
                digitalWrite( LED_PIN, HIGH );
              }
              if ( currentLine.startsWith( "GET /L" ) )
              {
                digitalWrite( LED_PIN, LOW );
              }
              if ( currentLine.startsWith( "GET /B" ) )
              {
                // GET /B switch to Bluetooth
                networkInitialized = false;
                wifiModeFlag = false;
              }
              currentLine = "";
            }
          }
          else if ( c != '\r' )
          {
            currentLine += c;
          }
        }
      }
      client.stop();
    }
  }
}

bool switch2BleMode()
{
  if ( !BLE.begin() )
  {
    return false;
  }

  // set advertised local name and service UUID:
  BLE.setDeviceName( "Arduino Nano 33 IoT" );
  BLE.setLocalName( "Arduino Nano 33 IoT" );
  BLE.setAdvertisedService( networkConfigService );

  // add the characteristic to the service
  networkConfigService.addCharacteristic( networkEnableCharacteristic );

  // add service
  BLE.addService( networkConfigService );

  // set the initial value for the characeristic:
  networkEnableCharacteristic.writeValue( false );

  BLE.advertise();

  return true;
}


bool switch2WiFiMode()
{
  BLE.stopAdvertise();
  BLE.end();

  status = WL_IDLE_STATUS;

  // Re-initialize the WiFi driver
  // This is currently necessary to switch from BLE to WiFi
  wiFiDrv.wifiDriverDeinit();
  wiFiDrv.wifiDriverInit();

  return true;
}

void printWiFiStatus()
{
  Serial.print( "SSID: " );
  Serial.println( WiFi.SSID() );

  IPAddress ip = WiFi.localIP();
  Serial.print( "IP address: " );
  Serial.println( ip );

  long rssi = WiFi.RSSI();
  Serial.print( "Signal strength (RSSI):" );
  Serial.print( rssi );
  Serial.println( " dBm" );
}

jerteach

I have cleaned up my code and did some more experimenting.
Wow. That is really sweet. Very fancy and easy how you communicate from the web page to the Arduino. Everything worked for me immediately. Thank you for sharing. That should be a great base for my project.

jerteach

#8
Jan 12, 2020, 04:56 pm Last Edit: Jan 13, 2020, 07:41 am by jerteach
I have cleaned up my code and did some more experimenting. I do not need to reset the Nina module but reinitialize the WiFi driver when switching to WiFi.
I am still working on code to generate a webpage, then read a few BLE devices then update the webpage. All my test code is on my github at https://github.com/hpssjellis/everything-nrf52840-usb-dongles/tree/master/code/simple-ble-led





Quick question for Klaus_K or anyone else, when you restart WiFi you use the below code (Thank you very much, no idea how I would have figured that out myself):


Code: [Select]

#include "utility/wifi_drv.h"

  // Re-initialize the WiFi driver
  // This is currently necessary to switch from BLE to WiFi
  wiFiDrv.wifiDriverDeinit();
  wiFiDrv.wifiDriverInit();




Curious what, in your opinion, is needed to shutdown WiFi and shutdown BLE. Also is there code to start BLE?




I  will test things in the next few days, but I am guessing after basic setup is done the pseudo code for starts and stops is:


After some testing these commands seem to work individually. Next step is to put them together.




Code: [Select]


//Start Wifi
  wiFiDrv.wifiDriverDeinit();
  wiFiDrv.wifiDriverInit();
  status = WiFi.begin(ssid, pass);
  server.begin();



// End Wifi    

  WiFi.end()    


//Start BLE
    BLE.begin();
    BLE.scan();



// End BLE
   BLE.stopAdvertise();   //don't think this is needed as I am reading other BLE devices
   BLE.stopScan();
   BLE.end();




 

Klaus_K

If you want to stop WiFi you can use the WiFi.end() function.

https://www.arduino.cc/en/Reference/WiFiNINAEnd

To enable BLE you only need the code from my functions. BLE.begin(), setup a GATT and start advertising.

There is also a pin to reset the Nina module. I thought I needed that to switch from BLE to WiFi but it turned out it was not necessary.

jerteach

#10
Jan 13, 2020, 06:26 pm Last Edit: Jan 13, 2020, 08:22 pm by jerteach
So I basically have what I want working and will show the code once it is tidied up. Just stuck on something that I will use trial and error to probably solve today. Wondering if anyone has a solution.

I barely understand the following code but I need to rewrite it to return a  boolean result, not to do a serial print. Any suggestions?

It is given the hex numbers 0x00 or 0x01 from a BLE device and returns a serial print of "00" if the LED is off and "01" if the LED is on. I need it to return true or false. Here is the working code and my latest attempt below it:


Code: [Select]


// helper function to print HEX variables, takes from device 0x00 or 0x01 Hex numbers

void printData(const unsigned char data[], int length) {
  for (int i = 0; i < length; i++) {
    unsigned char b = data[i];
    if (b < 16) {
      Serial.print("0");
    }
    Serial.print(b, HEX);
  }
}






// And here is my attempt to return a boolean.
//Ignored the length as it is fixed at 1
//Probably could make this much shorter.


bool ledIsOn(const unsigned char data[]) {
  for (int i = 0; i < 1; i++) {
    unsigned char b = data[i];
    if (b <= 0) {
      return false;
    } else {
      return true;
   }
  }
}




// Any suggestions from someone who actually understands the code snipets.


Klaus_K

You do not need the for loop. One of the two returns will be executed in any case in the first loop.

Code: [Select]
unsigned char b = data[0];
if (b <= 0)
{
  return false;
}
else
{
  return true;
}

jerteach

You do not need the for loop.
Thanks Klaus_K, that was a lot easier than it originally looked. I guess the first method was for any size HEX value.


I am finding an interesting little BLE LED bug.

When you use nrf-connect to change the value of the LED, say turn it on, then reset the device, the LED stays on, but my program does not know that the device LED is turned on, it assumes the BLE device LED on startup is off. Not sure if this is a bug or something I have programmed.



Klaus_K

I would not call it a bug. Call it a feature. :)
You usually do not care about memory locations that are no longer used. There are plenty of bytes in your microcontroller that are in a random state from the last operations when they were used. In case of an I/O you are just in the position to notice this fact and sometimes its a bad situation.
I can imagine different scenarios. In some you need to make sure the I/O is reset in others you will be happy to have the I/O stay in the state it was last.

jerteach

#14
Jan 14, 2020, 08:16 am Last Edit: Jan 15, 2020, 10:19 pm by jerteach
I would not call it a bug. Call it a feature. :)
Actually it is a bit strange and probably a bug in my Simple-LED-Peripheral program, but the Nano 33 IoT and the Nano 33 BLE seem to compile differently. The Nano 33 IoT seems to work fine. I can set the LED off at startup and as a characteristic it is off, but the Nano 33 BLE seems to always set the LED on, but as a characteristic it thinks it is off.

Also I think the button only works on the second attempt, which sounds like something is not being setup properly.

Here is a link to my Simple-LED-peripheral or see the code below  


Code: [Select]



/*
  simple-led-peripheral.ino

  This example creates a BLE peripheral with service that contains a
  characteristic to control an LED and another characteristic that
  represents the state of the button.

  The circuit:
  - Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
    Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
  - Button connected to pin 4

  You can use a generic BLE central app, like LightBlue (iOS and Android) or
  nRF Connect (Android), to interact with the services and characteristics
  created in this sketch.

  This example code is in the public domain.
  Edited By Jeremy Ellis
  Jan 2020

Can use Simple-LED-Central To communicated with this.
  
*/


#include <ArduinoBLE.h>

// Global constants and variables
const int ledPin = LED_BUILTIN; // set ledPin to on-board LED
const int buttonPin = 4;        // set buttonPin to digital pin 4
int myOldButtonValue;

BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service

// allow remote device to read and write
BLEByteCharacteristic ledCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);

// create button characteristics
BLEByteCharacteristic buttonCharacteristic("19B10012-E8F2-537E-4F6C-D104768A1214", BLERead | BLENotify);


void setup() {
  
  pinMode(ledPin, OUTPUT);          
  pinMode(buttonPin, INPUT_PULLDOWN); // use button pin as an input

  // Note: No serial needing a line feed so this works with battery power.
  // just to prove it is running, flash LED twice, since no serial.
  digitalWrite(ledPin, HIGH);
  delay(400);
  digitalWrite(ledPin, LOW);
  delay(400);

  digitalWrite(ledPin, HIGH);
  delay(400);
  digitalWrite(ledPin, LOW);
  delay(400);

  // begin initialization
  if (!BLE.begin()) {

    digitalWrite(ledPin, HIGH);
    while (1);  // kills it here
  }

  // set the local name peripheral advertises
  BLE.setLocalName("SimpleLED");
  
  // set the service
  BLE.setAdvertisedService(ledService);

  // add both characteristics to the service
  ledService.addCharacteristic(ledCharacteristic);
  ledService.addCharacteristic(buttonCharacteristic);

  // add the service
  BLE.addService(ledService);

  // set default values
  ledCharacteristic.writeValue(0);
  buttonCharacteristic.writeValue(0);

  // start advertising
  BLE.advertise();

}


void loop() {
  
  BLE.poll();  // poll for BLE events

  // Read button, compare and set button
  int myButtonValue = digitalRead(buttonPin);

  if (myOldButtonValue != myButtonValue){
    myOldButtonValue = myButtonValue;
    digitalWrite(ledPin, myOldButtonValue);
  }

  // Check if external program controlling button
  else if (ledCharacteristic.written()){
    digitalWrite(ledPin, ledCharacteristic.value());
  }

  delay(10);
  
}







Some really good news, is that my Wifi-BLE-generic program is working here I could not get @KLAUS_K buttons working but what I set out to do works fine. Thanks to both @Klaus_K and @pert. It generates a webpage showing the analog pins but also searches for any BLE device with "LED" in it's name and shows if the LED is on or off.

As with the above issue it works fine on the Nano 33 IoT but has some minor issue on the Nano 33 BLE.

Fairly happy though.

Go Up