В предыдущей части занятия мы создали и настроили проект, написали и проверили код WEB-сервера, а также написали и проверили код для использования SSI.
Теперь наоборот. Нам надо передать из клиентского браузера данные на сервер. Это уже CGI. Мы будем, также как и в примере из репозитория, управлять светодиодами, включая определённые, в зависимости от чекбоксов, в которые мы установим галочки.
Для этого у нас уже есть другой документ led.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><title>LED</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta content="MSHTML 6.00.2800.1561" name="GENERATOR">
<style ="font-weight: normal; font-family: Verdana;"></style></head>
<body>
<h4 style="text-align: center;">LED Form</h4>
<br>
<form align="center" method="get" action="/leds.cgi">
<input value="1" name="led" type="checkbox">LED1<br>
<input value="2" name="led" type="checkbox">LED2<br>
<input value="3" name="led" type="checkbox">LED3<br>
<input value="4" name="led" type="checkbox">LED4<br>
<br>
<input value="Send" type="submit"> </form>
</body></html>
В данном документе имеется форма с четырьмя чекбоксами и кнопкой. При нажатии на кнопку у нас из браузера на сервер будут уходить параметры, описанные в каждом input, в котором будет установлена галка.
Чтобы это всё работало, перейдём в проект и добавим ещё несколько глобальных переменных и прототип
uint32_t n=0;
const char * LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
const tCGI LEDS_CGI={"/leds.cgi", LEDS_CGI_Handler};
tCGI CGI_TAB[1];
uint8_t ledstate=0;
Мы добавили прототип обработчика приема параметров из браузера, тажке строку с файлом, которого на самом деле нет, просто это будет началом GET-запроса браузера, а также с адресом функции, в которой будет данный запрос обрабатываться. Также специальный массив из одного элемента и переменную для хранения статуса светодиодов.
Добавим обработчик запроса после обработчика SSI
//--------------------------------------------------------
const char * LEDS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
uint32_t i=0;
if (iIndex==0)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_RESET);
ledstate = 0;
for (i=0; i<iNumParams; i++)
{
if (strcmp(pcParam[i] , "led")==0)
{
if(strcmp(pcValue[i], "1") ==0)
{
ledstate |= 1;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
}
else if(strcmp(pcValue[i], "2") ==0)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
ledstate |= 2;
}
else if(strcmp(pcValue[i], "3") ==0)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
ledstate |= 4;
}
else if(strcmp(pcValue[i], "4") ==0)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
ledstate |= 8;
}
}
}
}
return "/led.html";
}
//---------------------------------------------------------------
В данном обработчике мы проверяем параметр iIndex на ноль, но у нас другого и не будет, так как у нас только один параметр, а значения могут быть разные. А в значениях у нас уже будет строка, часть которой мы проверим сразу, а затем будем проверять цифру и если придёт одна из четырех, то мы зажжем соответствующий светодиод и также включим в статусной переменной определённый бит. Данный статус нас пока особо не волнует, это всё будет потом. И в конце мы возвращаем результат в виде страницы.
Но это не всё. Надо ещё в main() кое-что проделать
http_set_ssi_handler(SSI_Handler, (char const **)TAGS, 4);
CGI_TAB[0] = LEDS_CGI;
http_set_cgi_handlers(CGI_TAB, 1);
Мы инициализировали элемент массива и обработчик.
Теперь можно собрать код, прошить контроллер и проверить работу кода
Установим галки в любые чекбоксы и нажмём кнопку «Send». У нас будут доджны загореться соответствующие светодиоды
Только не понравилось мне одно. После нажатия кнопки и перезагрузки документа галки убираются, хотелось бы, чтобы они остались на месте. Я долго думал как это реализовать. Но всё же выход нашел. Я применил здесь SSI, который вернёт в качестве параметра все, что нам надо. Для этого-то и был придуман статус.
Вызывать в браузере мы будем уже другой документ — led.shtml со следующим кодом
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><title>LED</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta content="MSHTML 6.00.2800.1561" name="GENERATOR">
<style ="font-weight: normal; font-family: Verdana;"></style></head>
<body>
<h4 style="text-align: center;">LED Form</h4>
<br>
<form align="center" method="get" action="/leds.cgi">
<!--#p--> LED1<br>
<!--#r--> LED2<br>
<!--#s--> LED3<br>
<!--#t--> LED4<br>
<br>
<input value="Send" type="submit"> </form>
</body></html>
В данном документе у нас уже отстуствуют теги и вместо них находятся комментарии с маркерами.
Только маркеры с комментариями в тело тегов добавить не удалось, поэтому мы будем передавать весь тег.
Смысл в том, что для того, чтобы чекбокс был с галкой, мы должны в тег input вставить свойство в виде атрибута без параметра. Это атрибут checked.
Вернёмся в проект и добавим ещё два глобальных строковых массива, представляющие из себя части строки с тегом, которые не будут меняться
uint8_t ledstate=0;
char htmstr1[] = "<input value=\"";
char htmstr2[] = "\" name=\"led\" type=\"checkbox\"";
В фукнции-обработчике SSI SSI_Handler закомментируем весь код кроме последней строки с возвратом нулевой длины и добавим другой код
*/
strcpy(pcInsert,htmstr1);
if (iIndex ==0)
{
strcat(pcInsert,"1");
strcat(pcInsert,htmstr2);
if(ledstate&0x01)
{
strcat(pcInsert," checked> ");
}
else
{
strcat(pcInsert,"> ");
}
return strlen(pcInsert);
}
else if (iIndex ==1)
{
strcat(pcInsert,"2");
strcat(pcInsert,htmstr2);
if(ledstate&0x02)
{
strcat(pcInsert," checked> ");
}
else
{
strcat(pcInsert,"> ");
}
return strlen(pcInsert);
}
else if (iIndex ==2)
{
strcat(pcInsert,"3");
strcat(pcInsert,htmstr2);
if(ledstate&0x04)
{
strcat(pcInsert," checked> ");
}
else
{
strcat(pcInsert,"> ");
}
return strlen(pcInsert);
}
else if (iIndex ==3)
{
strcat(pcInsert,"4");
strcat(pcInsert,htmstr2);
if(ledstate&0x08)
{
strcat(pcInsert," checked> ");
}
else
{
strcat(pcInsert,"> ");
}
return strlen(pcInsert);
}
return 0;
Код здесь хоть и большой, но довольно простой. Мы сначала занесем в строку начало тега, затем зайдём в условие, соответствующее индексу запроса, добавим к строке в теле условия циферку, соответствующую индексу, затем добавим следующую часть тега. Затем уже в зависимости от состояния бита, назначенному для светодиода завершим тег. Если бит в единице, то есть светодиод зажжён, то мы попадаем в истинное тело и добавляем аттрибут checked, соответствующий установленной галке и закрываем тег, а если в нуле, то просто закрываем тег. Вот и всё.
Но нет, вообще-то не совсем всё. В функции LEDS_CGI_Handler не забываем изменить расширение документа при возврате, а то мы вернем не тот документ
return "/led.shtml";
}
Собираем код, прошиваем контроллер и смотрим результат, вызывая в браузере конечно led.shtml
Код отлично работает!
У нас также управляются светодиоды и галки уже из чекбоксов при перезагрузке страницы никуда не пропадают.
Таким образом, мы сегодня научились создавать простой HTTP-сервер, а также научились использовать функционал данного сервера по работе с CGI и SSI, причем также и одновременно с обоими.
Всем спасибо за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь STM32F4-DISCOVERY
Модуль LAN можно приобрести здесь: LAN8720
Плату расширения можно приобрести здесь: STM32F4DIS-BB
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
STM32Cube_FW_F4_V1.26.2 жалуется на отсутствие файла «fsdata_custom.c»…
Файла в проекте нет.Заработало после замены на «../../../apps/httpd/fsdata.c»
после перезаписи проекта из куба строку надо менять заново…
Что за файл «fsdata_custom.c» где искать?