esp-ir icon indicating copy to clipboard operation
esp-ir copied to clipboard

homekit and ir combination

Open andrei-gavrila opened this issue 3 years ago • 2 comments

Hi,

I'm having an intermittent issue with the following code. At random times, the IR sending part is no longer generating the proper signal to send the remote codes - sometimes are not recognised at all by the TV, sometimes are sent with minor variations and it is received as a different button press. I have tried to reinitialise the TX part by also changing the ir_tx_init function to delete the event group if defined, but I had absolutely no luck. Sometimes, the IR sending part comes back to proper behaviour, sometimes I loose my patience and I reset the ESP. Also, as you can see, I'm using ir_generic_send() and ir_raw_send(). Once sending is starting to randomly fail, the same applies to codes sent by the both functions. Can the timer be affected by the homekit part? As you can see from the code, the channel (input source) name is editable from the iPhone, so if you define an input source '51 Viasat History HD', the ESP will send the codes for 5, 1, OK to the TV when this input source is selected. Once the failure starts, I get random combinations of just 5 send, just 1 sent, just 5 and 1 sent, random button press sent - I even managed to turn off the TV several times :)

Here's the complete code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <esp/uart.h>
#include <esp/gpio.h>
#include <espressif/esp_common.h>
#include <sysparam.h>
#include <FreeRTOS.h>
#include <task.h>
#include <homekit/characteristics.h>
#include <homekit/homekit.h>
#include <wifi_config.h>
#include <ir/ir.h>
#include <ir/generic.h>
#include <ir/raw.h>

homekit_server_config_t config;

#define CHANNELS 10

uint8_t ch_data[10][4] = 
{
    {0x20, 0x08, 0x47, 0xB8},
    {0x20, 0x08, 0x1C, 0xE3},
    {0x20, 0x08, 0x1D, 0xE2},
    {0x20, 0x08, 0x1E, 0xE1},
    {0x20, 0x08, 0x40, 0xBF},
    {0x20, 0x08, 0x41, 0xBE},
    {0x20, 0x08, 0x42, 0xBD},
    {0x20, 0x08, 0x44, 0xBB},
    {0x20, 0x08, 0x45, 0xBA},
    {0x20, 0x08, 0x46, 0xB9},
};

ir_generic_config_t protocol = {
    .header_mark = 9000,
    .header_space = -4500,

    .bit1_mark = 560,
    .bit1_space = -1690,

    .bit0_mark = 560,
    .bit0_space = -560,

    .footer_mark = 560,
    .footer_space = -9000,

    .tolerance = 20,
};

void ir_send(uint8_t *data, uint8_t size)
{
    ir_tx_init();

    ir_generic_send(&protocol, data, size);
}

struct
{
    uint8_t active;
    uint32_t active_identifier;
    char configured_name[1024];
    uint8_t sleep_discovery_mode;
} device_state;

void device_identify(homekit_value_t value)
{
    printf("device_identify: %d\n", value.bool_value);
}

homekit_value_t device_active_get()
{
    printf("device_active_get: %d\n", device_state.active);

    return HOMEKIT_UINT8(device_state.active);
}

void device_active_set(homekit_value_t value)
{
    if (device_state.active != value.uint8_value)
    {
        int16_t data[] = {8934, -4534, 525, -630, 503, -630, 503, -1713, 527, -601, 532, -630, 503, -630, 503, -630, 502, -631, 503, -1714, 525, -1738, 502, -630, 503, -1716, 524, -1738, 501, -1714, 526, -1716, 524, -1737, 502, -631, 502, -631, 502, -631, 502, -1715, 525, -631, 502, -631, 502, -602, 531, -631, 502, -1738, 502, -1716, 523, -1716, 525, -630, 502, -1714, 526, -1709, 531, -1715, 524, -1710, 530};

        ir_tx_init();

        ir_raw_send(data, sizeof(data)/sizeof(*data));
    }

    device_state.active = value.uint8_value;

    printf("device_active_set: %d\n", device_state.active);
}

homekit_value_t device_active_identifier_get()
{
    printf("device_active_identifier_get: %u\n", device_state.active_identifier);

    return HOMEKIT_UINT32(device_state.active_identifier);
}

void device_active_identifier_set(homekit_value_t value)
{
    if (device_state.active_identifier != value.uint32_value)
    {
        char *ch_name_value = NULL;

        homekit_service_t **srv = config.accessories[0]->services + 3;

        while (*srv)
        {
            if (value.uint32_value == (*srv)->characteristics[1]->value.uint32_value)
            {
                ch_name_value = (*srv)->characteristics[2]->value.string_value;

                break;
            }

            srv++;
        }

        if (ch_name_value)
        {
            printf("device_active_identifier_set: channel name value: %s\n", ch_name_value);

            bool sent = false;

            for (uint8_t i = 0; i < strlen(ch_name_value); i++)
            {
                if (ch_name_value[i] >= '0' && ch_name_value[i] <= '9')
                {
                    sent = true;

                    ir_send(ch_data[ch_name_value[i] - '0'], 4);

                    vTaskDelay(500 / portTICK_PERIOD_MS);

                    printf("device_active_identifier_set: sending code for %d\n", (int16_t)(ch_name_value[i] - '0'));
                }
                else
                {
                    break;
                }
            }

            if (sent)
            {
                uint8_t data[] = {0x20, 0x08, 0x15, 0xEA};

                ir_send(data, 4);
            }
        } else {
            printf("device_active_identifier_set: channel not found: %u\n", value.uint32_value);
        }
    }

    device_state.active_identifier = value.uint32_value;

    printf("device_active_identifier_set: %u\n", device_state.active_identifier);
}

homekit_value_t device_configured_name_get()
{
    printf("device_configured_name_get: %s\n", device_state.configured_name);

    return HOMEKIT_STRING(strdup(device_state.configured_name), .is_static = 0);
}

void device_configured_name_set(homekit_value_t value)
{
    strncpy(device_state.configured_name, value.string_value, 1024);

    sysparam_set_string("device_state_configured_name", device_state.configured_name);

    printf("device_configured_name_set: %s\n", device_state.configured_name);
}

homekit_value_t device_sleep_discovery_mode_get()
{
    printf("device_sleep_discovery_mode_get: %d\n", device_state.sleep_discovery_mode);

    return HOMEKIT_UINT8(device_state.sleep_discovery_mode);
}

void device_sleep_discovery_mode_set(homekit_value_t value)
{
    device_state.sleep_discovery_mode = value.uint8_value;

    printf("device_sleep_discovery_mode_set: %d\n", device_state.sleep_discovery_mode);
}

void device_remote_key_set(homekit_value_t value)
{
    switch (value.uint8_value)
    {
        case HOMEKIT_REMOTE_KEY_ARROW_UP:
        {
            uint8_t data[] = {0x20, 0x08, 0x12, 0xED};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_REMOTE_KEY_ARROW_DOWN:
        {
            uint8_t data[] = {0x20, 0x08, 0x13, 0xEC};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_REMOTE_KEY_ARROW_LEFT:
        {
            uint8_t data[] = {0x20, 0x08, 0x14, 0xEB};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_REMOTE_KEY_ARROW_RIGHT:
        {
            uint8_t data[] = {0x20, 0x08, 0x16, 0xE9};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_REMOTE_KEY_SELECT:
        {
            uint8_t data[] = {0x20, 0x08, 0x15, 0xEA};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_REMOTE_KEY_BACK:
        {
            uint8_t data[] = {0x20, 0x08, 0x0C, 0xF3};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_REMOTE_KEY_INFORMATION:
        {
            uint8_t data[] = {0x20, 0x08, 0x0D, 0xF2};

            ir_send(data, 4);
        }
        break;

        default:
        break;
    }

    printf("device_remote_key_set: %d\n", value.uint8_value);
}

void speaker_mute_set(homekit_value_t value)
{
    printf("speaker_mute_set: %d\n", value.bool_value);
}

void speaker_volume_selector_set(homekit_value_t value)
{
    switch (value.uint8_value)
    {
        case HOMEKIT_VOLUME_SELECTOR_INCREMENT:
        {
            uint8_t data[] = {0x20, 0x08, 0x02, 0xFD};

            ir_send(data, 4);
        }
        break;

        case HOMEKIT_VOLUME_SELECTOR_DECREMENT:
        {
            uint8_t data[] = {0x20, 0x08, 0x03, 0xFC};
            ir_send(data, 4);
        }
        break;

        default:
        break;
    }

    printf("speaker_volume_selector_set: %d\n", value.uint8_value);
}

void channel_on_configured_name(homekit_characteristic_t *ch, homekit_value_t value, void *arg)
{
    const char *ch_name = NULL;

    homekit_service_t **srv = config.accessories[0]->services + 3;

    while (*srv)
    {
        if (ch == (*srv)->characteristics[2])
        {
            ch_name = (*srv)->characteristics[0]->value.string_value;

            break;
        }

        srv++;
    }

    if (ch_name)
    {
        sysparam_set_string(ch_name, value.string_value);

        printf("channel_on_configured_name: set name %s for channel %s\n", value.string_value, ch_name);
    }
}

homekit_server_config_t config = {
    .accessories = (homekit_accessory_t *[]){
        HOMEKIT_ACCESSORY(
            .id = 1,
            .category = homekit_accessory_category_television,
            .services = (homekit_service_t*[1 + 1 + 1 + CHANNELS + 1])
            {
                HOMEKIT_SERVICE(
                    ACCESSORY_INFORMATION,
                    .characteristics = (homekit_characteristic_t*[])
                    {
                        HOMEKIT_CHARACTERISTIC(NAME, "IrTvRemote"),
                        HOMEKIT_CHARACTERISTIC(MANUFACTURER, "Andrei Gavrila"),
                        HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "000000000000"),
                        HOMEKIT_CHARACTERISTIC(MODEL, "Infrared Television Remote"),
                        HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.1"),
                        HOMEKIT_CHARACTERISTIC(IDENTIFY, device_identify),
                        NULL
                    }
                ),
                HOMEKIT_SERVICE(
                    TELEVISION,
                    .primary = true,
                    .characteristics = (homekit_characteristic_t*[])
                    {
                        HOMEKIT_CHARACTERISTIC(ACTIVE, 0, .getter = device_active_get, .setter = device_active_set),
                        HOMEKIT_CHARACTERISTIC(ACTIVE_IDENTIFIER, 1, .getter = device_active_identifier_get, .setter = device_active_identifier_set),
                        HOMEKIT_CHARACTERISTIC(CONFIGURED_NAME, "Television", .getter = device_configured_name_get, .setter = device_configured_name_set),
                        HOMEKIT_CHARACTERISTIC(SLEEP_DISCOVERY_MODE, HOMEKIT_SLEEP_DISCOVERY_MODE_ALWAYS_DISCOVERABLE, .getter = device_sleep_discovery_mode_get, .setter = device_sleep_discovery_mode_set),
                        HOMEKIT_CHARACTERISTIC(REMOTE_KEY, .setter = device_remote_key_set),
                        NULL
                    },
                    .linked = (homekit_service_t*[CHANNELS + 1]){}
                ),
                HOMEKIT_SERVICE(
                    TELEVISION_SPEAKER,
                    .characteristics = (homekit_characteristic_t*[])
                    {
                        HOMEKIT_CHARACTERISTIC(MUTE, 0, .setter = speaker_mute_set),
                        HOMEKIT_CHARACTERISTIC(ACTIVE, 1),
                        HOMEKIT_CHARACTERISTIC(VOLUME_CONTROL_TYPE, HOMEKIT_VOLUME_CONTROL_TYPE_RELATIVE),
                        HOMEKIT_CHARACTERISTIC(VOLUME_SELECTOR, .setter = speaker_volume_selector_set),
                        NULL
                    }
                )
            }
        ),
        NULL
    },
    .password = "111-11-111"
};

void on_wifi_ready()
{
    homekit_server_init(&config);
}

void user_init(void) {
    char *s = NULL;

    uart_set_baud(0, 115200);

    gpio_set_pullup(14, false, false);

    ir_tx_init();

    for (uint8_t i = 0; i < CHANNELS; i++)
    {
        int ch_name_len = snprintf(NULL, 0, "ch_name_%d", i);
        char *ch_name = (char *)malloc(sizeof(char)*(ch_name_len + 1));
        snprintf(ch_name, ch_name_len + 1, "ch_name_%d", i);

        int ch_name_value_len = snprintf(NULL, 0, "Channel %d", i);
        char *ch_name_value = (char *)malloc(sizeof(char)*(ch_name_value_len + 1));
        snprintf(ch_name_value, ch_name_value_len + 1, "Channel %d", i);

        homekit_service_t *srv = NEW_HOMEKIT_SERVICE(
            INPUT_SOURCE,
            .characteristics = (homekit_characteristic_t*[]) {
                NEW_HOMEKIT_CHARACTERISTIC(NAME, ch_name),
                NEW_HOMEKIT_CHARACTERISTIC(IDENTIFIER,  i + 1),
                NEW_HOMEKIT_CHARACTERISTIC(
                    CONFIGURED_NAME,
                    ch_name_value,
                    .callback = HOMEKIT_CHARACTERISTIC_CALLBACK(channel_on_configured_name)
                ),
                NEW_HOMEKIT_CHARACTERISTIC(INPUT_SOURCE_TYPE, HOMEKIT_INPUT_SOURCE_TYPE_TUNER),
                NEW_HOMEKIT_CHARACTERISTIC(IS_CONFIGURED, 1),
                NEW_HOMEKIT_CHARACTERISTIC(CURRENT_VISIBILITY_STATE, HOMEKIT_CURRENT_VISIBILITY_STATE_SHOWN),
                NULL
            }
        );

        config.accessories[0]->services[1]->linked[i] = srv;
        config.accessories[0]->services[1 + 1 + 1 + i] = srv;
    }

    config.accessories[0]->services[1]->linked[CHANNELS] = NULL;
    config.accessories[0]->services[1 + 1 + 1 + CHANNELS] = NULL;

    device_state.active = 0;
    device_state.active_identifier = 1;
    if (sysparam_get_string("device_state_configured_name", &s) == SYSPARAM_OK)
    {
        strncpy(device_state.configured_name, s, 1024);

        free(s);
    }
    else
    {
        strncpy(device_state.configured_name, "Television", 1024);
    }
    device_state.sleep_discovery_mode = HOMEKIT_SLEEP_DISCOVERY_MODE_ALWAYS_DISCOVERABLE;

    homekit_service_t **srv = config.accessories[0]->services + 3;

    while (*srv)
    {
        if (sysparam_get_string((*srv)->characteristics[0]->value.string_value, &s) == SYSPARAM_OK)
        {
            homekit_value_destruct(&((*srv)->characteristics[2]->value));
            (*srv)->characteristics[2]->value = HOMEKIT_STRING(s);

            printf("user_init: set name %s for channel %s\n", s, (*srv)->characteristics[0]->value.string_value);
        }

        srv++;
    }

    wifi_config_init("Infrared Television Remote", NULL, on_wifi_ready);
}

andrei-gavrila avatar Sep 02 '20 10:09 andrei-gavrila