实现了利用udp通信去重新设置mcu的ip地址、子网掩码、网关
udp使用的是静态的ip
默认已经用cubemx配好ETH、lwip已经配好并能进行udp通信
关于udp的配置可看这位大佬
用cubemx配置生成udp代码
1、ip信息结构体
- 为了方便使用,需要自定义
- 不同的lwip驱动ip4_addr_t可能不同
struct IP_INFO{
uint8_t ip_addr[4];
uint8_t netmask_addr[4];
uint8_t gateway_addr[4];
ip4_addr_t ip;//地址
ip4_addr_t nm;//掩码
ip4_addr_t gw;//网关
};
struct IP_INFO ip_info;
2、修改ip的核心函数
- 一般cubemx生成的代码在lwip.c中会有NET_set_addr
- 只需要将绑定好的ip地址、掩码、网关传给NET_set_addr就行
- 需要用这个函数:IP4_ADDR 来绑定你的ip地址、网关等
/*
使用示例
IP4_ADDR(&ip_info.ip,192,168,2,100);//掩码和网关也是这样绑
......
NET_set_addr(&ip_info.ip, &ip_info.nm,&ip_info.gw);
*/
void NET_set_addr(const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
const ip4_addr_t *gw)
{
if (ip4_addr_isany(ipaddr)) {
/* when removing an address, we have to remove it *before* changing netmask/gw
to ensure that tcp RST segment can be sent correctly */
netif_set_ipaddr(&gnetif, ipaddr);
netif_set_netmask(&gnetif, netmask);
netif_set_gw(&gnetif, gw);
} else {
netif_set_netmask(&gnetif, netmask);
netif_set_gw(&gnetif, gw);
/* set ipaddr last to ensure netmask/gw have been set when status callback is called */
netif_set_ipaddr(&gnetif, ipaddr);
}
}
3、从消息里获取ip信息
- 需要一个全局变量struct IP_INFO ip_info;
- 命令格式(前面可自定义 只需修改函数中i的初始值)
$setip<xxx.xxx.xxx.xxx><xxx.xxx.xxx.xxx><xxx.xxx.xxx.xxx>
最好做个判断决定是否要进入消息解析函数 - 仅加入了输入语法检测,即保证拿到的是12个数字
无法保证这12个数字是否合法
/*用于从消息中获取要设置的ip地址等信息*/
struct ip_tmp_udp{
uint8_t tmp[3];
int8_t count;
};
/*********************************************************************
功 能:将字符串转换数字
示例
str.tmp[0] = 1
str.tmp[1] = 9
str.tmp[2] = 2
转化后
dest = 192
*********************************************************************/
void udp_string_to_num(struct ip_tmp_udp str,uint8_t *dest){
uint8_t num = 0;
/*
这段有问题,会传出奇怪的值
int i = 0;
str.count--;
while(1){
if(tr.count < 0){
break;
}
num = num + str.tmp[i]*10^str.count;
i++;
str.count--;
}
*/
if(str.count == 3){
num = str.tmp[0]*100+str.tmp[1]*10+str.tmp[2];
}
else if(str.count == 2){
num = str.tmp[0]*10+str.tmp[1];
}
else{
num = str.tmp[0];
}
*dest = num;
}
/*********************************************************************
功 能:从数据中获取ip地址,子网掩码,网关等信息
ip 掩码 网关
命令:$setip<xxx.xxx.xxx.xxx><xxx.xxx.xxx.xxx><xxx.xxx.xxx.xxx>
循环1:解析xxx,由于xxx的位数不确定(可能为3,2,1位)
循环2:解析<...>
循环3:解析<><><>
*********************************************************************/
void udp_GetIp_AND_Reset(char *recv_msg){
char send_data[128] = {0};
uint8_t left = 0;//计数<
uint8_t right = 0;//计数>
uint8_t i = 6;//遍历recv_msg;
uint8_t j = 4;//计数“.”
uint8_t check = 1;//检测输入合法性 1合法,0不合法
struct IP_INFO ip_info_tmp;
struct ip_tmp_udp str;//暂存xxx
//读到$setip进入本循环
while(check)//循环3
{
if(i > 57 || right > 4 || j <4)//异常结束循环
{
sprintf(send_data,"(1)fail to setip : %d\r\n",i);
check = 0;
}
else if(right == 3 && right == left)//正常结束所有循环
{
break;
}
if(recv_msg[i] == '<')
{
i++; //recv_msg[i] == '<' 所以要i+1;
j = 0;//“.”计数置0
while(recv_msg[i-1] != '>' && check)//循环2
{
str.count = 0;
/* <xxx.xxx.xxx.xxx> <>*/
while(recv_msg[i] != '.' && recv_msg[i] != '>' && check)//循环1
{
/* xxx每一位合理的范围是 0 ~ 9 */
if(recv_msg[i]>57 || recv_msg[i]<48)
{
sprintf(send_data,"(2)fail to setip : %d\r\n",i);
check = 0;
break;
}
str.tmp[str.count] = recv_msg[i]-48;//逐位复制xxx
str.count++;//xxx 计数+1
/* xxx合理位数为1~3,<xxxx.xx*/
if(str.count >3)
{
sprintf(send_data,"(3)fail to setip : %d\r\n",i);
check = 0;
break;
}
i++;
}
/* xxx合理位数为1~3,<xxx..xxx提醒出错的位数 */
if(str.count == 0)
{
sprintf(send_data,"(4)fail to setip : %d\r\n",i);
check = 0;
break;
}
if(check)
{
if(left == 0)
{
udp_string_to_num(str,&ip_info_tmp.ip_addr[j]);
}
else if(left == 1)
{
udp_string_to_num(str,&ip_info_tmp.netmask_addr[j]);
}
else
{
udp_string_to_num(str,&ip_info_tmp.gateway_addr[j]);
}
}
j++;//“.”计数+1
if(j > 4)
{
sprintf(send_data,"(5)fail to setip : %d\r\n",i);
check = 0;
break;
}
i++;
}
left++;
}
right++;
}
if(check)
{
for(i = 0;i<4;i++){
ip_info.ip_addr[i] = ip_info_tmp.ip_addr[i];
ip_info.netmask_addr[i] = ip_info_tmp.netmask_addr[i];
ip_info.gateway_addr[i] = ip_info_tmp.gateway_addr[i];
}
sprintf(send_data,"step1 of setip is complete\r\n");
if(1) //ip 掩码 网关 合法性检测(未完成)
{
udp_ResetIp();//重置ip
}
}
// udp_app_send(send_data,strlen(send_data));//消息回传到指定地址
}
4、接收回调函数
- 我是先解析一次消息再调用的上面的函数
static void udp_receive_callback(void *arg, struct udp_pcb *pcb,
struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
char udp_msg[512] = {0};
if(p!=NULL){
pbuf_copy_partial(p,(void *)udp_msg,p->len,0);//这样复制不会有乱码
udp_app_sendto(udp_msg,strlen(udp_msg),*addr,port);//消息回传,并重置udp发送端口
udp_parse_cmd(udp_msg);//命令解析
pbuf_free(p);
}
}
5、运行截图
- 修改测试
- 语法检测(仅能检测 $setip后面)