Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: lwIP Client
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Александр П.
Добрый день.
Есть связка MCU + удаленный девайс. Связаны по Ethernet. Требуется периодически посылать на удаленный девайс сообщение и принимать от него ответ.
Использую lwIP стек. Ниже приведен работающий код. В конечном итоге callback функция ReturnPg возвращает ответ удаленного девайса. Вопрос. Как реализовать периодический опрос.
main.c
CODE
//==============================================================================
static void ReturnPg(u8_t num, hc_errormsg msg, char *data, u16_t len)
{
// data - ответ удаленного устройства
}
//==============================================================================
int main(void)
{
struct ip_addr ipaddr, netmask, gw;
struct netif NetIf, *netif;
#if LWIP_DHCP
u8_t dhcp_state = DHCP_INIT;
#endif
// Disable watchdog
WDT_Disable( WDT );
#if defined (ddram)
MMU_Initialize((uint32_t *)0x30C000);
CP15_EnableMMU();
CP15_EnableIcache();
CP15_EnableDcache();
#endif
// Initialize system timing
sys_init_timing();
// Initialize lwIP modules
lwip_init();
// Initialize net interface for lwIP
gmacif_setmac((u8_t*)gMacAddress);

IP4_ADDR(&gw, gGateWay[0], gGateWay[1], gGateWay[2], gGateWay[3]);
IP4_ADDR(&ipaddr, gIpAddress[0], gIpAddress[1], gIpAddress[2], gIpAddress[3]);
IP4_ADDR(&netmask, gNetMask[0], gNetMask[1], gNetMask[2], gNetMask[3]);

netif = netif_add(&NetIf, &gw, &netmask, &ipaddr, NULL, gmacif_init, ip_input);
netif_set_default(netif);
netif_set_up(netif);

char page[] ="get_param1";

hc_open(ipaddr, page, 0, &ReturnPg);

while(1)
{
// Run periodic tasks
timers_update();
// Run polling tasks
gmacif_poll(netif);

}
}


webclient.c
CODE
#include <board.h>
#include <liblwip.h>
#include "lwip/opt.h"
#include "lwip/tcp.h"

#include <stdlib.h>
#include <string.h>
#include "webclient.h"
//==============================================================================
// Close a PCB(connection)
//==============================================================================
static void hc_clearpcb(struct tcp_pcb *pcb)
{
if(pcb != NULL)
{
// Close the TCP connection
tcp_close(pcb);
}
}
//==============================================================================
// Function that lwip calls for handling recv'd data
//==============================================================================
static err_t hc_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct hc_state *state = arg;
char * page = NULL;
struct pbuf * temp_p;
hc_errormsg errormsg = GEN_ERROR;
int i;

if((err == ERR_OK) && (p != NULL))
{
tcp_recved(pcb, p->tot_len);

// Add payload (p) to state
temp_p = p;
while(temp_p != NULL)
{
state->RecvData = realloc(state->RecvData, temp_p->len + state->Len + 1);

// CHECK 'OUT OF MEM'
if(state->RecvData == NULL)
{
// OUT OF MEMORY
(*state->ReturnPage)(state->Num, OUT_MEM, NULL, 0);
return(ERR_OK);
}

strncpy(state->RecvData + state->Len, temp_p->payload, temp_p->len);
state->RecvData[temp_p->len + state->Len] = '\0';
state->Len += temp_p->len;

temp_p = temp_p->next;
}

// Removing payloads

while(p != NULL)
{
temp_p = p->next;
pbuf_free(p);
p = temp_p;
}

}

// NULL packet == CONNECTION IS CLOSED(by remote host)
else if((err == ERR_OK) && (p == NULL))
{
// Simple code for checking 200 OK
for(i=0; i < state->Len; i++)
{
if(errormsg == GEN_ERROR)
{
// Check for 200 OK
if((*(state->RecvData+i) == '2') && (*(state->RecvData+ ++i) == '0') && (*(state->RecvData+ ++i) == '0')) errormsg = OK;
if(*(state->RecvData+i) == '\n') errormsg = NOT_FOUND;
}
else
{
// Remove headers
if((*(state->RecvData+i) == '\r') && (*(state->RecvData+ ++i) == '\n') && (*(state->RecvData+ ++i) == '\r') && (*(state->RecvData + ++i) == '\n'))
{
i++;
page = malloc(strlen(state->RecvData+i));
strcpy(page, state->RecvData+i);
break;
}
}
}

if(errormsg == OK)
{
// Put recv data to ---> p->ReturnPage
(*state->ReturnPage)(state->Num, OK, page, state->Len);
}
else
{
// 200 OK not found Return NOT_FOUND (WARNING: NOT_FOUND COULD ALSO BE 5xx SERVER ERROR, ...)
(*state->ReturnPage)(state->Num, errormsg, NULL, 0);
}

// Clear the PCB
hc_clearpcb(pcb);

// free the memory containing state
free(state->RecvData);
free(state);
}

return(ERR_OK);
}
//==============================================================================
// Function that lwip calls when there is an error
//==============================================================================
static void hc_error(void *arg, err_t err)
{
struct hc_state *state = arg;
// pcb already deallocated

// Call return function
// TO-DO: Check err_t err for out_mem, ...
(*state->ReturnPage)(state->Num, GEN_ERROR, NULL, 0);

free(state->RecvData);
//free(state->PostVars);
free(state->Page);
free(state);
}
//==============================================================================
// Function that lwip calls when the connection is idle
// Here we can kill connections that have stayed idle for too long
//==============================================================================
static err_t hc_poll(void *arg, struct tcp_pcb *pcb)
{
struct hc_state *state = arg;

state->ConnectionTimeout++;
if(state->ConnectionTimeout > 20)
{
// Close the connection
tcp_abort(pcb);

// Give err msg to callback function
// Call return function
(*state->ReturnPage)(state->Num, TIMEOUT, NULL, 0);
}

return(ERR_OK);
}
//==============================================================================
// lwip calls this function when the remote host has successfully received data (ack)
//==============================================================================
static err_t hc_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
struct hc_state *state = arg;

// Reset connection timeout
state->ConnectionTimeout = 0;

return(ERR_OK);
}
//==============================================================================
// lwip calls this function when the connection is established
//==============================================================================
char *str1;
char str2[100];
static err_t hc_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
struct hc_state *state = arg;
char * headers;

// error?
if(err != ERR_OK)
{
hc_clearpcb(pcb);

// Call return function
(*state->ReturnPage)(state->Num, GEN_ERROR, NULL, 0);

// Free wc state
free(state->RecvData);
free(state);

return(ERR_OK);
}

// Define Headers

// GET headers (without page)(+ \0) = 19
headers = malloc(19 + strlen(state->Page));
//usprintf(headers,"GET /%s HTTP/1.0\r\n\r\n", state->Page);
sprintf(headers,"GET /%s HTTP/1.0\r\n\r\n", state->Page);

// Check if we are nut running out of memory
if(headers == NULL)
{
hc_clearpcb(pcb);

// Call return function
(*state->ReturnPage)(state->Num, OUT_MEM, NULL, 0);

// Free wc state
free(state->RecvData);
free(state);

return(ERR_OK);
}

// Setup the TCP receive function
tcp_recv(pcb, hc_recv);

// Setup the TCP error function
tcp_err(pcb, hc_error);

// Setup the TCP polling function/interval //TCP_POLL IS NOT CORRECT DEFINED @ DOC!!!
tcp_poll(pcb, hc_poll, 10);

// Setup the TCP sent callback function
tcp_sent(pcb, hc_sent);

// Send data
strcpy(str2,headers);
tcp_write(pcb, headers, strlen(headers), 1);
tcp_output(pcb);

// remove headers
free(headers);
//free(state->PostVars); // postvars are send, so we don't need them anymore
free(state->Page); // page is requested, so we don't need it anymore

return(ERR_OK);
}
//==============================================================================
// Public function for request a webpage (REMOTEIP, ...
//==============================================================================
int hc_open(struct ip_addr remoteIP, char *Page, char *PostVars, void (* returnpage)(u8_t, hc_errormsg, char *, u16_t))
{
struct tcp_pcb *pcb = NULL;
struct hc_state *state;
static u8_t num = 0;
// local port
u16_t port= 4545;

// Get a place for a new webclient state in the memory
state = malloc(sizeof(struct hc_state));

// Create a new PCB (PROTOCOL CONTROL BLOCK)
pcb = tcp_new();
if(pcb == NULL || state == NULL)
{
//UARTprintf("hc_open: Not enough memory for pcb or state\n");
//Not enough memory
return 0;
}

// Define webclient state vars
num++;
state->Num = num;
state->RecvData = NULL;
state->ConnectionTimeout = 0;
state->Len = 0;
state->ReturnPage = returnpage;

// Make place for PostVars & Page
//if(PostVars != NULL) state->PostVars = malloc(strlen(PostVars) +1);
state->Page = malloc(strlen(Page) +1);

// Check for "out of memory"
if(state->Page == NULL)
{
free(state->Page);
//free(state->PostVars);
free(state);
tcp_close(pcb);
return 0;
}
// Place allocated copy data
strcpy(state->Page, Page);
//if(PostVars != NULL) strcpy(state->PostVars, PostVars);

// Bind to local IP & local port
while(tcp_bind(pcb, IP_ADDR_ANY, port) != ERR_OK)
{
// Local port in use, use port+1
port++;
}

// Use conn -> argument(s)
tcp_arg(pcb, state);

// Open connect (SEND SYN)
tcp_connect(pcb, &remoteIP, 8080, hc_connected);

return num;
}
//==============================================================================


webclient.h
CODE
#ifndef __WEBCLIENT_H
#define __WEBCLIENT_H

/* The unsigned data types */
typedef unsigned char u8_t;
typedef unsigned short u16_t;
typedef unsigned int u32_t;

// You can replace this enum for saving MEMORY (replace with define's)
typedef enum
{
OK,
OUT_MEM,
TIMEOUT,
NOT_FOUND,
GEN_ERROR
} hc_errormsg;

struct hc_state {
u8_t Num;
char *Page;
//char *PostVars;
char *RecvData;
u16_t Len;
u8_t ConnectionTimeout;
void (* ReturnPage)(u8_t num, hc_errormsg, char *data, u16_t len);
};

// Public function
int hc_open(struct ip_addr, char *, char *, void (*)(u8_t, hc_errormsg, char *, u16_t));

#endif // __WEBCLIENT_H


Заранее спасибо за любые советы.
AndyBig
По прерыванию таймера, например... В чем именно загвоздка?
Александр П.
Мне необходимо посылать повторный запрос сразу же после получения ответа от удаленного устройства. Если я правильно понимаю, все последующие запросы делаются анологичным образом
Код
char page[] ="get_param1";        
headers = malloc(19 + strlen(page));
sprintf(headers,"GET /%s HTTP/1.0\r\n\r\n", page);
// Send data
tcp_write(pcb, headers, strlen(headers), 1);
tcp_output(pcb);


В функции...
Код
static err_t hc_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)

...я убрал hc_clearpcb(pcb) и добавил посылку сообщения. Контроллер остается в сети. Сообщение не отправляется
Код
//hc_clearpcb(pcb);
char page[] ="get_param1";        
headers = malloc(19 + strlen(page));
sprintf(headers,"GET /%s HTTP/1.0\r\n\r\n", page);
// Send data
tcp_write(pcb, headers, strlen(headers), 1);
tcp_output(pcb);


Почему? Разве по прерыванию таймера я буду делать не тоже самое?

После выполнения первого запрос-ответа периодически вызывается (20 раз. Затем принудительный сброс соединения)
Код
static err_t hc_poll(void *arg, struct tcp_pcb *pcb)


Попытался здесь отправить запрос, ради интереса. Результата нет
Golikov A.
если вы очереди не очищаете, у вас память переполнится, разве нет?
а если вы не чистите входные очереди, то у вас забьется окно и тоже посылки перестанут идти
scifi
Цитата(Александр П. @ Feb 20 2014, 20:26) *
...я убрал hc_clearpcb(pcb) и добавил посылку сообщения. Контроллер остается в сети. Сообщение не отправляется

У вас указано "GET /%s HTTP/1.0". В протоколе HTTP 1.0 по умолчанию TCP соединение закрывается после одного запроса-ответа. В протоколе HTTP 1.1 - наоборот. Так что для начала надо заменить 1.0 на 1.1. Ну и делайте захват трафика при помощи Wireshark и сравнивайте с захватом заведомо рабочего трафика, чтобы понять, где у вас отклонения. Это если лень читать все эти RFC (а вам именно лень их читать, очевидно).
Александр П.
Цитата
если вы очереди не очищаете, у вас память переполнится, разве нет?
а если вы не чистите входные очереди, то у вас забьется окно и тоже посылки перестанут идти


Да, наверное, но у меня не проходит уже второе сообщение, что меньше размера окна. Как-то криво я делаю следующий запрос. А вот как правильно?

Цитата
У вас указано "GET /%s HTTP/1.0". В протоколе HTTP 1.0 по умолчанию TCP соединение закрывается после одного запроса-ответа. В протоколе HTTP 1.1 - наоборот. Так что для начала надо заменить 1.0 на 1.1.

Спасибо, попробую.
Цитата
Это если лень читать все эти RFC (а вам именно лень их читать, очевидно).

scifi, пожалуйста, не судите строго. Вы не представляете сколько я всего читаю каждый день. Я совсем недавно "заплыл" в embedded. Мало чего знаю, но постепенно разбираюсь.

фокус с протоколами результата не дал
scifi
Цитата(Александр П. @ Feb 20 2014, 21:02) *
фокус с протоколами результата не дал

Это было бы слишком просто :-) Наверное, имеет смысл для начала открывать и закрывать соединение на каждый запрос. Просто довелось самому делать веб-сервер поверх lwip на небольшом МК (сейчас, конечно, понимаю, что лучше было бы потщательнее поискать уже готовый). Эти самые "persistent connections" там реализовал - летать стало быстро-быстро.
kolobok0
Цитата(scifi @ Feb 20 2014, 21:44) *
...летать стало быстро-быстро.


во-во, переписал lwip и фриртос, зараза, быстрый ответ(пара ms) данными от сервака МК - режится роутером. Пришлось вставлять задержку sm.gif


Цитата(Александр П. @ Feb 20 2014, 21:02) *
...фокус с протоколами результата не дал


это было обязательным но не необходимым условием. в самом протоколе HTTP указывается - рвётся коннекшен или нет, опсле отдачи данных.


по теме:
возьмите пример с FreeRTOS и не парьтесь. дышать будет. хоть сам стэк и ось желают лучшего но для "хэйлохты мир" пойдёт.
Golikov A.
Цитата
это было обязательным но не необходимым условием

бррр... необходимым, но не достаточным условием!
kolobok0
Цитата(Golikov A. @ Feb 21 2014, 08:53) *
необходимым, но не достаточным условием!


агась спасибо за правку sm.gif проблемы с многопроцессорностью головы. занята несколькоми проблемами параллельно - отсюда
и интерфейс человеческий пошаливает sm.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.