RSL10-SENSE-DB-GEVK环境监测节点设计
本帖最后由 dql2016 于 2021-7-5 21:02 编辑<h3> </h3>
<p>我的本次作品有一个环境监测节点,正好充分利用 RSL10-SENSE-DB-GEVK的板载传感器:LV0104CS(环境光)、BME680(集成式高精度气体、压力、湿度和温度传感器)、INMP522(超低噪声数字麦克风)。原来调通了微信小程序与RSL10双向通信,RSL10端基于peripheral _server例程,后来在将传感器相关驱动、BDK相关组件添加到peripheral _server中后,无法同时采集数据和进行蓝牙通信了,原因在于RSL10-SENSE-DB-GEVK板卡的SDK和RSL10-002GEVB板卡的SDK相差太大,暂时没有精力去深究这个问题了,详见帖子:<strong>RSL10蓝牙特征值</strong>读写<a href="https://bbs.eeworld.com.cn/thread-1170300-1-1.html">https://bbs.eeworld.com.cn/thread-1170300-1-1.html</a>。因此只好另辟蹊径,周末抽空深入学习了RSL10-SENSE-DB-GEVK的SDK,在sense_ics_firmware例程的基础上实现了本次项目中环境监测节点的功能。环境监测节点的功能是将采集到的室内环境光、空气质量、温度、湿度、气压、声音通过蓝牙发送到微信小程序,并接受微信小程序下发的控制指令,当触发预设的规则后可以开启风扇等进行通风以改善室内空气质量,类似HAVC系统的功效,如图1所示。微信小程序拿到采集数据后,可以进行存储、分析,手机强大的性能或者是微信云平台的AI能力带来了无限可能。</p>
<p></p>
<p>图1 环境监测节点与微信小程序通信示意图</p>
<p>sense_ics_firmware例程通过自定义服务协议与安森美的官方手机app <strong>RSL10 Sense and Control</strong>通信 ,实现在手机app端显示板载传感器各项数据。在这个例程中,RSL10-SENSE-DB-GEVK板卡作为服务端,每种传感器、板载按键都被注册为一个服务节点,传感器采集的数据和按键状态则作为节点属性,客户端(手机app)向服务端请求读写属性值,这是一个典型的客户端-服务器模型。在这个协议中节点和属性用于访问具体的数据值,节点是属性的逻辑分组,它包含了一个具体设备的所有属性。属性是指可以被读取或者被写入的数据端点,每个节点具有的属性都是具体的。例如一个BLDC节点用于控制电动机,电动机具有可读可写的转速属性RPM,可写的方向属性DIR。请求令牌用于同步,客户端发出的每个请求包以‘0’---‘~’之间的随机数开头,服务端响应相同的请求令牌,因此两个不同的请求必须具有不同的请求令牌。分隔符'/'用于分割请求令牌、节点名、属性名、属性值。数据包长度可变,最大为20字节,一共有3中数据包:<br />
读请求包:用于索取给定属性的当前值。格式是<strong>请求令牌/节点名/属性名</strong>,例如s/ALS/LUX用于请求ALS节点的光照LUX属性值。<br />
写请求包:用于设置给定属性的新值。格式是<strong>请求令牌/节点名/属性名/属性值</strong> ,例如d/BLDC/TGT/2560用于请求改变BLDC节点的TGT属性为整数值2560。客户端需要知道给定属性的数据类型,规范定义了标准节点和节点属性发现协议。<br />
响应包:一旦客户端请求到达服务端立即发送,格式是<strong>请求令牌/数据类型/值</strong>。<br />
响应包数据类型有:i有符号整数;f浮点数;h十六进制数;s字符串;t多个连续字符串,读请求时是页数,写请求时是给定页数的内容,可用于保存节点类型、节点描述、设备名、配置文件等;e错误字符串;n节点定义;p属性定义;c复合值,需要在应用中解析,在响应包中不包含数据类型。数据包是ascill字符串形式。</p>
<p></p>
<p>CMSIS-Pack文件夹下面的文档<em><strong>SL10 BLE Custom Service communication protocol.pdf</strong></em>详细介绍了这个通信协议。如图2、3、4展示了蓝牙调试app请求服务的过程:</p>
<p>图2 蓝牙app请求数据</p>
<p>图3 服务端响应数据1</p>
<p>图4 服务端响应数据2</p>
<p> </p>
<p>本次项目不需要用到这个协议,只需要在这个例子的基础上进行一些修改即可。由于板卡硬件版本不支持光照传感器NOA1305取而代之的是LV0104CS,论坛大佬已分享相关帖子,这里我就直接把驱动拿来用,在此表示感谢。另外sense_ics_firmware例程也没有采集麦克风数据,因此对sense_ics_firmware例程的主要有2点:main.c函数中加入光照传感器LV0104CS和数字麦克风INMP522的初始化</p>
<pre>
<code class="language-cpp">//-----------------------------------------------------------------------------
// Copyright (c) 2018 Semiconductor Components Industries LLC
// (d/b/a "ON Semiconductor").All rights reserved.
// This software and/or documentation is licensed by ON Semiconductor under
// limited terms and conditions.The terms and conditions pertaining to the
// software and/or documentation are available at
// http://www.onsemi.com/site/pdf/ONSEMI_T&C.pdf ("ON Semiconductor Standard
// Terms and Conditions of Sale, Section 8 Software") and if applicable the
// software license agreement.Do not use this software and/or documentation
// unless you have carefully read and you agree to the limited terms and
// conditions.By using this software and/or documentation, you agree to the
// limited terms and conditions.
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <BDK.h>
#include <BSP_Components.h>
#include <BLE_Components.h>
#include <ics/CS.h>
#include <ics/CS_Nodes.h>
#include <CSN_PB.h> // PushButton custom node creation example
#include <lv0104cs.h>
#include <lv0104cs_lux.h>
#define AUDIO_DMIC0_GAIN 0x800
#define AUDIO_DMIC1_GAIN 0x800
#define AUDIO_OD_GAIN 0x800
#define AUDIO_CONFIG (OD_AUDIOSLOWCLK | \
DMIC_AUDIOCLK | \
DECIMATE_BY_64 | \
OD_UNDERRUN_PROTECT_ENABLE | \
OD_DATA_MSB_ALIGNED | \
DMIC0_DATA_LSB_ALIGNED | \
DMIC1_DATA_LSB_ALIGNED | \
OD_DMA_REQ_DISABLE | \
DMIC0_DMA_REQ_DISABLE | \
DMIC1_DMA_REQ_DISABLE | \
OD_INT_GEN_DISABLE | \
DMIC0_INT_GEN_ENABLE | \
DMIC1_INT_GEN_DISABLE | \
OD_DISABLE | \
DMIC0_ENABLE | \
DMIC1_DISABLE)
int32_t dmic_value = 0;
int32_t dmic_max = 0;
int32_t dmic_min = INT32_MAX;
int32_t lv0104cs_status = -1;
int main(void)
{
int32_t retval = 0;
/* Initialize all needed BDK components. */
BDK_Initialize();
/* Initialize all LEDs. */
LED_Initialize(LED_RED);
LED_Initialize(LED_GREEN);
LED_Initialize(LED_BLUE);
//Initialize LV0104CS.
retval = LV0104CS_LUX_Initialize();
if (retval == LV0104CS_OK)
{
retval = LV0104CS_LUX_StartContinuous(0, NULL);
if (retval == LV0104CS_OK)
{
lv0104cs_status= 0;
}
else
{
lv0104cs_status= 2;
}
}
else
{
lv0104cs_status= 1;
}
printf("LV0104CS initialization: %s\n", lv0104cs_status == 0 ? "OK":"ERROR");
//Configure DMIC input to test INMP522 microphone.
// Configure AUDIOCLK to 2 MHz and AUDIOSLOWCLK to 1 MHz.
CLK->DIV_CFG1 &= ~(AUDIOCLK_PRESCALE_64 | AUDIOSLOWCLK_PRESCALE_4);
CLK->DIV_CFG1 |= AUDIOCLK_PRESCALE_4 | AUDIOSLOWCLK_PRESCALE_2;
//Configure OD, DMIC0 and DMIC1
Sys_Audio_Set_Config(AUDIO_CONFIG);
Sys_Audio_Set_DMICConfig(DMIC0_DCRM_CUTOFF_20HZ | DMIC1_DCRM_CUTOFF_20HZ |
DMIC1_DELAY_DISABLE | DMIC0_FALLING_EDGE |
DMIC1_RISING_EDGE, 0);
Sys_Audio_DMICDIOConfig(DIO_6X_DRIVE | DIO_LPF_DISABLE | DIO_NO_PULL,
10, 6, DIO_MODE_AUDIOCLK);
//Configure Gains for DMIC0, DMIC1 and OD
AUDIO->DMIC0_GAIN = AUDIO_DMIC0_GAIN;
NVIC_EnableIRQ(DMIC_OUT_OD_IN_IRQn);
/* Indication - Initialization started. */
LED_On(LED_BLUE);
/* Initialize CS protocol, start Peripheral Server, Add Custom Service
* Profile to it.
*/
CS_Init();
/* Optionally set advertising interval to be 200 to 250 ms long. */
BDK_BLE_SetAdvertisementInterval(320, 400);
/* Optionally set custom device name.
* The name needs to contain one of 'IDK', 'BDK', 'RSL10' or 'BLE_Terminal'
* patterns to be recognized by RSL10 Sense & control mobile application.
* Default: 'HB_BLE_Terminal'
*/
BDK_BLE_SetLocalName("环境监测");
/* Also add battery service if its RTE component is enabled. */
#if defined (RTE_BDK_BLE_PERIPHERAL_SERVER_BASS)
BLE_BASS_Initialize(1000, 16);
BLE_BASS_SetVoltageRange(CALC_VBAT_MEASURED(2.4f), CALC_VBAT_MEASURED(3.0f));
// BLE_BASS_SetBattLevelInd(BattLevelChangeCallback);
#endif /* RTE_BDK_BLE_BASS_PRESENT */
// Change default bus speed to 400kHz
HAL_I2C_SetBusSpeed(HAL_I2C_BUS_SPEED_FAST);
/* Add all enabled Custom Service Nodes. */
// ASSERT_ALWAYS(CSN_ALS_CheckAvailability() == true);
// ASSERT_ALWAYS(CS_RegisterNode(CSN_ALS_Create()) == CS_OK);
ASSERT_ALWAYS(CSN_ENV_CheckAvailability() == true);
ASSERT_ALWAYS(CS_RegisterNode(CSN_ENV_Create()) == CS_OK);
ASSERT_ALWAYS(CSN_AO_CheckAvailability() == true);
ASSERT_ALWAYS(CS_RegisterNode(CSN_AO_Create()) == CS_OK);
ASSERT_ALWAYS(CS_RegisterNode(CSN_PB_Create()) == CS_OK);
/* Indication - Initialization complete. */
LED_Off(LED_BLUE);
LED_On(LED_GREEN);
HAL_Delay(250);
LED_Off(LED_GREEN);
CS_SYS_Info("Entering main loop.");
while (1)
{
/* Execute any events that have occurred and refresh Watchdog. */
BDK_Schedule();
/* Enter sleep mode until an interrupt occurs. */
SYS_WAIT_FOR_INTERRUPT;
}
return 0;
}
void DMIC_OUT_OD_IN_IRQHandler(void)
{
dmic_value = (int32_t)AUDIO->DMIC0_DATA;
if (dmic_max < dmic_value)
{
dmic_max = dmic_value;
}
else
{
if (dmic_min > dmic_value)
{
dmic_min = dmic_value;
}
}
}
</code></pre>
<p>在BSEC_ENV.c中BSEC_ENV_ReadData函数是一个定时调用的函数,在其中读取到数据后调用int CS_PlatformWrite(const char* tx_data, int tx_data_len)将数据以通知的方式发送出去。</p>
<p>这里的通信协议比较简单,将温度、湿度、光照、压力、空气质量、声音依次发送,先发高位字节,一共10个字节。</p>
<pre>
<code class="language-cpp">extern int32_t dmic_value;
extern int32_t dmic_max;
extern int32_t dmic_min;
extern lv0104cs_status;
extern struct BLE_ICS_Resources cs_res;
uint32_t lux = 0;
static void BSEC_ENV_ReadData(int64_t time_stamp_trigger, bsec_input_t *inputs,
uint8_t *num_bsec_inputs, int32_t bsec_process_data)
{
static struct bme680_field_data data;
int8_t bme680_status = BME680_OK;
int8_t buffer={0};
/* We only have to read data if the previous call the bsec_sensor_control() actually asked for it */
if (bsec_process_data)
{
bme680_status = bme680_get_sensor_data(&data, &bme680_g);
ASSERT_DEBUG(bme680_status == BME680_OK);
if (data.status & BME680_NEW_DATA_MSK)
{
/* Pressure to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_PRESSURE)
{
/* Place presssure sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[*num_bsec_inputs].signal = data.pressure;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
/* Temperature to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_TEMPERATURE)
{
/* Place temperature sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[*num_bsec_inputs].signal = data.temperature;
#else
inputs[*num_bsec_inputs].signal = data.temperature / 100.0f;
#endif
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
/* Also add optional heatsource input which will be subtracted from the temperature reading to
* compensate for device-specific self-heating (supported in BSEC IAQ solution)*/
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[*num_bsec_inputs].signal = bme680_temperature_offset_g;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
/* Humidity to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_HUMIDITY)
{
/* Place humidity sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[*num_bsec_inputs].signal = data.humidity;
#else
inputs[*num_bsec_inputs].signal = data.humidity / 1000.0f;
#endif
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
/* Gas to be processed by BSEC */
if (bsec_process_data & BSEC_PROCESS_GAS)
{
/* Check whether gas_valid flag is set */
if (data.status & BME680_GASM_VALID_MSK)
{
/* Place sample into input struct */
inputs[*num_bsec_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[*num_bsec_inputs].signal = data.gas_resistance;
inputs[*num_bsec_inputs].time_stamp = time_stamp_trigger;
(*num_bsec_inputs)++;
}
}
}
if (lv0104cs_status == 0)
{
LV0104CS_LUX_ReadLux(&lux);
printf("LV0104CS measured value:lux=%lu\n", lux);
}
CS_SYS_Info("INMP522 measured value: dmic_min=%ld dmic_value=%ld dmic_max=%ld\n", dmic_min, dmic_value,dmic_max);
CS_SYS_Info("BME680 measured value: temperature=%d humidity=%d pressure=%d gas_resistance=%d\n",data.temperature/100,data.humidity/1000,data.pressure,data.gas_resistance);
//通过通知发送给微信小程序
buffer=data.temperature/100;//温度
buffer=data.humidity/1000;//湿度
buffer=((data.pressure & 0xff00) >> 8 );//气压高8位
buffer=(data.pressure & 0x00ff);//气压低8位
buffer=((data.gas_resistance & 0xff00) >> 8 );//AQI高8位
buffer=(data.gas_resistance & 0x00ff);//AQI低8位
buffer=((lux & 0x0000ff00) >> 8 );//光照高8位
buffer=(lux & 0x000000ff);//光照低8位
buffer=((dmic_value & 0x0000ff00) >> 8 );//噪声高8位
buffer=(dmic_value & 0x000000ff);//噪声低8位
if(cs_res.tx_cccd_value==0x0001)
{
if(CS_PlatformWrite(buffer, sizeof(buffer))!=CS_OK)
{
CS_SYS_Info("=== CS_PlatformWrite Error ===");
}
}
}
}</code></pre>
<p>微信小程序发送给RSL10数据后会调用BLE_ICS.c中的BLE_ICS_GATTC_WriteReqInd回调,在这里可以得到通知是否开启/关闭</p>
<pre>
<code class="language-cpp">static int BLE_ICS_GATTC_WriteReqInd(ke_msg_id_t const msg_id,
struct gattc_write_req_ind const *param, ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
uint8_t status = GAP_ERR_NO_ERROR;
uint16_t att_num = 0;
int conidx = BDK_BLE_GetConIdx();
struct gattc_write_cfm *cfm;
/* Check if connection is valid. */
if (conidx == INVALID_DEV_IDX)
{
return KE_MSG_CONSUMED;
}
/* Check that offset is valid */
if (param->offset != 0)
{
status = ATT_ERR_INVALID_OFFSET;
}
/* Get index of characteristic which was requested. */
if (param->handle > cs_res.start_hdl)
{
att_num = param->handle - cs_res.start_hdl - 1;
}
else
{
status = ATT_ERR_INVALID_HANDLE;
}
printf("[%d %s] get data from phone,att_num=%d\n",__LINE__,__FUNCTION__,att_num);
if (status == GAP_ERR_NO_ERROR)
{
switch (att_num)
{
case ICS_IDX_TX_VALUE_CCC:
if (param->length == 2)
{
//手机APP端开启通知就是设置tx_cccd_value为0x0001关闭通知就是设置它的值为0x0000,
//这是原来的值
printf("[%d %s]old tx_cccd_value:0x%04x\n",__LINE__,__FUNCTION__,cs_res.tx_cccd_value);
//要写入的新值
printf("[%d %s]will write 0x",__LINE__,__FUNCTION__);
//大端模式传输
for(int i=param->length-1;i>=0;i--)
{
printf("%02x",param->value<i>);
}
printf(" to it\n");
memcpy(&cs_res.tx_cccd_value, param->value, 2);
}
else
{
status = ATT_ERR_INVALID_ATTRIBUTE_VAL_LEN;
}
break;
/* New command was written. */
case ICS_IDX_RX_VALUE_VAL:
if (param->length <= ICS_CHARACTERISTIC_VALUE_LENGTH)
{
printf("[%d %s] get data from phone,rx_value:",__LINE__,__FUNCTION__);
for(int i=0;i<param->length;i++)
{
printf("0x%02x ",cs_res.tx_cccd_value);
}
printf("\n");
memcpy(&cs_res.rx_value, param->value, param->length);
cs_res.rx_value_length = param->length;
}
else
{
status = ATT_ERR_INVALID_ATTRIBUTE_VAL_LEN;
}
break;
case ICS_IDX_RX_VALUE_CCC:
if (param->length == 2)
{
printf("[%d %s] get data from phone,rx_cccd_value=0x%04x\n",__LINE__,__FUNCTION__,cs_res.rx_cccd_value);
memcpy(&cs_res.rx_cccd_value, param->value, 2);
}
else
{
status = ATT_ERR_INVALID_ATTRIBUTE_VAL_LEN;
}
break;
default:
status = ATT_ERR_WRITE_NOT_PERMITTED;
break;
}
}
cfm = KE_MSG_ALLOC(GATTC_WRITE_CFM, KE_BUILD_ID(TASK_GATTC, conidx),
TASK_APP, gattc_write_cfm);
cfm->handle = param->handle;
cfm->status = status;
ke_msg_send(cfm);
/* Write indication handler */
if (att_num == ICS_IDX_RX_VALUE_VAL && cs_res.rx_write_handler != NULL
&& status == GAP_ERR_NO_ERROR)
{
struct BLE_ICS_RxIndData ind;
memcpy(ind.data, cs_res.rx_value, cs_res.rx_value_length);
ind.data_len = cs_res.rx_value_length;
cs_res.rx_write_handler(&ind);
}
return KE_MSG_CONSUMED;
}</i></code></pre>
<p><i>在CS.c中CS_Loop函数可取得接收到的数据</i></p>
<pre>
<i>
<code class="language-cpp">int CS_Loop(int timeout)
{
int errcode, bytes, i;
uint32_t timestamp;
struct CS_Request_Struct request;
// Wait for BLE data with 10 ms timeout.
errcode = CS_PlatformSleep(timeout);
if (errcode != CS_OK)
{
return errcode;
}
timestamp = CS_PlatformTime();
// Read available BLE packet.
memset(cs_rx_buffer, 0, 21);
//接收微信小程序发的数据
errcode = CS_PlatformRead(cs_rx_buffer, 21, &bytes);
if (errcode != CS_OK || bytes <= 0)
{
CS_SYS_Error("Platform read failed. (errcode=%d)", errcode);
return CS_ERROR;
}
#if CS_LOG_WITH_ANSI_COLORS != 0 && defined RTE_DEVICE_BDK_OUTPUT_REDIRECTION
CS_SYS_Info("Received request packet: '" COLORIZE("%s", CYAN, BOLD) "'",
cs_rx_buffer);
#else
CS_SYS_Info("Received request packet: '%s'", cs_rx_buffer);
#endif
// Parse header information
request.token = strtok(cs_rx_buffer, "/");
if (request.token == NULL)
{
CS_SYS_Error("Failed to parse request token.");
return CS_ERROR;
}
if (strlen(request.token) != 1)
{
CS_SYS_Error("Invalid request token length.");
return CS_ERROR;
}
if (request.token < '0' || request.token > '~')
{
CS_SYS_Error("Invalid request token value.");
return CS_ERROR;
}
request.node = strtok(NULL, "/");
if (request.node == NULL)
{
CS_SYS_Error("Failed to parse request node name.");
return CS_ERROR;
}
request.property = strtok(NULL, "/");
if (request.property == NULL)
{
CS_SYS_Error("Failed to parse request node property.");
return CS_ERROR;
}
// NULL for read requests
request.property_value = strtok(NULL, "/");
// Iterate all available nodes to find a match.
for (i = 0; i < cs.node_cnt; ++i)
{
if (strcmp(request.node, cs.node<i>->name) == 0)
{
// Matching node was found -> pass request
errcode = cs.node<i>->request_handler(&request, cs_node_response);
if (errcode == CS_OK &&
strlen(cs_node_response) <= 18) // 2b for token + response = 20b
{
// Compose response packet from token + node response
sprintf(cs_tx_buffer, "%s/%s", request.token, cs_node_response);
#if CS_LOG_WITH_ANSI_COLORS != 0 && defined RTE_DEVICE_BDK_OUTPUT_REDIRECTION
CS_SYS_Info(
"Composed response packet '" COLORIZE("%s", MAGENTA, BOLD) "'",
cs_tx_buffer);
#else
CS_SYS_Info("Composed response packet '%s'", cs_tx_buffer);
#endif
// Send response to platform
int res_len = strlen(cs_tx_buffer);
if (CS_PlatformWrite(cs_tx_buffer, res_len) == CS_OK)
{
timestamp = CS_PlatformTime() - timestamp;
CS_SYS_Verbose("Request completed in %lu ms.", timestamp);
return CS_OK;
}
else
{
CS_SYS_Error("Platform send failed. (errcode=%d)", errcode);
return CS_ERROR;
}
}
else
{
CS_SYS_Error("Node request processing error. (errcode=%d)", errcode);
sprintf(cs_tx_buffer, "%s/e/UNK_ERROR", request.token);
errcode = CS_PlatformWrite(cs_tx_buffer, strlen(cs_tx_buffer));
if (errcode != CS_OK)
{
CS_SYS_Error("Platform send failed. (errcode=%d)", errcode);
return CS_ERROR;
}
// Node failed to process request
return CS_ERROR;
}
}
}
CS_SYS_Error("No matching node found for '%s'", request.node);
sprintf(cs_tx_buffer, "%s/e/UNK_NODE", request.token);
errcode = CS_PlatformWrite(cs_tx_buffer, strlen(cs_tx_buffer));
if (errcode != CS_OK)
{
CS_SYS_Error("Platform send failed. (errcode=%d)", errcode);
return CS_ERROR;
}
return CS_ERROR;
}</i></i></code></i></pre>
<p> </p>
<p><font face="monospace">下面来看看数据交互过程吧!</font></p>
<p><i><i><i></i></i></i></p>
<p><i><i><i> </i></i></i></p>
<p><i><i><i></i></i></i></p>
<p><i><i><i> </i></i></i></p>
<p><i><i><i></i></i></i></p>
<p><i><i><i> </i></i></i></p>
<p><i><i><i>微信小程序端接收到数据:</i></i></i></p>
<p><iframe allowfullscreen="true" frameborder="0" height="450" src="//player.bilibili.com/player.html?bvid=1to4y1C77d&page=1" style="background:#eee;margin-bottom:10px;" width="750"></iframe><br />
</p>
<p><i><i><i>至此 RSL10-SENSE-DB-GEVK板卡的功能就基于sense_ics_firmware例程实现,RSL10-002GEVB板卡的功能就基于peripheral _server例程实现,后续需要实现的节点功能就变的简单了。</i></i></i></p>
<p>不错不错,用微信小程序方便多了,都不用下载APP了!</p>
w494143467 发表于 2021-7-6 10:19
不错不错,用微信小程序方便多了,都不用下载APP了!
<p>跨平台方便</p>
<p>小程序确实挺方便的。</p>
okhxyyo 发表于 2021-7-6 15:28
小程序确实挺方便的。
<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/smile.gif" width="48" /></p>
<p>小程序都做好了,从单片机到后台,厉害了。</p>
freebsder 发表于 2021-7-6 22:17
小程序都做好了,从单片机到后台,厉害了。
<p>正在研究数据库,小程序需要用微信云数据库,数据量大点要money<img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/wanwan51.gif" width="48" /></p>
dql2016 发表于 2021-7-7 11:01
正在研究数据库,小程序需要用微信云数据库,数据量大点要money
<p>买个基本的云服务器呗,可玩性还比较大。</p>
页:
[1]