Skip to content

Commit 4b34b2c

Browse files
committed
feat(subghz): file loader
1 parent 39ed227 commit 4b34b2c

4 files changed

Lines changed: 181 additions & 23 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef SUBGHZ_FILE_LOADER_H
2+
#define SUBGHZ_FILE_LOADER_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
/**
9+
* @brief Percorre um diretório procurando arquivos .sub e tenta identificar o protocolo
10+
*
11+
* @param dir_path Caminho do diretório (ex: "/assets/tmp")
12+
*/
13+
void subghz_loader_process_directory(const char* dir_path);
14+
15+
#ifdef __cplusplus
16+
}
17+
#endif
18+
19+
#endif // SUBGHZ_FILE_LOADER_H

components/Applications/SubGhz/protocols/protocol_rcswitch.c

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,23 @@ static bool check_pulse(int32_t raw_len, uint16_t base_len, uint8_t multiplier)
3737
return (raw_len >= target - tol) && (raw_len <= target + tol);
3838
}
3939

40+
static bool check_polarity(int32_t raw_len, bool expect_high) {
41+
if (expect_high) return raw_len > 0;
42+
else return raw_len < 0;
43+
}
44+
4045
static bool protocol_rcswitch_decode(const int32_t* raw_data, size_t count, subghz_data_t* out_data) {
4146
if (count < 20) return false;
4247

4348
// Tenta cada protocolo
4449
for (int p = 0; p < RC_PROTO_COUNT; p++) {
4550
const rcswitch_proto_t* proto = &rc_protos[p];
4651

47-
// Pular protocolo 1 se já tivermos um decoder Princeton dedicado melhor
48-
// Mas o RCSwitch 1 é bom para pegar variantes que o nosso Princeton estrito rejeitou.
49-
52+
// Determina a polaridade esperada para o primeiro pulso do par (Pulse)
53+
// Normal (inverted=false): Pulse=High (>0), Gap=Low (<0)
54+
// Inverted (inverted=true): Pulse=Low (<0), Gap=High (>0)
55+
bool expect_pulse_high = !proto->inverted;
56+
5057
// RCSwitch geralmente procura Sync Bit primeiro.
5158
// Sync pode estar no começo ou no fim.
5259
// Vamos varrer o sinal procurando Sync.
@@ -56,15 +63,13 @@ static bool protocol_rcswitch_decode(const int32_t* raw_data, size_t count, subg
5663
int32_t p_sync = raw_data[i];
5764
int32_t g_sync = raw_data[i+1];
5865

59-
// Se invertido, o pulso é o GAP e o gap é o PULSO (logicamente),
60-
// mas o raw_data já vem ajustado pelo RMT invert_in?
61-
// O raw_data vem: P, G, P, G... onde P é High (ativo) e G é Low (pausa).
62-
// O parametro "inverted" do RCSwitch muda o significado de High/Low.
63-
// Se inverted=true, o sinal físico é invertido.
64-
// Nosso receiver JÁ inverteu via hardware (invert_in=true).
65-
// Então podemos tratar inverted=false como padrão?
66-
// Vamos testar sem inversão extra primeiro.
67-
66+
// 1. Verifica Polaridade do Sync
67+
if (!check_polarity(p_sync, expect_pulse_high) ||
68+
!check_polarity(g_sync, !expect_pulse_high)) {
69+
continue;
70+
}
71+
72+
// 2. Verifica Duração do Sync
6873
if (check_pulse(p_sync, proto->pulse_len, proto->sync.high) &&
6974
check_pulse(g_sync, proto->pulse_len, proto->sync.low)) {
7075

@@ -78,6 +83,13 @@ static bool protocol_rcswitch_decode(const int32_t* raw_data, size_t count, subg
7883
while (k < count - 1 && bits < 32) {
7984
int32_t p0 = raw_data[k];
8085
int32_t g0 = raw_data[k+1];
86+
87+
// Verifica Polaridade dos Dados
88+
if (!check_polarity(p0, expect_pulse_high) ||
89+
!check_polarity(g0, !expect_pulse_high)) {
90+
fail = true;
91+
break;
92+
}
8193

8294
// Test 0
8395
if (check_pulse(p0, proto->pulse_len, proto->zero.high) &&
@@ -99,15 +111,9 @@ static bool protocol_rcswitch_decode(const int32_t* raw_data, size_t count, subg
99111

100112
if (!fail && bits >= 8) {
101113
// Sucesso!
102-
// Formatar nome "RCSwitch vX"
103-
static char proto_name[20];
104-
snprintf(proto_name, sizeof(proto_name), "RCSwitch v%d", p+1);
105-
106-
out_data->protocol_name = strdup(proto_name); // Leak? Deveriamos usar const string
107-
// Como não podemos alocar, vamos usar uma lista estática de nomes ou hack.
108-
// Vamos usar "RCSwitch" genérico e colocar versão no serial ou log
109-
out_data->protocol_name = "RCSwitch"; // Simplificação
110-
// Podemos codificar o protocolo nos bits altos do raw se quiser
114+
static char proto_name[24];
115+
snprintf(proto_name, sizeof(proto_name), "RCSwitch(%d)", p + 1);
116+
out_data->protocol_name = proto_name;
111117

112118
out_data->bit_count = bits;
113119
out_data->raw_value = decoded_val;

components/Applications/SubGhz/protocols/subghz_protocol_registry.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ extern subghz_protocol_t protocol_rcswitch;
1515

1616
// Lista de protocolos ativos
1717
static subghz_protocol_t* registry[] = {
18-
&protocol_rossi, // Check first (distinctive header)
18+
&protocol_rcswitch, // Generic fallback
19+
// &protocol_rossi, // Check first (distinctive header)
1920
&protocol_princeton,
2021
&protocol_came,
2122
&protocol_nice_flo,
@@ -24,7 +25,6 @@ static subghz_protocol_t* registry[] = {
2425
&protocol_holtek,
2526
&protocol_linear,
2627
&protocol_liftmaster,
27-
&protocol_rcswitch, // Generic fallback
2828
NULL
2929
};
3030

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "subghz_file_loader.h"
2+
#include "subghz_protocol_registry.h"
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <dirent.h>
7+
#include <sys/stat.h>
8+
#include "esp_log.h"
9+
#include "storage_assets.h"
10+
11+
static const char *TAG = "SUBGHZ_LOADER";
12+
13+
#define MAX_LINE_LEN 2048
14+
#define INITIAL_BUF_SIZE 2048 // Start with 2048 samples
15+
16+
static void process_file(const char *filepath) {
17+
ESP_LOGI(TAG, "Processing file: %s", filepath);
18+
19+
FILE *f = fopen(filepath, "r");
20+
if (!f) {
21+
ESP_LOGE(TAG, "Failed to open file: %s", filepath);
22+
return;
23+
}
24+
25+
int32_t *raw_data = malloc(INITIAL_BUF_SIZE * sizeof(int32_t));
26+
if (!raw_data) {
27+
ESP_LOGE(TAG, "OOM: Failed to allocate initial buffer");
28+
fclose(f);
29+
return;
30+
}
31+
32+
size_t capacity = INITIAL_BUF_SIZE;
33+
size_t count = 0;
34+
char *line = malloc(MAX_LINE_LEN);
35+
if (!line) {
36+
ESP_LOGE(TAG, "OOM: Failed to allocate line buffer");
37+
free(raw_data);
38+
fclose(f);
39+
return;
40+
}
41+
42+
while (fgets(line, MAX_LINE_LEN, f)) {
43+
if (strncmp(line, "RAW_Data:", 9) == 0) {
44+
char *ptr = line + 9;
45+
char *endptr;
46+
47+
while (*ptr) {
48+
// Skip whitespace
49+
while (*ptr == ' ' || *ptr == '\t') ptr++;
50+
if (*ptr == '\0' || *ptr == '\r' || *ptr == '\n') break;
51+
52+
long val = strtol(ptr, &endptr, 10);
53+
if (ptr == endptr) break; // Conversion failed or no number
54+
ptr = endptr;
55+
56+
// Add to buffer
57+
if (count >= capacity) {
58+
size_t new_cap = capacity * 2;
59+
int32_t *new_buf = realloc(raw_data, new_cap * sizeof(int32_t));
60+
if (!new_buf) {
61+
ESP_LOGE(TAG, "OOM: Failed to grow buffer to %d", new_cap);
62+
break;
63+
}
64+
raw_data = new_buf;
65+
capacity = new_cap;
66+
}
67+
raw_data[count++] = (int32_t)val;
68+
}
69+
}
70+
}
71+
72+
free(line);
73+
fclose(f);
74+
75+
if (count > 0) {
76+
ESP_LOGI(TAG, "Loaded %d samples from %s. Decoding...", count, filepath);
77+
subghz_data_t result = {0};
78+
79+
// Ensure result is clean
80+
memset(&result, 0, sizeof(result));
81+
82+
if (subghz_process_raw(raw_data, count, &result)) {
83+
ESP_LOGI(TAG, ">>> SUCCESS [%s] Protocol: %s | Serial: 0x%lX | Btn: 0x%X | Bits: %d",
84+
filepath, result.protocol_name, result.serial, result.btn, result.bit_count);
85+
} else {
86+
ESP_LOGW(TAG, ">>> FAILED [%s] No protocol identified", filepath);
87+
}
88+
} else {
89+
ESP_LOGW(TAG, "No RAW_Data found in file: %s", filepath);
90+
}
91+
92+
free(raw_data);
93+
}
94+
95+
void subghz_loader_process_directory(const char* dir_path) {
96+
if (!storage_assets_is_mounted()) {
97+
ESP_LOGE(TAG, "Assets partition not mounted! Call storage_assets_init() first.");
98+
return;
99+
}
100+
101+
ESP_LOGI(TAG, "Scanning directory: %s", dir_path);
102+
103+
DIR *dir = opendir(dir_path);
104+
if (!dir) {
105+
ESP_LOGE(TAG, "Failed to open directory: %s", dir_path);
106+
return;
107+
}
108+
109+
struct dirent *entry;
110+
int files_processed = 0;
111+
112+
while ((entry = readdir(dir)) != NULL) {
113+
// Skip hidden files or current/parent
114+
if (entry->d_name[0] == '.') continue;
115+
116+
char fullpath[256];
117+
int len = snprintf(fullpath, sizeof(fullpath), "%s/%s", dir_path, entry->d_name);
118+
if (len >= sizeof(fullpath)) continue;
119+
120+
struct stat st;
121+
if (stat(fullpath, &st) == 0 && S_ISREG(st.st_mode)) {
122+
// Check extension .sub
123+
char *ext = strrchr(entry->d_name, '.');
124+
if (ext && strcmp(ext, ".sub") == 0) {
125+
process_file(fullpath);
126+
files_processed++;
127+
}
128+
}
129+
}
130+
closedir(dir);
131+
132+
ESP_LOGI(TAG, "Done. Processed %d files.", files_processed);
133+
}

0 commit comments

Comments
 (0)