【Ai-WB2高级篇】BLE蓝牙无线通讯浅析

[复制链接]
查看840 | 回复4 | 2024-9-15 22:01:34 | 显示全部楼层 |阅读模式
蓝牙BLE简介:
  BLE蓝牙(Bluetooth Low Energy)是一种低功耗蓝牙技术,旨在为物联网设备和传感器提供无线通信。普通蓝牙(Classic Bluetooth)是一种传统的蓝牙技术,主要用于音频传输和数据通信,其广泛应用于各种各类智能设备,主要应用包括:定位标签,资产跟踪,运动及健身传感器,医疗传感器,智能手表,遥控器,玩具等。两者在通信距离、传输速率、功耗和连接稳定性等方面存在差异。

低功耗蓝牙与传统蓝牙的区别:

  低功耗蓝牙和传统蓝牙相比,只有在配对绑定上相同,其他不同之处分别为:

  1、 低功耗蓝牙节能,接收成功后会自动断开,下一次连接的时候再激活就可以了;但是传统蓝牙一旦激活就会始终保持连接,比较耗能;

  2、低功耗蓝牙的广播信道仅有3个;传统蓝牙则是32个;

  3、低功耗蓝牙的速度快,几毫秒就能实现一次“完成”;但是传统蓝牙在相同的连接周期下却可能需要耗费的时间会是低耗蓝牙的好几十倍;

  4、传统蓝牙使用因为其数据包常所以比较常用与数据量比较大的传输,但是低耗能蓝牙数据包非常短,所以多用于小应用中;

  5、低功耗蓝牙无功率级别,一般发送功率在+4dBm,在空旷距离可达到70m的传输距离;传统蓝牙有3个功率级别,Class1,Class2,Class3,分别支持100m,10m,1m的传输距离。


蓝牙的工作模式:
  1. 主机模式/从机模式
    主机模式下,蓝牙模块具备扫描从机广播以及主动建立连接的能力,能够与一个或者多个从设备实现连接通信。
  从机模式则是蓝牙模块先进入广播状态,等待被主机扫描。一旦主机扫描到从设备并建立连接之后,便能与主机设备进行数据的收发。在此模式下,从机无法主动建立连接,只能被动等待主机扫描并建立连接。
  2. 主从一体工作模式
    主从一体模式是指蓝牙模块既可以作为主设备,也可以作为从设备。这种模式允许蓝牙模块在两种角色之间自由切换。在从模式下,蓝牙模块会等待其他主设备前来连接,必要时再转换为主模式,向其他设备发起连接请求。
  3. 广播/观察模式
    广播模式:蓝牙模块会定期且持续地向周围发送一定长度的广播数据包,这些数据包可以被扫描到。在低功耗模式下,蓝牙模块可以持续进行广播操作,适用于极低功耗、小数据量以及单向传输的应用场景。
    观察模式:该模式下,蓝牙模块是非连接状态的。与广播模式的一对多发送广播相比,观察者可以一对多地接收数据。在该模式下,设备只能够监听和读取空中的广播数据,却无法发起连接,只能持续扫描从机。
  4. iBeacon模式
    苹果公司推出的一款基于低功耗蓝牙技术的新型通信协议,称之为iBeacon,实现持续不断地广播蓝牙设备的MAC地址、UUID等固定字节的字符串信息。是近些年来开始流行起来的蓝牙通讯技术应用,在精确营销方面有着广泛的应用,例如博物馆、展览馆等场所提供信息推送服务,或是在购物中心为商家提供向消费者发放优惠券和积分的功能。在室内高精度定位方面的应用也越来越多。

GATT简单介绍:
  1.什么是GATT?
   GATT(Generic Attribute Profile)是BLE中用来定义通信数据结构的协议。GATT定义了如何在BLE设备之间传输数据,并规定了服务(Services)、特征(Characteristics)和描述符(Descriptors)的使用方式。通过这些概念,GATT实现了设备间的标准化通信。

  2. GATT服务
   GATT服务是用于组织特征的集合。每个服务通常代表一个设备或应用的特定功能模块。。
  3. GATT特征

   GATT特征是包含实际数据的基本单元。每个 GATT 服务都有一个唯一的 UUID(Universally Unique Identifier),用于唯一标识该服务。UUID 可以是 16 位、32 位或 128 位的标识符,其中 16 位和 32 位 UUID 通常是由蓝牙 SIG 定义的标准服务,128 位 UUID 通常用于自定义服务。特征可以用于读取、写入和订阅通知。标准的GATT特征值,可以从国际蓝牙联盟(BT-SIG)官方渠道了解:https://www.bluetooth.com/

     当一个BLE设备的特征接收GATT通知时,它意味着它订阅了一个特征的通知,并且当该特征的值发生变化时,它会接收到通知。这种通知机制可以用于实时监测特征值的变化,例如温度传感器的实时温度数据。

     GATT服务、特征、属性的关系大致如下:

    74c5ad2ed893cdd9350fd730ade743d6.png


AI-WB2-32S BLE从机模式GATT实现浅析:
  1. #include "ble_interface.h"

  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <FreeRTOS.h>
  5. #include <semphr.h>

  6. #include "bluetooth.h"
  7. #include "hci_driver.h"
  8. #include "hci_core.h"
  9. #include "ble_lib_api.h"
  10. #include "conn.h"
  11. #include "conn_internal.h"
  12. #include "gatt.h"

  13. /*自定义UUID*/
  14. #define UUID1_USER_SER BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x55535343, 0xfe7d, 0x4ae5, 0x8fa9, 0x9fafd205e455))
  15. #define UUID1_USER_TXD BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x49535343, 0x8841, 0x43f4, 0xa8d4, 0xecbe34729bb3))
  16. #define UUID1_USER_RXD BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x49535343, 0x1e4d, 0x4bd9, 0xba61, 0x23c647249616))

  17. #define UUID2_USER_SER BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x10190d0c, 0x0b0a, 0x0908, 0x0706, 0x050403020100))
  18. #define UUID2_USER_TXD BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x102B0d0c, 0x0b0a, 0x0908, 0x0706, 0x050403020100))
  19. #define UUID2_USER_RXD BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x102B0d0d, 0x0b0a, 0x0908, 0x0706, 0x050403020100))

  20. #define SALVE_CMD_SERVER_TX_INDEX 2

  21. static struct bt_conn *conn_cur;
  22. ble_gatt_conn_cb_t conn_cb;
  23. ble_gatt_conn_cb_t disconn_cb;

  24. #define ble_slave_name "Ai-thinker"

  25. static const struct bt_data salve_adv[] = {
  26.     BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
  27.     BT_DATA(BT_DATA_NAME_COMPLETE, ble_slave_name, sizeof(ble_slave_name) - 1),

  28. };

  29. static ssize_t ble_uuid1_write_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  30.                                    const void *buf, u16_t len, u16_t offset, u8_t flags);
  31. static ssize_t ble_uuid2_write_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  32.                                    const void *buf, u16_t len, u16_t offset, u8_t flags);
  33. static void ble_ccc_cfg_changed(const struct bt_gatt_attr *attr,
  34.                                 u16_t value);

  35. static struct bt_gatt_attr salve_uuid1_server[] = {
  36.     /* Primary Service */
  37.     BT_GATT_PRIMARY_SERVICE(UUID1_USER_SER),

  38.     /* Characteristic && Characteristic User Declaration */
  39.     BT_GATT_CHARACTERISTIC(UUID1_USER_TXD,
  40.                            BT_GATT_CHRC_NOTIFY,
  41.                            BT_GATT_PERM_READ, NULL, NULL,
  42.                            NULL),
  43.     BT_GATT_CCC(ble_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),

  44.     /* Characteristic && Characteristic User Declaration */
  45.     BT_GATT_CHARACTERISTIC(UUID1_USER_RXD,
  46.                            BT_GATT_CHRC_WRITE_WITHOUT_RESP,
  47.                            BT_GATT_PERM_WRITE, NULL, ble_uuid1_write_val,
  48.                            NULL),
  49. };

  50. static struct bt_gatt_attr salve_uuid2_server[] = {
  51.     /* Primary Service */
  52.     BT_GATT_PRIMARY_SERVICE(UUID2_USER_SER),

  53.     /* Characteristic && Characteristic User Declaration */
  54.     BT_GATT_CHARACTERISTIC(UUID2_USER_TXD,
  55.                            BT_GATT_CHRC_NOTIFY,
  56.                            BT_GATT_PERM_READ, NULL, NULL,
  57.                            NULL),
  58.     BT_GATT_CCC(ble_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),

  59.     /* Characteristic && Characteristic User Declaration */
  60.     BT_GATT_CHARACTERISTIC(UUID2_USER_RXD,
  61.                            BT_GATT_CHRC_WRITE_WITHOUT_RESP,
  62.                            BT_GATT_PERM_WRITE, NULL, ble_uuid2_write_val,
  63.                            NULL),
  64. };

  65. static struct bt_gatt_service ble_uuid1_server = BT_GATT_SERVICE(salve_uuid1_server);
  66. static struct bt_gatt_service ble_uuid2_server = BT_GATT_SERVICE(salve_uuid2_server);

  67. static ssize_t ble_uuid1_write_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  68.                                    const void *buf, u16_t len, u16_t offset,
  69.                                    u8_t flags)
  70. {
  71.     uint8_t *recv_buffer;
  72.     recv_buffer = pvPortMalloc(sizeof(uint8_t) * len);
  73.     memcpy(recv_buffer, buf, len);
  74.     printf("recv ble data len: %d\r\n", len);
  75.     for (size_t i = 0; i < len; i++)
  76.     {
  77.         printf("0x%x ", recv_buffer[i]);
  78.     }
  79.     printf("\r\n");
  80.     vPortFree(recv_buffer);

  81.     return len;
  82. }

  83. static ssize_t ble_uuid2_write_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
  84.                                    const void *buf, u16_t len, u16_t offset,
  85.                                    u8_t flags)
  86. {
  87.     uint8_t *recv_buffer;
  88.     recv_buffer = pvPortMalloc(sizeof(uint8_t) * len);
  89.     memcpy(recv_buffer, buf, len);
  90.     printf("recv ble data len: %d\r\n", len);
  91.     for (size_t i = 0; i < len; i++)
  92.     {
  93.         printf("0x%x ", recv_buffer[i]);
  94.     }
  95.     printf("\r\n");
  96.     vPortFree(recv_buffer);
  97.     return len;
  98. }

  99. static void ble_ccc_cfg_changed(const struct bt_gatt_attr *attr,
  100.                                 u16_t value)
  101. {
  102.     char *str = "disabled";

  103.     if (value == BT_GATT_CCC_NOTIFY)
  104.     {
  105.         str = "notify";
  106.     }
  107.     else if (value == BT_GATT_CCC_INDICATE)
  108.     {
  109.         str = "indicate";
  110.     }

  111.     printf("[BLE] ccc change %s", str);
  112. }

  113. static void _connected(struct bt_conn *conn, u8_t err)
  114. {
  115.     if (conn_cb)
  116.     {
  117.         if (conn_cb(conn, err) != 0)
  118.         {
  119.             return;
  120.         }
  121.     }

  122.     if (conn->type != BT_CONN_TYPE_LE)
  123.     {
  124.         return;
  125.     }

  126.     conn_cur = conn;

  127.     printf("[BLE] connected \r\n");
  128.     BleSetMtu();
  129.     return;
  130. }

  131. static void _disconnected(struct bt_conn *conn, u8_t reason)
  132. {
  133.     if (disconn_cb)
  134.     {
  135.         if (disconn_cb(conn, reason) != 0)
  136.         {
  137.             return;
  138.         }
  139.     }

  140.     if (conn->type != BT_CONN_TYPE_LE)
  141.     {
  142.         return;
  143.     }

  144.     conn_cur = NULL;

  145.     printf("[BLE] disconnected, reason:%d \r\n", reason);
  146. }

  147. static bool _le_param_req(struct bt_conn *conn,
  148.                           struct bt_le_conn_param *param)
  149. {
  150.     printf("[BLE] conn param request: int 0x%04x-0x%04x lat %d to %d \r\n",
  151.            param->interval_min,
  152.            param->interval_max,
  153.            param->latency,
  154.            param->timeout);

  155.     return true;
  156. }

  157. static void _le_param_updated(struct bt_conn *conn, u16_t interval,
  158.                               u16_t latency, u16_t timeout)
  159. {
  160.     printf("[BLE] conn param updated: int 0x%04x lat %d to %d \r\n", interval, latency, timeout);
  161. }

  162. static void _le_phy_updated(struct bt_conn *conn, u8_t tx_phy, u8_t rx_phy)
  163. {
  164.     printf("[BLE] phy updated: rx_phy %d, rx_phy %d \r\n", tx_phy, rx_phy);
  165. }

  166. static struct bt_conn_cb conn_callbacks = {
  167.     .connected = _connected,
  168.     .disconnected = _disconnected,
  169.     .le_param_req = _le_param_req,
  170.     .le_param_updated = _le_param_updated,
  171.     .le_phy_updated = _le_phy_updated,
  172. };

  173. static void ble_disconnect_all(struct bt_conn *conn, void *data)
  174. {
  175.     if (conn->state == BT_CONN_CONNECTED)
  176.     {
  177.         printf("[BLE] disconn id:%d \r\n", conn->id);
  178.         bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
  179.     }
  180. }

  181. static void _ble_mtu_changed_cb(struct bt_conn *conn, int mtu)
  182. {
  183.     printf("[BLE] mtu updated:%d \r\n", mtu);
  184. }

  185. struct bt_conn *ble_get_conn_cur(void)
  186. {
  187.     return conn_cur;
  188. }

  189. int ble_regist_conn(ble_gatt_conn_cb_t cb)
  190. {
  191.     conn_cb = cb;

  192.     return 0;
  193. }

  194. int ble_regist_disconn(ble_gatt_conn_cb_t cb)
  195. {
  196.     disconn_cb = cb;

  197.     return 0;
  198. }

  199. static int ble_salve_conn_cb(struct bt_conn *conn, uint8_t code)
  200. {
  201.     int err;

  202.     struct bt_le_conn_param param;
  203.     param.interval_max = 24;
  204.     param.interval_min = 24;
  205.     param.latency = 0;
  206.     param.timeout = 600;
  207.     err = bt_conn_le_param_update(conn, &param);

  208.     return 0;
  209. }

  210. static int ble_salve_disconn_cb(struct bt_conn *conn, uint8_t code)
  211. {
  212.     if (set_adv_enable(true))
  213.     {
  214.         printf("[BLE] Restart adv fail. \r\n");
  215.     }
  216.     else
  217.     {
  218.         printf("[BLE] Restart adv success. \r\n");
  219.     }

  220.     return 0;
  221. }

  222. int ble_salve_adv()
  223. {
  224.     int err = -1;
  225.     err = bt_le_adv_start(BT_LE_ADV_CONN, salve_adv, ARRAY_SIZE(salve_adv), NULL, 0);
  226.     if (err)
  227.     {
  228.         printf("[BLE] adv fail(err %d) \r\n", err);
  229.         return -1;
  230.     }

  231.     return 0;
  232. }

  233. static void bt_enable_cb(int err)
  234. {
  235.     if (!err)
  236.     {
  237.         bt_addr_le_t bt_addr;
  238.         bt_get_local_public_address(&bt_addr);
  239.         bt_addr.a.val[5] = 0x88;
  240.         bt_addr.a.val[4] = 0x88;
  241.         bt_addr.a.val[3] = 0x88;
  242.         bt_addr.a.val[2] = 0x88;
  243.         bt_addr.a.val[1] = 0x88;
  244.         bt_addr.a.val[0] = 0x88;
  245.         printf("BD_ADDR:(MSB)%02x:%02x:%02x:%02x:%02x:%02x(LSB) \r\n",
  246.                bt_addr.a.val[5], bt_addr.a.val[4], bt_addr.a.val[3], bt_addr.a.val[2], bt_addr.a.val[1], bt_addr.a.val[0]);
  247.     }
  248. }

  249. void ble_reverse_byte(uint8_t *arr, uint32_t size)
  250. {
  251.     uint8_t i, tmp;

  252.     for (i = 0; i < size / 2; i++)
  253.     {
  254.         tmp = arr[i];
  255.         arr[i] = arr[size - 1 - i];
  256.         arr[size - 1 - i] = tmp;
  257.     }
  258. }

  259. int ble_uuid1_notify_data(void *handle, void *data, uint16_t length)
  260. {
  261.     int ret;
  262.     uint16_t mtu;
  263.     uint16_t offset;
  264.     uint16_t send_len;

  265.     offset = 0;
  266.     mtu = bt_gatt_get_mtu(handle) - 3;
  267.     while (length > 0)
  268.     {
  269.         /* calculate send_len */
  270.         send_len = length > mtu ? mtu : length;
  271.         /* send data */
  272.         ret = bt_gatt_notify(handle, &salve_uuid1_server[SALVE_CMD_SERVER_TX_INDEX], data + offset, send_len);
  273.         /* set offset */
  274.         offset += send_len;
  275.         length -= send_len;

  276.         printf("[BLE] notify len:%d \r\n", send_len);

  277.         if (ret != 0)
  278.         {
  279.             break;
  280.         }
  281.     }

  282.     return ret;
  283. }

  284. int ble_uuid2_notify_data(void *handle, void *data, uint16_t length)
  285. {
  286.     int ret;
  287.     uint16_t mtu;
  288.     uint16_t offset;
  289.     uint16_t send_len;

  290.     offset = 0;
  291.     mtu = bt_gatt_get_mtu(handle) - 3;
  292.     while (length > 0)
  293.     {
  294.         /* calculate send_len */
  295.         send_len = length > mtu ? mtu : length;
  296.         /* send data */
  297.         ret = bt_gatt_notify(handle, &salve_uuid2_server[SALVE_CMD_SERVER_TX_INDEX], data + offset, send_len);
  298.         /* set offset */
  299.         offset += send_len;
  300.         length -= send_len;

  301.         printf("[BLE] notify len:%d \r\n", send_len);

  302.         if (ret != 0)
  303.         {
  304.             break;
  305.         }
  306.     }

  307.     return ret;
  308. }

  309. // 从机模式向蓝牙UUID1服务发送数据
  310. // 参数
  311. //     len:需要发送的数据长度
  312. //     data:要发送的数据
  313. // 返回值
  314. //     >=0:成功发送的数据长度
  315. //     -1:蓝牙状态错误
  316. //     -2:数据长度错误
  317. //     -3:data为NULL
  318. //     -4:发送失败
  319. int UUID1_SendNotify(uint16_t len, uint8_t *data)
  320. {
  321.     int ret;
  322.     struct bt_conn *conn;

  323.     conn = ble_get_conn_cur();
  324.     if (conn == NULL)
  325.     {
  326.         return -1;
  327.     }

  328.     ret = ble_uuid1_notify_data(conn, (void *)data, len);
  329.     if (ret != 0)
  330.     {
  331.         return -4;
  332.     }

  333.     return len;
  334. }

  335. // 从机模式向UUID2服务发送数据
  336. // 参数
  337. //     len:需要发送的数据长度
  338. //     data:要发送的数据
  339. // 返回值
  340. //     >=0:成功发送的数据长度
  341. //     -1:蓝牙状态错误
  342. //     -2:数据长度错误
  343. //     -3:data为NULL
  344. //     -4:发送失败
  345. int UUID2_SendNotify(uint16_t len, uint8_t *data)
  346. {
  347.     int ret;
  348.     struct bt_conn *conn;

  349.     conn = ble_get_conn_cur();
  350.     if (conn == NULL)
  351.     {
  352.         return -1;
  353.     }

  354.     ret = ble_uuid2_notify_data(conn, (void *)data, len);
  355.     if (ret != 0)
  356.     {
  357.         return -4;
  358.     }

  359.     return len;
  360. }

  361. static void exchange_func(struct bt_conn *conn, u8_t err,
  362.                           struct bt_gatt_exchange_params *params)
  363. {
  364.     if (conn)
  365.     {
  366.         printf("[BLE] Exchange %s MTU Size =%d \r\n", err == 0U ? "successful" : "failed", bt_gatt_get_mtu(conn));
  367.     }
  368. }

  369. static struct bt_gatt_exchange_params exchange_params;

  370. uint8_t BleSetMtu()
  371. {
  372.     int ret = -1;
  373.     if (conn_cur == NULL)
  374.     {
  375.         return 1;
  376.     }

  377.     exchange_params.func = exchange_func;
  378.     ret = bt_gatt_exchange_mtu(conn_cur, &exchange_params);
  379.     if (ret != 0)
  380.     {
  381.         return 1;
  382.     }

  383.     return 0;
  384. }

  385. int ble_slave_init()
  386. {

  387.     ble_regist_conn(ble_salve_conn_cb);
  388.     ble_regist_disconn(ble_salve_disconn_cb);

  389.     ble_server_init();
  390.     ble_salve_adv();

  391.     return 0;
  392. }

  393. int ble_slave_deinit(void)
  394. {
  395.     /* comment out gatt server deinit as it may cause crash */
  396.     bt_le_adv_stop();
  397.     ble_regist_conn(NULL);
  398.     ble_regist_disconn(NULL);

  399.     return 0;
  400. }

  401. int ble_server_init()
  402. {
  403.     int ret = 0;

  404.     ret = bt_gatt_service_register(&ble_uuid1_server);
  405.     ret |= bt_gatt_service_register(&ble_uuid2_server);

  406.     return ret;
  407. }

  408. int ble_server_deinit(void)
  409. {
  410.     int ret = 0;

  411.     ret = bt_gatt_service_unregister(&ble_uuid1_server);
  412.     ret |= bt_gatt_service_unregister(&ble_uuid2_server);

  413.     return ret;
  414. }

  415. void ble_stack_start(void)
  416. {
  417.     // Initialize BLE controller
  418.     ble_controller_init(configMAX_PRIORITIES - 1);
  419.     // Initialize BLE Host stack
  420.     hci_driver_init();
  421.     bt_enable(bt_enable_cb);
  422. }

  423. // 开启蓝牙
  424. // start ble
  425. void apps_ble_start()
  426. {
  427.     ble_stack_start();
  428.     ble_slave_init();
  429.     bt_gatt_register_mtu_callback(_ble_mtu_changed_cb);
  430.     bt_conn_cb_register(&conn_callbacks);
  431.     /* avoid callback infinite loop */
  432.     conn_callbacks._next = NULL;
  433. }

  434. // 关闭蓝牙
  435. // stop ble
  436. void apps_ble_stop()
  437. {
  438.     ble_slave_deinit();

  439.     bt_conn_foreach(BT_CONN_TYPE_ALL, ble_disconnect_all, NULL);

  440.     int disconn_cnt = 0;
  441.     while (le_check_valid_conn() && disconn_cnt++ < 10)
  442.     {
  443.         printf("[BLE] wait for ble_disconnect_all\r\n");
  444.         vTaskDelay(pdMS_TO_TICKS(500));
  445.     }
  446.     bt_disable();
  447. }
复制代码
以上代码定义了一个名为Ai-thinker的从机BLE GATT服务,烧录后,在手机上使用BLE助手可以与本设备通讯,其中,关键部分在于:
  apps_ble_start:入口函数,开启BLE使能
  ble_slave_init:开启从机初始化
  ble_server_init:注册gatt服务
  ble_uuid1_notify_data:处理通知数据
  ble_uuid1_write_val:读取经GATT发送过来的数据
  BT_GATT_PRIMARY_SERVICE:定义GATT服务UUID
  BT_GATT_CHARACTERISTIC:定义收发的特征值
  BT_GATT_CCC:定义配置改变时的监听

本帖被以下淘专辑推荐:

回复

使用道具 举报

iiv | 2024-9-16 13:12:19 | 显示全部楼层
佬,秀
回复

使用道具 举报

djy876 | 2024-9-16 20:12:01 | 显示全部楼层
佬,秀
回复

使用道具 举报

bzhou830 | 2024-9-17 17:07:25 | 显示全部楼层
佬,秀
选择去发光,而不是被照亮
回复

使用道具 举报

lovzx | 3 天前 | 显示全部楼层
学习
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则