Loading... Search articles

Search for articles

Sorry, but we couldn't find any matches...

But perhaps we can interest you in one of our more popular articles?
Creating an IoT based Flutter app to interact with any home electrical equipment

Creating an IoT based Flutter app to interact with any home electrical equipment

Apr 2, 2020

Use M1 Mac mini VMs by default with Codemagic🚀 Build faster

Written by Souvik Biswas

It always looks cool to control any electrical equipment with a mobile device remotely without even going near it.

Today, we will demonstrate how you can give any normal AC power plug, socket or switch a touch of magic using IoT. This will enable us to control any electrical equipment in our house using a mobile device, with the help of an app (of course, built with Flutter) having Bluetooth capability.

This article will cover a lot of things:

  • Integrating Bluetooth in a Flutter app (for communicating with the IoT device)
  • Connecting various modules to Arduino (Bluetooth module and a 5V relay)
  • Setting up Arduino (to send and receive signals)
Tutorial by Codemagic: Creating an IoT based Flutter app to interact with any home electrical equipment. Wow!
Click to tweet

Let’s get started by integrating Bluetooth in a Flutter app.

Integrating Bluetooth

The final app UI will look like this:

For this project, we will be using a plugin called flutter_bluetooth_serial. There is another awesome plugin for integrating Bluetooth in a Flutter app, flutter_blue.

I am using flutter_bluetooth_serial plugin in this project because it has a lot more features and gives much greater control over the Bluetooth connection while communicating with other devices.

The downside of this plugin is that it only has support for Android as of now. Any iOS developer who wants to contribute to this project can add iOS support to it (there is also an issue open on the plugin’s GitHub repo for this).

Defining the basic structure

Create a new Flutter app and add the following to the main.dart file after removing all of the Flutter Demo Counter app code:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BluetoothApp(),
    );
  }
}

class BluetoothApp extends StatefulWidget {
  @override
  _BluetoothAppState createState() => _BluetoothAppState();
}

class _BluetoothAppState extends State<BluetoothApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(),
    );
  }
}

This is the basic structure for starting our project. We have to use a StatefulWidget for the BluetoothApp class because we will need to mutate the state multiple times during the lifecycle of the app.

Enabling Bluetooth and retrieving devices

Now that we have the basic structure of the app, we can focus on enabling the Bluetooth connection of the user’s device and retrieve the paired devices.

  • Initialize some of the variables which will be required (we will be adding more class member variables as needed later while building the app):

    // main.dart
    
    class _BluetoothAppState extends State<BluetoothApp> {
      // Initializing the Bluetooth connection state to be unknown
      BluetoothState _bluetoothState = BluetoothState.UNKNOWN;
    
      // Get the instance of the Bluetooth
      FlutterBluetoothSerial _bluetooth = FlutterBluetoothSerial.instance;
    
      // Track the Bluetooth connection with the remote device
      BluetoothConnection connection;
    
      // To track whether the device is still connected to Bluetooth
      bool get isConnected => connection != null && connection.isConnected;
    
      // ...
    }
    
  • Inside initState(), we first have to get the current Bluetooth state. If the state indicates that Bluetooth is not turned on, request that the user give Bluetooth permissions for enabling Bluetooth on their device.

    // main.dart
    // Inside _BluetoothAppState class
    
    // This member variable will be used for tracking
    // the Bluetooth device connection state
    int _deviceState;
    
    @override
    void initState() {
      super.initState();
    
      // Get current state
      FlutterBluetoothSerial.instance.state.then((state) {
        setState(() {
          _bluetoothState = state;
        });
      });
    
      _deviceState = 0; // neutral
    
      // If the Bluetooth of the device is not enabled,
      // then request permission to turn on Bluetooth
      // as the app starts up
      enableBluetooth();
    
      // Listen for further state changes
      FlutterBluetoothSerial.instance
          .onStateChanged()
          .listen((BluetoothState state) {
        setState(() {
          _bluetoothState = state;
    
          // For retrieving the paired devices list
          getPairedDevices();
        });
      });
    }
    
  • Now, we have to define the two functions:

    • enableBluetooth() for getting Bluetooth permissions from the user.
    • getPairedDevices() for retrieving the paired devices list.

    The function implementation for enabling Bluetooth:

    // main.dart
    // Inside _BluetoothAppState class
    
    Future<void> enableBluetooth() async {
      // Retrieving the current Bluetooth state
      _bluetoothState = await FlutterBluetoothSerial.instance.state;
    
      // If the Bluetooth is off, then turn it on first
      // and then retrieve the devices that are paired.
      if (_bluetoothState == BluetoothState.STATE_OFF) {
        await FlutterBluetoothSerial.instance.requestEnable();
        await getPairedDevices();
        return true;
      } else {
        await getPairedDevices();
      }
      return false;
    }
    

    The function implementation for retrieving the Bluetooth devices list:

    // main.dart
    // Inside _BluetoothAppState class
    
    // Define a new class member variable
    // for storing the devices list
    List<BluetoothDevice> _devicesList = [];
    
    Future<void> getPairedDevices() async {
      List<BluetoothDevice> devices = [];
    
      // To get the list of paired devices
      try {
        devices = await _bluetooth.getBondedDevices();
      } on PlatformException {
        print("Error");
      }
    
      // It is an error to call [setState] unless [mounted] is true.
      if (!mounted) {
        return;
      }
    
      // Store the [devices] list in the [_devicesList] for accessing
      // the list outside this class
      setState(() {
        _devicesList = devices;
      });
    }
    
  • In order to avoid any memory leaks, we will be defining the dispose() method to make sure that the connection is closed and resources are freed.

    // Define a member variable to track
    // when the disconnection is in progress
    bool isDisconnecting = false;
    
    @override
    void dispose() {
      if (isConnected) {
        isDisconnecting = true;
        connection.dispose();
        connection = null;
      }
    
      super.dispose();
    }
    

UI for enabling Bluetooth

So, we have all the methods required for establishing a proper Bluetooth connection with a device, and now we have to map them to some UI widgets.

First of all, we will be adding a Switch widget which can toggle between only two modes (on/off).

The code for this component is available below:

// main.dart
// Inside _BluetoothAppState class

// Define this member variable for storing
// the current device connectivity status
bool _connected = false;

// Inside build method
Switch(
  value: _bluetoothState.isEnabled,
  onChanged: (bool value) {
    future() async {
      if (value) {
        // Enable Bluetooth
        await FlutterBluetoothSerial.instance
            .requestEnable();
      } else {
        // Disable Bluetooth
        await FlutterBluetoothSerial.instance
            .requestDisable();
      }

      // In order to update the devices list
      await getPairedDevices();
      _isButtonUnavailable = false;

      // Disconnect from any device before
      // turning off Bluetooth
      if (_connected) {
        _disconnect();
      }
    }

    future().then((_) {
      setState(() {});
    });
  },
)

Here, the Switch will toggle between on/off depending on whether the Bluetooth connection is enabled/disabled. Irrespective of whether Bluetooth is enabled or disabled by the toggle, the getPairedDevices() method is called in order to refresh the device list (i.e., if Bluetooth is enabled then the device list will be updated, and if it is disabled then the list content will be made empty).

UI for paired devices list

We will be using a DropdownButton widget for displaying the list of paired devices.

The code for it is:

// main.dart
// Inside _BluetoothAppState class

// Define this member variable for storing
// each device from the dropdown items
BluetoothDevice _device;

DropdownButton(
  items: _getDeviceItems(),
  onChanged: (value) =>
      setState(() => _device = value),
  value: _devicesList.isNotEmpty ? _device : null,
),

Define the method _getDeviceItems() for storing the device names in a list. When there are no devices to store in the list, a text value, ‘NONE’, is stored as the only item.

// main.dart
// Inside _BluetoothAppState class

List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
  List<DropdownMenuItem<BluetoothDevice>> items = [];
  if (_devicesList.isEmpty) {
    items.add(DropdownMenuItem(
      child: Text('NONE'),
    ));
  } else {
    _devicesList.forEach((device) {
      items.add(DropdownMenuItem(
        child: Text(device.name),
        value: device,
      ));
    });
  }
  return items;
}

Connecting to a device via Bluetooth

Now we have come to the main part of the process: connecting to the Bluetooth device.

  • Check if a device is selected from the list.

    // main.dart
    // Inside _BluetoothAppState class
    
    void _connect() async {
      if (_device == null) {
        show('No device selected');
      } else {
        // If a device is selected from the
        // dropdown, then use it here
      }
    }
    
  • If a device is selected, then try to establish the connection.

    // Define the following inside else block
    // of the previous
    
    // Making sure the device is not connected
    if (!isConnected) {
      // Trying to connect to the device using
      // its address
      await BluetoothConnection.toAddress(_device.address)
          .then((_connection) {
        print('Connected to the device');
        connection = _connection;
    
        // Updating the device connectivity
        // status to [true]
        setState(() {
          _connected = true;
        });
    
        // This is for tracking when the disconnecting process
        // is in progress which uses the [isDisconnecting] variable
        // defined before.
        // Whenever we make a disconnection call, this [onDone]
        // method is fired.
        connection.input.listen(null).onDone(() {
          if (isDisconnecting) {
            print('Disconnecting locally!');
          } else {
            print('Disconnected remotely!');
          }
          if (this.mounted) {
            setState(() {});
          }
        });
      }).catchError((error) {
        print('Cannot connect, exception occurred');
        print(error);
      });
      show('Device connected');
    }
    

    The listener is set to the connection to track it. This will be helpful while we try to disconnect from the device. The onDone() method will be triggered when the dispose(), finish() or close() method is called from the connection, all of which cause the connection to disconnect.

Disconnecting from the device

To disconnect from the Bluetooth device, we have to call the close() method on the connection variable.

// main.dart
// Inside _BluetoothAppState class

void _disconnect() async {
  // Closing the Bluetooth connection
  await connection.close();
  show('Device disconnected');

  // Update the [_connected] variable
  if (!connection.isConnected) {
    setState(() {
      _connected = false;
    });
  }
}

UI for the connect/disconnect button

The UI for the connect/disconnect button is very simple. The code for it is available below:

// main.dart
// Inside _BluetoothAppState class

RaisedButton(
  onPressed: _isButtonUnavailable
      ? null
      : _connected ? _disconnect : _connect,
  child:
      Text(_connected ? 'Disconnect' : 'Connect'),
)

Send messages to the Bluetooth device

In this app, we will only be adding on/off functionality, so that a user can turn on or turn off their Bluetooth device from the app.

For turning on the device, we will be sending “1” as the message, and for turning off the device, we will be sending “0” as the message.

The code for this functionality is available below:

// main.dart
// Inside _BluetoothAppState class

// Method to send message
// for turning the Bluetooth device on
void _sendOnMessageToBluetooth() async {
  connection.output.add(utf8.encode("1" + "\r\n"));
  await connection.output.allSent;
  show('Device Turned On');
  setState(() {
    _deviceState = 1; // device on
  });
}

// Method to send message
// for turning the Bluetooth device off
void _sendOffMessageToBluetooth() async {
  connection.output.add(utf8.encode("0" + "\r\n"));
  await connection.output.allSent;
  show('Device Turned Off');
  setState(() {
    _deviceState = -1; // device off
  });
}

UI for sending messages via Bluetooth

We will be using two buttons, one for sending an ON instruction to the device and the other one for sending an OFF instruction to the device.

The code for the UI is available below:

// main.dart
// Inside build method of
// _BluetoothAppState class

// ON button
FlatButton(
  onPressed: _connected
      ? _sendOnMessageToBluetooth
      : null,
  child: Text("ON"),
),
// OFF button
FlatButton(
  onPressed: _connected
      ? _sendOffMessageToBluetooth
      : null,
  child: Text("OFF"),
)

With this, all the main functionalities of our Bluetooth app are complete. There can be some UI improvements that can be made in the finished version of the app.

The whole Bluetooth app project is available on GitHub here.

Connecting various modules to Arduino

The hardware components required for creating the controlling device are:

  • Arduino UNO REV3
  • 5V relay module (can have any number of channels, but you will be able to connect more devices with more channels)
  • HC-05 Bluetooth Transceiver Module
  • Jumper wires
  • Some electrical wires
  • Switch (together with an enclosure)

The module pin configurations and circuit diagram are given below:

NOTE: The connection between the relay and the switch is done in such a way that the relay will only function when the switch is kept in off mode (as the NC or Normally Closed terminal of the relay is connected to the positive terminal of the switch).

When the switch is in on mode manually, it will get power directly from the main supply line.

Complete circuit design

Setting up Arduino

You can use Arduino Web Editor for uploading the code to your Arduino Uno device. But before writing and uploading the code, you will need to install a plugin on your system, which is used for detecting and connecting to the Arduino device from the Web Editor.

Just follow the steps here in order to download and set up the Arduino Create Plugin.

After the setup is complete, click on GO TO WEB EDITOR to start writing code for the Arduino device.

You can Sign In using an account on the Arduino Create webpage in order to safely store your projects in the cloud so that you can access them anywhere and anytime you want.

Click on NEW SKETCH to create a new project.

Now, you can write your program here.

Basically, what we have to do is take the input through Bluetooth from the app and turn on/off the relay connected to the switch.

The code is available below:

// Power_outlet.ino

// Variable for storing incoming value
char Incoming_value = 0;

void setup()
{
  // Sets the data rate in bits per second (baud)
  // for serial data transmission
  Serial.begin(9600);

  // Sets digital pin 8 as output pin
  pinMode(8, OUTPUT);

  // Initializes the pin 8 to LOW (i.e., OFF)
  digitalWrite(8, LOW);
}

void loop()
{
  if (Serial.available() > 0)
  {
    // Read the incoming data and store it in the variable
    Incoming_value = Serial.read();

    // Print value of Incoming_value in Serial monitor
    Serial.print(Incoming_value);
    Serial.print("\n");

    // Checking whether value of the variable
    // is equal to 0
    if (Incoming_value == '0')
      digitalWrite(8, LOW); // If value is 0, turn OFF the device

    // Checking whether value of the variable
    // is equal to 1
    else if (Incoming_value == '1')
      digitalWrite(8, HIGH); // If value is 1, turn ON the device
  }

  // Adding a delay before running the loop again
  delay(1);

}

Here, the Arduino signal pin is 8, which is connected to IN2 of the relay.

Connect the Arduino to your system.

You can check whether there are any syntactical mistakes in the code by clicking on the check mark beside the connected device name.

REMEMBER TO DO THIS STEP: Before uploading the code to Arduino, disconnect the wire connected to the RX pin on the Arduino board only (the top, rightmost wire in the diagram).

Click on the Upload button.

After you get the SUCCESSFUL upload message on the console, reconnect the RX pin wire.

Hoorah! You have finally completed this long project.

Now, you can test whether the Bluetooth functionality of the app is working correctly and controlling the relay.

The GitHub repo of this app is available here.


Souvik Biswas is a passionate Mobile App Developer (Android and Flutter). He has worked on a number of mobile apps throughout his journey. Loves open source contribution on GitHub. He is currently pursuing a B.Tech degree in Computer Science and Engineering from Indian Institute of Information Technology Kalyani. He also writes Flutter articles on Medium - Flutter Community.

Latest articles

Show more posts