diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 047615a..f853547 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -3,7 +3,8 @@ set(srcs "adb.c" "esp_hid_gap.c" "gpio.c" "led.c" - "main.c") + "main.c" + "quad.c") set(include_dirs ".") diff --git a/main/adb.c b/main/adb.c index f86af70..25985af 100644 --- a/main/adb.c +++ b/main/adb.c @@ -65,14 +65,15 @@ void adb_init(void) { /* If jumper is set, switch to ADB host mode */ if (gpio_get_level(GPIO_ADBSRC) == 0) - xTaskCreatePinnedToCore(&adb_task_host, "ADB_HOST", 6 * 1024, NULL, tskADB_PRIORITY, NULL, 1); + xTaskCreatePinnedToCore(adb_task_host, "ADB_HOST", 6 * 1024, NULL, tskADB_PRIORITY, NULL, 1); else - xTaskCreatePinnedToCore(&adb_task_mouse, "ADB_MOUSE", 6 * 1024, NULL, tskADB_PRIORITY, NULL, 1); + xTaskCreatePinnedToCore(adb_task_mouse, "ADB_MOUSE", 6 * 1024, NULL, tskADB_PRIORITY, NULL, 1); } void adb_task_host(void *pvParameters) { uint16_t data; + /* put green led to steady if BT is disabled. Otherwise BT init will do it */ if (gpio_get_level(GPIO_BTOFF) == 0) xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite); ESP_LOGI("ADB", "ADB host started"); @@ -80,7 +81,7 @@ void adb_task_host(void *pvParameters) { /* poll the mouse like a maniac. It will answer only if there is user input */ ESP_ERROR_CHECK(rmt_driver_install(RMT_RX_CHANNEL, 200, 0)); - while (1) { + while (true) { vTaskDelay(20 / portTICK_PERIOD_MS); adb_tx_cmd(ADB_MOUSE|ADB_TALK|ADB_REG0); data = adb_rx_mouse(); @@ -88,7 +89,8 @@ void adb_task_host(void *pvParameters) { } void adb_task_mouse(void *pvParameters) { - + ESP_LOGI("ADB", "ADB mouse started"); + vTaskSuspend(NULL); } int dur( uint32_t level, uint32_t duration ) { diff --git a/main/blue.c b/main/blue.c index 4228f62..1396fc5 100644 --- a/main/blue.c +++ b/main/blue.c @@ -39,6 +39,10 @@ /* 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; @@ -142,14 +146,15 @@ void blue_init(void) }; 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_SLOW, eSetValueWithOverwrite); + xTaskNotify(t_blue, LED_FAST, eSetValueWithOverwrite); xTaskCreatePinnedToCore(blue_scan, "blue_scan", 6 * 1024, NULL, 2, NULL, 0); } @@ -158,38 +163,85 @@ void blue_close(esp_hidh_event_data_t *p) { configASSERT(p != NULL); bda = esp_hidh_dev_bda_get(p->close.dev); - ESP_LOGI(TAG, "closed connection with device " ESP_BD_ADDR_STR ", reason: %s", ESP_BD_ADDR_HEX(bda), - esp_hid_disconnect_reason_str(esp_hidh_dev_transport_get(p->close.dev), p->close.reason)); + 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)); - //fprintf(fp, "BDA:" ESP_BD_ADDR_STR ", Status: %s, Connected: %s, Handle: %d, Usage: %s\n", ESP_BD_ADDR_HEX(dev->bda), s_bta_hh_status_names[dev->status], dev->connected ? "YES" : "NO", dev->bt.handle, esp_hid_usage_str(dev->usage)); + /* 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); - //ESP_LOGI(TAG, "opened connection with " - // ESP_LOGI(TAG, "PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x\n", p->open.dev->config.product_id, p->open.dev->config.vendor_id, p->open.dev->config.version); - //fprintf(fp, "Report Map Length: %d\n", dev->config.report_maps[0].len); - - //esp_hidh_dev_report_t *report = dev->reports; - //while (report) { - // fprintf(fp, " %8s %7s %6s, ID: %3u, Length: %3u\n", - // esp_hid_usage_str(report->usage), esp_hid_report_type_str(report->report_type), esp_hid_protocol_mode_str(report->protocol_mode), - // report->report_id, report->value_len); - // report = report->next; - //} - - // (p->open.dev, stdout); 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; @@ -198,28 +250,27 @@ void blue_scan(void *pvParameters) { 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 device: " ESP_BD_ADDR_STR ", RSSI: %d, NAME: %s", - (r->transport == ESP_HID_TRANSPORT_BLE) ? "BLE" : "BT", - ESP_BD_ADDR_HEX(r->bda), r->rssi, r->name ? r->name : ""); - - if (r->transport == ESP_HID_TRANSPORT_BLE) { - printf("APPEARANCE: 0x%04x, ", r->ble.appearance); - printf("ADDR_TYPE: '%s', ", ble_addr_type_str(r->ble.addr_type)); - } else { - if (strcmp("PERIPHERAL", esp_hid_cod_major_str(r->bt.cod.major)) == 0 - && (r->bt.cod.minor & ESP_HID_COD_MIN_MOUSE)) { - ESP_LOGI(TAG, "found generic mouse"); + 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 mouse found + /* try to connect to the last candidate found */ if (mouse) esp_hidh_dev_open(mouse->bda, mouse->transport, mouse->ble.addr_type); else diff --git a/main/blue.h b/main/blue.h index c0d7c41..9d43714 100644 --- a/main/blue.h +++ b/main/blue.h @@ -27,6 +27,7 @@ /* defines */ #define BLUE_SCAN_DURATION 8 +#define BLUE_SNIP "---8<------------------------------------------" /* prototypes */ void blue_init(void); diff --git a/main/gpio.c b/main/gpio.c index 29d0eae..06686f8 100644 --- a/main/gpio.c +++ b/main/gpio.c @@ -72,10 +72,6 @@ void gpio_init(void) { gpio_set_direction(GPIO_QX2, GPIO_MODE_OUTPUT); gpio_set_direction(GPIO_QY1, GPIO_MODE_OUTPUT); gpio_set_direction(GPIO_QY2, GPIO_MODE_OUTPUT); - - gpio_set_level(GPIO_CLICK, 1); - gpio_set_level(GPIO_QY1, 1); - gpio_set_level(GPIO_QY2, 1); } void gpio_output_disable(void) { diff --git a/main/main.c b/main/main.c index e756709..3e14041 100644 --- a/main/main.c +++ b/main/main.c @@ -36,6 +36,7 @@ #include "blue.h" #include "led.h" #include "gpio.h" +#include "quad.h" /* global TAG for ESP_LOG */ static const char* TAG = "quack"; @@ -56,23 +57,27 @@ void app_main(void) (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); ESP_LOGI(TAG, "Minimum free heap size: %d bytes", esp_get_minimum_free_heap_size()); + ESP_LOGI(TAG, ""); + ESP_LOGI(TAG, "\\_o< \\_o< \\_o< \\_O<"); + ESP_LOGI(TAG, ""); gpio_init(); led_init(); + quad_init(); adb_init(); /* * Check /BTOFF and enable bluetooth if needed - * of bluetooth is disabled, enable ADB and Quadrature outputs + * of bluetooth is disabled, enable quadrature outputs */ if (gpio_get_level(GPIO_BTOFF) == 1) blue_init(); else gpio_output_enable(); - /* Blink error if no inputs (/BTOFF and no /ADBSRC) */ + /* put LED error ON if no inputs (/BTOFF and no /ADBSRC) */ if (gpio_get_level(GPIO_BTOFF) == 0 && gpio_get_level(GPIO_ADBSRC) == 1) { ESP_LOGE(TAG, "Bluetooth is off and ADB is in device mode!"); - xTaskNotify(t_red, LED_SLOW, eSetValueWithOverwrite); + xTaskNotify(t_red, LED_ON, eSetValueWithOverwrite); } } diff --git a/main/quad.c b/main/quad.c new file mode 100644 index 0000000..4de4ba7 --- /dev/null +++ b/main/quad.c @@ -0,0 +1,177 @@ +/* + * quad.c + * quack + * + * Created by Michel DEPEIGE on 17/04/2021. + * Copyright (c) 2021 Michel DEPEIGE. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +#include +#include "driver/gpio.h" +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_err.h" +#include "esp_log.h" +#include "driver/timer.h" +#include "esp_timer.h" + +#include "quad.h" +#include "gpio.h" + +/* global variables for tasks handles */ +TaskHandle_t t_click, t_qx, t_qy; +esp_timer_handle_t quad_qx, quad_qy; + +/* static functions */ +static void quad_timer(void* arg); + +/* phases */ +const bool q1[] = {true, false, false, true}; +const bool q2[] = {true, true, false, false}; + +/* init all leds, blink everything once and start background tasks */ +void quad_init(void) { + esp_timer_create_args_t args; + + /* create quadrature tasks */ + xTaskCreate(quad_click, "CLICK", 1024, NULL, tskIDLE_PRIORITY + 1, &t_click); + xTaskCreate(quad_move_x, "QX", 1024, NULL, tskIDLE_PRIORITY + 1, &t_qx); + xTaskCreate(quad_move_y, "QY", 1024, NULL, tskIDLE_PRIORITY + 1, &t_qy); + + /* click is active low */ + gpio_set_level(GPIO_CLICK, 1); + + /* init quadrature position */ + gpio_set_level(GPIO_QX1, q1[0]); + gpio_set_level(GPIO_QX2, q2[0]); + gpio_set_level(GPIO_QY1, q1[0]); + gpio_set_level(GPIO_QY2, q2[0]); + + /* create timers for quadrature phases */ + args.callback = &quad_timer; + args.arg = &t_qx; + args.name = "quad_qx"; + ESP_ERROR_CHECK(esp_timer_create(&args, &quad_qx)); + args.callback = &quad_timer; + args.arg = &t_qy; + args.name = "quad_qy"; + ESP_ERROR_CHECK(esp_timer_create(&args, &quad_qy)); + + ESP_LOGI("quad", "Quadrature tasks started"); +} + +void quad_click(void *pvParameters) { + unsigned int click = 0; + + (void)pvParameters; + + while (true) { + xTaskNotifyWait(0, 0, &click, portMAX_DELAY); + switch (click) + { + case true: + gpio_set_level(GPIO_CLICK, 0); + break; + case false: + gpio_set_level(GPIO_CLICK, 1); + break; + default: + break; + } + } +} + +void quad_move_x(void *pvParameters) { + unsigned int value = 0; + int move = 0; + uint8_t i = 0; + + (void)pvParameters; + + while (true) { + xTaskNotifyWait(0, 0, &value, portMAX_DELAY); + assert(value != 0); + move = (signed)value; + + if (move > 0) { + while (move--) { + i++; + gpio_set_level(GPIO_QX1, q1[i % 4]); + gpio_set_level(GPIO_QX2, q2[i % 4]); + ESP_ERROR_CHECK(esp_timer_start_once(quad_qx, QUAD_INTERVAL)); + vTaskSuspend(NULL); + } + } + else { + while (move++) { + i--; + gpio_set_level(GPIO_QX1, q1[i % 4]); + gpio_set_level(GPIO_QX2, q2[i % 4]); + ESP_ERROR_CHECK(esp_timer_start_once(quad_qx, QUAD_INTERVAL)); + vTaskSuspend(NULL); + } + } + } +} + +void quad_move_y(void *pvParameters) { + unsigned int value = 0; + int move = 0; + uint8_t i = 0; + + (void)pvParameters; + + while (true) { + xTaskNotifyWait(0, 0, &value, portMAX_DELAY); + assert(value != 0); + move = (signed)value; + + if (move > 0) { + while (move--) { + i++; + gpio_set_level(GPIO_QY1, q1[i % 4]); + gpio_set_level(GPIO_QY2, q2[i % 4]); + ESP_ERROR_CHECK(esp_timer_start_once(quad_qy, QUAD_INTERVAL)); + vTaskSuspend(NULL); + } + } + else { + while (move++) { + i--; + gpio_set_level(GPIO_QY1, q1[i % 4]); + gpio_set_level(GPIO_QY2, q2[i % 4]); + ESP_ERROR_CHECK(esp_timer_start_once(quad_qy, QUAD_INTERVAL)); + vTaskSuspend(NULL); + } + } + } +} + +/* simple ISR function. Resume task that called the oneshot timer */ +static void quad_timer(void* arg) { + BaseType_t xYieldRequired = pdFALSE; + + xYieldRequired = xTaskResumeFromISR(arg); + + /* switch context of needed */ + if( xYieldRequired == pdTRUE ) + portYIELD_FROM_ISR(); +} diff --git a/main/quad.h b/main/quad.h new file mode 100644 index 0000000..feda14b --- /dev/null +++ b/main/quad.h @@ -0,0 +1,38 @@ +/* + * quad.h + * quack + * + * Created by Michel DEPEIGE on 17/01/2021. + * Copyright (c) 2021 Michel DEPEIGE. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING); if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +#ifndef QUAD_H +#define QUAD_H + +/* prototypes */ +void quad_init(void); +void quad_click(void *pvParameters); +void quad_move_x(void *pvParameters); +void quad_move_y(void *pvParameters); + +/* defines */ +#define QUAD_INTERVAL 100 + +#endif + diff --git a/sdkconfig b/sdkconfig index 55ccc72..45ab804 100644 --- a/sdkconfig +++ b/sdkconfig @@ -842,10 +842,10 @@ CONFIG_HEAP_TRACING_OFF=y # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -CONFIG_LOG_DEFAULT_LEVEL_INFO=y -# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_DEFAULT_LEVEL=4 CONFIG_LOG_COLORS=y CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set