ESP8266WebServer - server send()

ki314
Offline
Зарегистрирован: 03.03.2017

В Arduino IDE->Примеры->ESP8266WebServer есть скетч AdvancedWebServer.ino

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const char *ssid = "YourSSIDHere";
const char *password = "YourPSKHere";

ESP8266WebServer server ( 80 );

const int led = 13;

void handleRoot() {
	digitalWrite ( led, 1 );
	char temp[400];
	int sec = millis() / 1000;
	int min = sec / 60;
	int hr = min / 60;

	snprintf ( temp, 400,

"<html>\
  <head>\
    <meta http-equiv='refresh' content='5'/>\
    <title>ESP8266 Demo</title>\
    <style>\
      body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
    </style>\
  </head>\
  <body>\
    <h1>Hello from ESP8266!</h1>\
    <p>Uptime: %02d:%02d:%02d</p>\
    <img src=\"/test.svg\" />\
  </body>\
</html>",

		hr, min % 60, sec % 60
	);
	server.send ( 200, "text/html", temp );
	digitalWrite ( led, 0 );
}

void handleNotFound() {
	digitalWrite ( led, 1 );
	String message = "File Not Found\n\n";
	message += "URI: ";
	message += server.uri();
	message += "\nMethod: ";
	message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
	message += "\nArguments: ";
	message += server.args();
	message += "\n";

	for ( uint8_t i = 0; i < server.args(); i++ ) {
		message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
	}

	server.send ( 404, "text/plain", message );
	digitalWrite ( led, 0 );
}

void setup ( void ) {
	pinMode ( led, OUTPUT );
	digitalWrite ( led, 0 );
	Serial.begin ( 115200 );
	WiFi.begin ( ssid, password );
	Serial.println ( "" );

	// Wait for connection
	while ( WiFi.status() != WL_CONNECTED ) {
		delay ( 500 );
		Serial.print ( "." );
	}

	Serial.println ( "" );
	Serial.print ( "Connected to " );
	Serial.println ( ssid );
	Serial.print ( "IP address: " );
	Serial.println ( WiFi.localIP() );

	if ( MDNS.begin ( "esp8266" ) ) {
		Serial.println ( "MDNS responder started" );
	}

	server.on ( "/", handleRoot );
	server.on ( "/test.svg", drawGraph );
	server.on ( "/inline", []() {
		server.send ( 200, "text/plain", "this works as well" );
	} );
	server.onNotFound ( handleNotFound );
	server.begin();
	Serial.println ( "HTTP server started" );
}

void loop ( void ) {
	server.handleClient();
}

void drawGraph() {
	String out = "";
	char temp[100];
	out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
 	out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
 	out += "<g stroke=\"black\">\n";
 	int y = rand() % 130;
 	for (int x = 10; x < 390; x+= 10) {
 		int y2 = rand() % 130;
 		sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
 		out += temp;
 		y = y2;
 	}
	out += "</g>\n</svg>\n";

	server.send ( 200, "image/svg+xml", out);
}

Работает он прекрасно.

Но когда я в функции void drawGraph()

void drawGraph() {
	String out = "";
	char temp[100];
	out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
 	out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
 	out += "<g stroke=\"black\">\n";
 	int y = rand() % 130;
 	for (int x = 10; x < 390; x+= 10) {
 		int y2 = rand() % 130;
 		sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
 		out += temp;
 		y = y2;
 	}
	out += "</g>\n</svg>\n";

	server.send ( 200, "image/svg+xml", out);
}

определяю данные размером более 2500 байт ( char temp[2500]; ) и впоследствии сервер отправляет их в ответ на обращение к вебсерверу (server.send ( 200, "image/svg+xml", out);) - esp в этот момент крашиться.

При размере данных вплоть до ~2500 байт - esp не крашится.

В функции void drawGraph()  я строю график на языке разметки svg, объем ~10кб

Подскажите, как правильно передавать такие объемы?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Код, который не работает секретен? Или почему не показали?

Думаю, что проблема с памятью. Попробуйте для начала избавиться от типа String. Тем более, от такого варванского его использования. Уверен, что поможет.

ki314
Offline
Зарегистрирован: 03.03.2017

Вот код:


void drawGraph() {
	
char temp1[2500];

snprintf ( temp1, 2500, 
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"1000\" height=\"330\">\
<line x1=\"20\" y1=\"0\" x2=\"20\" y2=\"310\" stroke=\"black\"/>\
<line x1=\"20\" y1=\"10\" x2=\"1000\" y2=\"10\" stroke=\"lightgray\"/>\
<line x1=\"20\" y1=\"110\" x2=\"1000\" y2=\"110\" stroke=\"lightgray\"/>\
<line x1=\"20\" y1=\"210\" x2=\"1000\" y2=\"210\" stroke=\"lightgray\"/>\
<line x1=\"20\" y1=\"310\" x2=\"1000\" y2=\"310\" stroke=\"black\"/>\
<text x=\"0\" y=\"15\" fill = \"red\">30</text>\
<text x=\"0\" y=\"115\" fill = \"red\">20</text>\
<text x=\"0\" y=\"215\" fill = \"red\">10</text>\
<text x=\"0\" y=\"315\" fill = \"red\">0</text>\
<text x=\"12\" y=\"325\">00</text>\
<text x=\"52\" y=\"325\">01</text>\
<text x=\"92\" y=\"325\">02</text>\
<text x=\"132\" y=\"325\">03</text>\
<text x=\"172\" y=\"325\">04</text>\
<text x=\"212\" y=\"325\">05</text>\
<text x=\"252\" y=\"325\">06</text>\
<text x=\"292\" y=\"325\">07</text>\
<text x=\"332\" y=\"325\">08</text>\
<text x=\"372\" y=\"325\">09</text>\
<text x=\"412\" y=\"325\">10</text>\
<text x=\"452\" y=\"325\">11</text>\
<text x=\"492\" y=\"325\">12</text>\
<text x=\"532\" y=\"325\">13</text>\
<text x=\"572\" y=\"325\">14</text>\
<text x=\"612\" y=\"325\">15</text>\
<text x=\"652\" y=\"325\">16</text>\
<text x=\"692\" y=\"325\">17</text>\
<text x=\"732\" y=\"325\">18</text>\
<text x=\"772\" y=\"325\">19</text>\
<text x=\"812\" y=\"325\">20</text>\
<text x=\"852\" y=\"325\">21</text>\
<text x=\"892\" y=\"325\">22</text>\
<text x=\"932\" y=\"325\">23</text>\
<circle cx=\"20\" cy=\"270\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"60\" cy=\"280\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"100\" cy=\"265\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"140\" cy=\"265\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"180\" cy=\"260\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"220\" cy=\"250\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"260\" cy=\"250\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"300\" cy=\"250\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"340\" cy=\"250\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
<circle cx=\"380\" cy=\"240\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"red\" />\
</svg>\n");

server.send ( 200, "image/svg+xml", temp1);
}

если еще увеличить размер temp1 и добавить строк - esp крашится.

ki314
Offline
Зарегистрирован: 03.03.2017

ЕвгенийП пишет:

Думаю, что проблема с памятью. Попробуйте для начала избавиться от типа String. Тем более, от такого варванского его использования. Уверен, что поможет.

Делаю как в примере - ни шага в сторону :)

Если на вывод идет текст - как же его пихать, если не как String?

andriano
andriano аватар
Онлайн
Зарегистрирован: 20.06.2015

ki314 пишет:

Делаю как в примере - ни шага в сторону :)

Пример - он на то и пример, чтобы показать принцип, используя минимум строк кода. Кроме того, в примере работы с единственным устройством, как правило, ресурсов МК хватает с избытком, так что ради понятности кода эти ресурсы зачастую приносятся в жертву.

В реальном проекте оптимизировать следует по совсем иным принципам.

Цитата:

Если на вывод идет текст - как же его пихать, если не как String?

Вообще-то в Ардуино целесообразнее применять char*. Но если касается вывода - то выводить побайтно можно сообщения вообще неограниченной длины. Т.е. не надо ждать, пока накопится посылка целиком, либо составлять длинную строку из отдельных кусочков: каждый из ее фрагментов выводится побайтно один за другим. Расход ресурсов - 1 байт.

ki314
Offline
Зарегистрирован: 03.03.2017

не победил этот snprintf и server.send но хотелось бы. если кто-то сможет подсказать - буду благодарен.

использую

server.sendContent("<html><head><body><h1>Hello from ESP8266!</h1>");
server.sendContent("<svg width=\"1000\" height=\"330\">");
server.sendContent("<line x1=\"20\" y1=\"0\" x2=\"20\" y2=\"310\" stroke=\"black\"/>");
server.sendContent("<line x1=\"20\" y1=\"10\" x2=\"1000\" y2=\"10\" stroke=\"lightgray\"/>");
server.sendContent("<line x1=\"20\" y1=\"110\" x2=\"1000\" y2=\"110\" stroke=\"lightgray\"/>");
server.sendContent("<line x1=\"20\" y1=\"210\" x2=\"1000\" y2=\"210\" stroke=\"lightgray\"/>");
server.sendContent("<line x1=\"20\" y1=\"310\" x2=\"1000\" y2=\"310\" stroke=\"black\"/>");
server.sendContent("</svg>");
server.sendContent("</body></html>");

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

ki314 пишет:

Делаю как в примере - ни шага в сторону :)

Сомневаюсь. 

Объект/класс String я не мучал, поэтому не знаю насколько корректно передавать вместо указателя на него - указатель на char* , а ведь в https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WebServer/src/ESP8266WebServer.h написано, что нет оверлоада функции send(), который принимал бы указатель на char[]... Однако там есть прекрасный вариант - send_P(), который позволяет засылать строку прямо с PROGMEM (наверняка).

ki314
Offline
Зарегистрирован: 03.03.2017

Спасибо. Но за плечами у меня только программирование на паскале и ассемблере в школе (цать лет назад). Ниче не понял :-\

Видимо в моем случае костыли - наше все.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

http://www.esp8266.com/viewtopic.php?f=29&t=2417 - в первом посте видите объявление с PROGMEM? Делайте такое же и .send_P(200, "image/svg+xml", имя_progmem_переменной_в_которой_лежит_svg_картинка);

Возможно, что всё будет ОК.

ki314
Offline
Зарегистрирован: 03.03.2017

Что-то лыжи не едут...

Скопировал кусок кода, который приводит автор по ссылке и вставил в пустой скетч.

Данный код выдает ошибку "Ошибка компиляции для платы Generic ESP8266 Module"

void setup() {}

void loop() 
{
 const char index_html[] PROGMEM = R"=====(
<!DOCTYPE HTML>
<html>
<head><title>ESP8266 Arduino Demo Page</title></head>
<body>ESP8266 power!<p><img src="logo.png"></body>
</html>
)=====";
}

Подробный текст ошибки (вдруг что-то полезное увидите):

Arduino: 1.8.5 (Windows 7), Плата:"Generic ESP8266 Module, 80 MHz, ck, 26 MHz, 40MHz, QIO, 512K (no SPIFFS), v2 Prebuilt (MSS=536), Disabled, None, 115200"
In file included from C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0\cores\esp8266/esp8266_peri.h:24:0,
                 from C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0\cores\esp8266/Arduino.h:38,
                 from sketch\my-svg.ino.ino.cpp:1:
Z:\Скетчи\my-svg\my-svg.ino\my-svg.ino.ino: In function 'void handleRoot()':
C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0/tools/sdk/include/c_types.h:89:66: error: section attribute cannot be specified for local variables
 #define ICACHE_RODATA_ATTR  __attribute__((section(".irom.text")))
                                                                  ^
C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0\cores\esp8266/pgmspace.h:13:21: note: in expansion of macro 'ICACHE_RODATA_ATTR'
 #define PROGMEM     ICACHE_RODATA_ATTR
                     ^
Z:\Скетчи\my-svg\my-svg.ino\my-svg.ino.ino:31:26: note: in expansion of macro 'PROGMEM'
exit status 1
Ошибка компиляции для платы Generic ESP8266 Module.
 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ki314 пишет:

Что-то лыжи не едут...

Да, нет, скорее второе. Кто Вас так учил символьные константы писать?

 const char index_html[] PROGMEM = "=====("
	"<!DOCTYPE HTML>"
	"<html>"
	"<head><title>ESP8266 Arduino Demo Page</title></head>"
	"<body>ESP8266 power!<p><img src=\"logo.png\"></body>"
	"</html>"
	")=====";

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Ну... сделайте переменную глобальной, например.

https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

Please note that variables must be either globally defined, OR defined with the static keyword, in order to work with PROGMEM.
The following code will NOT work when inside a function...
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, лучше static если она глобально не нужна. Нафига глобальное прострнство имён засорять?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

ЕвгенийП пишет:

Да, нет, скорее второе. Кто Вас так учил символьные константы писать?

Это на форуме по ESP так нарисовано. Я тоже долго на это пялился, но так и не понял зачем так. Может быть специфика ESP-шного кода...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нет, это у них длинаая строка перенеслась в движке на форуме. А Вы не поправили. Надо её или одной строкой делать, либо, если уж разбивать, то так, как я написал - на каждой строке в своих кавычках и никаких символов (кроме комменятариев) между ними.

zalias7
Offline
Зарегистрирован: 18.03.2015

ki314 пишет:

......

При размере данных вплоть до ~2500 байт - esp не крашится.

.....

 

http://uzebox.org/wiki/ESP8266_AT_Commands  

AT+CIPSEND   send data: AT+CIPSEND=4,15 and then enter the data MAX 2048 bytes

sadman41
Онлайн
Зарегистрирован: 19.10.2016

ЕвгенийП пишет:

Нет, это у них длинаая строка перенеслась в движке на форуме. А Вы не поправили. Надо её или одной строкой делать, либо, если уж разбивать, то так, как я написал - на каждой строке в своих кавычках

Мультилайн еще можно забацать, насколько я помню. Но вот еще там какой-то префикс R прилеплен. Не знаю, что это означает.

ki314
Offline
Зарегистрирован: 03.03.2017


Подскажите глупому, как отправить русский текст, если отправляю так, то вместо русских букв в браузере отображаются кракозябры.

server.sendContent("<html><head></head><body><h1>Привет</h1></body></html>");

Jeka_M
Jeka_M аватар
Онлайн
Зарегистрирован: 06.07.2014

Есть тег <meta>, в котором можно указать кодировку для браузера - http://htmlbook.ru/html/meta/charset

Поиграйтесь с кодировками: utf-8, windows-1251