Home » Blog » Projects » Electronics Projects » NodeMCU Serial Passthrough for Remote Control

NodeMCU Serial Passthrough for Remote Control

NodeMCU Serial Passthrough

Using NodeMCU serial passthrough with a partner Arduino is a fast, simple and cheap way to overcome ESP-12E limitations.

Recently, I have been exploring solutions to overcome NodeMCU GPIO pin limitations. This article describes using NodeMCU serial passthrough with an Arduino Pro Mini to remote control a signal generator. Because the Pro Mini has a 3.3V version is compatible with the ESP-12E. Most of all, it has enough pins for connecting the direct digital synthesizer, optical encoder and TFT display. These connections require 12 IO pins, which is more than the NodeMCU has available.

The NodeMCU handles all the wireless requirements. It passes commands and data back and forth to the Arduino. This is achieved using Software Serial at a common baud rate between the two devices. The control applications can be anything from a simple TCP Terminal to a custom written application. The optical encoder provides the option for local changes, which are also communicated back to the remote control app.

NodeMCU Serial Passthrough Program Code

The NodeMCU serial passthrough does not care exactly what you are controlling or what are the commands and data.

  • First of all, a common baud rate with the Arduino. I am using 57600 baud. If you get the occasional serial error, try slowing this down to 38400 or 19200. If your command and data packets are short, any of these rates should be fast enough to prevent blocking on the ESP-12E system-on-chip controlling the wireless.
  • Second, a simple data format. In my case, I use plain text terminated by a carriage return and line feed. These terminators are generated automatically when you send data using the println() function.
  • Finally, set up the WiFi with a Static IP address. This way, you always know where the remote device shows up on the network.

This is a great way to expand the capabilities of NodeMCU. Sharing the work with an Arduino Pro Mini 3.3V is a $2 solution. What’s more, since you are using plain serial communication, the Arduino does not care whether its connected by wireless or to your computer. It’s all serial data.

/*
   NodeMCU 12-E Passthrough to Serial
   November 10, 2016
   Connect to ESP-12E IP on port 4000
   Commands (ASCII terminated by rn)
*/
#include <SoftwareSerial.h>
#include <WiFiServer.h>
#include <WiFiClient.h>
#include <ESP8266WiFi.h>


#define PIN_SS_RX D3
#define PIN_SS_TX D4
#define SERIAL_BAUDRATE 57600
/* Note: serial data may have errors at higher 
   baud rates with Software Serial. If this happens, 
   reduce baud rate slightly */

const char*     ssid = "xxxx";
const char*     password = "xxxx";
const IPAddress SERVERIP(192, 168, 0, 201);
const IPAddress GATEWAY(192, 168, 0, 1);
const IPAddress SUBNET(255, 255, 255, 0);
const IPAddress DNS1(192, 168, 0, 1);
const IPAddress DNS2(192, 168, 0, 1);
boolean ClientConnected = false;
boolean LastClientConnected = false;

/* Objects */
WiFiServer server(4000);
WiFiClient client;
SoftwareSerial serialPass(PIN_SS_RX, PIN_SS_TX);

void setup()
{
	delay(100);

	wifiConnect(ssid, password);
	server.begin();
	serialPass.begin(SERIAL_BAUDRATE);
	serialPass.flush();
	delay(500);
}

void loop()
{
	String c = "";
	
	/* Connect or Disconnect Remote Client if Needed */
	ChangeClientConnected(CheckClientConnection());
	
	if (ClientConnected)
	{
		/* Pass thru data from client to remote device */
		if (client.available())
		{
			c = client.readStringUntil('n');
			client.flush();
			c.toUpperCase();
			serialPass.println(c);
			yield();
		}
		
		/* Pass through data from remote device to client */
		if (serialPass.available())
		{
			c = serialPass.readStringUntil('n');
			serialPass.flush();
			client.println(c);
			yield();
		}
	}
/* Note: These routines block. If data is exchanged as complete, 
short terminated text, ESP SoC should not be effected. If you
get watchdog timer resets, modify to pass one byte at a time
which is non-blocking */
}

void ChangeClientConnected(boolean flag)
{
	/* Only process this routine when the ClientConnected state
	has actually changed. 	Otherwise, return immediately. */
	if (flag != LastClientConnected)
	{
		ClientConnected = flag;
		LastClientConnected = ClientConnected;
	}
}

boolean CheckClientConnection()
{
	/* If we have a running WiFiClient and there is a remote connection,
	just confirm the connection */
	if (client &amp;&amp; client.connected())
	{
		return true;
	}

	/* If we have a running WiFiClient but the remote has disconnected,
	disable WiFiClient and report no connection */
	if (client &amp;&amp; !client.connected())
	{
		client.stop();
		/* Optional Auto Stop Remote */
		serialPass.println("X");
		return false;
	}

	/* At this point we are ready for a new remote connection.
	Create the WiFiClient and confirn the connection */
	if (server.hasClient())
	{
		if ((!client) || (!client.connected()))
		{
			if (client) client.stop();
			client = server.available();
			/* Optional Auto Start Remote */
			serialPass.println("+");
			return true;
		}
	}
}

void wifiConnect(const char* id, const char* pass)
{
	/* Set up on LAN with a Static Server IP Address */
	WiFi.config(SERVERIP, GATEWAY, SUBNET, DNS1, DNS2);
	WiFi.begin(id, pass);
	while (WiFi.status() != WL_CONNECTED)
	{
		delay(250);
	}
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.