Продолжим работу с LWIP SOCKET, а также с протоколом HTTP.
И сегодня мы попытаемся не просто отправить страничку браузеру с нашего контроллера, но отправить данные в определённое место страницы, причём без перезагрузки всей страницы.
В этом нам поможет технология AJAX, которой мы уже с вами пользовались, когда использовали API NETCONN, поэтому, думаю, нам будет гораздо легче, чем если бы мы знакомились с данной технологией с нуля.
Кто не видел урок 128 по применению технологии AJAX — непременно посмотрите.
Ну, теперь, когда все посмотрели урок, сразу же перейдём к проекту, который был сделан из проекта урока 136 с именем LAN8742_HTTP_SERVER_SOCKET и присвоим ему новое имя LAN8742_HTTP_SERVER_AJAX_SOCKET.
Откроем проект в Cube MX и и включим генератор случайных величин RNG
Затем сгенерируем проект для System Workbench, откроем его там, уберём при наличии отладочные настройки, настроим уровень оптимизации в 1 и пока собирать не будем, так как у нас пока нет файла fsdata.c.
Весь состав документального контента мы возьмём тот же, что и в уроке 128. Скопируем папку fs, сохранённую для данного урока, вместе с содержимым, а также файлы makefsdata.exe, makefsdata.cmd и msvcr100d.dll, чтобы мы могли свободно собирать недостающий файл с контентом, в папку «Папка с проектом//Middlewares/Third_Party/LwIP/src/apps/httpd/». Также в папку fs помести файл favicon.ico, который вы сами себе сгенерируйте под себя. Это иконка сайта, которая отображается в закладке с браузером вместе с именем документа. Если этот файл поместить, то серверу не придется постоянно отвечать браузерам об его отсутствии. Для генерации данного файла существует ряд онлайн-сервисов.
Соберём с помощью файла makefsdata.cmd файл fsdata.c, вернёмся в проект, освежим проект в дереве (Refresh) и отключим от компиляции наш файл fsdata.c.
Попробуем теперь собрать проект, проект должен будет нормально собраться.
В файле main.c из функции tcp_thread удалим ответ на запрос файла index1.html, так как у нас такого уже нет
else if (strncmp((char const *)buf,«GET /index1.html«,16)==0)
{
fs_open(&file, «/index1.html«);
write(accept_sock, (const unsigned char*)(file.data), (size_t)file.len);
fs_close(&file);
}
Добавим скрипты
1 2 3 4 5 6 7 8 9 10 11 12 13 |
else if (strncmp((char const *)buf,"GET /style.css",14)==0) { fs_open(&file, "/style.css"); write(accept_sock, (const unsigned char*)(file.data), (size_t)file.len); fs_close(&file); } else if (strncmp((char const *)buf,"GET /js/Chart.min.js",20)==0) { fs_open(&file, "/js/Chart.min.js"); write(accept_sock, (const unsigned char*)(file.data), (size_t)file.len); fs_close(&file); } else if (strncmp((char const *)buf,"GET /IMG/img01.jpg",18)==0) |
Добавим также ответы на запросы фоновых изображений
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
else if (strncmp((char const *)buf,"GET /IMG/bg01.png",17)==0) { fs_open(&file, "/IMG/bg01.png"); write(accept_sock, (const unsigned char*)(file.data), (size_t)file.len); fs_close(&file); } else if (strncmp((char const *)buf,"GET /IMG/bg02.jpg",17)==0) { fs_open(&file, "/IMG/bg02.jpg"); write(accept_sock, (const unsigned char*)(file.data), (size_t)file.len); fs_close(&file); } else if (strncmp((char const *)buf,"GET /IMG/bg03.png",17)==0) { fs_open(&file, "/IMG/bg03.png"); write(accept_sock, (const unsigned char*)(file.data), (size_t)file.len); fs_close(&file); } else |
Добавим глобальный статический строковый массив
1 2 |
#define MAIL_SIZE (uint32_t) 5 char str_buf[1000]={'\0'}; |
Над функцией tcp_thread добавим функцию для сортировки строк практически такую же, как и в уроке 128
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/* USER CODE BEGIN 4 */ //--------------------------------------------------------------- void StrSort (char* str) { typedef struct strarray{ char data[20][50]; } strarray_ptr; char *str_tmp = (void*) str; char *istr; int cnt = 0, cnt_pass=0, res = 0, i, j; uint32_t itemsize; strarray_ptr *strarray1 = (void*) str_buf; //copy to array while(1) { istr = strstr(str_tmp,"\r\n"); itemsize = istr - str_tmp; if((itemsize>10)&&(itemsize<100)) { strncpy(strarray1->data[cnt],str_tmp,itemsize+2); strarray1->data[cnt][itemsize+2] = 0; str_tmp+=itemsize+2; } else { break; } cnt++; } //sort cnt_pass=cnt; for(j=0;j<(cnt-1);j++) { for(i=0;i<(cnt_pass-1);i++) { res = strcmp (strarray1->data[i], strarray1->data[i+1]); if (res>0) { //swap strcpy (strarray1->data[cnt],strarray1->data[i+1]); strcpy (strarray1->data[i+1],strarray1->data[i]); strcpy (strarray1->data[i],strarray1->data[cnt]); } } cnt_pass--; } //copy from array str[0] = 0; for(i=0;i<cnt;i++) { if (i==0) strcpy (str,strarray1->data[i]); else strcat (str,strarray1->data[i]); } str_tmp[0] = 0; } //--------------------------------------------------------------- |
Также добавим несколько статических массивов и расположим их в памяти FLASH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
uint8_t heap_sram2[32*1024]; static const unsigned char PAGE_HEADER_200_OK[] = { //"HTTP/1.1 200 OK" 0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d, 0x0a, //zero 0x00 }; static const unsigned char PAGE_HEADER_SERVER[] = { //"Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)" 0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33, 0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e, 0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70, 0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a, //zero 0x00 }; static const unsigned char PAGE_HEADER_CONTENT_TEXT[] = { //"Content-type: text/html" 0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65, 0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a, //zero 0x00 }; //* static const unsigned char PAGE_HEADER_CONTENT_STREAM[] = { //"Content-Type: application/octet-stream" 0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20,0x61,0x70, 0x70,0x6c,0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x2f,0x6f,0x63,0x74,0x65,0x74,0x2d, 0x73,0x74,0x72,0x65,0x61,0x6d,0x0d,0x0a, //zero 0x00 }; static const unsigned char PAGE_HEADER_LEN[] = { //"Content-Length: " 0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20, //zero 0x00 }; static const unsigned char PAGE_HEADER_BYTES[] = { //"Accept-Ranges: bytes" 0x41,0x63,0x63,0x65,0x70,0x74,0x2d,0x52,0x61,0x6e,0x67,0x65,0x73,0x3a,0x20,0x62, 0x79,0x74,0x65,0x73,0x0d,0x0a,0x0d,0x0a, //zero 0x00 }; |
После функции StrSort добавим функцию сборки и отправки строковой WEB-страницы
1 2 3 4 5 6 7 8 9 10 11 12 |
//--------------------------------------------------------------- void DynWebPageStr(int sock) { portCHAR PAGE_BODY[768]; uint16_t len = 0; sprintf(PAGE_BODY,"%s%s%s",PAGE_HEADER_200_OK,PAGE_HEADER_SERVER,PAGE_HEADER_CONTENT_TEXT); len = strlen(PAGE_BODY); osThreadList((uint8_t *)(PAGE_BODY + len)); StrSort((char *)(PAGE_BODY + len)); write(sock, (const void*)PAGE_BODY, (size_t)strlen((char*)PAGE_BODY)); } //--------------------------------------------------------------- |
Также ниже добавим функцию сборки и отправки WEB-страницы с данными для графика, сгенерированными случайно в заданном диапазоне
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void DynWebPage(int sock, uint16_t y_pos) { portCHAR PAGE_BODY[1300]; uint16_t len = 0; int i; PAGE_BODY[0] = 0; int val = 0; sprintf(PAGE_BODY,"%s%s%s%s%drn%s",PAGE_HEADER_200_OK,PAGE_HEADER_SERVER,PAGE_HEADER_CONTENT_STREAM, PAGE_HEADER_LEN,1024,PAGE_HEADER_BYTES); len = strlen(PAGE_BODY); for(i=0;i<512;i++) { val = HAL_RNG_GetRandomNumber(&hrng)%4096; PAGE_BODY[len + i * 2] = (uint8_t)val; PAGE_BODY[len + i * 2 + 1] = (uint8_t)(val>>8); } write(sock, (const void*)PAGE_BODY, (size_t)(len + 1024)); } //--------------------------------------------------------------- |
И напоследок в функции tcp_thread ответим на запросы таких страниц, а заодно и примем страницу с командой на вывод квадрата определённого цвета на дисплее
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
else if (strncmp((char const *)buf,"GET /color.html?c=",18)==0) { switch (buf[18]) { case '1': TFT_FillRectangle(200,120,300,220,LCD_COLOR_RED); break; case '2': TFT_FillRectangle(200,120,300,220,LCD_COLOR_GREEN); break; case '3': TFT_FillRectangle(200,120,300,220,LCD_COLOR_BLUE); break; case '4': TFT_FillRectangle(200,120,300,220,LCD_COLOR_BLACK); break; case '5': TFT_FillRectangle(200,120,300,220,LCD_COLOR_CYAN); break; case '6': TFT_FillRectangle(200,120,300,220,LCD_COLOR_MAGENTA); break; case '7': TFT_FillRectangle(200,120,300,220,LCD_COLOR_YELLOW); break; case '8': TFT_FillRectangle(200,120,300,220,LCD_COLOR_WHITE); break; } } else if (strncmp((char const *)buf,"GET /content.html",17)==0) { DynWebPageStr(accept_sock); } else if (strncmp((char const *)buf,"GET /content.bin",16)==0) { DynWebPage(accept_sock, arg_sock->y_pos); } else |
Соберём код, прошьём контроллер, запустим WEB-браузер, запросим нашу страницу index.html (как мы знаем из кода, для этого достаточно введения в адресную строку только адреса IP нашего сервера) и попробуем сначала с помощью соответствующих кнопочек вывести квадратики различных цветов на дисплее нашей платы
Должна наблюдаться вот такая картина на дисплее
Теперь нажмём кнопку «START» для получения текстовой информации от сервера о задачах
Начнётся динамический вывод информации о задачах
Нажмём кнопку «STOP«
Вывод информации прекратится и появится возможность нажать кнопку «START» также и для вывода графической информации, пришедшей с сервера, чем мы сейчас и воспользуемся
Начнётся вывод графической информации, состоящей из случайных величин, которые нам в браузер передал сервер
Нажмём кнопку «STOP» и вывод информации прекратится
Таким образом, используя накопленные знания, полученные в предыдущих занятиях, мы без особого труда написали сервер HTTP, который обрабатывает запросы AJAX и также на них отлично отвечает.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь 32F746G-DISCOVERY
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий