ESP32蓝牙的Gatt Client的例子演练
翻译内容仅供参考,原文链接:
https://github.com/espressif/esp-idf/blob/dd8db6621/examples/bluetooth/bluedroid/ble/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "nvs.h"
#include "nvs_flash.h"
#include "controller.h"#include "bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gattc_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"
void app_main()
{// Initialize NVS.esp_err_t ret = nvs_flash_init();if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();}ESP_ERROR_CHECK( ret );esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();ret = esp_bt_controller_init(&bt_cfg);if (ret) {ESP_LOGE(GATTC_TAG, "%s initialize controller failed, error code = %x\n", __func__, ret);return;}ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);if (ret) {ESP_LOGE(GATTC_TAG, "%s enable controller failed, error code = %x\n", __func__, ret);return;}ret = esp_bluedroid_init();if (ret) {ESP_LOGE(GATTC_TAG, "%s init bluetooth failed, error code = %x\n", __func__, ret);return;}ret = esp_bluedroid_enable();if (ret) {ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed, error code = %x\n", __func__, ret);return;}//register the callback function to the gap moduleret = esp_ble_gap_register_callback(esp_gap_cb);if (ret){ESP_LOGE(GATTC_TAG, "%s gap register failed, error code = %x\n", __func__, ret);return;}//register the callback function to the gattc moduleret = esp_ble_gattc_register_callback(esp_gattc_cb);if(ret){ESP_LOGE(GATTC_TAG, "%s gattc register failed, error code = %x\n", __func__, ret);return;}ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID);if (ret){ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x\n", __func__, ret);}esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);if (local_mtu_ret){ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret);}}
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
ret = esp_bluedroid_init();
ret = esp_bluedroid_enable();
//register the callback function to the gap moduleret = esp_ble_gap_register_callback(esp_gap_cb);//register the callback function to the gattc moduleret = esp_ble_gattc_register_callback(esp_gattc_cb);ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID);esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);if (local_mtu_ret){ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret);}
esp_ble_gap_register_callback();
esp_ble_gattc_register_callback();
struct gattc_profile_inst {esp_gattc_cb_t gattc_cb;uint16_t gattc_if;uint16_t app_id;uint16_t conn_id;uint16_t service_start_handle;uint16_t service_end_handle;uint16_t char_handle;esp_bd_addr_t remote_bda;
};
#define PROFILE_NUM 1
#define PROFILE_A_APP_ID 0
/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {[PROFILE_A_APP_ID] = {.gattc_cb = gattc_profile_event_handler,.gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */},
};
static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d", event, gattc_if);/* If event is register event, store the gattc_if for each profile */if (event == ESP_GATTC_REG_EVT) {if (param->reg.status == ESP_GATT_OK) {gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;} else {ESP_LOGI(GATTC_TAG, "reg app failed, app_id %04x, status %d",param->reg.app_id,param->reg.status);return;}}
…
…
/* If the gattc_if equal to profile A, call profile A cb handler,* so here call each profile's callback */do {int idx;for (idx = 0; idx < PROFILE_NUM; idx++) {if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */gattc_if == gl_profile_tab[idx].gattc_if) {if (gl_profile_tab[idx].gattc_cb) {gl_profile_tab[idx].gattc_cb(event, gattc_if, param);}}}} while (0);
}
/// Ble scan parameters
typedef struct {esp_ble_scan_type_t scan_type; /*!< Scan type */esp_ble_addr_type_t own_addr_type; /*!< Owner address type */esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan.*/ //Range: 0x0004 to 0x4000 //Default: 0x0010 (10 ms)//Time = N * 0.625 msec//Time Range: 2.5 msec to 10.24 secondsuint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval*///Range: 0x0004 to 0x4000 //Default: 0x0010 (10 ms)//Time = N * 0.625 msec//Time Range: 2.5 msec to 10240 msec
} esp_ble_scan_params_t;
static esp_ble_scan_params_t ble_scan_params = {.scan_type = BLE_SCAN_TYPE_ACTIVE,.own_addr_type = BLE_ADDR_TYPE_PUBLIC,.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,.scan_interval = 0x50,.scan_window = 0x30
};
case ESP_GATTC_REG_EVT:ESP_LOGI(GATTC_TAG, "REG_EVT");esp_err_t scan_ret = esp_ble_gap_set_scan_params(&ble_scan_params);if (scan_ret){ESP_LOGE(GATTC_TAG, "set scan params error, error code = %x", scan_ret);}break;
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {//the unit of the duration is seconduint32_t duration = 30;esp_ble_gap_start_scanning(duration);break;}
/*** @brief ESP_GAP_BLE_SCAN_RESULT_EVT*/struct ble_scan_result_evt_param {esp_gap_search_evt_t search_evt; /*!< Search event type */esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */esp_bt_dev_type_t dev_type; /*!< Device type */esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */int rssi; /*!< Searched device's RSSI */uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */int flag; /*!< Advertising data flag bit */int num_resps; /*!< Scan result number */uint8_t adv_data_len; /*!< Adv data length */uint8_t scan_rsp_len; /*!< Scan response length */} scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */
/// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT
typedef enum {ESP_GAP_SEARCH_INQ_RES_EVT = 0, /*!< Inquiry result for a peer device. */ESP_GAP_SEARCH_INQ_CMPL_EVT = 1, /*!< Inquiry complete. */ESP_GAP_SEARCH_DISC_RES_EVT = 2, /*!< Discovery result for a peer device. */ESP_GAP_SEARCH_DISC_BLE_RES_EVT = 3, /*!< Discovery result for BLE GATT based service on a peer device. */ESP_GAP_SEARCH_DISC_CMPL_EVT = 4, /*!< Discovery complete. */ESP_GAP_SEARCH_DI_DISC_CMPL_EVT = 5, /*!< Discovery complete. */ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT = 6, /*!< Search cancelled */
} esp_gap_search_evt_t;
case ESP_GAP_BLE_SCAN_RESULT_EVT: {esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;switch (scan_result->scan_rst.search_evt) {case ESP_GAP_SEARCH_INQ_RES_EVT:esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len);esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);ESP_LOGI(GATTC_TAG, "\n");if (adv_name != NULL) {if (strlen(remote_device_name) == adv_name_len && strncmp((char *)adv_name, remote_device_name, adv_name_len) == 0) {ESP_LOGI(GATTC_TAG, "searched device %s\n", remote_device_name);if (connect == false) {connect = true;ESP_LOGI(GATTC_TAG, "connect to the remote device.");esp_ble_gap_stop_scanning();esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, scan_result->scan_rst.ble_addr_type, true);}}}break;
case ESP_GAP_SEARCH_INQ_RES_EVT:esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len);
esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
case ESP_GATTC_CONNECT_EVT://p_data->connect.status always be ESP_GATT_OKESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d, status %d", conn_id, gattc_if, p_data->connect.status);conn_id = p_data->connect.conn_id;gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id;memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));ESP_LOGI(GATTC_TAG, "REMOTE BDA:");esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, sizeof(esp_bd_addr_t));esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, conn_id);if (mtu_ret){ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);}break;
conn_id = p_data->connect.conn_id;
gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id;
memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, sizeof(esp_bd_addr_t));
esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, conn_id);
if (mtu_ret){ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
}
break;
case ESP_GATTC_OPEN_EVT:if (param->open.status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "open failed, status %d", p_data->open.status);break;}
ESP_LOGI(GATTC_TAG, "open success");
case ESP_GATTC_CFG_MTU_EVT:if (param->cfg_mtu.status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG,"config mtu failed, error status = %x", param->cfg_mtu.status);}ESP_LOGI(GATTC_TAG, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id);
…
static esp_bt_uuid_t remote_filter_service_uuid = {.len = ESP_UUID_LEN_16,.uuid = {.uuid16 = REMOTE_SERVICE_UUID,},
};
#define REMOTE_SERVICE_UUID 0x00FF
esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid);break;
case ESP_GATTC_SEARCH_RES_EVT: {esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id;conn_id = p_data->search_res.conn_id;if (srvc_id->id.uuid.len == ESP_UUID_LEN_16 && srvc_id->id.uuid.uuid.uuid16 ==
REMOTE_SERVICE_UUID) {get_server = true;gl_profile_tab[PROFILE_A_APP_ID].service_start_handle = p_data->search_res.start_handle;gl_profile_tab[PROFILE_A_APP_ID].service_end_handle = p_data->search_res.end_handle;ESP_LOGI(GATTC_TAG, "UUID16: %x", srvc_id->id.uuid.uuid.uuid16);}break;
#define REMOTE_NOTIFY_CHAR_UUID 0xFF01
/*** @brief Gatt id, include uuid and instance id*/
typedef struct {esp_bt_uuid_t uuid; /*!< UUID */uint8_t inst_id; /*!< Instance id */
} __attribute__((packed)) esp_gatt_id_t;
static esp_gatt_srvc_id_t remote_service_id = {.id = {.uuid = {.len = ESP_UUID_LEN_16,.uuid = {.uuid16 = REMOTE_SERVICE_UUID,},},.inst_id = 0,},.is_primary = true,
};
case ESP_GATTC_SEARCH_CMPL_EVT:if (p_data->search_cmpl.status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);break;}conn_id = p_data->search_cmpl.conn_id;if (get_server){uint16_t count = 0;esp_gatt_status_t status = esp_ble_gattc_get_attr_count( gattc_if,p_data->search_cmpl.conn_id,ESP_GATT_DB_CHARACTERISTIC, gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, gl_profile_tab[PROFILE_A_APP_ID].service_end_handle, INVALID_HANDLE, &count);if (status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");}if (count > 0){char_elem_result = (esp_gattc_char_elem_t*)malloc(sizeof(esp_gattc_char_elem_t) * count);if (!char_elem_result){ESP_LOGE(GATTC_TAG, "gattc no mem");}else{status = esp_ble_gattc_get_char_by_uuid( gattc_if,p_data->search_cmpl.conn_id, gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,remote_filter_char_uuid,char_elem_result,&count);if (status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_char_by_uuid error");}/* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used first 'char_elem_result' */if (count > 0 && (char_elem_result[0].properties &ESP_GATT_CHAR_PROP_BIT_NOTIFY)){gl_profile_tab[PROFILE_A_APP_ID].char_handle = char_elem_result[0].char_handle;esp_ble_gattc_register_for_notify (gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, char_elem_result[0].char_handle);}}/* free char_elem_result */free(char_elem_result);}else{ESP_LOGE(GATTC_TAG, "no char found");} }break;
…
/* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used first 'char_elem_result' */if(count > 0 && (char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)){gl_profile_tab[PROFILE_A_APP_ID].char_handle = char_elem_result[0].char_handle;esp_ble_gattc_register_for_notify (gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda,char_elem_result[0].char_handle);}
…
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {ESP_LOGI(GATTC_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT");if (p_data->reg_for_notify.status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "REG FOR NOTIFY failed: error status = %d", p_data->reg_for_notify.status);}else{uint16_t count = 0;uint16_t notify_en = 1;esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count( gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id,ESP_GATT_DB_DESCRIPTOR,gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,gl_profile_tab[PROFILE_A_APP_ID].char_handle, &count);if (ret_status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");}if (count > 0){descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count);if (!descr_elem_result){ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");}else{ret_status = esp_ble_gattc_get_descr_by_char_handle( gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, p_data->reg_for_notify.handle, notify_descr_uuid, descr_elem_result,&count);if (ret_status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_descr_by_char_handle error");}/* Every char has only one descriptor in our 'ESP_GATTS_DEMO' demo, so we used first 'descr_elem_result' */if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG){ret_status = esp_ble_gattc_write_char_descr( gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id,descr_elem_result[0].handle,sizeof(notify_en),(Uint8 *)¬ify_en,ESP_GATT_WRITE_TYPE_RSP,ESP_GATT_AUTH_REQ_NONE);}if (ret_status != ESP_GATT_OK){ESP_LOGE(GATTC_TAG, "esp_ble_gattc_write_char_descr error");}/* free descr_elem_result */free(descr_elem_result);}}else{ESP_LOGE(GATTC_TAG, "decsr not found");}}break;}
static esp_gatt_id_t notify_descr_id = {.uuid = {.len = ESP_UUID_LEN_16,.uuid = {.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,},},.inst_id = 0,
};
#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */
ESP32蓝牙的Gatt Client的例子演练相关推荐
- 【ESP32蓝牙通信】gatt_client 和 gatt_server 调试
ESP32蓝牙通信 蓝牙协议基本概念 ESP32 蓝牙客户端和服务端 ESP32 作为服务器调试 ESP32 作为客户端调试 项目中需要用到ESP32的蓝牙通信,查资料知道.当esp32作为客户端 c ...
- ESP32:蓝牙BLE控制M3508电机
ESP32:蓝牙BLE控制M3508电机 先给各位朋友拜个年,祝大家新春快乐,事事顺利,身体健康啊! 还是熟悉的3508,内容概述: ESP32主控 蓝牙BLE通信 使用实时系统(FreeRTOS) ...
- esp32系列(5):esp32 蓝牙架构学习
目录 1 ESP32 蓝牙架构学习 1.1 蓝牙 1.1.1 HCI 接口选择 1.1.2 蓝牙运行环境 1.1.3 框架 1.1.3.1 控制器 1.1.3.2 BLUEDROID 1.2 经典蓝牙 ...
- esp32与android蓝牙,ESP32蓝牙架构(官方)_esp32蓝牙,esp32如何连接手机蓝牙
ESP32 蓝牙开发资料,用于了解ESP32内部的蓝牙实现. 本⼿册为 ESP32 的蓝⽛架构简介,主要分三个章节介绍了蓝⽛.经典蓝⽛和蓝⽛低功耗 ⽅⾯的整体架构.注意,本⼿册仅针对 ESP-IDF ...
- esp32系列(6):esp32 蓝牙HID设备demo学习
目录 1 USB 相关知识 2 HID 基础知识 2.1 HID 描述符的概念 2.2 功能特性 2.2.1 HID Class 2.2.2 Subclass 2.2.3 Protocols 2.2. ...
- APP Invertor 制作BLE蓝牙APP 控制esp32蓝牙小车
APP Invertor蓝牙小车制作 1.插件下载 1.1.导入插件 2.APP界面展示 2.1 .可视化编程 3.esp32 蓝牙代码 4.实测效果 4.1 APP控制端 4.2 蓝牙接收端 经 ...
- esp32 camera_利用Phyphox和ESP32蓝牙制作欧姆表测电阻
近日,微主在利用Phyphox和ESP32蓝牙研究热敏电阻的阻值与温度的关系时,需要绘制热敏电阻阻值与时间的关系图像,通过用手捏住或者放开热敏电阻,观察电阻与时间关系图像的变化情况,进而了解温度对热敏 ...
- Java实现的简单的WebService服务发布和Client调用例子
做大作业,要求用到WS,在网上看了看,自己写了一个,很简单的服务发布和Client调用例子. WebService有很多实现的方式,但是原理基本都是一样的,都是基于SOA的三角模型,所以重点在于理解了 ...
- 关于esp32蓝牙模块的使用——esp32学习笔记
关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 零.前言 一.经典蓝牙BT 二.低功耗蓝牙B ...
最新文章
- 一点历史--Python
- (005)RN开发 js jsx ts tsx的区别
- 余额宝 vs. P2P网贷,谁更有生命力?
- 【跃迁之路】【451天】程序员高效学习方法论探索系列(实验阶段208-2018.05.02)...
- Vh和Vw的简介和使用
- AliOS Things图形界面开发指南
- vue 生成发布包_Vue 3.0 终于正正正正正式发布了!
- java基础大概_Java基础知识(一)
- VB 按指定编码格式写入文本文件
- 原来竟然还有这种局部变量!
- Hive压缩存储性能测试
- react替换元素节点_React万字长文面试题梳理
- 排序算法专题-基数排序
- hdu 1712 ACboy needs your help (DP)
- Mysql第一天笔记02——安装Navicat
- Google Play要求app从2019年8月1日起支持64位CPU
- 跟开涛学SpringMVC
- Web前端和后端之区分
- WordPress主题分享:Flatsome主题v3.15.7免费下载 2022年最新版
- 【最新】Firefox Manifest V3:进展及下一步计划
热门文章
- Sender的意义及使用举例
- 华创资本“细+慢活儿”的企业服务SaaS
- 邻居交换——冒泡排序
- 基于51单片机的教室车辆计数器报警proteus仿真原理图PCB
- linux 用记事本打开文件,Ubuntu 10.04下Gedit打开Windows记事本.txt文件乱码解决
- mysql查询 31到40_sql语句,取出表A中的第31条到40条记录
- 31条指令单周期cpu设计(Verilog)-(十)上代码→顶层模块设计总结
- 接口神器:Apifox,究竟有多香!
- 街拍视频和街拍第一站的爬虫
- 中国商业印刷行业市场深度调研及投资价值评估研究报告