Задержка в веб-сокете
Я использую 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
- Команда продолжает повторяться, потому что веб-страница пытается обновить
- Отправка состояния подключения Wi-Fi NodeMCU в базу данных MySQL
- ESP8266 не подключается к Wi-Fi
- Каково использование зарезервированных контактов и контактов SDD2, SDD3 NodeMCU?
- Как разрешить междоменные запросы на ESP8266 WebServer
- Как получить текущий уровень сигнала WiFi?
- Обнаружение ESP8266 в сети
Ваш сервер WebSocket прослушивает порт 81, тогда как ваш клиент подключается к портам 9000–9003. Вы уверены, что размещенный вами HTML-код соответствует скетчу над ним? Пожалуйста, убедитесь, что элементы вашего вопроса согласуются друг с другом., @Edgar Bonet
О, мне очень жаль, на самом деле я сделал несколько версий этого проекта, и ранее я делал с одним портом веб-сокета, и на самом деле я по ошибке вставил его из этого проекта. Я просто делаю это правильно., @Pritam Sarkar
теперь можно проверить!, @Pritam Sarkar
Наличие 4 серверов не обязательно. Возникает ли задержка время от времени и восстанавливается ли она или остается высокой? В настоящее время у меня есть аналогичная проблема с WebsocketsClient из этого репо., @Sim Son