Hello everyone,
I'm working on a home automation project using an Arduino Nano 33 IoT and an Arduino Nano 33 BLE. The goal is to control a relay connected to the Nano BLE from the Nano IoT via BLE, and communicate with Home Assistant via WiFi using MQTT.
Why, you might ask:
Three reasons actually:
- I am running Home assistant on raspberry pi, home assistant prefers the MQTT and MQTT only works on WiFi
- I am planning to have another Arduino even further away, so this Nano IOT would be used in a way as a range extendor.
- I have multiple Nano BLE boards I want to make use of.
Current Setup:
- Arduino Nano 33 IoT:
- Connects to Home Assistant via Wi-Fi using MQTT.
- Switches to BLE to control the relay on the Nano BLE.
- Switches back to Wi-Fi after controlling the relay or checking its status.
- Arduino Nano 33 BLE:
- Controls a relay.
- Advertises a BLE service with a characteristic to control the relay.
The Issue:
Considering Nano IOT cannot run both communications with the same time, I am forced to switch. The goal is to have the Nano IOT connected on the Wi-Fi most of the time, and only connect via BLE whenever it receives a command from Home Assistant or every 1-5 minutes to check if the state changed.
I’m facing difficulties with reliably switching between BLE and Wi-Fi on the Arduino Nano 33 IoT. Although the Wi-Fi connection and MQTT communication work fine, the BLE connection to the Nano BLE fails with messages indicating an inability to connect.
Each of the communications work perfectly fine on its own, so I am suspecting the issue lies in reusing the antenna (switching between BLE and Wi-Fi)
Observations:
- BLE scanning finds the peripheral, but connection attempts always fail.
- The connection sequence seems to be correctly followed, but the BLE connection is unstable.
Example output:
WiFi connected
IP address:
192.168.64.117
Attempting MQTT connection... to broker: 192.168.64.115 at port: 1883
connected
Switching to BLE
Found 40:33:47:7e:d3:03 'NanoBLERelay' 19b10010-e8f2-537e-4f6c-d104768a1214
Attempting to connect to peripheral...
Failed to connect to peripheral
Here you can find the code for NANO IOT:
#include <WiFiNINA.h>
#include <PubSubClient.h>
#include <ArduinoBLE.h>
#include "utility/wifi_drv.h"
// WiFi credentials
const char* ssid = "************";
const char* password = "***************";
// MQTT broker details
const char* mqtt_server = "192.168.64.115";
const int mqtt_port = 1883;
const char* mqtt_user = "homeassistant-mosquitto";
const char* mqtt_password = "************";
// BLE UUIDs
#define BLE_UUID_RELAY_SERVICE "19B10010-E8F2-537E-4F6C-D104768A1214"
#define BLE_UUID_RELAY_CHARACTERISTIC "19B10011-E8F2-537E-4F6C-D104768A1214"
WiFiClient wifiClient;
PubSubClient client(wifiClient);
bool wifiConnected = false;
bool bleConnected = false;
bool checkBLE = false;
bool controlBLE = false;
bool relayCommand = false;
unsigned long lastSwitchTime = 0;
const unsigned long switchInterval = 60000; // 1 minute interval
void setup() {
Serial.begin(9600);
while (!Serial);
pinMode(LED_BUILTIN, OUTPUT);
Serial.println("Starting...");
// Initialize WiFi
setup_wifi();
// Initialize MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqttCallback);
// Ensure MQTT client is connected
reconnect();
}
void loop() {
unsigned long currentTime = millis();
if (checkBLE || (currentTime - lastSwitchTime > switchInterval)) {
lastSwitchTime = currentTime;
switchToBLE();
checkRelayStatusBLE();
checkBLE = false;
switchToWiFi();
}
// Poll MQTT messages
if (wifiConnected) {
if (!client.connected()) {
reconnect();
}
client.loop();
}
// Control BLE if instructed by MQTT
if (controlBLE) {
switchToBLE();
controlRelayBLE();
controlBLE = false;
switchToWiFi();
}
}
void setup_wifi() {
delay(10);
// Connect to WiFi
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis();
// Wait for connection with timeout
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 30000) { // 30 seconds timeout
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
wifiConnected = true;
} else {
Serial.println("");
Serial.println("Failed to connect to WiFi");
wifiConnected = false;
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
Serial.print(" to broker: ");
Serial.print(mqtt_server);
Serial.print(" at port: ");
Serial.println(mqtt_port);
// Attempt to connect
if (client.connect("ArduinoNanoIoT", mqtt_user, mqtt_password)) {
Serial.println("connected");
// Subscribe to control topic
client.subscribe("home/relay_control");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String incoming = "";
for (unsigned int i = 0; i < length; i++) {
incoming += (char)payload[i];
}
incoming.trim();
// Control relay based on MQTT message
if (String(topic) == "home/relay_control") {
if (incoming == "ON") {
relayCommand = true;
controlBLE = true;
} else if (incoming == "OFF") {
relayCommand = false;
controlBLE = true;
}
}
}
void switchToBLE() {
Serial.println("Switching to BLE");
if (WiFi.status() == WL_CONNECTED) {
WiFi.disconnect();
WiFi.end();
wifiConnected = false;
}
// Initialize BLE
if (!BLE.begin()) {
Serial.println("Starting BLE failed!");
while (1);
}
// Set advertised service and add characteristics
BLE.setDeviceName("Arduino Nano 33 IoT");
BLE.setLocalName("Arduino Nano 33 IoT");
BLE.setAdvertisedServiceUuid(BLE_UUID_RELAY_SERVICE);
BLE.scanForUuid(BLE_UUID_RELAY_SERVICE);
delay(5000); // Ensure BLE is ready and scanning
bleConnected = true;
}
void switchToWiFi() {
Serial.println("Switching to WiFi");
if (BLE.connected()) {
BLE.disconnect();
}
BLE.end();
bleConnected = false;
setup_wifi();
if (wifiConnected) {
reconnect();
}
}
void controlRelayBLE() {
BLEDevice peripheral = connectToPeripheral();
if (peripheral) {
BLECharacteristic relayCharacteristic = peripheral.characteristic(BLE_UUID_RELAY_CHARACTERISTIC);
if (relayCharacteristic) {
Serial.println("Relay characteristic found");
if (relayCommand) {
relayCharacteristic.writeValue((uint8_t)0x01);
Serial.println("Relay turned ON via BLE");
} else {
relayCharacteristic.writeValue((uint8_t)0x00);
Serial.println("Relay turned OFF via BLE");
}
delay(5000); // Ensure the command is executed
}
peripheral.disconnect();
}
}
void checkRelayStatusBLE() {
BLEDevice peripheral = connectToPeripheral();
if (peripheral) {
BLECharacteristic relayCharacteristic = peripheral.characteristic(BLE_UUID_RELAY_CHARACTERISTIC);
if (relayCharacteristic) {
Serial.println("Relay characteristic found");
uint8_t relayState;
relayCharacteristic.readValue(relayState);
Serial.print("Relay state: ");
Serial.println(relayState);
delay(5000); // Ensure the state is read
}
peripheral.disconnect();
}
}
BLEDevice connectToPeripheral() {
BLEDevice peripheral;
// Retry connecting to peripheral to ensure connection is established
for (int attempt = 0; attempt < 5; attempt++) {
BLE.scanForUuid(BLE_UUID_RELAY_SERVICE); // Ensure scan is active
delay(2000); // Wait for scan results
peripheral = BLE.available();
if (peripheral) {
Serial.print("Found ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
for (int connAttempt = 0; connAttempt < 3; connAttempt++) {
Serial.println("Attempting to connect to peripheral...");
if (peripheral.connect()) {
Serial.println("Connected to peripheral");
if (peripheral.discoverAttributes()) {
Serial.println("Attributes discovered");
return peripheral;
} else {
Serial.println("Attribute discovery failed!");
peripheral.disconnect();
}
} else {
Serial.println("Failed to connect to peripheral");
}
delay(5000); // Delay between connection attempts
}
} else {
Serial.println("Peripheral not found in scan results.");
}
delay(2000); // Delay between scan attempts
}
return BLEDevice();
}
and the code for NANO BLE:
#include <ArduinoBLE.h>
const int relayPin = 2; // Relay pin
const int buttonPin = 3; // Button pin
const int ledPin = LED_BUILTIN; // Built-in LED for BLE connection status
bool relayState = false;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // Debounce delay
const unsigned long relayDelay = 1000; // 1 second delay between relay state changes
unsigned long lastRelayTime = 0;
BLEService relayService("19B10010-E8F2-537E-4F6C-D104768A1214"); // Relay service
// Relay characteristic
BLEByteCharacteristic relayCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
void setup() {
Serial.begin(9600);
pinMode(relayPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Begin initialization
if (!BLE.begin()) {
Serial.println("Starting BLE failed!");
while (1);
}
// Set local name and advertised service
BLE.setLocalName("NanoBLERelay");
BLE.setAdvertisedService(relayService);
// Add characteristics to service
relayService.addCharacteristic(relayCharacteristic);
// Add service
BLE.addService(relayService);
// Initialize characteristic values
relayCharacteristic.writeValue(0);
// Start advertising
BLE.advertise();
Serial.println("Bluetooth® device active, waiting for connections...");
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
Serial.print("Connected to central: ");
Serial.println(central.address());
digitalWrite(ledPin, HIGH);
while (central.connected()) {
BLE.poll();
// Check button press
checkButton();
// Handle relay state change from BLE
if (relayCharacteristic.written()) {
relayState = relayCharacteristic.value();
digitalWrite(relayPin, relayState ? HIGH : LOW);
Serial.print("Relay state changed via BLE to: ");
Serial.println(relayState ? "ON" : "OFF");
}
}
Serial.print("Disconnected from central: ");
Serial.println(central.address());
digitalWrite(ledPin, LOW);
} else {
// Ensure relay control is available even when BLE is not connected
checkButton();
}
}
void checkButton() {
unsigned long currentTime = millis();
int buttonState = digitalRead(buttonPin);
if (buttonState == LOW && (currentTime - lastDebounceTime) > debounceDelay) {
if ((currentTime - lastRelayTime) > relayDelay) {
relayState = !relayState;
relayCharacteristic.writeValue(relayState);
digitalWrite(relayPin, relayState ? HIGH : LOW);
Serial.print("Relay state toggled via button to: ");
Serial.println(relayState ? "ON" : "OFF");
lastRelayTime = currentTime;
}
lastDebounceTime = currentTime;
}
}
Thanks for all the help!