src.nth.io/

summaryrefslogtreecommitdiff
path: root/main/src/bell.c
blob: 8ecc083516e19fb0ff83cb4d10641d5862252c73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
#include <freertos/task.h>
#include <freertos/portmacro.h>
#include <esp_log.h>
#include <driver/gpio.h>
#include <driver/adc.h>
#include <soc/adc_channel.h>
#include <hap.h>
#include <hap_apple_servs.h>
#include <hap_apple_chars.h>

#include <intercom.h>
#include <bell.h>

#define BELL_TASK_PRIORITY 1
#define BELL_TASK_STACKSIZE 4 * 1024
#define BELL_TASK_NAME "hap_intercom_bell"

static TaskHandle_t bell_read_task_handle;
static TimerHandle_t bell_block_timer_handle; // ignore new bells until timer triggered
volatile bool is_bell_blocked;
static hap_char_t *bell_current_state;

void bell_rang()
{
    ESP_LOGI(TAG, "bell HAP RING");
    hap_val_t HAP_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS = {.u = 0};
    hap_char_update_val(bell_current_state, &HAP_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS);

    is_bell_blocked = true;
    ESP_LOGI(TAG, "bell timer set; bell updated [blocked: true]");
    xTimerReset(bell_block_timer_handle, pdFALSE);
}

bool is_bell_ringing()
{
    int val = adc1_get_raw(ADC1_GPIO33_CHANNEL); // sample the doorbell voltage
    bool is_ringing = 1935 < val && val < 1945;  // check doorbell voltage range
    ESP_LOGI(TAG, "bell rang [val: %d; is_ringing: %s]", val, is_ringing ? "true" : "false");
    return is_ringing;
}

void sample_bell_adc()
{
    for (int i = 0; i < 15; i++) // sample 15 times
    {
        vTaskDelay(20); // wait 20 ticks for signal to stabalize (at 80MHz clock, 12.5ns per tick)
        if (is_bell_ringing())
            return bell_rang();
    }
}

void bell_read_task(void *p)
{
    for (;;)
    {
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // block until doorbell hardware interrupt
        sample_bell_adc();
    }
}

void IRAM_ATTR bell_isr(void *arg)
{
    if (!is_bell_blocked)
    {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        configASSERT(bell_read_task_handle != NULL);
        vTaskNotifyGiveFromISR(bell_read_task_handle, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR();
    }
}

void bell_block_timer_cb(TimerHandle_t timer)
{
    is_bell_blocked = false;
    ESP_LOGI(TAG, "bell timer triggered; bell updated [blocked: false]");
}

void bell_blocker_init()
{
    is_bell_blocked = false;
    bell_block_timer_handle = xTimerCreate("intercom_bell_timer", pdMS_TO_TICKS(CONFIG_HOMEKIT_INTERCOM_LOCK_TIMEOUT),
                                           pdFALSE, 0, bell_block_timer_cb);
}

hap_serv_t *bell_init()
{
    hap_serv_t *bell_service = hap_serv_doorbell_create(0);
    hap_serv_add_char(bell_service, hap_char_name_create("Intercom Bell"));
    bell_current_state = hap_serv_get_char_by_uuid(bell_service, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT);
    return bell_service;
}

void bell_isr_gpio_init()
{
    // Configure ISR GPIO Pin 27
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    io_conf.pin_bit_mask = GPIO_SEL_27;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    gpio_config(&io_conf);

    gpio_install_isr_service(0);
    gpio_isr_handler_add(GPIO_NUM_27, bell_isr, (void *)0);
}

void bell_adc_gpio_init()
{
    // Configure ADC1 Channel 5, GPIO Pin 33
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.pin_bit_mask = GPIO_SEL_33;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    gpio_config(&io_conf);

    // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#_CPPv425adc1_config_channel_atten14adc1_channel_t11adc_atten_t
    adc1_config_width(ADC_WIDTH_BIT_12); /* The value read is 12 bits wide (range 0-4095). */
    adc1_config_channel_atten(ADC1_GPIO33_CHANNEL, ADC_ATTEN_DB_11);
}

hap_serv_t *bell_service_init()
{
    xTaskCreate(bell_read_task, BELL_TASK_NAME, BELL_TASK_STACKSIZE, NULL,
                BELL_TASK_PRIORITY, &bell_read_task_handle);

    bell_blocker_init();
    bell_isr_gpio_init();
    bell_adc_gpio_init();
    return bell_init();
}