Wireless Joystick Module Experiment With ESP8266 on ESP-Now

by Trevor Lee in Circuits > Arduino

2884 Views, 7 Favorites, 0 Comments

Wireless Joystick Module Experiment With ESP8266 on ESP-Now

joystick_now.jpg

Extending from the idea of my previous post Joystick Module Calibration and Press Detection, this post is an attempt to futher ehnance the code to implement wireless Joystick support over ESP-Now communication protocol, which is built-in to ESP microcontroller boards.

Two ESP microcontroller boards are need.

  • One as server, which will have physical Joystick module attached.
  • One as client, which will be receiving Joystick signals (ESP-Now packets) from the server, to realize wireless "virtual" Joystick.

Here, I will only use ESP8266 microcontroller boards -- ESP01 board and ESP8266 board (NodeMCU). Of cause, the ESP-Now communication protocol doesn't require that the boards be the same type, and therefore can be ESP32. However, since the ESP-Now coding of ESP32 is a bit different from that of ESP8266, here, I will only code for ESP8266.

ESP8266 With 5D Joystick Module

joystick_now_connect.png

Instead of using Joystick module and buttons. This time, a 5D Joystick module is used. Basically, it is just a bunch of buttons, arranged in such a way that it facilities directional key presses, behaving like a Joystick. And it has 2 addition buttons, which can be treated as buttons on a Joystick module.

There are totally 7 buttons. Using the 5D Joystick module is easier, since all the 7 buttons on the module have a common ground.

  • Connect a GND pin of ESP8266 (NodeMCU) to COM, the common GND, of 5D module.
  • Connect D7 of ESP8266 to UP of 5D module.
  • Connect D6 of ESP8266 to DWN of 5D module.
  • Connect D5 of ESP8266 to LFT of 5D module.
  • Connect D4 of ESP8266 to RHT of 5D module.
  • Connect D3 of ESP8266 to MID of 5D module.
  • Connect D2 of ESP8266 to SET of 5D module. (Note that the SET button is treated as A button.)
  • Connect D1 of ESP8266 to RST of 5D module. (Note that the RST button is treated as B button.)

The Sketch

The sketch joystick_now.ino (download here) is fairly lenthy. Moreover, the joystick press detection code is actullay in the include file ddjoystick.h (download here). Please note that ddjoystick.h is included with DumbDisplay Arduino Library as well, and hence if you have DumbDisplay Arduino Library installed, you don't need to separately download ddjoystick.h.

In fact, the sketch works for all the joystick attachments mentioned in my previous post Joystick Module Calibration and Press Detection.

Of cause, it additionally

  • works for ESP8266 board, with 5D Joystick module attached (as mention above)
  • works as wireless Joystick server (with the same 5D Joystick module attached)
  • works as wireless Joystick client; here I will use a ESP01 board for the purpose

ESP8266 With 5D Joystick Module

Out of the box, the sketch will not enable ESP-Now code. Hence, if the sketch is uploaded to ESP8266 board, 5D Joystick module is assumed.

Here is the code to connect the buttons with the button press detection class, etc

ButtonPressTracker *upTracker = SetupNewButtonPressTracker(D7);
ButtonPressTracker *downTracker = SetupNewButtonPressTracker(D6);
ButtonPressTracker *leftTracker = SetupNewButtonPressTracker(D5);
ButtonPressTracker *rightTracker = SetupNewButtonPressTracker(D4);
ButtonPressTracker *midTracker = SetupNewButtonPressTracker(D3);
ButtonPressTracker *setTracker = SetupNewButtonPressTracker(D2);
ButtonPressTracker *rstTracker = SetupNewButtonPressTracker(D1);
ButtonJoystick *joystick = new ButtonJoystick(upTracker, rightTracker, downTracker, leftTracker, midTracker);
ButtonsOnly *buttons = new ButtonsOnly(setTracker, rstTracker, NULL, NULL);
JoystickInterface *Joystick1 = joystick;
JoystickInterface *Joystick2 = buttons;
  • A button press detection object ButtonPressTracker is assoiated with each button.
  • With the help of the class ButtonJoystick, joystick press detected can be realized. It is used for the first joystick object -- Joystick1.
  • ButtonsOnly is just a form of the class ButtonJoystick. And it is used as the second joystick object -- Joystick2.
  • Two joystick objects are used to detect Joystick and ABCD button presses independently.

Note that setting the button pin mode is the responsibility of SetupNewButtonPressTracker, which is as simple as

ButtonPressTracker *SetupNewButtonPressTracker(uint8_t pin)
{
  pinMode(pin, INPUT_PULLUP);
  return new ButtonPressTracker(pin);
}

The joystick press action will be output to Serial, and hence you will need Serial monitor set to baud 115200 to see the output.

[↑][.]
[→][.]
[↓][.]
[←][.]
[#][.]
[.][A]
[.][B]

Hopefully, it is obvious what press will produce what output :-)

ESP01 Wireless Joystick Client

Before uploading the sketch to ESP01 to make it the wireless Joystick client, you will need to modify the sketch a tiny bit

  • uncomment the line #define ESP_NOW_CLIENT
  • keep ESP_NOW_SERVER_FOR_MAC commented out
//#define ESP_NOW_SERVER_FOR_MAC 0x48, 0x3F, 0xDA, 0x51, 0x22, 0x15
#define ESP_NOW_CLIENT

Instead of the joystick objects created in previous scenario, when ESP_NOW_CLIENT is defined, two "virtual" joystick objects are used as the two joystick objects -- Joystick1 and Joystick2:

DecodedJoystick *Joystick1 = new DecodedJoystick(false);
DecodedJoystick *Joystick2 = new DecodedJoystick(true);

There are several steps to initialize ESP-Now for receiving ESP-Now packet (in the setup block):

WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnReceivedData);
  • set the board's WIFI to "station" mode
  • call the ESP-Now subroutine esp_now_init
  • call esp_now_set_self_role to set that the board will be receiving ESP-Now packet
  • call esp_now_register_recv_cb to set the call-back handler to handle they received ESP-Now packets

The receiving of ESP-Now packets is actually the meat.

First, declare the packet structure (joystick signals encoding) ESPNowJoystickData

struct ESPNowJoystickData
{
  JoystickPressCode joystickPressCode1;
  JoystickPressCode joystickPressCode2;
};

JoystickPressCode is itself data structure -- for encoding Joystick signal -- and is used by joystick press detection class. Here, a pair of such signals will be encoded and sent for each joystick action, for the two joystick objects.

The code of the "receive ESP-Now packet" callback is as shown:

void OnReceivedData(uint8_t *mac, uint8_t *incomingData, uint8_t len)
{
  ESPNowJoystickData joystickData;
  memcpy(&joystickData, incomingData, len);
  Joystick1->decode(joystickData.joystickPressCode1);
  Joystick2->decode(joystickData.joystickPressCode2);
}

When ESP-Now packet / joystick signals received, the encoded joystick signals are decoded by the two "virtual" joystick objects, so that the joystick objects can behave as if they induced the joystick actions.

Since no server providing joystick signals, initially, ESP01 (the client) will simply print it's MAC address to Serial. It turns out that this MAC address is very important for the server to target it to send ESP-Now packets to; so, take note of it.

IDLE: my MAC is 48:3F:DA:51:22:15
IDLE: my MAC is 48:3F:DA:51:22:15
IDLE: my MAC is 48:3F:DA:51:22:15
IDLE: my MAC is 48:3F:DA:51:22:15
IDLE: my MAC is 48:3F:DA:51:22:15
IDLE: my MAC is 48:3F:DA:51:22:15

Of cause, your ESP01 board will have different MAC address than shown above.

Now, the ESP01 board is all ready.

ESP8266 Wireless Joystick Server

Before uploading the sketch to ESP8266 (with 5D Joystick module) to make it the wireless Joystick server, you will need to modify the sketch a tiny bit as well

  • make sure the line #define ESP_NOW_CLIENT is commented out
  • uncomment the like #define ESP_NOW_SERVER_FOR_MAC
  • it is important that you put the correct MAC address of the client with ESP_NOW_SERVER_FOR_MAC, in the format as show below (for MAC 48:3F:DA:51:22:15 as shown previously)
#define ESP_NOW_SERVER_FOR_MAC 0x48, 0x3F, 0xDA, 0x51, 0x22, 0x15
//#define ESP_NOW_CLIENT

Please be reminded that the ESP8266 board has 5D module attached, and hence the joystick objects are [as shown in previous scenario]

ButtonPressTracker *upTracker = SetupNewButtonPressTracker(D7);
ButtonPressTracker *downTracker = SetupNewButtonPressTracker(D6);
ButtonPressTracker *leftTracker = SetupNewButtonPressTracker(D5);
ButtonPressTracker *rightTracker = SetupNewButtonPressTracker(D4);
ButtonPressTracker *midTracker = SetupNewButtonPressTracker(D3);
ButtonPressTracker *setTracker = SetupNewButtonPressTracker(D2);
ButtonPressTracker *rstTracker = SetupNewButtonPressTracker(D1);
ButtonJoystick *joystick = new ButtonJoystick(upTracker, rightTracker, downTracker, leftTracker, midTracker);
ButtonsOnly *buttons = new ButtonsOnly(setTracker, rstTracker, NULL, NULL);
JoystickInterface *Joystick1 = joystick;
JoystickInterface *Joystick2 = buttons;

Setting up the ESP8266 board to send ESP-Now packet is similary to client case, but with send / receive role reversed

WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_add_peer(ClientMACAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0); // 1 is the channel
  • set the board's WIFI to "station" mode
  • call the ESP-Now subroutine esp_now_init
  • call esp_now_set_self_role to set that the board is going to send ESP-Now packet; in ESP-Now's term, controller is the role for packet sender
  • call esp_now_add_peer to tell to which ESP to send ESP-Now packet to; such peer is another ESP board that it knows of [in advance]

When joystick action detected, send the joystick signals as ESP-Now packet to the server

JoystickPressCode joystickPressCode1;
JoystickPressCode joystickPressCode2;
bool checkJoystick1 = CheckJoystickPressCode(Joystick1, joystickPressCode1, 200, representation1); // 200 millis auto repeat delay
bool checkJoystick2 = CheckJoystickPressCode(Joystick2, joystickPressCode2, 200, representation2); // 200 millis auto repeat delay
if (checkJoystick1 || checkJoystick2)
{
  ESPNowJoystickData joystickData;
  memcpy(&joystickData.joystickPressCode1, &joystickPressCode1, sizeof(joystickPressCode1));
  memcpy(&joystickData.joystickPressCode2, &joystickPressCode2, sizeof(joystickPressCode2));
  esp_now_send(ClientMACAddress, (uint8_t *)&joystickData, sizeof(joystickData));
}

Notice that esp_now_send is the subroutine to call to send ESP-Now packet / joystick signals, which is encoded in the ESPNowJoystickData, as show previously.

Now, the server is also ready.

The output will be different than before

[x:0/y:-1/sw:0][.]
[x:0/y:-1/sw:0][.]
[x:1/y:0/sw:0][.]
[x:0/y:1/sw:0][.]
[x:-1/y:0/sw:0][.]
[.][x:0/y:-1/sw:0]
[.][x:1/y:0/sw:0]

And the corresponding ESP01 (the client) output will be like

IDLE: my MAC is 48:3F:DA:51:22:15
IDLE: my MAC is 48:3F:DA:51:22:15
[↑][.]
[→][.]
[↓][.]
[←][.]
[.][A]
[.][B]

Enjoy!

joystick_now_gif.gif

In a future post, I will demonstrate applying this wireless Joystick support to ESP01, to make it do something more fun. Until then, enjoy!

Peace be with you! Jesus loves you! May God bless you!