萤火工场 GD32VW553-IOT-V2 简单的远程控制浇水

[复制链接]
2181|10
本帖最后由 iCEasy商城-小易 于 2025-10-22 10:34 编辑

本文转载自iCEasy商城口碑评测作者:dh9824
欢迎大家戳链接萤火工场 GD32VW553-IOT-V2 简单的远程控制浇水,与原贴作者互动交流哦~

视频链接
【纯白的试用GD32VW553-IOT-V2开发板】 https://www.bilibili.com/video/BV1oiWSz5E6v/?share_source=copy_web&vd_source=da6011adb79511b55647b1b2c83ffb45

前言
纯小白艰难自学,还好有元宝,你值得拥有

开源口碑分享内容
纯小白艰难自学,还好有元宝,你值得拥有,
打开GD32EmbeddedBuilder,导入官方提供的演示案例,softap_tcp_ server,
然后我根据我爱小易大佬的代码在元宝里滚了我需要的代码,大概功能是远程控制浇水系统启动关闭,后续还想添加湿度传感器,太阳能供电充电,水位检测,显示器等等,有试过,可惜毛病很多,浪费了半个多月时间,还是不行,只能先实现远程控制,配合内网穿透,就能实现外网访问控制网站了
基于您的代码框架,我将修改为完整的智能浇水系统,添加远程定时浇水、湿度检测、手动控制等功能。以下是完整的修改后代码:
  1. /*!
  2. \file main.c
  3. \brief GD32VW553 智能浇水系统 - SoftAP + 远程控制
  4. \author 我爱小易
  5. \company 萤火工场
  6. */

  7. #include <stdint.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include "gd32vw55x.h"
  11. #include "app_cfg.h"
  12. #include "gd32vw55x_platform.h"
  13. #include "lwip/sockets.h"
  14. #include "lwip/priv/sockets_priv.h"
  15. #include "wifi_management.h"
  16. #include "wifi_init.h"
  17. #include "gd32vw55x_adc.h"

  18. /* 硬件引脚定义 - 请根据实际硬件调整 */
  19. #define WATER_PUMP_PORT GPIOB
  20. #define WATER_PUMP_PIN GPIO_PIN_2

  21. #define SOIL_MOISTURE_ADC_CHANNEL ADC_CHANNEL_0
  22. #define WATER_LEVEL_PORT GPIOA
  23. #define WATER_LEVEL_PIN GPIO_PIN_1

  24. #define SYSTEM_LED_PORT GPIOB
  25. #define SYSTEM_LED_PIN GPIO_PIN_0

  26. #define AP_SSID "SmartWatering_AP"
  27. #define AP_PASSWORD "12345678"
  28. #define HTTP_PORT 80

  29. /* 系统参数 */
  30. #define MOISTURE_THRESHOLD_LOW 30 /* 自动浇水阈值 */
  31. #define MOISTURE_THRESHOLD_HIGH 60 /* 停止浇水阈值 */
  32. #define WATERING_DURATION 30000 /* 浇水持续时间(ms) */

  33. /* 全局变量 */
  34. volatile uint8_t watering_mode = 0; /* 0:自动 1:定时 2:手动 */
  35. volatile uint8_t watering_status = 0;
  36. volatile uint16_t soil_moisture = 0;
  37. volatile uint8_t water_level = 0;
  38. volatile uint32_t watering_timer = 0;
  39. volatile uint32_t next_watering_time = 0;

  40. /* 硬件初始化 */
  41. void hardware_init(void) {
  42. /* 初始化水泵控制 */
  43. rcu_periph_clock_enable(RCU_GPIOB);
  44. gpio_mode_set(WATER_PUMP_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, WATER_PUMP_PIN);
  45. gpio_output_options_set(WATER_PUMP_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, WATER_PUMP_PIN);
  46. gpio_bit_reset(WATER_PUMP_PORT, WATER_PUMP_PIN);

  47. /* 初始化水位检测 */
  48. rcu_periph_clock_enable(RCU_GPIOA);
  49. gpio_mode_set(WATER_LEVEL_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, WATER_LEVEL_PIN);

  50. /* 初始化系统LED */
  51. gpio_mode_set(SYSTEM_LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SYSTEM_LED_PIN);
  52. gpio_output_options_set(SYSTEM_LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, SYSTEM_LED_PIN);
  53. gpio_bit_reset(SYSTEM_LED_PORT, SYSTEM_LED_PIN);

  54. /* 初始化ADC用于土壤湿度检测 */
  55. rcu_periph_clock_enable(RCU_ADC0);
  56. adc_mode_config(ADC_MODE_FREE);
  57. adc_special_function_config(ADC_SCAN_MODE, ENABLE);
  58. adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE);
  59. adc_clock_config(ADC_CLK_SYSCLK_DIV6);
  60. adc_channel_length_config(ADC_REGULAR_CHANNEL, 1);
  61. adc_regular_channel_config(0, SOIL_MOISTURE_ADC_CHANNEL, ADC_SAMPLETIME_55POINT5);
  62. adc_enable();
  63. delay_1ms(1);
  64. adc_calibration_enable();
  65. adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
  66. }

  67. /* 控制函数 */
  68. void water_pump_on(void) {
  69. gpio_bit_set(WATER_PUMP_PORT, WATER_PUMP_PIN);
  70. watering_status = 1;
  71. printf("水泵开启\n");
  72. }

  73. void water_pump_off(void) {
  74. gpio_bit_reset(WATER_PUMP_PORT, WATER_PUMP_PIN);
  75. watering_status = 0;
  76. printf("水泵关闭\n");
  77. }

  78. void system_led_toggle(void) {
  79. gpio_bit_toggle(SYSTEM_LED_PORT, SYSTEM_LED_PIN);
  80. }

  81. /* 传感器读取函数 */
  82. uint16_t read_soil_moisture(void) {
  83. while(!adc_flag_get(ADC0, ADC_FLAG_EOC));
  84. uint16_t adc_value = adc_regular_data_read(ADC0);
  85. /* 转换为百分比 (需要根据实际传感器校准) */
  86. return (100 - ((adc_value * 100) / 4095));
  87. }

  88. uint8_t check_water_level(void) {
  89. return (gpio_input_bit_get(WATER_LEVEL_PORT, WATER_LEVEL_PIN) == RESET) ? 1 : 0;
  90. }

  91. /* 自动浇水逻辑 */
  92. void auto_watering_task(void) {
  93. soil_moisture = read_soil_moisture();
  94. water_level = check_water_level();

  95. if(watering_mode == 0) { /* 自动模式 */
  96. if(soil_moisture < MOISTURE_THRESHOLD_LOW && !watering_status && !water_level) {
  97. water_pump_on();
  98. watering_timer = WATERING_DURATION;
  99. }
  100. else if((soil_moisture > MOISTURE_THRESHOLD_HIGH && watering_status) || water_level) {
  101. water_pump_off();
  102. watering_timer = 0;
  103. }
  104. }

  105. /* 处理浇水定时器 */
  106. if(watering_timer > 0) {
  107. if(--watering_timer == 0) {
  108. water_pump_off();
  109. }
  110. }
  111. }

  112. // 解析 URL 参数
  113. int get_query_param(const char* request, const char* key, char* value, size_t val_size) {
  114. const char* query_start = strchr(request, '?');
  115. if (!query_start) return 0;
  116. const char* key_pos = strstr(query_start, key);
  117. if (!key_pos) return 0;

  118. const char* eq = strchr(key_pos, '=');
  119. if (!eq) return 0;
  120. eq++;

  121. const char* end = eq;
  122. while (*end && *end != ' ' && *end != '\r' && *end != '&' && *end != '\n') {
  123. end++;
  124. }

  125. size_t len = (end - eq) < (val_size - 1) ? (end - eq) : (val_size - 1);
  126. memcpy(value, eq, len);
  127. value[len] = '\0';
  128. return 1;
  129. }

  130. // 发送 HTTP 响应
  131. void send_response(int fd, const char* content_type, const char* body) {
  132. char header[256];
  133. int len = strlen(body);
  134. snprintf(header, sizeof(header),
  135. "HTTP/1.1 200 OK\r\n"
  136. "Content-Type: %s\r\n"
  137. "Content-Length: %d\r\n"
  138. "Connection: close\r\n"
  139. "\r\n", content_type, len);
  140. write(fd, header, strlen(header));
  141. write(fd, body, len);
  142. }

  143. static void http_server_serve(int conn_fd) {
  144. char buffer[1024];
  145. int len = read(conn_fd, buffer, sizeof(buffer) - 1);
  146. if (len <= 0) {
  147. close(conn_fd);
  148. return;
  149. }
  150. buffer[len] = '\0';

  151. printf("请求: %.100s\n", buffer);

  152. // API 接口处理
  153. if (strncmp(buffer, "GET /api/", 8) == 0) {
  154. char action[16] = {0};
  155. char value[16] = {0};

  156. if (strncmp(buffer, "GET /api/control", 16) == 0) {
  157. if (get_query_param(buffer, "action", action, sizeof(action))) {
  158. if (strcmp(action, "on") == 0) {
  159. watering_mode = 2; /* 手动模式 */
  160. water_pump_on();
  161. watering_timer = WATERING_DURATION;
  162. } else if (strcmp(action, "off") == 0) {
  163. water_pump_off();
  164. } else if (strcmp(action, "auto") == 0) {
  165. watering_mode = 0; /* 自动模式 */
  166. } else if (strcmp(action, "timer") == 0) {
  167. watering_mode = 1; /* 定时模式 */
  168. }
  169. }
  170. }
  171. else if (strncmp(buffer, "GET /api/timer", 14) == 0) {
  172. if (get_query_param(buffer, "set", value, sizeof(value))) {
  173. next_watering_time = atoi(value) * 60 * 1000; /* 转换为毫秒 */
  174. printf("定时浇水设置: %s分钟后\n", value);
  175. }
  176. }

  177. /* 返回系统状态JSON */
  178. char json[256];
  179. const char* mode_str[] = {"自动", "定时", "手动"};
  180. const char* pump_status = watering_status ? "开启" : "关闭";
  181. const char* water_level_str = water_level ? "低水位" : "正常";

  182. snprintf(json, sizeof(json),
  183. "{"mode":"%s","pump":"%s","moisture":%d,"water_level":"%s","timer":%lu}",
  184. mode_str[watering_mode], pump_status, soil_moisture, water_level_str,
  185. watering_timer / 1000);

  186. send_response(conn_fd, "application/json", json);
  187. close(conn_fd);
  188. return;
  189. }

  190. // 返回智能浇水系统网页
  191. const char* html =
  192. "<!DOCTYPE html>"
  193. "<html lang="zh-CN">"
  194. "<head>"
  195. " <meta charset="UTF-8">"
  196. " <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">"
  197. " <title>智能浇水系统 - GD32VW553</title>"
  198. " <style>"
  199. " body {"
  200. " margin: 0; padding: 20px;"
  201. " font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;"
  202. " background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);"
  203. " min-height: 100vh; color: #333;"
  204. " }"
  205. " .container { max-width: 500px; margin: 0 auto; background: rgba(255,255,255,0.95); border-radius: 20px; padding: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); }"
  206. " h1 { text-align: center; color: #2c3e50; margin-bottom: 10px; }"
  207. " .subtitle { text-align: center; color: #7f8c8d; margin-bottom: 30px; }"
  208. " .status-card { background: #ecf0f1; border-radius: 15px; padding: 20px; margin: 15px 0; }"
  209. " .status-item { display: flex; justify-content: space-between; margin: 10px 0; }"
  210. " .moisture-bar { height: 20px; background: #bdc3c7; border-radius: 10px; overflow: hidden; margin: 10px 0; }"
  211. " .moisture-fill { height: 100%; background: linear-gradient(90deg, #e74c3c, #f39c12, #27ae60); transition: width 0.5s; }"
  212. " .btn-group { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 15px 0; }"
  213. " .btn { padding: 12px; border: none; border-radius: 10px; font-size: 16px; font-weight: bold; cursor: pointer; transition: all 0.3s; }"
  214. " .btn-primary { background: #3498db; color: white; }"
  215. " .btn-success { background: #27ae60; color: white; }"
  216. " .btn-warning { background: #f39c12; color: white; }"
  217. " .btn-danger { background: #e74c3c; color: white; }"
  218. " .btn:active { transform: scale(0.98); }"
  219. " .timer-set { display: flex; gap: 10px; margin: 15px 0; }"
  220. " .timer-input { flex: 1; padding: 12px; border: 2px solid #bdc3c7; border-radius: 10px; font-size: 16px; }"
  221. " .alert { padding: 10px; border-radius: 5px; margin: 10px 0; text-align: center; }"
  222. " .alert-warning { background: #fff3cd; color: #856404; border: 1px solid #ffeaa7; }"
  223. " .footer { text-align: center; margin-top: 30px; color: #7f8c8d; font-size: 14px; }"
  224. " </style>"
  225. "</head>"
  226. "<body>"
  227. " <div class="container">"
  228. " <h1>🌱 智能浇水系统</h1>"
  229. " <div class="subtitle">GD32VW553 · 萤火工场 · 我爱小易</div>"
  230. " "
  231. " <div class="status-card">"
  232. " <h3>📊 系统状态</h3>"
  233. " <div class="status-item"><span>工作模式:</span><span id="mode">自动</span></div>"
  234. " <div class="status-item"><span>水泵状态:</span><span id="pump">关闭</span></div>"
  235. " <div class="status-item"><span>土壤湿度:</span><span id="moisture">0%</span></div>"
  236. " <div class="moisture-bar"><div id="moistureBar" class="moisture-fill" style="width: 0%"></div></div>"
  237. " <div class="status-item"><span>水位状态:</span><span id="waterLevel">正常</span></div>"
  238. " <div class="status-item"><span>剩余时间:</span><span id="timer">0秒</span></div>"
  239. " </div>"
  240. " "
  241. " <div id="waterAlert" class="alert alert-warning" style="display:none;">⚠️ 水位过低,浇水已停止!</div>"
  242. " "
  243. " <div class="btn-group">"
  244. " <button class="btn btn-primary" onclick="setMode('auto')">🤖 自动模式</button>"
  245. " <button class="btn btn-warning" onclick="setMode('timer')">⏰ 定时模式</button>"
  246. " </div>"
  247. " "
  248. " <div class="btn-group">"
  249. " <button class="btn btn-success" onclick="controlPump('on')">💧 开始浇水</button>"
  250. " <button class="btn btn-danger" onclick="controlPump('off')">🛑 停止浇水</button>"
  251. " </div>"
  252. " "
  253. " <div style="margin: 20px 0;">"
  254. " <h4>定时浇水设置:</h4>"
  255. " <div class="timer-set">"
  256. " <input type="number" id="timerInput" class="timer-input" placeholder="分钟后浇水" min="1" max="1440">"
  257. " <button class="btn btn-warning" onclick="setTimer()">设置定时</button>"
  258. " </div>"
  259. " </div>"
  260. " "
  261. " <div class="footer">"
  262. " 连接开发板 Wi-Fi 即可远程控制<br>"
  263. " 系统将自动监测土壤湿度和水位"
  264. " </div>"
  265. " </div>"
  266. " "
  267. " <script>"
  268. " let updateInterval;"
  269. " "
  270. " function updateStatus() {"
  271. " fetch('/api/control?action=status')"
  272. " .then(res => res.json())"
  273. " .then(data => {"
  274. " document.getElementById('mode').textContent = data.mode;"
  275. " document.getElementById('pump').textContent = data.pump;"
  276. " document.getElementById('moisture').textContent = data.moisture + '%';"
  277. " document.getElementById('waterLevel').textContent = data.water_level;"
  278. " document.getElementById('timer').textContent = data.timer + '秒';"
  279. " "
  280. " document.getElementById('moistureBar').style.width = data.moisture + '%';"
  281. " "
  282. " const alert = document.getElementById('waterAlert');"
  283. " alert.style.display = data.water_level === '低水位' ? 'block' : 'none';"
  284. " "
  285. " // 根据湿度改变颜色"
  286. " const moisture = data.moisture;"
  287. " if(moisture < 30) {"
  288. " document.getElementById('moistureBar').style.background = '#e74c3c';"
  289. " } else if(moisture < 60) {"
  290. " document.getElementById('moistureBar').style.background = '#f39c12';"
  291. " } else {"
  292. " document.getElementById('moistureBar').style.background = '#27ae60';"
  293. " }"
  294. " })"
  295. " .catch(err => console.error('更新状态失败:', err));"
  296. " }"
  297. " "
  298. " function setMode(mode) {"
  299. " fetch('/api/control?action=' + mode)"
  300. " .then(() => updateStatus())"
  301. " .catch(err => console.error('设置模式失败:', err));"
  302. " }"
  303. " "
  304. " function controlPump(action) {"
  305. " fetch('/api/control?action=' + action)"
  306. " .then(() => updateStatus())"
  307. " .catch(err => console.error('控制水泵失败:', err));"
  308. " }"
  309. " "
  310. " function setTimer() {"
  311. " const minutes = document.getElementById('timerInput').value;"
  312. " if(minutes && minutes > 0) {"
  313. " fetch('/api/timer?set=' + minutes)"
  314. " .then(() => {"
  315. " updateStatus();"
  316. " document.getElementById('timerInput').value = '';"
  317. " alert('定时浇水设置成功: ' + minutes + '分钟后');"
  318. " })"
  319. " .catch(err => console.error('设置定时失败:', err));"
  320. " }"
  321. " }"
  322. " "
  323. " // 页面加载时开始定时更新"
  324. " document.addEventListener('DOMContentLoaded', function() {"
  325. " updateStatus();"
  326. " updateInterval = setInterval(updateStatus, 2000); // 每2秒更新一次"
  327. " });"
  328. " "
  329. " // 页面卸载时清除定时器"
  330. " window.addEventListener('beforeunload', function() {"
  331. " clearInterval(updateInterval);"
  332. " });"
  333. " </script>"
  334. "</body>"
  335. "</html>";

  336. send_response(conn_fd, "text/html; charset=utf-8", html);
  337. close(conn_fd);
  338. }

  339. static void http_server_task(void *arg) {
  340. int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
  341. if (listen_fd < 0) {
  342. printf("创建 Socket 失败!\n");
  343. return;
  344. }

  345. struct sockaddr_in server_addr = {0};
  346. server_addr.sin_family = AF_INET;
  347. server_addr.sin_port = htons(HTTP_PORT);
  348. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

  349. if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
  350. printf("绑定端口失败!\n");
  351. close(listen_fd);
  352. return;
  353. }

  354. if (listen(listen_fd, 5) < 0) {
  355. printf("监听失败!\n");
  356. close(listen_fd);
  357. return;
  358. }

  359. printf("智能浇水系统已启动!\n");
  360. printf("请连接 Wi-Fi: %s, 密码: %s\n", AP_SSID, AP_PASSWORD);
  361. printf("然后访问: http://192.168.237.1\n");

  362. while (1) {
  363. struct sockaddr_in client_addr;
  364. socklen_t len = sizeof(client_addr);
  365. int conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &len);
  366. if (conn_fd >= 0) {
  367. printf("客户端连接: %s\n", inet_ntoa(client_addr.sin_addr));
  368. http_server_serve(conn_fd);
  369. }

  370. /* 在主循环中处理自动浇水任务 */
  371. auto_watering_task();
  372. system_led_toggle();
  373. delay_1ms(100); /* 短暂延时 */
  374. }
  375. }

  376. static void system_monitor_task(void *param) {
  377. while(1) {
  378. auto_watering_task();
  379. system_led_toggle();
  380. delay_1ms(1000); /* 1秒间隔 */
  381. }
  382. }

  383. static void wifi_ap_task(void *param) {
  384. printf("正在启动智能浇水系统 AP...\n");
  385. int ret = wifi_management_ap_start((char *)AP_SSID, (char *)AP_PASSWORD, 1, AUTH_MODE_WPA_WPA2, 0);
  386. if (ret != 0) {
  387. printf("AP 启动失败!错误码: %d\n", ret);
  388. sys_task_delete(NULL);
  389. return;
  390. }
  391. printf("AP 启动成功: %s\n", AP_SSID);

  392. /* 初始化硬件 */
  393. hardware_init();

  394. /* 创建系统监控任务 */
  395. sys_task_create_dynamic((const uint8_t *)"monitor_task", 2048, OS_TASK_PRIORITY(1), system_monitor_task, NULL);

  396. /* 启动HTTP服务器 */
  397. http_server_task(NULL);

  398. sys_task_delete(NULL);
  399. }

  400. int main(void) {
  401. platform_init();

  402. if (wifi_init()) {
  403. printf("Wi-Fi 初始化失败!\n");
  404. while (1);
  405. }

  406. /* 创建AP任务 */
  407. sys_task_create_dynamic((const uint8_t *)"ap_task", 4096, OS_TASK_PRIORITY(0), wifi_ap_task, NULL);
  408. sys_os_start();

  409. while (1);
  410. }
主要新增功能:​​土壤湿度检测​​ - 实时读取并显示湿度值​​水位检测​​ - 监测水位状态,低水位时自动停止浇水​​三种工作模式​​:🤖 自动模式:根据湿度自动控制⏰ 定时模式:设置定时浇水👆 手动模式:远程手动控制​​实时状态显示​​ - 湿度进度条、水位警告、倒计时等​​响应式界面​​ - 美观的现代化UI设计使用说明:​​编译烧录​​后,设备会创建WiFi热点 SmartWatering_AP​​手机连接​​该热点,密码 12345678​​浏览器访问​​ http://192.168.237.1​​实时监控​​土壤湿度和系统状态​​远程控制​​浇水模式和定时设置硬件连接建议:水泵继电器 → PB2土壤湿度传感器 → ADC0通道0 (PA0)水位传感器 → PA1状态LED → PB0这个系统具备了您需要的所有功能,界面美观易用,适合实际部署使用!
可惜这不知道哪里有问题,元宝也解决不了。
在画个板子整合的好看点,可惜俺是新人得一点点学,ai给的代码总是有问题,第一次测试的时候是能用的,我后面重新烧录之后就出现通电就掉wifi,继电器持续运行等问题,只能以后有空再解决,简单的功能是勉强实现了
以下是我最后能用的简化版,还有一些调整其他文件,可能这就是造成我第二次无法使用的原因。
  1. /*!
  2. \file main.c
  3. \brief GD32VW553 SoftAP + 中文 Captive Portal + 无刷新 水泵控制
  4. \author 作者我爱小易
  5. \company 萤火工场
  6. */

  7. #include <stdint.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include "gd32vw55x.h"
  11. #include "app_cfg.h"
  12. #include "gd32vw55x_platform.h"
  13. #include "lwip/sockets.h"
  14. #include "lwip/priv/sockets_priv.h"
  15. #include "wifi_management.h"
  16. #include "wifi_init.h"

  17. #define PUMP_PORT GPIOB
  18. #define PUMP_PIN GPIO_PIN_2

  19. #define AP_SSID "GD32_PUMP_AP"
  20. #define AP_PASSWORD "12345678"
  21. #define HTTP_PORT 80

  22. void pump_init(void) {
  23. rcu_periph_clock_enable(RCU_GPIOB);
  24. gpio_mode_set(PUMP_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, PUMP_PIN);
  25. gpio_output_options_set(PUMP_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, PUMP_PIN);
  26. gpio_bit_reset(PUMP_PORT, PUMP_PIN); // 初始状态关闭水泵
  27. }

  28. void pump_on(void) {
  29. gpio_bit_set(PUMP_PORT, PUMP_PIN);
  30. printf("水泵开启\n");
  31. }

  32. void pump_off(void) {
  33. gpio_bit_reset(PUMP_PORT, PUMP_PIN);
  34. printf("水泵关闭\n");
  35. }

  36. // 解析 URL 参数
  37. int get_query_param(const char* request, const char* key, char* value, size_t val_size) {
  38. const char* query_start = strchr(request, '?');
  39. if (!query_start) return 0;
  40. const char* key_pos = strstr(query_start, key);
  41. if (!key_pos) return 0;

  42. const char* eq = strchr(key_pos, '=');
  43. if (!eq) return 0;
  44. eq++;

  45. const char* end = eq;
  46. while (*end && *end != ' ' && *end != '\r' && *end != '&' && *end != '\n') {
  47. end++;
  48. }

  49. size_t len = (end - eq) < (val_size - 1) ? (end - eq) : (val_size - 1);
  50. memcpy(value, eq, len);
  51. value[len] = '\0';
  52. return 1;
  53. }

  54. // 发送 HTTP 响应
  55. void send_response(int fd, const char* content_type, const char* body) {
  56. char header[256];
  57. int len = strlen(body);
  58. snprintf(header, sizeof(header),
  59. "HTTP/1.1 200 OK\r\n"
  60. "Content-Type: %s\r\n"
  61. "Content-Length: %d\r\n"
  62. "Connection: close\r\n"
  63. "\r\n", content_type, len);
  64. write(fd, header, strlen(header));
  65. write(fd, body, len);
  66. }

  67. static void http_server_serve(int conn_fd) {
  68. char buffer[1024];
  69. int len = read(conn_fd, buffer, sizeof(buffer) - 1);
  70. if (len <= 0) {
  71. close(conn_fd);
  72. return;
  73. }
  74. buffer[len] = '\0';

  75. printf("请求: %.100s\n", buffer);

  76. // 处理 API:/api/control?action=on/off
  77. if (strncmp(buffer, "GET /api/control", 16) == 0) {
  78. char action[16] = {0};
  79. if (get_query_param(buffer, "action", action, sizeof(action))) {
  80. if (strcmp(action, "on") == 0) {
  81. pump_on();
  82. } else if (strcmp(action, "off") == 0) {
  83. pump_off();
  84. }
  85. }
  86. const char* status = gpio_input_bit_get(PUMP_PORT, PUMP_PIN) ? "on" : "off";
  87. char json[64];
  88. snprintf(json, sizeof(json), "{"status":"%s"}", status);
  89. send_response(conn_fd, "application/json", json);
  90. close(conn_fd);
  91. return;
  92. }

  93. // 返回中文美化页面(修改后)
  94. const char* html =
  95. "<!DOCTYPE html>"
  96. "<html lang="zh-CN">"
  97. "<head>"
  98. " <meta charset="UTF-8">"
  99. " <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">"
  100. " <title>GD32VW553 水泵远程控制</title>"
  101. " <style>"
  102. " body {"
  103. " margin: 0; padding: 0;"
  104. " font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;"
  105. " background: #ffffff;"
  106. " height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center;"
  107. " color: #2c3e50; text-align: center;"
  108. " }"
  109. " .container { "
  110. " padding: 40px 30px; "
  111. " max-width: 480px; "
  112. " background: #f8f9fa;"
  113. " border-radius: 20px;"
  114. " box-shadow: 0 10px 30px rgba(0,0,0,0.08);"
  115. " }"
  116. " h1 { font-size: 32px; margin: 10px 0; color: #2c3e50; font-weight: 700; }"
  117. " .subtitle { font-size: 16px; opacity: 0.7; margin-bottom: 30px; color: #5a6c7d; }"
  118. " .pump-status { "
  119. " width: 140px; height: 140px; border-radius: 50%; margin: 30px auto; "
  120. " transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); "
  121. " display: flex; align-items: center; justify-content: center; "
  122. " font-size: 60px; border: 3px solid #e9ecef;"
  123. " }"
  124. " .pump-status.on { "
  125. " background: linear-gradient(135deg, #00b894, #00a085); "
  126. " box-shadow: 0 10px 25px rgba(0, 184, 148, 0.3); "
  127. " border-color: #00b894;"
  128. " }"
  129. " .pump-status.off { "
  130. " background: linear-gradient(135deg, #dfe6e9, #b2bec3); "
  131. " box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05); "
  132. " border-color: #dfe6e9;"
  133. " }"
  134. " .status { font-size: 24px; margin: 20px 0; font-weight: 600; color: #2c3e50; }"
  135. " .btn {"
  136. " display: block; width: 100%; padding: 20px;"
  137. " font-size: 20px; font-weight: 600; border: none; border-radius: 15px;"
  138. " background: linear-gradient(135deg, #3498db, #2980b9);"
  139. " color: white; cursor: pointer; margin-top: 25px;"
  140. " transition: all 0.3s ease;"
  141. " box-shadow: 0 5px 15px rgba(52, 152, 219, 0.3);"
  142. " letter-spacing: 1px;"
  143. " }"
  144. " .btn.on { "
  145. " background: linear-gradient(135deg, #00b894, #00a085);"
  146. " box-shadow: 0 5px 15px rgba(0, 184, 148, 0.3);"
  147. " }"
  148. " .btn.off { "
  149. " background: linear-gradient(135deg, #e74c3c, #c0392b);"
  150. " box-shadow: 0 5px 15px rgba(231, 76, 60, 0.3);"
  151. " }"
  152. " .btn:hover { "
  153. " transform: translateY(-2px); "
  154. " box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);"
  155. " }"
  156. " .btn:active { transform: translateY(0); }"
  157. " .footer { margin-top: 25px; font-size: 14px; opacity: 0.6; color: #7f8c8d; }"
  158. " .warning { "
  159. " background: #fff3cd; padding: 12px; border-radius: 10px; "
  160. " margin: 20px 0; font-size: 14px; color: #856404;"
  161. " border-left: 4px solid #ffc107;"
  162. " }"
  163. " [url=/u/media]@media[/url] (max-width: 480px) {"
  164. " .container { padding: 30px 20px; margin: 20px; }"
  165. " h1 { font-size: 28px; }"
  166. " .pump-status { width: 120px; height: 120px; font-size: 50px; }"
  167. " }"
  168. " </style>"
  169. "</head>"
  170. "<body>"
  171. " <div class="container">"
  172. " <h1>GD32VW553 水泵远程控制</h1>"
  173. " <div class="subtitle">作者:我爱小易|出品:萤火工场</div>"
  174. " <div id="pumpStatus" class="pump-status off">💧</div>"
  175. " <div id="status" class="status">水泵状态:关闭</div>"
  176. " <div class="warning">⚠️ 远程控制水泵,请确保安全操作</div>"
  177. " <button id="controlBtn" class="btn off">启动水泵</button>"
  178. " <div class="footer">连接开发板 Wi-Fi 即可远程控制</div>"
  179. " </div>"
  180. ""
  181. " <script>"
  182. " let isPumpOn = false;"
  183. ""
  184. " function updateUI() {"
  185. " const pumpStatus = document.getElementById('pumpStatus');"
  186. " const status = document.getElementById('status');"
  187. " const btn = document.getElementById('controlBtn');"
  188. " if (isPumpOn) {"
  189. " pumpStatus.className = 'pump-status on';"
  190. " pumpStatus.textContent = '💦';"
  191. " status.textContent = '水泵状态:运行中';"
  192. " btn.textContent = '停止水泵';"
  193. " btn.className = 'btn on';"
  194. " } else {"
  195. " pumpStatus.className = 'pump-status off';"
  196. " pumpStatus.textContent = '💧';"
  197. " status.textContent = '水泵状态:关闭';"
  198. " btn.textContent = '启动水泵';"
  199. " btn.className = 'btn off';"
  200. " }"
  201. " }"
  202. ""
  203. " function sendAction(action) {"
  204. " fetch('/api/control?action=' + action)"
  205. " .then(res => res.json())"
  206. " .then(data => {"
  207. " isPumpOn = (data.status === 'on');"
  208. " updateUI();"
  209. " })"
  210. " .catch(err => console.error('请求失败:', err));"
  211. " }"
  212. ""
  213. " document.getElementById('controlBtn').addEventListener('click', () => {"
  214. " sendAction(isPumpOn ? 'off' : 'on');"
  215. " });"
  216. ""
  217. " // 初始化:获取当前状态"
  218. " fetch('/api/control?action=get')"
  219. " .then(res => res.json())"
  220. " .then(data => {"
  221. " isPumpOn = (data.status === 'on');"
  222. " updateUI();"
  223. " });"
  224. " </script>"
  225. "</body>"
  226. "</html>";

  227. send_response(conn_fd, "text/html; charset=utf-8", html);
  228. close(conn_fd);
  229. }

  230. static void http_server_task(void *arg) {
  231. int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
  232. if (listen_fd < 0) {
  233. printf("创建 Socket 失败!\n");
  234. return;
  235. }

  236. struct sockaddr_in server_addr = {0};
  237. server_addr.sin_family = AF_INET;
  238. server_addr.sin_port = htons(HTTP_PORT);
  239. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

  240. if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
  241. printf("绑定端口失败!\n");
  242. close(listen_fd);
  243. return;
  244. }

  245. if (listen(listen_fd, 5) < 0) {
  246. printf("监听失败!\n");
  247. close(listen_fd);
  248. return;
  249. }

  250. printf("HTTP 服务器已启动,请连接 Wi-Fi 后访问 http://192.168.237.1\n");

  251. while (1) {
  252. struct sockaddr_in client_addr;
  253. socklen_t len = sizeof(client_addr);
  254. int conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &len);
  255. if (conn_fd >= 0) {
  256. printf("客户端已连接:%s\n", inet_ntoa(client_addr.sin_addr));
  257. http_server_serve(conn_fd);
  258. }
  259. }
  260. }

  261. static void wifi_ap_task(void *param) {
  262. printf("正在启动 SoftAP:%s ...\n", AP_SSID);
  263. int ret = wifi_management_ap_start((char *)AP_SSID, (char *)AP_PASSWORD, 1, AUTH_MODE_WPA_WPA2, 0);
  264. if (ret != 0) {
  265. printf("SoftAP 启动失败!错误码:%d\n", ret);
  266. sys_task_delete(NULL);
  267. return;
  268. }
  269. printf("SoftAP 已启动,SSID:%s,密码:%s\n", AP_SSID, AP_PASSWORD);
  270. pump_init();
  271. http_server_task(NULL);
  272. sys_task_delete(NULL);
  273. }

  274. int main(void) {
  275. platform_init();

  276. if (wifi_init()) {
  277. printf("Wi-Fi 初始化失败!\n");
  278. while (1);
  279. }

  280. sys_task_create_dynamic((const uint8_t *)"ap_task", 4096, OS_TASK_PRIORITY(0), wifi_ap_task, NULL);
  281. sys_os_start();

  282. while (1);
  283. }
总结,这个板子是一款很适合新人尝试的开发板,可惜成熟的项目还是太少,相对主流板子,希望以后优秀的项目能越来越多




磨砂 发表于 2025-11-3 11:46 | 显示全部楼层
GD32VW553-IOT-V2是一款基于RISC-V架构的物联网开发板,由萤火工场基于兆易创新的GD32VW553HMQ7主控芯片设计
晓伍 发表于 2025-11-3 14:12 | 显示全部楼层
该芯片为RISC-V架构,主频高达160MHz
八层楼 发表于 2025-11-3 16:33 | 显示全部楼层
支持Wi-Fi 6和BLE 5.2双模无线通信,适用于低功耗物联网场景
观海 发表于 2025-11-3 19:03 | 显示全部楼层
集成CH340N芯片,支持供电、程序下载及串口通信,无需外接调试模块
guanjiaer 发表于 2025-11-3 21:19 | 显示全部楼层
新增用户按键和指示灯,优化背面引脚丝印,提升交互便利性
heimaojingzhang 发表于 2025-11-4 07:43 | 显示全部楼层
PCB厚度增至1mm,增强抗变形能力
keaibukelian 发表于 2025-11-4 09:59 | 显示全部楼层
FLASH焊接位移至背面,降低二次开发焊接风险
paotangsan 发表于 2025-11-4 12:16 | 显示全部楼层
通过0Ω电阻隔离数字电路与MCU专用电源,优先保障核心供电稳定性
renzheshengui 发表于 2025-11-4 14:45 | 显示全部楼层
BOOT开关升级为拨码式设计,避免跳线帽操作不便的问题,支持从SIP Flash、ROM或SRAM等多区域启动
wowu 发表于 2025-11-4 17:08 | 显示全部楼层
BOOT开关升级为拨码式设计,避免跳线帽操作不便的问题,支持从SIP Flash、ROM或SRAM等多区域启动
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

19

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部