Skip to content

Instantly share code, notes, and snippets.

@murilopolese
Last active September 7, 2019 11:40
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save murilopolese/67c657863a14641df26abe670cb0da29 to your computer and use it in GitHub Desktop.
Fabulous Car!

Fabulous Car

This is an attempt to make it easy to control motors with a remote control over wifi using the ESP8266 board.

// CODE FOR REMOTE CONTROLLED CAR USING 2 SERVOS
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#include <Servo.h>
#define USE_SERIAL Serial
#define PIN1 4
#define PIN2 5
Servo servo1, servo2;
ESP8266WebServer server (80);
ESP8266WiFiMulti WiFiMulti;
const char *ssid = "fabulouscar";
const char *password = "bananabanana";
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
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);
String str((char*)payload);
parse_instructions(str);
break;
}
}
void parse_instructions(String str)
{
int delimiter_pos = str.indexOf(" ");
String command = str.substring(0, delimiter_pos); // command
String params = str.substring(delimiter_pos+1); // parameter
if(command.equals("m1f")) {
// Motor 1 forward
servo1.attach(PIN1);
servo1.write(180);
}
if(command.equals("m1b")) {
// Motor 1 backward
servo1.attach(PIN1);
servo1.write(0);
}
if(command.equals("m1s")) {
// Motor 1 stop
servo1.detach();
}
if(command.equals("m2f")) {
// Motor 2 forward
servo2.attach(PIN2);
servo2.write(0);
}
if(command.equals("m2b")) {
// Motor 2 backward
servo2.attach(PIN2);
servo2.write(180);
}
if(command.equals("m2s")) {
// Motor 2 stop
servo2.detach();
}
if(command.equals("m1p")) {
// Motor 1 pwm
servo1.attach(PIN1);
servo1.write(params.toInt());
}
if(command.equals("m2p")) {
// Motor 2 pwm
servo2.attach(PIN2);
servo2.write(params.toInt());
}
}
void setup() {
//USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
pinMode(PIN1, OUTPUT);
pinMode(PIN2, OUTPUT);
// servo1.attach(PIN1);
// servo2.attach(PIN2);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
// WiFiMulti.addAP("SSID", "XXX");
// while(WiFiMulti.run() != WL_CONNECTED) {
// delay(100);
// }
USE_SERIAL.print("Setting soft-AP ... ");
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
USE_SERIAL.print("AP IP address: ");
USE_SERIAL.println(myIP);
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
if(MDNS.begin("fabulouscar")) {
USE_SERIAL.println("MDNS responder started");
}
// handle index
server.on("/", []() {
// send index.html
server.send(200, "text/html", "<meta charset=utf-8><title>Remote Control</title><meta content='width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no'name=viewport><style>.pad{width:50%;height:50%;position:absolute}</style><div style=width:100vw><div style=top:0;left:0;background:#6686b7 class=pad id=m1f></div><div style=top:50%;left:0;background:#54c1de class=pad id=m1b></div><div style=top:0;right:0;background:#ff5e63 class=pad id=m2f></div><div style=top:50%;right:0;background:#fcba55 class=pad id=m2b></div></div><script>window.onload=function(){boardPath=location.hash?location.hash.substring(1):location.hostname,connection=new WebSocket('ws://'+boardPath+':81/',['arduino']),connection.onopen=function(){connection.send('Connect '+new Date)},connection.onerror=function(e){console.log('WebSocket Error ',e)},connection.onmessage=function(e){console.log('Server: ',e.data)},createEvent=function(e){return function(t){console.log(e),t.preventDefault(),connection.send(e)}},registerEvents=function(e){forwardButton=document.getElementById('m'+e+'f'),backwardButton=document.getElementById('m'+e+'b'),forwardButton.addEventListener('touchstart',createEvent('m'+e+'f')),forwardButton.addEventListener('mousedown',createEvent('m'+e+'f')),forwardButton.addEventListener('touchend',createEvent('m'+e+'s')),forwardButton.addEventListener('mouseup',createEvent('m'+e+'s')),backwardButton.addEventListener('touchstart',createEvent('m'+e+'b')),backwardButton.addEventListener('mousedown',createEvent('m'+e+'b')),backwardButton.addEventListener('touchend',createEvent('m'+e+'s')),backwardButton.addEventListener('mouseup',createEvent('m'+e+'s'))},registerEvents(1),registerEvents(2)}</script>');");
});
server.begin();
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 81);
}
void loop() {
webSocket.loop();
server.handleClient();
}
// CODE FOR REMOTE CONTROLLED CAR USING DC MOTOR WITH H-BRIDGE (DRIVER)
// AND THE MOST REGULAR TOWER SERVO
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#include <Servo.h>
#define USE_SERIAL Serial
Servo servo;
int servoPin = 12;
int forwardPin = 5;
int backwardPin = 4;
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server = ESP8266WebServer(80);
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
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);
String str((char*)payload);
parse_instructions(str);
break;
}
}
void parse_instructions(String str)
{
int delimiter_pos = str.indexOf(" ");
String command = str.substring(0, delimiter_pos); // command
String params = str.substring(delimiter_pos+1); // parameter
if(command.equals("l")) {
// left
servo.write(180);
}
if(command.equals("c")) {
// center
servo.write(90);
}
if(command.equals("r")) {
// right
servo.write(0);
}
if(command.equals("f")) {
// forward
digitalWrite(forwardPin, LOW);
digitalWrite(backwardPin, HIGH);
}
if(command.equals("b")) {
// forward
digitalWrite(forwardPin, HIGH);
digitalWrite(backwardPin, LOW);
}
if(command.equals("s")) {
// stop
digitalWrite(forwardPin, HIGH);
digitalWrite(backwardPin, HIGH);
}
}
void setup() {
//USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
servo.attach(servoPin);
pinMode(forwardPin, OUTPUT);
pinMode(backwardPin, OUTPUT);
digitalWrite(forwardPin, HIGH);
digitalWrite(backwardPin, HIGH);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("xxx", "xxx");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
if(MDNS.begin("speed2")) {
USE_SERIAL.println("MDNS responder started");
}
// handle index
server.on("/", []() {
// send index.html
server.send(200, "text/html", "<html> <head> <meta charset='utf-8'> <title>Remote Control</title> <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'/> <style>.pad{width:50%; height:50%; position:absolute}</style> </head> <body> <div style='width:100vw;height100vh;'> <div id='f' class='pad' style='top:0;left:0;background:#6686b7'></div><div id='s' class='pad' style='top:50%;left:0;background:#54c1de'></div><div id='l' class='pad' style='top:0;right:0;background:#ff5e63'></div><div id='r' class='pad' style='top:50%;right:0;background:#fcba55'></div></div><script type='text/javascript'> window.onload=function (){boardPath=location.hash ? location.hash.substring(1) : location.hostname; connection=new WebSocket('ws://'+boardPath+':81/', ['arduino']); connection.onopen=function (){connection.send('Connect ' + new Date());}; connection.onerror=function (error){console.log('WebSocket Error ', error);}; connection.onmessage=function (e){console.log('Server: ', e.data);}; createEvent=function (n){return function (e){console.log(n); e.preventDefault(); connection.send(n);};}; forwardButton=document.getElementById('f'); backwardButton=document.getElementById('s'); leftButton=document.getElementById('l'); rightButton=document.getElementById('r'); forwardButton.addEventListener('touchstart', createEvent('f')); forwardButton.addEventListener('mousedown', createEvent('f')); forwardButton.addEventListener('touchend', createEvent('s')); forwardButton.addEventListener('mouseup', createEvent('s')); backwardButton.addEventListener('touchstart', createEvent('b')); backwardButton.addEventListener('mousedown', createEvent('b')); backwardButton.addEventListener('touchend', createEvent('s')); backwardButton.addEventListener('mouseup', createEvent('s')); leftButton.addEventListener('touchstart', createEvent('l')); leftButton.addEventListener('mousedown', createEvent('l')); leftButton.addEventListener('touchend', createEvent('c')); leftButton.addEventListener('mouseup', createEvent('c')); rightButton.addEventListener('touchstart', createEvent('r')); rightButton.addEventListener('mousedown', createEvent('r')); rightButton.addEventListener('touchend', createEvent('c')); rightButton.addEventListener('mouseup', createEvent('c'));}; </script> </body></html>");
});
server.begin();
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 81);
}
void loop() {
webSocket.loop();
server.handleClient();
}
print('Booting...')
import network
import utime
import webrepl
SSID = 'WIFI NETWORK NAME'
PASSWORD = 'WIFI NETWORK PASSWORD'
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.connect(SSID, PASSWORD)
timeout = 50
while not sta.isconnected():
utime.sleep_ms(100)
timeout -= 1
if timeout == 0:
print('Could not connect to wifi network')
break
if sta.isconnected():
print('connected', sta.ifconfig())
webrepl.start(password=123456)
else:
print('not connected')
from machine import Pin, PWM
class Motor():
def __init__(self, pin_number):
pin = Pin(pin_number, Pin.OUT)
self.motor = PWM(pin)
self.motor.freq(1000)
self.motor.duty(1)
def set_value(self, value=1):
self.motor.duty(value)
def forward(self):
self.set_value(1020)
def backward(self):
self.set_value(100)
def stop(self):
self.set_value(1)
motor1 = Motor(13)
motor2 = Motor(15)
m1f = motor1.forward
m1b = motor1.backward
m1s = motor1.stop
m2f = motor2.forward
m2b = motor2.backward
m2s = motor2.stop
<!-- minified with on https://kangax.github.io/html-minifier/ -->
<html>
<head>
<meta charset='utf-8'>
<title>Remote Control</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<style>
.pad {
width:50%;
height:50%;
position:absolute
}
</style>
</head>
<body>
<div style='width:100vw;height100vh;'>
<div id='m1f' class='pad' style='top:0;left:0;background:#6686b7'></div>
<div id='m1b' class='pad' style='top:50%;left:0;background:#54c1de'></div>
<div id='m2f' class='pad' style='top:0;right:0;background:#ff5e63'></div>
<div id='m2b' class='pad' style='top:50%;right:0;background:#fcba55'></div>
</div>
<script type="text/javascript" src="webrepl.js"></script>
<script type='text/javascript'>
// Please: All the credit to the FABULOUS work of https://github.com/micropython/webrepl
class WebREPL {
constructor(opts) {
opts = opts || {}
this.binaryState = 0
this.ip = opts.ip || '192.168.1.4'
this.password = opts.password || 'micropythoN'
this.sendFileName = ''
this.sendFileData = new ArrayBuffer()
this.getFileName = ''
this.getFileData = new ArrayBuffer()
this.STOP = '\r\x03' // CTRL-C 2x
this.RESET = '\r\x04' // CTRL-D
this.ENTER_RAW_REPL = '\r\x01' // CTRL-A
this.EXIT_RAW_REPL = '\r\x04\r\x02' // CTRL-D + CTRL-B
if (opts.autoconnect) {
this.connect()
}
}
_decodeResp(data) {
if (data[0] == 'W'.charCodeAt(0) && data[1] == 'B'.charCodeAt(0)) {
var code = data[2] | (data[3] << 8)
return code;
} else {
return -1;
}
}
_handleMessage(event) {
if (event.data instanceof ArrayBuffer) {
let data = new Uint8Array(event.data)
switch (this.binaryState) {
case 11:
// first response for put
if (this._decodeResp(data) == 0) {
// send file data in chunks
for (let offset = 0; offset < this.sendFileData.length; offset += 1024) {
this.ws.send(this.sendFileData.slice(offset, offset + 1024))
}
this.binaryState = 12
}
break
case 12:
// final response for put
if (this._decodeResp(data) == 0) {
console.log(`Sent ${this.sendFileName}, ${this.sendFileData.length} bytes`)
} else {
console.log(`Failed sending ${this.sendFileName}`)
}
this.binaryState = 0
break;
case 21:
// first response for get
if (this._decodeResp(data) == 0) {
this.binaryState = 22
var rec = new Uint8Array(1)
rec[0] = 0
this.ws.send(rec)
}
break;
case 22: {
// file data
var sz = data[0] | (data[1] << 8)
if (data.length == 2 + sz) {
// we assume that the data comes in single chunks
if (sz == 0) {
// end of file
this.binaryState = 23
} else {
// accumulate incoming data to this.getFileData
var new_buf = new Uint8Array(this.getFileData.length + sz)
new_buf.set(this.getFileData)
new_buf.set(data.slice(2), this.getFileData.length)
this.getFileData = new_buf
console.log('Getting ' + this.getFileName + ', ' + this.getFileData.length + ' bytes')
var rec = new Uint8Array(1)
rec[0] = 0
this.ws.send(rec)
}
} else {
this.binaryState = 0
}
break;
}
case 23:
// final response
if (this._decodeResp(data) == 0) {
console.log(`Got ${this.getFileName}, ${this.getFileData.length} bytes`)
this.saveAs(new Blob([this.getFileData], {type: "application/octet-stream"}), this.getFileName)
} else {
console.log(`Failed getting ${this.getFileName}`)
}
this.binaryState = 0
break
case 31:
// first (and last) response for GET_VER
console.log('GET_VER', data)
this.binaryState = 0
break
}
}
// If is asking for password, send password
if( event.data == 'Password: ' ) {
this.ws.send(`${this.password}\r`)
}
this.onMessage(event.data)
}
connect() {
this.ws = new WebSocket(`ws://${this.ip}:8266`)
this.ws.binaryType = 'arraybuffer'
this.ws.onopen = () => {
this.onConnected()
this.ws.onmessage = this._handleMessage.bind(this)
}
}
disconnect() {
this.ws.close()
}
onConnected() {
console.log('onConnected')
}
onMessage(msg) {
console.log('onMessage', msg)
}
saveAs(blob) {
console.log(`saving as: ${blob}`)
}
sendStop() {
this.eval(this.STOP)
}
softReset() {
this.sendStop()
this.eval(this.RESET)
}
enterRawRepl() {
this.eval(this.ENTER_RAW_REPL)
}
exitRawRepl() {
this.eval(this.EXIT_RAW_REPL)
}
execRaw(raw) {
this.eval(raw)
if (raw.indexOf('\n') == -1) {
this.eval('\r')
}
}
exec(command) {
this.eval(command)
}
execFromString(code) {
this.sendStop()
this.enterRawRepl()
this.execRaw(code)
this.exitRawRepl()
}
eval(command) {
this.ws.send(`${command}`)
}
_sendFile() {
let dest_fname = this.sendFileName
let dest_fsize = this.sendFileData.length
// WEBREPL_FILE = "<2sBBQLH64s"
let rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64)
rec[0] = 'W'.charCodeAt(0)
rec[1] = 'A'.charCodeAt(0)
rec[2] = 1 // put
rec[3] = 0
rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0;
rec[12] = dest_fsize & 0xff; rec[13] = (dest_fsize >> 8) & 0xff; rec[14] = (dest_fsize >> 16) & 0xff; rec[15] = (dest_fsize >> 24) & 0xff;
rec[16] = dest_fname.length & 0xff; rec[17] = (dest_fname.length >> 8) & 0xff;
for (let i = 0; i < 64; ++i) {
if (i < dest_fname.length) {
rec[18 + i] = dest_fname.charCodeAt(i)
} else {
rec[18 + i] = 0
}
}
// initiate put
this.binaryState = 11
console.log('Sending ' + this.sendFileName + '...')
this.ws.send(rec)
}
sendFile(file) {
this.sendFileName = file.name
let reader = new FileReader()
reader.onload = (e) => {
this.sendFileData = new Uint8Array(e.target.result)
this._sendFile()
};
reader.readAsArrayBuffer(file)
}
loadFile(path) {
// WEBREPL_FILE = "<2sBBQLH64s"
let rec = new Uint8Array(2 + 1 + 1 + 8 + 4 + 2 + 64);
rec[0] = 'W'.charCodeAt(0);
rec[1] = 'A'.charCodeAt(0);
rec[2] = 2; // get
rec[3] = 0;
rec[4] = 0; rec[5] = 0; rec[6] = 0; rec[7] = 0; rec[8] = 0; rec[9] = 0; rec[10] = 0; rec[11] = 0;
rec[12] = 0; rec[13] = 0; rec[14] = 0; rec[15] = 0;
rec[16] = path.length & 0xff; rec[17] = (path.length >> 8) & 0xff;
for (let i = 0; i < 64; ++i) {
if (i < path.length) {
rec[18 + i] = path.charCodeAt(i);
} else {
rec[18 + i] = 0;
}
}
// initiate get
this.binaryState = 21;
this.getFileName = path;
this.getFileData = new Uint8Array(0);
console.log('Getting ' + this.getFileName + '...');
this.ws.send(rec);
}
removeFile(filename) {
const pCode = `from os import remove
remove('${filename}')`
this.execFromString(pCode)
}
}
window.onload = function () {
boardPath = location.hash ? location.hash.substring(1) : location.hostname;
window.repl = new WebREPL({
ip: boardPath || '192.168.0.17',
password: '123456',
autoconnect: true
})
repl.onMessage = (msg) => {
if (typeof msg === 'string') {
console.log(msg)
// consoleOut.innerText = (consoleOut.innerText + msg).slice(-100)
}
}
createEvent = function (n) {
return function (e) {
// console.log(`${n}()\r\n`);
e.preventDefault();
repl.eval(`${n}()\r\n`)
};
};
registerEvents = function (n) {
forwardButton = document.getElementById('m'+n+'f');
backwardButton = document.getElementById('m'+n+'b');
forwardButton.addEventListener('touchstart', createEvent('m'+n+'f'));
forwardButton.addEventListener('mousedown', createEvent('m'+n+'f'));
forwardButton.addEventListener('touchend', createEvent('m'+n+'s'));
forwardButton.addEventListener('mouseup', createEvent('m'+n+'s'));
backwardButton.addEventListener('touchstart', createEvent('m'+n+'b'));
backwardButton.addEventListener('mousedown', createEvent('m'+n+'b'));
backwardButton.addEventListener('touchend', createEvent('m'+n+'s'));
backwardButton.addEventListener('mouseup', createEvent('m'+n+'s'));
}
registerEvents(1);
registerEvents(2);
};
</script>
</body>
</html>
<!-- minified with on https://kangax.github.io/html-minifier/ -->
<html>
<head>
<meta charset='utf-8'>
<title>Remote Control</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<style>
.pad {
width:50%;
height:50%;
position:absolute
}
</style>
</head>
<body>
<div style='width:100vw;height100vh;'>
<div id='m1f' class='pad' style='top:0;left:0;background:#6686b7'></div>
<div id='m1b' class='pad' style='top:50%;left:0;background:#54c1de'></div>
<div id='m2f' class='pad' style='top:0;right:0;background:#ff5e63'></div>
<div id='m2b' class='pad' style='top:50%;right:0;background:#fcba55'></div>
</div>
<script type='text/javascript'>
window.onload = function () {
boardPath = location.hash ? location.hash.substring(1) : location.hostname;
connection = new WebSocket('ws://'+boardPath+':81/', ['arduino']);
connection.onopen = function () {
connection.send('Connect ' + new Date());
};
connection.onerror = function (error) {
console.log('WebSocket Error ', error);
};
connection.onmessage = function (e) {
console.log('Server: ', e.data);
};
createEvent = function (n) {
return function (e) {
console.log(n);
e.preventDefault();
connection.send(n);
};
};
registerEvents = function (n) {
forwardButton = document.getElementById('m'+n+'f');
backwardButton = document.getElementById('m'+n+'b');
forwardButton.addEventListener('touchstart', createEvent('m'+n+'f'));
forwardButton.addEventListener('mousedown', createEvent('m'+n+'f'));
forwardButton.addEventListener('touchend', createEvent('m'+n+'s'));
forwardButton.addEventListener('mouseup', createEvent('m'+n+'s'));
backwardButton.addEventListener('touchstart', createEvent('m'+n+'b'));
backwardButton.addEventListener('mousedown', createEvent('m'+n+'b'));
backwardButton.addEventListener('touchend', createEvent('m'+n+'s'));
backwardButton.addEventListener('mouseup', createEvent('m'+n+'s'));
}
registerEvents(1);
registerEvents(2);
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment