Как создать минимальный пример веб-сервера с выпадающим меню?
Я хочу создать меню из трех пунктов: Verizon, T-Mobile, AT&T. Поскольку в примере используются светодиоды, я сделаю то же самое, но с выпадающим меню для выбора из трех.
Я пытаюсь объединить эти два примера:
- https://randomnerdtutorials.com/esp8266-web-server
- https://www.w3schools.com/html/tryit.asp?filename=tryhtml_elem_select
За исключением случаев, когда вместо автомобилей используются операторы мобильной связи:
Я с трудом справляюсь с полиглотной частью программирования, совмещая в середине программу HTML и Arduino:
// Заголовок веб-страницы
client.println("<form action='/action_page.php'>");
client.println(" <select id='providor' name='providor'>");
client.println(" <option value='verizon'>Verizon</option>");
client.println(" <option value='tmobile'>T-Mobile</option>");
client.println(" <option value='atnt'>AT&T</option>");
client.println(" </select>");
client.println(" <input type='submit'>");
client.println("</form>");
if (providor==verizon)
client.println("<p>Provider Selected: Verizon</p>");
else if (providor==tmobile)
client.println("<p>Provider Selected: T-Mobile</p>");
else if (providor==atnt)
client.println("<p>Provider Selected: AT&T</p>");
Вот остальная часть программы:
/*
Based from
https://randomnerdtutorials.com/esp8266-web-server
HTML tool
https://www.w3schools.com/html/tryit.asp?filename=tryhtml_elem_select
*/
#include <ESP8266WiFi.h>
// Замените своими сетевыми учетными данными
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// Установите номер порта веб-сервера на 80
WiFiServer server(80);
// Переменная для хранения HTTP-запроса
String header;
// Вспомогательные переменные для хранения текущего состояния вывода
//Строка вывода5State = "выключено";
//String output4State = "off";
// Назначаем выходные переменные контактам GPIO
const int output5 = 14; // версия D5
const int output6 = 12; // D6 мобильный
const int output7 = 13; // D7 внимание
// Текущее время
unsigned long currentTime = millis();
// Прошлый раз
unsigned long previousTime = 0;
// Определить время тайм-аута в миллисекундах (пример: 2000 мс = 2 с)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Инициализируем выходные переменные как выходные данные
pinMode(output5, OUTPUT);
pinMode(output6, OUTPUT);
pinMode(output7, OUTPUT);
// Установка выходов на НИЗКИЙ уровень
digitalWrite(output5, LOW);
digitalWrite(output6, LOW);
digitalWrite(output7, LOW);
// Подключаемся к сети Wi-Fi с SSID и паролем
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Распечатываем локальный IP-адрес и запускаем веб-сервер
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop()
{
WiFiClient client = server.available(); // Прослушиваем входящих клиентов
if (client) { // Если подключается новый клиент,
Serial.println("New Client."); // распечатываем сообщение в последовательный порт
String currentLine = ""; // создаем строку для хранения входящих данных от клиента
currentTime = millis();
previousTime = currentTime;
while (client.connected() && currentTime - previousTime <= timeoutTime) { // цикл, пока клиент подключен
currentTime = millis();
if (client.available()) { // если есть байты, которые нужно прочитать от клиента,
char c = client.read(); // читаем байт, затем
Serial.write(c); // распечатываем его на последовательном мониторе
header += c;
if (c == '\n') { // если байт является символом новой строки
// если текущая строка пуста, значит, вы получили два символа новой строки подряд.
// это конец HTTP-запроса клиента, поэтому отправьте ответ:
if (currentLine.length() == 0) {
// HTTP-заголовки всегда начинаются с кода ответа (например, HTTP/1.1 200 OK)
// и тип контента, чтобы клиент знал, что произойдет, затем пустая строка:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// включает и выключает GPIO
if (header.indexOf("GET /5/on") >= 0)
{
Serial.println("D5 on");
output5State = "on";
digitalWrite(output5, HIGH);
}
else if (header.indexOf("GET /5/off") >= 0)
{
Serial.println("D5 off");
output5State = "off";
digitalWrite(output5, LOW);
}
else if (header.indexOf("GET /6/on") >= 0)
{
Serial.println("D6 on");
output4State = "on";
digitalWrite(output6, HIGH);
}
else if (header.indexOf("GET /6/off") >= 0)
{
Serial.println("D6 off");
output4State = "off";
digitalWrite(output6, LOW);
}
else if (header.indexOf("GET /7/on") >= 0)
{
Serial.println("D7 on");
output4State = "on";
digitalWrite(output7, HIGH);
}
else if (header.indexOf("GET /7/off") >= 0)
{
Serial.println("D7 off");
output4State = "off";
digitalWrite(output7, LOW);
}
// Отображение веб-страницы HTML
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS для оформления кнопок включения/выключения
// Не стесняйтесь изменять атрибуты background-color и font-size в соответствии с вашими предпочтениями.
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #77878A;}</style></head>");
// ЗДЕСЬ
// ↓
// ↓
// ↓
// ↓
// Заголовок веб-страницы
client.println("<form action='/action_page.php'>");
client.println(" <select id='provider' name='providor'>");
client.println(" <option value='verizon'>Verizon</option>");
client.println(" <option value='tmobile'>T-Mobile</option>");
client.println(" <option value='atnt'>AT&T</option>");
client.println(" </select>");
client.println(" <input type='submit'>");
client.println("</form>");
if (providor==verizon)
client.println("<p>Provider Selected: Verizon</p>");
else if (providor==tmobile)
client.println("<p>Provider Selected: T-Mobile</p>");
else if (providor==atnt)
client.println("<p>Provider Selected: AT&T</p>");
// ↑
// ↑
// ↑
// ↑
client.println("</body></html>");
// Ответ HTTP заканчивается еще одной пустой строкой
client.println();
// Выходим из цикла while
break;
} else { // если появилась новая строка, то очистим currentLine
currentLine = "";
}
} else if (c != '\r') { // если у вас есть что-то еще, кроме символа возврата каретки,
currentLine += c; // добавляем его в конец текущей строки
}
}
}
// Очищаем переменную заголовка
header = "";
// Закрываем соединение
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
Есть предложения или решения по изменению или правильному коду?
@adamaero, 👍-1
Обсуждение2 ответа
Во-первых: будьте осторожны при сопоставлении цитат. Каждый строковый литерал
следует писать как "бла, бла, бла"
, без неэкранированных двойных кавычек
между. Обязательно используйте подсветку синтаксиса: она здесь именно для
помогая вам обнаружить такого рода тривиальные ошибки программирования. Кроме того, для
при написании очень длинных константных строк используйте «необработанный строковый литерал» C++.
особенность: это упрощает задачу.
Во-вторых, вы должны понимать, как работает отправка формы HTTP-уровень. Вы должны быть в состоянии найти информацию в Интернете, но чтобы сделать Короче говоря, когда пользователь нажимает на вашу форму, браузер отправляет запрос выглядит так
GET /action_page.php?providor=verizon HTTP/1.1
Some-Header-Name: some header value
Other-Header-Name: other header value
...
Вам следует проанализировать это, чтобы найти фактического поставщика между '='
символ и следующий пробел. Также избавьтесь от action_page.php
,
здесь это бесполезно.
Тогда несколько случайных замечаний:
Не пишите «поставщик»: правильное написание — «поставщик» (хотя в этом контексте вы можете предпочесть «перевозчик»).
В HTML-документе амперсанд должен быть записан в виде
&
.Чтобы HTML-страница была допустимой, необходим элемент
<title>
.Удалите весь раздел кода под названием «включает GPIO и выключено»: это не имеет смысла в вашем контексте.
Теперь, чтобы проиллюстрировать эти моменты, я бы описал код, анализирующий запрос:
// Найдите поставщика по URL-адресу.
String provider;
const String provider_before = "GET /?provider=";
int request_begin = header.indexOf(provider_before);
if (request_begin < 0) {
provider = "unknown";
} else {
int provider_begin = request_begin + provider_before.length();
int provider_end = header.indexOf(' ', provider_begin);
provider = header.substring(provider_begin, provider_end);
}
А вот как бы я отправил страницу клиенту:
// В начале скетча (в глобальном контексте).
const char page_before_provider[] = R"rawstr(<!DOCTYPE html>
<html>
<head>
<title>Phone carrier selection</title>
[...]
<p>Provider Selected: )rawstr";
const char page_after_provider[] = "</p>\n</body>\n</html>\n";
// В том месте, куда должна быть отправлена страница.
client.print(page_before_provider);
if (provider == "verizon")
client.print("Verizon");
else if (provider == "tmobile")
client.print("T-Mobile");
else if (provider == "atnt")
client.print("AT&T");
else
client.print(provider);
client.print(page_after_provider);
Меня смущает глобальная константа char. Это функция, которую я должен создать? Попробовал добавить что-то из этого, но довольно запутался: https://github.com/adamaero/WebServer/blob/main/polyglot/ESP8266dropDownMenu/ESP8266dropDownMenu.ino, @adamaero
@adamaero: Нет, не в функции. «Глобальный контекст» означает вне какой-либо функции. Это объявление константы, как и ssid, которое нужно поместить в начало программы. PS: не беспокойтесь об этом до тех пор, пока не решите первую, самую важную проблему: **согласовать ваши котировки**., @Edgar Bonet
Котировки совпали., @adamaero
Поскольку все текущие ответы мне не ясны, я попробую придумать свой с помощью const char page_before_provider[] = R"rawstr(<!DOCTYPE html>
)rawstr";
, @adamaero
@adamaero: 1. Считаете ли вы, что кавычки совпадают в client.println("<p>выбран оператор связи: T-Mobile" </ p > ");
? 2. Если что-то неясно в этом ответе, сообщите, пожалуйста, что именно вам не понятно, поэтому я могу уточнить этот конкретный момент., @Edgar Bonet
Хорошо, теперь кавычки совпадают... в псевдокоде, который изначально ничего не делает., @adamaero
Если не считать синтаксических ошибок (вы не используете двойные кавычки в нескольких местах), веб-страницы так не работают.
Чтобы сделать что-то вроде вашей формы, вы:
Отправьте форму примерно так же, как и вы.
-
if (providor==verizon) client.println("<p>Providor Selected: Verizon</p>");
Проверить провайдера (провайдера?) тут же нельзя. Вы имеете в виду
if (provider=="verizon")
? Форма должна быть отправлена клиенту, пользователь должен ее заполнить и отправить обратно. Таким образом, вы не можете проверить, что они заполнили в строке или около того при отправке формы.
Итак, тестирование того, что они ввели, — это следующий шаг. Это немного сложно объяснить, не рассмотрев десятки абзацев. Советую прочитать, как работают веб-формы.
Вот аналогия:
Предположим, вы хотите написать в компанию и подать заявку на работу. Вы заполняете форму и отправляете ее. Через мгновение вы не проверите, получили ли вы работу. Форма должна быть отправлена в компанию. Кто-то должен это обработать. Затем они дают ответ. В ответе может быть сказано: «В отношении вашего заявления о приеме на работу от 11 сентября 2023 года вы были/не удовлетворены».
То же самое и с HTML. Форма, которую заполняет пользователь, является начальным шагом. Затем пользователь заполняет его. Это может быть час спустя. Затем они нажимают «Отправить», и вам возвращается ответ. Это время обработки ответа.
- Как читать и записывать EEPROM в ESP8266
- Как сделать выводы Tx и Rx на ESP-8266-01 в выводах GPIO?
- Как навсегда изменить скорость передачи данных ESP8266 (12e)?
- Как заставить 5-вольтовое реле работать с NodeMCU
- Как исправить: Invalid conversion from 'const char*' to 'char*' [-fpermissive]
- ESP8266 не подключается к Wi-Fi
- AT-команда не отвечает на последовательный монитор
- Разница между этими двумя платами NodeMCU?
что вы ожидаете от выпадающего меню?, @jsotola
Измените переменную «поставщика» на любую выбранную. И в этом случае включить соответствующий ему светодиод (должен гореть один из трех светодиодов)., @adamaero
поместите HTML-код раскрывающегося меню сразу после второй кнопки, @jsotola
Я использую
<script>document.getElementById('providor').selectedIndex = 'tmobile';</script>
, @JurajПосмотрите код в вашем посте. Он выделен синтаксисом, т.е. имеет цветовую кодировку в соответствии с грамматикой языка C++. Разве вы не видите что-то странное в цветах вокруг «ЗДЕСЬ»? Это индикатор синтаксиса, показывающий вашу синтаксическую ошибку., @Edgar Bonet
И далее в первом блоке кода. Ваши цитаты не совпадают повсюду., @Nick Gammon