NodeMCU: Turn ON/OFF LED using WebSocket on Mobile App

Are you making an IoT Android/iOS app to control NodeMCU (ESP8266)? I recommend you to use WebSocket to communicate between App and NodeMCU. If you are using HTTP web-server then it will be slow communication between Chip and Mobile App therefore use WebSocket instead. Follow the guideline and codes below. This is the basic project for beginners to learn Turn ON/OFF LED using WebSocket using Flutter and NOdeMCU (ESP8266). Communication using WebSocket is very useful for instant controlling bots.

We are using PlatromIO on Visual Studio Code as an IDE. Download the WebSocket.zip CPP library for NodeMCU. Extract the zip folder to lib/ folder.

#include <Arduino.h>
#include <ESP8266WiFi.h> //import for wifi functionality
#include <WebSocketsServer.h> //import for websocket

#define ledpin D2 //defining the OUTPUT pin for LED

const char *ssid =  "HelloHPC - Wifi";   //Wifi SSID (Name)   
const char *pass =  "12345678"; //wifi password

WebSocketsServer webSocket = WebSocketsServer(81); //websocket init with port 81

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
//webscket event method
    String cmd = "";
    switch(type) {
        case WStype_DISCONNECTED:
            Serial.println("Websocket is disconnected");
            //case when Websocket is disconnected
            break;
        case WStype_CONNECTED:{
            //wcase when websocket is connected
            Serial.println("Websocket is connected");
            Serial.println(webSocket.remoteIP(num).toString());
            webSocket.sendTXT(num, "connected");}
            break;
        case WStype_TEXT:
            cmd = "";
            for(int i = 0; i < length; i++) {
                cmd = cmd + (char) payload[i]; 
            } //merging payload to single string
            Serial.println(cmd);

            if(cmd == "poweron"){ //when command from app is "poweron"
                digitalWrite(ledpin, HIGH);   //make ledpin output to HIGH  
            }else if(cmd == "poweroff"){
                digitalWrite(ledpin, LOW);    //make ledpin output to LOW on 'pweroff' command.
            }

             webSocket.sendTXT(num, cmd+":success");
             //send response to mobile, if command is "poweron" then response will be "poweron:success"
             //this response can be used to track down the success of command in mobile app.
            break;
        case WStype_FRAGMENT_TEXT_START:
            break;
        case WStype_FRAGMENT_BIN_START:
            break;
        case WStype_BIN:
            hexdump(payload, length);
            break;
        default:
            break;
    }
}

void setup() {
   pinMode(ledpin, OUTPUT); //set ledpin (D2) as OUTPUT pin
   Serial.begin(9600); //serial start

   Serial.println("Connecting to wifi");
   
   IPAddress apIP(192, 168, 0, 1);   //Static IP for wifi gateway
   WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); //set Static IP gateway on NodeMCU
   WiFi.softAP(ssid, pass); //turn on WIFI

   webSocket.begin(); //websocket Begin
   webSocket.onEvent(webSocketEvent); //set Event for websocket
   Serial.println("Websocket is started");
}

void loop() {
   webSocket.loop(); //keep this line on loop method
}

Now on the Flutter part, add web_socket_channel plugin as a dependency on pubspec.yaml file.

dependencies:
  flutter:
    sdk: flutter
  web_socket_channel: ^1.1.0

Add Internet Permission by adding this line in android/app/src/main/AndroidManifest.xml before <application>

<uses-permission android:name="android.permission.INTERNET"/>

Here we are using ws, non-secure WebSocket protocol. Therefore, add the following lines to AndroidManifest.xml file as well.

<application
android:usesCleartextTraffic="true"

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:web_socket_channel/io.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
          home: WebSocketLed(),
    );
  }
}

//apply this class on home: attribute at MaterialApp()
class WebSocketLed extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return _WebSocketLed(); 
  }
}

class _WebSocketLed extends State<WebSocketLed>{
  bool ledstatus; //boolean value to track LED status, if its ON or OFF
  IOWebSocketChannel channel;
  bool connected; //boolean value to track if WebSocket is connected

  @override
  void initState() {
    ledstatus = false; //initially leadstatus is off so its FALSE
    connected = false; //initially connection status is "NO" so its FALSE

    Future.delayed(Duration.zero,() async {
        channelconnect(); //connect to WebSocket wth NodeMCU
    });

    super.initState();
  }

  channelconnect(){ //function to connect 
    try{
         channel = IOWebSocketChannel.connect("ws://192.168.0.1:81"); //channel IP : Port
         channel.stream.listen((message) {
            print(message);
            setState(() {
                 if(message == "connected"){
                      connected = true; //message is "connected" from NodeMCU
                 }else if(message == "poweron:success"){
                      ledstatus = true; 
                 }else if(message == "poweroff:success"){
                      ledstatus = false;
                 }
            });
          }, 
        onDone: () {
          //if WebSocket is disconnected
          print("Web socket is closed");
          setState(() {
                connected = false;
          });    
        },
        onError: (error) {
             print(error.toString());
        },);
    }catch (_){
      print("error on connecting to websocket.");
    }
  }

  Future<void> sendcmd(String cmd) async {
         if(connected == true){
                if(ledstatus == false && cmd != "poweron" && cmd!= "poweroff"){
                    print("Send the valid command");
                }else{
                   channel.sink.add(cmd); //sending Command to NodeMCU
                }
         }else{
            channelconnect();
            print("Websocket is not connected.");
         }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title:Text("LED - ON/OFF NodeMCU"),
        backgroundColor: Colors.redAccent
      ),
      body:Container(
         alignment: Alignment.topCenter, //inner widget alignment to center
         padding: EdgeInsets.all(20),
         child:Column(
           children:[
               Container(
                  child: connected?Text("WEBSOCKET: CONNECTED"):Text("DISCONNECTED")    
                ),

                Container(
                  child: ledstatus?Text("LED IS: ON"):Text("LED IS: OFF")      
                ),

                Container(
                  margin: EdgeInsets.only(top:30),
                  child: FlatButton( //button to start scanning
                  color: Colors.redAccent,
                  colorBrightness: Brightness.dark,
                  onPressed: (){ //on button press
                      if(ledstatus){ //if ledstatus is true, then turn off the led
                          //if led is on, turn off
                          sendcmd("poweroff");
                          ledstatus = false;
                      }else{ //if ledstatus is false, then turn on the led
                          //if led is off, turn on
                          sendcmd("poweron");
                          ledstatus = true;
                      }
                      setState(() {  
                      });
                  }, 
                  child: ledstatus?Text("TURN LED OFF"):Text("TURN LED ON")
                  )
                )
           ],
         )
      ),
    );
  }
}

This is the way you can control NodeMCU using the Android/iOS mobile APP. Follow the guidelines above and integrate it into your project.

6 Comments on this Article

pjdf

ESP:39:40: error: cannot bind non-const lvalue reference of type ’String&’ to an rvalue of type ’String’

2 years ago

dfsd

 

ESP:39:40: error: cannot bind non-const lvalue reference of type ’String&’ to an rvalue of type ’String’

 

2 years ago

tsdf

’D2’ was not declared in this scope

 

2 years ago

mahdi

hi.

when I connect to esp’s wifi the application says websocket: connected and after some seconds it says disconnected and when I tap on turn on or off led button this cycle happens . how can I solve this problem?

 

2 years ago

Hari Prasad Chaudhary

Inside a loop() of cpp code, check the WebSocket connection on each loop, better to use async code, and if it is disconnected, reconnect the WebSocket. 

2 years ago

mahdi

thanks ,

but I didn’t undrestand how can I do the things you said.

2 years ago


Please Wait...