Major Update: Developed all the backend programming for full duplex transmission in real time, using websockets. Added some minor fixes to formatting of text. Tested using a button and pot to simulate odometer and battery values to test real time information transfer from rover to client. Used LED to signifiy movement direction, and tested the same from client to rover. Tests passed!

This commit is contained in:
jc4419 2021-05-29 07:54:53 +04:00
parent 7478a4b0db
commit bf2675fa45
2 changed files with 467 additions and 54 deletions

View file

@ -90,19 +90,90 @@ meter::after {
</style> </style>
<script> <script>
var connection = new WebSocket('ws://'+location.hostname+':81/');
var MVM_F_status = 0;
var MVM_L_status = 0;
var MVM_R_status = 0;
var MVM_B_status = 0;
var BTRY_VOLT = 0;
var ODO_DIST = 0;
function round(value, precision) {
var multiplier = Math.pow(10, precision || 0);
return Math.round(value * multiplier) / multiplier;
}
connection.onmessage = function(event){
var raw_data = event.data;
console.log(raw_data);
var data = JSON.parse(raw_data);
digiBTRY_VOLT = data.BTRY_VOLT;
BTRY_VOLT = round((digiBTRY_VOLT*(4.8e-4)+4), 1)
ODO_DIST = data.ODO_DIST;
document.getElementById("btry_meter").value = BTRY_VOLT;
document.getElementById("Odometer").innerHTML = ODO_DIST;
}
function send_data()
{
var raw_data = '{"MVM_F":'+MVM_F_status+',"MVM_L":'+MVM_L_status+',"MVM_R":'+MVM_R_status+',"MVM_B":'+MVM_B_status+'}';
connection.send(raw_data);
console.log(raw_data);
}
function left_pressed(){
MVM_L_status = 1;
send_data();
}
function left_unpressed(){
MVM_L_status = 0;
send_data();
}
function up_pressed(){
MVM_F_status = 1;
send_data();
}
function up_unpressed(){
MVM_F_status = 0;
send_data();
}
function right_pressed(){
MVM_R_status = 1;
send_data();
}
function right_unpressed(){
MVM_R_status = 0;
send_data();
}
function down_pressed(){
MVM_B_status = 1;
send_data();
}
function down_unpressed(){
MVM_B_status = 0;
send_data();
}
document.onkeydown = function(e) { document.onkeydown = function(e) {
switch (e.keyCode) { switch (e.keyCode) {
case 37: case 37:
document.getElementById("left-arrow").className = "button pressed"; document.getElementById("left-arrow").className = "button pressed";
left_pressed();
break; break;
case 38: case 38:
document.getElementById("up-arrow").className = "button pressed"; document.getElementById("up-arrow").className = "button pressed";
up_pressed();
break; break;
case 39: case 39:
document.getElementById("right-arrow").className = "button pressed"; document.getElementById("right-arrow").className = "button pressed";
right_pressed();
break; break;
case 40: case 40:
document.getElementById("down-arrow").className = "button pressed"; document.getElementById("down-arrow").className = "button pressed";
down_pressed();
break; break;
} }
}; };
@ -110,20 +181,23 @@ document.onkeyup = function(e) {
switch (e.keyCode) { switch (e.keyCode) {
case 37: case 37:
document.getElementById("left-arrow").className = "button"; document.getElementById("left-arrow").className = "button";
left_unpressed();
break; break;
case 38: case 38:
document.getElementById("up-arrow").className = "button"; document.getElementById("up-arrow").className = "button";
up_unpressed();
break; break;
case 39: case 39:
document.getElementById("right-arrow").className = "button"; document.getElementById("right-arrow").className = "button";
right_unpressed();
break; break;
case 40: case 40:
document.getElementById("down-arrow").className = "button"; document.getElementById("down-arrow").className = "button";
down_unpressed();
break; break;
} }
}; };
</script> </script>
</head> </head>
<body> <body>
@ -150,39 +224,25 @@ document.onkeyup = function(e) {
<div class="section-container"> <div class="section-container">
<div class="sensor-data"> <div class="sensor-data">
<h2>Sensor Data</h2> <h2>Sensor Data</h2>
<!-- <div class="section-container">
<div class="sensor-name">
<ul>
<li><label>Battery Voltage</label></li>
<li><label>Odometer</label></li>
</ul>
</div>
</div>
<div class="section-container">
<div class="sensor-value">
<ul>
<li><meter min="4.0" max="6.0" low ="4.5" optimum="5.0" high="4.8" value="5.5" title="V"></meter></li>
<li>25</li>
</ul>
</div>
</div> -->
<ul> <ul>
<li><div class="section-container"> <li><div class="section-container">
<label>Battery Voltage</label> <label>Battery Voltage</label>
</div> </div>
<div class="section-container"> <div class="section-container">
<meter min="4.0" max="6.0" low ="4.5" optimum="5.0" high="4.8" value="5.8" title="V"></meter> <meter id="btry_meter" min="4.0" max="6.0" low ="4.5" optimum="5.0" high="4.8" value="5.8" title="V"></meter>
</div> </div>
</li> </li>
<li><div class="section-container"> <li><div class="section-container">
<label>Odometer</label> <label>Odometer</label>
</div> </div>
<div class="section-container"> <div class="section-container">
<strong id="Odometer">28mm</strong> <strong id="Odometer">28</strong><strong>mm</strong>
</div> </div>
</li> </li>
</ul> </ul>
</div> </div>
@ -192,4 +252,4 @@ document.onkeyup = function(e) {
</body> </body>
</html> </html>

View file

@ -1,38 +1,391 @@
const int potPin = 27; #include <WiFi.h>
const int butPin = 16; #include<ESPmDNS.h>
const int U_Led = 14; #include <WebSocketsServer.h>
const int L_Led = 12; #include <ESPAsyncWebServer.h>
const int R_Led = 15; #include <ArduinoJson.h>
const int D_Led = 13; #include "Ticker.h"
const int potPin = 34; //used to simulate battery voltage.
const int butPin = 16; //used to increment a variable to simulate distance increasing.
const int U_Led = 14; //LED subsitute for the 'movement forward command'.
const int L_Led = 12; //LED subsitute for the 'movement left command'.
const int R_Led = 15; //LED subsitute for the 'movement right command'.
const int D_Led = 13; //LED subsitute for the 'movement back command'.
int potVal = 0; int potVal = 0;
bool butState = 0; bool butState = 1; //Variables only for testing - will be removed in final
void setup() {
// put your setup code here, to run once: int d = 0; //Initializing variable for odometer distance.
Serial.begin(115200);
pinMode(U_Led, OUTPUT); void send_sensor();
pinMode(L_Led, OUTPUT); Ticker timer;
pinMode(R_Led, OUTPUT);
pinMode(D_Led, OUTPUT); char index_html[] PROGMEM = R"=====(
pinMode(butPin, INPUT_PULLUP);
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Rover Command Center</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
box-sizing: border-box;
}
.section-container {
float: left;
width: 50%;
padding: 10px;
}
.flex-container {
display: flex;
flex-wrap: nowrap;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
} }
void loop() { li {
// put your main code here, to run repeatedly: padding: 0px;
//potVal = analogRead(potPin); margin-bottom: 0px;
//Serial.println(potVal);
butState = digitalRead(butPin);
if (butState == LOW) {
// turn LED on:
digitalWrite(U_Led, HIGH);
digitalWrite(L_Led, HIGH);
digitalWrite(R_Led, HIGH);
digitalWrite(D_Led, HIGH);
} else {
// turn LED off:
digitalWrite(U_Led, LOW);
digitalWrite(L_Led, LOW);
digitalWrite(R_Led, LOW);
digitalWrite(D_Led, LOW);
}
delay(20);
} }
:is(h1, h2, h3, h4, h5, h6, label, strong, meter) {
font-family: Arial, Helvetica, sans-serif;
}
.movement-control {
text-align: center;
}
.sensor-data {
text-align: center;
}
meter{
width: 100%;
height: 40px;
transform: translateY(-8px);
}
meter::after {
content : attr(value) attr(title);
top:-28px;
left:0px;
position:relative;
}
.button {
display: inline-block;
padding: 15px 25px;
font-size: 24px;
cursor: pointer;
text-align: center;
text-decoration: none;
outline: none;
color: rgb(255, 255, 255);
background-color: #161616;
border: none;
border-radius: 15px;
box-shadow: 0 9px rgb(161, 161, 161);
}
.button:hover {background-color: #585858}
.button:active {
background-color: #107C10;
box-shadow: 0 5px rgb(161, 161, 161);
transform: translateY(4px);
}
.pressed {
background-color: #107C10;
box-shadow: 0 5px rgb(161, 161, 161);
transform: translateY(4px);
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
<script>
var connection = new WebSocket('ws://'+location.hostname+':81/');
var MVM_F_status = 0;
var MVM_L_status = 0;
var MVM_R_status = 0;
var MVM_B_status = 0;
var BTRY_VOLT = 0;
var ODO_DIST = 0;
function round(value, precision) {
var multiplier = Math.pow(10, precision || 0);
return Math.round(value * multiplier) / multiplier;
}
connection.onmessage = function(event){
var raw_data = event.data;
console.log(raw_data);
var data = JSON.parse(raw_data);
digiBTRY_VOLT = data.BTRY_VOLT;
BTRY_VOLT = round((digiBTRY_VOLT*(4.8e-4)+4), 1)
ODO_DIST = data.ODO_DIST;
document.getElementById("btry_meter").value = BTRY_VOLT;
document.getElementById("Odometer").innerHTML = ODO_DIST;
}
function send_data()
{
var raw_data = '{"MVM_F":'+MVM_F_status+',"MVM_L":'+MVM_L_status+',"MVM_R":'+MVM_R_status+',"MVM_B":'+MVM_B_status+'}';
connection.send(raw_data);
console.log(raw_data);
}
function left_pressed(){
MVM_L_status = 1;
send_data();
}
function left_unpressed(){
MVM_L_status = 0;
send_data();
}
function up_pressed(){
MVM_F_status = 1;
send_data();
}
function up_unpressed(){
MVM_F_status = 0;
send_data();
}
function right_pressed(){
MVM_R_status = 1;
send_data();
}
function right_unpressed(){
MVM_R_status = 0;
send_data();
}
function down_pressed(){
MVM_B_status = 1;
send_data();
}
function down_unpressed(){
MVM_B_status = 0;
send_data();
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 37:
document.getElementById("left-arrow").className = "button pressed";
left_pressed();
break;
case 38:
document.getElementById("up-arrow").className = "button pressed";
up_pressed();
break;
case 39:
document.getElementById("right-arrow").className = "button pressed";
right_pressed();
break;
case 40:
document.getElementById("down-arrow").className = "button pressed";
down_pressed();
break;
}
};
document.onkeyup = function(e) {
switch (e.keyCode) {
case 37:
document.getElementById("left-arrow").className = "button";
left_unpressed();
break;
case 38:
document.getElementById("up-arrow").className = "button";
up_unpressed();
break;
case 39:
document.getElementById("right-arrow").className = "button";
right_unpressed();
break;
case 40:
document.getElementById("down-arrow").className = "button";
down_unpressed();
break;
}
};
</script>
</head>
<body>
<h1 style="text-align:center;">ROVER COMMAND CENTER</h1>
<div class="clearfix">
<div class="section-container">
<div class ="movement-control">
<h2>Movement Control</h2>
<div style="transform: translateY(0px);">
<button id="up-arrow" class="button" ><span>&#8679;</span></button>
</div>
<div style="transform: translateY(13px);">
<button id="left-arrow" class="button"><span>&#8678;</span></button>
<button id="down-arrow" class="button"><span>&#8681;</span></button>
<button id="right-arrow" class="button"><span>&#8680;</span></button>
</div>
</div>
</div>
<div class="section-container">
<div class="sensor-data">
<h2>Sensor Data</h2>
<ul>
<li><div class="section-container">
<label>Battery Voltage</label>
</div>
<div class="section-container">
<meter id="btry_meter" min="4.0" max="6.0" low ="4.5" optimum="5.0" high="4.8" value="5.8" title="V"></meter>
</div>
</li>
<li><div class="section-container">
<label>Odometer</label>
</div>
<div class="section-container">
<strong id="Odometer">28</strong><strong>mm</strong>
</div>
</li>
</ul>
</div>
</div>
</div>
</body>
</html>
)=====";
AsyncWebServer server(80); // server port 80 for initial HTTP request for the main webpage.
WebSocketsServer websockets(81); // server port 81 for real time data flow through websockets.
void notFound(AsyncWebServerRequest *request)
{
request->send(404, "text/plain", "Page Not found. Check URI/IP address.");
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch (type)
{
case WStype_DISCONNECTED:
Serial.printf("Client[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = websockets.remoteIP(num);
Serial.printf("Client[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
}
break;
case WStype_TEXT: {
Serial.printf("Client[%u] sent Text: %s\n", num, payload);
String command = String((char*)( payload));
DynamicJsonDocument doc(200); //creating an instance of a DynamicJsonDocument allocating 200bytes on the heap.
DeserializationError error = deserializeJson(doc, command); // deserialize 'doc' and parse for parameters we expect to receive.
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
int MVM_F_status = doc["MVM_F"];
int MVM_L_status = doc["MVM_L"];
int MVM_R_status = doc["MVM_R"];
int MVM_B_status = doc["MVM_B"];
digitalWrite(U_Led,MVM_F_status);
digitalWrite(L_Led,MVM_L_status);
digitalWrite(R_Led,MVM_R_status);
digitalWrite(D_Led,MVM_B_status);
}
}
}
void setup()
{
Serial.begin(115200);
pinMode(U_Led,OUTPUT);
pinMode(L_Led,OUTPUT);
pinMode(R_Led,OUTPUT);
pinMode(D_Led,OUTPUT);
pinMode(butPin, INPUT_PULLUP);
WiFi.softAP("RoverAP", "SplendidCheeks");
Serial.println();
Serial.println("RoverAP running");
Serial.print("Rover IP address: ");
Serial.println(WiFi.softAPIP());
if (!MDNS.begin("rover")) {
Serial.println("Error setting up MDNS responder!");
while (1) {
delay(2000);
}
}
Serial.println("mDNS responder started! Rover Command Center can now be accessed at 'rover.local' ");
server.on("/", [](AsyncWebServerRequest * request)
{
request->send_P(200, "text/html", index_html);
});
server.onNotFound(notFound);
server.begin(); // it will start webserver
websockets.begin();
websockets.onEvent(webSocketEvent);
timer.attach(0.5,send_sensor_data);
}
void loop()
{
websockets.loop();
potVal = analogRead(potPin);
}
void send_sensor_data()
{
butState = digitalRead(butPin);
if (butState == LOW) {
//increment ODO:
d += 10;
}
// JSON_Data = {"BTRY_VOLT":v,"ODO_DIST":d}
String JSON_Data = "{\"BTRY_VOLT\":";
JSON_Data += potVal;
JSON_Data += ",\"ODO_DIST\":";
JSON_Data += d;
JSON_Data += "}";
//Serial.println(JSON_Data);
websockets.broadcastTXT(JSON_Data);
}