Просмотр исходного кода

Ability to save in file read data (ReadBatch) as WriteBatch

vad7 2 лет назад
Родитель
Сommit
47926cdfd6
5 измененных файлов с 127 добавлено и 57 удалено
  1. 3 3
      Distr/nrf24batch/CO2_mini.txt
  2. BIN
      Pics/descript.png
  3. 9 8
      README.md
  4. 1 1
      descript.txt
  5. 114 45
      nrf24batch.c

+ 3 - 3
Distr/nrf24batch/CO2_mini.txt

@@ -20,13 +20,13 @@ R: OSCCAL=0x51,RAM
 R: OSCCAL_EMEM=0
 R: OSCCAL_EMEM=0
 R: CO2=0x67,RAM,0xC2
 R: CO2=0x67,RAM,0xC2
 
 
-R: RxAddr=1
+R: RxAddr=1#
 W: RxAddr=,1
 W: RxAddr=,1
 
 
 R: Ch=2
 R: Ch=2
 W: Ch=,2
 W: Ch=,2
 
 
-R: nRF RETR=3
+R: nRF RETR=3#
 W: nRF RETR=,3
 W: nRF RETR=,3
 
 
 R: Send period=4
 R: Send period=4
@@ -38,7 +38,7 @@ W: CO2 threshold=,5,0x82
 R: CO2 correct*2=7,,0xC2
 R: CO2 correct*2=7,,0xC2
 W: CO2 correct=,7,0x82
 W: CO2 correct=,7,0x82
 
 
-R: FanLSB[10]=i:9
+R: FanLSB[10]=i:9#
 W: FanLSB=,i:9
 W: FanLSB=,i:9
 
 
 W: Reset=,RESET,0xC1
 W: Reset=,RESET,0xC1

BIN
Pics/descript.png


+ 9 - 8
README.md

@@ -5,20 +5,21 @@ Flipper Zero application for nRF24L01 external board. Sends batch commands. (IN
 Можно использовать для настройки или чтения данных с удаленного устройства. На удаленной стороне для команды чтения требуется поддержка.<br>
 Можно использовать для настройки или чтения данных с удаленного устройства. На удаленной стороне для команды чтения требуется поддержка.<br>
 Сначала выбирается файл настройки с описанием команд.  
 Сначала выбирается файл настройки с описанием команд.  
 Затем стрелками влево или вправо выбирается нужный режим - Пакетное чтение (Read Batch), Чтение по одной команде (Read cmd), Пакетная запись (Write Batch).  
 Затем стрелками влево или вправо выбирается нужный режим - Пакетное чтение (Read Batch), Чтение по одной команде (Read cmd), Пакетная запись (Write Batch).  
-<br>
+<br><br>
 Есть два вида команд: запрос-ответ и запись.<br>
 Есть два вида команд: запрос-ответ и запись.<br>
 Запрос-ответ - отправка пакета, переключение на прием и отображение на экране, что получили.<br>
 Запрос-ответ - отправка пакета, переключение на прием и отображение на экране, что получили.<br>
 Запись - фактически отправка пакетов подряд с нужными данными.<br>
 Запись - фактически отправка пакетов подряд с нужными данными.<br>
-<br>
-Формат пакета для отправки (payload) задается в виде размера полей структуры в байтах, например, так "Payload struct: 2,1,1",<br>
-что означает структуру из 3 полей: 2 байта, 1 байт, 1 байт.<br>
+<br><br>
+Формат пакета для отправки (payload) задается в виде размера полей структуры в байтах, например, так "Payload struct: 2,1,1", что означает структуру из 3 полей: 2 байта, 1 байт, 1 байт.<br>
 Полученный в ответ пакет состоит из одного значения, размерность по умолчанию 1 байт (int8), при необходимости, задается числом после '*' после имени команды.<br><br>
 Полученный в ответ пакет состоит из одного значения, размерность по умолчанию 1 байт (int8), при необходимости, задается числом после '*' после имени команды.<br><br>
 Перед отправкой пакета, он заполняется сначала шаблону по умолчанию "R default" для запроса чтения, "W default" - для записи.<br>
 Перед отправкой пакета, он заполняется сначала шаблону по умолчанию "R default" для запроса чтения, "W default" - для записи.<br>
 Можно использовать константы по их имени, они задаются в формате "имя=число", число либо десятичное или шестнадцатеричное с префиксом 0x.<br>
 Можно использовать константы по их имени, они задаются в формате "имя=число", число либо десятичное или шестнадцатеричное с префиксом 0x.<br>
-Затем берутся заполненные значения из самой команды ("R:" или "W:").<br>
-Пакет состоит из списка имен команд, перечисленных через ";".<br>
-Перед пакетом команд для записи отправляется пакет 'Write start', если эта строка присутствует в файле.<br>
-Значение команды для записи можно редактировать - Ok на списке команд, стрелка +/- и переход по цифрам, завершить - Назад, вставка цифры - Ok, удаление цифру - длительный Ok.<br>
+Затем берутся заполненные значения полей из самой команды ("R:" или "W:").<br>
+Если в конце строки с командой чтения символ '#', считанное значение будет показано в шестнадцатеричном виде.<br><br>
+Пакет состоит из списка имен команд, перечисленных через ";".<br><br>
+Отправка пакета для записи - длительно нажать Ok в списке и подтвердить.<br>
+Перед пакетом команд для записи отправляется пакет 'Write start', если эта строка присутствует в файле настроек.<br><br>
+Значение команды для записи можно редактировать - Ok на списке команд, стрелки - +/- и переход по цифрам, завершить - Назад, вставка цифры - Ok, удаление цифры - длительный Ok.<br>
 <br>
 <br>
 Пример файл [CO2_mini](https://raw.githubusercontent.com/vad7/nRF24-Batch/main/Distr/nrf24batch/CO2_mini.txt)<br>
 Пример файл [CO2_mini](https://raw.githubusercontent.com/vad7/nRF24-Batch/main/Distr/nrf24batch/CO2_mini.txt)<br>
 Для устройства на Attiny44A, которое отправляет данные с датчика CO2 на контроллеры, управляющие вентиляцией или проветриватели: https://github.com/vad7/CO2-mini
 Для устройства на Attiny44A, которое отправляет данные с датчика CO2 на контроллеры, управляющие вентиляцией или проветриватели: https://github.com/vad7/CO2-mini

+ 1 - 1
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: 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=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=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 }
 R: CO2 threshold*2=5,,0xC2  <- Read cmd, '*2' - means received field with 2 bytes size (int16), result in bytes = { 5, 0, 0, 0xC2 }

+ 114 - 45
nrf24batch.c

@@ -14,7 +14,7 @@
 #include <u8g2.h>
 #include <u8g2.h>
 
 
 #define TAG 		"nrf24batch"
 #define TAG 		"nrf24batch"
-#define VERSION		"1.1"
+#define VERSION		"1.2"
 
 
 #define SCAN_APP_PATH_FOLDER "/ext/nrf24batch"
 #define SCAN_APP_PATH_FOLDER "/ext/nrf24batch"
 #define LOG_FILENAME 		"log"
 #define LOG_FILENAME 		"log"
@@ -64,6 +64,7 @@ uint8_t send_status = sst_none;// sst_*
 bool cmd_array = false;
 bool cmd_array = false;
 uint8_t cmd_array_idx;
 uint8_t cmd_array_idx;
 uint8_t cmd_array_cnt = 0;
 uint8_t cmd_array_cnt = 0;
+bool cmd_array_hex;
 uint8_t save_settings = 0;
 uint8_t save_settings = 0;
 uint16_t view_cmd[3] = {0, 0, 0}; // ReadBatch, Read, WriteBatch
 uint16_t view_cmd[3] = {0, 0, 0}; // ReadBatch, Read, WriteBatch
 uint8_t view_x = 0;
 uint8_t view_x = 0;
@@ -257,16 +258,13 @@ static bool select_settings_file() {
 	FuriString* path;
 	FuriString* path;
 	path = furi_string_alloc();
 	path = furi_string_alloc();
 	furi_string_set(path, SCAN_APP_PATH_FOLDER);
 	furi_string_set(path, SCAN_APP_PATH_FOLDER);
-
 	DialogsFileBrowserOptions browser_options;
 	DialogsFileBrowserOptions browser_options;
 	dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL);
 	dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL);
 	browser_options.hide_ext = false;
 	browser_options.hide_ext = false;
-
 	bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options);
 	bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options);
-
 	furi_record_close("dialogs");
 	furi_record_close("dialogs");
 	if(ret) {
 	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));
 			FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path));
 			file_stream_close(file_stream);
 			file_stream_close(file_stream);
 		} else {
 		} else {
@@ -333,10 +331,13 @@ uint8_t nrf24_send_packet()
 
 
 uint8_t nrf24_resend_read_packet()
 uint8_t nrf24_resend_read_packet()
 {
 {
-	if(Log_Total) {
+	if(Log_Total && !cmd_array) {
 		FuriString *str = Log[Log_Total - 1];
 		FuriString *str = Log[Log_Total - 1];
 		char *p = strstr(furi_string_get_cstr(str), ": ");
 		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();
 	return nrf24_send_packet();
 }
 }
@@ -369,23 +370,23 @@ bool nrf24_read_newpacket() {
 			else if(size == 3) var = (*(uint32_t*)payload_receive) & 0xFFFFFF;
 			else if(size == 3) var = (*(uint32_t*)payload_receive) & 0xFFFFFF;
 			else var = *(int32_t*)payload_receive;
 			else var = *(int32_t*)payload_receive;
 			//FURI_LOG_D(TAG, "VAR(%d): %ld", size, var);
 			//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 {
 			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 {
 				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) {
 				if(--cmd_array_cnt) {
 				if(--cmd_array_cnt) {
 					furi_string_cat_str(str, ",");
 					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;
 					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 send_status = sst_ok;
 			} else {
 			} else {
 				if(size == 0) { // string, until '\0'
 				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) {
 		} else if(end == p) {
 			idx += payload_struct[fld];
 			idx += payload_struct[fld];
+		} else if(*p == '#') { // value in Hexadecimal, end string
+			break;
 		} else {
 		} else {
 			ERR = 2;
 			ERR = 2;
 			strcpy(ERR_STR, "char: ");
 			strcpy(ERR_STR, "char: ");
@@ -486,6 +489,8 @@ bool Run_Read_cmd(FuriString *cmd)
 	FuriString *fs = furi_string_alloc();
 	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_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, ": ");
 	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;
 	Log[Log_Total++] = fs;
 	p++;
 	p++;
 	memset(payload, 0, sizeof(payload));
 	memset(payload, 0, sizeof(payload));
@@ -497,7 +502,10 @@ bool Run_Read_cmd(FuriString *cmd)
 		p = strchr(furi_string_get_cstr(cmd), '[');
 		p = strchr(furi_string_get_cstr(cmd), '[');
 		if(p) {
 		if(p) {
 			cmd_array_cnt = str_to_int(p + 1);
 			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();
 	prepare_nrf24();
@@ -778,7 +786,51 @@ static uint8_t load_settings_file() {
 
 
 static void save_batch(void)
 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) {
 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);
 	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)
 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;
 	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) {
 			if(*(end - 1) == '*') end--; // skip *
 			if(*(end - 1) == '*') end--; // skip *
 			else if(*(end - 2) == '*') end -= 2; // 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';
 			screen_buf[len] = '\0';
 			canvas_draw_str(canvas, 5, y, screen_buf);
 			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) {
 static void render_callback(Canvas* const canvas, void* ctx) {
 	const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
 	const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
 	if(plugin_state == NULL) return;
 	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: ");
 				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(rw_type == rwt_read_batch || send_status != sst_none) {
 					if(NRF_ERROR) strcat(screen_buf, "nRF24 ERROR!");
 					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_error) strcat(screen_buf, "NO ACK!");
 					else if(send_status == sst_timeout) strcat(screen_buf, "TIMEOUT!");
 					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) 
 					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 *p = (char*)furi_string_get_cstr(WriteBatch_cmd[view_cmd[rwt_write_batch]]);
 					char *end = strchr(p, ':');
 					char *end = strchr(p, ':');
 					if(end) {
 					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, 0, y, ">");
 						canvas_draw_str(canvas, -1, y, ">");
 						canvas_draw_str(canvas, -1, y, ">");
 						if(Edit) {
 						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
 							if(n > 0) vx += n;	// fix out of screen
 							int x = 6 + (Edit_pos - p - vx) * 5;
 							int x = 6 + (Edit_pos - p - vx) * 5;
 							canvas_draw_line(canvas, x - 1, y, x - 1, y - 1);
 							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(rw_type == rwt_write_batch) {
 			if(send_status == sst_ok) {
 			if(send_status == sst_ok) {
 				if(ERR == 0 && WriteBatch_cmd_curr < Log_Total && furi_get_tick() - NRF_time >= delay_between_pkt) {
 				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) {
 			} else if(send_status == sst_sending) {
 				if(NRF_repeat++ < NRF_resend) nrf24_send_packet(); 
 				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
 					send_status = sst_error; // error NO_ACK
 				}
 				}
 			}
 			}
+		// ReadBatch or ReadCmd
 		} else if(send_status == sst_sending) { // sending
 		} 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(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
 		} else if(send_status == sst_receiving) { // receiving
 			for(uint8_t i = 0; i < 3; i++) {
 			for(uint8_t i = 0; i < 3; i++) {
 				bool new = nrf24_read_newpacket();
 				bool new = nrf24_read_newpacket();
@@ -1057,16 +1122,14 @@ int32_t nrf24batch_app(void* p) {
 							ask_question_answer ^= 1;
 							ask_question_answer ^= 1;
 						} else if(what_doing == 0) {
 						} else if(what_doing == 0) {
 						} else if(what_doing == 1) {
 						} 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) {
 						} else if(what_doing == 2) {
 							if(Edit) {
 							if(Edit) {
 								if(is_digit(Edit_pos - 1, Edit_hex)) Edit_pos--;
 								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--;
 							} else if(view_x) view_x--;
 						}
 						}
 					}
 					}
@@ -1078,12 +1141,15 @@ int32_t nrf24batch_app(void* p) {
 						} else if(what_doing == 0) {
 						} else if(what_doing == 0) {
 							what_doing = 1;
 							what_doing = 1;
 						} else if(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) {
 						} else if(what_doing == 2) {
 							if(Edit) {
 							if(Edit) {
 								if(is_digit(Edit_pos + 1, Edit_hex)) Edit_pos++;
 								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++;
 							} else view_x++;
 						}
 						}
@@ -1106,6 +1172,7 @@ int32_t nrf24batch_app(void* p) {
 									if(what_doing == 2) {
 									if(what_doing == 2) {
 										ERR = 0;
 										ERR = 0;
 										send_status = sst_none;
 										send_status = sst_none;
+										Edited = false;
 										what_doing--;
 										what_doing--;
 									}
 									}
 								}
 								}
@@ -1122,6 +1189,7 @@ int32_t nrf24batch_app(void* p) {
 								if(ReadBatch_cmd_Total) {
 								if(ReadBatch_cmd_Total) {
 									ERR = 0;
 									ERR = 0;
 									Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]);
 									Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]);
+									view_x = 0;
 									view_Batch = 0;
 									view_Batch = 0;
 									what_doing = 2;
 									what_doing = 2;
 								}
 								}
@@ -1130,13 +1198,15 @@ int32_t nrf24batch_app(void* p) {
 									ERR = 0;
 									ERR = 0;
 									free_Log();
 									free_Log();
 									Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]);
 									Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]);
+									view_x = 0;
 									what_doing = 2;
 									what_doing = 2;
 								}
 								}
 							} else if(rw_type == rwt_write_batch) {
 							} else if(rw_type == rwt_write_batch) {
 								if(WriteBatch_cmd_Total) {
 								if(WriteBatch_cmd_Total) {
 									Prepare_Write_cmd(WriteBatch_cmd[view_cmd[rwt_write_batch]]);
 									Prepare_Write_cmd(WriteBatch_cmd[view_cmd[rwt_write_batch]]);
-									Edited = false;
 									send_status = sst_none;
 									send_status = sst_none;
+									Edited = false;
+									view_x = 0;
 									view_Batch = 0;
 									view_Batch = 0;
 									what_doing = 2;
 									what_doing = 2;
 								}
 								}
@@ -1195,11 +1265,10 @@ int32_t nrf24batch_app(void* p) {
 					break;
 					break;
 				case InputKeyBack:
 				case InputKeyBack:
 					if(event.input.type == InputTypeLong) {
 					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;
 							if(!ask_question) ask_question_answer = 1;
 							ask_question = ask_exit;
 							ask_question = ask_exit;
-						}
+						} else processing = false;
 					} else if(event.input.type == InputTypeShort) {
 					} else if(event.input.type == InputTypeShort) {
 						if(Edit) Edit = 0;
 						if(Edit) Edit = 0;
 						else if(ask_question) ask_question = 0;
 						else if(ask_question) ask_question = 0;