#include #include #include #include "irda_nec.h" #include "irda_samsung.h" #include "irda_protocols.h" #include "irda-decoder/irda-decoder.h" typedef enum { EventTypeTick, EventTypeKey, EventTypeRX, } EventType; typedef struct { bool edge; uint32_t lasted; } RXValue; typedef struct { union { InputEvent input; RXValue rx; } value; EventType type; } AppEvent; typedef struct { IrDAProtocolType protocol; uint32_t address; uint32_t command; } IrDAPacket; #define IRDA_PACKET_COUNT 8 typedef struct { uint8_t mode_id; uint16_t carrier_freq; uint8_t carrier_duty_cycle_id; uint8_t packet_id; IrDAPacket packets[IRDA_PACKET_COUNT]; } State; typedef void (*ModeInput)(AppEvent*, State*); typedef void (*ModeRender)(Canvas*, State*); void input_carrier(AppEvent* event, State* state); void render_carrier(Canvas* canvas, State* state); void input_packet(AppEvent* event, State* state); void render_packet(Canvas* canvas, State* state); typedef struct { ModeRender render; ModeInput input; } Mode; const Mode modes[] = { {.render = render_carrier, .input = input_carrier}, {.render = render_packet, .input = input_packet}, }; const float duty_cycles[] = {0.1, 0.25, 0.333, 0.5, 1.0}; void render_carrier(Canvas* canvas, State* state) { canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 25, "carrier mode >"); canvas_draw_str(canvas, 2, 37, "? /\\ freq | \\/ duty cycle"); { char buf[24]; sprintf(buf, "frequency: %u Hz", state->carrier_freq); canvas_draw_str(canvas, 2, 50, buf); sprintf( buf, "duty cycle: %d/1000", (int)(duty_cycles[state->carrier_duty_cycle_id] * 1000)); canvas_draw_str(canvas, 2, 62, buf); } } void input_carrier(AppEvent* event, State* state) { if(event->value.input.key == InputKeyOk) { if(event->value.input.type == InputTypePress) { irda_pwm_set(duty_cycles[state->carrier_duty_cycle_id], state->carrier_freq); } else if(event->value.input.type == InputTypeRelease) { irda_pwm_stop(); } } if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { if(state->carrier_freq < 45000) { state->carrier_freq += 1000; } else { state->carrier_freq = 33000; } } if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]); if(state->carrier_duty_cycle_id < (duty_cycles_count - 1)) { state->carrier_duty_cycle_id++; } else { state->carrier_duty_cycle_id = 0; } } } void render_packet(Canvas* canvas, State* state) { canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 25, "< packet mode"); canvas_draw_str(canvas, 2, 37, "? /\\ \\/ packet"); { const char* protocol; switch(state->packets[state->packet_id].protocol) { case IRDA_NEC: protocol = "NEC"; break; case IRDA_SAMSUNG: protocol = "SAMS"; break; case IRDA_UNKNOWN: default: protocol = "UNK"; break; } char buf[24]; sprintf( buf, "P[%d]: %s 0x%lX 0x%lX", state->packet_id, protocol, state->packets[state->packet_id].address, state->packets[state->packet_id].command); canvas_draw_str(canvas, 2, 50, buf); } } void input_packet(AppEvent* event, State* state) { if(event->value.input.key == InputKeyOk) { if(event->value.input.type == InputTypeShort) { switch(state->packets[state->packet_id].protocol) { case IRDA_NEC: ir_nec_send( state->packets[state->packet_id].address, state->packets[state->packet_id].command); break; case IRDA_SAMSUNG: ir_samsung_send( state->packets[state->packet_id].address, state->packets[state->packet_id].command); break; default: break; } } } if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) { if(state->packet_id < (IRDA_PACKET_COUNT - 1)) { state->packet_id++; }; } if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) { if(state->packet_id > 0) { state->packet_id--; }; } } static void render_callback(Canvas* canvas, void* ctx) { State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); if(state != NULL) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 2, 12, "irda test"); modes[state->mode_id].render(canvas, state); release_mutex((ValueMutex*)ctx, state); } } static void input_callback(InputEvent* input_event, void* ctx) { osMessageQueueId_t event_queue = ctx; AppEvent event; event.type = EventTypeKey; event.value.input = *input_event; osMessageQueuePut(event_queue, &event, 0, 0); } void irda_timer_capture_callback(void* htim, void* comp_ctx) { TIM_HandleTypeDef* _htim = (TIM_HandleTypeDef*)htim; osMessageQueueId_t event_queue = (osMessageQueueId_t)comp_ctx; if(_htim->Instance == TIM2) { AppEvent event; event.type = EventTypeRX; uint32_t channel; if(_htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { // falling event event.value.rx.edge = false; channel = TIM_CHANNEL_1; } else if(_htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) { // rising event event.value.rx.edge = true; channel = TIM_CHANNEL_2; } else { // not our event return; } event.value.rx.lasted = HAL_TIM_ReadCapturedValue(_htim, channel); __HAL_TIM_SET_COUNTER(_htim, 0); osMessageQueuePut(event_queue, &event, 0, 0); } } void init_packet( State* state, uint8_t index, IrDAProtocolType protocol, uint32_t address, uint32_t command) { if(index >= IRDA_PACKET_COUNT) return; state->packets[index].protocol = protocol; state->packets[index].address = address; state->packets[index].command = command; } int32_t irda(void* p) { osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL); State _state; uint8_t mode_count = sizeof(modes) / sizeof(modes[0]); uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]); _state.carrier_duty_cycle_id = duty_cycles_count - 2; _state.carrier_freq = 36000; _state.mode_id = 0; _state.packet_id = 0; for(uint8_t i = 0; i < IRDA_PACKET_COUNT; i++) { init_packet(&_state, i, IRDA_UNKNOWN, 0, 0); } init_packet(&_state, 0, IRDA_NEC, 0xFF00, 0x11); init_packet(&_state, 1, IRDA_NEC, 0xF708, 0x59); init_packet(&_state, 2, IRDA_NEC, 0xFF00, 0x10); init_packet(&_state, 3, IRDA_NEC, 0xFF00, 0x15); init_packet(&_state, 4, IRDA_NEC, 0xFF00, 0x25); init_packet(&_state, 5, IRDA_SAMSUNG, 0xE0E, 0xF30C); init_packet(&_state, 6, IRDA_SAMSUNG, 0xE0E, 0xF40D); init_packet(&_state, 7, IRDA_SAMSUNG, 0xE0E, 0xF50E); ValueMutex state_mutex; if(!init_mutex(&state_mutex, &_state, sizeof(State))) { printf("cannot create mutex\r\n"); return 255; } ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, render_callback, &state_mutex); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port Gui* gui = furi_record_open("gui"); gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Red LED // TODO open record const GpioPin* red_led_record = &led_gpio[0]; const GpioPin* green_led_record = &led_gpio[1]; // configure pin gpio_init(red_led_record, GpioModeOutputOpenDrain); gpio_init(green_led_record, GpioModeOutputOpenDrain); // setup irda rx timer tim_irda_rx_init(); // add timer capture interrupt api_interrupt_add(irda_timer_capture_callback, InterruptTypeTimerCapture, event_queue); IrDADecoder* decoder = alloc_decoder(); AppEvent event; while(1) { osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 500); if(event_status == osOK) { if(event.type == EventTypeKey) { State* state = (State*)acquire_mutex_block(&state_mutex); // press events if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) { api_interrupt_remove(irda_timer_capture_callback, InterruptTypeTimerCapture); release_mutex(&state_mutex, state); // remove all view_ports create by app gui_remove_view_port(gui, view_port); view_port_free(view_port); // free decoder free_decoder(decoder); delete_mutex(&state_mutex); osMessageQueueDelete(event_queue); // exit return 0; } if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyLeft) { if(state->mode_id > 0) { state->mode_id--; } } if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyRight) { if(state->mode_id < (mode_count - 1)) { state->mode_id++; } } modes[state->mode_id].input(&event, state); release_mutex(&state_mutex, state); view_port_update(view_port); } else if(event.type == EventTypeRX) { IrDADecoderOutputData out; const uint8_t out_data_length = 4; uint8_t out_data[out_data_length]; out.data_length = out_data_length; out.data = out_data; gpio_write(red_led_record, event.value.rx.edge); bool decoded = process_decoder(decoder, event.value.rx.edge, &event.value.rx.lasted, 1, &out); if(decoded) { // save only if we in packet mode State* state = (State*)acquire_mutex_block(&state_mutex); if(state->mode_id == 1) { if(out.protocol == IRDA_NEC) { printf("P=NEC "); printf("A=0x%02X%02X ", out_data[1], out_data[0]); printf("C=0x%02X ", out_data[2]); if(out.flags & IRDA_REPEAT) { printf("R"); } printf("\r\n"); state->packets[state->packet_id].protocol = IRDA_NEC; state->packets[state->packet_id].address = out_data[1] << 8 | out_data[0]; state->packets[state->packet_id].command = out_data[2]; } else { printf("Unknown protocol\r\n"); } } release_mutex(&state_mutex, state); view_port_update(view_port); // blink anyway gpio_write(green_led_record, false); gpio_write(green_led_record, true); } } } else { // event timeout } } }