страницы печатаются Json-данные
У меня ESP32 с веб-сервером, на странице, где я показываю показания датчиков и состояние системы, сейчас показания отображаются хорошо, проблема возникает, когда я хочу показать состояние системы.
Сейчас я тестирую поведение только со страницей /encender
. Состояние системы отправляется в файле JSON только для обновления элементов, содержащих состояние системы, но вместо обновления этих элементов HTML страница исчезает, и веб-обозреватель показывает мне ошибку пустого ответа.
Вот мой фактический код, относящийся к моей проблеме:
Код, относящийся к странице /encender (элемент) в backend.cpp:
server.on("/encender", HTTP_GET,[](AsyncWebServerRequest *request){
digitalWrite(ledPinSistemaApagado,LOW);
digitalWrite(ledPinSistemaEncendido,HIGH);
if(ultimaPaginaCargada == "/encender");
{
Serial.println("No se envía la página /encender, solo se actualizará el estado del sistema.");
request->send(200);
return;
}
ultimaPaginaCargada = "/encender";
cuentaAcceso = true;
request->send(SPIFFS,"/index.html", String(), false, Procesador);
});
код в backend.cpp, связанный с обновлением состояния функции, выглядит следующим образом
server.on("/data-estado", HTTP_GET, [](AsyncWebServerRequest *request){
if(!request->authenticate(usuarioHTTP, claveHTTP) && !cuentaAcceso) {
return request->requestAuthentication("Ingreso al Sistema");
};
Serial.println("Enviando la data de estado.");
sistemaEstado["estado-sistema"] = ledEstado;
sistemaEstado["modo-sistema"] = modoSistema;
String json = "";
serializeJsonPretty(sistemaEstado, json);
Serial.println(json);
ultimaPaginaCargada = "/data-estado";
cuentaAcceso = true;
request->send(200, "application/json", json);
json = String();
Serial.println("Enviada la data de estado.");
});
В данный момент, если переменная ùltimaPaginaCargada
равна "/encender", это означает, что страница /encender была ранее загружена, поэтому она отправляет только "200" в веб-обозреватель, в противном случае "/encender" не был ранее загружен, и эта страница отправляется.
Ниже приведен код моего app.js:
function Actualiza_Estado() {
console.log("Entrando a Actualiza_Estado().");
var xxhr = new XMLHttpRequest();
if(this.readyState == 4 && this.status == 200)
{
console.log("Analizando los componentes reibidos en Actualiza_Estado()");
var estadoWeb = JSON.parse(this.responseText);
console.log(estadoWeb);
var estadoEnc = document.getElementById("estado-sistema");
estadoEnc.innerHTML = "<p>Estado Sistema: " + "<strong>" + `${estadoWeb.estado-sistema}` + "</strong>";
var modoEnc = document.getElementById("modo-sistema");
modoEnc.innerHTML = "<p>Modo Sistema: " + "<strong>" + `${modoEnc.modo-sistema}` + "</strong>";
console.log("Enviados los componentes a actualizar a la pantalla");
}
else
{
console.log("Contenido no cargado en Actualiza_Estado()");
}
xxhr.open("HTTP_GET", "/data-estado", true);
xxhr.send();
console.log("Solicitada la actualización del estado del sistema");
};
// Функции для отображения уровня резервуара на странице дисплея.
function Lee_Nivel_Tanque() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200)
{
var volTanqueWeb = JSON.parse(this.responseText);
console.log(volTanqueWeb);
var volActualWeb = document.getElementById("mask01");
volActualWeb.innerHTML = volTanqueWeb.colAgua.toFixed(2);
}
else
{
console.log("Contenido no cargado en Lee_Nivel_Tanque()");
}
};
xhr.open("HTTP_GET", "/data-tanque", true);
xhr.send();
console.log("Solicitada actualización del tanque.");
};
// Функции для отправки времени и даты в окно
function Fecha_Hora() {
let hoyFecha = new Date();
let hoyHora = hoyFecha.toLocaleTimeString();
let dia = hoyFecha.getDate();
let mes = hoyFecha.getMonth() + 1;
let anio = hoyFecha.getFullYear();
let diaSemana = hoyFecha.getDay();
dia = ('0' + dia).slice(-2);
mes = ('0' + mes).slice(-2);
let semana = ['DOMINGO', 'LUNES', 'MARTES', 'MIÉRCOLES', 'JUEVES', 'VIERNES', 'SÁBADO'];
let muestraSemana = semana[diaSemana];
let fechaCompleta = `${muestraSemana}-${dia}-${mes}-${anio}`;
document.getElementById("la-fecha").innerHTML = fechaCompleta;
document.getElementById("la-hora").innerHTML = hoyHora;
console.log(fechaCompleta + '->' + hoyHora);
};
window.onload = function() {
let intervaloVolumen;
let intervaloFecha;
intervaloFecha = setInterval(Fecha_Hora, 1000);
if((window.location.pathname === "/index") ||
(window.location.pathname === "/apagar") ||
(window.location.pathname === "/encender") ||
(window.location.pathname === "/auto") ||
(window.location.pathname === "/manual"))
{
//Обновить_Состояние();
intervaloVolumen = setInterval(Lee_Nivel_Tanque, 20000);
}
else
{
clearInterval(intervaloVolumen);
intervaloVolumen = null;
}
}
var datoEstado = document.getElementsByClassName("boton-control");
datoEstado.addEventListener("click", function(event) {
event.preventDefault();
Actualiza_Estado();
});
выше функций, которые, по моему мнению, имеют значение, это Àctualiza_Estado()
и var datoEstado
, datoEstado
по сути является прослушивателем, который, когда я нажимаю на любую из кнопок, созданных с помощью элементов <a>
, вызывает Àctualiza_Estado()
для запроса JSON-файла с состоянием системы.
В функции window.onload()
, если загружена какая-либо из кнопок или основная страница, она определяет интервал обновления параметра для считывания с датчика и каждые 20 секунд запрашивает JSON-файл с этими данными (это работает отлично).
заключается в том, что в функции server.on()
, связанной со страницей /encender
, я заставляю программу отправлять ответ 200
на запрос, чтобы заставить код ESP32 отправлять только файл JSON, вызывая функцию Actualiza_Estado()
с запросами /data-estado
, но, как я уже сказал, веб-обозреватель, похоже, получает пустой ответ.
Я внес изменение в функцию server.on()
, связанную со страницей /encender
в backend.cpp, чтобы принудительно отправлять JSON-файл напрямую оттуда, как показано ниже
server.on("/encender", HTTP_GET,[](AsyncWebServerRequest *request){
digitalWrite(ledPinSistemaApagado,LOW);
digitalWrite(ledPinSistemaEncendido,HIGH);
if(ultimaPaginaCargada == "/encender");
{
Serial.println("Enviando la data de estado.");
sistemaEstado["estado-sistema"] = ledEstado;
sistemaEstado["modo-sistema"] = modoSistema;
String json = "";
serializeJsonPretty(sistemaEstado, json);
Serial.println(json);
ultimaPaginaCargada = "/data-estado";
cuentaAcceso = true;
request->send(200, "application/json", json);
json = String();
}
ultimaPaginaCargada = "/encender";
cuentaAcceso = true;
request->send(SPIFFS,"/index.html", String(), false, Procesador);
});
В приведенном выше случае веб-браузер получает файл Json и отображает данные, но на пустой черной странице, а также показывает отформатированный файл Json.
Я не знаю, что происходит, потому что метод файла Json работает очень хорошо при использовании его для считывания показаний датчика с помощью Lee_Nivel_Tanque()
.
Что я могу сделать, чтобы решить эту проблему?
@vram, 👍2
Обсуждение1 ответ
Я не разбирал весь вопрос. Просто смотрю на код, там есть довольно много вопросов, которые заслуживают внимания.
В бэкэнде для /encender у вас есть
if(ultimaPaginaCargada == "/encender");
{
// ...
Обратите внимание на точку с запятой сразу после условия. В C++ голая точка с запятой
является пустым оператором (no-op). Здесь этот пустой оператор является телом
оператора if
. Следующий за ним блок в фигурных скобках выполняется
безусловно. Скорее всего, это не то, что вы имеете в виду.
request->send(200);
Не отправляйте пустой ответ 200. Если вы хотите сказать клиенту «ваш запрос успешно обработан, но мне нечего отправить вы», вам следует отправить ответ 204 No Content.
ultimaPaginaCargada = "/encender";
Веб-API должен быть без состояния. Если вам нужно запомнить последний страница загружена, весь ваш протокол, скорее всего, имеет изъяны.
В функции JavaScript Actualiza_Estado()
:
var xxhr = new XMLHttpRequest();
if (this.readyState == 4 && this.status == 200) {
Вторая строка здесь неуместна: она должна быть внутри
onreadystatechange
обработчик. Посмотрите, как написан Lee_Nivel_Tanque()
.
Редактировать: Еще несколько замечаний.
if(ultimaPaginaCargada == "/encender");
Ранее я писал: «Если вам нужно запомнить последнюю загруженную страницу, ваш Весь протокол, скорее всего, имеет изъяны». Вы могли неправильно это понять. точка. Проблема не в том, что сервер помнит, какая страница была загружено последним. Проблема в том, что его поведение зависит от этого информация. Ответ сервера не должен зависеть от того, какая страница была загружено последним.
request->send(SPIFFS, "/index.html", String(), false, Procesador);
Это выглядит подозрительно. Я не уверен, что полностью понимаю ваш подход, но, Из того немногого, что вы показали, мне кажется, что вы строите Веб-интерфейс пользователя для некоторого устройства в виде одностраничное приложение. При таком подходе у вас есть:
- Единственный URL (обычно корневой:
/
), который доставляет пользователя интерфейс в виде статического HTML-документа, возможно с встроенный JavaScript. - Ноль или более URL-адресов, которые предоставляют статические ресурсы, требуемые этим документ: JavaScript, изображения, шрифты…
- Одна или несколько конечных точек, которые позволяют вам взаимодействовать с устройством либо
извлечение динамических данных в формате JSON (конечные точки
GET
) или изменение состояние устройства (конечные точкиPOST
).
При таком подходе единственный случай, когда вы можете предоставить HTML-документ
в ответ на запрос GET /
. Конечная точка динамических данных должна
никогда не доставляйте index.html.
xxhr.open("HTTP_GET", "/data-estado", true);
Метод HTTP следует записывать как "GET"
, а не "HTTP_GET"
.
Привет @Edgar Bonet, спасибо за исправления, я не заметил, что оставил там точку с запятой, и спасибо за совет по поводу API без сохранения состояния, я внесу некоторые изменения и дам вам знать., @vram
и я должен отметить, что для Actualiza_Estado() мне не нужен временной интервал, кроме того, я знаю, что вы имеете в виду запоминание последней загруженной страницы в моем backend.cpp, в то время мне не нужно было запоминать страницы, я просто хотел сохранить последнюю загруженную страницу. Далее, функция Actualiza_Estado() предназначена для отображения на веб-странице состояния аппаратной части моего проекта, включена она или выключена и т. д., @vram
и спасибо за совет по ответу 204, @vram
- Использование ESPAsyncWebServer.h с ArduinoJson версии 6 для транзакций мастер-клиент
- ESP32 в Arduino-IDE с FS.h и SPIFFS
- Почему функция server.on() из "ESPAsyncWebServer.h" выполняется на стороне setup(), а не на стороне loop()?
- Интернет-сервер Arduino IDE + ESP32 — как перенаправить на root после нажатия кнопки
- Автоматическая веб-страница ESP32 AP
- Публиковать данные json в mqtt
- Точка доступа ESP32 и веб сервер HTTP, как отправить несколько параметров?
- Arduino uno R4 против платы разработчика ESP32
estadoWeb.estado-sistema
должен бытьestadoWeb["estado-sistema"]
, @dandavisПроблема, с которой вы столкнулись, скорее всего, связана с обработкой request->send(200); в конечной точке /encender. Вы уже решили проблему?, @tepalia
@tepalia да, я смогу решить эту проблему, основываясь на всех твоих советах, @vram