NODE.JS SMPP Server & Client

Introduction

This is a complete implementation of SMPP v5.0 in node.js, with support for custom commands and TLVs.

SMPP v5.0, by design, is backward compatible with v3.4, so you would be able to use this module with 3.4 implementations. Even you can use this module with 3.3 implementations as far as you don’t use TLV parameters and don’t bind in transceiver mode.

The name of the methods and parameters in this implementation are equivalent to the names defined in SMPP specification. So get a copy of SMPP v5.0 Specification for a list of available operations and their parameters.

Creating a SMPP session

var smpp = require('smpp');
var session = smpp.connect({
	url: 'smpp://example.com:2775',
	auto_enquire_link_period: 10000,
	debug: true
}, function() {
	session.bind_transceiver({
		system_id: 'YOUR_SYSTEM_ID',
		password: 'YOUR_PASSWORD'
	}, function(pdu) {
		if (pdu.command_status === 0) {
			// Successfully bound
			session.submit_sm({
				destination_addr: 'DESTINATION NUMBER',
				short_message: 'Hello!'
			}, function(pdu) {
				if (pdu.command_status === 0) {
					// Message successfully sent
					console.log(pdu.message_id);
				}
			});
		}
	});
});

Creating a SMPP server

var smpp = require('smpp');
const utf8 = require('utf8');


var server = smpp.createServer(function(session) {

	console.log("\nIncoming session %s", session);

	session.on('bind_transceiver', function(pdu) {
		// we pause the session to prevent further incoming pdu events,
		// untill we authorize the session with some async operation.
		console.log("\nPDU bind_transceiver %s",pdu);
		session.pause();

		checkAsyncUserPass(pdu.system_id, pdu.password, function(err) {
			var system_id = pdu.system_id;
			console.log("\nAtemting to connect with system_id %s",system_id);

			if (err) {
				session.send(pdu.response({
					command_status: smpp.ESME_RBINDFAIL
				}));
				session.close();
				console.log("\nAuth Failed for id %s password %s \n",pdu.system_id,pdu.password);
				return;
			}
			session.send(pdu.response());
			session.resume();

			session.on('submit_sm', function(pdu) {
				var destination_addr = pdu.destination_addr;
				var short_message = pdu.short_message.message;
				if(pdu.message_payload && pdu.message_payload.length>0) {
					console.log("Payload %s \n",pdu.message_payload);
					if(pdu.message_payload.message) short_message = pdu.message_payload.message;
				}
				if(destination_addr && destination_addr.length==11 && destination_addr.substr(0,2)==="94"){
					if(short_message && short_message.length>0){
						destination_addr = "0" + destination_addr.substr(-9);

						let sms = utf8.decode(short_message);
						let char_count = sms.length;
						let no_of_sms = Math.ceil(char_count / 160);

						var msgid = "1234567890"; // generate a unique number here

						var message_id = msgid.padStart(10, '0');
						session.send(pdu.response({
							message_id: message_id
						}));

						if (pdu.registered_delivery) {
							send_delivery_reports(pdu, session, system_id, message_id);
						}

						console.log("\nSMS Sent, msg id %s",msgid);
						
					}
					else {
						console.log("\nInvalid message content %s \n", short_message);
					}
				}
				else {
					console.log("\nInvalid destination number %s \n", destination_addr);
				}

				console.log("\nPDU bind_transceiver :: submit_sm %s",pdu);
			});

			session.on('unbind', function(pdu) {
				session.send(pdu.response());
				session.close();
				console.log("\nPDU bind_transceiver :: unbind %s",pdu);
			});

			session.on('enquire_link', function(pdu) {
				session.send(pdu.response());
				console.log("\nPDU bind_transceiver :: enquire_link %s",pdu);
			});

			session.on('deliver_sm_resp', function(pdu) {
				console.log("\nPDU bind_transceiver :: deliver_sm_resp for %s \n %s",system_id, pdu);
			});
		});
	});

	session.on('bind_transmitter',function (pdu) {

		session.pause();
		checkAsyncUserPass(pdu.system_id, pdu.password, function(err) {
			var system_id = pdu.system_id;
			console.log("\nAtemting to connect with system_id %s",system_id);

			if (err) {
				session.send(pdu.response({
					command_status: smpp.ESME_RBINDFAIL
				}));
				session.close();
				console.log("\nAuth Failed for id %s password %s \n",pdu.system_id,pdu.password);
				return;
			}
			session.send(pdu.response());
			session.resume();


			session.on('submit_sm', function(pdu) {
				
				var destination_addr = pdu.destination_addr;
				var short_message = pdu.short_message.message;
				if(pdu.message_payload) {
					console.log("Payload %s \n",pdu.message_payload);
					if(pdu.message_payload.message) short_message = pdu.message_payload.message;
				}
				if(destination_addr && destination_addr.length==11 && destination_addr.substr(0,2)==="94"){
					if(short_message && short_message.length>0){
						destination_addr = "0" + destination_addr.substr(-9);

						let sms = utf8.decode(short_message);
						let char_count = sms.length;
						let no_of_sms = Math.ceil(char_count / 160);

						var msgid = "1234567890"; // generate a unique number here
						var message_id = msgid.padStart(10, '0');
						session.send(pdu.response({
							message_id: message_id
						}));

						if (pdu.registered_delivery) {
							send_delivery_reports(pdu, session, system_id, message_id);
						}

						console.log("\nSMS Sent, msg id %s",msgid);
					}
					else {
						console.log("Invalid message content %s \n", short_message);
					}
				}
				else {
					console.log("\nInvalid destination number %s \n", destination_addr);
				}

				console.log("\nPDU bind_transmitter :: submit_sm %s", pdu);
			});

			session.on('unbind', function(pdu) {
				session.send(pdu.response());
				session.close();

				console.log("\nPDU bind_transmitter :: unbind %s", pdu);
			});

			session.on('enquire_link', function(pdu) {
				session.send(pdu.response());
				
				console.log("\nPDU bind_transmitter :: enquire_link %s", pdu);
			});
		});

		console.log("\nPDU bind_transmitter %s",pdu);
	});

	session.on('bind_receiver',function (pdu) {
		
		session.pause();
		checkAsyncUserPass(pdu.system_id, pdu.password, function(err) {
			var system_id = pdu.system_id;
			console.log("\nAtemting to connect with system_id %s",system_id);

			if (err) {
				session.send(pdu.response({
					command_status: smpp.ESME_RBINDFAIL
				}));
				session.close();
				console.log("\nAuth Failed for id %s password %s \n",pdu.system_id,pdu.password);
				return;
			}
			session.send(pdu.response());
			session.resume();

			session.on('deliver_sm_resp', function(pdu) {
				
				console.log("\nPDU bind_receiver :: deliver_sm_resp %s", pdu);
			});

			session.on('unbind', function(pdu) {
				session.send(pdu.response());
				session.close();

				console.log("\nPDU bind_receiver :: unbind %s", pdu);
			});

			session.on('enquire_link', function(pdu) {
				session.send(pdu.response());

				console.log("\nPDU bind_receiver :: enquire_link %s", pdu);
			});
		});

		console.log("\nPDU bind_receiver %s",pdu);
	});
});
server.listen(2775);


function checkAsyncUserPass(system_id, password, callback) {

	// check & validate system_id and password from your authentication mechanism
	var err = false; // set false for testing

	if(err) {
		callback(1); // error
		console.log("\nLogin failed for system_id: %s", system_id);
	}
	else {
		callback(0); // no error
		console.log("\nUser %s successfully logged in.", system_id);
	}
}


function send_delivery_reports(pdu, session, system_id, message_id){
	
	var nsms = "" + 1;
	nsms = nsms.padStart(3, '0');

	var m = new Date();
	var c_year = "" + m.getFullYear();
	var c_month = "" + (m.getMonth()+1);
	var c_date = "" + m.getDate();
	var c_hour = "" + m.getHours();
	var c_minute = "" + m.getMinutes();
	var second = "" + m.getSeconds();

	var current_time = c_year+c_month+c_date+c_hour+c_minute+second;

	session.deliver_sm({
		esm_class: 4,
	    source_addr: pdu.destination_addr,
	    destination_addr: pdu.source_addr,
	    short_message: {
	    	message: 'id:'+message_id+' sub:'+nsms+' dlvrd:'+nsms+' submit date:'+current_time+' done date:'+current_time+' stat:DELIVRD err:0 text:@'
	    },
	    receipted_message_id: message_id,
	    user_message_reference: message_id
	});

	console.log("\nsend_delivery_report DELIVRD :: deliver_sm pdu sent for %s",system_id);
}

 

It’s very important to listen for session errors, not listening for error events will terminate the program.

Project is forked from: https://github.com/farhadi/node-smpp

To see the full source code and implementation guide, visit: https://github.com/lahirutm/node-smpp

CBC Encryption and Decryption | What is CBC?

CBC

Cipher Block Chaining (significantly known as CBC) is a mode of encryption used in block ciphers. It is an advanced form of block cipher encryption and also more secured when compared to ECB. This mode uses a random block of bytes known as Initialization Vector (IV) to ensure randomization of encryption. It is important to use IV only once to ensure security.

Encryption:

CBC is one of the most popularly used mode of encryption in AES. Unlike in ECB mode, in CBC mode, encryption of one block of plaintext is dependent on the previous block. Each block of plaintext is XORed with the previous block of ciphertext, for the first block it is XORed with IV(16 bytes) and then sent through the whitebox. So, change in one single byte changes the entire ciphertext. Hence, eavesdropper can’t attack the data easily.

 C[i] = E(Pt[i]  ^ C[i-1])
 C[1] = E(Pt[i] ^ IV)

Advantages over CBC:

Dependency of one block encryption over the previous one ensures that traces of similar data aren’t left behind, whereas in ECB, change in a byte changes only the corresponding block i.e encrypting 2 blocks that contain same plaintext gives the same ciphertext in ECB. This gives a pattern to the attacker whereas in CBC this doesn’t happen.

Decryption:

Decryption is just the reverse. Each block of ciphertext is sent through the whitebox and then XORed with the previous block of ciphertext to recover the plaintext. So, one need not decrypt the previous block to get the required block of plaintext.

Pt[i] = D(C[i]) ^ C[i-1]
Pt[1] = D(C[1]) ^ IV

Attacks possible:

IV reusing might lead to leakage of information regarding the first block of plaintext. However the rest are unaffected. It is also vulnerable to attacks like bit flipping attack, padding oracle attack etc.

ECB Encryption and Decryption | What is ECB?

ECB

What is ECB?

Electronic Code Book (or ECB) mode of encryption is the simplest of all the modes of encryption. This mode of encryption is the first one which was implemented under AES. But, later we’ll analyze the plus and the minus features of this mode because of which it’s popularity is reduced. Now, we’ll see how is ECB mode of encryption is applied to any of the above described data encryption algorithm.

How is the encryption done?

In this mode of encryption the plain text is divided into different blocks of equal block sizes. Then each block is encrypted in parallel.

As you can see the different blocks of the plain text are encrypted separately using any of the algorithms described above (Like AES, DES) The parallel encryption of each block is the basic essence of the ECB mode.

How is decryption done?

Similar to the encryption the cipher text is divided into different blocks depending upon the block size. Thus, decryption is done on each block, at the same time, using the algorithm with which it was encrypted.

Advantages of ECB

The advantage of ECB mode of encryption over others is that here each block is encrypted separately. Therefore, if any of the block goes corrupted it doesn’t affect the rest of the encryption. Also, because of this feature a multiprocessor can simultaneously encrypt different blocks thus saving time.

Disadvantages of ECB

The main disadvantage of ECB is that the similar part of the plain text are encrypted with the same key to the same cipher text. That is, if in block 1 the plain text character ‘e’ is encrypted as ‘f’ in block 2 also if there’s ‘e’ in the plain text it would be encrypted as ‘f’. Also, the blocks of cipher text can be rearranged which when decrypted would give deranged output which is undesirable.

Vulnerabilities of ECB

The most common attack on ECB is the “ECB byte at a time attack” which exploits the loophole in ECB encryption which would give the same cipher text when applied to the similar characters of plain text.

Basic AES-128-CBC/AES-256-CBC Encryption/Decryption with PHP

For AES-128-CBC Encryption/Decryption | PHP

To use CBC encryption, you also need to sort the $iv offset, as follows: AES-128-CBC encryption and decryption.

Here i used simple keys for the example, But Keys need to be generated using a cryptographically secure random number generator if you are using this in a production.

$iv = '1234567890123456'; // 16 byte
$key = '1234567890123456'; // 16 byte
function decode($str,$key,$iv)
{
    return openssl_decrypt(base64_decode($str),"AES-128-CBC",$key,OPENSSL_RAW_DATA, $iv);
}

function encode($str,$key,$iv)
{
     return base64_encode(openssl_encrypt($str,"AES-128-CBC",$key,OPENSSL_RAW_DATA, $iv));
}

echo "String: Hellow World !";
echo "<br/>Encrypted String: ";
echo encode("Hellow World !",$key,$iv);
echo "<br/>Decryped String: ";
echo decode("l3mMP/irpStRPTIfYsdZmg==",$key,$iv); 

OUT PUT

String: Hellow World !
Encrypted String: l3mMP/irpStRPTIfYsdZmg==
Decryped String: Hellow World !

 

 

For AES-256-CBC Encryption/Decryption | PHP

function encrypt_decrypt($action, $string) 
    {
        $output = false;
        $encrypt_method = "AES-256-CBC";
        $secret_key = '12345678901234561234567890123456'; // 32 byte
        $secret_iv = '1234567890123456'; // 16 byte $key = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv), 0, 16); if ( $action == 'encrypt' ) { $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv); $output = base64_encode($output); } else if( $action == 'decrypt' ) { $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv); } return $output; } echo "String: Hellow World !"; echo "<br/>Encrypted String: "; echo encrypt_decrypt('encrypt', "Hellow World !"); echo "<br/>Decryped String: "; echo encrypt_decrypt('decrypt', "QkNZWjlab2pSTUtqVnMyMHlYeTV4dz09");

 

OUT PUT

String: Hellow World !
Encrypted String: QkNZWjlab2pSTUtqVnMyMHlYeTV4dz09
Decryped String: Hellow World !

Basic AES-128-ECB Encryption/Decryption with PHP

Note: ECB is useful for random data, but structured data should use a stronger mode like MCRYPT_MODE_CBC, Because ECB is an insecure method for sensitive data.

Lets see an example of its usage in PHP.

The Key must be 16bit long.

    $key = '1234567890123456';
   
    function encrypt($data,$key) {
        return base64_encode(openssl_encrypt($data, "aes-128-ecb", $key, OPENSSL_RAW_DATA));
    }

    function decrypt($data,$key) {
        return openssl_decrypt(base64_decode($data), "aes-128-ecb", $key, OPENSSL_RAW_DATA);
    }

 

To Encrypt, Simply call

$data = "This is to be encrypted";
echo $encrypted_text = encrypt($data,$key);

 

To Decrypt the above encrypted text

$data = "This is to be encrypted";
$encrypted_text = encrypt($data,$key);

$data = $encrypted_text;
echo $decrypted_text = decrypt($data,$key);

But there are other problems in this code which make it insecure, in particular the use of ECB (which is not an encryption mode, only a building block on top of which encryption modes can be defined).

Why ECB is insecure ?

You have a cipher, that with a key will encrypt 16 bytes of data. And you have some data, that is more than 16 bytes. Its a problem. ECB is the wrong solution to that problem: you just encrypt each 16-bytes block separately.

Why is it wrong? Because this way blocks that were equal before encryption will remain equal also after! And this will lead to all kinds of unwanted consequences.

 

Zip directory using Node.JS

Archiving a file/folder using nodejs is very simple. I have used nodejs archiver library to zip directory and fs library to get the file information in this post.

const fs = require('fs');
var archiver = require('archiver');

function zip_directory(filename, copyFrom, copyToPath){
    var copyTo = copyToPath + filename;

    if(!fs.existsSync(copyToPath)){
        fs.mkdirSync(copyToPath);
    }

    var output = fs.createWriteStream(copyTo);
    var archive = archiver('zip', {
      zlib: { level: 9 } // Sets the compression level.
    });

    output.on('close', function () {
        console.log(`\n<< ARCHIVE LOG: TOTAL BYTES ${archive.pointer()} >>`);
        console.log(`\n<< ARCHIVE LOG: archiver has been finalized and the output file descriptor has closed. >>`);
    });

    output.on('end', function() {
	  console.log(`\n<< ARCHIVE LOG: END : Data has been drained. >>`);
	});

    archive.on('warning', function(err) {
	  if (err.code === 'ENOENT') {
	    console.log(`\n<< ARCHIVE LOG: ENOENT ${err} >>`);
	  } else {
	    console.log(`\n<< ARCHIVE LOG: WARNING ${err} >>`);
	  }
	});

    archive.on('error', function(err){
        console.log(`\n<< ARCHIVE LOG: Error : ${err} >>`);
    });

    try {
        archive.pipe(output);
        archive.directory(copyFrom, { name: filename });
        archive.finalize();
    } catch (err) {
        console.log(`\n<< ARCHIVE LOG: Error : ${err} >>`);
    }
}


zip_directory('myzipfile.zip', '/home/', '/opt/zipfiles/');

 

Note: Should install required packages as well as declare require things in code.

Tagged : / /

NodeJs Http Server (Web Server)

Making a simple HTTP server in Node.js is very simple. Node.js provides extremely easy-to-use HTTP APIs, a simple web server.

Let’s take a look at a very simple example:

const http = require('http');

const http_port = 8000;

// Instantiate the HTTP server.
const httpServer = http.createServer((req, res) => {
    if (req.method == 'POST') {
        var jsonString = '';

        req.on('data', function (data) {
    	    try {
                console.log(data.toString());
    	    }
    	    catch(err) {
    		console.log(err);
    	    }
        });

      	req.on('error', (err) => {
            // This prints the error message and stack trace to `stderr`.
            console.log("POST ERROR \n" + err.stack);
      	});

        req.on('end', function () {
             console.log('POST Request ended here.');
        });
    }
    
    res.writeHead(200);
    res.end();
    console.log('Response ended here. \n ');
});


httpServer.listen(http_port, () => {
  console.log("Web server is listening on port %s \n", http_port);
});

Save this in a file called server.js – run node server.js, and your program  will hang there… it’s waiting for connections to respond to, so you’ll have to give it one if you want to see it do anything. If everything has been set up correctly, you should see your server’s request & response log.

Let’s take a more in-depth look at what the above code is doing. Function in http.createServer takes a request object and a response object as parameters.

The request object contains things such as the requested URL, Request Method and data.

The response object is how we send the headers and contents of the response back to the user making the request. Here we return a 200 response code (signaling a successful response) with empty body. Other headers, such as Content-type, would also be set here.

The listen method, which causes the server to wait for incoming requests on the specified port – 8000, in this case.

There you have it – your most basic Node.js HTTP server.

Manage IoT Sensor Network with ISTSOS (Open Source)

IstSOS is Free and Open Source Sensor Observation Service Data Management System (OGC SOS server implementation) written in Python. istSOS allows for managing and dispatch observations from monitoring sensors according to the Sensor Observation Service standard. It provides a Graphical user Interface that allows for easing the daily operations and a RESTFull Web api for automatizing administration procedures. istSOS is released under the GPL License, and runs on all major platforms (Windows, Linux, Mac OS X), even though it has been used in production in linux environment only.

Main Features

  • Offer your data according to the Sensor Observation Service standard from Open Geospatial Consortium.
  • Administer your sensors and your data with a comfortable interface.
  • Use a complete API for accessing functionalities to makes it easy for new clients to use istSOS application.
  • Get notified trough mail, twitter or other social when your sensor data met specific conditions.

How to Install (On Ubuntu 18.04)

Installation on Linux with the Debian package

The easiest way to install istSOS on a Debian distribution is to use the istSOS deb packages.

Download the debian file from the repository

Please go to https://sourceforge.net/projects/istsos/files to get the latest release.

Install the debian file

Open a terminal and move to the folder containing the downloaded deb file.

sudo dpkg -i python3-istsos_2.4.0-RC4.deb;sudo apt-get -f -y install

This command will install all the required dependencies, with the exception of PostgreSQL and PostGIS. In fact it could reside on other servers.

If everything has gone well, you should see the administration page at this address: http://localhost/istsos/admin/

NodeMCU ESP8266 Over The Air (OTA) Programming In Arduino IDE

A fantastic feature of any WiFi-enabled microcontroller like ESP8266 NodeMCU is the ability to update its firmware wirelessly. This is known as Over-The-Air (OTA) programming.

The OTA programming allows updating/uploading a new program to ESP8266 using Wi-Fi instead of requiring the user to connect the ESP8266 to a computer via USB to perform the update.

OTA functionality is extremely useful in case of no physical access to the ESP module. It helps reduce the amount of time spent for updating each ESP module at the time of maintenance.

One important feature of OTA is that one central location can send an update to multiple ESPs sharing same network.

The only disadvantage is that you have to add an extra code for OTA with every sketch you upload, so that you’re able to use OTA in the next update.

The factory image in ESP8266 doesn’t have an OTA Upgrade capability. So, you need to load the OTA firmware on the ESP8266 through serial interface first.

It’s a mandatory step to initially update the firmware, so that you’re able to do the next updates/uploads over-the-air.

The ESP8266 add-on for the Arduino IDE comes with a OTA library & BasicOTA example. You can access it through File > Examples > ArduinoOTA > BasicOTA.

The following code should load. But, before you head for uploading the sketch, you need to make some changes to make it work for you. You need to modify the following two variables with your network credentials, so that ESP8266 can establish a connection with existing network.

const char* ssid = "..........";
const char* password = "..........";

Once you are done, go ahead and upload the sketch.

 

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "..........";
const char* password = "..........";

void setup() {
  Serial.begin(115200);
  Serial.println("Booting NodeMCU");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Sorry, Connection Failed! Rebooting NodeMCU...");
    delay(5000);
    ESP.restart();
  }
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else 
      type = "filesystem";

    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connecting Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receiving Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();
}

Now, open the Serial Monitor at a baud rate of 115200. And press the RST button on ESP8266. If everything is OK, it will output the dynamic IP address obtained from your router. Note it down.

Upload New Sketch Over-The-Air

Now, let’s upload a new sketch over-the-air.

Remember! you need to add the code for OTA in every sketch you upload. Otherwise, you’ll loose OTA capability and will not be able to do next uploads over-the-air. So, it’s recommended to modify the above code to include your new code.

As an example we will include a simple Blink sketch in the Basic OTA code. Remember to modify the SSID and password variables with your network credentials.

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "..........";
const char* password = "..........";

//variabls for blinking an LED with Millis
const int led = D0; // ESP8266 Pin to which onboard LED is connected
unsigned long previousMillis = 0;  // will store last time LED was updated
const long interval = 1000;  // interval at which to blink (milliseconds)
int ledState = LOW;  // ledState used to set the LED
void setup() {
pinMode(led, OUTPUT);
    
  Serial.begin(115200);
  Serial.println("Booting NodeMCU");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting NodeMCU...");
    delay(5000);
    ESP.restart();
  }
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else
      type = "filesystem";

    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connecting Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receiving Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {

  previousMillis = currentMillis;
  ledState = not(ledState);
  digitalWrite(led,  ledState);
  }
}

Once you copy above sketch to your Arduino IDE, go to Tools > Port option and you should see something like this: esp8266-xxxxxx at your_esp_ip_address If you can’t find it, you may need to restart your IDE.

Select the port and click Upload button. Within a few seconds, the new sketch will be uploaded. And you should see the on-board LED blinking.