Skip to content

Commit 3d35df2

Browse files
committed
basic IR support
1 parent d43f3e1 commit 3d35df2

File tree

1 file changed

+152
-15
lines changed

1 file changed

+152
-15
lines changed

actions/action_ir.c

+152-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
// Methods for IR transmission
22

3+
// infrared
34
#include <infrared.h>
45
#include <infrared/encoder_decoder/infrared.h>
5-
#include <applications/services/cli/cli.h>
6+
#include <infrared/worker/infrared_transmit.h>
7+
#include <infrared/worker/infrared_worker.h>
8+
9+
#include <flipper_format/flipper_format.h>
610

711
#include "action_i.h"
812
#include "quac.h"
913

14+
#define INFRARED_FILE_TYPE "IR signals file"
15+
#define INFRARED_FILE_VERSION 1
16+
1017
typedef struct {
1118
size_t timings_size; /**< Number of elements in the timings array. */
1219
uint32_t* timings; /**< Pointer to an array of timings describing the signal. */
@@ -17,7 +24,7 @@ typedef struct {
1724
typedef struct InfraredSignal {
1825
bool is_raw;
1926
union {
20-
InfraredMessage message;
27+
InfraredMessage message; // protocol, address, command, repeat
2128
InfraredRawSignal raw;
2229
} payload;
2330
} InfraredSignal;
@@ -29,19 +36,149 @@ InfraredSignal* infrared_signal_alloc() {
2936
return signal;
3037
}
3138

39+
void infrared_signal_free(InfraredSignal* signal) {
40+
if(signal->is_raw) {
41+
free(signal->payload.raw.timings);
42+
signal->payload.raw.timings = NULL;
43+
}
44+
free(signal);
45+
}
46+
3247
void action_ir_tx(void* context, const FuriString* action_path, FuriString* error) {
33-
UNUSED(action_path);
3448
UNUSED(error);
35-
UNUSED(context);
36-
// App* app = context;
37-
38-
// InfraredSignal* signal = infrared_signal_alloc();
39-
// const char* ir_file = furi_string_get_cstr(action_path);
40-
// bool success = infrared_parse_message(ir_file, signal) || infrared_parse_raw(ir_file, signal);
41-
// if(success) {
42-
// infrared_signal_transmit(signal);
43-
// } else {
44-
// ACTION_SET_ERROR("IR: Error sending signal");
45-
// }
46-
// infrared_signal_free(signal);
49+
App* app = context;
50+
const char* file_name = furi_string_get_cstr(action_path);
51+
InfraredSignal* signal = infrared_signal_alloc();
52+
53+
FlipperFormat* fff_data_file = flipper_format_file_alloc(app->storage);
54+
FuriString* temp_str;
55+
temp_str = furi_string_alloc();
56+
uint32_t temp_data32;
57+
58+
// https://developer.flipper.net/flipperzero/doxygen/infrared_file_format.html
59+
// TODO: Right now we only read the first signal found in the file. Add support
60+
// for reading any signal by 'name'
61+
do {
62+
if(!flipper_format_file_open_existing(fff_data_file, file_name)) {
63+
ACTION_SET_ERROR("IR: Error opening %s", file_name);
64+
break;
65+
}
66+
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
67+
ACTION_SET_ERROR("IR: Missing or incorrect header");
68+
break;
69+
}
70+
if(!furi_string_cmp_str(temp_str, INFRARED_FILE_TYPE) &&
71+
temp_data32 == INFRARED_FILE_VERSION) {
72+
} else {
73+
ACTION_SET_ERROR("IR: File type or version mismatch");
74+
break;
75+
}
76+
if(!flipper_format_read_string(fff_data_file, "name", temp_str)) {
77+
ACTION_SET_ERROR("IR: Invalid or missing name");
78+
break;
79+
}
80+
// FURI_LOG_I(TAG, "Reading signal %s", furi_string_get_cstr(temp_str));
81+
if(!flipper_format_read_string(fff_data_file, "type", temp_str)) {
82+
ACTION_SET_ERROR("IR: Type missing");
83+
break;
84+
}
85+
if(!furi_string_cmp_str(temp_str, "parsed")) {
86+
// FURI_LOG_I(TAG, "IR File is PARSED");
87+
signal->is_raw = false;
88+
89+
if(!flipper_format_read_string(fff_data_file, "protocol", temp_str)) {
90+
ACTION_SET_ERROR("IR: Invalid or missing protocol");
91+
break;
92+
}
93+
signal->payload.message.protocol =
94+
infrared_get_protocol_by_name(furi_string_get_cstr(temp_str));
95+
if(!infrared_is_protocol_valid(signal->payload.message.protocol)) {
96+
ACTION_SET_ERROR("IR: Invalid or unknown protocol");
97+
break;
98+
}
99+
100+
// Why do these methods exist, when the spec says the address and command
101+
// lengths MUST be 4 bytes?
102+
103+
// uint8_t address_len;
104+
// address_len = infrared_get_protocol_address_length(signal->payload.message.protocol);
105+
106+
// uint8_t command_len;
107+
// command_len = infrared_get_protocol_command_length(signal->payload.message.protocol);
108+
109+
if(!flipper_format_read_hex(
110+
fff_data_file, "address", (uint8_t*)&signal->payload.message.address, 4)) {
111+
ACTION_SET_ERROR("IR: Failed to read address");
112+
break;
113+
}
114+
if(!flipper_format_read_hex(
115+
fff_data_file, "command", (uint8_t*)&signal->payload.message.command, 4)) {
116+
ACTION_SET_ERROR("IR: Failed to read command");
117+
break;
118+
}
119+
120+
// FURI_LOG_I(
121+
// TAG,
122+
// "IR: Sending parsed => %s %lu %lu",
123+
// infrared_get_protocol_name(signal->payload.message.protocol),
124+
// signal->payload.message.address,
125+
// signal->payload.message.command);
126+
127+
infrared_send(&signal->payload.message, 1);
128+
129+
} else if(!furi_string_cmp_str(temp_str, "raw")) {
130+
// FURI_LOG_I(TAG, "IR File is RAW");
131+
signal->is_raw = true;
132+
133+
if(!flipper_format_read_uint32(
134+
fff_data_file, "frequency", &signal->payload.raw.frequency, 1)) {
135+
ACTION_SET_ERROR("IR: Failed to read frequency");
136+
break;
137+
}
138+
if(!flipper_format_read_float(
139+
fff_data_file, "duty_cycle", &signal->payload.raw.duty_cycle, 1)) {
140+
ACTION_SET_ERROR("IR: Failed to read duty cycle");
141+
break;
142+
}
143+
if(!flipper_format_get_value_count(fff_data_file, "data", &temp_data32)) {
144+
ACTION_SET_ERROR("IR: Failed to get size of data");
145+
break;
146+
}
147+
if(temp_data32 > MAX_TIMINGS_AMOUNT) {
148+
ACTION_SET_ERROR("IR: Data size exceeds limit");
149+
break;
150+
}
151+
signal->payload.raw.timings_size = temp_data32;
152+
153+
signal->payload.raw.timings =
154+
malloc(sizeof(uint32_t) * signal->payload.raw.timings_size);
155+
if(!flipper_format_read_uint32(
156+
fff_data_file, "data", signal->payload.raw.timings, temp_data32)) {
157+
ACTION_SET_ERROR("IR: Failed to read data");
158+
break;
159+
}
160+
161+
// FURI_LOG_I(
162+
// TAG,
163+
// "IR: Sending raw => %d timings, %lu Hz, %f",
164+
// signal->payload.raw.timings_size,
165+
// signal->payload.raw.frequency,
166+
// (double)signal->payload.raw.duty_cycle);
167+
168+
infrared_send_raw_ext(
169+
signal->payload.raw.timings,
170+
signal->payload.raw.timings_size,
171+
true,
172+
signal->payload.raw.frequency,
173+
signal->payload.raw.duty_cycle);
174+
} else {
175+
ACTION_SET_ERROR("IR: Unknown type: %s", furi_string_get_cstr(temp_str));
176+
break;
177+
}
178+
179+
} while(false);
180+
181+
furi_string_free(temp_str);
182+
flipper_format_free(fff_data_file);
183+
infrared_signal_free(signal);
47184
}

0 commit comments

Comments
 (0)