diff --git a/ReadMe.md b/ReadMe.md index 068fe3559..085afa3be 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -32,8 +32,8 @@ Thank you to all the supporters! - Renamed some FAPs to fix ordering in Applications, delete your `/ext/apps` folder to get rid of duplicates - Removed: [VB Lab Migration Assistant (By GMMan)](https://github.com/GMMan/flipperzero-vb-migrate) `Req: Vital Bracelet` (due to fap error) - Updated: [Dice (By Ka3u6y6a)](https://github.com/Ka3u6y6a/flipper-zero-dice) -- Updated: [NRF24 Scanner (By vad7)](https://github.com/vad7/nrf24scan) - Updated: [Wii EC Analyser (By csBlueChip)](https://github.com/csBlueChip/FlipperZero_WiiEC) +- Updated: [NRF24 Scanner v1.5 (By vad7)](https://github.com/vad7/nrf24scan) ## Install from Release FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE! @@ -225,7 +225,7 @@ $ ./fbt resources icons dolphin_ext - [Music Player (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1189) - [NFC Magic (By gornekich)](https://github.com/flipperdevices/flipperzero-firmware/pull/1966) - [NRF Sniff (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/nrfsniff) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/nrfsniff) from nocomp/Frog/UberGuidoZ) `Req: NRF24` -- [NRF24 Scanner (By vad7)](https://github.com/vad7/nrf24scan) +- [NRF24 Scanner v1.5 (By vad7)](https://github.com/vad7/nrf24scan) - [Ocarina (By invalidna-me)](https://github.com/invalidna-me/flipperzero-ocarina) [Here are the LOTZ Songs](https://www.zeldadungeon.net/wiki/Ocarina_of_Time_Songs) - [Paint (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e) - [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen) diff --git a/applications/plugins/nrf24scan/README.md b/applications/plugins/nrf24scan/README.md index d47c97a6f..6c4d91a4e 100644 --- a/applications/plugins/nrf24scan/README.md +++ b/applications/plugins/nrf24scan/README.md @@ -1,7 +1,10 @@ # NRF24 scanner with logging and resend ability for Flipper Zero -An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional. - +An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors.
+NRF24L01+ Enhanced ShockBurst packet decoder example using Python: [nrf24_packet_decoder.py](https://raw.githubusercontent.com/vad7/nrf24scan/master/nrf24_packet_decoder.py)
+
+Flipper Zero FAP file: [Nrf24_Scanner.fap](https://raw.githubusercontent.com/vad7/nrf24scan/master/Nrf24_Scanner.fap)
+ Settings file (default addr.txt) format:
Rate: 0/1/2 - rate in Mbps (=0.25/1/2)
@@ -31,15 +34,16 @@ Long press OK - view addresses.



-Decode the Packet Control Field (long press OK in the list and then press '>').
+Decode the Packet Control Field and check CRC (long press OK in the list and then press '<' / '>').
ESB (Enhanced Shockburst) option must be turned off. +Press '>' to decode CRC.



1 - pipe #
-2 - Payload length (for valide packet must be 1..20 hex, or 0x33 if not DPL)
+2 - Payload length (for valide packet must be 1..20 or 33 hex)
3 - PID (2 bit) + NO_ACK (1 bit)


diff --git a/applications/plugins/nrf24scan/Screenshot-1.png b/applications/plugins/nrf24scan/Screenshot-1.png index 082e7d5af..61ca892d8 100644 Binary files a/applications/plugins/nrf24scan/Screenshot-1.png and b/applications/plugins/nrf24scan/Screenshot-1.png differ diff --git a/applications/plugins/nrf24scan/Screenshot-4.png b/applications/plugins/nrf24scan/Screenshot-4.png index 375f7b777..6351cdd3f 100644 Binary files a/applications/plugins/nrf24scan/Screenshot-4.png and b/applications/plugins/nrf24scan/Screenshot-4.png differ diff --git a/applications/plugins/nrf24scan/Screenshot-5.png b/applications/plugins/nrf24scan/Screenshot-5.png index 0b4a59dd1..d9e3173a2 100644 Binary files a/applications/plugins/nrf24scan/Screenshot-5.png and b/applications/plugins/nrf24scan/Screenshot-5.png differ diff --git a/applications/plugins/nrf24scan/addr.txt b/applications/plugins/nrf24scan/addr.txt deleted file mode 100644 index a79064baf..000000000 --- a/applications/plugins/nrf24scan/addr.txt +++ /dev/null @@ -1,5 +0,0 @@ -Rate: 1 -Ch: 120 -P0: C8C801 -P1: C8C802 -P2: 03 diff --git a/applications/plugins/nrf24scan/addr1.txt b/applications/plugins/nrf24scan/addr1.txt deleted file mode 100644 index 5a10c4298..000000000 --- a/applications/plugins/nrf24scan/addr1.txt +++ /dev/null @@ -1,3 +0,0 @@ -Rate: 1 -Ch: 120 -P0: C8C8E5 diff --git a/applications/plugins/nrf24scan/nrf24_packet_decoder.py b/applications/plugins/nrf24scan/nrf24_packet_decoder.py new file mode 100644 index 000000000..9e1a42a18 --- /dev/null +++ b/applications/plugins/nrf24scan/nrf24_packet_decoder.py @@ -0,0 +1,99 @@ +# +# NRF24L01+ Enhanced ShockBurst packets decoder +# +payload_len_default = 4 +packets = \ +( + '10101010 11101110 00000011 00001000 00001011 01000111 000100 10 0 10101010 10101010 10101010 10101010 00011101', + '10101010 11001000 11001000 11000011 110011 10 0 00001011 00000011 00000101 00000000 0010001100100000', +) + +def bin2hex(x): + def bin2hex_helper(r): + while r: + yield r[0:2].upper() + r = r[2:] + + fmt = "{0:0" + str(int(len(x) / 8 * 2)) + "X}" + hex_data = fmt.format(int(x, 2)) + return list(bin2hex_helper(hex_data)) + + +def split_packet(packet, parts): + """Split a string of 1s and 0s into multiple substrings as specified by parts. + Example: "111000011000", (3, 4, 2) -> ["111", "0000", "11", "000"] + :param packet: String of 1s and 0s + :param parts: Tuple of length of substrings + :return: list of substrings + """ + out = [] + packet = packet.replace(' ', '') + for part_length in parts: + out.append(packet[0:part_length]) + packet = packet[part_length:] + out.append(packet) + return out + + +def parse_packet(packet, address_length): + """Split a packet into its fields and return them as tuple.""" + preamble, address, payload_length, pid, no_ack, rest = split_packet(packet=packet, + parts=(8, 8 * address_length, 6, 2, 1)) + payload, crc = split_packet(packet=rest, parts=((payload_len_default if int(payload_length, 2) > 32 else int(payload_length, 2)) * 8,)) + + assert preamble in ('10101010', '01010101') + assert len(crc) in (8, 16) + + return preamble, address, payload_length, pid, no_ack, payload, crc + + +def crc(bits, size=8): + """Calculate the crc value for the polynomial initialized with 0xFF/0xFFFF) + :param size: 8 or 16 bit crc + :param bits: String of 1s and 0s + :return: + :polynomial: 1 byte CRC - standard is 0x107 = 0b100000111 = x^8+x^2+x^1+1, result the same for 0x07 + :polynomial: 2 byte CRC - standard is 0x11021 = X^16+X^12+X^5+1, result the same for 0x1021 + """ + if size == 8: + polynomial = 0x107 + crc = 0xFF + else: + polynomial = 0x11021 + crc = 0xFFFF + max_crc_value = (1 << size) - 1 # e.g. 0xFF for mode 8bit-crc + for bit in bits: + bit = int(bit, 2) + crc <<= 1 + if (crc >> size) ^ bit: # top most lfsr bit xor current data bit + crc ^= polynomial + crc &= max_crc_value # trim the crc to reject carry over bits + return crc + + +if __name__ == '__main__': + for packet in packets: + fld = packet.split(' '); + address_length = -1 + for f in fld: + if len(f) == 6: break + address_length += 1 + preamble, address, payload_length, pid, no_ack, payload, crc_received = \ + parse_packet(packet=packet, address_length=address_length) + crc_size = len(crc_received) + crc_received = hex(int(crc_received, 2)) + print(f"Packet: {packet}") + print('\n'.join(( + f'Preamble: {preamble}', + f'Address: {address_length} bytes - {bin2hex(address)}', + f'Payload length in packet: {int(payload_length, 2)}, used: {(payload_len_default if int(payload_length, 2) > 32 else int(payload_length, 2))}', + f'Pid: {int(pid, 2)}', + f'No_ack: {int(no_ack, 2) == 1}', + f'Payload: {bin2hex(payload)}', + f'CRC{crc_size}: {crc_received}'))) + crc_calculated = hex(crc(address + payload_length + pid + no_ack + payload, size=crc_size)) + if crc_received == crc_calculated: + print('CRC is valid!') + else: + print(f'CRC mismatch! Calculated CRC is f{crc_calculated}.') + print('-------------') diff --git a/applications/plugins/nrf24scan/nrf24scan.c b/applications/plugins/nrf24scan/nrf24scan.c index 49aa7976d..9a62af271 100644 --- a/applications/plugins/nrf24scan/nrf24scan.c +++ b/applications/plugins/nrf24scan/nrf24scan.c @@ -11,9 +11,10 @@ #include #include #include +#include #define TAG "nrf24scan" -#define VERSION "1.4" +#define VERSION "1.5" #define MAX_CHANNEL 125 #define MAX_ADDR 6 @@ -61,6 +62,7 @@ uint8_t NRF_ESB = 0; // 0 - ShockBurst, 1 - Enhanced ShockBurst uint8_t NRF_DPL = 0; // 1 - Dynamic Payload Length uint8_t NRF_CRC = 0; // 1 - No, 1 - CRC 1byte, 2 - CRC 2byte uint8_t NRF_Payload = 32; // len in bytes, max 32 +uint8_t NRF_AA_OFF = 0; // Disable Auto Acknowledgement bool NRF_ERROR = 0; struct { @@ -86,6 +88,7 @@ uint8_t menu_selected = 0; uint32_t start_time; uint8_t view_log_decode_PCF = 0; // view log: 1 - decode packet control field (9b) when ESB off. After pipe # (hex): +uint8_t view_log_decode_CRC = 0; // CRC bytes - 1/2, 0 - none #define menu_selected_max 5 enum { @@ -138,7 +141,7 @@ static void add_to_str_hex_bytes_shift_9b(char* out, char* arr, int bytes) { out += strlen(out); arr++; // +8b do { - snprintf(out, 4, "%02X", (((*arr) & 0x7F) << 1) | (*(arr + 1) >> 7)); + snprintf(out, 4, "%02X", ((uint8_t)(*arr << 1)) | (*(arr + 1) >> 7)); arr++; out += 2; } while(--bytes); @@ -325,7 +328,7 @@ static uint8_t load_settings_file(Stream* file_stream) { FURI_LOG_D(TAG, "load failed. file_size: %d", file_size); return 1; } - file_size = MIN(file_size, LOG_REC_SIZE * MAX_LOG_RECORDS * 2 + 100); + file_size = MIN(file_size, (size_t)LOG_REC_SIZE * MAX_LOG_RECORDS * 2 + 100); file_buf = malloc(file_size + 1); if(file_buf == NULL) { FURI_LOG_D(TAG, "Memory low, need: %d", file_size); @@ -338,7 +341,7 @@ static uint8_t load_settings_file(Stream* file_stream) { int16_t line_num = 0; memset((uint8_t*)&addrs, 0, sizeof(addrs)); bool log_loaded = false; - while(line_ptr && line_ptr - file_buf < file_size) { + while(line_ptr && (size_t)(line_ptr - file_buf) < file_size) { char* end_ptr = strstr((char*)line_ptr, "\n"); if(end_ptr == NULL) end_ptr = file_buf + file_size; @@ -353,7 +356,7 @@ static uint8_t load_settings_file(Stream* file_stream) { *(end_ptr - 1) = '\0'; line_len--; } - FURI_LOG_D(TAG, " L#%d: [%d]%s", line_num, line_len, line_ptr); + //FURI_LOG_D(TAG, " L#%d: [%d]%s", line_num, line_len, line_ptr); if(strncmp(line_ptr, SettingsFld_Rate, sizeof(SettingsFld_Rate) - 1) == 0) { NRF_rate = atoi(line_ptr + sizeof(SettingsFld_Rate)); } else if(strncmp(line_ptr, SettingsFld_Ch, sizeof(SettingsFld_Ch) - 1) == 0) { @@ -373,23 +376,12 @@ static uint8_t load_settings_file(Stream* file_stream) { switch(a) { case '0': addrs.addr_len = ConvertHexToArray(line_ptr, addrs.addr_P0, 5); - FURI_LOG_D( - TAG, - " +Addr(%d): %02X%02X%02X...", - addrs.addr_len, - addrs.addr_P0[0], - addrs.addr_P0[1], - addrs.addr_P0[2]); + //FURI_LOG_D(TAG, " +Addr(%d): %02X%02X%02X...", addrs.addr_len, addrs.addr_P0[0], addrs.addr_P0[1], addrs.addr_P0[2]); if(addrs.addr_len >= 2) err = 0; break; case '1': ConvertHexToArray(line_ptr, addrs.addr_P1, 5); - FURI_LOG_D( - TAG, - " +Addr: %02X%02X%02X...", - addrs.addr_P0[1], - addrs.addr_P1[1], - addrs.addr_P1[2]); + //FURI_LOG_D(TAG, " +Addr: %02X%02X%02X...", addrs.addr_P0[1], addrs.addr_P1[1], addrs.addr_P1[2]); break; case '2': ConvertHexToArray(line_ptr, &addrs.addr_P2, 1); @@ -416,7 +408,9 @@ static uint8_t load_settings_file(Stream* file_stream) { } if(log_arr_idx < MAX_LOG_RECORDS - 1) { ConvertHexToArray( - line_ptr, APP->log_arr + log_arr_idx * LOG_REC_SIZE, MIN(NRF_Payload, 32)); + line_ptr, + APP->log_arr + log_arr_idx * LOG_REC_SIZE, + MIN(NRF_Payload + 1, LOG_REC_SIZE)); log_arr_idx++; } } @@ -447,14 +441,15 @@ static void prepare_nrf24() { 0x70 | ((NRF_CRC == 1 ? 0b1000 : NRF_CRC == 2 ? 0b1100 : 0))); // Mask all interrupts - nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, NRF_ESB ? 1 : 0); // Automatic Retransmission - nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0); // Disable auto acknowledgement + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, NRF_ESB ? 0x11 : 0); // Automatic Retransmission + nrf24_write_reg( + nrf24_HANDLE, REG_EN_AA, NRF_AA_OFF || !NRF_ESB ? 0 : 0x3F); // Auto acknowledgement nrf24_write_reg( nrf24_HANDLE, REG_FEATURE, - 1 + (NRF_DPL ? - 4 : - 1)); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload + NRF_DPL ? + 4 + 1 : + 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload nrf24_set_maclen(nrf24_HANDLE, addrs.addr_len); for(int i = 0; i < addrs.addr_len; i++) addr[i] = addrs.addr_P0[addrs.addr_len - i - 1]; nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P0, addr, addrs.addr_len); @@ -525,7 +520,6 @@ bool nrf24_read_newpacket() { if(APP->log_arr == NULL) return false; bool found = false; uint8_t packetsize; - uint8_t packet[32] = {0}; uint8_t* ptr = APP->log_arr + log_arr_idx * LOG_REC_SIZE; uint8_t status = nrf24_rxpacket(nrf24_HANDLE, ptr + 1, &packetsize, !NRF_DPL); if(status & RX_DR) { @@ -561,6 +555,37 @@ bool nrf24_send_packet() { return last_packet_send_st; } +uint16_t calc_crc(uint32_t crc, uint8_t* ptr, uint8_t bitnum, uint16_t bits) { + //uint8_t bitnum = 7; + uint32_t crc_high, polynom; + if(view_log_decode_CRC == 2) { + crc_high = (1 << 16); + polynom = 0x11021; // X^16+X^12+X^5+1 + } else { + crc_high = (1 << 8); + polynom = 0x107; // x^8+x^2+x^1+1 + } + while(bits--) { + crc <<= 1; + if(((crc & crc_high) != 0) ^ ((*ptr >> bitnum) & 1)) crc ^= polynom; + if(bitnum == 0) { + ptr++; + bitnum = 7; + } else + bitnum--; + } + return crc & (view_log_decode_CRC == 2 ? 0xFFFF : 0xFF); +} + +// shifted 1 bit right +uint16_t get_shifted_crc(uint8_t* pcrc) { + uint16_t crc = ((uint8_t)(*pcrc << 1)) | (*(pcrc + 1) >> 7); + if(view_log_decode_CRC == 2) { + crc = (crc << 8) | (((uint8_t)(*(pcrc + 1) << 1))) | (*(pcrc + 2) >> 7); + } + return crc; +} + static void render_callback(Canvas* const canvas, void* ctx) { const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); if(plugin_state == NULL) return; @@ -580,6 +605,10 @@ static void render_callback(Canvas* const canvas, void* ctx) { if(NRF_DPL) strcat(screen_buf, " DPL"); canvas_draw_str(canvas, 80, 20, screen_buf); } + if(NRF_AA_OFF) { + canvas_draw_str(canvas, 61, 20, "AA"); + canvas_draw_line(canvas, 60, 21, 72, 12); + } snprintf( screen_buf, sizeof(screen_buf), @@ -588,21 +617,19 @@ static void render_callback(Canvas* const canvas, void* ctx) { NRF_rate == 1 ? "1M" : "250K"); // menu_selected = 2 canvas_draw_str(canvas, 10, 30, screen_buf); - canvas_set_font(canvas, FontBatteryPercent); - snprintf(screen_buf, sizeof(screen_buf), "R:%d", NRF_Payload); + snprintf(screen_buf, sizeof(screen_buf), "Payload: %d", NRF_Payload); canvas_draw_str(canvas, 80, 30, screen_buf); - if(NRF_CRC == 1) - canvas_draw_str(canvas, 105, 30, "CRC1"); - else if(NRF_CRC == 2) - canvas_draw_str(canvas, 105, 30, "CRC2"); - canvas_set_font(canvas, FontSecondary); - strcpy(screen_buf, "Find channel period: "); // menu_selected = 3 + strcpy(screen_buf, "Next Ch time: "); // menu_selected = 3 if(find_channel_period == 0) strcat(screen_buf, "off"); else snprintf( screen_buf + strlen(screen_buf), sizeof(screen_buf), "%d s", find_channel_period); canvas_draw_str(canvas, 10, 40, screen_buf); + if(NRF_CRC == 1) + canvas_draw_str(canvas, 99, 40, "CRC1"); + else if(NRF_CRC == 2) + canvas_draw_str(canvas, 99, 40, "CRC2"); snprintf( screen_buf, sizeof(screen_buf), @@ -649,8 +676,6 @@ static void render_callback(Canvas* const canvas, void* ctx) { if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1; uint16_t page = view_log_arr_idx & ~7; for(uint8_t i = 0; i < 8 && page + i < log_arr_idx; i++) { - snprintf(screen_buf, sizeof(screen_buf), "%d:", page + i + 1); - canvas_draw_str(canvas, 0, 14 + i * 7, screen_buf); screen_buf[0] = (view_log_arr_idx & 7) != i ? ' ' : last_packet_send != view_log_arr_idx ? '>' : last_packet_send_st ? '*' : @@ -661,31 +686,110 @@ static void render_callback(Canvas* const canvas, void* ctx) { uint8_t pipe = dpl & 7; dpl >>= 3; if(dpl == 0) dpl = 32; - if(view_log_decode_PCF && dpl == 32) dpl -= 2; int count = dpl - view_log_arr_x; - uint8_t max_width = view_log_arr_x == 0 && addrs.addr_count > 1 ? - view_log_decode_PCF ? VIEW_LOG_WIDTH_B - 3 : - VIEW_LOG_WIDTH_B - 1 : - VIEW_LOG_WIDTH_B; + if(view_log_decode_PCF) count--; + uint8_t max_width = VIEW_LOG_WIDTH_B; + if(view_log_arr_x == 0) { + if(addrs.addr_count > 1) max_width--; + if(view_log_decode_PCF) max_width -= 2; + } if(count > max_width) count = max_width; ptr += view_log_arr_x; + uint8_t* crcptr = NULL; + uint8_t pre = 0; if(count > 0) { + if(view_log_decode_CRC) { + static uint16_t prev_addr_CRC; + static int8_t prev_pipe = -1; + uint8_t* pcrc = APP->log_arr + (page + i) * LOG_REC_SIZE + 1; + uint16_t crc; + if(prev_pipe == pipe) { + crc = prev_addr_CRC; + } else { + crc = view_log_decode_CRC == 2 ? 0xFFFF : 0xFF; + if(pipe <= 1) { + crc = calc_crc( + crc, + pipe == 0 ? addrs.addr_P0 : addrs.addr_P1, + 7, + addrs.addr_len * 8); + } else { + crc = calc_crc(crc, addrs.addr_P1, 7, (addrs.addr_len - 1) * 8); + crc = calc_crc( + crc, + pipe == 2 ? &addrs.addr_P2 : + pipe == 3 ? &addrs.addr_P3 : + pipe == 4 ? &addrs.addr_P4 : + &addrs.addr_P5, + 7, + 8); + } + prev_addr_CRC = crc; + prev_pipe = pipe; + } + if(view_log_decode_PCF) { + crc = calc_crc(crc, pcrc++, 7, 9); + if(crc == get_shifted_crc(pcrc)) crcptr = pcrc; + if(crcptr == NULL) { + for(int8_t j = 0; j < (int8_t)dpl - view_log_decode_CRC - 1; j++) { + crc = calc_crc(crc, pcrc++, 6, 8); + if(crc == get_shifted_crc(pcrc)) { + crcptr = pcrc; + break; + } + } + } + } else { + for(int8_t j = 0; j < (int8_t)dpl - view_log_decode_CRC; j++) { + crc = calc_crc(crc, pcrc++, 7, 8); + if((view_log_decode_CRC == 1 && crc == *pcrc) || + (view_log_decode_CRC == 2 && + crc == ((*pcrc << 8) | *(pcrc + 1)))) { + crcptr = pcrc; + break; + } + } + } + } if(max_width < VIEW_LOG_WIDTH_B) { - snprintf(screen_buf + 1, 10, "%X-", pipe); - if(view_log_decode_PCF) - snprintf( + pre += snprintf(screen_buf + 1, 10, "%X-", pipe); + if(view_log_decode_PCF) { + pre += snprintf( screen_buf + strlen(screen_buf), 10, "%02X%01X-", *ptr >> 2, ((*ptr & 3) << 1) | (*(ptr + 1) >> 7)); + } } if(view_log_decode_PCF) add_to_str_hex_bytes_shift_9b(screen_buf, ptr, count); else add_to_str_hex_bytes(screen_buf, ptr, count); } - canvas_draw_str(canvas, 3 * 5, 14 + i * 7, screen_buf); + uint16_t y = 14 + i * 7; + canvas_draw_str(canvas, 3 * 5, y, screen_buf); + uint16_t x = snprintf(screen_buf, sizeof(screen_buf), "%d", page + i + 1); + canvas_draw_str(canvas, 0, y, screen_buf); + if(crcptr) { // 5x7 font, 9 lines + canvas_draw_str(canvas, x * 5, y, "="); + int n = crcptr - (uint8_t*)ptr - 1; + if(n > -view_log_decode_CRC && n < count) { + int len; + x = (4 + pre) * 5; + if(n < 0) { + len = view_log_decode_CRC + n; + n = 0; + } else { + len = MIN(view_log_decode_CRC, count - n); + x += n * 2 * 5; + canvas_draw_line(canvas, x - 1, y, x - 1, y - 1); + } + canvas_draw_line(canvas, x - 1, y, n = x + len * 2 * 5 - 1, y); + canvas_draw_line(canvas, n, y, n, y - 1); + } + } else + canvas_draw_str(canvas, x * 5, y, ":"); } } } else { @@ -723,7 +827,15 @@ static void render_callback(Canvas* const canvas, void* ctx) { screen_buf[0] = 'v'; strcpy(screen_buf + 1, VERSION); canvas_draw_str(canvas, 108, 7, screen_buf); - if(view_log_decode_PCF) canvas_draw_str(canvas, 78, 64, "Decode PCF"); + if(view_log_decode_PCF || view_log_decode_CRC) { + strcpy(screen_buf, "Decode: "); + if(view_log_decode_PCF) strcat(screen_buf, "PCF "); + if(view_log_decode_CRC == 1) + strcat(screen_buf, "CRC1"); + else if(view_log_decode_CRC == 2) + strcat(screen_buf, "CRC2"); + canvas_draw_str(canvas, 0, 64, screen_buf); + } } release_mutex((ValueMutex*)ctx, plugin_state); } @@ -809,39 +921,6 @@ int32_t nrf24scan_app(void* p) { } } break; - case InputKeyRight: - if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { - if(what_doing == 0) { - switch(menu_selected) { - case Menu_open_file: - save_settings ^= 1; - break; - case Menu_enter_channel: - NRF_channel += event.input.type == InputTypeRepeat ? 10 : 1; - if(NRF_channel > MAX_CHANNEL) NRF_channel = 0; - break; - case Menu_enter_rate: - NRF_rate++; - if(NRF_rate > 2) NRF_rate = 0; - break; - case Menu_enter_scan_period: - find_channel_period += event.input.type == InputTypeRepeat ? 10 : - 1; - break; - case Menu_log: - if(++log_to_file > 2) log_to_file = -1; - break; - case Menu_ok: - what_to_do = !what_to_do; - break; - } - } else if(what_doing == 1) { - if(view_log_arr_x < VIEW_LOG_MAX_X) view_log_arr_x++; - } else if(what_doing == 2) { - if(NRF_ESB == 0) view_log_decode_PCF ^= 1; - } - } - break; case InputKeyLeft: if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { if(what_doing == 0) { @@ -852,7 +931,7 @@ int32_t nrf24scan_app(void* p) { break; case Menu_enter_rate: NRF_Payload -= event.input.type == InputTypeRepeat ? 10 : 1; - if(NRF_Payload == 0 || NRF_Payload > 32) NRF_Payload = 32; + if(NRF_Payload == 0 || NRF_Payload > 32) NRF_Payload = 1; break; case Menu_enter_scan_period: find_channel_period -= event.input.type == InputTypeRepeat ? 10 : @@ -868,6 +947,42 @@ int32_t nrf24scan_app(void* p) { } } else if(what_doing == 1) { if(view_log_arr_x > 0) view_log_arr_x--; + } else if(what_doing == 2) { + //if(NRF_ESB == 0) + view_log_decode_PCF ^= 1; + } + } + break; + case InputKeyRight: + if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + switch(menu_selected) { + case Menu_open_file: + save_settings ^= 1; + break; + case Menu_enter_channel: + NRF_channel += event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_channel > MAX_CHANNEL) NRF_channel = 0; + break; + case Menu_enter_rate: + NRF_Payload += event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_Payload == 0 || NRF_Payload > 32) NRF_Payload = 32; + break; + case Menu_enter_scan_period: + find_channel_period += event.input.type == InputTypeRepeat ? 10 : + 1; + break; + case Menu_log: + if(++log_to_file > 2) log_to_file = -1; + break; + case Menu_ok: + what_to_do = !what_to_do; + break; + } + } else if(what_doing == 1) { + if(view_log_arr_x < VIEW_LOG_MAX_X) view_log_arr_x++; + } else if(what_doing == 2) { + if(++view_log_decode_CRC > 2) view_log_decode_CRC = 0; } } break; @@ -895,29 +1010,23 @@ int32_t nrf24scan_app(void* p) { stream_free(file_stream); } break; - case Menu_enter_channel: - if(NRF_ESB) { - if(NRF_DPL) - NRF_DPL = NRF_ESB = 0; - else - NRF_DPL = 1; - } else - NRF_ESB = 1; - if(NRF_ESB) view_log_decode_PCF = 0; - break; case Menu_enter_rate: + NRF_rate++; + if(NRF_rate > 2) NRF_rate = 0; + break; + case Menu_enter_scan_period: if(++NRF_CRC > 2) NRF_CRC = 0; break; case Menu_ok: if(what_to_do) { if(addrs.addr_count) { - what_doing = 1; if(log_to_file == -1) { clear_log(); save_to_new_log = true; } else if(log_to_file == 1) save_to_new_log = true; start_scanning(); + if(!NRF_ERROR) what_doing = 1; } } else what_doing = 1; @@ -927,19 +1036,34 @@ int32_t nrf24scan_app(void* p) { } } else if(event.input.type == InputTypeLong) { if(what_doing == 0) { - if(menu_selected == Menu_log) { // Log + if(menu_selected == Menu_enter_channel) { + NRF_AA_OFF ^= 1; + key_press_seq_ok = event.input.sequence; + } else if(menu_selected == Menu_log) { // Log if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) { write_to_log_file(APP->storage, false); clear_log(); } } - nrf24_set_idle(nrf24_HANDLE); } else if(what_doing == 1) { what_doing = 2; } } else if(event.input.type == InputTypeRelease) { - if(what_doing == 1 && key_press_seq_ok != event.input.sequence) { // Send - nrf24_send_packet(); + if(key_press_seq_ok != event.input.sequence) { + if(what_doing == 0) { + if(menu_selected == Menu_enter_channel) { + if(NRF_ESB) { + if(NRF_DPL) + NRF_DPL = NRF_ESB = 0; + else + NRF_DPL = 1; + } else + NRF_ESB = 1; + //if(NRF_ESB) view_log_decode_PCF = 0; + } + } else if(what_doing == 1) { // Send + nrf24_send_packet(); + } } key_press_seq_ok--; } diff --git a/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt b/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt index aa9584f03..3154d0aef 100644 --- a/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt +++ b/assets/resources/dolphin/Kuronons_RMCFW_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 10 -Active frames: 42 -Frames order: 11 11 12 13 14 15 14 13 12 11 0 1 1 1 2 2 3 4 4 5 6 7 7 8 8 9 10 10 10 11 11 11 11 11 11 12 12 13 13 14 14 15 15 15 15 15 16 16 17 18 18 18 -Active cycles: 1 +Passive frames: 54 +Active frames: 0 +Frames order: 0 0 0 0 1 1 1 2 2 3 4 4 5 6 7 7 8 8 9 10 10 10 11 11 11 11 11 11 12 12 13 13 14 14 15 15 15 15 15 16 16 17 18 18 18 18 18 18 18 18 18 18 18 18 +Active cycles: 0 Frame rate: 6 Duration: 3600 -Active cooldown: 4 +Active cooldown: 0 Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_New_year_128x64/frame_0.bm b/assets/resources/dolphin/L1_New_year_128x64/frame_0.bm new file mode 100644 index 000000000..17f53095e Binary files /dev/null and b/assets/resources/dolphin/L1_New_year_128x64/frame_0.bm differ diff --git a/assets/resources/dolphin/L1_New_year_128x64/frame_1.bm b/assets/resources/dolphin/L1_New_year_128x64/frame_1.bm new file mode 100644 index 000000000..a8c1277fe Binary files /dev/null and b/assets/resources/dolphin/L1_New_year_128x64/frame_1.bm differ diff --git a/assets/resources/dolphin/L1_New_year_128x64/frame_2.bm b/assets/resources/dolphin/L1_New_year_128x64/frame_2.bm new file mode 100644 index 000000000..ca1fa852a Binary files /dev/null and b/assets/resources/dolphin/L1_New_year_128x64/frame_2.bm differ diff --git a/assets/resources/dolphin/L1_New_year_128x64/frame_3.bm b/assets/resources/dolphin/L1_New_year_128x64/frame_3.bm new file mode 100644 index 000000000..4b89c67a6 Binary files /dev/null and b/assets/resources/dolphin/L1_New_year_128x64/frame_3.bm differ diff --git a/assets/resources/dolphin/L1_New_year_128x64/meta.txt b/assets/resources/dolphin/L1_New_year_128x64/meta.txt new file mode 100644 index 000000000..542392c19 --- /dev/null +++ b/assets/resources/dolphin/L1_New_year_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 4 +Active frames: 0 +Frames order: 0 1 2 3 +Active cycles: 0 +Frame rate: 2 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt b/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt index 63d6e4750..4d5cb4433 100644 --- a/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_Blaster_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 10 -Active frames: 40 +Passive frames: 50 +Active frames: 0 Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 -Active cycles: 1 +Active cycles: 0 Frame rate: 6 Duration: 3600 -Active cooldown: 4 +Active cooldown: 0 Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt b/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt index e780d7e95..cb006d0e0 100644 --- a/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_CloudG0ku_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 10 -Active frames: 38 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 46 -Active cycles: 1 +Passive frames: 47 +Active frames: 0 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 +Active cycles: 0 Frame rate: 5 Duration: 3600 -Active cooldown: 1 +Active cooldown: 0 Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt b/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt index 1c4b1c117..7e7e55b26 100644 --- a/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_Narut0_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 10 -Active frames: 20 +Passive frames: 30 +Active frames: 0 Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -Active cycles: 1 +Active cycles: 0 Frame rate: 6 Duration: 3600 -Active cooldown: 4 +Active cooldown: 0 Bubble slots: 0 diff --git a/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt b/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt index ffc01409d..b6b3fdf3e 100644 --- a/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt +++ b/assets/resources/dolphin/Sasquach_RMCF_128x64/meta.txt @@ -3,13 +3,13 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 9 -Active frames: 99 -Frames order: 0 1 2 3 4 3 2 1 0 1 2 3 4 5 6 7 8 9 10 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 13 14 15 16 17 18 17 18 17 18 19 20 19 20 19 20 19 20 19 20 21 22 21 22 22 22 23 24 25 26 27 28 29 30 31 32 33 0 34 35 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 37 38 -Active cycles: 1 +Passive frames: 100 +Active frames: 0 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 13 14 15 16 17 18 17 18 17 18 19 20 19 20 19 20 19 20 19 20 21 22 21 22 22 22 23 24 25 26 27 28 29 30 31 32 33 0 34 35 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36 37 38 +Active cycles: 0 Frame rate: 4 Duration: 3600 -Active cooldown: 4 +Active cooldown: 0 Bubble slots: 1