- const express = require('express');
- const app = express();
- // 存储最新数据(内存中)
- let latestData = { value: null, time: null };
- // 提供静态页面(可选)
- app.get('/', (req, res) => {
- res.sendFile(__dirname + '/index.html'); // 见下方的HTML文件
- });
- // 接收传感器数据的API(WiFi模块POST到此接口)
- app.post('/data', express.json(), (req, res) => {
- latestData = {
- sensor: req.body.sensor || 'unknown',
- value: req.body.value|| 0,
- time: new Date().toLocaleTimeString()
- };
- res.send('OK');
- });
- // SSE 路由 - 向浏览器推送数据
- app.get('/sse', (req, res) => {
- // 设置SSE所需的响应头
- res.setHeader('Content-Type', 'text/event-stream');
- res.setHeader('Cache-Control', 'no-cache');
- res.setHeader('Connection', 'keep-alive');
- // 首次连接立即发送当前数据
- sendSSEData(res);
- // 定时推送数据(示例:每2秒一次)
- const intervalId = setInterval(() => {
- sendSSEData(res);
- }, 2000);
- // 客户端断开连接时清理定时器
- req.on('close', () => {
- clearInterval(intervalId);
- console.log('Client disconnected');
- });
- });
- // 发送SSE数据(格式必须符合SSE协议)
- function sendSSEData(res) {
- const data = {
- sensor: latestData.sensor || 'N/A',
- value: latestData.value || 'N/A',
- time: latestData.time || 'N/A'
- };
- // SSE数据格式要求:
- // - "data:" 开头 + 实际数据 + 两个换行符
- res.write(`data: ${JSON.stringify(data)}\n\n`);
- }
- // 启动服务器
- const PORT = 3000;
- app.listen(PORT, () => {
- console.log(`Server running at http://localhost:${PORT}`);
- });
3、创建index.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>SSE Example with Grid</title>
- <style>
- body { font-family: Arial; text-align: center; margin-top: 50px; }
- #data { font-size: 24px; margin: 20px; padding: 20px; border: 0px solid #ddd; }
-
- body {
- margin: 0;
- overflow: hidden;
- }
- #grid {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- pointer-events: none; /* 防止网格干扰鼠标事件 */
- }
- .grid-line {
- stroke: #ccc;
- stroke-width: 1;
- }
- #square {
- width: 50px;
- height: 50px;
- background-color: red;
- position: absolute;
- top: 0; /* 初始位置调整为网格起点 */
- left: 0; /* 初始位置调整为网格起点 */
- }
- </style>
- </head>
- <body>
- <svg id="grid"></svg>
- <div id="square"></div>
-
- <h1>利用PSOC™ 4000T Multi-Sense Kit隔空按键产生指令<br><small>发送给WebServer-控制红色方块运行</small></h1>
- <div id="data">等待数据...</div>
-
- <script>
- const square = document.getElementById('square');
- const step = 50; // 网格大小和方块大小一致
- const gridSize = 50; // 网格大小
- // 创建网格
- const grid = document.getElementById('grid');
- const width = window.innerWidth;
- const height = window.innerHeight;
- for (let x = 0; x <= width; x += gridSize) {
- const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
- line.setAttribute('x1', x);
- line.setAttribute('y1', 0);
- line.setAttribute('x2', x);
- line.setAttribute('y2', height);
- line.classList.add('grid-line');
- grid.appendChild(line);
- }
- for (let y = 0; y <= height; y += gridSize) {
- const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
- line.setAttribute('x1', 0);
- line.setAttribute('y1', y);
- line.setAttribute('x2', width);
- line.setAttribute('y2', y);
- line.classList.add('grid-line');
- grid.appendChild(line);
- }
- var lasttime;
- const eventSource = new EventSource('/sse');
- eventSource.onmessage = function(event) {
- const instruction = event.data;
-
-
-
- const data = JSON.parse(event.data);
-
- document.getElementById('data').innerHTML = `
- <p>传感器: ${data.sensor || '无'}</p>
- <p>数值: ${data.value || '0'}</p>
- <p>更新时间: ${data.time || '未知'}</p>
- `;
-
- if(lasttime!=data.time)
- {
-
- let currentTop = parseInt(square.offsetTop, 10);
- let currentLeft = parseInt(square.offsetLeft, 10);
- switch (data.value) {
- case 'up':
- currentTop = Math.max(0, currentTop - step);
- break;
- case 'down':
- currentTop = Math.min(height - square.offsetHeight, currentTop + step);
- break;
- case 'left':
- currentLeft = Math.max(0, currentLeft - step);
- break;
- case 'right':
- currentLeft = Math.min(width - square.offsetWidth, currentLeft + step);
- break;
- default:
-
- break;
- }
-
- // 更新方块位置
- square.style.top = `${currentTop}px`;
- square.style.left = `${currentLeft}px`;
- lasttime=data.time;
- }
- };
- </script>
- </body>
- </html>
4、运行server.js
- C:\Infineon\webserver>node server.js
- Server running at http://localhost:3000
此时websever已搭建完成并运行。
打开浏览器访问:http://localhost:3000
可以看到红色方形已绘制,等待接收http协议的数据进行执行并改变位置
三、MCU软件部分
基于例程MSCLP_CAPSENSE_Hover_Touch修改
1、初始化串口并使能中断
- /* Configure and enable the UART peripheral */
- Cy_SCB_UART_Init(scb_0_HW, &scb_0_config, &CYBSP_UART_context);
- Cy_SCB_UART_Enable(scb_0_HW);
- /* 配置 UART 中断 */
- Cy_SysInt_Init(&UART_IRQ_cfg, UART_InterruptHandler);
- NVIC_EnableIRQ(UART_IRQ_cfg.intrSrc); // 使能 NVIC 中断
- // Register the event callback function
- //Cy_SCB_UART_RegisterCallback(scb_0_HW,(cy_cb_scb_uart_handle_events_t)UART_InterruptHandler, &CYBSP_UART_context);
- // 当 RX FIFO 非空时启用 UART RX 中断
- Cy_SCB_SetRxInterruptMask(scb_0_HW , CY_SCB_RX_INTR_NOT_EMPTY);
- // 当 TX FIFO 未满时启用 UART TX 中断
- Cy_SCB_SetTxInterruptMask (scb_0_HW ,CY_SCB_TX_INTR_LEVEL);
- /* Enable global interrupts */
- __enable_irq();
RX中断处理:
- static void UART_InterruptHandler(void)
- {
- /* 检查是否 RX FIFO 非空 */
- if (Cy_SCB_GetRxInterruptStatusMasked(scb_0_HW) & CY_SCB_UART_RX_NOT_EMPTY)
- {
- /* 读取接收到的数据 */
- uint8_t rxData = Cy_SCB_UART_Get(scb_0_HW);
- /* 存入缓冲区(可改为环形缓冲区) */
- if (rxCount < RX_BUFFER_SIZE)
- {
- rxBuffer[rxCount++] = rxData;
- }
- /* 可选:检查结束符(如 '\n') */
- if (rxData == '\n'&&rxCount>2)
- {
- // 可以设置标志位通知主循环
- atResponseReady=true;
- }
- }
- /* 清除中断标志 */
- Cy_SCB_ClearRxInterrupt(scb_0_HW, CY_SCB_UART_RX_NOT_EMPTY);
- }
2、发送AT指令函数
- bool sendATCommand(const char* cmd, const char* expectedResponse, uint32_t timeout) {
- Cy_SCB_UART_PutString(scb_0_HW, cmd);
- Cy_SysLib_Delay(timeout);
- return true;
- }
3、连接wifi,关闭所有tcp连接
- void wifiConnect(void)
- {
- sendATCommand("AT+RST\r\n", "OK", AT_TIMEOUT_MS);
- sendATCommand("AT+CWJAP=ssid,passwordpassword\r\n", "OK", 5000);
- //closeAllConnections();
- sendATCommand("AT+CIPCLOSE=20\r\n", "OK", AT_TIMEOUT_MS);
- }
4、建立TCP连接并保存linkid
- bool tcpLink(const char* host)
- {
- char tcpCmd[128];
- snprintf(tcpCmd, sizeof(tcpCmd), "AT+CIPSTART=TCP,%s,3000\r\n", host);
- Cy_SCB_UART_PutString(scb_0_HW, tcpCmd);
- while(!atResponseReady){}
- sscanf(rxBuffer, "+CIPSTART:%d", &linkId);
- atResponseReady=false;
- rxCount=0;
- memset(rxBuffer,0,RX_BUFFER_SIZE);
- for(int i=0;i<linkId;i++)
- {
- Cy_GPIO_Set(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_SysLib_Delay(200);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_SysLib_Delay(200);
- }
- return true;
- }
5、发送HTTP POST请求
- // 发送HTTP POST请求
- void sendHttpPost(const char* url, const char* host, const char* jsonData) {
- char cipsendCmd[32];
- char postHeader[256];
- sendATCommand("AT+CIPCLOSE=20\r\n", "OK", 300);
- // 1. 建立TCP连接
- char tcpCmd[128];
- tcpLink(host);
- // 2. 透传,发送POST数据
- int dataLen = strlen(jsonData);
- snprintf(postHeader, sizeof(postHeader),
- "POST %s HTTP/1.1\r\n"
- "Host: %s\r\n"
- "Content-Type: application/json\r\n"
- "Content-Length: %d\r\n\r\n"
- "%s\r\n", url, host, dataLen, jsonData);
- snprintf(cipsendCmd, sizeof(cipsendCmd), "AT+CIPSEND=%d,%d\r\n", linkId,strlen(postHeader));
- sendATCommand(cipsendCmd, ">", 300);
- sendATCommand(postHeader, "OK", 1000);
- // 3. 关闭连接
- snprintf(cipsendCmd, sizeof(cipsendCmd), "AT+CIPCLOSE=%d\r\n", linkId);
- sendATCommand(cipsendCmd, "OK", 1000);
- }
通过POST向服务器发送json数据
格式{"sensor":"direction","value":"up"}
- void sendHttpData(uint8_t dat)
- {
- char* action[4]={
- "up","down","left","right"
- };
- const char* url = "/data";
- //const char* host = "192.168.31.45";
- const char* host = "192.168.8.11";
- //const char* jsonData = "{"sensor":"temp","value":25}";
- char jsonData[50]={0};
- snprintf(jsonData, sizeof(jsonData),
- "{"sensor":"direction","value":"%s"}",
- action[dat]);
- sendHttpPost(url, host, jsonData);
- }
6、悬停触摸处理
按下触摸执行点灯,并调用sendHttpData发送POST给webserver
- void led_control(){
- if(Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON1_WDGT_ID, &cy_capsense_context)){
- Cy_GPIO_Set(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED2_PORT, CYBSP_CS_HT_LED2_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED3_PORT, CYBSP_CS_HT_LED3_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED4_PORT, CYBSP_CS_HT_LED4_PIN);
- sendHttpData(0);
- }
- else if(Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON2_WDGT_ID, &cy_capsense_context)){
- Cy_GPIO_Clr(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_GPIO_Set(CYBSP_CS_HT_LED2_PORT, CYBSP_CS_HT_LED2_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED3_PORT, CYBSP_CS_HT_LED3_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED4_PORT, CYBSP_CS_HT_LED4_PIN);
- sendHttpData(1);
- }
- else if(Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON3_WDGT_ID, &cy_capsense_context)){
- Cy_GPIO_Clr(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED2_PORT, CYBSP_CS_HT_LED2_PIN);
- Cy_GPIO_Set(CYBSP_CS_HT_LED3_PORT, CYBSP_CS_HT_LED3_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED4_PORT, CYBSP_CS_HT_LED4_PIN);
- sendHttpData(2);
- }
- else if(Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON4_WDGT_ID, &cy_capsense_context)){
- Cy_GPIO_Clr(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED2_PORT, CYBSP_CS_HT_LED2_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED3_PORT, CYBSP_CS_HT_LED3_PIN);
- Cy_GPIO_Set(CYBSP_CS_HT_LED4_PORT, CYBSP_CS_HT_LED4_PIN);
- sendHttpData(3);
- }
- else {
- Cy_GPIO_Clr(CYBSP_CS_HT_LED1_PORT, CYBSP_CS_HT_LED1_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED2_PORT, CYBSP_CS_HT_LED2_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED3_PORT, CYBSP_CS_HT_LED3_PIN);
- Cy_GPIO_Clr(CYBSP_CS_HT_LED4_PORT, CYBSP_CS_HT_LED4_PIN);
- }
- }
四、运行效果
发送AT的串口输出: