Skip to content

Commit 0a312ff

Browse files
authored
Merge pull request #32 from leedave/feature/gpio_ir
Feature/gpio ir
2 parents 69194d1 + f59526e commit 0a312ff

11 files changed

+126
-50
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Wouldn't it be nicer to simply click one button and let everything happen? This
3636
- Save settings, stores a file with your settings in it on exit
3737
- IR time ms, the default duration of an IR signal transmission. Individual times can be set
3838
- SubG. time ms, the default duration of a SubGhz signal. Only needed for Encoded signals, RAW files play until finished
39+
- Loop transmip, repeats the command chain until cancelled
40+
- External IR & 5V on GPIO, settings for external IR boards
3941

4042
### Limitations
4143
SubGhz commands will stop working if you move/rename/delete the original files on your Flipper. This is because of how the Flippers SubGhz worker expects data.

application.fam

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ App(
66
stack_size=3 * 1024,
77
fap_icon="icons/xremote_10px.png",
88
fap_icon_assets="icons",
9-
fap_version="3.1",
9+
fap_version="3.2",
1010
fap_category="Infrared",
1111
fap_author="Leedave",
1212
fap_description="One-Click, sends multiple commands",

docs/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ This app combines your IR and SubGhz commands into a playlist that can be run wi
44

55
## Features
66
- Read out commands you recorded in the IR app
7+
- Supports external GPIO Boards like IR Blaster or Masta-Blasta
78
- Read out commands you saved as .sub files
89
- Combine commands to a chain/playlist
910
- Add pauses inbetween commands

docs/changelog.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 3.2
2+
- Added support for external IR GPIO boards, tested on IR Blaster & Masta-Blasta.
3+
14
## 3.1
25
- Bugfix to enable save on first use (thanks to WillyJL)
36
- Bugfix for loop transmit when using RAW SubGHz transmissions

helpers/xremote_custom_event.h

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ typedef enum {
5252
XRemoteCustomEventPauseSetOk,
5353

5454
XRemoteCustomEventViewTransmitterSendStop,
55+
56+
XRemoteCustomEventTypeIrGpioPinChanged,
57+
XRemoteCustomEventTypeIrGpioOtgChanged,
5558
} XRemoteCustomEvent;
5659

5760
static inline uint32_t xremote_custom_menu_event_pack(uint16_t type, int16_t value) {

helpers/xremote_storage.c

+8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ void xremote_save_settings(void* context) {
6161
flipper_format_write_uint32(fff_file, XREMOTE_SETTINGS_KEY_SG_TIMING, &app->sg_timing, 1);
6262
flipper_format_write_uint32(fff_file, XREMOTE_SETTINGS_KEY_LOOP_TRANSMIT, &app->loop_transmit, 1);
6363

64+
//IR GPIO Settings
65+
flipper_format_write_uint32(fff_file, XREMOTE_SETTINGS_KEY_IR_TX_PIN, &app->ir_tx_pin, 1);
66+
flipper_format_write_bool(fff_file, XREMOTE_SETTINGS_KEY_IR_USE_OTP, &app->ir_is_otg_enabled, 1);
67+
6468
if(!flipper_format_rewind(fff_file)) {
6569
xremote_close_config_file(fff_file);
6670
FURI_LOG_E(TAG, "Rewind error");
@@ -115,6 +119,10 @@ void xremote_read_settings(void* context) {
115119
flipper_format_read_uint32(fff_file, XREMOTE_SETTINGS_KEY_SG_TIMING, &app->sg_timing, 1);
116120
flipper_format_read_uint32(fff_file, XREMOTE_SETTINGS_KEY_LOOP_TRANSMIT, &app->loop_transmit, 1);
117121

122+
// IR GPIO Settings
123+
flipper_format_read_uint32(fff_file, XREMOTE_SETTINGS_KEY_IR_TX_PIN, &app->ir_tx_pin, 1);
124+
flipper_format_read_bool(fff_file, XREMOTE_SETTINGS_KEY_IR_USE_OTP, &app->ir_is_otg_enabled, 1);
125+
118126
flipper_format_rewind(fff_file);
119127

120128
furi_string_free(temp_str);

helpers/xremote_storage.h

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#define XREMOTE_SETTINGS_KEY_SPEAKER "Speaker"
1313
#define XREMOTE_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings"
1414
#define XREMOTE_SETTINGS_KEY_IR_TIMING "IRTiming"
15+
#define XREMOTE_SETTINGS_KEY_IR_TX_PIN "IRTXPin"
16+
#define XREMOTE_SETTINGS_KEY_IR_USE_OTP "IRUSEOTP"
1517
#define XREMOTE_SETTINGS_KEY_SG_TIMING "SGTiming"
1618
#define XREMOTE_SETTINGS_KEY_LOOP_TRANSMIT "LoopTransmit"
1719

scenes/xremote_scene_settings.c

+68-45
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,16 @@
11
#include "../xremote.h"
22
#include <lib/toolbox/value_index.h>
3-
/*
4-
enum SettingsIndex {
5-
SettingsIndexHaptic = 10,
6-
SettingsIndexValue1,
7-
SettingsIndexValue2,
8-
};*/
9-
10-
/*const char* const haptic_text[2] = {
11-
"OFF",
12-
"ON",
3+
4+
static const char* infrared_pin_text[] = {
5+
"Flipper",
6+
"2 (A7)",
7+
"Detect",
138
};
14-
const uint32_t haptic_value[2] = {
15-
XRemoteHapticOff,
16-
XRemoteHapticOn,
17-
};*/
189

19-
/*const char* const speaker_text[2] = {
10+
static const char* infrared_otg_text[] = {
2011
"OFF",
2112
"ON",
2213
};
23-
const uint32_t speaker_value[2] = {
24-
XRemoteSpeakerOff,
25-
XRemoteSpeakerOn,
26-
};*/
2714

2815
const char* const led_text[2] = {
2916
"OFF",
@@ -33,6 +20,7 @@ const uint32_t led_value[2] = {
3320
XRemoteLedOff,
3421
XRemoteLedOn,
3522
};
23+
3624
const char* const loop_text[2] = {
3725
"OFF",
3826
"ON",
@@ -41,6 +29,7 @@ const uint32_t loop_value[2] = {
4129
XRemoteLoopOff,
4230
XRemoteLoopOn,
4331
};
32+
4433
const char* const settings_text[2] = {
4534
"OFF",
4635
"ON",
@@ -50,20 +39,27 @@ const uint32_t settings_value[2] = {
5039
XRemoteSettingsOn,
5140
};
5241

53-
/*static void xremote_scene_settings_set_haptic(VariableItem* item) {
42+
static void xremote_scene_settings_set_ir_pin(VariableItem* item) {
5443
XRemote* app = variable_item_get_context(item);
5544
uint8_t index = variable_item_get_current_value_index(item);
5645

57-
variable_item_set_current_value_text(item, haptic_text[index]);
58-
app->haptic = haptic_value[index];
59-
}*/
46+
variable_item_set_current_value_text(item, infrared_pin_text[index]);
47+
app->ir_tx_pin = index;
48+
view_dispatcher_send_custom_event(
49+
app->view_dispatcher,
50+
xremote_custom_menu_event_pack(XRemoteCustomEventTypeIrGpioPinChanged, index));
51+
}
6052

61-
/*static void xremote_scene_settings_set_speaker(VariableItem* item) {
53+
static void xremote_scene_settings_set_ir_is_otg_enabled(VariableItem* item) {
6254
XRemote* app = variable_item_get_context(item);
6355
uint8_t index = variable_item_get_current_value_index(item);
64-
variable_item_set_current_value_text(item, speaker_text[index]);
65-
app->speaker = speaker_value[index];
66-
}*/
56+
57+
variable_item_set_current_value_text(item, infrared_otg_text[index]);
58+
app->ir_is_otg_enabled = index;
59+
view_dispatcher_send_custom_event(
60+
app->view_dispatcher,
61+
xremote_custom_menu_event_pack(XRemoteCustomEventTypeIrGpioOtgChanged, index));
62+
}
6763

6864
static void xremote_scene_settings_set_led(VariableItem* item) {
6965
XRemote* app = variable_item_get_context(item);
@@ -109,25 +105,11 @@ void xremote_scene_settings_submenu_callback(void* context, uint32_t index) {
109105
view_dispatcher_send_custom_event(app->view_dispatcher, index);
110106
}
111107

112-
void xremote_scene_settings_on_enter(void* context) {
113-
XRemote* app = context;
108+
void xremote_scene_settings_init(void* context) {
109+
XRemote* app = context;
114110
VariableItem* item;
115111
uint8_t value_index;
116112

117-
// Vibro on/off
118-
/* item = variable_item_list_add(
119-
app->variable_item_list, "Vibro/Haptic:", 2, xremote_scene_settings_set_haptic, app);
120-
value_index = value_index_uint32(app->haptic, haptic_value, 2);
121-
variable_item_set_current_value_index(item, value_index);
122-
variable_item_set_current_value_text(item, haptic_text[value_index]);*/
123-
124-
// Sound on/off
125-
/* item = variable_item_list_add(
126-
app->variable_item_list, "Sound:", 2, xremote_scene_settings_set_speaker, app);
127-
value_index = value_index_uint32(app->speaker, speaker_value, 2);
128-
variable_item_set_current_value_index(item, value_index);
129-
variable_item_set_current_value_text(item, speaker_text[value_index]);*/
130-
131113
// LED Effects on/off
132114
item = variable_item_list_add(
133115
app->variable_item_list, "LED FX", 2, xremote_scene_settings_set_led, app);
@@ -149,6 +131,35 @@ void xremote_scene_settings_on_enter(void* context) {
149131
variable_item_set_current_value_index(item, value_index);
150132
variable_item_set_current_value_text(item, settings_text[value_index]);
151133

134+
// Infrared GPIO Board
135+
item = variable_item_list_add(
136+
app->variable_item_list,
137+
"External IR",
138+
COUNT_OF(infrared_pin_text),
139+
xremote_scene_settings_set_ir_pin,
140+
app);
141+
value_index = app->ir_tx_pin;
142+
variable_item_set_current_value_index(item, value_index);
143+
variable_item_set_current_value_text(item, infrared_pin_text[value_index]);
144+
145+
// Infrared GPIO 5V
146+
item = variable_item_list_add(
147+
app->variable_item_list,
148+
"5V on IR GPIO",
149+
COUNT_OF(infrared_otg_text),
150+
xremote_scene_settings_set_ir_is_otg_enabled,
151+
app);
152+
153+
if(app->ir_tx_pin < FuriHalInfraredTxPinMax) {
154+
value_index = app->ir_is_otg_enabled;
155+
variable_item_set_current_value_index(item, value_index);
156+
variable_item_set_current_value_text(item, infrared_otg_text[value_index]);
157+
} else {
158+
variable_item_set_values_count(item, 1);
159+
variable_item_set_current_value_index(item, 0);
160+
variable_item_set_current_value_text(item, "Auto");
161+
}
162+
152163
// Set Infrared Timer
153164
item = variable_item_list_add(
154165
app->variable_item_list, "IR Time ms", 30, xremote_scene_settings_set_ir_timing, app);
@@ -163,16 +174,28 @@ void xremote_scene_settings_on_enter(void* context) {
163174

164175
variable_item_set_current_value_index(item, (uint8_t)(app->sg_timing / 100));
165176
snprintf(app->sg_timing_char, 20, "%lu", app->sg_timing);
166-
variable_item_set_current_value_text(item, app->sg_timing_char);
177+
variable_item_set_current_value_text(item, app->sg_timing_char);
178+
}
167179

180+
void xremote_scene_settings_on_enter(void* context) {
181+
XRemote* app = context;
182+
xremote_scene_settings_init(app);
168183
view_dispatcher_switch_to_view(app->view_dispatcher, XRemoteViewIdSettings);
169184
}
170185

171186
bool xremote_scene_settings_on_event(void* context, SceneManagerEvent event) {
172187
XRemote* app = context;
173-
UNUSED(app);
174188
bool consumed = false;
175189
if(event.type == SceneManagerEventTypeCustom) {
190+
const uint16_t custom_event_type = xremote_custom_menu_event_get_type(event.event);
191+
192+
if (custom_event_type == XRemoteCustomEventTypeIrGpioPinChanged) {
193+
variable_item_list_reset(app->variable_item_list);
194+
xremote_scene_settings_init(app);
195+
xremote_ir_set_tx_pin(app);
196+
} else if(custom_event_type == XRemoteCustomEventTypeIrGpioOtgChanged) {
197+
xremote_ir_enable_otg(app, app->ir_is_otg_enabled);
198+
}
176199
}
177200
return consumed;
178201
}

xremote.c

+32-2
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,47 @@ void xremote_text_input_callback(void* context) {
191191
view_dispatcher_send_custom_event(app->view_dispatcher, XRemoteCustomEventTextInput);
192192
}
193193

194+
void xremote_ir_enable_otg(XRemote* app, bool enable) {
195+
if(enable) {
196+
furi_hal_power_enable_otg();
197+
} else {
198+
furi_hal_power_disable_otg();
199+
}
200+
app->ir_is_otg_enabled = enable;
201+
}
202+
203+
void xremote_ir_set_tx_pin(XRemote* app) {
204+
if(app->ir_tx_pin < FuriHalInfraredTxPinMax) {
205+
furi_hal_infrared_set_tx_output(app->ir_tx_pin);
206+
} else {
207+
FuriHalInfraredTxPin tx_pin_detected = furi_hal_infrared_detect_tx_output();
208+
furi_hal_infrared_set_tx_output(tx_pin_detected);
209+
if(tx_pin_detected != FuriHalInfraredTxPinInternal) {
210+
xremote_ir_enable_otg(app, true);
211+
}
212+
}
213+
}
214+
215+
static void xremote_ir_load_settings(XRemote* app) {
216+
xremote_ir_set_tx_pin(app);
217+
if(app->ir_tx_pin < FuriHalInfraredTxPinMax) {
218+
xremote_ir_enable_otg(app, app->ir_is_otg_enabled);
219+
}
220+
}
221+
194222
int32_t xremote_app(void* p) {
195223
UNUSED(p);
196224
XRemote* app = xremote_app_alloc();
197-
225+
198226
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
199227

200228
//scene_manager_next_scene(app->scene_manager, XRemoteSceneInfoscreen); //Start with start screen
201229
scene_manager_next_scene(
202230
app->scene_manager, XRemoteSceneMenu); //if you want to directly start with Menu
203231

204232
furi_hal_power_suppress_charge_enter();
233+
xremote_ir_load_settings(app);
234+
205235

206236
Storage* storage = furi_record_open(RECORD_STORAGE);
207237
storage_common_mkdir(storage, XREMOTE_APP_FOLDER);
@@ -210,8 +240,8 @@ int32_t xremote_app(void* p) {
210240
view_dispatcher_run(app->view_dispatcher);
211241

212242
xremote_save_settings(app);
213-
214243
furi_hal_power_suppress_charge_exit();
244+
xremote_ir_enable_otg(app, false);
215245
xremote_app_free(app);
216246

217247
return 0;

xremote.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ typedef struct {
3636
XRemotePauseSet* xremote_pause_set;
3737
InfraredRemote* ir_remote_buffer;
3838
InfraredWorker* ir_worker;
39+
bool ir_is_otg_enabled; /**< Whether OTG power (external 5V) is enabled for IR. */
40+
uint32_t ir_tx_pin;
3941
SubGhzRemote* sg_remote_buffer;
4042
CrossRemote* cross_remote;
4143
uint32_t haptic;
@@ -98,4 +100,6 @@ typedef enum {
98100
} XRemoteSettingsStoreState;
99101

100102
void xremote_popup_closed_callback(void* context);
101-
void xremote_text_input_callback(void* context);
103+
void xremote_text_input_callback(void* context);
104+
void xremote_ir_enable_otg(XRemote* app, bool enable);
105+
void xremote_ir_set_tx_pin(XRemote* app);

xremote_i.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
#define XREMOTE_TEXT_STORE_SIZE 128
5252
#define XREMOTE_MAX_ITEM_NAME_LENGTH 22
5353
#define XREMOTE_MAX_REMOTE_NAME_LENGTH 22
54-
#define XREMOTE_VERSION "3.1"
54+
#define XREMOTE_VERSION "3.2"
5555

5656
#define INFRARED_APP_EXTENSION ".ir"
5757
#define INFRARED_APP_FOLDER EXT_PATH("infrared")

0 commit comments

Comments
 (0)