change README.md
|
@ -0,0 +1,99 @@
|
|||
# 基于矽璓已实现的Lwip,在ARM基础上实现基本的Web服务
|
||||
|
||||
## 1. 简介
|
||||
|
||||
本赛题基于矽璓已实现的Lwip网络栈,在ARM上移植了Mongoose嵌入式Web服务框架,通过netdev功能进行终端的网络参数获取与配置,实现了通过外部网页对IP、Gate Way、子网掩码等网络参数的设置。
|
||||
|
||||
## 2. 数据结构设计说明
|
||||
|
||||
解决方案使用到了矽璓的netdev模块,并通过一个缓存结构保存从netdev处获得的网络参数,其中缓存结构如下所示:
|
||||
```C
|
||||
static struct netdev* p_netdev;
|
||||
|
||||
static struct config {
|
||||
char *ip, *mask, *gw, *dns;
|
||||
} s_config;
|
||||
```
|
||||
|
||||
Web服务基于Mongoose框架,使用事件驱动模型,事件响应函数实现如下:
|
||||
```C
|
||||
static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data)
|
||||
{
|
||||
if (ev == MG_EV_OPEN && c->is_listening) {
|
||||
strcpy(s_config.ip, inet_ntoa(p_netdev->ip_addr));
|
||||
strcpy(s_config.mask, inet_ntoa(p_netdev->netmask));
|
||||
strcpy(s_config.gw, inet_ntoa(p_netdev->gw));
|
||||
strcpy(s_config.dns, inet_ntoa(p_netdev->dns_servers[0]));
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message* hm = (struct mg_http_message*)ev_data;
|
||||
if (mg_http_match_uri(hm, "/api/config/get")) {
|
||||
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
|
||||
"{%m:%m,%m:%m,%m:%m,%m:%m}\n",
|
||||
MG_ESC("ip"), MG_ESC(s_config.ip),
|
||||
MG_ESC("mask"), MG_ESC(s_config.mask),
|
||||
MG_ESC("gw"), MG_ESC(s_config.gw),
|
||||
MG_ESC("dns"), MG_ESC(s_config.dns));
|
||||
} else if (mg_http_match_uri(hm, "/api/config/set")) {
|
||||
struct mg_str json = hm->body;
|
||||
update_config(json, "$.ip", (char**)&s_config.ip);
|
||||
update_config(json, "$.mask", (char**)&s_config.mask);
|
||||
update_config(json, "$.gw", (char**)&s_config.gw);
|
||||
update_config(json, "$.dns", (char**)&s_config.dns);
|
||||
mg_http_reply(c, 200, "", "ok\n");
|
||||
|
||||
ip_addr_t ipaddr, maskaddr, gwaddr;
|
||||
inet_aton(s_config.ip, &ipaddr);
|
||||
inet_aton(s_config.mask, &maskaddr);
|
||||
inet_aton(s_config.gw, &gwaddr);
|
||||
p_netdev->ops->set_addr_info(p_netdev, &ipaddr, &maskaddr, &gwaddr);
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = { .root_dir = s_root_dir };
|
||||
mg_http_serve_dir(c, ev_data, &opts);
|
||||
}
|
||||
}
|
||||
(void)fn_data;
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 测试程序说明
|
||||
|
||||
1. 通过数据线将开发板连接到电脑,并通过网线将开发板连接到与电脑相同的局域子网中。
|
||||
2. 选择Menuconfig中的Lwip和SD Card选项而后编译,将编译得到的程度文件烧录到开发板中,程序中将测试程序注册为NetSettingDemo命令。
|
||||
3. 将对应的netsetting文件夹存放到插入开发板的SD卡中。
|
||||
4. 进入XiUOS,首先执行setip命令初始化开发板网络设置,通过ping命令确定开发板与电脑处于同一局域网中。通过ls命令确认开发板成功识别SD卡,并且其中存在netsetting文件夹。
|
||||
5. 执行NetSettingDemo命令,并在电脑中访问192.168.130.77:8000(其中192.168.130.77为setip默认的开发板IP地址)验证程序。
|
||||
|
||||
|
||||
## 4. 运行结果(##需结合运行测试截图按步骤说明##)
|
||||
|
||||
1. 正确连接开发板:
|
||||
|
||||
<img src="imgs\board.jpg" style="zoom: 33%;" />
|
||||
|
||||
2. 选择Menuconfig中的Lwip和SD Card选项
|
||||
<img src="imgs\cleanAndMenu.png" style="zoom: 33%;" />
|
||||
<img src="imgs\menuconfig.png" style="zoom: 33%;" />
|
||||
|
||||
3. 编译成果如下,编译无误,在xiuos/Ubiquitous/XiZi_IIoT/build路径下得到编译成果XiZi-edu-arm32.elf和XiZi-edu-arm32.bin
|
||||
<img src="imgs\build(1).png" style="zoom: 33%;" />
|
||||
<img src="imgs\build2.png" style="zoom: 33%;" />
|
||||
|
||||
4. 进入开发板内XiUOS界面,如图为其中的命令:
|
||||
|
||||

|
||||
|
||||
5. 执行setip和NetSettingDemo:
|
||||
|
||||

|
||||
|
||||
6. 在电脑上访问192.168.130.77:8000:
|
||||
|
||||

|
||||
|
||||
7. 修改其中的IP,并在新IP中访问网页,此次将IP修改为192.168.130.80:
|
||||
|
||||

|
||||
|
||||
8. 在访问过程中开发板上也会有对应打印,部分打印如下图所示。在IP被修改后原有的Socket连接将产生错误,网页与192.168.130.77的连接随之关闭。网页访问192.168.130.80后将重新建议正确连接。
|
||||
|
||||

|
After Width: | Height: | Size: 753 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
<script type="module" src="main.js"></script>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,95 @@
|
|||
'use strict';
|
||||
import { h, html, render, useEffect, useState } from './preact.min.js';
|
||||
|
||||
const Configuration = function (props) {
|
||||
const [ip, setIp] = useState(props.config.ip || '');
|
||||
const [mask, setMask] = useState(props.config.mask || '');
|
||||
const [gw, setGw] = useState(props.config.gw || '');
|
||||
const [dns, setDns] = useState(props.config.dns || '');
|
||||
|
||||
useEffect(() => {
|
||||
setIp(props.config.ip);
|
||||
setMask(props.config.mask);
|
||||
setGw(props.config.gw);
|
||||
setDns(props.config.dns);
|
||||
}, [props.config]);
|
||||
|
||||
const update = (name, val) =>
|
||||
fetch('/api/config/set', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
[name]: val
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
enable(false);
|
||||
});
|
||||
|
||||
const updateip = ev => update('ip', ip);
|
||||
const updatemask = ev => update('mask', mask);
|
||||
const updategw = ev => update('gw', gw);
|
||||
const updatedns = ev => update('dns', dns);
|
||||
|
||||
return html`
|
||||
<h3 style="background: #00868b; color: #fff; padding: 0.4em;">
|
||||
Device Configuration</h3>
|
||||
<div style="margin: 0.5em 0; display: flex;">
|
||||
<span class="addon nowrap">IP:</span>
|
||||
<input type="text" style="flex: 1 100%;"
|
||||
value=${ip} onchange=${updateip}
|
||||
oninput=${ev => setIp(ev.target.value)} />
|
||||
<button class="btn" disabled=${!ip} onclick=${updateip}
|
||||
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||
</div>
|
||||
<div style="margin: 0.5em 0; display: flex; ">
|
||||
<span class="addon nowrap">Mask:</span>
|
||||
<input type="text" style="flex: 1 100%;"
|
||||
value=${gw} onchange=${updategw}
|
||||
oninput=${ev => setGw(ev.target.value)} />
|
||||
<button class="btn" disabled=${!gw} onclick=${updategw}
|
||||
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||
</div>
|
||||
<div style="margin: 0.5em 0; display: flex;">
|
||||
<span class="addon nowrap">Net Gate:</span>
|
||||
<input type="text" style="flex: 1 100%;"
|
||||
value=${mask} onchange=${updatemask}
|
||||
oninput=${ev => setMask(ev.target.value)} />
|
||||
<button class="btn" disabled=${!mask} onclick=${updatemask}
|
||||
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||
</div>
|
||||
<div style="margin: 0.5em 0; display: flex;">
|
||||
<span class="addon nowrap">DNS:</span>
|
||||
<input type="text" style="flex: 1 100%;"
|
||||
value=${dns} onchange=${updatedns}
|
||||
oninput=${ev => setDns(ev.target.value)} />
|
||||
<button class="btn" disabled=${!dns} onclick=${updatedns}
|
||||
style="margin-left: 1em; background: #8aa;">Update</button>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
const App = function (props) {
|
||||
const [config, setConfig] = useState({});
|
||||
|
||||
const getconfig = () =>
|
||||
fetch('/api/config/get')
|
||||
.then(r => r.json())
|
||||
.then(r => setConfig(r))
|
||||
.catch(err => console.log(err));
|
||||
|
||||
useEffect(() => {
|
||||
getconfig();
|
||||
}, []);
|
||||
|
||||
return html`
|
||||
<h1>配置路由信息</h1>
|
||||
<div class="col col-6">
|
||||
${h(Configuration, { config })}
|
||||
</div>
|
||||
<button onclick=${getconfig}>Get configuration</button>`;
|
||||
};
|
||||
|
||||
window.onload = () => render(h(App), document.body);
|
1
APP_Framework/Applications/app_test/test_webserver/netsetting/preact.min.js
vendored
Executable file
|
@ -0,0 +1,43 @@
|
|||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; }
|
||||
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
|
||||
code, pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 0.1em 0.3em; border-radius: 0.2em; }
|
||||
textarea, input, .addon { font-size: 15px; border: 1px solid #ccc; padding: 0.5em; }
|
||||
a, a:visited, a:active { color: #55f; }
|
||||
.addon { background: #eee; min-width: 9em;}
|
||||
.btn {
|
||||
background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer;
|
||||
display: inline-block; padding: 0.6em 2em; font-weight: bolder;
|
||||
}
|
||||
.btn[disabled] { opacity: 0.5; cursor: auto;}
|
||||
.smooth { transition: all .2s; }
|
||||
.container { margin: 0 20px; width: auto; }
|
||||
.d-flex { display: flex; }
|
||||
.d-none { display: none; }
|
||||
.border { border: 1px solid #ddd; }
|
||||
.rounded { border-radius: 0.5em; }
|
||||
.nowrap { white-space: nowrap; }
|
||||
.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; }
|
||||
.section { margin: 0 1em; }
|
||||
.topic, .data, .qos { padding: 0.2em 0.5em; border-radius: 0.4em; margin-right: 0.5em; }
|
||||
.qos { background: #efa; }
|
||||
.topic { background: #fea; }
|
||||
.data { background: #aef; }
|
||||
|
||||
/* Grid */
|
||||
.row { display: flex; flex-wrap: wrap; }
|
||||
.col { margin: 0; padding: 0; overflow: auto; }
|
||||
.col-12 { width: 100%; }
|
||||
.col-11 { width: 91.66%; }
|
||||
.col-10 { width: 83.33%; }
|
||||
.col-9 { width: 75%; }
|
||||
.col-8 { width: 66.66%; }
|
||||
.col-7 { width: 58.33%; }
|
||||
.col-6 { width: 50%; }
|
||||
.col-5 { width: 41.66%; }
|
||||
.col-4 { width: 33.33%; }
|
||||
.col-3 { width: 25%; }
|
||||
.col-2 { width: 16.66%; }
|
||||
.col-1 { width: 8.33%; }
|
||||
@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } }
|
||||
@media (max-width: 920px) { .row .col { width: 100%; } }
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// UI example
|
||||
// It implements the following endpoints:
|
||||
// /api/config/get - respond with current config
|
||||
// /api/config/set - POST a config change
|
||||
// any other URI serves static files from s_root_dir
|
||||
// Data and results are JSON strings
|
||||
|
||||
#include "ip_addr.h"
|
||||
#include "mongoose.h"
|
||||
#include "netdev.h"
|
||||
|
||||
static const char* s_http_addr = "http://0.0.0.0:8000"; // HTTP port
|
||||
static const char* s_root_dir = "netsetting";
|
||||
|
||||
static struct netdev* p_netdev;
|
||||
|
||||
static struct config {
|
||||
char *ip, *mask, *gw, *dns;
|
||||
} s_config;
|
||||
|
||||
// Try to update a single configuration value
|
||||
static void update_config(struct mg_str json, const char* path, char** value)
|
||||
{
|
||||
char* jval;
|
||||
if ((jval = mg_json_get_str(json, path)) != NULL) {
|
||||
free(*value);
|
||||
*value = strdup(jval);
|
||||
}
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data)
|
||||
{
|
||||
if (ev == MG_EV_OPEN && c->is_listening) {
|
||||
s_config.ip = strdup(inet_ntoa(p_netdev->ip_addr));
|
||||
s_config.mask = strdup(inet_ntoa(p_netdev->netmask));
|
||||
s_config.gw = strdup(inet_ntoa(p_netdev->gw));
|
||||
s_config.dns = strdup(inet_ntoa(p_netdev->dns_servers[0]));
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message* hm = (struct mg_http_message*)ev_data;
|
||||
if (mg_http_match_uri(hm, "/api/config/get")) {
|
||||
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
|
||||
"{%m:%m,%m:%m,%m:%m,%m:%m}\n",
|
||||
MG_ESC("ip"), MG_ESC(s_config.ip),
|
||||
MG_ESC("mask"), MG_ESC(s_config.mask),
|
||||
MG_ESC("gw"), MG_ESC(s_config.gw),
|
||||
MG_ESC("dns"), MG_ESC(s_config.dns));
|
||||
} else if (mg_http_match_uri(hm, "/api/config/set")) {
|
||||
struct mg_str json = hm->body;
|
||||
update_config(json, "$.ip", &s_config.ip);
|
||||
update_config(json, "$.mask", &s_config.mask);
|
||||
update_config(json, "$.gw", &s_config.gw);
|
||||
update_config(json, "$.dns", &s_config.dns);
|
||||
mg_http_reply(c, 200, "", "ok\n");
|
||||
|
||||
ip_addr_t ipaddr, maskaddr, gwaddr;
|
||||
inet_aton(s_config.ip, &ipaddr);
|
||||
inet_aton(s_config.mask, &maskaddr);
|
||||
inet_aton(s_config.gw, &gwaddr);
|
||||
p_netdev->ops->set_addr_info(p_netdev, &ipaddr, &maskaddr, &gwaddr);
|
||||
|
||||
printf("Board Net Configuration changed to [IP: %s, Mask: %s, GW: %s]\n",
|
||||
s_config.ip,
|
||||
s_config.mask,
|
||||
s_config.gw);
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = { .root_dir = s_root_dir };
|
||||
mg_http_serve_dir(c, ev_data, &opts);
|
||||
}
|
||||
}
|
||||
(void)fn_data;
|
||||
}
|
||||
|
||||
static void* do_net_setting_demo(void* none)
|
||||
{
|
||||
p_netdev = NETDEV_DEFAULT;
|
||||
|
||||
struct mg_mgr mgr; // Event manager
|
||||
mg_log_set(MG_LL_DEBUG); // Set to 3 to enable debug
|
||||
mg_mgr_init(&mgr); // Initialise event manager
|
||||
mg_http_listen(&mgr, s_http_addr, fn, NULL); // Create HTTP listener
|
||||
for (;;)
|
||||
mg_mgr_poll(&mgr, 10); // Infinite event loop
|
||||
mg_mgr_free(&mgr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int net_setting_demo(int argc, char* argv[])
|
||||
{
|
||||
pthread_t tid = -1;
|
||||
pthread_attr_t attr;
|
||||
attr.schedparam.sched_priority = 30;
|
||||
attr.stacksize = 16384;
|
||||
|
||||
PrivTaskCreate(&tid, &attr, do_net_setting_demo, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN) | SHELL_CMD_PARAM_NUM(5), NetSettingDemo, net_setting_demo, webserver to set net configurations);
|