Index

auto-plow / 8b6f76f

A wheelchair motor-propelled battery-powered ESP32-driven remote control snow plow.

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
8412 Jan 2023 16:048b6f76fRadio receiver handlersJosh Stockin11480G

Blob @ auto-plow / src / input.c

text/plain4987 bytesdownload raw
1// espy Copyright (c) 2023 Josh Stockin <josh@joshstock.in>
2// [https://joshstock.in] [https://github.com/joshuas3]
3//
4// This software is licensed and distributed under the terms of the MIT License.
5// See the MIT License in the LICENSE file of this project's root folder.
6//
7// This comment block and its contents, including this disclaimer, MUST be
8// preserved in all copies or distributions of this software's source.
9
10
11// Filename: input.c
12// Description: Handlers for radio receiver, rotary encoder
13
14
15#include "esp_attr.h"
16#include "esp_err.h"
17#include "esp_check.h"
18#include "esp_log.h"
19#include "freertos/FreeRTOS.h"
20#include "freertos/queue.h"
21#include "driver/gpio.h"
22#include "driver/gptimer.h"
23
24#include "input.h"
25#include "pins.h"
26
27
28static const char* TAG = "input.c";
29
30static uint32_t current_radio_channel = 0;
31static uint32_t radio_channels[NUM_RADIO_CHANNELS] = {0};
32static QueueHandle_t xInputQueue;
33gptimer_handle_t pulse_timer = NULL;
34
35esp_err_t setup_radio_and_rotenc_input( void ) {
36
37 ESP_LOGI( TAG, "creating input queue" );
38
39 xInputQueue = xQueueCreate( 10, sizeof( xInputEvent* ) ); // xInputEvent from input.h
40 ESP_RETURN_ON_FALSE( xInputQueue != 0, ESP_FAIL, TAG, "xQueueCreate failed" );
41
42 ESP_LOGI( TAG, "configuring RADIO_PPM pin for input, pull_down_en" );
43
44 gpio_config_t radio_pin_conf = {
45 .pin_bit_mask = (1ULL << RADIO_PPM),
46 .mode = GPIO_MODE_INPUT,
47 .pull_down_en = 1,
48 .pull_up_en = 0,
49 };
50 ESP_RETURN_ON_ERROR( gpio_config( &radio_pin_conf ), TAG, "gpio_config failed" );
51
52 ESP_LOGI( TAG, "creating positive edge RADIO_PPM interrupt for radio_pulse callback" );
53
54 ESP_RETURN_ON_ERROR( gpio_set_intr_type( RADIO_PPM, GPIO_INTR_POSEDGE ), TAG, "gpio_set_intr_type failed" );
55 ESP_RETURN_ON_ERROR( gpio_isr_handler_add( RADIO_PPM, radio_pulse, NULL ), TAG, "gpio_isr_handler_add failed" );
56 ESP_RETURN_ON_ERROR( gpio_intr_enable( RADIO_PPM ), TAG, "gpio_intr_enable failed" );
57
58 ESP_RETURN_ON_ERROR( gpio_wakeup_enable( RADIO_PPM, GPIO_INTR_POSEDGE ), TAG, "gpio_wakeup_enable failed" );
59
60 gptimer_config_t timer_config = {
61 .clk_src = GPTIMER_CLK_SRC_DEFAULT,
62 .direction = GPTIMER_COUNT_UP,
63 .resolution_hz = 1000000, // 1MHz, microsecond precision
64 };
65 ESP_RETURN_ON_ERROR( gptimer_new_timer( &timer_config, &pulse_timer ), TAG, "gpitimer_new_timer failed" );
66 ESP_RETURN_ON_ERROR( gptimer_enable( pulse_timer ), TAG, "gptimer_enable failed" );
67 ESP_RETURN_ON_ERROR( gptimer_start( pulse_timer ), TAG, "gptimer_enable failed" );
68
69 TaskHandle_t input_task = NULL;
70 xTaskCreate( input_handler_task, "input_handler_task", 400, NULL, 2, &input_task ); // stack size 400 bytes, priority 2
71
72 return ESP_OK;
73
74}
75
76void IRAM_ATTR radio_pulse( void* arg ) {
77
78 // get pulse timer value
79 uint64_t offset = 0;
80 gptimer_get_raw_count( pulse_timer, &offset );
81
82 // insert timer value into queue
83 xInputEvent pulse_event = {
84 .type = INPUT_EVENT_RADIOPULSE,
85 .data = offset,
86 };
87 xQueueSendFromISR( xInputQueue, &pulse_event, NULL );
88
89 // reset timer
90 gptimer_set_raw_count( pulse_timer, 0 );
91
92 // debug log event
93 ESP_DRAM_LOGD( TAG, "INTR: received radio pulse (offset %lu), queued", offset );
94
95}
96
97void input_handler_task( void* arg ) {
98 // clears input event queue
99
100 xInputEvent event;
101 for ( ;; ) {
102 while ( xQueueReceive( xInputQueue, &event, 0 ) ) {
103
104 if ( event.type == INPUT_EVENT_RADIOPULSE ) {
105 // ppm radio receiver handling
106
107 if ( event.data >= RADIO_PULSE_STOPGAP_US ) {
108
109 // pulse gap is larger than designated stopgap time,
110 // indicates end of a stopgap (beginning of transmission);
111 // reset data
112 current_radio_channel = 0;
113 for ( int i = 0; i < NUM_RADIO_CHANNELS; i++ ) {
114 radio_channels[i] = 0;
115 }
116
117 } else {
118
119 if ( ++current_radio_channel >= NUM_RADIO_CHANNELS ) {
120 // surpassed number of expected pulses without a stopgap;
121 // something's wrong, continue until next stopgap
122 continue;
123 }
124
125 // valid data; since the time between the Nth pulse and the
126 // Nth-1 pulse indicates the value of channel N-1, set it
127 // accordingly
128 radio_channels[ current_radio_channel - 1 ] = event.data;
129
130 ESP_LOGD( TAG, "radio channel %lu: %lu", current_radio_channel, event.data );
131 }
132
133 } else if ( event.type == INPUT_EVENT_ROTENCSW ) {
134
135 // TODO
136
137 } else if ( event.type == INPUT_EVENT_ROTENCTURN ) {
138
139 // TODO
140
141 }
142
143 }
144
145 taskYIELD(); // queue is empty, don't waste anymore processor time
146 }
147
148}
149