Задержка в веб-сокете
Я использую Websocket для связи с моего мобильного телефона на NodeMCU и передачи 4-канальных данных через 4 разных порта Websocket. управляющий код NodeMCU находится здесь:
// ============ By Pritam Pagla ============ //
// Единственная программа Websocket подключена к основной ветке
// СТОРОНА NodeMCU
#include <SoftwareSerial.h>
#include <ArduinoJson.h> // Предпочтительно использовать ArduinoJson 5.x, так как объект StaticJsonBuffer не определен в дальнейших версиях
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include<WebSocketsServer.h>
#include "index_NodeMCU_Test1.h"; // Полная веб-страница
SoftwareSerial s(D6,D5); // Rx как D6 и Tx и D5
int16_t throttle_Val = 0;
int16_t yaw_Val = 0;
int16_t pitch_Val = 0;
int16_t roll_Val = 0;
uint8_t num;
WStype_t type;
uint8_t *payload;
size_t length;
//Конфигурация WiFi-соединения
const char *ssid = "PriCopter"; // Можно выбрать все, что угодно
const char *password = "01234567";
WebSocketsServer Twebsocket = WebSocketsServer(9001);
WebSocketsServer Ywebsocket = WebSocketsServer(9002);
WebSocketsServer Rwebsocket = WebSocketsServer(9003);
WebSocketsServer Pwebsocket = WebSocketsServer(9004);
ESP8266WebServer server(80);
//===============================================
void handleRoot() {
String s = MAIN_page; //Чтение HTML-содержимого
server.send(200, "text/html", s); //Отправить веб-страницу
}
//===============================================
// Настраивать
//===============================================
void setup() {
s.begin(115200);
Serial.begin(115200);
delay(100);
//Настройте NodeMCU как точку доступа
WiFi.softAP(ssid, password, 1, false, 1); //Используем NodeMCU как точку доступа
//Если подключение успешное, показать IP-адрес в последовательном мониторе
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
//Инициализировать веб-сервер
server.on("/",handleRoot);
server.begin();
Twebsocket.begin();
Ywebsocket.begin();
Rwebsocket.begin();
Pwebsocket.begin();
}
void loop() {
StaticJsonBuffer<100> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
server.handleClient();
Twebsocket.loop();
Ywebsocket.loop();
Rwebsocket.loop();
Pwebsocket.loop();
Twebsocket.onEvent(ThrottleEvent);
Ywebsocket.onEvent(YawEvent);
Rwebsocket.onEvent(RollEvent);
Pwebsocket.onEvent(PitchEvent);
Serial.print("Throttle: "); Serial.print(throttle_Val);
Serial.print(" Yaw: "); Serial.print(yaw_Val);
Serial.print(" Pitch: "); Serial.print(pitch_Val);
Serial.print(" Roll: "); Serial.print(roll_Val);
root["throttle"] = throttle_Val;
root["yaw"] = yaw_Val;
root["roll"] = pitch_Val;
root["pitch"] = roll_Val;
root.printTo(s);
Serial.println(" (Sent)");
}
void YawEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length){
if (type == WStype_TEXT){
yaw_Val = (int16_t)strtol((const char*) &payload[0], NULL, 10);
}
}
void ThrottleEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length){
if (type == WStype_TEXT){
throttle_Val = (int16_t)strtol((const char*) &payload[0], NULL, 10);
}
}
void RollEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length){
if (type == WStype_TEXT){
roll_Val = (int16_t)strtol((const char*) &payload[0], NULL, 10);
}
}
void PitchEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length){
if (type == WStype_TEXT){
pitch_Val = (int16_t)strtol((const char*) &payload[0], NULL, 10);
}
}
и запрос сведений о веб-странице выглядит следующим образом:
const char MAIN_page[] PROGMEM = R"=====(<!DOCTYPE html>
<html lang = 'en'>
<head>
<meta charset="UTF-8"/>
<meta name="viewport"
content="width=950, height=420 initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible"
content="ie=edge"/>
<title>Drone Remote</title>
<style>
body{
background-color: #333;
color: 'black';
display: flex;
flex-direction: row;
align-items: center;
font-family: Arial, Helvetica, sans-serif;
min-height: 100vh;
margin:0;
}
#canvasL{
background: #f0f0f0;
border-radius: 5x
}
#canvasM{
background: #f0f0f0;
border-radius: 5px
}
#canvasR{
background: #f0f0f0;
border-radius: 5px
}
#source{
display: none;
}
</style>
</head>
<body>
<canvas id="canvasL" width="450" height="420"></canvas>
<canvas id="canvasM" width="50" height="420"></canvas>
<canvas id="canvasR" width="450" height="420"></canvas>
<img src="data:image/png;base64,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" alt="" id="source"/>
<script>
const canvasL = document.getElementById('canvasL'); // Объект для левого холста
const Lctx = canvasL.getContext('2d');
const canvasM = document.getElementById('canvasM'); // Объект для среднего холста
const Mctx = canvasM.getContext('2d');
const canvasR = document.getElementById('canvasR'); // Объект для правого холста
const Rctx = canvasR.getContext('2d');
const LcenterX = (canvasL.width / 2); // Маркировка центра для левого джойстика
const LcenterY = (canvasL.height / 2);
const RcenterX = (canvasR.width / 2); // Отметка центра для правого джойстика
const RcenterY = (canvasR.height / 2);
const McenterX = (canvasM.width / 2); // Центр маркировки средней части
const McenterY = (canvasM.height / 2);
var Tsocket = new WebSocket('ws://' + window.location.hostname + ':9001/');
var Ysocket = new WebSocket('ws://' + window.location.hostname + ':9002/');
var Psocket = new WebSocket('ws://' + window.location.hostname + ':9003/');
var Rsocket = new WebSocket('ws://' + window.location.hostname + ':9004/');
const image = document.getElementById('source');
var throttle_Val = 0;
var yaw_Val = 0;
var pitch_Val = 0;
var roll_Val = 0;
const throttle = "t";
const yaw = "y";
const pitch = "p";
const roll = "r";
const LJoyStickBall = { // определяем левый джойстик как объект
w: 60,
h: 60,
x: LcenterX,
y: LcenterY+195,
speed: 1,
dx: 0,
dy: 0
};
const RJoyStickBall = { // определяем правый джойстик как объект
w: 60,
h: 60,
x: RcenterX,
y: RcenterY,
speed: 1,
dx: 0,
dy: 0
};
function drawLeftCenterBall(){ //Рисование шара левого джойстика
Lctx.drawImage(image, LJoyStickBall.x-30, LJoyStickBall.y-30, LJoyStickBall.w, LJoyStickBall.h);
}
function drawRightCenterBall(){ //Рисование шара правого джойстика
Rctx.drawImage(image, RJoyStickBall.x-30, RJoyStickBall.y-30, RJoyStickBall.w, RJoyStickBall.h);
}
function LnewPos() { // изменение позиции клавиатуры
LJoyStickBall.x += LJoyStickBall.dx;
LJoyStickBall.y += LJoyStickBall.dy;
LdetectBoundary();
}
function RnewPos() { // изменение позиции клавиатуры
RJoyStickBall.x += RJoyStickBall.dx;
RJoyStickBall.y += RJoyStickBall.dy;
RdetectBoundary();
}
function LdetectBoundary(){ // определение границы для LeftJoystickCenterBall
// Левая сторона
if (LJoyStickBall.x < LcenterX-195) {
LJoyStickBall.x = LcenterX-195;
}
// Правая сторона
if (LJoyStickBall.x > LcenterX+195) {
LJoyStickBall.x = LcenterX+195;
}
// Верхняя сторона
if (LJoyStickBall.y < LcenterY-195) {
LJoyStickBall.y = LcenterY-195;
}
// Нижняя сторона
if (LJoyStickBall.y > LcenterY+195) {
LJoyStickBall.y = LcenterY+195;
}
}
function RdetectBoundary(){ // определение границы для LeftJoystickCenterBall
// Левая сторона
if (RJoyStickBall.x < RcenterX-195) {
RJoyStickBall.x = RcenterX-195;
}
// Правая сторона
if (RJoyStickBall.x > RcenterX+195) {
RJoyStickBall.x = RcenterX+195;
}
// Верхняя сторона
if (RJoyStickBall.y < RcenterY-195) {
RJoyStickBall.y = RcenterY-195;
}
// Нижняя сторона
if (RJoyStickBall.y > RcenterY+195) {
RJoyStickBall.y = RcenterY+195;
}
}
function drawDesigns(){ // 4 пересекающихся круга и 2 перекрещенные линии для лучшего интерфейса
Lctx.beginPath();
Lctx.lineWidth = 2.5;
Lctx.moveTo(LcenterX, LcenterY-195); // Пересечение линий слева
Lctx.lineTo(LcenterX, LcenterY+195);
Lctx.moveTo(LcenterX-195, LcenterY);
Lctx.lineTo(LcenterX+195, LcenterY);
Lctx.arc(LcenterX, LcenterY, 195, 0, Math.PI * 2); //Концентрические круги слева
Lctx.arc(LcenterX, LcenterY, 150, 0, Math.PI * 2);
Lctx.arc(LcenterX, LcenterY, 100, 0, Math.PI * 2);
Lctx.arc(LcenterX, LcenterY, 50, 0, Math.PI * 2);
Lctx.stroke();
Rctx.beginPath();
Rctx.lineWidth = 2.5;
Rctx.moveTo(RcenterX, RcenterY-195); // Пересечение линий справа
Rctx.lineTo(RcenterX, RcenterY+195);
Rctx.moveTo(RcenterX-195, RcenterY);
Rctx.lineTo(RcenterX+195, RcenterY);
Rctx.arc(RcenterX, RcenterY, 195, 0, Math.PI * 2); //Концентрические круги справа
Rctx.arc(RcenterX, RcenterY, 150, 0, Math.PI * 2);
Rctx.arc(RcenterX, RcenterY, 100, 0, Math.PI * 2);
Rctx.arc(RcenterX, RcenterY, 50, 0, Math.PI * 2);
Rctx.stroke();
}
function clearScreen(){ // Очистка LeftCanvas и RightCanvas в каждом цикле анимации
Lctx.clearRect(0, 0, canvasL.width, canvasL.height);
Rctx.clearRect(0, 0, canvasR.width, canvasR.height);
}
function ConsoleOUT(a,b,c,d){
throttle_Val = (405-a);
yaw_Val = (b-225);
pitch_Val = (210-c);
roll_Val = (d-225);
console.log('Throttle: '+throttle_Val+' Yaw: '+yaw_Val+' Pitch: '+pitch_Val+' Roll: '+roll_Val);
}
function LeftUpdate(){
clearScreen(); // Левый джойстик
drawDesigns();
drawLeftCenterBall();
drawRightCenterBall();
LnewPos();
RnewPos();
ConsoleOUT(LJoyStickBall.y,LJoyStickBall.x,RJoyStickBall.y,RJoyStickBall.x);
requestAnimationFrame(LeftUpdate);
}
function RightUpdate(){ //Правый джойстик
clearScreen();
drawDesigns();
drawLeftCenterBall();
drawRightCenterBall();
LnewPos();
RnewPos();
ConsoleOUT(LJoyStickBall.y,LJoyStickBall.x,RJoyStickBall.y,RJoyStickBall.x);
requestAnimationFrame(RightUpdate);
}
function LmoveUp() { // Для активности клавиатуры
LJoyStickBall.dy = -LJoyStickBall.speed;
}
function LmoveDown() {
LJoyStickBall.dy = LJoyStickBall.speed;
}
function LmoveRight() {
LJoyStickBall.dx = LJoyStickBall.speed;
}
function LmoveLeft() {
LJoyStickBall.dx = -LJoyStickBall.speed;
}
function RmoveUp() {
RJoyStickBall.dy = -RJoyStickBall.speed;
}
function RmoveDown() {
RJoyStickBall.dy = RJoyStickBall.speed;
}
function RmoveRight() {
RJoyStickBall.dx = RJoyStickBall.speed;
}
function RmoveLeft() {
RJoyStickBall.dx = -RJoyStickBall.speed;
}
canvasL.addEventListener('touchstart', function(e){ // событие 'touchstart' в LeftCanvas
e.preventDefault();
canvasL.addEventListener('touchmove', function(e){
var touchobj = e.touches[0]; // ссылка на первую точку касания для этого события
LJoyStickBall.x = touchobj.pageX;
LJoyStickBall.y = touchobj.pageY;
sendData(throttle,405-LJoyStickBall.y);
sendData(yaw,LJoyStickBall.x-225);
}, false)
}, false)
canvasL.addEventListener('touchend', function(e){ // событие 'touchend' в LeftCanvas
e.preventDefault();
LJoyStickBall.x = LcenterX;
sendData(yaw,0);
}, false)
canvasR.addEventListener('touchstart', function(e){ // событие 'touchstart' в RightCanvas
e.preventDefault();
canvasR.addEventListener('touchmove', function(e){
var touchobj = e.touches[0]; // ссылка на первую точку касания для этого события
RJoyStickBall.x = touchobj.pageX - (canvasR.width + RJoyStickBall.w);
RJoyStickBall.y = touchobj.pageY - RJoyStickBall.h;
sendData(pitch,210-RJoyStickBall.y);
sendData(roll,RJoyStickBall.x-225);
}, false)
}, false)
canvasR.addEventListener('touchend', function(e){ // событие 'touchend' в RightCanvas
e.preventDefault();
RJoyStickBall.x = RcenterX;
RJoyStickBall.y = RcenterY;
sendData(pitch,0);
sendData(roll,0);
}, false)
function sendData(a,b){
if(a == throttle){
Tsocket.send(b);
}
if(a == yaw){
Ysocket.send(b);
}
if(a == roll){
Rsocket.send(b);
}
if(a == pitch){
Psocket.send(b);
}
// переключатель(а){
// корпус дроссельной заслонки:
// Tsocket.send(b);
// ломать;
// случай рыскания:
// Ysocket.send(b);
// ломать;
// список дел:
// Rsocket.send(b);
// ломать;
// шаг корпуса:
// Psocket.send(b);
// ломать;
// дефолт:
// console.log('Произошло прерывание.')
// }
}
document.addEventListener('keydown', function (e) { // Когда нажата определенная клавиша
if (e.key == 'ArrowRight') {
LmoveRight();
sendData(yaw,LJoyStickBall.x-225);
} if (e.key == 'ArrowLeft') {
LmoveLeft();
sendData(yaw,LJoyStickBall.x-225);
} if (e.key == 'ArrowUp') {
LmoveUp();
sendData(throttle,405-LJoyStickBall.y);
} if (e.key == 'ArrowDown') {
LmoveDown();
sendData(throttle,405-LJoyStickBall.y);
}
if(e.key == 'w'){
RmoveUp();
sendData(pitch,210-RJoyStickBall.y);
}
if(e.key == 's'){
RmoveDown();
sendData(pitch,210-RJoyStickBall.y);
}
if(e.key == 'a'){
RmoveLeft();
sendData(roll,RJoyStickBall.x-225);
}
if(e.key == 'd'){
RmoveRight();
sendData(roll,RJoyStickBall.x-225);
}
});
document.addEventListener('keyup', function (e) { // Когда отпускается определенная клавиша
if (
e.key == 'ArrowLeft' ||
e.key == 'ArrowRight' ||
e.key == 'ArrowUp' ||
e.key == 'ArrowDown' ||
e.key == 'w' ||
e.key == 's' ||
e.key == 'a' ||
e.key == 'd'
) {
LJoyStickBall.dx = 0;
LJoyStickBall.dy = 0;
RJoyStickBall.dx = 0;
RJoyStickBall.dy = 0;
LJoyStickBall.x = LcenterX;
RJoyStickBall.x = RcenterX;
RJoyStickBall.y = RcenterY;
sendData(yaw,0);
sendData(pitch,0);
sendData(roll,0);
}
});
LeftUpdate();
RightUpdate();
</script>
</body>
</html>
)=====";
Теперь я новичок в веб-проектах и явно новичок в веб-сокетах. Вначале я использовал один порт веб-сокета и передавал данные четырех каналов в виде файла JSON, но дифференцирование данных в 4 разных каналах немного увеличило скорость, но все же одна проблема сохраняется на всем протяжении, то есть через несколько секунд ( 30-35), он начинает достигать задержки и остается до тех пор, пока страница не обновится снова. Может кто-нибудь предложить какое-либо решение для этого, так как я использую его для управления своим дроном, поэтому любая ненужная задержка может быть опасной.
Для получения дополнительной информации см. подробное описание и аналогичные коды в этом репозитории.
Заранее спасибо.
@Pritam Sarkar, 👍2
Обсуждение1 ответ
Не совсем правильный ответ, но несколько идей, которые, надеюсь, помогут вы отлаживаете проблему:
- Я сомневаюсь, что использование нескольких серверов WebSocket действительно поможет, поскольку в конечном итоге вы добавление большего количества переключений контекста, т.е. вам нужно перебрать все их, даже если у них нет данных для обработки.
SoftwareSerialработает очень медленно, так как блокирует выполнение скетча во время его отправки. Вы можете попробовать отключить его, по крайней мере пока вы устраняете эту проблему с задержкой.- Вызов
Serial.print()на каждой итерации цикла также замедляет работу. скетч вниз, пока он не будет быстрее, чем последовательный порт. Пытаться печатать только время от времени или только тогда, когда что-то действительно меняется. - И в Firefox, и в Chrome есть «инструменты разработчика», которые должны вам помочь диагностировать проблему. По крайней мере, вы должны быть в состоянии видеть связано ли замедление с сервером или с клиентом.
Нет, я просто использовал SoftwareSerial для SBUS между NodeMCU и Nano, который не имеет ничего общего с веб-сокетом, и без этого он по-прежнему вызывает ту же проблему., @Pritam Sarkar
- WebSocketsServer.h: No such file or directory
- Проблема с nodemcu esp-01 не могу подключиться к wifi
- Команда продолжает повторяться, потому что веб-страница пытается обновить
- Отправка состояния подключения Wi-Fi NodeMCU в базу данных MySQL
- ESP8266 не подключается к Wi-Fi
- Каково использование зарезервированных контактов и контактов SDD2, SDD3 NodeMCU?
- Как разрешить междоменные запросы на ESP8266 WebServer
- Как получить текущий уровень сигнала WiFi?
Ваш сервер WebSocket прослушивает порт 81, тогда как ваш клиент подключается к портам 9000–9003. Вы уверены, что размещенный вами HTML-код соответствует скетчу над ним? Пожалуйста, убедитесь, что элементы вашего вопроса согласуются друг с другом., @Edgar Bonet
О, мне очень жаль, на самом деле я сделал несколько версий этого проекта, и ранее я делал с одним портом веб-сокета, и на самом деле я по ошибке вставил его из этого проекта. Я просто делаю это правильно., @Pritam Sarkar
теперь можно проверить!, @Pritam Sarkar
Наличие 4 серверов не обязательно. Возникает ли задержка время от времени и восстанавливается ли она или остается высокой? В настоящее время у меня есть аналогичная проблема с WebsocketsClient из этого репо., @Sim Son