Веб-сервер Arduino: более быстрая альтернатива «indexof» для разбора запросов GET?
У меня есть веб-сервер, успешно работающий на Arduino Mega, но я пытаюсь убедиться, что он анализирует запросы GET как можно быстрее. Вот как выглядит соответствующий код для запросов GET:
String readString;
EthernetClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (readString.length() < 20) {
readString += c;
}
...
...
if (readString.indexOf("?a1o") >0){
mySwitch.send(a1o, 24);
}
if (readString.indexOf("?a1c") >0){
mySwitch.send(a1c, 24);
}
Скетч проверяет 40 возможных переменных GET. Я запускаю его как локальный веб-сервер с адресом: http://192.168.0.180.
-Есть ли команда быстрее, чем "indexOf", которую я мог бы использовать?
-Я видел некоторую информацию, предполагающую, что иногда "parseInt" может быть быстрее. Есть ли способ заставить это работать, даже если мой локальный базовый URL уже заполнен целыми числами? Я был бы готов преобразовать команды в целые числа (например, «11» вместо «a1o»), если бы был способ использовать с ним parseInt или другую более быструю команду.
Спасибо!
@Jerry, 👍3
Обсуждение1 ответ
Лучший ответ:
Ну, для быстрого разбора я бы не стал использовать String для начала. Я написал пример (ниже), который избавляется от класса String, а также демонстрирует однократный синтаксический анализ строки.
#include <SPI.h>
#include <Ethernet.h>
// Введите ниже MAC-адрес и IP-адрес вашего контроллера.
byte mac[] = { 0xB3, 0x8D, 0x72, 0x1D, 0xCE, 0x91 };
// Наш IP-адрес
IPAddress ip(10,0,0,241);
// Инициализируем библиотеку сервера Ethernet
// с IP-адресом и портом, который вы хотите использовать
// (порт 80 по умолчанию для HTTP):
EthernetServer server(80);
void setup()
{
// Открытие последовательной связи и ожидание открытия порта:
Serial.begin(115200);
while (!Serial) { } // ждем подключения последовательного порта.
// запускаем соединение Ethernet и сервер:
Ethernet.begin(mac, ip);
server.begin();
Serial.print(F("Server is at "));
Serial.println(Ethernet.localIP());
} // конец настройки
// сколько последовательных данных мы ожидаем перед новой строкой
const unsigned int MAX_INPUT = 100;
// максимальная длина принимаемых параметров
const int MAX_PARAM = 10;
// Пример строки GET: GET /?foo=bar HTTP/1.1
void processGet (const char * data)
{
// найти, где начинаются параметры
const char * paramsPos = strchr (data, '?');
if (paramsPos == NULL)
return; // без параметров
// найти пробел в конце
const char * spacePos = strchr (paramsPos, ' ');
if (spacePos == NULL)
return; // место не найдено
// определить длину параметров
int paramLength = spacePos - paramsPos - 1;
// смотрим, не слишком ли длинно
if (paramLength >= MAX_PARAM)
return; // слишком долго для нас
// копируем параметры в буфер
char param [MAX_PARAM];
memcpy (param, paramsPos + 1, paramLength); // пропустить "?"
param [paramLength] = 0; // нулевой терминатор
// делаем что-то в зависимости от аргумента (параметры GET)
if (strcmp (param, "foo") == 0)
Serial.println (F("Activating foo"));
else if (strcmp (param, "bar") == 0)
Serial.println (F("Activating bar"));
} // конец процессаGet
// здесь для обработки входящих последовательных данных после получения терминатора
void processData (const char * data)
{
Serial.println (data);
if (strlen (data) < 4)
return;
if (memcmp (data, "GET ", 4) == 0)
processGet (&data [4]);
} // конец данных процесса
bool processIncomingByte (const byte inByte)
{
static char input_line [MAX_INPUT];
static unsigned int input_pos = 0;
switch (inByte)
{
case '\n': // конец текста
input_line [input_pos] = 0; // завершающий нулевой байт
if (input_pos == 0)
return true; // получили пустую строку
// терминатор достигнут! обработать input_line здесь...
processData (input_line);
// сброс буфера для следующего раза
input_pos = 0;
break;
case '\r': // отменить возврат каретки
break;
default:
// продолжаем добавлять, если не полный ... разрешить завершающий нулевой байт
if (input_pos < (MAX_INPUT - 1))
input_line [input_pos++] = inByte;
break;
} // конец переключателя
return false; // еще нет пустой строки
} // конец процессаIncomingByte
void loop()
{
// прослушивание входящих клиентов
EthernetClient client = server.available();
if (client)
{
Serial.println(F("Client connected"));
// HTTP-запрос заканчивается пустой строкой
boolean done = false;
while (client.connected() && !done)
{
while (client.available () > 0 && !done)
done = processIncomingByte (client.read ());
} // конец, пока клиент подключен
// отправляем стандартный HTTP-заголовок ответа
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println(F("Connection: close")); // закрыть после завершения ответа
client.println(); // конец заголовка HTTP
client.println(F("<!DOCTYPE HTML>"));
client.println(F("<html>"));
client.println(F("<head>"));
client.println(F("<title>Test page</title>"));
client.println(F("</head>"));
client.println(F("<body>"));
client.println(F("<h1>My web page</h1>"));
client.println(F("<p>Requested actions performed"));
client.println(F("</body>"));
client.println(F("</html>"));
// дать веб-браузеру время для получения данных
delay(10);
// закрыть соединение:
client.stop();
Serial.println(F("Client disconnected"));
} // конец полученного нового клиента
} // конец цикла
Входящие строки от клиента фиксируются в статическом буфере. Это устраняет проблемы с фрагментацией памяти (вызванные использованием String).
processIncomingByte
вызывается для каждого байта от клиента. После сборки всей строки вызывается processData
. Если он находит строку «GET», он вызывает processGet
. Это ищет параметры в строке GET. Строка GET выглядит так:
GET /?bar HTTP/1.1
Итак, нам нужно попасть между "?" и следующий пробел, чтобы получить параметр (в данном случае "bar"). Этого можно добиться, найдя их позиции с помощью strchr
.
Наконец (в качестве простого примера) я проверил с помощью strcmp прямое совпадение различных слов. Возможно, вы могли бы сделать это более эффективным, но я сомневаюсь, что это будет иметь большое значение.
Я только что внедрил эти изменения, и это заметно НА ТОННУ быстрее!!!!! Я так счастлив с этим! Большое спасибо за помощь!, @Jerry
Я преобразовал все свои параметры GET в целые числа, чтобы можно было использовать переключатель. Я преобразовал "param" в целое число следующим образом: param2 = atoi(param); а затем я сделал «переключатель (парам2);». Не могли бы вы сказать мне, если это плохой способ делать вещи? Спасибо., @Jerry
Это нормально, это даст вам небольшое увеличение скорости. Если вам понравился мой ответ, пожалуйста, «примите» его, нажав на галочку рядом с ним. Это позволяет другим пользователям узнать, что ответ был полезен. Спасибо!, @Nick Gammon
Спасибо! JSYK, насколько быстрее был ваш код: раньше требовалось 2-3 секунды после нажатия на веб-ссылку, прежде чем активировались беспроводные переключатели. Теперь это занимает всего 200-300 мс. Это очень полезно для меня., @Jerry
- Несколько клиентских серверов через Wi-Fi
- WebSocketsServer.h: No such file or directory
- Как получить данные из базы данных моего сервера в переменную в моем Arduino?
- Как получить параметры запроса от ESPAsyncWebServer?
- контент» не захватывается
- ESP32 в Arduino-IDE с FS.h и SPIFFS
- Как разрешить междоменные запросы на ESP8266 WebServer
- Почему функция server.on() из "ESPAsyncWebServer.h" выполняется на стороне setup(), а не на стороне loop()?
Можете ли вы опубликовать пример типа ввода, который вы обрабатываете? Возможно, это:
http://192.168.0.180?a1o
**или**http://192.168.0.180?a1c
, или их может быть несколько в строке GET?, @Nick GammonМожет быть, укажите, что это за 40 переменных GET. Есть ли образец? Например. а1о, а1с, а2о, а2с, а3о, а3с...?, @Nick Gammon
да, именно так это и выглядит: 192.168.0.180?a1o, @Jerry
Есть шаблон, но он не должен быть таким. В настоящее время идет: a1o, a1c, a2o, a2c и т. д. до a5o, a5c. Затем начинается: b1o, b1c и т. д. («o» означает «открыть», а «c» означает «закрыть», это открытие и закрытие различных беспроводных выходов). У меня не было бы проблем с переименованием их всех, например, просто присвоением им всех номеров или четкого шаблона, если бы был способ ускорить синтаксический анализ., @Jerry
Извлеките строку запроса один раз и сохраните ее в переменной. Затем проверьте, является ли эта переменная «a1o» или «a1c» и т. д. Использование [switch-case](https://www.arduino.cc/en/Reference/SwitchCase) будет быстрее, чем целый список операторов if., @Gerben
Спасибо Гербен! Я попытаюсь использовать приведенный ниже код Гаммона, но с добавлением вашего совета о переключателе., @Jerry
Я знаю, что это очень старый пост, но не могли бы вы показать, как анализировать метод POST с вашим кодом? Это должно быть очень полезно для начинающих, как я :-) Большое спасибо! РРТ, @user58497