mirror of
https://github.com/demik/quack.git
synced 2024-11-22 20:32:58 +00:00
284 lines
9.0 KiB
C
284 lines
9.0 KiB
C
/* This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
Unless required by applicable law or agreed to in writing, this software is
|
|
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
CONDITIONS OF ANY KIND, either express or implied.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/event_groups.h"
|
|
#include "esp_system.h"
|
|
#include "esp_wifi.h"
|
|
#include "esp_event.h"
|
|
#include "esp_log.h"
|
|
#include "nvs_flash.h"
|
|
#include "esp_bt.h"
|
|
#include "esp_bt_defs.h"
|
|
#include "esp_gap_ble_api.h"
|
|
#include "esp_gatts_api.h"
|
|
#include "esp_gatt_defs.h"
|
|
#include "esp_bt_main.h"
|
|
#include "esp_bt_device.h"
|
|
|
|
#include "esp_hidh.h"
|
|
#include "esp_hid_gap.h"
|
|
|
|
#include "blue.h"
|
|
#include "gpio.h"
|
|
#include "led.h"
|
|
|
|
#define BTMOUSE_BUTTON1 (1 << 0)
|
|
#define BTMOUSE_BUTTON2 (1 << 1)
|
|
#define BTMOUSE_BUTTON3 (1 << 2)
|
|
#define BTMOUSE_BUTTONE 248
|
|
|
|
/* debug tag */
|
|
static const char *TAG = "blue";
|
|
|
|
/* private functions */
|
|
static esp_hid_raw_report_map_t *blue_hid_rm_get(esp_hidh_dev_t *dev);
|
|
static esp_hid_report_item_t blue_hid_ri_find(esp_hidh_dev_t *d, esp_hid_usage_t u, uint8_t t, uint8_t p);
|
|
|
|
void hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
|
|
{
|
|
esp_hidh_event_t event = (esp_hidh_event_t)id;
|
|
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
|
|
|
|
const uint8_t *bda = NULL;
|
|
|
|
/*
|
|
* esp_hidh_event_data_t = union
|
|
*
|
|
* struct {
|
|
* esp_hidh_dev_t *dev; // HID Remote bluetooth device
|
|
* } open;
|
|
*
|
|
* struct {
|
|
* esp_hidh_dev_t *dev; // HID Remote bluetooth device
|
|
* int reason; // Reason why the connection was closed. BLE Only
|
|
* } close;
|
|
*
|
|
* struct {
|
|
* esp_hidh_dev_t *dev; // HID Remote bluetooth device
|
|
* uint8_t level; // Battery Level (0-100%)
|
|
* } battery;
|
|
*
|
|
* struct {
|
|
* esp_hidh_dev_t *dev; // HID Remote bluetooth device
|
|
* esp_hid_usage_t usage; // HID report usage
|
|
* uint16_t report_id; // HID report index
|
|
* uint16_t length; // HID data length
|
|
* uint8_t *data; // The pointer to the HID data
|
|
* uint8_t map_index; // HID report map index
|
|
* } input;
|
|
*
|
|
* struct {
|
|
* esp_hidh_dev_t *dev; // HID Remote bluetooth device
|
|
* esp_hid_usage_t usage; // HID report usage
|
|
* uint16_t report_id; // HID report index
|
|
* uint16_t length; // HID data length
|
|
* uint8_t *data; // The pointer to the HID data
|
|
* uint8_t map_index; // HID report map index
|
|
* } feature;
|
|
*/
|
|
|
|
char click;
|
|
short x, y;
|
|
|
|
switch (event) {
|
|
case ESP_HIDH_OPEN_EVENT: {
|
|
blue_open(param);
|
|
break;
|
|
}
|
|
case ESP_HIDH_BATTERY_EVENT: {
|
|
bda = esp_hidh_dev_bda_get(param->battery.dev);
|
|
ESP_LOGI(TAG, ESP_BD_ADDR_STR " battery: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
|
|
break;
|
|
}
|
|
case ESP_HIDH_INPUT_EVENT: {
|
|
bda = esp_hidh_dev_bda_get(param->input.dev);
|
|
ESP_LOGD(TAG, ESP_BD_ADDR_STR " INPUT: %8s, MAP: %2u, ID: %3u, Len: %d, Data:", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->input.usage), param->input.map_index, param->input.report_id, param->input.length);
|
|
ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
|
|
xTaskNotify(t_yellow, LED_ONCE, eSetValueWithOverwrite);
|
|
memcpy (&click, param->input.data, sizeof(uint8_t));
|
|
click = click & (BTMOUSE_BUTTON1 | BTMOUSE_BUTTON2 | BTMOUSE_BUTTON3);
|
|
if (click)
|
|
ESP_LOGI(TAG, "CLICK: %d", click);
|
|
break;
|
|
}
|
|
case ESP_HIDH_FEATURE_EVENT: {
|
|
bda = esp_hidh_dev_bda_get(param->feature.dev);
|
|
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length);
|
|
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
|
|
break;
|
|
}
|
|
case ESP_HIDH_CLOSE_EVENT: {
|
|
blue_close(param);
|
|
break;
|
|
}
|
|
default:
|
|
ESP_LOGI(TAG, "Unknwown event: %d", event);
|
|
}
|
|
}
|
|
|
|
void blue_init(void)
|
|
{
|
|
esp_err_t ret;
|
|
|
|
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_LOGD(TAG, "Starting Bluetooth init");
|
|
ESP_ERROR_CHECK(ret);
|
|
ESP_ERROR_CHECK(esp_hid_gap_init(ESP_BT_MODE_BTDM));
|
|
ESP_ERROR_CHECK(esp_ble_gattc_register_callback(esp_hidh_gattc_event_handler));
|
|
|
|
esp_hidh_config_t config = {
|
|
.callback = hidh_callback,
|
|
.event_stack_size = 4096
|
|
};
|
|
|
|
ESP_ERROR_CHECK(esp_hidh_init(&config));
|
|
esp_log_level_set("event", ESP_LOG_INFO);
|
|
|
|
/*
|
|
* at this point, everything but bluetooth is started.
|
|
* put green steady, start blinking blue and keep scanning until a device is found
|
|
*/
|
|
|
|
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
|
|
xTaskNotify(t_blue, LED_FAST, eSetValueWithOverwrite);
|
|
xTaskCreatePinnedToCore(blue_scan, "blue_scan", 6 * 1024, NULL, 2, NULL, 0);
|
|
}
|
|
|
|
void blue_close(esp_hidh_event_data_t *p) {
|
|
const uint8_t *bda = NULL;
|
|
|
|
configASSERT(p != NULL);
|
|
bda = esp_hidh_dev_bda_get(p->close.dev);
|
|
ESP_LOGI(TAG, "closed connection with device " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
|
|
esp_hidh_dev_free(p->close.dev);
|
|
|
|
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
|
|
}
|
|
|
|
void blue_open(esp_hidh_event_data_t *p) {
|
|
const uint8_t *bda = NULL;
|
|
|
|
esp_hid_report_item_t mir; /* MOUSE INPUT REPORT */
|
|
|
|
configASSERT(p != NULL);
|
|
bda = esp_hidh_dev_bda_get(p->open.dev);
|
|
ESP_LOGI(TAG, "opened connection with " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
|
|
|
|
/* search for MIR section */
|
|
esp_hid_raw_report_map_t *maps;
|
|
maps = blue_hid_rm_get(p->open.dev);
|
|
mir = blue_hid_ri_find(p->open.dev, ESP_HID_USAGE_MOUSE, ESP_HID_REPORT_TYPE_INPUT, ESP_HID_PROTOCOL_MODE_REPORT);
|
|
|
|
xTaskNotify(t_blue, LED_ON, eSetValueWithOverwrite);
|
|
gpio_output_enable();
|
|
}
|
|
|
|
/* get specific report from report map matching specified usage + type + protocol */
|
|
static esp_hid_report_item_t blue_hid_ri_find(esp_hidh_dev_t *d, esp_hid_usage_t u, uint8_t t, uint8_t p) {
|
|
size_t num_reports = 0;
|
|
esp_hid_report_item_t *reports;
|
|
esp_hid_report_item_t search;
|
|
|
|
configASSERT(d != NULL);
|
|
configASSERT(p <= ESP_HID_REPORT_TYPE_FEATURE);
|
|
configASSERT(t > 0);
|
|
|
|
esp_hidh_dev_reports_get(d, &num_reports, &reports);
|
|
memset(&search, 0, sizeof(esp_hid_report_item_t));
|
|
|
|
for (uint8_t i = 0; i < num_reports; i++) {
|
|
if (reports[i].protocol_mode == p && reports[i].report_type == t && reports[i].usage == u) {
|
|
memcpy(&search, &reports[i], sizeof(esp_hid_report_item_t));
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(reports);
|
|
if (search.value_len == 0) {
|
|
ESP_LOGW(TAG, "%s %s %s not found",
|
|
esp_hid_usage_str(u),
|
|
esp_hid_report_type_str(t),
|
|
esp_hid_protocol_mode_str(p));
|
|
}
|
|
else {
|
|
ESP_LOGD(TAG, "%s %s %s is id %i size %i byte(s)",
|
|
esp_hid_usage_str(u),
|
|
esp_hid_report_type_str(t),
|
|
esp_hid_protocol_mode_str(p),
|
|
search.report_id,
|
|
search.value_len);
|
|
}
|
|
|
|
return search;
|
|
}
|
|
|
|
/* get report map raw pointer. Also dump it on JTAG */
|
|
static esp_hid_raw_report_map_t *blue_hid_rm_get(esp_hidh_dev_t *dev) {
|
|
size_t num_maps = 0;
|
|
esp_hid_raw_report_map_t *maps;
|
|
|
|
configASSERT(dev != NULL);
|
|
ESP_LOGI(TAG, BLUE_SNIP);
|
|
esp_hidh_dev_report_maps_get(dev, &num_maps, &maps);
|
|
ESP_LOG_BUFFER_HEX(TAG, maps[0].data, maps[0].len);
|
|
ESP_LOGI(TAG, BLUE_SNIP);
|
|
|
|
/* looking at 4.2 SDK source, seems there is always only one map */
|
|
configASSERT(num_maps == 1);
|
|
return maps;
|
|
}
|
|
|
|
void blue_scan(void *pvParameters) {
|
|
size_t len = 0;
|
|
esp_hid_scan_result_t *mouse = NULL;
|
|
esp_hid_scan_result_t *results = NULL;
|
|
|
|
ESP_LOGI(TAG, "starting scan on core %d…", xPortGetCoreID());
|
|
esp_hid_scan(BLUE_SCAN_DURATION, &len, &results);
|
|
ESP_LOGI(TAG, "scan returned %u result(s)", len);
|
|
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
|
|
|
|
if (len) {
|
|
esp_hid_scan_result_t *r = results;
|
|
|
|
while (r) {
|
|
ESP_LOGI(TAG, "found %s %s device: " ESP_BD_ADDR_STR ", RSSI: %d, NAME: %s",
|
|
(r->transport == ESP_HID_TRANSPORT_BLE) ? "BLE" : "BT",
|
|
esp_hid_cod_major_str(r->bt.cod.major),
|
|
ESP_BD_ADDR_HEX(r->bda), r->rssi, r->name ? r->name : "");
|
|
|
|
/* search for something that looks like Bluetooth Classic mouse */
|
|
if (r->transport == ESP_HID_TRANSPORT_BT &&
|
|
strcmp("PERIPHERAL", esp_hid_cod_major_str(r->bt.cod.major)) == 0
|
|
&& (r->bt.cod.minor & ESP_HID_COD_MIN_MOUSE)) {
|
|
mouse = r;
|
|
}
|
|
r = r->next;
|
|
}
|
|
|
|
/* try to connect to the last candidate found */
|
|
if (mouse)
|
|
esp_hidh_dev_open(mouse->bda, mouse->transport, mouse->ble.addr_type);
|
|
else
|
|
ESP_LOGI(TAG, "devices found but no mouse detected");
|
|
|
|
esp_hid_scan_results_free(results);
|
|
}
|
|
|
|
vTaskDelete(NULL);
|
|
}
|