ברוכים הבאים לשיעור השמיני!
השיעור מורכב מסדרה של סרטונים, צפו בהם לפי הסדר.
תהנו 🙂
/*
* Ultrasonic Sensor HC-SR04 and Arduino Tutorial
*
* by Dejan Nedelkovski,
* www.HowToMechatronics.com
*
*/
// defines pins numbers
const int trigPin = 9;
const int echoPin = 10;
// defines variables
long duration;
int distance;
void setup() {
pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
pinMode(echoPin, INPUT); // Sets the echoPin as an Input
Serial.begin(9600); // Starts the serial communication
}
void loop() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance= duration*0.034/2;
// Prints the distance on the Serial Monitor
Serial.print("Distance: ");
Serial.println(distance);
delay(100);
}
void setup() {
// put your setup code here, to run once:
pinMode(2, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(2, HIGH);
delay(1000);
digitalWrite(2, LOW);
delay(1000);
}
//Ver 1.6 - 10/9/2020
//Calibrate the servo, change servo_offset value until the wheels will be straight
//Higher number will turn the wheels left, lower number will turn them right
int servo_offset = 0;
int servo_center = 90;
//=======================================
//Install the following libraries:
//FastLED
#include "FastLED.h"
//ESP32Servo
#include <ESP32Servo.h>
//IRremoteESP8266
#include <IRremoteESP8266.h>
//Melody_player
#include <melody_player.h>
#include <melody_factory.h>
//=======================================
#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <SPI.h>
#include "RF24.h"
#include <Arduino.h>
#include <IRrecv.h>
#include <IRsend.h>
#include <IRutils.h>
// Library to read and write from flash memory
#include <EEPROM.h>
//Ir recive pin
const uint16_t kRecvPin = 34;
//const uint16_t kRecvPin = 32; //v 1.6
//const uint16_t kRecvPin = 15; // IR REC2
IRrecv irrecv(kRecvPin);
decode_results results;
//Ir send pin
IRsend irsend(13);
extern RF24 radio;
const byte robot[6] = "00001";
const byte remote_control[6] = "00002";
const byte race_controller[6] = "00003";
Servo myservo;
#define SERVO_PIN 32 // GPIO pin used to connect the servo control (digital out)
//#define SERVO_PIN 27 // v1.6
#define BUZZER_PIN 14
const int CE = 26, CSN = 25;
RF24 radio(CE, CSN); // CE, CSN
const char* PARAM_MESSAGE = "message";
void printDetail(uint8_t type, int value);
#define MOTOR_PIN_IN1 5
#define MOTOR_PIN_IN2 2
#define MOTOR_PIN_PWMB 4
#define PWMB_CHANNEL 5
#define PWM_FREQ 20000
#define SPEED_RESOLUTION 8
MelodyPlayer player(BUZZER_PIN,6);
//Global variables
int pos; // variable to store the servo position
int speed;
int icon;
int sound;
int degrees;
int turn_speed;
int steering_value;
unsigned long current_time;
unsigned long last_lap_time;
unsigned long last_sensor_lap_reading;
float best_lap_time=99999;
float lap_time =99998;
int random_number=1;
int last_random_number=1;
int loop_counter=10;
char formatted_lap_time[6]="0.00";
char formatted_best_lap_time[6];
//Ultrasonic Setup
int trigPin = 12; // Trigger
int echoPin = 35; // Echo
long compass_angle;
float heading;
float final_heading;
CRGB leds[16];
int total_laps_counter;
WebServer server(80);
long microsecondsToInches(long microseconds) {
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
return microseconds / 29 / 2;
}
void drive_rc(bool forward, int speed) {
speed = speed/3;
//If somehow we got negative speed convert it to positive
if (speed<0)
speed = speed*-1;
Serial.printf("motor front engine speed: %d\n", speed);
if (forward) {
digitalWrite(MOTOR_PIN_IN1, LOW);
digitalWrite(MOTOR_PIN_IN2, HIGH);
} else {
digitalWrite(MOTOR_PIN_IN1, HIGH);
digitalWrite(MOTOR_PIN_IN2, LOW);
}
ledcWrite(PWMB_CHANNEL, speed );
}
void steering_rc(int position) {
//The input this function gets is 0-180, but our turning range is using just a part of the 180 degrees range of the servo
//So we need to map it to just 60 degrees range (30 degrees to each side)
//SERVO_CENTER is a variable we are getting from the remote
position = map(position, 0, 180, servo_center - 20 + servo_offset, servo_center + 20 + servo_offset);
Serial.printf("steering: %d\n", position);
myservo.write(position);
//server.sendHeader("Access-Control-Allow-Origin", "*");
//server.send(200, "text/html", "a");
}
void stop() {
digitalWrite(MOTOR_PIN_IN1, LOW);
digitalWrite(MOTOR_PIN_IN2, LOW);
//motor.drive(0);
Serial.printf("Stop\n");
//server.sendHeader("Access-Control-Allow-Origin", "*");
//server.send(200, "text/html", "a");
}
void read_from_remote()
{
int speed;
int direction;
int steering;
char text[17] = "";
//xSemaphoreTake(radio_baton, portMAX_DELAY);
if (radio.available())
{
radio.read(&text, sizeof(text));
if(text[0] == 'M')
{
//We got a movement request
int speed_one = (int)text[1];
int speed_two = (int)text[2];
//Because of size issues we divided speed into two values, now are are combining them again
int speed = (speed_one * 254) + speed_two;
//The value we have now in speed is the value we sent (0-512), but the motor controller should get a value of (0-255)
//We need to make the mapping
speed = map(speed, 0, 512, 0, 255);
//Serial.printf("speed:%d | ", speed);
int direction = (int)text[3];
//Serial.printf("direction:%d | ", direction);
int steering = (int)text[4];
Serial.printf("steering:%d | ", steering);
if (direction==1)
drive_rc(true, speed); //forward
else
drive_rc(false, speed); //back
int received_servo_center = (int)text[5];
//Verify we got a correct value
if ((received_servo_center>29)&&(received_servo_center<121))
servo_center = received_servo_center;
steering_rc(steering);
}
}
}
//void handle_NotFound(){
//server.send(404, "text/plain", "Not found");
//}
String SendHTML(){
String ptr = "<html><head><meta charset='utf-8'> \n\
<meta name='viewport' content='width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'> \n\
<style> \
body {overflow : hidden;background-color: #BBB;} \n\
#container \
{width: 100%;height: 100%;overflow: hidden;padding:0;margin:0;-webkit-user-select:none;-moz-user-select:none;} \n\
html, body { \n\
position: absolute; \n\
top: 0; \n\
left: 0; \n\
right: 0; \n\
bottom: 0; \n\
padding: 0; \n\
margin: 0; \n\
-webkit-touch-callout: none; \n\
-webkit-text-size-adjust: none; \n\
-webkit-user-select: none; \n\
} \n\
#left { \n\
position: absolute; \n\
left: 0; \n\
top: 0; \n\
height: 100%; \n\
width: 50%; \n\
background: rgba(0, 255, 0, 0.1); \n\
text-align: center; \n\
} \n\
#right { \n\
position: absolute; \n\
right: 0; \n\
top: 0; \n\
height: 100%; \n\
width: 50%; \n\
background: rgba(0, 0, 255, 0.1); \n\
text-align: center; \n\
} \n\
#lap-titles {font-family: sans-serif; font-size: 30px; padding-left: 5px;} \n\
#svg-title-icon { position: relative; top: 7px; } \n\
#total-laps-container { padding-left: 5px;position: absolute;bottom: 10px;left: 10px;font-family: sans-serif;font-size: 20px; } \n\
</style> \n\
<title> PlayRobotics - Racing game </title></head><body> \n\
<div id='container'> \n\
</div> \n\
<div id='left'> \n\
<! –– Icons made by http://www.freepik.com/ from https://www.flaticon.com/ ––> \n\
<svg id='svg-title-icon' height='40' viewBox='0 0 512.392 512.392' width='40' xmlns='http://www.w3.org/2000/svg'> \n\
<path d='M511.005,279.646c-4.597-46.238-25.254-89.829-58.168-122.744 c-28.128-28.127-62.556-46.202-98.782-54.239V77.255c14.796-3.681,25.794-17.074,25.794-32.993c0-18.748-15.252-34-34-34h-72 c-18.748,0-34,15.252-34,34c0,15.918,10.998,29.311,25.793,32.993v25.479c-36.115,8.071-70.429,26.121-98.477,54.169 c-6.138,6.138-11.798,12.577-16.979,19.269c-0.251-0.019-0.502-0.038-0.758-0.038H78.167c-5.522,0-10,4.477-10,10s4.478,10,10,10 h58.412c-7.332,12.275-13.244,25.166-17.744,38.436H10c-5.522,0-10,4.477-10,10s4.478,10,10,10h103.184 c-2.882,12.651-4.536,25.526-4.963,38.437H64c-5.522,0-10,4.477-10,10s4.478,10,10,10h44.54 c0.844,12.944,2.925,25.82,6.244,38.437H50c-5.522,0-10,4.477-10,10s4.478,10,10,10h71.166 c9.81,25.951,25.141,50.274,45.999,71.132c32.946,32.946,76.582,53.608,122.868,58.181c6.606,0.652,13.217,0.975,19.819,0.975 c39.022,0,77.548-11.293,110.238-32.581c4.628-3.014,5.937-9.209,2.923-13.837s-9.209-5.937-13.837-2.923 c-71.557,46.597-167.39,36.522-227.869-23.957c-70.962-70.962-70.962-186.425,0-257.388c70.961-70.961,186.424-70.961,257.387,0 c60.399,60.4,70.529,156.151,24.086,227.673c-3.008,4.632-1.691,10.826,2.94,13.833c4.634,3.008,10.826,1.691,13.833-2.941 C504.367,371.396,515.537,325.241,511.005,279.646z M259.849,44.263c0-7.72,6.28-14,14-14h72c7.72,0,14,6.28,14,14s-6.28,14-14,14 h-1.794h-68.413h-1.793C266.129,58.263,259.849,51.982,259.849,44.263z M285.642,99.296V78.263h48.413v20.997 C317.979,97.348,301.715,97.36,285.642,99.296z'/> \n\
<path d='M445.77,425.5c-2.64,0-5.21,1.07-7.069,2.93c-1.87,1.86-2.931,4.44-2.931,7.07 c0,2.63,1.061,5.21,2.931,7.07c1.859,1.87,4.43,2.93,7.069,2.93c2.63,0,5.2-1.06,7.07-2.93c1.86-1.86,2.93-4.44,2.93-7.07 c0-2.63-1.069-5.21-2.93-7.07C450.97,426.57,448.399,425.5,445.77,425.5z'/> \n\
<path d='M310.001,144.609c-85.538,0-155.129,69.59-155.129,155.129s69.591,155.129,155.129,155.129 s155.129-69.59,155.129-155.129S395.539,144.609,310.001,144.609z M310.001,434.867c-74.511,0-135.129-60.619-135.129-135.129 s60.618-135.129,135.129-135.129S445.13,225.228,445.13,299.738S384.512,434.867,310.001,434.867z'/> \n\
<path d='M373.257,222.34l-49.53,49.529c-4.142-2.048-8.801-3.205-13.726-3.205c-4.926,0-9.584,1.157-13.726,3.205 l-22.167-22.167c-3.906-3.905-10.236-3.905-14.143,0c-3.905,3.905-3.905,10.237,0,14.142l22.167,22.167 c-2.049,4.142-3.205,8.801-3.205,13.726c0,17.134,13.939,31.074,31.074,31.074s31.074-13.94,31.074-31.074 c0-4.925-1.157-9.584-3.205-13.726l48.076-48.076v0l1.453-1.453c3.905-3.905,3.905-10.237,0-14.142 S377.164,218.435,373.257,222.34z M310.001,310.812c-6.106,0-11.074-4.968-11.074-11.074s4.968-11.074,11.074-11.074 s11.074,4.968,11.074,11.074S316.107,310.812,310.001,310.812z'/> \n\
<path d='M416.92,289.86h-9.265c-5.522,0-10,4.477-10,10s4.478,10,10,10h9.265c5.522,0,10-4.477,10-10 S422.442,289.86,416.92,289.86z'/> \n\
<path d='M212.346,289.616h-9.264c-5.522,0-10,4.477-10,10s4.478,10,10,10h9.264c5.522,0,10-4.477,10-10 S217.868,289.616,212.346,289.616z'/> \n\
<path d='M310.123,212.083c5.522,0,10-4.477,10-10v-9.264c0-5.523-4.478-10-10-10s-10,4.477-10,10v9.264 C300.123,207.606,304.601,212.083,310.123,212.083z'/> \n\
<path d='M309.879,387.393c-5.522,0-10,4.477-10,10v9.264c0,5.523,4.478,10,10,10s10-4.477,10-10v-9.264 C319.879,391.87,315.401,387.393,309.879,387.393z'/> \n\
<path d='M10,351.44c-2.63,0-5.21,1.07-7.07,2.93c-1.86,1.86-2.93,4.44-2.93,7.07c0,2.64,1.069,5.21,2.93,7.07 s4.44,2.93,7.07,2.93s5.21-1.07,7.069-2.93c1.86-1.86,2.931-4.44,2.931-7.07s-1.07-5.21-2.931-7.07 C15.21,352.51,12.63,351.44,10,351.44z'/> \n\
</svg> \n\
<span id='lap-titles'>Lap: <span id='lap_time'>0.00</span></span>\n\
<span id='total-laps-container'>\n\
<! –– Icons made by http://www.freepik.com/ from https://www.flaticon.com/ ––> \n\
<svg id='svg-title-icon' height='40' viewBox='0 0 512.392 512.392' width='40' xmlns='http://www.w3.org/2000/svg'>\n\
<g><path d='m399 210h18c52.383 0 95-42.617 95-95v-40c0-41.355-33.645-75-75-75h-332c-57.897 0-105 47.103-105 105v211.236c0 57.897 47.103 105 105 105h69v15c0 41.777 33.987 75.764 75.764 75.764h167.236c52.383 0 95-42.617 95-95v-67c0-52.383-42.617-95-95-95h-18c-8.271 0-15-6.729-15-15v-15c0-8.271 6.729-15 15-15zm-339 80.236h-30v-30h30zm339-5.236h18c35.841 0 65 29.159 65 65v67c0 35.841-29.159 65-65 65h-167.236c-25.235 0-45.764-20.529-45.764-45.764v-20c0-13.785-11.215-25-25-25h-74c-40.013 0-72.805-31.497-74.891-71h30.079c2.032 22.945 21.348 41 44.813 41h83.236c25.234 0 45.763 20.53 45.763 45.764v20c0 13.785 11.215 25 25 25h148c24.813 0 45-20.187 45-45v-47c0-24.813-20.187-45-45-45h-11.292c-39.54 0-71.708-32.168-71.708-71.708v-21.584c0-39.54 32.168-71.708 71.708-71.708h21.292c19.299 0 35-15.701 35-35v-20c0-19.299-15.701-35-35-35h-312c-24.813 0-45 20.187-45 45v125.236h-30v-125.236c0-41.355 33.645-75 75-75h332c24.813 0 45 20.187 45 45v40c0 35.841-29.159 65-65 65h-18c-24.813 0-45 20.187-45 45v15c0 24.813 20.187 45 45 45zm-3.292 60h11.292c8.271 0 15 6.729 15 15v47c0 8.271-6.729 15-15 15h-143v-15c0-41.776-33.987-75.764-75.764-75.764h-83.236c-8.271 0-15-6.729-15-15v-211.236c0-8.271 6.729-15 15-15h312c2.757 0 5 2.243 5 5v20c0 2.757-2.243 5-5 5h-21.292c-56.082 0-101.708 45.626-101.708 101.708v21.584c0 56.082 45.626 101.708 101.708 101.708z'/></g> \n\
</svg>\n\
<span id='total_laps'></span> Laps\n\
</span>\n\
</div> \n\
<div id='right'> \n\
<! –– Icons made by http://www.freepik.com/ from https://www.flaticon.com/ ––> \n\
<svg id='svg-title-icon' height='40' viewBox='0 0 512.392 512.392' width='40' xmlns='http://www.w3.org/2000/svg'> \n\
<g><g><g> \n\
<path d='m484.534 89.03c-1.158-12.986-11.863-22.779-24.901-22.779h-64.68v-9.532h1.259c9.236 0 16.75-7.514 16.75-16.749v-23.221c0-9.235-7.514-16.749-16.75-16.749h-190.37c-4.142 0-7.5 3.357-7.5 7.5s3.358 7.5 7.5 7.5h190.37c.965 0 1.75.784 1.75 1.749v23.221c0 .965-.785 1.749-1.75 1.749h-280.406c-.965 0-1.75-.784-1.75-1.749v-23.221c0-.965.785-1.749 1.75-1.749h54.871c4.142 0 7.5-3.357 7.5-7.5s-3.358-7.5-7.5-7.5h-54.871c-9.236 0-16.75 7.514-16.75 16.749v23.221c0 9.235 7.514 16.749 16.75 16.749h1.259v9.532h-64.68c-13.038 0-23.743 9.793-24.901 22.779-.152 1.698-3.522 42.121 13.031 86.891 15.145 40.962 49.891 93.264 129.501 113.79 12.816 10.126 27.45 18.05 43.327 23.185l3.775 6.231c7.135 11.779 10.906 25.704 10.906 40.269 0 8.897-1.418 17.589-4.214 25.832l-5.242 15.448h-17.441c-17.898 0-32.46 14.562-32.46 32.461v15.711h-27.296c-7.703 0-13.97 6.268-13.97 13.971v35.23c0 7.703 6.267 13.97 13.97 13.97h229.277c7.703 0 13.97-6.267 13.97-13.97v-35.23c0-7.703-6.267-13.971-13.97-13.971h-27.296v-15.711c0-17.899-14.562-32.461-32.46-32.461h-17.441l-5.242-15.448c-2.797-8.242-4.215-16.934-4.215-25.832 0-14.564 3.771-28.489 10.906-40.269l3.775-6.231c15.877-5.135 30.51-13.058 43.327-23.185 79.61-20.526 114.356-72.828 129.501-113.79 16.553-44.77 13.183-85.192 13.031-86.891zm-364.692 119.324c-13.984-13.092-24.697-29.333-32.024-48.69-5.091-13.45-8.423-28.053-9.906-43.413h39.153v64.412c0 9.48.959 18.74 2.777 27.691zm-65.257-37.634c-15.395-41.638-12.299-78.796-12.16-80.357.463-5.194 4.745-9.111 9.96-9.111h64.68v20h-39.774c-4.052 0-7.94 1.717-10.668 4.711-2.731 2.998-4.081 7.034-3.704 11.074 1.579 16.923 5.236 33.052 10.87 47.938 11.127 29.398 29.138 52.353 53.792 68.695 4.851 11.708 11.258 22.614 18.964 32.459-54.338-22.523-79.929-62.871-91.96-95.409zm315.033 293.128v33.171h-227.218v-33.171zm-58.726-48.172c9.627 0 17.46 7.833 17.46 17.461v15.711h-144.685v-15.711c0-9.628 7.833-17.461 17.46-17.461zm-36.887-25.628 3.606 10.628h-43.205l3.607-10.629c3.324-9.799 5.01-20.111 5.01-30.651 0-14.753-3.297-28.996-9.567-41.629 7.343 1.204 14.874 1.84 22.553 1.84 7.678 0 15.21-.636 22.552-1.84-6.27 12.633-9.567 26.876-9.567 41.629.001 10.541 1.686 20.853 5.011 30.652zm-17.996-85.442c-68.343 0-123.944-55.601-123.944-123.943v-123.944h247.888v123.944c0 68.343-55.601 123.943-123.944 123.943zm201.425-133.886c-12.031 32.539-37.623 72.891-91.964 95.414 7.72-9.863 14.137-20.79 18.992-32.522 21.169-14.057 37.472-32.989 48.698-56.695 1.773-3.743.175-8.216-3.568-9.988-3.742-1.772-8.214-.176-9.988 3.568-6.95 14.677-16.116 27.322-27.434 37.903 1.824-8.965 2.785-18.24 2.785-27.736v-64.412h39.153c-.746 7.717-1.986 15.37-3.687 22.758-.929 4.037 1.59 8.063 5.627 8.991 4.033.934 8.062-1.589 8.991-5.626 1.894-8.229 3.26-16.754 4.062-25.338.377-4.04-.973-8.076-3.704-11.074-2.728-2.994-6.616-4.711-10.668-4.711h-39.774v-20h64.68c5.215 0 9.497 3.917 9.96 9.112.137 1.56 3.233 38.718-12.161 80.356z'/> \n\
<path d='m305.328 145.663-21.506-3.125c-.937-.137-1.746-.725-2.165-1.573l-9.618-19.488c-3.033-6.146-9.175-9.964-16.029-9.964s-12.996 3.818-16.029 9.964l-9.618 19.488c-.419.849-1.228 1.437-2.165 1.573l-21.506 3.125c-6.783.985-12.312 5.646-14.43 12.165s-.385 13.54 4.523 18.325l15.562 15.168c.678.661.987 1.612.827 2.546l-3.674 21.42c-1.159 6.755 1.566 13.453 7.111 17.482 5.544 4.027 12.758 4.55 18.826 1.361l19.235-10.113c.838-.441 1.838-.441 2.676 0l19.236 10.113c2.639 1.387 5.494 2.072 8.334 2.072 3.691 0 7.358-1.157 10.491-3.434 5.545-4.029 8.27-10.728 7.111-17.482l-3.674-21.42c-.16-.934.149-1.885.827-2.545l15.563-15.17c4.908-4.784 6.641-11.806 4.523-18.324s-7.649-11.179-14.431-12.164zm-.563 19.748-15.563 15.17c-4.213 4.107-6.135 10.021-5.141 15.821l3.674 21.42c.263 1.533-.604 2.42-1.144 2.813-.539.393-1.651.945-3.028.219l-19.236-10.113c-2.604-1.369-5.461-2.054-8.318-2.054s-5.714.685-8.318 2.054l-19.235 10.113c-1.376.725-2.488.173-3.029-.219-.54-.393-1.406-1.279-1.144-2.813l3.674-21.42c.995-5.8-.927-11.714-5.141-15.822l-15.562-15.168c-1.114-1.086-.935-2.313-.728-2.948.206-.635.782-1.733 2.321-1.957l21.506-3.125c5.824-.847 10.855-4.502 13.459-9.778l9.618-19.488c.688-1.395 1.911-1.603 2.578-1.603s1.89.208 2.578 1.603l9.618 19.488c2.604 5.276 7.635 8.932 13.459 9.778l21.507 3.125c1.539.224 2.115 1.322 2.321 1.957.208.635.387 1.862-.726 2.947z'/> \n\
</g></g></g> \n\
</svg> \n\
<span id='lap-titles'>Best: <span id='best_lap_time'>0.00</span></span> \n\
</div> \n\
<script>";
//Including nipple.js library: https://yoannmoi.net/nipplejs/
ptr += "!function(t){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=t();else if(\"function\"==typeof define&&define.amd)define([],t);\
else{var e;e=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:this,e.nipplejs=t()}}(function(){function t(){}function e(t,i){return this.identifier=i.identifier,this.position=i.position,this.frontPosition=i.frontPosition,this.collection=t,this.defaults={size:100,threshold:.1,color:\"white\",fadeTime:250,dataOnly:!1,restJoystick:!0,restOpacity:.5,mode:\"dynamic\",zone:document.body,lockX:!1,lockY:!1},this.config(i),\"dynamic\"===this.options.mode&&(this.options.restOpacity=0),this.id=e.id,e.id+=1,this.buildEl().stylize(),this.instance={el:this.ui.el,on:this.on.bind(this),off:this.off.bind(this),show:this.show.bind(this),hide:this.hide.bind(this),add:this.addToDom.bind(this),remove:this.removeFromDom.bind(this),destroy:this.destroy.bind(this),resetDirection:this.resetDirection.bind(this),computeDirection:this.computeDirection.bind(this),trigger:this.trigger.bind(this),position:this.position,frontPosition:this.frontPosition,ui:this.ui,identifier:this.identifier,id:this.id,options:this.options},this.instance}function i(t,e){var n=this;return n.nipples=[],n.idles=[],n.actives=[],n.ids=[],n.pressureIntervals={},n.manager=t,n.id=i.id,i.id+=1,n.defaults={zone:document.body,multitouch:!1,maxNumberOfNipples:10,mode:\"dynamic\",position:{top:0,left:0},catchDistance:200,size:100,threshold:.1,color:\"white\",fadeTime:250,dataOnly:!1,restJoystick:!0,restOpacity:.5,lockX:!1,lockY:!1},n.config(e),\"static\"!==n.options.mode&&\"semi\"!==n.options.mode||(n.options.multitouch=!1),n.options.multitouch||(n.options.maxNumberOfNipples=1),n.updateBox(),n.prepareNipples(),n.bindings(),n.begin(),n.nipples}function n(t){var e=this;e.ids={},e.index=0,e.collections=[],e.config(t),e.prepareCollections();var i;return c.bindEvt(window,\"resize\",function(t){clearTimeout(i),i=setTimeout(function(){var t,i=c.getScroll();e.collections.forEach(function(e){e.forEach(function(e){t=e.el.getBoundingClientRect(),e.position={x:i.x+t.left,y:i.y+t.top}})})},100)}),e.collections}var o,r=!!(\"ontouchstart\"in window),s=!!window.PointerEvent,d=!!window.MSPointerEvent,a={touch:{start:\"touchstart\",move:\"touchmove\",end:\"touchend, touchcancel\"},mouse:{start:\"mousedown\",move:\"mousemove\",end:\"mouseup\"},pointer:{start:\"pointerdown\",move:\"pointermove\",end:\"pointerup, pointercancel\"},MSPointer:{start:\"MSPointerDown\",move:\"MSPointerMove\",end:\"MSPointerUp\"}},p={};s?o=a.pointer:d?o=a.MSPointer:r?(o=a.touch,p=a.mouse):o=a.mouse;var c={};c.distance=function(t,e){var i=e.x-t.x,n=e.y-t.y;return Math.sqrt(i*i+n*n)},c.angle=function(t,e){var i=e.x-t.x,n=e.y-t.y;return c.degrees(Math.atan2(n,i))},c.findCoord=function(t,e,i){var n={x:0,y:0};return i=c.radians(i),n.x=t.x-e*Math.cos(i),n.y=t.y-e*Math.sin(i),n},c.radians=function(t){return t*(Math.PI/180)},c.degrees=function(t){return t*(180/Math.PI)},c.bindEvt=function(t,e,i){for(var n,o=e.split(/[ ,]+/g),r=0;r<o.length;r+=1)n=o[r],t.addEventListener?t.addEventListener(n,i,!1):t.attachEvent&&t.attachEvent(n,i)},c.unbindEvt=function(t,e,i){for(var n,o=e.split(/[ ,]+/g),r=0;r<o.length;r+=1)n=o[r],t.removeEventListener?t.removeEventListener(n,i):t.detachEvent&&t.detachEvent(n,i)},c.trigger=function(t,e,i){var n=new CustomEvent(e,i);t.dispatchEvent(n)},c.prepareEvent=function(t){return t.preventDefault(),t.type.match(/^touch/)?t.changedTouches:t},c.getScroll=function(){return{x:void 0!==window.pageXOffset?window.pageXOffset:(document.documentElement||document.body.parentNode||document.body).scrollLeft,y:void 0!==window.pageYOffset?window.pageYOffset:(document.documentElement||document.body.parentNode||document.body).scrollTop}},c.applyPosition=function(t,e){e.top||e.right||e.bottom||e.left?(t.style.top=e.top,t.style.right=e.right,t.style.bottom=e.bottom,t.style.left=e.left):(t.style.left=e.x+\"px\",t.style.top=e.y+\"px\")},c.getTransitionStyle=function(t,e,i){var n=c.configStylePropertyObject(t);for(var o in n)if(n.hasOwnProperty(o))if(\"string\"==typeof e)n[o]=e+\" \"+i;else{for(var r=\"\",s=0,d=e.length;s<d;s+=1)r+=e[s]+\" \"+i+\", \";n[o]=r.slice(0,-2)}return n},c.getVendorStyle=function(t,e){var i=c.configStylePropertyObject(t);for(var n in i)i.hasOwnProperty(n)&&(i[n]=e);return i},c.configStylePropertyObject=function(t){var e={};return e[t]=\"\",[\"webkit\",\"Moz\",\"o\"].forEach(function(i){e[i+t.charAt(0).toUpperCase()+t.slice(1)]=\"\"}),e},c.extend=function(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t},c.safeExtend=function(t,e){var i={};for(var n in t)t.hasOwnProperty(n)&&e.hasOwnProperty(n)?i[n]=e[n]:t.hasOwnProperty(n)&&(i[n]=t[n]);return i},c.map=function(t,e){if(t.length)for(var i=0,n=t.length;i<n;i+=1)e(t[i]);else e(t)},t.prototype.on=function(t,e){var i,n=this,o=t.split(/[ ,]+/g);n._handlers_=n._handlers_||{};for(var r=0;r<o.length;r+=1)i=o[r],n._handlers_[i]=n._handlers_[i]||[],n._handlers_[i].push(e);return n},t.prototype.off=function(t,e){var i=this;return i._handlers_=i._handlers_||{},void 0===t?i._handlers_={}:void 0===e?i._handlers_[t]=null:i._handlers_[t]&&i._handlers_[t].indexOf(e)>=0&&i._handlers_[t].splice(i._handlers_[t].indexOf(e),1),i},t.prototype.trigger=function(t,e){var i,n=this,o=t.split(/[ ,]+/g);n._handlers_=n._handlers_||{};for(var r=0;r<o.length;r+=1)i=o[r],n._handlers_[i]&&n._handlers_[i].length&&n._handlers_[i].forEach(function(t){t.call(n,{type:i,target:n},e)})},t.prototype.config=function(t){var e=this;e.options=e.defaults||{},t&&(e.options=c.safeExtend(e.options,t))},t.prototype.bindEvt=function(t,e){var i=this;return i._domHandlers_=i._domHandlers_||{},i._domHandlers_[e]=function(){\"function\"==typeof i[\"on\"+e]?i[\"on\"+e].apply(i,arguments):console.warn('[WARNING] : Missing \"on'+e+'\" handler.')},c.bindEvt(t,o[e],i._domHandlers_[e]),p[e]&&c.bindEvt(t,p[e],i._domHandlers_[e]),i},t.prototype.unbindEvt=function(t,e){var i=this;return i._domHandlers_=i._domHandlers_||{},c.unbindEvt(t,o[e],i._domHandlers_[e]),p[e]&&c.unbindEvt(t,p[e],i._domHandlers_[e]),delete i._domHandlers_[e],this},e.prototype=new t,e.constructor=e,e.id=0,e.prototype.buildEl=function(t){return this.ui={},this.options.dataOnly?this:(this.ui.el=document.createElement(\"div\"),this.ui.back=document.createElement(\"div\"),this.ui.front=document.createElement(\"div\"),this.ui.el.className=\"nipple collection_\"+this.collection.id,this.ui.back.className=\"back\",this.ui.front.className=\"front\",this.ui.el.setAttribute(\"id\",\"nipple_\"+this.collection.id+\"_\"+this.id),this.ui.el.appendChild(this.ui.back),this.ui.el.appendChild(this.ui.front),this)},e.prototype.stylize=function(){if(this.options.dataOnly)return this;var t=this.options.fadeTime+\"ms\",e=c.getVendorStyle(\"borderRadius\",\"50%\"),i=c.getTransitionStyle(\"transition\",\"opacity\",t),n={};return n.el={position:\"absolute\",opacity:this.options.restOpacity,display:\"block\",zIndex:999},n.back={position:\"absolute\",display:\"block\",width:this.options.size+\"px\",height:this.options.size+\"px\",marginLeft:-this.options.size/2+\"px\",marginTop:-this.options.size/2+\"px\",background:this.options.color,opacity:\".5\"},n.front={width:this.options.size/2+\"px\",height:this.options.size/2+\"px\",position:\"absolute\",display:\"block\",marginLeft:-this.options.size/4+\"px\",marginTop:-this.options.size/4+\"px\",background:this.options.color,opacity:\".5\"},c.extend(n.el,i),c.extend(n.back,e),c.extend(n.front,e),this.applyStyles(n),this},e.prototype.applyStyles=function(t){for(var e in this.ui)if(this.ui.hasOwnProperty(e))for(var i in t[e])this.ui[e].style[i]=t[e][i];return this},e.prototype.addToDom=function(){return this.options.dataOnly||document.body.contains(this.ui.el)?this:(this.options.zone.appendChild(this.ui.el),this)},e.prototype.removeFromDom=function(){return this.options.dataOnly||!document.body.contains(this.ui.el)?this:(this.options.zone.removeChild(this.ui.el),this)},e.prototype.destroy=function(){clearTimeout(this.removeTimeout),clearTimeout(this.showTimeout),clearTimeout(this.restTimeout),this.trigger(\"destroyed\",this.instance),this.removeFromDom(),this.off()},e.prototype.show=function(t){var e=this;return e.options.dataOnly?e:(clearTimeout(e.removeTimeout),clearTimeout(e.showTimeout),clearTimeout(e.restTimeout),e.addToDom(),e.restCallback(),setTimeout(function(){e.ui.el.style.opacity=1},0),e.showTimeout=setTimeout(function(){e.trigger(\"shown\",e.instance),\"function\"==typeof t&&t.call(this)},e.options.fadeTime),e)},e.prototype.hide=function(t){var e=this;return e.options.dataOnly?e:(e.ui.el.style.opacity=e.options.restOpacity,clearTimeout(e.removeTimeout),clearTimeout(e.showTimeout),clearTimeout(e.restTimeout),e.removeTimeout=setTimeout(function(){var i=\"dynamic\"===e.options.mode?\"none\":\"block\";e.ui.el.style.display=i,\"function\"==typeof t&&t.call(e),e.trigger(\"hidden\",e.instance)},e.options.fadeTime),e.options.restJoystick&&e.restPosition(),e)},e.prototype.restPosition=function(t){var e=this;e.frontPosition={x:0,y:0};var i=e.options.fadeTime+\"ms\",n={};n.front=c.getTransitionStyle(\"transition\",[\"top\",\"left\"],i);var o={front:{}};o.front={left:e.frontPosition.x+\"px\",top:e.frontPosition.y+\"px\"},e.applyStyles(n),e.applyStyles(o),e.restTimeout=setTimeout(function(){\"function\"==typeof t&&t.call(e),e.restCallback()},e.options.fadeTime)},e.prototype.restCallback=function(){var t=this,e={};e.front=c.getTransitionStyle(\"transition\",\"none\",\"\"),t.applyStyles(e),t.trigger(\"rested\",t.instance)},e.prototype.resetDirection=function(){this.direction={x:!1,y:!1,angle:!1}},e.prototype.computeDirection=function(t){var e,i,n,o=t.angle.radian,r=Math.PI/4,s=Math.PI/2;if(o>r&&o<3*r&&!t.lockX?e=\"up\":o>-r&&o<=r&&!t.lockY?e=\"left\":o>3*-r&&o<=-r&&!t.lockX?e=\"down\":t.lockY||(e=\"right\"),t.lockY||(i=o>-s&&o<s?\"left\":\"right\"),t.lockX||(n=o>0?\"up\":\"down\"),t.force>this.options.threshold){var d={};for(var a in this.direction)this.direction.hasOwnProperty(a)&&(d[a]=this.direction[a]);var p={};\
this.direction={x:i,y:n,angle:e},t.direction=this.direction;for(var a in d)d[a]===this.direction[a]&&(p[a]=!0);if(p.x&&p.y&&p.angle)return t;p.x&&p.y||this.trigger(\"plain\",t),p.x||this.trigger(\"plain:\"+i,t),p.y||this.trigger(\"plain:\"+n,t),p.angle||this.trigger(\"dir dir:\"+e,t)}return t},i.prototype=new t,i.constructor=i,i.id=0,i.prototype.prepareNipples=function(){var t=this,e=t.nipples;e.on=t.on.bind(t),e.off=t.off.bind(t),e.options=t.options,e.destroy=t.destroy.bind(t),e.ids=t.ids,e.id=t.id,e.processOnMove=t.processOnMove.bind(t),e.processOnEnd=t.processOnEnd.bind(t),e.get=function(t){if(void 0===t)return e[0];for(var i=0,n=e.length;i<n;i+=1)if(e[i].identifier===t)return e[i];return!1}},i.prototype.bindings=function(){var t=this;t.bindEvt(t.options.zone,\"start\"),t.options.zone.style.touchAction=\"none\",t.options.zone.style.msTouchAction=\"none\"},i.prototype.begin=function(){var t=this,e=t.options;if(\"static\"===e.mode){var i=t.createNipple(e.position,t.manager.getIdentifier());i.add(),t.idles.push(i)}},i.prototype.createNipple=function(t,i){var n=this,o=c.getScroll(),r={},s=n.options;if(t.x&&t.y)r={x:t.x-(o.x+n.box.left),y:t.y-(o.y+n.box.top)};else if(t.top||t.right||t.bottom||t.left){var d=document.createElement(\"DIV\");d.style.display=\"hidden\",d.style.top=t.top,d.style.right=t.right,d.style.bottom=t.bottom,d.style.left=t.left,d.style.position=\"absolute\",s.zone.appendChild(d);var a=d.getBoundingClientRect();s.zone.removeChild(d),r=t,t={x:a.left+o.x,y:a.top+o.y}}var p=new e(n,{color:s.color,size:s.size,threshold:s.threshold,fadeTime:s.fadeTime,dataOnly:s.dataOnly,restJoystick:s.restJoystick,restOpacity:s.restOpacity,mode:s.mode,identifier:i,position:t,zone:s.zone,frontPosition:{x:0,y:0}});return s.dataOnly||(c.applyPosition(p.ui.el,r),c.applyPosition(p.ui.front,p.frontPosition)),n.nipples.push(p),n.trigger(\"added \"+p.identifier+\":added\",p),n.manager.trigger(\"added \"+p.identifier+\":added\",p),n.bindNipple(p),p},i.prototype.updateBox=function(){var t=this;t.box=t.options.zone.getBoundingClientRect()},i.prototype.bindNipple=function(t){var e,i=this,n=function(t,n){e=t.type+\" \"+n.id+\":\"+t.type,i.trigger(e,n)};t.on(\"destroyed\",i.onDestroyed.bind(i)),t.on(\"shown hidden rested dir plain\",n),t.on(\"dir:up dir:right dir:down dir:left\",n),t.on(\"plain:up plain:right plain:down plain:left\",n)},i.prototype.pressureFn=function(t,e,i){var n=this,o=0;clearInterval(n.pressureIntervals[i]),n.pressureIntervals[i]=setInterval(function(){var i=t.force||t.pressure||t.webkitForce||0;i!==o&&(e.trigger(\"pressure\",i),n.trigger(\"pressure \"+e.identifier+\":pressure\",i),o=i)}.bind(n),100)},i.prototype.onstart=function(t){var e=this,i=e.options;t=c.prepareEvent(t),e.updateBox();var n=function(t){e.actives.length<i.maxNumberOfNipples&&e.processOnStart(t)};return c.map(t,n),e.manager.bindDocument(),!1},i.prototype.processOnStart=function(t){var e,i=this,n=i.options,o=i.manager.getIdentifier(t),r=t.force||t.pressure||t.webkitForce||0,s={x:t.pageX,y:t.pageY},d=i.getOrCreate(o,s);d.identifier!==o&&i.manager.removeIdentifier(d.identifier),d.identifier=o;var a=function(e){e.trigger(\"start\",e),i.trigger(\"start \"+e.id+\":start\",e),e.show(),r>0&&i.pressureFn(t,e,e.identifier),i.processOnMove(t)};if((e=i.idles.indexOf(d))>=0&&i.idles.splice(e,1),i.actives.push(d),i.ids.push(d.identifier),\"semi\"!==n.mode)a(d);else{if(!(c.distance(s,d.position)<=n.catchDistance))return d.destroy(),void i.processOnStart(t);a(d)}return d},i.prototype.getOrCreate=function(t,e){var i,n=this,o=n.options;return/(semi|static)/.test(o.mode)?(i=n.idles[0])?(n.idles.splice(0,1),i):\"semi\"===o.mode?n.createNipple(e,t):(console.warn(\"Coudln't find the needed nipple.\"),!1):i=n.createNipple(e,t)},i.prototype.processOnMove=function(t){var e=this,i=e.options,n=e.manager.getIdentifier(t),o=e.nipples.get(n);if(!o)return console.error(\"Found zombie joystick with ID \"+n),void e.manager.removeIdentifier(n);o.identifier=n;var r=o.options.size/2,s={x:t.pageX,y:t.pageY},d=c.distance(s,o.position),a=c.angle(s,o.position),p=c.radians(a),l=d/r;d>r&&(d=r,s=c.findCoord(o.position,d,a));var u=s.x-o.position.x,f=s.y-o.position.y;i.lockX&&(f=0),i.lockY&&(u=0),o.frontPosition={x:u,y:f},i.dataOnly||c.applyPosition(o.ui.front,o.frontPosition);var h={identifier:o.identifier,position:s,force:l,pressure:t.force||t.pressure||t.webkitForce||0,distance:d,angle:{radian:p,degree:a},instance:o,lockX:i.lockX,lockY:i.lockY};h=o.computeDirection(h),h.angle={radian:c.radians(180-a),degree:180-a},o.trigger(\"move\",h),e.trigger(\"move \"+o.id+\":move\",h)},i.prototype.processOnEnd=function(t){var e=this,i=e.options,n=e.manager.getIdentifier(t),o=e.nipples.get(n),r=e.manager.removeIdentifier(o.identifier);o&&(i.dataOnly||o.hide(function(){\"dynamic\"===i.mode&&(o.trigger(\"removed\",o),e.trigger(\"removed \"+o.id+\":removed\",o),e.manager.trigger(\"removed \"+o.id+\":removed\",o),o.destroy())}),clearInterval(e.pressureIntervals[o.identifier]),o.resetDirection(),o.trigger(\"end\",o),e.trigger(\"end \"+o.id+\":end\",o),e.ids.indexOf(o.identifier)>=0&&e.ids.splice(e.ids.indexOf(o.identifier),1),e.actives.indexOf(o)>=0&&e.actives.splice(e.actives.indexOf(o),1),/(semi|static)/.test(i.mode)?e.idles.push(o):e.nipples.indexOf(o)>=0&&e.nipples.splice(e.nipples.indexOf(o),1),e.manager.unbindDocument(),/(semi|static)/.test(i.mode)&&(e.manager.ids[r.id]=r.identifier))},i.prototype.onDestroyed=function(t,e){var i=this;i.nipples.indexOf(e)>=0&&i.nipples.splice(i.nipples.indexOf(e),1),i.actives.indexOf(e)>=0&&i.actives.splice(i.actives.indexOf(e),1),i.idles.indexOf(e)>=0&&i.idles.splice(i.idles.indexOf(e),1),i.ids.indexOf(e.identifier)>=0&&i.ids.splice(i.ids.indexOf(e.identifier),1),i.manager.removeIdentifier(e.identifier),i.manager.unbindDocument()},i.prototype.destroy=function(){var t=this;t.unbindEvt(t.options.zone,\"start\"),t.nipples.forEach(function(t){t.destroy()});for(var e in t.pressureIntervals)t.pressureIntervals.hasOwnProperty(e)&&clearInterval(t.pressureIntervals[e]);t.trigger(\"destroyed\",t.nipples),t.manager.unbindDocument(),t.off()},n.prototype=new t,n.constructor=n,n.prototype.prepareCollections=function(){var t=this;t.collections.create=t.create.bind(t),t.collections.on=t.on.bind(t),t.collections.off=t.off.bind(t),t.collections.destroy=t.destroy.bind(t),t.collections.get=function(e){var i;return t.collections.every(function(t){return!(i=t.get(e))}),i}},n.prototype.create=function(t){return this.createCollection(t)},n.prototype.createCollection=function(t){var e=this,n=new i(e,t);return e.bindCollection(n),e.collections.push(n),n},n.prototype.bindCollection=function(t){var e,i=this,n=function(t,n){e=t.type+\" \"+n.id+\":\"+t.type,i.trigger(e,n)};t.on(\"destroyed\",i.onDestroyed.bind(i)),t.on(\"shown hidden rested dir plain\",n),t.on(\"dir:up dir:right dir:down dir:left\",n),t.on(\"plain:up plain:right plain:down plain:left\",n)},n.prototype.bindDocument=function(){var t=this;t.binded||(t.bindEvt(document,\"move\").bindEvt(document,\"end\"),t.binded=!0)},n.prototype.unbindDocument=function(t){var e=this;Object.keys(e.ids).length&&!0!==t||(e.unbindEvt(document,\"move\").unbindEvt(document,\"end\"),e.binded=!1)},n.prototype.getIdentifier=function(t){var e;return t?void 0===(e=void 0===t.identifier?t.pointerId:t.identifier)&&(e=this.latest||0):e=this.index,void 0===this.ids[e]&&(this.ids[e]=this.index,this.index+=1),this.latest=e,this.ids[e]},n.prototype.removeIdentifier=function(t){var e={};for(var i in this.ids)if(this.ids[i]===t){e.id=i,e.identifier=this.ids[i],delete this.ids[i];break}return e},n.prototype.onmove=function(t){return this.onAny(\"move\",t),!1},n.prototype.onend=function(t){return this.onAny(\"end\",t),!1},n.prototype.oncancel=function(t){return this.onAny(\"end\",t),!1},n.prototype.onAny=function(t,e){var i,n=this,o=\"processOn\"+t.charAt(0).toUpperCase()+t.slice(1);e=c.prepareEvent(e);var r=function(t,e,i){i.ids.indexOf(e)>=0&&(i[o](t),t._found_=!0)},s=function(t){i=n.getIdentifier(t),c.map(n.collections,r.bind(null,t,i)),t._found_||n.removeIdentifier(i)};return c.map(e,s),!1},n.prototype.destroy=function(){var t=this;t.unbindDocument(!0),t.ids={},t.index=0,t.collections.forEach(function(t){t.destroy()}),t.off()},n.prototype.onDestroyed=function(t,e){var i=this;if(i.collections.indexOf(e)<0)return!1;i.collections.splice(i.collections.indexOf(e),1)};var l=new n;return{create:function(t){return l.create(t)},factory:l}});\n";
ptr +="//Global variables \n\
let steering=90; \n\
let speed=0; \n\
let direction=1; \n\
var http = new XMLHttpRequest() \n\
http.onreadystatechange = function() { \n\
if (this.readyState == 4 && this.status == 200) { \n\
//the response we are getting looks like this: L|lap_time|best_lap_time|total_laps \n\
var response = this.responseText \n\
var response_array = response.split('|'); \n\
if (response_array[0]=='L') \n\
{ \n\
var lap_time = response_array[1]; \n\
var best_lap_time = response_array[2]; \n\
var total_laps = response_array[3]; \n\
document.getElementById('lap_time').innerHTML = lap_time; \n\
document.getElementById('best_lap_time').innerHTML = best_lap_time; \n\
document.getElementById('total_laps').innerHTML = total_laps; \n\
}\n\
} \n\
}; \n\
//Configure joysticks \n\
var joystickL = nipplejs.create({ \n\
zone: document.getElementById('left'), \n\
mode: 'static', \n\
position: { left: '50%', top: '50%' }, \n\
color: 'green', \n\
size: 200, \n\
fadeTime: 25, \n\
lockX: true \n\
}); \
var joystickR = nipplejs.create({ \n\
zone: document.getElementById('right'), \n\
mode: 'static', \n\
position: { left: '50%', top: '50%' }, \n\
color: 'red', \n\
size: 200, \n\
fadeTime: 25, \n\
lockY: true \n\
}); \n\
this.joystickR.on('move', (evt, data) => { \n\
direction = data.direction.y; \n\
//Mapping: Joystick will return direction as 'up'/'down', we need to map it to: 1->forward , 2->back \n\
if (direction=='up') \n\
direction=1; \n\
else \n\
direction=2; \n\
//Mapping: Joystick will give us 0-100 , The range of speed is 0-255 , we need to make simple mapping \n\
speed = Math.round(data.distance * 2.55) ; \n\
console.log('direction', direction, speed); \n\
}); \n\
this.joystickR.on('end', (evt, data) => { \n\
speed = 0 ; \n\
console.log('released: speed=0'); \n\
}); \n\
this.joystickL.on('move', (evt, data) => { \n\
steering_direction = data.direction.x; \n\
//Mapping: Joystick will give us 0-100 , The range of the steering is 0-180 (90 angles on each side) \n\
steering = Math.round(data.distance*0.9) ; \n\
if (steering_direction=='left') \n\
steering = steering + 90; \n\
else \n\
steering= 90-steering; \n\
console.log('steering_direction', steering_direction, steering); \n\
}); \n\
this.joystickL.on('end', (evt, data) => { \n\
steering = 90 ; \n\
console.log('released: steering=90'); \n\
}); \n\
setInterval(function(){ \n\
var message = '/drive?steering='+steering+'&speed='+speed+'&direction='+direction; \n\
http.open('GET', message, true); \n\
http.send(); \n\
}, 200); \n\
</script> \n\
</body> \n\
</html>";
return ptr;
}
void handle_OnConnect() {
server.send(200, "text/html", SendHTML());
}
void setup() {
Serial.begin(115200);
//We are using permanent memory of the ESP32 to store the total number of laps robot drove
//This value will be stored even if esp32 is restarted or turned off
// Initialize EEPROM
EEPROM.begin(512);
//Read the total number of laps from flash memory (address=0)
EEPROM.get(0,total_laps_counter);
if (total_laps_counter<0)
{
//This means this memory cell was no used before and we need to initialize it with 0 count
//EEPROM.put(address, value);
EEPROM.put(0, 0);
EEPROM.commit();
total_laps_counter=0;
}
Serial.println("total_laps_counter");
Serial.println(total_laps_counter);
EEPROM.put(0, 501);
EEPROM.commit();
//Play Music
Serial.println();
Serial.println("Melody Player - Simple Play (blocking vs non-blocking play");
Serial.println("Loading melody...");
String notes[] = { "C4", "G3", "G3", "A3", "G3", "SILENCE", "B3", "C4" };
// Load and play a correct melody
Melody melody = MelodyFactory.load("Nice Melody", 175, notes, 8);
Serial.println(String(" Title:") + melody.getTitle());
Serial.println(String(" Time unit:") + melody.getTimeUnit());
Serial.print("Play in blocking mode...");
player.playAsync(melody);
Serial.println("The end!");
// set up LEDs
FastLED.addLeds<WS2812, 33, GRB>(leds, 16);
FastLED.setBrightness(30);
//LED Matrix Animation
for(int i = 0; i < 4; i++)
{
//The animation is lighting all the leds one after another (first loop)
//Then it turns off all the leds one after another (second loop)
for(int dot = 0; dot < 16; dot++) {
leds[dot] = CRGB::Blue;
FastLED.show();
delay(15);
}
for(int dot = 0; dot < 16; dot++) {
leds[dot] = CRGB::Black;
FastLED.show();
delay(15);
}
}
//After the animation we want to dispaly a simple shape, blue rectangle with green rectangle inside of it
leds[0] = CRGB::Blue;
leds[1] = CRGB::Blue;
leds[2] = CRGB::Blue;
leds[3] = CRGB::Blue;
leds[4] = CRGB::Blue;
leds[8] = CRGB::Blue;
leds[12] = CRGB::Blue;
leds[13] = CRGB::Blue;
leds[14] = CRGB::Blue;
leds[15] = CRGB::Blue;
leds[7] = CRGB::Blue;
leds[11] = CRGB::Blue;
leds[5] = CRGB::Red;
leds[6] = CRGB::Red;
leds[9] = CRGB::Red;
leds[10] = CRGB::Red;
FastLED.show();
//Wifi MODE
/*
// Replace with your network credentials
const char* ssid = "Kilometer_2.4G";
const char* password = "kilo2005#";
//const char* ssid = "PlayRobotics";
//const char* password = "";
//const char* ssid = "slom";
//const char* password = "308765205";
//WIFI
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
delay(1000);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Got IP: "); Serial.println(WiFi.localIP());
*/
//Acccess Point Mode
/* Put IP Address details */
IPAddress local_ip(192,168,1,4);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
const char* ssid = "Playro";
const char* password = "playrobotics";
WiFi.persistent(false);
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
//Server
server.on("/", handle_OnConnect);
server.on("/drive", []() {
int steering = server.arg("steering").toInt();
//Serial.printf("Steering %d\n", steering);
steering_rc(steering);
int speed = server.arg("speed").toInt();
Serial.printf("Speed %d\n", speed);
int direction = server.arg("direction").toInt();
Serial.printf("direction %d\n", direction);
if (direction==1)
drive_rc(true, speed); //forward
else
drive_rc(false, speed*-1); //back
server.sendHeader("Access-Control-Allow-Origin", "*");
String lap_time_response = "L|";
lap_time_response += formatted_lap_time;
lap_time_response += "|";
lap_time_response += formatted_best_lap_time;
lap_time_response += "|";
lap_time_response += total_laps_counter;
server.send(200, "text/html", lap_time_response);
});
server.begin();
Serial.println("HTTP server started");
irrecv.enableIRIn(); // Start the receiver
while (!Serial) // Wait for the serial connection to be established.
delay(50);
Serial.println();
Serial.print("IRrecvDemo is now running and waiting for IR message on Pin ");
Serial.println(kRecvPin);
//activate ir send pin
irsend.begin();
//motors
pinMode(MOTOR_PIN_IN1, OUTPUT);
pinMode(MOTOR_PIN_IN2, OUTPUT);
ledcSetup(PWMB_CHANNEL, PWM_FREQ, SPEED_RESOLUTION);
ledcAttachPin(MOTOR_PIN_PWMB, PWMB_CHANNEL);
//Servo
myservo.setPeriodHertz(50);// Standard 50hz servo
myservo.attach(SERVO_PIN, 500, 2400); // attaches the servo on pin 18 to the servo object
//Center the servo on startup (you can adjust servo_offset value to find the real center at the top of the file)
myservo.write(servo_center+servo_offset);
radio.begin();
radio.openReadingPipe(1, robot);
radio.setPALevel(RF24_PA_MAX);
radio.startListening();
//we need to initialize those variables with initial values, so our lap counter will work
int last_lap_time = millis();
int last_sensor_lap_reading = millis();
}
void loop() {
/*
//Ultrasonic
long duration, inches, cm;
pinMode(trigPin, OUTPUT);
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT);
duration = pulseIn(echoPin, HIGH);
cm = duration / 29 / 2;
Serial.println(cm);
if ((cm<50)&&(cm>0))
{
for(int i=0; i<16; i++) {
leds[i] = CRGB::Red;
}
}
else
{
for(int i=0; i<16; i++) {
leds[i] = CRGB::Black;
}
}
FastLED.show();
*/
server.handleClient();
//Check if we got some ir value
read_from_remote();
//delay(10);
if (irrecv.decode(&results)) {
Serial.println("GOT IR:");
Serial.print((uint32_t)results.value, HEX);
Serial.println("");
if((results.value == 0X9999)||(results.value == 0X8888)||(results.value == 0X7777)||(results.value == 0X6666))
{
//We know we got the correct IR , but we need to add some protections:
//Problem #1
//When the robot passes the finish line it might get multiple ir readings of corect code
//We want to make sure there is at least 3 seconds gap between the pervious lap and the current one,
//Otherwise this is not really a new lap, but multiple readings of the sensor when the robot passes the finish line
//Problem #2
//If the robot is just standing at the finish line, sensor will continuesly receive the ir code
//So not only we need 3 seconds gap between laps, we actually need 3 seconds between sensor readings
//(if we get correct IR code for lap, we need to make sure the last time we got this code before was 3 seconds ago or more )
//You can change 2000 (2 seconds)to other value if you wish
//Another solution to this both those problems can be to use another IR led with different code as checkpoint
//(requires another lap counter or any other way to send ir code)
current_time = millis();
if((current_time - last_sensor_lap_reading) > 2000 )
{
//Time that the lap took
float lap_time = current_time - last_lap_time;
//From miliseconds to seconds
lap_time = lap_time / 1000;
//Now our lap time is a float number , which can be something like this 12.3425235234
//We will use a function to turn it into something like 12.34 (lenght=4, digits after deimal point=2)
dtostrf(lap_time,4, 2, formatted_lap_time);
Serial.println("=====Lap Time=====");
Serial.println(lap_time);
Serial.println("==================");
//Set last lap time to current time because the lap is over and we are stating a new one
last_lap_time = current_time;
//Now we can check if this is the best lap or just a regular lap
//First lap will always be best because the default value of float best_lap_time is very high
if (lap_time<best_lap_time)
{
//This is a best lap
//We will send a best lap message to the remote control, so it can display it on screen
//And we will play a special melody
//update best lap time
best_lap_time = lap_time;
//Our lap time is already formated and stored in formatted_lap_time varaible
//We will also fomat formatted_BEST_lap_time variable in order to save this value for later use
dtostrf(best_lap_time,4, 2, formatted_best_lap_time);
//Send the lap time to the remote control
Serial.println("Sending --== BEST ==- lap time to remote");
// "B" - Is a command which means - Best lap time
String lap_time_to_send = "B";
lap_time_to_send += formatted_best_lap_time;
//We will be using an array of chars to send the message
char message_to_send[6];
//We will conert our string to an array of chars, because it is required by the radio library
lap_time_to_send.toCharArray(message_to_send, 50);
//Set the radio to transmitter mode
radio.stopListening();
radio.openWritingPipe(remote_control);
if (!radio.write(&message_to_send, sizeof(message_to_send)))
Serial.println("----====Sending to remote control failed====-----");
else
Serial.println("----====SENT: REMOTE CONTROL");
delay(5);
//Now we ill send the same message to race controller
radio.openWritingPipe(race_controller);
if (!radio.write(&message_to_send, sizeof(message_to_send)))
Serial.println("----====Sending to race controller failed====-----");
else
Serial.println("----====SENT: RACE CONTROLLER");
Serial.println("Loading melody...");
String notes[] = { "C4", "G3", "C4"};
// Load and play a correct melody
Melody melody = MelodyFactory.load("Nice Melody", 175, notes, 3);
player.playAsync(melody);
//Now display random color on LED ring
//Pick a random number
int random_number = random(8);
//We dont want to have the same color twice, so we need to make sure the random number is new
//If it is not new we will just try another random
while (last_random_number==random_number)
random_number = random(8);
//Now we surely got a new random number, lets save it for next time
last_random_number = random_number;
CRGB randomcolor;
//Depending on the number we will choose a color
switch (random_number) {
case 1:
randomcolor =CRGB::Blue;
break;
case 2:
randomcolor =CRGB::Red;
break;
case 3:
randomcolor =CRGB::Purple;
break;
case 4:
randomcolor =CRGB::Brown;
break;
case 5:
randomcolor =CRGB::Green;
break;
case 6:
randomcolor =CRGB::Yellow;
break;
case 7:
randomcolor =CRGB::Pink;
default:
randomcolor =CRGB::White;
break;
}
//Paint all 16 leds of our ring in this color
for(int i=0; i<16; i++) {
leds[i] = randomcolor;
//FastLED.show();
}
FastLED.show();
delay(5);
//Once finished to send the message we need to continue listening to remote control commands asap
radio.startListening();
}
else
{
//NOT a best lap
//We will send a lap message to the remote control, so it can display it on screen
//And we will play a melody
//Send the lap time to the remote control
Serial.println("Sending lap time to remote");
// "L" - Is a command which means - Lap time
String lap_time_to_send = "L";
lap_time_to_send += formatted_lap_time;
//We will be using an array of chars to send the message
char message_to_send[6];
//We will conert our string to an array of chars, because it is required by the radio library
lap_time_to_send.toCharArray(message_to_send, 50);
//Set the radio to transmitter mode
radio.stopListening();
radio.openWritingPipe(remote_control);
delay(5);
if (!radio.write(&message_to_send, sizeof(message_to_send)))
Serial.println("----====Sending to remote control failed====-----");
else
Serial.println("----====SENT: REMOTE CONTROL");
delay(5);
//Now we ill send the same message to race controller
radio.openWritingPipe(race_controller);
if (!radio.write(&message_to_send, sizeof(message_to_send)))
Serial.println("----====Sending to race controller failed====-----");
else
Serial.println("----====SENT: RACE CONTROLLER");
Serial.println("Loading melody...");
String notes[] = { "C5", "G4"};
// Load and play a correct melody
Melody melody = MelodyFactory.load("Nice Melody", 175, notes, 2);
player.playAsync(melody);
delay(5);
//Once finished to send the message we need to continue listening to remote control commands asap
radio.startListening();
}
//Update our total laps counter (number of laps the robot ever drove);
total_laps_counter++;
//Write this value to flash memory for permanent storage
EEPROM.put(0, total_laps_counter);
EEPROM.commit();
}
//Doesn't metter if we counted this lap or not we need to restart this variable to solve problem #2 explained above
last_sensor_lap_reading = current_time;
}
irrecv.resume(); // Receive the next value
}
}
אם לאחר העלאת הקוד פליירו לא נוסע ישר (כלומר פליירו נוטה לאחד הצדדים גם כאשר אתם נוסעים קדימה בלי לגעת בהיגוי כלל) , ניתן לשחק עם הערך של המשתנה המוגדר בתחילת הקוד:
servo_center = 90
תפקידו של משתנה זה הוא להגדיר את זווית המרכז של הסרבו.
במקום 90, ניתן לתת למשתנה הזה ערכים בטווח בין 85 ל95 (אם פיירו נוטה ימינה, נסו לתת למשתנה זה ערך 95, אם הוא נוטה שמאלה, נסו ערך 85). לא לשכוח להעלות את הקוד כל פעם שמחליפים ערך.