diff --git a/Distr/nrf24batch/CO2_mini.txt b/Distr/nrf24batch/CO2_mini.txt
index fa37a37b702..70e9bbca4cf 100644
--- a/Distr/nrf24batch/CO2_mini.txt
+++ b/Distr/nrf24batch/CO2_mini.txt
@@ -20,13 +20,13 @@ R: OSCCAL=0x51,RAM
R: OSCCAL_EMEM=0
R: CO2=0x67,RAM,0xC2
-R: RxAddr=1
+R: RxAddr=1#
W: RxAddr=,1
R: Ch=2
W: Ch=,2
-R: nRF RETR=3
+R: nRF RETR=3#
W: nRF RETR=,3
R: Send period=4
@@ -38,7 +38,7 @@ W: CO2 threshold=,5,0x82
R: CO2 correct*2=7,,0xC2
W: CO2 correct=,7,0x82
-R: FanLSB[10]=i:9
+R: FanLSB[10]=i:9#
W: FanLSB=,i:9
W: Reset=,RESET,0xC1
diff --git a/Pics/descript.png b/Pics/descript.png
index 640fbd7cd5e..418bc50f545 100644
Binary files a/Pics/descript.png and b/Pics/descript.png differ
diff --git a/README.md b/README.md
index 79e78b3499e..8b17ef8d0ff 100644
--- a/README.md
+++ b/README.md
@@ -5,20 +5,21 @@ Flipper Zero application for nRF24L01 external board. Sends batch commands. (IN
Можно использовать для настройки или чтения данных с удаленного устройства. На удаленной стороне для команды чтения требуется поддержка.
Сначала выбирается файл настройки с описанием команд.
Затем стрелками влево или вправо выбирается нужный режим - Пакетное чтение (Read Batch), Чтение по одной команде (Read cmd), Пакетная запись (Write Batch).
-
+
Есть два вида команд: запрос-ответ и запись.
Запрос-ответ - отправка пакета, переключение на прием и отображение на экране, что получили.
Запись - фактически отправка пакетов подряд с нужными данными.
-
-Формат пакета для отправки (payload) задается в виде размера полей структуры в байтах, например, так "Payload struct: 2,1,1",
-что означает структуру из 3 полей: 2 байта, 1 байт, 1 байт.
+
+Формат пакета для отправки (payload) задается в виде размера полей структуры в байтах, например, так "Payload struct: 2,1,1", что означает структуру из 3 полей: 2 байта, 1 байт, 1 байт.
Полученный в ответ пакет состоит из одного значения, размерность по умолчанию 1 байт (int8), при необходимости, задается числом после '*' после имени команды.
Перед отправкой пакета, он заполняется сначала шаблону по умолчанию "R default" для запроса чтения, "W default" - для записи.
Можно использовать константы по их имени, они задаются в формате "имя=число", число либо десятичное или шестнадцатеричное с префиксом 0x.
-Затем берутся заполненные значения из самой команды ("R:" или "W:").
-Пакет состоит из списка имен команд, перечисленных через ";".
-Перед пакетом команд для записи отправляется пакет 'Write start', если эта строка присутствует в файле.
-Значение команды для записи можно редактировать - Ok на списке команд, стрелка +/- и переход по цифрам, завершить - Назад, вставка цифры - Ok, удаление цифру - длительный Ok.
+Затем берутся заполненные значения полей из самой команды ("R:" или "W:").
+Если в конце строки с командой чтения символ '#', считанное значение будет показано в шестнадцатеричном виде.
+Пакет состоит из списка имен команд, перечисленных через ";".
+Отправка пакета для записи - длительно нажать Ok в списке и подтвердить.
+Перед пакетом команд для записи отправляется пакет 'Write start', если эта строка присутствует в файле настроек.
+Значение команды для записи можно редактировать - Ok на списке команд, стрелки - +/- и переход по цифрам, завершить - Назад, вставка цифры - Ok, удаление цифры - длительный Ok.
Пример файл [CO2_mini](https://raw.githubusercontent.com/vad7/nRF24-Batch/main/Distr/nrf24batch/CO2_mini.txt)
Для устройства на Attiny44A, которое отправляет данные с датчика CO2 на контроллеры, управляющие вентиляцией или проветриватели: https://github.com/vad7/CO2-mini
diff --git a/descript.txt b/descript.txt
index bddb8fd810e..fa2ca605512 100644
--- a/descript.txt
+++ b/descript.txt
@@ -16,7 +16,7 @@ Write start: 0,0,0x8F <- if exist in the file - packet before write a batc
R: ID*=,ID <- Read cmd, '*' - means string like device ID, result in bytes = { 0, 0, 3, 0xC1 }
R: OSCCAL=0x51,RAM <- Read cmd, result in bytes = { 0x51, 0, 1, 0xC1 }
-R: OSCCAL_EMEM=0 <- Read cmd, result in bytes = { 0, 0, 0, 0xC1 }
+R: OSCCAL_EMEM=0# <- Read cmd, result in bytes = { 0, 0, 0, 0xC1 }, in the end of line - '#', returned value in hexadecimal format
R: CO2=0x67,RAM,0xC2 <- Read cmd, result in bytes = { 0x67, 0, 1, 0xC2 }
R: CO2 threshold*2=5,,0xC2 <- Read cmd, '*2' - means received field with 2 bytes size (int16), result in bytes = { 5, 0, 0, 0xC2 }
diff --git a/nrf24batch.c b/nrf24batch.c
index 32a92693f0a..705fa98234f 100644
--- a/nrf24batch.c
+++ b/nrf24batch.c
@@ -14,7 +14,7 @@
#include
#define TAG "nrf24batch"
-#define VERSION "1.1"
+#define VERSION "1.2"
#define SCAN_APP_PATH_FOLDER "/ext/nrf24batch"
#define LOG_FILENAME "log"
@@ -64,6 +64,7 @@ uint8_t send_status = sst_none;// sst_*
bool cmd_array = false;
uint8_t cmd_array_idx;
uint8_t cmd_array_cnt = 0;
+bool cmd_array_hex;
uint8_t save_settings = 0;
uint16_t view_cmd[3] = {0, 0, 0}; // ReadBatch, Read, WriteBatch
uint8_t view_x = 0;
@@ -257,16 +258,13 @@ static bool select_settings_file() {
FuriString* path;
path = furi_string_alloc();
furi_string_set(path, SCAN_APP_PATH_FOLDER);
-
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL);
browser_options.hide_ext = false;
-
bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options);
-
furi_record_close("dialogs");
if(ret) {
- if(!file_stream_open(file_stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+ if(!file_stream_open(file_stream, furi_string_get_cstr(path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path));
file_stream_close(file_stream);
} else {
@@ -333,10 +331,13 @@ uint8_t nrf24_send_packet()
uint8_t nrf24_resend_read_packet()
{
- if(Log_Total) {
+ if(Log_Total && !cmd_array) {
FuriString *str = Log[Log_Total - 1];
char *p = strstr(furi_string_get_cstr(str), ": ");
- if(p) furi_string_left(str, p - furi_string_get_cstr(str) + 2);
+ if(p) {
+ if(strncmp(p + 2, "0x", 2) == 0) p += 2;
+ furi_string_left(str, p - furi_string_get_cstr(str) + 2);
+ }
}
return nrf24_send_packet();
}
@@ -369,23 +370,23 @@ bool nrf24_read_newpacket() {
else if(size == 3) var = (*(uint32_t*)payload_receive) & 0xFFFFFF;
else var = *(int32_t*)payload_receive;
//FURI_LOG_D(TAG, "VAR(%d): %ld", size, var);
- if(size == 0) furi_string_cat_printf(str, "%c", (char)var);
+ if(size == 0) furi_string_cat_printf(str, "%c", (char)var);
else {
- if(var >= 0 && var <= 9) furi_string_cat_printf(str, "%ld", var);
+ char hex[9];
+ snprintf(hex, sizeof(hex), "%lX", var);
+ if((cmd_array && cmd_array_hex) || furi_string_end_with_str(str, "0x")) furi_string_cat_str(str, hex);
else {
- char hex[9];
- snprintf(hex, sizeof(hex), "%lX", var);
- furi_string_cat_printf(str, "%ld (%s)", var, hex + (var < 0 ? 8 - size * 2 : 0));
+ if(var >= 0 && var <= 9) furi_string_cat_printf(str, "%ld", var);
+ else furi_string_cat_printf(str, "%ld (%s)", var, hex + (var < 0 ? 8 - size * 2 : 0));
}
}
if(cmd_array) {
if(--cmd_array_cnt) {
furi_string_cat_str(str, ",");
- payload[cmd_array_idx]++;
+ if(cmd_array_hex) furi_string_cat_str(str, "0x");
+ payload[cmd_array_idx]++; // next array element
NRF_repeat = 0;
- send_status = sst_sending;
- furi_delay_ms(delay_between_pkt);
- nrf24_send_packet();
+ send_status = sst_sending; // Will be send after delay_between_pkt
} else send_status = sst_ok;
} else {
if(size == 0) { // string, until '\0'
@@ -448,6 +449,8 @@ bool fill_payload(char *p, uint8_t *idx_i, int32_t var_n)
}
} else if(end == p) {
idx += payload_struct[fld];
+ } else if(*p == '#') { // value in Hexadecimal, end string
+ break;
} else {
ERR = 2;
strcpy(ERR_STR, "char: ");
@@ -486,6 +489,8 @@ bool Run_Read_cmd(FuriString *cmd)
FuriString *fs = furi_string_alloc();
furi_string_set_strn(fs, (char*)furi_string_get_cstr(cmd), (*(p-2)=='*' ? p-2 : p) - (char*)furi_string_get_cstr(cmd)); // skip *n
furi_string_cat_str(fs, ": ");
+ bool hexval;
+ if((hexval = *(p + strlen(p) - 1) == '#')) furi_string_cat_str(fs, "0x"); // value in Hex format
Log[Log_Total++] = fs;
p++;
memset(payload, 0, sizeof(payload));
@@ -497,7 +502,10 @@ bool Run_Read_cmd(FuriString *cmd)
p = strchr(furi_string_get_cstr(cmd), '[');
if(p) {
cmd_array_cnt = str_to_int(p + 1);
- if(cmd_array_cnt > 1) cmd_array = true; // array
+ if(cmd_array_cnt > 1) {
+ cmd_array_hex = hexval;
+ cmd_array = true; // array
+ }
}
}
prepare_nrf24();
@@ -778,7 +786,51 @@ static uint8_t load_settings_file() {
static void save_batch(void)
{
-// to do...
+ FURI_LOG_D(TAG, "Save Batch");
+ char *p, *p2;
+ stream_seek(file_stream, 0, StreamOffsetFromEnd);
+ FuriHalRtcDateTime dt;
+ furi_hal_rtc_get_datetime(&dt);
+ stream_write_format(file_stream, "\n%s ", SettingsFld_WriteBatch);
+ p = (char*)furi_string_get_cstr(ReadBatch_cmd[view_cmd[rwt_read_batch]]);
+ p2 = strchr(p, ':');
+ if(p2 == NULL) p2 = p + strlen(p);
+ stream_write(file_stream, (uint8_t*)p, p2 - p);
+ stream_write_format(file_stream, " %02d.%02d.%02d %02d.%02d: ", dt.day, dt.month, dt.year % 100, dt.hour, dt.minute);
+ for(uint16_t i = 0; i < Log_Total; i++) {
+ p = (char*) furi_string_get_cstr(Log[i]);
+ p2 = strchr(p, ':');
+ if(p2 && *(p2-1) != '*') { // skip string
+ if(*(p2-1) == ']') { // array
+ char *p3 = strchr(p, '[');
+ if(p3 == NULL) p3 = p2;
+ stream_write(file_stream, (uint8_t*)p, p3 - p);
+ stream_write_cstring(file_stream, "={");
+ p = (p2 += 2);
+ do {
+ while(is_digit(p2, true) || *p2 == 'x') p2++;
+ stream_write(file_stream, (uint8_t*)p, p2 - p);
+ char c = *p2;
+ if(c == '\0') break;
+ if(c != ',') {
+ p2 = strchr(p2, ',');
+ if(p2 == NULL) break;
+ }
+ stream_write_char(file_stream, ',');
+ p = ++p2;
+ } while(1);
+ stream_write_char(file_stream, '}');
+ } else {
+ stream_write(file_stream, (uint8_t*)p, p2 - p - (*(p2-2) == '*' ? 2 : 0));
+ stream_write_char(file_stream, '=');
+ p2 += 2;
+ p = strchr(p2, ' ');
+ if(p == NULL) p = p2 + strlen(p2);
+ stream_write(file_stream, (uint8_t*)p2, p - p2);
+ }
+ if(i < Log_Total - 1) stream_write_char(file_stream, ';');
+ }
+ }
}
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -787,6 +839,7 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
+#define FONT_5x7_SCREEN_WIDTH 25
void render_display_list(Canvas* const canvas, FuriString ***fsa, char delim, uint16_t view_pos, uint16_t max_i)
{
uint16_t page = view_pos & ~7;
@@ -799,8 +852,9 @@ void render_display_list(Canvas* const canvas, FuriString ***fsa, char delim, ui
if(end) {
if(*(end - 1) == '*') end--; // skip *
else if(*(end - 2) == '*') end -= 2; // skip *?
- len = MIN((end - p), 30);
- strncpy(screen_buf, p, len);
+ len = MIN(end - p, view_x);
+ len = MIN(end - p - len, FONT_5x7_SCREEN_WIDTH);
+ strncpy(screen_buf, p + view_x, len);
screen_buf[len] = '\0';
canvas_draw_str(canvas, 5, y, screen_buf);
}
@@ -811,7 +865,6 @@ void render_display_list(Canvas* const canvas, FuriString ***fsa, char delim, ui
}
}
-#define FONT_5x7_SCREEN_WIDTH 25
static void render_callback(Canvas* const canvas, void* ctx) {
const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
if(plugin_state == NULL) return;
@@ -875,7 +928,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
strcpy(screen_buf, rw_type == rwt_read_batch ? "Read Batch: " : what_doing == 1 ? "Write Batch: " : "Write: ");
if(rw_type == rwt_read_batch || send_status != sst_none) {
if(NRF_ERROR) strcat(screen_buf, "nRF24 ERROR!");
- else if(ERR) snprintf(screen_buf + strlen(screen_buf), 16, "Error %d", ERR);
+ else if(ERR) snprintf(screen_buf + strlen(screen_buf), FONT_5x7_SCREEN_WIDTH - 7, "Error %d", ERR);
else if(send_status == sst_error) strcat(screen_buf, "NO ACK!");
else if(send_status == sst_timeout) strcat(screen_buf, "TIMEOUT!");
else if(send_status == sst_ok && ((rw_type == rwt_read_batch && (uint32_t)ReadBatch_cmd_curr == 0xFFFFFFFF)
@@ -886,10 +939,19 @@ static void render_callback(Canvas* const canvas, void* ctx) {
char *p = (char*)furi_string_get_cstr(WriteBatch_cmd[view_cmd[rwt_write_batch]]);
char *end = strchr(p, ':');
if(end) {
- uint8_t len = MIN(end - p, 25);
- end = screen_buf + strlen(screen_buf);
- memcpy(end, p, len);
- end[len] = '\0';
+ uint8_t len = end - p;
+ uint8_t lenb = strlen(screen_buf);
+ end = screen_buf + lenb;
+ lenb = FONT_5x7_SCREEN_WIDTH - lenb;
+ if(len > lenb) {
+ if(view_x < len) {
+ strncpy(end, p + view_x, len = MIN(lenb, len - view_x));
+ end[len] = '\0';
+ }
+ } else {
+ strncpy(end, p, len);
+ end[len] = '\0';
+ }
}
}
}
@@ -906,7 +968,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_str(canvas, 0, y, ">");
canvas_draw_str(canvas, -1, y, ">");
if(Edit) {
- int n = Edit_pos - p - vx - (FONT_5x7_SCREEN_WIDTH - 3);
+ int n = Edit_pos - p - vx - (FONT_5x7_SCREEN_WIDTH - 4);
if(n > 0) vx += n; // fix out of screen
int x = 6 + (Edit_pos - p - vx) * 5;
canvas_draw_line(canvas, x - 1, y, x - 1, y - 1);
@@ -932,7 +994,7 @@ void work_timer_callback(void* ctx)
if(rw_type == rwt_write_batch) {
if(send_status == sst_ok) {
if(ERR == 0 && WriteBatch_cmd_curr < Log_Total && furi_get_tick() - NRF_time >= delay_between_pkt) {
- if(++WriteBatch_cmd_curr < Log_Total) Run_WriteBatch_cmd();
+ if(++WriteBatch_cmd_curr < Log_Total) Run_WriteBatch_cmd(); else Edited = false;
}
} else if(send_status == sst_sending) {
if(NRF_repeat++ < NRF_resend) nrf24_send_packet();
@@ -941,12 +1003,15 @@ void work_timer_callback(void* ctx)
send_status = sst_error; // error NO_ACK
}
}
+ // ReadBatch or ReadCmd
} else if(send_status == sst_sending) { // sending
- if(!NRF_last_packet_send_st) { // No ACK on last attempt
+// if(!NRF_last_packet_send_st) { // No ACK on last attempt
if(furi_get_tick() - NRF_time > delay_between_pkt) {
- if(NRF_repeat++ < NRF_resend) nrf24_resend_read_packet(); else send_status = sst_error; // error NO_ACK
+ if(NRF_repeat++ < NRF_resend) {
+ if(cmd_array) nrf24_send_packet(); else nrf24_resend_read_packet();
+ } else send_status = sst_error; // error NO_ACK
}
- }
+// }
} else if(send_status == sst_receiving) { // receiving
for(uint8_t i = 0; i < 3; i++) {
bool new = nrf24_read_newpacket();
@@ -1057,16 +1122,14 @@ int32_t nrf24batch_app(void* p) {
ask_question_answer ^= 1;
} else if(what_doing == 0) {
} else if(what_doing == 1) {
- if(--rw_type > rwt_write_batch) rw_type = rwt_write_batch;
+ if(event.input.type == InputTypeShort) {
+ if(--rw_type > rwt_write_batch) rw_type = rwt_write_batch;
+ } else if(view_x) view_x--;
} else if(what_doing == 2) {
if(Edit) {
if(is_digit(Edit_pos - 1, Edit_hex)) Edit_pos--;
- else if(*(Edit_pos - 1) == ',' && is_digit(Edit_pos - 2, true)) {
- Edit_pos -= 2;
- // char *p = Edit_pos;
- // while(is_digit(p, true)) p--;
- // Edit_hex = *p == 'x';
- }
+ else if(*(Edit_pos - 1) == 'x' && *(Edit_pos - 3) == ',') Edit_pos -= 4;
+ else if(*(Edit_pos - 1) == ',') Edit_pos -= 2;
} else if(view_x) view_x--;
}
}
@@ -1078,12 +1141,15 @@ int32_t nrf24batch_app(void* p) {
} else if(what_doing == 0) {
what_doing = 1;
} else if(what_doing == 1) {
- if(++rw_type > rwt_write_batch) rw_type = rwt_read_batch;
+ if(event.input.type == InputTypeShort) {
+ if(++rw_type > rwt_write_batch) rw_type = rwt_read_batch;
+ } else view_x++;
} else if(what_doing == 2) {
if(Edit) {
if(is_digit(Edit_pos + 1, Edit_hex)) Edit_pos++;
- else if(*(Edit_pos + 1) == ',' && is_digit(Edit_pos + 2, Edit_hex)) {
- if((/*Edit_hex =*/ *(Edit_pos + 3) == 'x')) Edit_pos += 3; else Edit_pos += 2;
+ else if(*(Edit_pos + 1) == ',') {
+ Edit_pos += 2;
+ if(*(Edit_pos + 1) == 'x') Edit_pos += 2;
}
} else view_x++;
}
@@ -1106,6 +1172,7 @@ int32_t nrf24batch_app(void* p) {
if(what_doing == 2) {
ERR = 0;
send_status = sst_none;
+ Edited = false;
what_doing--;
}
}
@@ -1122,6 +1189,7 @@ int32_t nrf24batch_app(void* p) {
if(ReadBatch_cmd_Total) {
ERR = 0;
Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]);
+ view_x = 0;
view_Batch = 0;
what_doing = 2;
}
@@ -1130,13 +1198,15 @@ int32_t nrf24batch_app(void* p) {
ERR = 0;
free_Log();
Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]);
+ view_x = 0;
what_doing = 2;
}
} else if(rw_type == rwt_write_batch) {
if(WriteBatch_cmd_Total) {
Prepare_Write_cmd(WriteBatch_cmd[view_cmd[rwt_write_batch]]);
- Edited = false;
send_status = sst_none;
+ Edited = false;
+ view_x = 0;
view_Batch = 0;
what_doing = 2;
}
@@ -1195,11 +1265,10 @@ int32_t nrf24batch_app(void* p) {
break;
case InputKeyBack:
if(event.input.type == InputTypeLong) {
- if(what_doing <= 1) processing = false;
- else if(what_doing == 2 && (Edited || rw_type == rwt_read_batch)) {
+ if(what_doing == 2 && (Edited || rw_type == rwt_read_batch)) {
if(!ask_question) ask_question_answer = 1;
ask_question = ask_exit;
- }
+ } else processing = false;
} else if(event.input.type == InputTypeShort) {
if(Edit) Edit = 0;
else if(ask_question) ask_question = 0;