- Web - сервер на STM32F4Discovery для удаленного управления

Web – сервер на платах STM32F4Discovery и STM32F4DIS-BB для удаленного управления по TCP/IP сети

Практика для студентов. Мясищев А.А.

Во 2-й статье рассматривался сервер, который позволял отображать лишь статические web - странички. Рассмотрим создание сервера на основе демонстрационного примера, который выполняет четырёхканальное управление удаленными устройствами (4-я светодиодами).

Добавим к предыдущей web – странички 4-е кнопки, которые позволяют включать и выключать независимые устройства (4-е светодиода, установленные на платеSTM32F4Discovery). Причем на web – страничке должна появляться информация о включенных и выключенных каналах (светодиодах).

Но перед этим модернизируем demo программу web – сервера таким образом, чтобы можно было компилировать ее на IAR Embedded Workbench for ARM, но с лицензией Kickstart, которая ограничивает размер кода программ 32килобайтами. Это можно сделать, так как в примере задействовано использование LCD, протоколDHCP.

Удаляем LCD. Для его выключения заходим в main.h и закомментируем USE_LCD:

Удаляем файл stm32f4_discovery_lcd.c

В файле stm32f4x7_eth_bsp.c закомментируем 76, 77, 78 строки:

Аналогично удаляем файл dhcp.c, а в файле etharp.c закомментируем 770 строку.

После этих корректировок размер кода будет равен 30918 Байт. Максимальный размер кода программы не должен превышать 32768 Байт. Таким образом, остается 32768-30918 = 1850 Байт свободного кода для модернизации программы.

Сформируем на начальной страничке index.html 4-е кнопки с использованием тега FORM и метода передачи GET:

<HTML>

<HEAD>

<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />

<TITLE>Пример web - сервера на STM32F4Discovery</TITLE>

</HEAD>

<BODY>

<H2>Ниже показано фото платы STM32F4Discovery</H2>

<img src="foto_stm32.jpg">

<br><br>

<table>

<tr>

<td>

<B><font color="green">LED1 ON</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led1" value="1" size=2>

<INPUT type="submit" value="LED1 ON"> </FORM>

</td>

<td>

<B><font color="red">LED2 ON</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led2" value="1" size=2>

<INPUT type="submit" value="LED2 ON"> </FORM>

</td>

<td>

<B><font color="blue">LED3 ON</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led3" value="1" size=2>

<INPUT type="submit" value="LED3 ON"> </FORM>

</td>

<td>

<B><font color="broun">LED4 ON</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led4" value="1" size=2>

<INPUT type="submit" value="LED4 ON"> </FORM>

</td>

</tr>

<tr>

<td>

<B><font color="green">LED1 OFF</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led10" value="0" size=2>

<INPUT type="submit" value="LED1 OFF"> </FORM>

</td>

<td>

<B><font color="red">LED2 OFF</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led20" value="0" size=2>

<INPUT type="submit" value="LED2 OFF"> </FORM>

</td>

<td>

<B><font color="blue">LED3 OFF</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led30" value="0" size=2>

<INPUT type="submit" value="LED3 OFF"> </FORM>

</td>

<td>

<B><font color="broun">LED4 OFF</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led40" value="0" size=2>

<INPUT type="submit" value="LED4 OFF"> </FORM>

</td>

</tr>

</table>

</BODY>

</HTML>

Как и в предыдущем случае поместим этот файл под именем index.html в каталог fs, где находится рисунок foto_stm32.jpg. Запустим утилиту makefsdata.exe, которая сформирует файл fsdata.c Скопируем этот файл в каталог D:\STM32F4DIS-BB Discover More Software Examples\STM32F4DIS-BB Discover More Software Examples\STM32F4xx_Ethernet_Example\Project\Standalone\httpserver\src Перед компиляцией программы отредактируем файл httpd_cgi_ssi.c. Меняем всю выделенную функцию:

на представленную ниже:

const char * LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])

{

uint32_t i=0;

STM_EVAL_LEDInit(LED3);

STM_EVAL_LEDInit(LED4);

STM_EVAL_LEDInit(LED5);

STM_EVAL_LEDInit(LED6);

/* We have only one SSI handler iIndex = 0 */

if (iIndex==0) {

/* Check cgi parameter : example GET /leds.cgi?led=2&led=4 */

// Проверяем параметры CGI

for (i=0; i<iNumParams; i++) {

/* check parameter "led" */

if (strcmp(pcParam[i] , "led1")==0) {

STM_EVAL_LEDOn(LED4); j0=1;

}

else if (strcmp(pcParam[i] , "led2")==0) {

STM_EVAL_LEDOn(LED5); j1=1;

}

else if (strcmp(pcParam[i] , "led3")==0) {

STM_EVAL_LEDOn(LED6); j2=1;

}

else if (strcmp(pcParam[i] , "led4")==0) {

STM_EVAL_LEDOn(LED3); j3=1;

}

else if (strcmp(pcParam[i] , "led10")==0) {

STM_EVAL_LEDOff(LED4); j0=2;

}

else if (strcmp(pcParam[i] , "led20")==0) {

STM_EVAL_LEDOff(LED5);j1=2;

}

else if (strcmp(pcParam[i] , "led30")==0) {

STM_EVAL_LEDOff(LED6); j2=2;

}

else if (strcmp(pcParam[i] , "led40")==0) {

STM_EVAL_LEDOff(LED3); j3=2;

}

}

}

/* uri to send after cgi call*/

return "/index.html";

}

И добавляем описание переменных в начале файла httpd_cgi_ssi.c:

#include <string.h>

#include <stdlib.h>

tSSIHandler ADC_Page_SSI_Handler;

uint32_t ADC_not_configured=1;

uint32_t j0=0,j1=0,j2=0,j3=0; // Переменные для кнопок

Далее компилируем программу и загружаем ее в микроконтроллер. При подключении по адресу http://192.168.1.10:8080 в браузере появится следующее изображение:

Недостатком сервера с удаленным управлением является отсутствие информации о том, какое устройство находится во включенном и выключенном состоянии. Необходимо изменить программу так, чтобы под кнопками высвечивалась надпись, в каком состоянии находятся светодиоды (включен или выключен). Для этого можно использовать технологию SSI (Server Side Include), которую поддерживает demo сервер. По этой технологии сервер работает следующим образом. Если в htmlдокументе существует строка вида:

<!--#t-->

которую называют tag, то вместо нее будет подставлена строка, которая записана в буфере *pcInsert.

Выбор символа для tag прописан в файле httpd_cgi_ssi.c:

/* we will use character "t" as tag for CGI */

char const* TAGCHAR="t";

char const** TAGS=&TAGCHAR;

Максимальный размер этого буфера (192байта) задается в файле httpd.h:

/* The maximum length of string that can be returned to replace any given tag */

#ifndef LWIP_HTTPD_MAX_TAG_INSERT_LEN

#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192

Для того чтобы SSI работал для выбранной странички, ее имя должно иметь расширение .shtml. Поэтому имя нашей модернизированной странички будет index.shtml. Ниже представлен ее фрагмент, полученный из index.html:

<td>

<B><font color="broun">LED4 OFF</font></B>

<FORM method="get" action="/leds.cgi" >

<INPUT type="HIDDEN" name="led40" value="0" size=2>

<INPUT type="submit" value="LED4 OFF"> </FORM>

</td>

</tr>

</table>

<!--#t-->

</BODY>

</HTML>

Изменим текст файла httpd_cgi_ssi.c. Заменим в нем полностью функцию

u16_t ADC_Handler(int iIndex, char *pcInsert, int iInsertLen)

{

/* We have only one SSI handler iIndex = 0 */

if (iIndex ==0) {

char Digit1=0, Digit2=0, Digit3=0, Digit4=0;

uint32_t ADCVal = 0;

}

на:

u16_t ADC_Handler(int iIndex, char *pcInsert, int iInsertLen)

{

//Установка начальных состояний надписи для светодиодов

char Ins[]={'L','E','D','1','_','O','F','F',';',' ','L','E','D','2','_','O','F','F',';',

' ','L','E','D','3','_','O','F','F',';',' ','L','E','D','4','_','O','F','F',';',' '};

for(char ii=0; ii<=39;ii++)

*(pcInsert+ii)=Ins[ii];

/* We have only one SSI handler iIndex = 0 */

if (iIndex ==0) {

/* prepare data to be inserted in html */

// Подготавливаем данные для вывода с использованием SSI

if (j0==1){

*(pcInsert + 0) ='L';

*(pcInsert + 1) = 'E';

*(pcInsert + 2) = 'D';

*(pcInsert + 3) = '1';

*(pcInsert + 4) ='_';

*(pcInsert + 5) = '_';

*(pcInsert + 6) = 'O';

*(pcInsert + 7) = 'N';

*(pcInsert + 8) = ';';

*(pcInsert + 9) = ' ';

}

if (j0==2){

*(pcInsert + 0) ='L';

*(pcInsert + 1) = 'E';

*(pcInsert + 2) = 'D';

*(pcInsert + 3) = '1';

*(pcInsert + 4) ='_';

*(pcInsert + 5) = 'O';

*(pcInsert + 6) = 'F';

*(pcInsert + 7) = 'F';

*(pcInsert + 8) = ';';

*(pcInsert + 9) = ' ';

}

if (j1==1){

*(pcInsert + 10) ='L';

*(pcInsert + 11) = 'E';

*(pcInsert + 12) = 'D';

*(pcInsert + 13) = '2';

*(pcInsert + 14) ='_';

*(pcInsert + 15) = '_';

*(pcInsert + 16) = 'O';

*(pcInsert + 17) = 'N';

*(pcInsert + 18) = ';';

*(pcInsert + 19) = ' ';

}

if (j1==2){

*(pcInsert + 10) ='L';

*(pcInsert + 11) = 'E';

*(pcInsert + 12) = 'D';

*(pcInsert + 13) = '2';

*(pcInsert + 14) ='_';

*(pcInsert + 15) = 'O';

*(pcInsert + 16) = 'F';

*(pcInsert + 17) = 'F';

*(pcInsert + 18) = ';';

*(pcInsert + 19) = ' ';

}

if (j2==1){

*(pcInsert + 20) ='L';

*(pcInsert + 21) = 'E';

*(pcInsert + 22) = 'D';

*(pcInsert + 23) = '3';

*(pcInsert + 24) ='_';

*(pcInsert + 25) = '_';

*(pcInsert + 26) = 'O';

*(pcInsert + 27) = 'N';

*(pcInsert + 28) = ';';

*(pcInsert + 29) = ' ';

}

if (j2==2){

*(pcInsert + 20) ='L';

*(pcInsert + 21) = 'E';

*(pcInsert + 22) = 'D';

*(pcInsert + 23) = '3';

*(pcInsert + 24) ='_';

*(pcInsert + 25) = 'O';

*(pcInsert + 26) = 'F';

*(pcInsert + 27) = 'F';

*(pcInsert + 28) = ';';

*(pcInsert + 29) = ' ';

}

if (j3==1){

*(pcInsert + 30) ='L';

*(pcInsert + 31) = 'E';

*(pcInsert + 32) = 'D';

*(pcInsert + 33) = '4';

*(pcInsert + 34) ='_';

*(pcInsert + 35) = '_';

*(pcInsert + 36) = 'O';

*(pcInsert + 37) = 'N';

*(pcInsert + 38) = ';';

*(pcInsert + 39) = ' ';

}

if (j3==2){

*(pcInsert + 30) ='L';

*(pcInsert + 31) = 'E';

*(pcInsert + 32) = 'D';

*(pcInsert + 33) = '4';

*(pcInsert + 34) ='_';

*(pcInsert + 35) = 'O';

*(pcInsert + 36) = 'F';

*(pcInsert + 37) = 'F';

*(pcInsert + 38) = ';';

*(pcInsert + 39) = ' ';

}

/* 40 символов будет помещено вместо t*/

return 40;

}

return 0;

}

А в функции const char * LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) вместо

return "/index.html";

следует поставить

return "/index.shtml";

После компиляции, пересылки файла в микроконтроллер и подключению по адресу http://192.168.1.10:8080 в браузере клиента появиться следующее изображение:

Выводы:

1. Отсутствуют зависания web – сервера на STM32F4Discovery и STM32F4DIS-BB.

2. По сравнению с web – сервером на Arduino + Ethernet Shield W5100 этот сервер работает значительно быстрее.

3. За счет большой флешь, оперативной памяти у STM32F407VGT6 сервер можно сделать с большим количеством каналов управления, датчиков и более информативным по сравнению с Arduino.

4. Стек протоколов TCP/IP для STM32F407VGT6 работает более надежно, чем для Arduino + Ethernet Shield W5100. Например, при относительно высокой загрузке сети для сервера на Arduino не всегда все данные доходили до клиента, а для сервера с протоколом LwIP таких проблем не замечалось.

5. Однако следует отметить большую сложность создания систем управления по TCP/IP сети с микроконтроллерами STM32 по сравнению с AVR (Arduino).

Видео работы web - сервера

Web - server STM32F4Discovery + STM23F4DIS-BB