Overview
The TCP Command Handler is an Arduino library to decode and dispatch commands sent over a wireless TCP connection. It supports Espressif micros based on the ESP32 and ESP8266 chipsets such as SparkFun’s ESP32 Thing and ESP8266 Thing, the Wemos D1 mini and D32 Pro.
When the library receives a command it can either:
- Call a function in your Arduino sketch
- Update a variable
Connect to an ESP8266 or ESP32 running the TCP Command Handler by creating a TCP/IP connection to your device with the Connection Manager. Our article on talking to an ESP32 over WiFi has a detailed walk-through.
Send commands to your wireless device using the Raw Serial Visualizer, Message Monitor Visualizer or build custom Interface Panel that sends commands when you click a button.
Serial Command Format
Commands start with a ‘!’ character and end with a carriage return (‘\r’) though both can be customized. Parameters can be included in the serial message and are separated using a space. Example commands:
!SetPoint 4\r
!StartMotor\r
!TurnLEDsOn 1 2 3\r
Using the TCP Command Handler
To use the serial command handler in your program you need to:
#include "TCPCommandHandler.h"
to make the library available to your program- Include the Arduino WiFi library:
#include "WiFi.h"
on the ESP32#include "EspWiFi.h"
on the ESP8266
- Create a
WiFiServer Server(23/*server port*/);
variable - Create a
TCPCommandHandler<> CmdHandler(Server);
variable - In your setup function, add the commands you want to handle:
- To call a function when a command is received use
CmdHandler.AddCommand(F("CommandName"), Cmd_FunctionToCall)
- To update a variable when a command is received use
CmdHandler.AddVariable(F("CommandName"), VariableName)
- To call a function when a command is received use
- Implement the command functions. Each must take a single
CommandParameter
reference, even if it isn’t used. - Call
CmdHandler.Process()
in your main loop function to receive and decode commands
The MegunoLink Interface Panel and Message Monitor visualizers can both be used to send commands.
You’ll also need to connect to your WiFi network and initialize the Server
in your setup function. Check out the example below for more detail, or our article how do I connect to a wireless network with the ESP32? for a easier way to manage your network credentials.
TCP Command Handler Reference
The TCPCommandHandler
is implemented as a C++ template:
template <int MAX_CONNECTIONS = 1,
int MAX_COMMANDS = 10, int CP_SERIAL_BUFFER_SIZE = 30,
int MAX_VARIABLES=10>
class TCPCommandHandler(…)
It uses a fixed size arrays to store:
- the state of each active connection (1, by default)
- command names and functions to call (up to 10, by default)
- variable names and the variables they map to (up to 10, by default)
The default values can be changed using the template arguments.
Although the TCP Command Handler can support multiple connections, only one active connection is supported by default. Once the number of available slots is exhausted further connections will be rejected until once of the active connections is released. Each connection reserves its own serial buffer, along with additional state to manage the TCP connection. So reserving space for too many active connections can consume a lot of memory. Typically 1 or 2 slots is sufficient.
TCP Command Handler construction
The TCPCommandHandler
constructor takes three arguments. The first is required; the remaining two are optional:
TCPCommandHandler(WiFiServer &Server, char StartOfMessage = '!', char EndOfMessage = '\r')
Server
is the TCP server that the command handler should listen to. It can be on any port but should be initialized (Server.begin()
) before the command handler’sProcess()
function is called,StartOfMessage
is the character that the command handler looks for to mark the start of a new message. By default it is a ‘!’. Whenever the command handler encounters the start character it will discard whatever it has so far and begin building a new message. So you need to make sure the start character will never be found in the message itself.EndOfMesasge
is the character that marks the end of a message. By default it is a carriage return (‘\r’). When the end character is found, the command handler tries to match the command it has received and call the matching function. You need to make sure the end character is never found as part of the message itself.
Example TCP Command Handler declarations
Declaration | Maximum number of Connections | Maximum number of Commands | Maximum command length | Maximum number of Variables | Start of Message | End of Message |
TCPCommandHandler<> CmdHandler(Server); |
1 (default) | 10 (default) | 30 characters (default) | 10 (default) | ! |
\r |
TCPCommandHandler<2, 15> CmdHandler(Server); |
2 | 15 | 30 characters (default) | 10 (default) | ! |
\r |
TCPCommandHandler<1,12,20> CmdHandler(Server); |
1 | 12 | 20 characters | 10 (default) | ! |
\r |
TCPCommandHandler<1,10, 30, 15> CmdHandler(Server); |
1 | 10 (default) | 30 characters (default) | 15 | ! |
\r |
TCPCommandHandler<> CmdHandler(Server, '#', '^'); |
1 (default) | 10 (default) | 30 characters (default) | 10 (default) | # |
^ |
Commands, Variables and the Default Handler
Commands, variables and the default handler are configured for the TCP Command Handler in the same way as the Serial Command Handler.
Briefly, in void setup()
, call:
CmdHandler.AddCommand(F("CommandName"), Cmd_HandlerFunction);
and the TCP Command Handler will callCmd_HandlerFunction
whenever it receivesCommandName
.CmdHandler.AddVariable(F("VariableName"), Variable);
and the command!VariableName Value\r
will updateVariable
toValue
.CmdHandler.SetDefaultHandler(UnknownMessageHandler);
and the functionvoid UnknownMessageHandler()
will be called whenever a command is received that doesn’t match any of the registered commands.
Please refer to the Serial Command Handler documentation for more details including function signatures for Cmd_HandlerFunction
and working with parameters.
Example
This example accepts commands to adjust the blink rate of an LED. On the ESP8266 it uses the built-in LED; on the ESP32 you have to connect an LED between Pin 23 and ground via a 330 ohm resistor.
The program connects to a WiFi access point (ConnectToWiFi()
) and sets up a TCP server on port 23. It advertises itself using mDNS so that MegunoLink can find the device’s IP address (AdvertiseServices()
). The MakeMine
function appends a semi-unique number to the device name “MyDevice” so you can distinguish between several blinking devices on the same WiFi network.
Use MegunoLink’s TCP Client connection to connect to the TCP Server on the ESP Device. This example advertises its IP address using mDNS, so to open a connection:
- Add a TCP Client connection in the Connection Manager
- Select the mDNS option
- Choose
_n8i-mlp._tcp
as the service - Choose the device from the host list
- Click Connect
The sketch responds to the commands:
!OnTime nnn\r\n
: changes the time the LED spends on to nnn ms.!OffTime nnn\r\n
: changes the time the LED spends off to nnn ms.!ListAll\r\n
: reports the current LED on and off time.
Once you’ve opened a connection, use the Set buttons to change the blink rate and List Config to display the current configuration in the serial monitor.
MegunoLink WiFi Blink Project
This MegunoLink project uses an Interface Panel visualizer to send commands to the WiFiBlink sketch.
Arduino Sketch for ESP32/ESP8266
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
/* ************************************************************************ BlinkWiFi This program is a demonstration of the MegunoLink library for sending commands to the Arduino over a wireless connection to the ESP32 or ESP8266. All commands start with a ! character and end with \r (carrige return). It responds to the following serial commands: !OnTime n\r\n Sets the amount of time the LED remains on to n [milliseconds] !OnTime ?\r\n Returns the amount of time the LED remains on [milliseconds] !OffTime n\r\n Sets the amount of time the LED remains off to n [milliseconds] !OffTime ?\r\n Returns the amount of time the LED remains off [milliseconds] The program folder also contains a MegunoLink project, with an Interface Panel to control the parameters. Visit: * http://www.MegunoLink.com to download MegunoLink. * https://www.megunolink.com/documentation/build-arduino-interface/ for more information on this example. ************************************************************************ */ #if defined(ARDUINO_ARCH_ESP32) #include "WiFi.h" #include <ESPmDNS.h> #elif defined(ARDUINO_ARCH_ESP8266) #include <ESP8266WiFi.h> #include <ESP8266mDNS.h> #else #error Not compatible #endif // MegunoLink command handler library. If this include fails, you may need to // install the Arduino library: https://www.megunolink.com/documentation/arduino-integration/ #include "TCPCommandHandler.h" #include "CommandProcessor.h" // Load WiFi configuration. Define the configuration for your own network // in a separate file. It should contain: /* const char *SSID = "Your SSID"; const char *WiFiPassword = "Your Password"; */ #include "WiFiConfig.h" const uint8_t ServerPort = 23; WiFiServer Server(ServerPort); TCPCommandHandler<2> Cmds(Server); CommandProcessor<> SerialCmds(Cmds); long LastBlink = 0; // Time we last blinked the LED int OnTime = 10; // Amount of time the LED remains on [milliseconds] int OffTime = 100; // Amount of time the LED remains off [milliseconds] #if defined(ARDUINO_ARCH_ESP32) const int LEDPin = 23; // Pin the LED is attached to #elif defined(ARDUINO_ARCH_ESP8266) const int LEDPin = BUILTIN_LED; // Pin the LED is attached to #endif void ConnectToWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(SSID, WiFiPassword); Serial.print("Connecting to "); Serial.println(SSID); uint8_t i = 0; while (WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(500); if ((++i % 16) == 0) { Serial.println(F(" still trying to connect")); } } Serial.print(F("Connected. My IP address is: ")); Serial.println(WiFi.localIP()); } void AdvertiseServices() { String MyName = MakeMine("MyDevice"); if (MDNS.begin(MyName.c_str())) { Serial.println(F("mDNS responder started")); Serial.print(F("My name is: ")); Serial.println(MyName.c_str()); // Add service to MDNS-SD MDNS.addService("n8i-mlp", "tcp", ServerPort); } else { Serial.println(F("Error setting up MDNS responder")); } } String MakeMine(const char *Template) { #if defined(ARDUINO_ARCH_ESP32) uint16_t uChipId = ESP.getEfuseMac(); #else uint16_t uChipId = ESP.getChipId(); #endif String Result = String(Template) + String(uChipId, HEX); return Result; } void Cmd_ListAll(CommandParameter &Parameters) { Parameters.GetSource().print(F("OnTime [ms]=")); Parameters.GetSource().println(OnTime); Parameters.GetSource().print(F("OffTime [ms]=")); Parameters.GetSource().println(OffTime); } void Cmd_SendToAll(CommandParameter &Parameters) { Cmds.print(F("OnTime [ms]=")); Cmds.println(OnTime); Cmds.print(F("OffTime [ms]=")); Cmds.println(OffTime); } void Cmd_SetOnTime(CommandParameter &Parameters) { OnTime = Parameters.NextParameterAsInteger(OnTime); } void Cmd_SetOffTime(CommandParameter &Parameters) { OffTime = Parameters.NextParameterAsInteger(OffTime); } void Cmd_Unknown() { Serial.println(F("I don't understand")); } // the setup function runs once when you press reset or power the board void setup() { Serial.begin(9600); Serial.println("TCP Command Example"); ConnectToWiFi(); AdvertiseServices(); // Start the TCP server Server.begin(); Server.setNoDelay(true); // Setup the serial commands we can repond to Cmds.AddCommand(F("OnTime"), Cmd_SetOnTime); Cmds.AddCommand(F("OffTime"), Cmd_SetOffTime); Cmds.AddCommand(F("ListAll"), Cmd_ListAll); Cmds.AddCommand(F("SendToAll"), Cmd_SendToAll); Cmds.SetDefaultHandler(Cmd_Unknown); pinMode(LEDPin, OUTPUT); Serial.println("Ready."); } // the loop function runs over and over again until power down or reset void loop() { // Check for serial commands and dispatch them. Cmds.Process(); SerialCmds.Process(); // Update the LED uint32_t uNow = millis(); if (uNow - LastBlink < OnTime) { digitalWrite(LEDPin, HIGH); } else { digitalWrite(LEDPin, LOW); } if (uNow - LastBlink > OnTime + OffTime) { LastBlink = uNow; } } |