Commit 26946de8 by mehtank

Merge branch 'master' of git.uclalemur.com:mehtank/paperbot

0 parents
## Wireless Servo Control, with ESP as Access Point
### Usage:
- Connect phone or laptop to "ESP_XXXX" wireless network, where XXXX is the ID of the robot
- Go to 192.168.4.1.
- A webpage with four buttons should appear. Click them to move the robot.
### Installation:
- In Arduino, go to Tools > ESP8266 Sketch Data Upload to upload the files from ./data to the ESP
- Then, in Arduino, compile and upload sketch to the ESP
### Requirements:
#### Arduino support for ESP8266 board
- In Arduino, add URL to Files > Preferences > Additional Board Managers URL.
- See https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/installing-the-esp8266-arduino-addon
#### Websockets library
- To install, Sketch > Include Library > Manage Libraries... > Websockets > Install
- https://github.com/Links2004/arduinoWebSockets
#### ESP8266FS tool
- To install, create "tools" folder in Arduino, download, and unzip. See
- https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md#uploading-files-to-file-system
### Hardware:
- NodeMCU Amica DevKit Board (ESP8266 chip)
- Motorshield for NodeMCU
- 2 continuous rotation servos plugged into motorshield pins D1, D2
- Paper chassis (see below)
Red lines: cut /
Blue dotted lines: mountain fold /
Green dotted lines: valley fold
![Paper chassis](/paperbot.png "Paper chassis")
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>whooo control robots</title>
<link rel="stylesheet" type="text/css" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
<h1>Yay Robots!</h1>
<table width="100%">
<tr>
<td>
<input type="range" name="slide" id="ltSlider" orient="vertical" min=0 max=180 value=90/>
</td>
<td>
<table class=btns>
<tr>
<td><button id="leftup" name="bt" value="UL" class="smbutton"></button></td>
<td><button id="forward" name="bt" value="FD" class="button"></button></td>
<td><button id="rightup" name="bt" value="UR" class="smbutton"></button></td>
</tr>
<tr>
<td><button id="left" name="bt" value="LT" class="button"></button></td>
<td><button id="center" name="bt" value="CC" class="smbutton"></button></td>
<td><button id="right" name="bt" value="RT" class="button"></button></td>
</tr>
<tr>
<td><button id="leftdown" name="bt" value="DL" class="smbutton"></button></td>
<td><button id="backward" name="bt" value="BK" class="button"></button></td>
<td><button id="rightdown" name="bt" value="DR" class="smbutton"></button></td>
<td></td>
</tr>
</table>
</td>
<td>
<input type="range" name="slide" id="rtSlider" orient="vertical" min=0 max=180 value=90/>
</td>
</tr>
</table>
<table class=wslog> <tr>
<td width="50%" valign="top">
<div id="wsRx" class="log"></div>
</td>
<td width="50%" valign="top">
<div id="wsTx" class="log"></div>
</td>
</tr></table>
<script>
/* Handle button / slider clicks + touches */
window.onload = function () {
var ltSlider = document.getElementById("ltSlider");
var rtSlider = document.getElementById("rtSlider");
var btns = document.getElementsByName("slide");
for (var i = 0; i < btns.length; i++) {
btns[i].oninput = function(e) {sliderSend(); e.stopPropagation(); e.preventDefault();};
btns[i].onchange = function() {btnRelease(); e.stopPropagation(); e.preventDefault();}
}
var btns = document.getElementsByName("bt");
for (var i = 0; i < btns.length; i++) {
btns[i].ontouchstart = function(e) {btnSend(this.value); e.stopPropagation(); e.preventDefault();};
btns[i].onmousedown = function(e) {btnSend(this.value); e.stopPropagation(); e.preventDefault();};
btns[i].ontouchend = function() {btnRelease(); e.stopPropagation(); e.preventDefault();}
btns[i].onmouseup = function() {btnRelease(); e.stopPropagation(); e.preventDefault();}
}
}
/* Handle keyboard commands */
document.onkeydown = function(e) {
e = e || window.event;
switch(e.which || e.keyCode) {
case 38:
btnSend("FD");
console.log("Up key");
break;
case 40:
btnSend("BK");
console.log("Down key");
break;
case 37:
btnSend("LT");
console.log("Left key");
break;
case 39:
btnSend("RT");
console.log("Right key");
break;
}
}
document.onkeyup = function(e) {
btnRelease();
console.log("Key up");
}
/* Logging */
var wsRx = document.getElementById("wsRx");
var wsTx = document.getElementById("wsTx");
function rxLog(text) {
wsRx.innerHTML = text + wsRx.innerHTML.split("<br>").slice(0, 4).join("<br>") ;
}
function txLog(text) {
wsTx.innerHTML = text + wsTx.innerHTML.split("<br>").slice(0, 4).join("<br>") ;
}
/* WebSocket utilities */
var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);
connection.onopen = function(){
var timeStr = new Date().toLocaleTimeString();
var text = "(" + timeStr + ") : CONNECTED <br>";
connection.send('Connect ' + new Date());
txLog(text);
};
connection.onerror = function(error){
var timeStr = new Date().toLocaleTimeString();
var text = "(" + timeStr + ") : EE = " + error + "<br>";
console.log('WebSocket Error ', error);
txLog(text);
};
connection.onmessage = function(e){
var timeStr = new Date().toLocaleTimeString();
var text = "(" + timeStr + ") : RX = " + e.data + "<br>";
console.log('Server: ', e.data);
rxLog(text);
};
/* Commands */
function sliderSend() {
var timeStr = new Date().toLocaleTimeString();
var text = "(" + timeStr + ") : TX = ~(" + ltSlider.value + ", " + rtSlider.value + ")<br>";
txLog(text);
var bbuf = new Uint8Array(3);
bbuf[0] = 126; // ~
bbuf[1] = ltSlider.value;
bbuf[2] = rtSlider.value;
console.log("Slider moved, sending ~(" + ltSlider.value + ", " + rtSlider.value + ")");
connection.send(bbuf.buffer);
}
function btnSend(val) {
var timeStr = new Date().toLocaleTimeString();
var text = "(" + timeStr + ") : TX = #" + val + "<br>";
txLog(text);
console.log('Button pressed, sending #'+val);
connection.send('#'+val);
}
function btnRelease() {
var timeStr = new Date().toLocaleTimeString();
var text = "(" + timeStr + ") : TX = #0<br>";
txLog(text);
ltSlider.value = 90;
rtSlider.value = 90;
console.log('Button released');
connection.send('#0');
}
</script>
</body>
</html>
h1 {
text-align: center;
}
.btns {
width: 200px;
text-align: center;
margin: 0 auto;
}
.wslog {
width: 100%;
}
button {
background-color:#ffaa42;
-moz-border-radius:13px;
-webkit-border-radius:13px;
border-radius:13px;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:20px;
font-weight:bold;
padding:20px 20px;
text-decoration:none;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently */
}
.smbutton {
padding: 5px 5px;
}
button:hover {
background-color:#ff8c00;
}
button:active {
position:relative;
top:1px;
}
button:focus {
outline:0;
}
input[type=range][orient=vertical]
{
writing-mode: bt-lr; /* IE */
-webkit-appearance: slider-vertical; /* WebKit */
width: 10px;
height: 200px;
padding: 0 0;
}
#ifndef __DEBUG_H
#define __DEBUG_H
# define DEBUG_ENABLED
# ifndef DEBUG_ENABLED
# define DEBUG1(str) (void)0
# define DEBUG2(str, var) (void)0
# else
# define DEBUG1(str) Serial.println(str)
# define DEBUG2(str, var) {Serial.print(str); Serial.println(var);}
# endif
# define GET_MACRO(_1,_2,NAME,...) NAME
# define DEBUG(...) GET_MACRO(__VA_ARGS__, DEBUG2, DEBUG1)(__VA_ARGS__)
# define LED_PIN D0
# define LED_ON digitalWrite(LED_PIN, LOW)
# define LED_OFF digitalWrite(LED_PIN, HIGH)
#endif
#include <Arduino.h>
#include "debug.h"
#include <FS.h>
bool isFileSetup = false;
void setupFile() {
DEBUG("Prepare file system");
SPIFFS.begin();
isFileSetup = true;
}
String loadFile(const char* filename) {
if (!isFileSetup)
setupFile();
File file = SPIFFS.open(filename, "r");
if (!file) {
DEBUG("File open failed");
} else {
DEBUG("File open success");
String text = "";
while (file.available()) {
text += file.readStringUntil('\n');
text += "\n";
}
file.close();
return text;
}
}
#ifndef __FILE_H
#define __FILE_H
void setupFile();
String loadFile(const char* filename);
#endif
/*
Wireless Servo Control, with ESP as Access Point
Usage:
Connect phone or laptop to "ESP_XXXX" wireless network, where XXXX is the ID of the robot
Go to 192.168.4.1.
A webpage with four buttons should appear. Click them to move the robot.
Installation:
In Arduino, go to Tools > ESP8266 Sketch Data Upload to upload the files from ./data to the ESP
Then, in Arduino, compile and upload sketch to the ESP
Requirements:
Arduino support for ESP8266 board
In Arduino, add URL to Files > Preferences > Additional Board Managers URL.
See https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/installing-the-esp8266-arduino-addon
Websockets library
To install, Sketch > Include Library > Manage Libraries... > Websockets > Install
https://github.com/Links2004/arduinoWebSockets
ESP8266FS tool
To install, create "tools" folder in Arduino, download, and unzip. See
https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md#uploading-files-to-file-system
Hardware:
* NodeMCU Amica DevKit Board (ESP8266 chip)
* Motorshield for NodeMCU
* 2 continuous rotation servos plugged into motorshield pins D1, D2
* Paper chassis
*/
#include <Arduino.h>
#include <Hash.h>
#include <FS.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <WebSocketsServer.h>
#include <ESP8266mDNS.h>
#include <Servo.h>
#include "debug.h"
#include "file.h"
#include "server.h"
const int SERVO_LEFT = D1;
const int SERVO_RIGHT = D2;
Servo servo_left;
Servo servo_right;
int servo_left_ctr = 90;
int servo_right_ctr = 90;
// WiFi AP parameters
char ap_ssid[13];
char* ap_password = "";
// WiFi STA parameters
char* sta_ssid =
"...";
char* sta_password =
"...";
char* mDNS_name = "paperbot";
String html;
String css;
void setup() {
setupPins();
sprintf(ap_ssid, "ESP_%08X", ESP.getChipId());
for(uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] BOOT WAIT %d...\n", t);
Serial.flush();
LED_ON;
delay(500);
LED_OFF;
delay(500);
}
LED_ON;
//setupSTA(sta_ssid, sta_password);
setupAP(ap_ssid, ap_password);
LED_OFF;
setupFile();
html = loadFile("/controls.html");
css = loadFile("/style.css");
registerPage("/", "text/html", html);
registerPage("/style.css", "text/css", css);
setupHTTP();
setupWS(webSocketEvent);
//setupMDNS(mDNS_name);
stop();
}
void loop() {
wsLoop();
httpLoop();
}
//
// Movement Functions //
//
void drive(int left, int right) {
servo_left.write(left);
servo_right.write(right);
}
void stop() {
DEBUG("stop");
drive(servo_left_ctr, servo_right_ctr);
LED_OFF;
}
void forward() {
DEBUG("forward");
drive(0, 180);
}
void backward() {
DEBUG("backward");
drive(180, 0);
}
void left() {
DEBUG("left");
drive(180, 180);
}
void right() {
DEBUG("right");
drive(0, 0);
}
//
// Setup //
//
void setupPins() {
// setup Serial, LEDs and Motors
Serial.begin(115200);
DEBUG("Started serial.");
pinMode(LED_PIN, OUTPUT); //Pin D0 is LED
LED_OFF; //Turn off LED
DEBUG("Setup LED pin.");
servo_left.attach(SERVO_LEFT);
servo_right.attach(SERVO_RIGHT);
DEBUG("Setup motor pins");
}
void webSocketEvent(uint8_t id, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
DEBUG("Web socket disconnected, id = ", id);
break;
case WStype_CONNECTED:
{
// IPAddress ip = webSocket.remoteIP(id);
// Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", id, ip[0], ip[1], ip[2], ip[3], payload);
DEBUG("Web socket connected, id = ", id);
// send message to client
wsSend(id, "Connected to ");
wsSend(id, ap_ssid);
break;
}
case WStype_BIN:
DEBUG("On connection #", id)
DEBUG(" got binary of length ", length);
for (int i = 0; i < length; i++)
DEBUG(" char : ", payload[i]);
if (payload[0] == '~')
drive(180-payload[1], payload[2]);
case WStype_TEXT:
DEBUG("On connection #", id)
DEBUG(" got text: ", (char *)payload);
if (payload[0] == '#') {
if(payload[1] == 'C') {
LED_ON;
wsSend(id, "Hello world!");
}
else if(payload[1] == 'F')
forward();
else if(payload[1] == 'B')
backward();
else if(payload[1] == 'L')
left();
else if(payload[1] == 'R')
right();
else if(payload[1] == 'U') {
if(payload[2] == 'L')
servo_left_ctr -= 1;
else if(payload[2] == 'R')
servo_right_ctr += 1;
char tx[20] = "Zero @ (xxx, xxx)";
sprintf(tx, "Zero @ (%3d, %3d)", servo_left_ctr, servo_right_ctr);
wsSend(id, tx);
}
else if(payload[1] == 'D') {
if(payload[2] == 'L')
servo_left_ctr += 1;
else if(payload[2] == 'R')
servo_right_ctr -= 1;
char tx[20] = "Zero @ (xxx, xxx)";
sprintf(tx, "Zero @ (%3d, %3d)", servo_left_ctr, servo_right_ctr);
wsSend(id, tx);
}
else
stop();
}
break;
}
}
paperbot.png

26.7 KB

#include <Arduino.h>
#include "server.h"
#include "debug.h"
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <WebSocketsServer.h>
#include <ESP8266mDNS.h>
#define STA_MAXTRIES 10
ESP8266WebServer httpServer = ESP8266WebServer(80);
WebSocketsServer wsServer = WebSocketsServer(81);
//
// Setup //
//
void setupSTA(char* ssid, char* password) {
DEBUG("Connecting to STA");
WiFi.begin(ssid, password);
int tries = 0;
while (WiFi.status() != WL_CONNECTED) {
if (tries++ > STA_MAXTRIES) {
DEBUG(" giving up.");
return;
}
delay(500);
DEBUG(" ... waiting");
}
IPAddress myIP = WiFi.localIP();
DEBUG("STA IP address: ");
DEBUG(myIP.toString());
}
void setupAP(char* ssid, char* password) {
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
DEBUG("AP IP address: ");
DEBUG(myIP.toString());
}
void registerPage(const char* url, const char* type, String &content) {
httpServer.on(url, [&type, &content]() { httpServer.send(200, type, content); });
}
void setupHTTP() {
httpServer.begin();
}
void setupWS(ws_callback_t callback) {
// start webSocket server
wsServer.begin();
wsServer.onEvent(callback);
}
void setupMDNS(char* name) {
if(MDNS.begin(name)) {
// Add services to mDNS
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 81);
DEBUG("mDNS responder started");
} else {
DEBUG("mDNS failed\n");
}
}
void httpLoop() {
httpServer.handleClient();
}
void wsLoop() {
wsServer.loop();
}
void wsSend(int id, char* txt) {
wsServer.sendTXT(id, txt);
}
#include <WebSocketsServer.h>
#ifndef __SERVER_H
#define __SERVER_H
typedef void (*ws_callback_t)(uint8_t, WStype_t, uint8_t* , size_t);
void setupSTA(char* ssid, char* password) ;
void setupAP(char* ssid, char* password) ;
void registerPage(const char* url, const char* type, String &content) ;
void setupHTTP() ;
void setupWS(ws_callback_t callback) ;
void setupMDNS(char* name) ;
void httpLoop();
void wsLoop();
void wsSend(int id, char* txt);
#endif
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!