Skip to content

Instantly share code, notes, and snippets.

@Xfennec
Created November 25, 2019 16:17
Show Gist options
  • Select an option

  • Save Xfennec/e1215febb15b40c21bf029b38a31640b to your computer and use it in GitHub Desktop.

Select an option

Save Xfennec/e1215febb15b40c21bf029b38a31640b to your computer and use it in GitHub Desktop.
ESP8266 RC car with Websocket and Gamepad API
//#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
// uses the L293D chip for the motor
#define MOTOR1_ENABLE D5
#define MOTOR1_DIRA D3
#define MOTOR1_DIRB D4
#define MOTOR2_ENABLE D6
#define MOTOR2_DIRA D7
#define MOTOR2_DIRB D8
#define MAX_DAC_VAL 1023
float currentSteering = 0;
float currentThrottle = 0;
String html = R"=====(
<html>
<head>
<script>
var ws = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);
ws.onopen = function () {
document.getElementById('status').innerHTML = 'connected';
//ws.send('Connect ' + new Date());
};
ws.onclose = function () {
document.getElementById('status').innerHTML = 'closed';
};
ws.onerror = function (error) {
document.getElementById('status').innerHTML = 'error';
console.log('WebSocket Error ', error);
};
ws.onmessage = function (e) {
console.log('Server: ', e.data);
};
function sendData(data) {
console.log('data', data);
ws.send(data.throttle + '/' + data.steering);
}
var haveEvents = 'GamepadEvent' in window;
var haveWebkitEvents = 'WebKitGamepadEvent' in window;
var controllers = {};
var controls = {
steering: 0,
throttle: 0,
}
var rAF = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.requestAnimationFrame;
function connecthandler(e) {
addgamepad(e.gamepad);
}
function addgamepad(gamepad) {
controllers[gamepad.index] = gamepad;
rAF(updateStatus);
}
function disconnecthandler(e) {
removegamepad(e.gamepad);
}
function removegamepad(gamepad) {
delete controllers[gamepad.index];
}
function updateStatus() {
scangamepads();
var throttle = 0;
var needUpdate = false;
for (j in controllers) {
var controller = controllers[j];
for (var i=0; i<controller.buttons.length; i++) {
var val = controller.buttons[i];
var pressed = val == 1.0;
if (typeof(val) == "object") {
pressed = val.pressed;
val = val.value;
}
//if (pressed) console.log(i, val, pressed);
if (i == 6) {
throttle -= val;
}
if (i == 7) {
throttle += val;
}
}
throttle = throttle.toFixed(4);
if (throttle != controls.throttle) {
needUpdate = true;
controls.throttle = throttle;
}
for (var i=0; i<controller.axes.length; i++) {
//console.log(i + ": " + controller.axes[i].toFixed(4));
if (i == 0) {
d = controller.axes[i].toFixed(4);
if (d != controls.steering) {
needUpdate = true;
controls.steering = d;
}
}
}
}
if (needUpdate) {
sendData(controls);
console.log(controls);
}
rAF(updateStatus);
}
function scangamepads() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
for (var i = 0; i < gamepads.length; i++) {
if (gamepads[i]) {
if (!(gamepads[i].index in controllers)) {
addgamepad(gamepads[i]);
} else {
controllers[gamepads[i].index] = gamepads[i];
}
}
}
}
if (haveEvents) {
window.addEventListener("gamepadconnected", connecthandler);
window.addEventListener("gamepaddisconnected", disconnecthandler);
} else if (haveWebkitEvents) {
window.addEventListener("webkitgamepadconnected", connecthandler);
window.addEventListener("webkitgamepaddisconnected", disconnecthandler);
} else {
setInterval(scangamepads, 500);
}
</script>
</head>
<body>
RC Car status: <span id="status">unknown</span>
</body>
</html>
)=====";
#define BUFF_LEN 128
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
char data[BUFF_LEN];
char *sep;
switch (type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
//USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
strncpy(data, (char *)payload, BUFF_LEN);
data[BUFF_LEN-1] = 0;
sep = strchr(data, '/');
*sep = 0;
currentThrottle = atof(data);
currentSteering = atof(sep + 1);
//USE_SERIAL.printf("%f - %f\n", currentThrottle, currentSteering);
break;
}
}
void setup() {
//USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//USE_SERIAL.begin(9600);
//USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
//---set pin directions
pinMode(MOTOR1_ENABLE,OUTPUT);
pinMode(MOTOR1_DIRA,OUTPUT);
pinMode(MOTOR1_DIRB,OUTPUT);
pinMode(MOTOR2_ENABLE,OUTPUT);
pinMode(MOTOR2_DIRA,OUTPUT);
pinMode(MOTOR2_DIRB,OUTPUT);
WiFiMulti.addAP("SSID", "passphrase");
while (WiFiMulti.run() != WL_CONNECTED) {
delay(1000);
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
if (MDNS.begin("esp")) {
USE_SERIAL.println("MDNS responder started");
}
// handle index
server.on("/", []() {
// send index.html
server.send(200, "text/html", html.c_str());
});
server.begin();
}
void loop() {
static float steering;
static float throttle;
if (currentSteering != steering) {
steering = currentSteering;
analogWrite(MOTOR2_ENABLE, abs(steering * MAX_DAC_VAL));
if (steering > 0) {
digitalWrite(MOTOR2_DIRA,HIGH);
digitalWrite(MOTOR2_DIRB,LOW);
} else {
digitalWrite(MOTOR2_DIRB,HIGH);
digitalWrite(MOTOR2_DIRA,LOW);
}
}
if (currentThrottle != throttle) {
throttle = currentThrottle;
analogWrite(MOTOR1_ENABLE, abs(throttle * MAX_DAC_VAL));
if (throttle > 0) {
digitalWrite(MOTOR1_DIRA,HIGH);
digitalWrite(MOTOR1_DIRB,LOW);
} else {
digitalWrite(MOTOR1_DIRB,HIGH);
digitalWrite(MOTOR1_DIRA,LOW);
}
}
MDNS.update();
webSocket.loop();
server.handleClient();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment