Skip to content
This repository was archived by the owner on Jul 25, 2022. It is now read-only.

Commit a247b38

Browse files
committed
Added plotting of route to HTML page.
The graph is generated by a script, which is deployed on a PHP capable server.
2 parents 5e3aa10 + 357ec31 commit a247b38

File tree

6 files changed

+197
-44
lines changed

6 files changed

+197
-44
lines changed

remote-website/robot-control.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
$image = imagecreatetruecolor(1000, 1000);
3+
$default_color = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
4+
5+
$positionString = $_GET["positions"];
6+
7+
$positions = array();
8+
if($positionString === FALSE) {
9+
echo "Could not read the data from ".$data_server.".";
10+
} else {
11+
$positions = explode(";", $positionString);
12+
}
13+
14+
$diameter = 10;
15+
$offset_x = 500;
16+
$offset_y = 500;
17+
$start_x = $offset_x;
18+
$start_y = $offset_y;
19+
20+
for ($i = 0; $i+1 < count($positions); $i += 2) {
21+
$x = ((int) $positions[$i])/10+$offset_x;
22+
$y = ((int) $positions[$i + 1])/10+$offset_y;
23+
24+
imagefilledellipse($image, $x, $y, $diameter, $diameter, $default_color);
25+
imagestring($image, 3, $x + $diameter / 2, $y + $diameter / 2, sprintf("%u", $i/2), $default_color);
26+
imageline($image, $start_x, $start_y, $x, $y, $default_color);
27+
28+
$start_x = $x;
29+
$start_y = $y;
30+
}
31+
32+
// Flush the image
33+
header('Content-type: image/png');
34+
imagepng($image);
35+
imagedestroy($image);
36+
37+
?>

robot-control-src/Drives.cpp

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
namespace drives
99
{
1010

11+
static constexpr float stepsPerDeg = 100/(4.8*360);
12+
static constexpr float stepsPerRad = stepsPerDeg / ((2*pi)/360);
13+
14+
static enum class Action
15+
{
16+
FORWARD, BACKWARD, TURN_LEFT, TURN_RIGHT
17+
} lastAction;
18+
19+
Position lastKnownPosition = {0,0};
20+
static float orientation = 0; // in radians
21+
1122
template<typename MOTORCONTROL, MOTORCONTROL &motorControlpin, typename DIRECTIONPIN, DIRECTIONPIN &directionPin, typename ODOPIN, ODOPIN &odoPin>
1223
Counter volatile Drive<MOTORCONTROL, motorControlpin, DIRECTIONPIN, directionPin, ODOPIN, odoPin>::counter = 0;
1324
template<typename MOTORCONTROL, MOTORCONTROL &motorControlpin, typename DIRECTIONPIN, DIRECTIONPIN &directionPin, typename ODOPIN, ODOPIN &odoPin>
@@ -49,19 +60,20 @@ static Amplitude calcRightSpeed(const Amplitude leftSpeed)
4960

5061
void rotateCounter(const Counter steps, const Amplitude amplitude, bool const clockwise)
5162
{
52-
LeftDrive::drive(steps, amplitude, !clockwise);
53-
RightDrive::drive(steps, calcRightSpeed(amplitude), clockwise);
63+
lastAction = clockwise ? Action::TURN_RIGHT : Action::TURN_LEFT;
64+
LeftDrive::drive(steps, amplitude, clockwise);
65+
RightDrive::drive(steps, calcRightSpeed(amplitude), !clockwise);
5466
}
5567

5668
void driveCounter(const Counter steps, const Amplitude amplitude, const bool backwards)
5769
{
70+
lastAction = backwards ? Action::BACKWARD : Action::FORWARD;
5871
LeftDrive::drive(steps, amplitude, backwards);
5972
RightDrive::drive(steps, calcRightSpeed(amplitude), backwards);
6073
}
6174

6275
void rotate(const float deg, const Amplitude amplitude, bool const clockwise)
6376
{
64-
constexpr float stepsPerDeg = 100/(4.8*360);
6577
const Counter steps = std::round(deg * stepsPerDeg);
6678
rotateCounter(steps, amplitude, clockwise);
6779
}
@@ -73,7 +85,37 @@ void drive(const float distance, const Amplitude amplitude, const bool backwards
7385
driveCounter(steps, amplitude, backwards);
7486
}
7587

76-
IRAM_ATTR void stopDrives() {
88+
Position flushCurrentPosition()
89+
{
90+
switch (lastAction) {
91+
case Action::FORWARD:
92+
case Action::BACKWARD:
93+
{
94+
const std::int8_t reversed = (lastAction == Action::BACKWARD) ? -1 : 1;
95+
lastKnownPosition.x += LeftDrive::counter * board::odoIntervalLength * std::cos(orientation) * reversed;
96+
lastKnownPosition.y += LeftDrive::counter * board::odoIntervalLength * std::sin(orientation) * reversed;
97+
break;
98+
}
99+
case Action::TURN_LEFT:
100+
{
101+
orientation -= LeftDrive::counter / stepsPerRad;
102+
break;
103+
}
104+
case Action::TURN_RIGHT:
105+
{
106+
orientation += LeftDrive::counter / stepsPerRad;
107+
break;
108+
}
109+
default:
110+
break;
111+
}
112+
LeftDrive::counter = 0;
113+
RightDrive::counter = 0;
114+
return lastKnownPosition;
115+
}
116+
117+
IRAM_ATTR void stopDrives()
118+
{
77119
LeftDrive::stop();
78120
RightDrive::stop();
79121
}

robot-control-src/Drives.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@
66
#include <cstdint>
77
#include <Arduino.h>
88

9+
using Distance = std::int16_t; //!< in [mm]
10+
struct Position
11+
{
12+
Distance x, y;
13+
bool operator==(const Position& rhs) const
14+
{
15+
return x == rhs.x && y == rhs.y;
16+
}
17+
bool operator!=(const Position& rhs) const
18+
{
19+
return !(*this == rhs);
20+
}
21+
};
22+
923
namespace drives
1024
{
1125

@@ -57,7 +71,7 @@ class Drive
5771
counter = 0;
5872
target = distance;
5973
isIdle = false;
60-
digitalWrite(directionPin, backwards ? LOW : HIGH);
74+
digitalWrite(directionPin, !backwards ? LOW : HIGH);
6175
analogWrite(motorControlpin, std::min(maxAmplitude, amplitude));
6276
}
6377
};
@@ -73,6 +87,8 @@ void rotate(const float deg, const Amplitude amplitude, bool const clockwise);
7387
void driveCounter(const Counter distance, const Amplitude amplitude, const bool backwards);
7488
void drive(const float distance, const Amplitude amplitude, const bool backwards);
7589

90+
Position flushCurrentPosition();
91+
7692
void stopDrives();
7793

7894
};

robot-control-src/control-mask.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
<head>
44
<title>Robot Control</title>
55
<meta charset="utf-8" />
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.6">
7+
<meta http-equiv="refresh" content="5">
78
</head>
89
<body>
910
<main>
@@ -16,6 +17,7 @@
1617
</tbody>
1718
</table>
1819
</form>
20+
<img style="max-width:90vw; max-height:100vh;" src="https://david.hebbeker.info/robot-control.php?positions=%s" />
1921
</main>
2022
</body>
21-
</html>
23+
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef ROBOT_CONTROL_SRC_LIBRARY_EXTENSION_HPP_
2+
#define ROBOT_CONTROL_SRC_LIBRARY_EXTENSION_HPP_
3+
4+
#include <cstddef>
5+
6+
template <class T, std::size_t N>
7+
constexpr std::size_t size(const T (&)[N]) noexcept
8+
{
9+
return N;
10+
}
11+
12+
#endif /* ROBOT_CONTROL_SRC_LIBRARY_EXTENSION_HPP_ */

robot-control-src/robot-control-src.ino

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include "board.hpp"
22
#include "Drives.hpp"
33
#include "wifi_ap.hpp"
4+
#include "library_extension.hpp"
5+
#include <assert.h>
46
#include <ESP8266WebServer.h>
7+
#include <atomic>
58

69
static ESP8266WebServer server(80);
710

@@ -14,28 +17,58 @@ static struct
1417
bool clockwise = true;
1518
} newTarget;
1619

17-
constexpr auto htmlSource =
18-
"<!DOCTYPE html>\n"
19-
"<html lang=\"en\">\n"
20-
" <head>\n"
21-
" <title>Robot Control</title>\n"
22-
" <meta charset=\"utf-8\" />\n"
23-
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.6\">\n"
24-
" </head>\n"
25-
" <body>\n"
26-
" <main>\n"
27-
" <form method=\"post\" action=\"/\">\n"
28-
" <table>\n"
29-
" <tbody align=center valign=middle>\n"
30-
" <tr><td></td><td><button type=\"submit\" name=\"forwards\" value=\"10\" title=\"+37,70mm\">&#8593;</button></td><td></td></tr>\n"
31-
" <tr><td><button type=\"submit\" name=\"left\" value=\"5\" title=\"-86,4&deg;\">&#8634;</button></td><td>&#x1F916;</td><td><button type=\"submit\" name=\"right\" value=\"5\" title=\"+86,4&deg;\">&#8635;</button></td></tr>\n"
32-
" <tr><td></td><td><button type=\"submit\" name=\"backwards\" value=\"10\" title=\"-37,70mm\">&#8595;</button></td><td></td></tr>\n"
33-
" </tbody>\n"
34-
" </table>\n"
35-
" </form>\n"
36-
" </main>\n"
37-
" </body>\n"
38-
"</html>";
20+
static constexpr std::size_t numberOfPositions = 50;
21+
static Position positions[numberOfPositions] = { {0,0} };
22+
static std::size_t positionIndex = 0;
23+
24+
static constexpr char htmlSourceTemplate[] =
25+
"<!DOCTYPE html>\n"
26+
"<html lang=\"en\">\n"
27+
" <head>\n"
28+
" <title>Robot Control</title>\n"
29+
" <meta charset=\"utf-8\" />\n"
30+
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.6\">\n"
31+
" <meta http-equiv=\"refresh\" content=\"5\">\n"
32+
" </head>\n"
33+
" <body>\n"
34+
" <main>\n"
35+
" <form method=\"post\" action=\"/\">\n"
36+
" <table>\n"
37+
" <tbody align=center valign=middle>\n"
38+
" <tr><td></td><td><button type=\"submit\" name=\"forwards\" value=\"10\" title=\"+37,70mm\">&#8593;</button></td><td></td></tr>\n"
39+
" <tr><td><button type=\"submit\" name=\"left\" value=\"5\" title=\"-86,4&deg;\">&#8634;</button></td><td>&#x1F916;</td><td><button type=\"submit\" name=\"right\" value=\"5\" title=\"+86,4&deg;\">&#8635;</button></td></tr>\n"
40+
" <tr><td></td><td><button type=\"submit\" name=\"backwards\" value=\"10\" title=\"-37,70mm\">&#8595;</button></td><td></td></tr>\n"
41+
" </tbody>\n"
42+
" </table>\n"
43+
" </form>\n"
44+
" <img style=\"max-width:90vw; max-height:100vh;\" src=\"https://david.hebbeker.info/robot-control.php?positions=%s\" />\n"
45+
" </main>\n"
46+
" </body>\n"
47+
"</html>";
48+
49+
50+
constexpr std::size_t maxCharPerPosition = (5+1)*2; //!< when serializing the position, the number of characters maximum used per position
51+
constexpr std::size_t positionsStringMaxLength = maxCharPerPosition*numberOfPositions+1;
52+
53+
static char htmlSourceBackBufferA[size(htmlSourceTemplate) + positionsStringMaxLength] = {0};
54+
static char htmlSourceBackBufferB[size(htmlSourceTemplate) + positionsStringMaxLength] = {0};
55+
static std::atomic<const char *> htmlSourceFrontBuffer(htmlSourceTemplate);
56+
57+
void updateHtmlSource()
58+
{
59+
char * const backBuffer = (htmlSourceFrontBuffer.load() == htmlSourceBackBufferA) ? htmlSourceBackBufferB : htmlSourceBackBufferA;
60+
char positionStringBuffer[positionsStringMaxLength] = { 0 };
61+
std::size_t charPos = 0;
62+
for(std::size_t i=0; i<=positionIndex; i++)
63+
{
64+
const int writtenCharacters = snprintf(&(positionStringBuffer[charPos]), maxCharPerPosition+1, "%i;%i;", positions[i].x, positions[i].y);
65+
assert(writtenCharacters>0);
66+
charPos += writtenCharacters;
67+
}
68+
const int writtenCharacters = snprintf(backBuffer, size(htmlSourceBackBufferA), htmlSourceTemplate, positionStringBuffer);
69+
assert(writtenCharacters>0);
70+
htmlSourceFrontBuffer = backBuffer;
71+
}
3972

4073
static void handleRoot()
4174
{
@@ -68,7 +101,7 @@ static void handleRoot()
68101
newTarget.isTargetNew = true;
69102
Serial.printf("Got right by %u!\n", newTarget.newRotate);
70103
}
71-
server.send(200, "text/html", htmlSource);
104+
server.send(200, "text/html", htmlSourceFrontBuffer.load());
72105
digitalWrite(board::debugLed, HIGH);
73106
}
74107

@@ -109,22 +142,33 @@ void setup()
109142
server.begin();
110143
Serial.printf("webserver has IP %s\n", WiFi.localIP().toString().c_str());
111144
server.on("/", handleRoot);
145+
updateHtmlSource();
112146
}
113147

114148
void loop()
115149
{
116-
server.handleClient();
117-
//Serial.printf("left: \t%3u, right: \t%3u\n", drives::LeftDrive::counter, drives::RightDrive::counter);
118-
if(drives::LeftDrive::isIdle && drives::RightDrive::isIdle && newTarget.isTargetNew)
119-
{
120-
if(newTarget.newDrive!=0)
121-
{
122-
drives::driveCounter(newTarget.newDrive, drives::cruiseSpeed, !newTarget.forward);
123-
}
124-
else if(newTarget.newRotate!=0)
125-
{
126-
drives::rotateCounter(newTarget.newRotate, drives::cruiseSpeed, newTarget.clockwise);
127-
}
128-
newTarget = { };
129-
}
150+
server.handleClient();
151+
//Serial.printf("left: \t%3u, right: \t%3u\n", drives::LeftDrive::counter, drives::RightDrive::counter);
152+
if(drives::LeftDrive::isIdle && drives::RightDrive::isIdle)
153+
{
154+
const Position newPositionCandidate = drives::flushCurrentPosition();
155+
if(positions[positionIndex] != newPositionCandidate)
156+
{
157+
positions[++positionIndex] = newPositionCandidate;
158+
positionIndex %= numberOfPositions;
159+
updateHtmlSource();
160+
}
161+
if(newTarget.isTargetNew)
162+
{
163+
if(newTarget.newDrive!=0)
164+
{
165+
drives::driveCounter(newTarget.newDrive, drives::cruiseSpeed, !newTarget.forward);
166+
}
167+
else if(newTarget.newRotate!=0)
168+
{
169+
drives::rotateCounter(newTarget.newRotate, drives::cruiseSpeed, newTarget.clockwise);
170+
}
171+
newTarget = { };
172+
}
173+
}
130174
}

0 commit comments

Comments
 (0)