From 3c77e375d902fd120675b8397f7530c3466fcd8c Mon Sep 17 00:00:00 2001 From: Jelmer Date: Thu, 24 Aug 2023 21:37:32 +0200 Subject: [PATCH] Added the tag-flasher --- ESP32_AP-Flasher/OEPL-Flash.py | 318 ---------- Tag_Flasher/ESP32_Flasher/.clang-format | 4 + Tag_Flasher/ESP32_Flasher/.gitignore | 5 + .../ESP32_Flasher/.vscode/extensions.json | 10 + .../ESP32_Flasher/.vscode/settings.json | 55 ++ Tag_Flasher/ESP32_Flasher/CMakeLists.txt | 3 + Tag_Flasher/ESP32_Flasher/include/flasher.h | 59 ++ Tag_Flasher/ESP32_Flasher/include/leds.h | 37 ++ Tag_Flasher/ESP32_Flasher/include/powermgt.h | 5 + Tag_Flasher/ESP32_Flasher/include/settings.h | 9 + Tag_Flasher/ESP32_Flasher/include/storage.h | 42 ++ Tag_Flasher/ESP32_Flasher/include/swd.h | 83 +++ .../ESP32_Flasher/include/usbflasher.h | 4 + .../ESP32_Flasher/include/zbs_interface.h | 64 ++ Tag_Flasher/ESP32_Flasher/platformio.ini | 64 ++ Tag_Flasher/ESP32_Flasher/src/CMakeLists.txt | 6 + Tag_Flasher/ESP32_Flasher/src/flasher.cpp | 540 ++++++++++++++++ Tag_Flasher/ESP32_Flasher/src/leds.cpp | 331 ++++++++++ Tag_Flasher/ESP32_Flasher/src/main.cpp | 51 ++ Tag_Flasher/ESP32_Flasher/src/powermgt.cpp | 31 + Tag_Flasher/ESP32_Flasher/src/storage.cpp | 216 +++++++ Tag_Flasher/ESP32_Flasher/src/swd.cpp | 393 ++++++++++++ Tag_Flasher/ESP32_Flasher/src/usbflasher.cpp | 582 ++++++++++++++++++ .../ESP32_Flasher/src/zbs_interface.cpp | 222 +++++++ Tag_Flasher/OEPL-Flasher.py | 39 +- 25 files changed, 2837 insertions(+), 336 deletions(-) delete mode 100644 ESP32_AP-Flasher/OEPL-Flash.py create mode 100644 Tag_Flasher/ESP32_Flasher/.clang-format create mode 100644 Tag_Flasher/ESP32_Flasher/.gitignore create mode 100644 Tag_Flasher/ESP32_Flasher/.vscode/extensions.json create mode 100644 Tag_Flasher/ESP32_Flasher/.vscode/settings.json create mode 100644 Tag_Flasher/ESP32_Flasher/CMakeLists.txt create mode 100644 Tag_Flasher/ESP32_Flasher/include/flasher.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/leds.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/powermgt.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/settings.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/storage.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/swd.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/usbflasher.h create mode 100644 Tag_Flasher/ESP32_Flasher/include/zbs_interface.h create mode 100644 Tag_Flasher/ESP32_Flasher/platformio.ini create mode 100644 Tag_Flasher/ESP32_Flasher/src/CMakeLists.txt create mode 100644 Tag_Flasher/ESP32_Flasher/src/flasher.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/leds.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/main.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/powermgt.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/storage.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/swd.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/usbflasher.cpp create mode 100644 Tag_Flasher/ESP32_Flasher/src/zbs_interface.cpp diff --git a/ESP32_AP-Flasher/OEPL-Flash.py b/ESP32_AP-Flasher/OEPL-Flash.py deleted file mode 100644 index 31591929..00000000 --- a/ESP32_AP-Flasher/OEPL-Flash.py +++ /dev/null @@ -1,318 +0,0 @@ -import argparse -import serial.tools.list_ports -import serial -import time -from intelhex import IntelHex -import os.path - -CMD_GET_VERSION = 1 -CMD_RESET_ESP = 2 -CMD_RESET = 11 -CMD_SET_POWER = 13 - -CMD_ERASE_FLASH = 26 -CMD_ERASE_INFOPAGE = 27 -CMD_SAVE_MAC_FROM_FW = 40 -CMD_PASS_THROUGH = 50 - -CMD_SELECT_ZBS243 = 60 -CMD_SELECT_NRF82511 = 61 - -CMD_SELECT_PORT = 70 - -CMD_READ_INFOPAGE = 80 -CMD_READ_FLASH = 81 -CMD_WRITE_INFOPAGE = 82 -CMD_WRITE_FLASH = 83 -CMD_AUTOFLASH = 87 -CMD_COMPLETE = 88 - - -def read_binary_file(file_path): - with open(file_path, 'rb') as file: - binary_data = file.read() - return binary_data - - -def read_hex_file(file_path): - ih = IntelHex(file_path) - binary_data = ih.tobinarray() - return bytes(binary_data) - - -def to_byte(input, number=4): - return input.to_bytes(number, byteorder='big') - - -def send_cmd(cmd, data): - return_data = to_byte(cmd, 1) + to_byte(len(data), 4) + data - crc_val = 0xAB34 - for x in return_data: - crc_val += x - return_data = b"AT" + return_data + to_byte(crc_val & 0xffff, 2) - ser.write(return_data) - - -def wait_for_command(): - start_time = time.time() - ser.timeout = 5 # Set the timeout to 1 second - while True: - if ser.in_waiting > 0: - command = ser.read(2) # Read the "AT" prefix - if command == b"AT": - # Read the command byte - cmd = int.from_bytes(ser.read(1), byteorder='big') - data_length = int.from_bytes( - ser.read(4), byteorder='big') # Read the data length - data = ser.read(data_length) # Read the data - # Read the CRC value - crc = int.from_bytes(ser.read(2), byteorder='big') - # Verify CRC - crc_val = 0xAB34 - for x in to_byte(cmd, 1) + to_byte(data_length, 4) + data: - crc_val += x - if crc_val & 0xffff == crc: - return cmd, data - else: - print("Invalid CRC. Discarding command. Got " + - str(crc_val) + " but was expecting "+str(crc)) - print("data was:"+str(data)) - if time.time() - start_time > ser.timeout: - print("timeout waiting for reply") - return None, None - - -def list_available_com_ports(): - ports = serial.tools.list_ports.comports() - available_ports = [port.device for port in ports] - print("Specify a serial port to use with -p ") - print("available COM ports:") - for port in available_ports: - print(port) - - -def validate_arguments(args): - if not (args.nrf82511 or args.zbs243): - print("Either -nrf82511 or -zbs243 option is required.") - return False - if not (args.internalap or args.external or args.altradio): - print("One of -internalap, -external, or -altradio options is required.") - return False - if args.command in ["read", "write"] and not (args.flash or args.infopage): - print("One of --flash or --infopage arguments is required for read and write commands.") - return False - if ((args.command == "debug" or args.pt) and not (args.external)): - print("Debugging/Passthrough is only available on the external port!") - return False - if args.command == "write" and not os.path.isfile(args.filename): - print("Couldn't find the specified file!") - return False; - if args.command in ["read", "write"] and not args.filename: - print("Please specify a file to save read data") - return False; - if args.command == "read" and len(args.filename) < 2: - print("Please specify a file to save read data") - return False; - return True - -def read_from_serial(port, filename, flash): - if flash: - print( - f"Reading flash data and saving to file: {filename}.") - else: - print( - f"Reading info page and saving to file: {filename}.") - - print("Now downloading...\n") - file = open(filename, "wb") - bytecount = 0 - while True: - - print(f'\r{bytecount} bytes', end='', flush=True) - if (flash): - send_cmd(CMD_READ_FLASH, bytearray([])) - else: - send_cmd(CMD_READ_INFOPAGE, bytearray([])) - - cmd, answer = wait_for_command() - if ((cmd == CMD_READ_FLASH) or (cmd == CMD_READ_INFOPAGE)): - file.write(bytearray(answer)) - bytecount += len(answer) - elif (cmd == CMD_COMPLETE): - file.close() - print("\r All done! \nFile saved\n", end='', flush=True) - break - else: - print("Failed reading block, timeout?") - -def write_to_serial(port, filename, flash): - - if flash: - print(f"\nErasing flash... ") - send_cmd(CMD_ERASE_FLASH, bytearray([])) - else: - print(f"\nErasing infopage... ") - send_cmd(CMD_ERASE_INFOPAGE, bytearray([])) - - cmd, answer = wait_for_command() - if ((cmd == CMD_ERASE_FLASH) or (cmd == CMD_ERASE_INFOPAGE)): - print("DONE!\n") - else: - print("\nFailed to erase the flash?") - exit(0) - - # TODO: Implement logic to write data from the file to the serial port with flash option - if flash: - print(f"Writing flash data from file: {filename}\n") - else: - print(f"Writing info page data from file: {filename}\n") - - chunk_size = 1024 - - if filename.endswith('.bin'): - file_data = read_binary_file(filename) - elif filename.endswith('.hex'): - file_data = read_hex_file(filename) - - for i in range(0, len(file_data), chunk_size): - chunk = file_data[i:i + chunk_size] - if (flash): - send_cmd(CMD_WRITE_FLASH, bytearray(chunk)) - else: - send_cmd(CMD_WRITE_INFOPAGE, bytearray(chunk)) - cmd, answer = wait_for_command() - if ((cmd == CMD_WRITE_FLASH) or (cmd == CMD_WRITE_INFOPAGE)): - print(f'\rSent {i} bytes', end='', flush=True) - elif (cmd == CMD_COMPLETE): - print( - '\Tried to write more bytes than we have room for! \n', end='', flush=True) - return - else: - print("Some other error, dunno\n") - return - print('\rAll done writing! ', end='', flush=True) - -def short_passthough(period_time): - start_time = time.time() - while time.time() - start_time < period_time: - data = ser.read() - if data: - print(data.decode(), end='') - if chr(0x04) in data.decode(): - break - -def main(): - try: - parser = argparse.ArgumentParser( - description="OpenEPaperLink Flasher for AP/Flasher board") - parser.add_argument("-p", "--port", help="COM port to use") - parser.add_argument("command", choices=[ - "read", "write", "autoflash", "debug"], help="Command to execute") - parser.add_argument("filename", nargs="?", - help="Filename for read/write commands") - parser.add_argument("-f", "--flash", action="store_true", - help="Write to the flash") - parser.add_argument("-i", "--infopage", action="store_true", - help="Write to the infopage/UICR") - parser.add_argument("-n", "--nrf82511", action="store_true", - help="nRF82511 programming") - parser.add_argument("-z", "--zbs243", action="store_true", - help="ZBS243 programming") - parser.add_argument("--internalap", action="store_true", - help="Selects the internal accesspoint port") - parser.add_argument("-e", "--external", action="store_true", - help="Selects the external(side) port") - parser.add_argument("--altradio", action="store_true", - help="Selects the alternate radio port") - parser.add_argument("--pt","--passthrough", action="store_true", - help="Enters serial passthrough for debug output after flashing") - - args = parser.parse_args() - - if not validate_arguments(args): - return - - if not args.port: - list_available_com_ports() - return - - global ser - ser = serial.Serial(args.port, baudrate=500000) - time.sleep(1) # Flush serial data - while (ser.inWaiting() > 0): - data_str = ser.read(ser.inWaiting()) - - send_cmd(CMD_GET_VERSION, bytearray([])) - cmd, answer = wait_for_command() - if (cmd == CMD_GET_VERSION): - print("AP/Flasher version: " + - str(answer[0] << 24 | answer[1] << 16 | answer[2] << 8 | answer[3])) - else: - print( - "Couldn't establish a connection with the flasher, did you select the correct serial port?") - exit(0) - - if (args.command!="debug"): - if args.internalap: - send_cmd(CMD_SELECT_PORT, bytearray([0])) - if args.external: - send_cmd(CMD_SELECT_PORT, bytearray([1])) - if args.altradio: - send_cmd(CMD_SELECT_PORT, bytearray([2])) - - if args.nrf82511: - send_cmd(CMD_SELECT_NRF82511, bytearray([])) - if args.zbs243: - send_cmd(CMD_SELECT_ZBS243, bytearray([])) - - cmd, answer = wait_for_command() - if (answer[0] == 1): - print("Connection established to microcontroller") - else: - print("Failed to establish a connection to the microcontroller") - exit(0) - - if args.command == "read": - read_from_serial(ser, args.filename, args.flash) - elif args.command == "write": - write_to_serial(ser, args.filename, args.flash) - elif args.command == "autoflash": - print("Starting automatic tag flash") - send_cmd(CMD_AUTOFLASH , bytearray([])) - short_passthough(30) - else: - print("Invalid command!") - - send_cmd(CMD_RESET, bytearray([])) - cmd, answer = wait_for_command() - send_cmd(CMD_SET_POWER, bytearray([1])) - cmd, answer = wait_for_command() - - - if (args.pt or args.command=="debug"): - # enter passthrough mode - send_cmd(CMD_PASS_THROUGH , bytearray([])) - print("Now showing debug output from the tag - CTRL+C to quit"); - print("---------------------------------------------------------------------------------"); - while True: - try: - data = ser.read() - if data: - print(data.decode('utf-8', errors='replace'), end='') - except UnicodeDecodeError: - print(" ") - data = ser.read() - if data: - print(data.decode(), end='') - if chr(0x04) in data.decode(): - break - - - except KeyboardInterrupt: - print("\nBye!") - ser.close() - exit(1) - - -if __name__ == "__main__": - main() diff --git a/Tag_Flasher/ESP32_Flasher/.clang-format b/Tag_Flasher/ESP32_Flasher/.clang-format new file mode 100644 index 00000000..e2b830c7 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/.clang-format @@ -0,0 +1,4 @@ +Language: Cpp +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 0 \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/.gitignore b/Tag_Flasher/ESP32_Flasher/.gitignore new file mode 100644 index 00000000..89cc49cb --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/Tag_Flasher/ESP32_Flasher/.vscode/extensions.json b/Tag_Flasher/ESP32_Flasher/.vscode/extensions.json new file mode 100644 index 00000000..080e70d0 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/Tag_Flasher/ESP32_Flasher/.vscode/settings.json b/Tag_Flasher/ESP32_Flasher/.vscode/settings.json new file mode 100644 index 00000000..ea185af3 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/.vscode/settings.json @@ -0,0 +1,55 @@ +{ + "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}", + "files.associations": { + "array": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "map": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "regex": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/CMakeLists.txt b/Tag_Flasher/ESP32_Flasher/CMakeLists.txt new file mode 100644 index 00000000..be4981fa --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16.0) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp32_fw) diff --git a/Tag_Flasher/ESP32_Flasher/include/flasher.h b/Tag_Flasher/ESP32_Flasher/include/flasher.h new file mode 100644 index 00000000..cbd78e79 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/flasher.h @@ -0,0 +1,59 @@ +#include +#include + +void flashCountDown(uint8_t c); + +#ifdef OPENEPAPERLINK_PCB +bool extTagConnected(); +bool doTagFlash(); +#endif + +class flasher { + public: + class ZBS_interface *zbs = nullptr; + uint8_t md5[16] = {0}; + char md5char[34]; + uint8_t tagtype; + uint8_t *infoblock = nullptr; + + // Infoblock structure: + // 0x00-0x0F - Calibration data + // 0x10-0x17 - MAC + // 0x19 - OpenEPaperLink Type + // 0x30 - Original firmware MD5 + + uint8_t mac[8] = {0}; + uint8_t mac_format = 0; + uint16_t mac_suffix = 0; + uint16_t mac_offset = 0; + + flasher(); + ~flasher(); + bool connectTag(uint8_t port); + void getFirmwareMD5(); + bool getFirmwareMac(); + bool findTagByMD5(); + bool findTagByType(uint8_t type); + bool getInfoBlockMD5(); + bool getInfoBlockMac(); + bool getInfoBlockType(); + void getMacFromWiFi(); + bool prepareInfoBlock(); + + bool backupFlash(); + + bool writeFlash(uint8_t *flashbuffer, uint16_t size); + bool writeFlashFromPack(String filename, uint8_t type); + bool writeFlashFromPackOffset(fs::File *file, uint16_t length); + + bool readBlock(uint16_t offset, uint8_t* data, uint16_t len, bool infopage); + bool writeBlock(uint16_t offset, uint8_t* data, uint16_t len, bool infopage); + + bool readInfoBlock(); + bool writeInfoBlock(); + + protected: + bool writeBlock256(uint16_t offset, uint8_t *flashbuffer); + void get_mac_format1(); + void get_mac_format2(); +}; diff --git a/Tag_Flasher/ESP32_Flasher/include/leds.h b/Tag_Flasher/ESP32_Flasher/include/leds.h new file mode 100644 index 00000000..56abe97c --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/leds.h @@ -0,0 +1,37 @@ +#include + +#ifdef HAS_RGB_LED +#define FASTLED_INTERNAL +#include +#endif + +const uint8_t PROGMEM gamma8[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, + 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114, + 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142, + 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175, + 177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, + 215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255}; + +void ledTask(void* parameter); +void setBrightness(int brightness); +void updateBrightnessFromConfig(); + +#ifdef HAS_RGB_LED +void shortBlink(CRGB cname); +void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree); +void rgbIdle(); +void addFadeColor(CRGB cname); +#endif + +void quickBlink(uint8_t repeat); \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/include/powermgt.h b/Tag_Flasher/ESP32_Flasher/include/powermgt.h new file mode 100644 index 00000000..958a4522 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/powermgt.h @@ -0,0 +1,5 @@ +#include + + +//void doLeds(); +void powerControl(bool powerState, uint8_t* pin, uint8_t pincount); diff --git a/Tag_Flasher/ESP32_Flasher/include/settings.h b/Tag_Flasher/ESP32_Flasher/include/settings.h new file mode 100644 index 00000000..6874ad87 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/settings.h @@ -0,0 +1,9 @@ +#include + + +#define FLASHER_EXT_PORT 1 + +// flasher options +#define CUSTOM_MAC_HDR 0x0000 + +#define MAX_WRITE_ATTEMPTS 5 \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/include/storage.h b/Tag_Flasher/ESP32_Flasher/include/storage.h new file mode 100644 index 00000000..c17713e8 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/storage.h @@ -0,0 +1,42 @@ +#ifndef _DYN_STORAGE_H_ +#define _DYN_STORAGE_H_ + +#include "FS.h" + +#ifdef HAS_SDCARD +#ifndef SD_CARD_SS +#error SD_CARD_SS UNDEFINED +#endif + +#ifndef SD_CARD_CLK +#define SD_CARD_CLK 18 +#endif + +#ifndef SD_CARD_MISO +#define SD_CARD_MISO 19 +#endif + +#ifndef SD_CARD_MOSI +#define SD_CARD_MOSI 23 +#endif +#endif + +class DynStorage { + public: + DynStorage(); + void begin(); + void end(); + void listFiles(); + size_t freeSpace(); + + private: + bool isInited; +}; + +extern DynStorage Storage; +extern fs::FS *contentFS; +extern void copyFile(File in, File out); + +#endif + + diff --git a/Tag_Flasher/ESP32_Flasher/include/swd.h b/Tag_Flasher/ESP32_Flasher/include/swd.h new file mode 100644 index 00000000..cdaab847 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/swd.h @@ -0,0 +1,83 @@ +#include +#include + +/* + Copyright (c) 2021 Aaron Christophel ATCnetz.de + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#pragma once + +class swd { + public: + swd(uint8_t swdio, uint8_t swdclk); + + uint32_t swd_Init(); + + bool AP_Write(unsigned addr, uint32_t data); + bool AP_Read(unsigned addr, uint32_t &data); + bool DP_Write(unsigned addr, uint32_t data); + bool DP_Read(unsigned addr, uint32_t &data); + + uint32_t idCode; + + protected: + void swd_Begin(); + void swd_Direction(bool WorR); + bool swd_Transfer(unsigned port_address, bool APorDP, bool RorW, uint32_t &data); + bool calculate_Parity(uint32_t in_data); + + void swd_Write(uint32_t in_data, uint8_t bits); + uint32_t swd_Read(uint8_t bits); + + uint8_t swdio_pin; + uint8_t swdclk_pin; + bool cur_swd_direction = 0; +}; + +struct nrf_info_struct { + int flash_size; + uint32_t connected; + uint32_t codepage_size; + uint32_t codesize; + uint32_t config_id; + uint32_t device_id0; + uint32_t device_id1; + uint32_t info_part; + uint32_t info_variant; + uint32_t info_package; + uint16_t sd_info_area; + uint32_t ucir_lock; +}; + +class nrfswd : protected swd { + public: + nrfswd(uint8_t swdio, uint8_t swdclk); + bool init(); + + uint8_t nrf_read_bank(uint32_t address, uint32_t buffer[], int size); + uint8_t nrf_write_bank(uint32_t address, uint32_t buffer[], int size); + uint8_t erase_all_flash(); + uint8_t erase_uicr(); + uint8_t erase_page(uint32_t page); + void nrf_soft_reset(); + + bool isConnected = false; + bool isLocked = false; + bool showDebug = true; + + struct nrf_info_struct nrf_info; + + protected: + uint32_t nrf_read_port(bool APorDP, uint8_t address); + void nrf_write_port(bool APorDP, uint8_t address, uint32_t value); + + void nrf_abort_all(); + void nrf_halt(); + + void nrf_port_selection(bool new_port); + bool nrf_read_lock_state(); + void nrf_read_ufcr(); + + void write_register(uint32_t address, uint32_t value); + uint32_t read_register(uint32_t address); +}; \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/include/usbflasher.h b/Tag_Flasher/ESP32_Flasher/include/usbflasher.h new file mode 100644 index 00000000..037b5cf3 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/usbflasher.h @@ -0,0 +1,4 @@ +#include + + +void usbFlasherTask(void* parameter); \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/include/zbs_interface.h b/Tag_Flasher/ESP32_Flasher/include/zbs_interface.h new file mode 100644 index 00000000..2bdaafa4 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/include/zbs_interface.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include + +/* Autor: Aaron Christophel ATCnetz.de */ +#include + +class ZBS_interface +{ +public: + uint8_t begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t* POWER, uint8_t powerPins, uint32_t spi_speed = 8000000); + void setSpeed(uint32_t speed); + void set_power(uint8_t state); + void enable_debug(); + void reset(); + void send_byte(uint8_t data); + uint8_t read_byte(); + void write_byte(uint8_t cmd, uint8_t addr, uint8_t data); + uint8_t read_byte(uint8_t cmd, uint8_t addr); + void write_flash(uint16_t addr, uint8_t data); + uint8_t read_flash(uint16_t addr); + void write_ram(uint8_t addr, uint8_t data); + uint8_t read_ram(uint8_t addr); + void write_sfr(uint8_t addr, uint8_t data); + uint8_t read_sfr(uint8_t addr); + uint8_t check_connection(); + uint8_t select_flash(uint8_t page); + void erase_flash(); + void erase_infoblock(); + ~ZBS_interface(); + +private: + SPIClass *spi = NULL; + SPISettings spiSettings; + uint8_t _SS_PIN = -1; + uint8_t _CLK_PIN = -1; + uint8_t _MOSI_PIN = -1; + uint8_t _MISO_PIN = -1; + uint8_t _RESET_PIN = -1; + uint8_t* _POWER_PIN = nullptr; + uint8_t _POWER_PINS = 1; + int ZBS_spi_delay = 1; + uint8_t spi_ready = 0; + uint32_t after_byte_delay = 10; + + typedef enum + { + ZBS_CMD_W_RAM = 0x02, + ZBS_CMD_R_RAM = 0x03, + ZBS_CMD_W_FLASH = 0x08, + ZBS_CMD_R_FLASH = 0x09, + ZBS_CMD_W_SFR = 0x12, + ZBS_CMD_R_SFR = 0x13, + ZBS_CMD_ERASE_FLASH = 0x88, + ZBS_CMD_ERASE_INFOBLOCK = 0x48, + } ZBS_CMD_LIST; + + typedef enum + { + ZBS_ON = 1, + ZBS_OFF = 0, + } ZBS_POWER_STATE; +}; \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/platformio.ini b/Tag_Flasher/ESP32_Flasher/platformio.ini new file mode 100644 index 00000000..f10f6bb8 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/platformio.ini @@ -0,0 +1,64 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env] +platform = espressif32 +framework = arduino +lib_deps = + https://github.com/MajenkoLibraries/SoftSPI + bblanchon/ArduinoJson + +platform_packages = +board_build.filesystem = littlefs +monitor_filters = esp32_exception_decoder +monitor_speed = 115200 +board_build.f_cpu = 240000000L +build_flags = + -D BUILD_ENV_NAME=$PIOENV + -D BUILD_TIME=$UNIX_TIME + -D USER_SETUP_LOADED + -D DISABLE_ALL_LIBRARY_WARNINGS + +; ---------------------------------------------------------------------------------------- +; !!! this configuration expects the Mini_AP +; +; ---------------------------------------------------------------------------------------- + +[env:S2_Tag_Flasher] +platform = https://github.com/platformio/platform-espressif32.git +board=lolin_s2_mini +board_build.partitions = default.csv +build_unflags = + -D ARDUINO_USB_MODE=1 + -D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +build_flags = + ${env.build_flags} + -D HAS_USB + -D OPENEPAPERLINK_NANO_AP_PCB + -D ARDUINO_USB_MODE=0 + -D CONFIG_SPIRAM_USE_MALLOC=1 + -D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y + -D BOARD_HAS_PSRAM + -D FLASHER_EXT_SS=34 + -D FLASHER_EXT_CLK=33 + -D FLASHER_EXT_MOSI=37 + -D FLASHER_EXT_MISO=35 + -D FLASHER_EXT_RESET=39 + -D FLASHER_EXT_POWER={16,17,18,21} + -D FLASHER_EXT_TXD=40 + -D FLASHER_EXT_RXD=36 + -D FLASHER_EXT_TEST=38 + -D FLASHER_LED=15 +build_src_filter = + +<*>-- +board_build.psram_type=qspi_opi +board_upload.maximum_size = 4194304 +board_upload.maximum_ram_size = 327680 +board_upload.flash_size = 4MB \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/src/CMakeLists.txt b/Tag_Flasher/ESP32_Flasher/src/CMakeLists.txt new file mode 100644 index 00000000..483bc0cf --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# This file was automatically generated for projects +# without default 'CMakeLists.txt' file. + +FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) + +idf_component_register(SRCS ${app_sources}) diff --git a/Tag_Flasher/ESP32_Flasher/src/flasher.cpp b/Tag_Flasher/ESP32_Flasher/src/flasher.cpp new file mode 100644 index 00000000..1fd2f0b7 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/flasher.cpp @@ -0,0 +1,540 @@ +#include "flasher.h" + +#include +#include +#include + +#include "LittleFS.h" +#include "storage.h" +// #include + +#include "leds.h" +#include "settings.h" +#include "time.h" +#include "zbs_interface.h" + +#define FINGERPRINT_FLASH_SIZE 10240 + +#ifdef OPENEPAPERLINK_PCB +bool extTagConnected() { + // checks if the TEST (P1.0) pin on the ZBS243 will come up high. If it doesn't, there's probably a tag connected. + pinMode(FLASHER_EXT_TEST, INPUT_PULLDOWN); + vTaskDelay(5 / portTICK_PERIOD_MS); + pinMode(FLASHER_EXT_TEST, INPUT_PULLUP); + vTaskDelay(5 / portTICK_PERIOD_MS); + return !digitalRead(FLASHER_EXT_TEST); +} +#endif + +void dump(uint8_t *a, uint16_t l) { + if (a == nullptr) { + Serial.print("Tried to dump the contents of a nullptr, this is probably not what you want.\n"); + } + Serial.printf(" "); +#define ROWS 16 + for (uint8_t c = 0; c < ROWS; c++) { + Serial.printf(" %02X", c); + } + Serial.printf("\n--------"); + for (uint8_t c = 0; c < ROWS; c++) { + Serial.printf("---"); + } + for (uint16_t c = 0; c < l; c++) { + if ((c % ROWS) == 0) { + Serial.printf("\n0x%04X | ", c); + } + Serial.printf("%02X ", a[c]); + } + Serial.printf("\n--------"); + for (uint8_t c = 0; c < ROWS; c++) { + Serial.printf("---"); + } + Serial.printf("\n"); +} + + +int8_t powerPinsExt[] = FLASHER_EXT_POWER; + +uint8_t pinsExt[] = {FLASHER_EXT_CLK, FLASHER_EXT_MISO, FLASHER_EXT_MOSI, FLASHER_EXT_RESET, FLASHER_EXT_RXD, FLASHER_EXT_SS, FLASHER_EXT_TEST, FLASHER_EXT_TXD}; + +flasher::flasher() { + zbs = new ZBS_interface; + Storage.end(); +} +flasher::~flasher() { + delete zbs; + Storage.begin(); +} + +static uint8_t validatePowerPinCount(int8_t *powerPin, uint8_t pinCount) { + + return pinCount; +} + +#ifndef FLASHER_AP_SPEED +#define FLASHER_AP_SPEED 4000000 +#endif + +bool flasher::connectTag(uint8_t port) { + bool result; + uint8_t power_pins = 0; + switch (port) { + + case 1: + power_pins = validatePowerPinCount(powerPinsExt, sizeof(powerPinsExt)); + result = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, (uint8_t *)powerPinsExt, power_pins, FLASHER_AP_SPEED); + break; + + default: + Serial.printf("Tried to connect to port %d, but this port isn't available. Some dev borked it up, probably Jelmer.\n", port); + return false; + } + if (!result) Serial.printf("I tried connecting to port %d, but I couldn't establish a link to the tag. That's all I know.\n", port); + return result; +} + +void flasher::getFirmwareMD5() { + uint8_t *buffer = (uint8_t *)malloc(FINGERPRINT_FLASH_SIZE); + if (buffer == nullptr) { + Serial.print("couldn't malloc bytes for firmware MD5\n"); + return; + } + + zbs->select_flash(0); + for (uint16_t c = 0; c < FINGERPRINT_FLASH_SIZE; c++) { + buffer[c] = zbs->read_flash(c); + } + + { + MD5Builder md5calc; + md5calc.begin(); + md5calc.add(buffer, FINGERPRINT_FLASH_SIZE); + md5calc.calculate(); + md5calc.getBytes(md5); + } + + for (uint8_t c = 0; c < 16; c++) { + sprintf(md5char + (2 * c), "%02X", md5[c]); + } + Serial.printf("MD5=%s\n", md5char); + free(buffer); +} + +bool flasher::getInfoBlockMac() { + if (!zbs->select_flash(1)) return false; + for (uint16_t c = 7; c < 8; c--) { + mac[7 - c] = zbs->read_flash(c + 0x10); + } + Serial.printf("Infopage mac="); + uint16_t macsum = 0; + for (uint8_t c = 0; c < 8; c++) { + macsum += mac[c]; + Serial.printf("%02X", mac[c]); + } + Serial.printf("\n"); + if (macsum == 0) return false; + if (macsum > 0x5F9) return false; + return true; +} + +bool flasher::getInfoBlockMD5() { + if (!zbs->select_flash(1)) return false; + for (uint16_t c = 0; c < 16; c++) { + md5[c] = zbs->read_flash(c + 0x30); + } + uint16_t macsum = 0; + for (uint8_t c = 0; c < 16; c++) { + macsum += md5[c]; + sprintf(md5char + (2 * c), "%02X", md5[c]); + } + Serial.printf("Infoblock MD5=%s\n", md5char); + if (macsum == 0) return false; // invalid mac + if (macsum > 0xF00) return false; // *probably* an invalid mac + return true; +} + +bool flasher::getInfoBlockType() { + if (!zbs->select_flash(1)) return false; + tagtype = zbs->read_flash(0x19); + return true; +} + +bool flasher::findTagByMD5() { + StaticJsonDocument<3000> doc; + DynamicJsonDocument APconfig(600); + fs::File readfile = contentFS->open("/tag_md5_db.json", "r"); + DeserializationError err = deserializeJson(doc, readfile); + if (!err) { + for (JsonObject elem : doc.as()) { + const char *jsonmd5 = elem["MD5"]; + if (jsonmd5 != nullptr) { + if (strncmp(md5char, jsonmd5, 32) == 0) { + Serial.print("MD5 Matches > "); + const char *name = elem["name"]; + Serial.println(name); + mac_suffix = strtoul(elem["mac_suffix"], 0, 16); + mac_format = elem["mac_format"]; + mac_offset = elem["mac_offset"]; + tagtype = elem["type"]; + readfile.close(); + return true; + } + } + } + Serial.print("Failed to find this tag's current firmware MD5 in the json database. If this tag is already OpenEpaperLink, this is to be expected.\n"); + } else { + Serial.print("Failed to read json file\n"); + } + readfile.close(); + return false; +} + +bool flasher::findTagByType(uint8_t type) { + StaticJsonDocument<3000> doc; + DynamicJsonDocument APconfig(600); + fs::File readfile = contentFS->open("/tag_md5_db.json", "r"); + DeserializationError err = deserializeJson(doc, readfile); + if (!err) { + for (JsonObject elem : doc.as()) { + if (elem["type"] != nullptr) { + uint8_t jtype = elem["type"]; + if (jtype == type) { + Serial.print("Type Matches > "); + const char *name = elem["name"]; + Serial.println(name); + const char *jsonmd5 = elem["MD5"]; + + for (uint8_t c = 0; c < 16; c++) { + uint32_t n = 0; + sscanf(jsonmd5 + (2 * c), "%02X", &n); + md5[c] = (uint8_t)n; + } + + for (uint8_t c = 0; c < 16; c++) { + sprintf(md5char + (2 * c), "%02X", md5[c]); + } + + mac_suffix = strtoul(elem["mac_suffix"], 0, 16); + mac_format = elem["mac_format"]; + mac_offset = elem["mac_offset"]; + tagtype = elem["type"]; + readfile.close(); + return true; + } + } + } + Serial.print("Failed to find this tag's type in the json database.\n"); + } else { + Serial.print("Failed to read json file\n"); + } + readfile.close(); + return false; +} + +bool flasher::getFirmwareMac() { + if (!mac_offset) return false; + switch (mac_format) { + case 1: + get_mac_format1(); + break; + case 2: + get_mac_format2(); + break; + default: + return false; + } + return true; +} + +void flasher::getMacFromWiFi() { + mac[0] = 0x00; + mac[1] = 0x00; + esp_read_mac(mac + 2, ESP_MAC_WIFI_SOFTAP); +} + +bool flasher::backupFlash() { + getFirmwareMD5(); + if (!zbs->select_flash(0)) return false; + md5char[16] = 0x00; + fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true); + for (uint32_t c = 0; c < 65535; c++) { + backup.write(zbs->read_flash(c)); + } + backup.close(); + return true; +} + +// extract original mac from firmware (1.54" and 2.9") and make it 2 bytes longer based on info in settings.h +void flasher::get_mac_format1() { + zbs->select_flash(0); + for (uint8_t c = 0; c < 6; c++) { + mac[c + 2] = zbs->read_flash(mac_offset + c); // 0xFC06 + } + mac[0] = (uint8_t)(CUSTOM_MAC_HDR >> 8); + mac[1] = (uint8_t)CUSTOM_MAC_HDR; + + mac[6] = (uint8_t)(mac_suffix >> 8); + mac[7] = (uint8_t)(mac_suffix & 0xFF); + + uint8_t xorchk = 0; + for (uint8_t c = 2; c < 8; c++) { + xorchk ^= (mac[c] & 0x0F); + xorchk ^= (mac[c] >> 4); + } + mac[7] |= xorchk; +} + +// extract original mac from segmented tag +void flasher::get_mac_format2() { + zbs->select_flash(0); + + for (uint8_t c = 0; c < 3; c++) { + mac[c] = zbs->read_flash(mac_offset + c); // 0x7802 + } + for (uint8_t c = 3; c < 6; c++) { + mac[c] = zbs->read_flash(mac_offset + 2 + c); + } + + uint16_t type = mac_suffix; + mac[6] = (uint8_t)(type >> 8); + mac[7] = (uint8_t)(type & 0xFF); +} + +// erase flash and program from flash buffer +bool flasher::writeFlash(uint8_t *flashbuffer, uint16_t size) { + if (!zbs->select_flash(0)) return false; + zbs->erase_flash(); + if (!zbs->select_flash(0)) return false; + Serial.printf("Starting flash, size=%d\n", size); + for (uint16_t c = 0; c < size; c++) { + if (flashbuffer[c] == 0xFF) goto flashWriteSuccess; + for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) { + zbs->write_flash(c, flashbuffer[c]); + if (zbs->read_flash(c) == flashbuffer[c]) { + goto flashWriteSuccess; + } + } + return false; + flashWriteSuccess: + if (c % 256 == 0) { +#ifdef HAS_RGB_LED + shortBlink(CRGB::Yellow); +#else + quickBlink(2); +#endif + Serial.printf("\rNow flashing, %d/%d ", c, size); + vTaskDelay(1 / portTICK_PERIOD_MS); + } + } + return true; +} + +bool flasher::writeBlock256(uint16_t offset, uint8_t *flashbuffer) { + for (uint16_t c = 0; c < 256; c++) { + if (flashbuffer[c] == 0xFF) goto flashWriteSuccess; + for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) { + zbs->write_flash(offset + c, flashbuffer[c]); + if (zbs->read_flash(offset + c) == flashbuffer[c]) { + goto flashWriteSuccess; + } + } + return false; + flashWriteSuccess: + continue; + } + return true; +} + +// get info from infoblock (eeprom flash, kinda) +bool flasher::readInfoBlock() { + if (!zbs->select_flash(1)) return false; + if (infoblock == nullptr) { + infoblock = (uint8_t *)malloc(1024); + if (infoblock == nullptr) return false; + } + for (uint16_t c = 0; c < 1024; c++) { + infoblock[c] = zbs->read_flash(c); + } + return true; +} + +// write info to infoblock +bool flasher::writeInfoBlock() { + if (infoblock == nullptr) return false; + if (!zbs->select_flash(1)) return false; + zbs->erase_infoblock(); + if (!zbs->select_flash(1)) return false; + // select info page + + for (uint16_t c = 0; c < 1024; c++) { + if (infoblock[c] == 0xFF) goto ifBlockWriteSuccess; + for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) { + zbs->write_flash(c, infoblock[c]); + if (zbs->read_flash(c) == infoblock[c]) { + goto ifBlockWriteSuccess; + } + } + return false; + ifBlockWriteSuccess: + continue; + } + return true; +} + +bool flasher::prepareInfoBlock() { + if (infoblock == nullptr) return false; + for (uint8_t c = 7; c < 8; c--) { + infoblock[0x10 + (7 - c)] = mac[c]; + } + infoblock[0x19] = tagtype; + for (uint8_t c = 0; c < 16; c++) { + infoblock[0x30 + c] = md5[c]; + } + return true; +} + +bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) { + if (!zbs->select_flash(0)) return false; + zbs->erase_flash(); + if (!zbs->select_flash(0)) return false; + Serial.printf("Starting flash, size=%d\n", length); + + uint8_t *buf = (uint8_t *)malloc(256); + uint16_t offset = 0; + while (length) { + if (length > 256) { + file->read(buf, 256); + length -= 256; + } else { + file->read(buf, length); + length = 0; + } +#ifdef HAS_RGB_LED + shortBlink(CRGB::Yellow); +#else + quickBlink(2); +#endif + Serial.printf("\rFlashing, %d bytes left ", length); + bool res = writeBlock256(offset, buf); + offset += 256; + if (!res) { + Serial.printf("Failed writing block to tag, probably a hardware failure\n"); + return false; + } + vTaskDelay(1 / portTICK_PERIOD_MS); + } + Serial.printf("\nFlashing done\n"); + return true; +} + +bool flasher::writeFlashFromPack(String filename, uint8_t type) { + StaticJsonDocument<512> doc; + DynamicJsonDocument APconfig(512); + fs::File readfile = contentFS->open(filename, "r"); + DeserializationError err = deserializeJson(doc, readfile); + if (!err) { + for (JsonObject elem : doc.as()) { + if (elem["type"] != nullptr) { + uint8_t jtype = elem["type"]; + if (jtype == type) { + const char *name = elem["name"]; + Serial.print("Flashing from FW pack: "); + Serial.println(name); + + uint32_t offset = elem["offset"]; + uint16_t length = elem["length"]; + readfile.seek(offset); + bool result = writeFlashFromPackOffset(&readfile, length); + readfile.close(); + return result; + } + } + } + Serial.print("Failed to find this tag's type in the FW pack database.\n"); + } else { + Serial.print("Failed to read json header from FW pack\n"); + } + readfile.close(); + return false; +} + +bool flasher::readBlock(uint16_t offset, uint8_t *data, uint16_t len, bool infopage) { + if (infopage) { + if (!zbs->select_flash(1)) return false; + if (offset > 1024) return false; + } else { + if (!zbs->select_flash(0)) return false; + if (offset > 65535) return false; + } + for (uint32_t c = 0; c < len; c++) { + data[c] = zbs->read_flash(offset + c); + } + return true; +} + +bool flasher::writeBlock(uint16_t offset, uint8_t *data, uint16_t len, bool infopage) { + if (infopage) { + if (!zbs->select_flash(1)) return false; + if (offset > 1024) return false; + } else { + if (!zbs->select_flash(0)) return false; + if (offset > 65535) return false; + } + for (uint32_t c = 0; c < len; c++) { + zbs->write_flash(c + offset, data[c]); + } + return true; +} + + +void flashCountDown(uint8_t c) { + Serial.printf("\r%d ", c); + for (c -= 1; c < 254; c--) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + Serial.printf("\r%d ", c); + } +} + +// perform device flash, save mac, everything +bool doTagFlash() { + class flasher *f = new flasher(); + if (!f->connectTag(FLASHER_EXT_PORT)) { + Serial.printf("Sorry, failed to connect to this tag...\n"); + return false; + } + + f->getFirmwareMD5(); + + if (f->findTagByMD5()) { + // this tag currently contains original firmware, found its fingerprint + Serial.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char); + f->readInfoBlock(); + f->getFirmwareMac(); + f->prepareInfoBlock(); + f->writeInfoBlock(); + f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype); + f->zbs->reset(); + } else if (f->getInfoBlockMD5()) { + // did find an infoblock MD5 that looks valid + if (f->findTagByMD5()) { + // did find the md5 in the database + Serial.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char); + f->getInfoBlockMac(); + f->getInfoBlockType(); + f->readInfoBlock(); + f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype); + f->zbs->reset(); + } else { + // couldn't find the md5 from the infoblock + Serial.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char); + return false; + } + } else { + // We couldn't recognize the tag from it's fingerprint... + Serial.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char); + f->backupFlash(); + Serial.printf("Saved this MD5 binary to filesystem\n"); + } + delete f; + return false; +} diff --git a/Tag_Flasher/ESP32_Flasher/src/leds.cpp b/Tag_Flasher/ESP32_Flasher/src/leds.cpp new file mode 100644 index 00000000..c8fa22e8 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/leds.cpp @@ -0,0 +1,331 @@ +#include + +#ifdef HAS_RGB_LED +#define FASTLED_INTERNAL +#include +#endif + +#include "leds.h" +#include "settings.h" + +QueueHandle_t ledQueue; +int maxledbrightness = 255; + +#ifdef HAS_RGB_LED +QueueHandle_t rgbLedQueue; + +struct ledInstructionRGB { + CRGB ledColor; + uint16_t fadeTime; + uint16_t length; + bool reQueue = false; +}; + +CRGB leds[1]; +volatile bool rgbQueueFlush = false; +#endif + +struct ledInstruction { + uint16_t value; + uint16_t fadeTime; + uint16_t length; + bool reQueue = false; +}; + +#ifdef HAS_RGB_LED + +void addToRGBQueue(struct ledInstructionRGB* rgb, bool requeue) { + rgb->reQueue = requeue; + if (!rgbLedQueue) { + delete rgb; + return; + } + BaseType_t queuestatus = xQueueSend(rgbLedQueue, &rgb, 0); + if (queuestatus == pdFALSE) { + delete rgb; + } +} + +void addFadeColor(CRGB cname) { + struct ledInstructionRGB* rgb = new struct ledInstructionRGB; + rgb->ledColor = cname; + rgb->fadeTime = 750; + rgb->length = 0; + addToRGBQueue(rgb, false); +} + +void shortBlink(CRGB cname) { + struct ledInstructionRGB* rgb = new struct ledInstructionRGB; + rgb->ledColor = CRGB::Black; + rgb->fadeTime = 0; + rgb->length = 3; + addToRGBQueue(rgb, false); + rgb = new struct ledInstructionRGB; + rgb->ledColor = cname; + rgb->ledColor.maximizeBrightness(0x80); + rgb->fadeTime = 0; + rgb->length = 10; + addToRGBQueue(rgb, false); + rgb = new struct ledInstructionRGB; + rgb->ledColor = CRGB::Black; + rgb->fadeTime = 0; + rgb->length = 3; + addToRGBQueue(rgb, false); +} + +void flushRGBQueue() { + rgbQueueFlush = true; +} + +void rgbIdle() { + flushRGBQueue(); +} + +void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree) { + struct ledInstructionRGB* rgb = new struct ledInstructionRGB; + rgb->ledColor = CRGB::Black; + rgb->fadeTime = 0; + rgb->length = 600; + addToRGBQueue(rgb, true); + rgb = new struct ledInstructionRGB; + rgb->ledColor = colorone; + rgb->fadeTime = 0; + rgb->length = 120; + addToRGBQueue(rgb, true); + rgb = new struct ledInstructionRGB; + rgb->ledColor = CRGB::Black; + rgb->fadeTime = 0; + rgb->length = 200; + addToRGBQueue(rgb, true); + rgb = new struct ledInstructionRGB; + rgb->ledColor = colortwo; + rgb->fadeTime = 0; + rgb->length = 120; + addToRGBQueue(rgb, true); + rgb = new struct ledInstructionRGB; + rgb->ledColor = CRGB::Black; + rgb->fadeTime = 0; + rgb->length = 200; + addToRGBQueue(rgb, true); + rgb = new struct ledInstructionRGB; + rgb->ledColor = colorthree; + rgb->fadeTime = 0; + rgb->length = 120; + addToRGBQueue(rgb, true); +} + +void showRGB() { + FastLED.show(); +} + +volatile CRGB rgbIdleColor = CRGB::Green; +volatile uint16_t rgbIdlePeriod = 800; + +void rgbIdleStep() { + static bool dirUp = true; + static uint16_t step = 0; + + if (dirUp) { + // up + step++; + if (step == rgbIdlePeriod) { + dirUp = false; + } + } else { + // down + step--; + if (step == 0) { + dirUp = true; + } + } + CRGB newvalue = blend(CRGB::Black, (const CRGB&)rgbIdleColor, gamma8[map(step, 0, rgbIdlePeriod, 0, 255)]); + if (newvalue != leds[0]) { + leds[0] = newvalue; + showRGB(); + } +} +#endif + +void setBrightness(int brightness) { + maxledbrightness = brightness; +#ifdef HAS_RGB_LED + FastLED.setBrightness(maxledbrightness); +#endif +} + +void addToMonoQueue(struct ledInstruction* mono) { + BaseType_t queuestatus = xQueueSend(ledQueue, &mono, 0); + if (queuestatus == pdFALSE) { + delete mono; + } +} + +void addFadeMono(uint8_t value) { + struct ledInstruction* mono = new struct ledInstruction; + mono->value = value; + mono->fadeTime = 750; + mono->length = 0; + addToMonoQueue(mono); +} + +void showMono(uint8_t brightness) { +#ifdef CONFIG_IDF_TARGET_ESP32S2 + ledcWrite(7, gamma8[brightness]); +#else + ledcWrite(7, 255 - gamma8[brightness]); +#endif +} + +void quickBlink(uint8_t repeat) { + for (int i = 0; i < repeat; i++) { + struct ledInstruction* mono = new struct ledInstruction; + mono->value = maxledbrightness; + mono->fadeTime = 120 / repeat; + mono->length = 0; + addToMonoQueue(mono); + mono = new struct ledInstruction; + mono->value = 0; + mono->fadeTime = 120 / repeat; + mono->length = 0; + addToMonoQueue(mono); + } +} + +volatile uint16_t monoIdlePeriod = 900; + +uint8_t monoValue = 0; + +void monoIdleStep() { + static bool dirUp = true; + static uint16_t step = 0; + if (dirUp) { + // up + step++; + if (step == monoIdlePeriod) { + dirUp = false; + } + } else { + // down + step--; + if (step == 0) { + dirUp = true; + } + } + uint8_t newvalue = map(step, 0, monoIdlePeriod, 0, maxledbrightness); + if (newvalue != monoValue) { + monoValue = newvalue; + showMono(newvalue); + } +} + +void ledTask(void* parameter) { +#ifdef HAS_RGB_LED + FastLED.addLeds(leds, 1); // GRB ordering is typical + leds[0] = CRGB::Blue; + showRGB(); + rgbLedQueue = xQueueCreate(30, sizeof(struct ledInstructionRGB*)); + + struct ledInstructionRGB* rgb = nullptr; + // open with a nice RGB crossfade + addFadeColor(CRGB::Red); + addFadeColor(CRGB::Green); + addFadeColor(CRGB::Blue); + addFadeColor(CRGB::Red); + addFadeColor(CRGB::Green); + addFadeColor(CRGB::Blue); + CRGB oldColor = CRGB::Black; + uint16_t rgbInstructionFadeTime = 0; +#endif + + ledQueue = xQueueCreate(30, sizeof(struct ledInstruction*)); + + ledcSetup(7, 5000, 8); + if (FLASHER_LED != -1) { + digitalWrite(FLASHER_LED, HIGH); + pinMode(FLASHER_LED, OUTPUT); + ledcAttachPin(FLASHER_LED, 7); + } + + struct ledInstruction* monoled = nullptr; + + addFadeMono(0); + addFadeMono(maxledbrightness); + addFadeMono(0); + + uint8_t oldBrightness = 0; + + uint16_t monoInstructionFadeTime = 0; + + while (1) { +#ifdef HAS_RGB_LED + // handle RGB led instructions + if (rgb == nullptr) { + // fetch a led instruction + BaseType_t q = xQueueReceive(rgbLedQueue, &rgb, 1); + if (q == pdTRUE) { + if (rgb->reQueue && !rgbQueueFlush) { + // requeue this instruction at the end of the queue, caveman style. + struct ledInstructionRGB* requeue = new ledInstructionRGB; + requeue->fadeTime = rgb->fadeTime; + requeue->ledColor = rgb->ledColor; + requeue->length = rgb->length; + addToRGBQueue(requeue, true); + } + + if (rgbQueueFlush) { + delete rgb; + rgb = nullptr; + } else { + rgbInstructionFadeTime = rgb->fadeTime; + if (rgb->fadeTime <= 1) { + leds[0] = rgb->ledColor; + showRGB(); + } + } + } else { + rgbQueueFlush = false; + // no commands, run idle led task + rgbIdleStep(); + } + } else { + // process instruction + if (rgb->fadeTime) { + rgb->fadeTime--; + leds[0] = blend(rgb->ledColor, oldColor, map(rgb->fadeTime, 0, rgbInstructionFadeTime, 0, 255)); + showRGB(); + } else if (rgb->length) { + rgb->length--; + } else { + oldColor = rgb->ledColor; + delete rgb; + rgb = nullptr; + } + } +#endif + // handle flasher LED (single color) + if (monoled == nullptr) { + BaseType_t q = xQueueReceive(ledQueue, &monoled, 1); + if (q == pdTRUE) { + monoInstructionFadeTime = monoled->fadeTime; + if (monoled->fadeTime <= 1) { + showMono(monoled->value); + } + } else { + // monoIdleStep(); + } + } else { + if (monoled->fadeTime) { + monoled->fadeTime--; + showMono(map(monoled->fadeTime, 0, monoInstructionFadeTime, monoled->value, oldBrightness)); + } else if (monoled->length) { + monoled->length--; + } else { + oldBrightness = monoled->value; + delete monoled; + monoled = nullptr; + } + } + + vTaskDelay(1 / portTICK_PERIOD_MS); + } +} \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/src/main.cpp b/Tag_Flasher/ESP32_Flasher/src/main.cpp new file mode 100644 index 00000000..7c6ade60 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/main.cpp @@ -0,0 +1,51 @@ + +#include +#include +#include + +#include "flasher.h" +#include "leds.h" +#include "settings.h" +#include "udp.h" +#include "usbflasher.h" +#include "powermgt.h" + +void setup() { + Serial.begin(115200); + Serial.print(">\n"); + + xTaskCreate(ledTask, "ledhandler", 2000, NULL, 2, NULL); + vTaskDelay(10 / portTICK_PERIOD_MS); + + // this allows us to view the booting process. After the device showing up, you have 3 seconds to open a terminal on the COM port + vTaskDelay(3000 / portTICK_PERIOD_MS); + // Specifically for the Mini-version (using an ESP32-S2), use another serial port for debug output. Makes it possible to see core dumps + Serial0.begin(115200, SERIAL_8N1, 2, 3); + Serial0.printf("Started debug output...\n"); + Serial0.setDebugOutput(true); + + // pinTest(); + if (!psramInit()) { + Serial.printf("This build of the AP expects PSRAM, but we couldn't find/init any. Something is terribly wrong here! System halted."); + while (1) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + }; + heap_caps_malloc_extmem_enable(64); + + int8_t powerPins2[] = FLASHER_EXT_POWER; + powerControl(0, (uint8_t*)powerPins2, 4); + + // We'll need to start the 'usbflasher' task for boards with a second (USB) port. This can be used as a 'flasher' interface, using a python script on the host + pinMode(FLASHER_EXT_RESET, OUTPUT); + digitalWrite(FLASHER_EXT_RESET, HIGH); + + // pinMode(FLASHER_EXT_TEST, OUTPUT); + // digitalWrite(FLASHER_EXT_TEST, HIGH); + + xTaskCreate(usbFlasherTask, "usbflasher", 10000, NULL, configMAX_PRIORITIES - 10, NULL); +} + +void loop() { + vTaskDelay(10000 / portTICK_PERIOD_MS); +} \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/src/powermgt.cpp b/Tag_Flasher/ESP32_Flasher/src/powermgt.cpp new file mode 100644 index 00000000..e340689d --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/powermgt.cpp @@ -0,0 +1,31 @@ +#include "powermgt.h" + +#include + +#include "settings.h" + +#ifdef OPENEPAPERLINK_PCB +#include "soc/rtc_cntl_reg.h" +#include "soc/soc.h" +#endif + +void simpleAPPower(uint8_t* pin, uint8_t pincount, bool state) { + for (uint8_t c = 0; c < pincount; c++) { + pinMode(pin[c], INPUT); + } + for (uint8_t c = 0; c < pincount; c++) { + digitalWrite(pin[c], state); + } + for (uint8_t c = 0; c < pincount; c++) { + pinMode(pin[c], OUTPUT); + } +} + +void powerControl(bool powerState, uint8_t* pin, uint8_t pincount) { + if (pincount == 0) return; + if (pin == nullptr) return; + + pincount = 4; + simpleAPPower(pin, pincount, powerState); + +} \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/src/storage.cpp b/Tag_Flasher/ESP32_Flasher/src/storage.cpp new file mode 100644 index 00000000..b251be68 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/storage.cpp @@ -0,0 +1,216 @@ +#include "storage.h" + +#ifdef HAS_SDCARD +#include "FS.h" +#include "SD.h" +#include "SPI.h" +#endif + +#include "LittleFS.h" + +DynStorage::DynStorage() : isInited(0) {} + +static void initLittleFS() { + LittleFS.begin(); + contentFS = &LittleFS; +} + +#ifdef HAS_SDCARD +static SPIClass* spi; + +static void initSDCard() { + uint8_t spi_bus = VSPI; + + // SD.begin and spi.begin are allocating memory so we dont want to do that + if(!spi) { + spi = new SPIClass(spi_bus); + spi->begin(SD_CARD_CLK, SD_CARD_MISO, SD_CARD_MOSI, SD_CARD_SS); + + bool res = SD.begin(SD_CARD_SS, *spi, 40000000); + if (!res) { + Serial.println("Card Mount Failed"); + return; + } + } + + uint8_t cardType = SD.cardType(); + + if (cardType == CARD_NONE) { + Serial.println("No SD card attached"); + return; + } + + contentFS = &SD; +} +#endif + +size_t DynStorage::freeSpace(){ + this->begin(); +#ifdef HAS_SDCARD + return SD.totalBytes() - SD.usedBytes(); +#endif + return LittleFS.totalBytes() - LittleFS.usedBytes(); +} + +void copyFile(File in, File out) { + Serial.print("Copying "); + Serial.print(in.path()); + Serial.print(" to "); + Serial.println(out.path()); + + size_t n; + uint8_t buf[64]; + while ((n = in.read(buf, sizeof(buf))) > 0) { + out.write(buf, n); + } +} + +#ifdef HAS_SDCARD + +void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) { + File root = sourceFS.open(source_path); + char next_path[128]; + + if (root.isDirectory()) { + if (!contentFS->exists(root.path())) { + if (!contentFS->mkdir(root.path())) { + Serial.print("Failed to create directory "); + Serial.println(root.path()); + return; + } + } + File file = root.openNextFile(); + while (file) { + if (file.isDirectory()) { + sprintf(next_path, "%s/%s\0", root.path(), file.path()); + + copyBetweenFS(sourceFS, file.path(), targetFS); + } else { + File target = contentFS->open(file.path(), "w"); + if (target) { + copyFile(file, target); + target.close(); + file.close(); + } else { + Serial.print("Couldn't create high target file"); + Serial.println(file.path()); + return; + } + } + file = root.openNextFile(); + } + } else { + File target = contentFS->open(root.path(), "w"); + if (target) { + copyFile(root, target); + } else { + Serial.print("Couldn't create target file "); + Serial.println(root.path()); + return; + } + } +} + +void copyIfNeeded(const char* path) { + if (!contentFS->exists(path) && LittleFS.exists(path)) { + Serial.printf("SDCard does not contain %s, littleFS does, copying\n", path); + copyBetweenFS(LittleFS, path, *contentFS); + } +} +#endif + +void DynStorage::begin() { + initLittleFS(); + +#ifdef HAS_SDCARD + initSDCard(); + + copyIfNeeded("/index.html"); + copyIfNeeded("/fonts"); + copyIfNeeded("/www"); + copyIfNeeded("/tagtypes"); + copyIfNeeded("/AP_FW_Pack.bin"); + copyIfNeeded("/tag_md5_db.json"); + copyIfNeeded("/update_actions.json"); + copyIfNeeded("/content_template.json"); +#endif + + if (!contentFS->exists("/current")) { + contentFS->mkdir("/current"); + } + if (!contentFS->exists("/temp")) { + contentFS->mkdir("/temp"); + } +} + +void DynStorage::end() { +#ifdef HAS_SDCARD + initLittleFS(); + if (SD_CARD_CLK == FLASHER_AP_CLK || + SD_CARD_MISO == FLASHER_AP_MISO || + SD_CARD_MOSI == FLASHER_AP_MOSI) { + Serial.println("Tearing down SD card connection"); + + copyBetweenFS(*contentFS, "/tag_md5_db.json", LittleFS); + copyBetweenFS(*contentFS, "/AP_FW_Pack.bin", LittleFS); + if (contentFS->exists("/AP_force_flash.bin")) { + copyBetweenFS(*contentFS, "/AP_force_flash.bin", LittleFS); + contentFS->remove("/AP_force_flash.bin"); + } + Serial.println("Swapping to LittleFS"); + + contentFS = &LittleFS; + } + +#endif +} + +void listDir(fs::FS& fs, const char* dirname, uint8_t levels) { + Storage.begin(); + Serial.printf(" \n "); + + Serial.printf("Listing directory: %s\n", dirname); + + File root = fs.open(dirname); + if (!root) { + Serial.println("Failed to open directory"); + return; + } + if (!root.isDirectory()) { + Serial.println("Not a directory"); + return; + } + + File file = root.openNextFile(); + while (file) { + if (!strcmp("System Volume Information", file.name())) { + file = root.openNextFile(); + continue; + } + + if (file.isDirectory()) { + Serial.print(" DIR : "); + Serial.println(file.name()); + if (levels) { + listDir(fs, file.path(), levels - 1); + } + Serial.println(); + } else { + Serial.print(" FILE: "); + Serial.print(file.name()); + Serial.print(" SIZE: "); + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void DynStorage::listFiles() { + listDir(LittleFS, "/", 1); +#ifdef HAS_SDCARD + listDir(*contentFS, "/", 1); +#endif +} + +fs::FS* contentFS; +DynStorage Storage; diff --git a/Tag_Flasher/ESP32_Flasher/src/swd.cpp b/Tag_Flasher/ESP32_Flasher/src/swd.cpp new file mode 100644 index 00000000..7508a796 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/swd.cpp @@ -0,0 +1,393 @@ +/* + Copyright (c) 2021 Aaron Christophel ATCnetz.de + SPDX-License-Identifier: GPL-3.0-or-later +*/ +#include "swd.h" + +#include "Arduino.h" + +// Many thanks to scanlime for the work on the ESP8266 SWD Library, parts of this code have inspiration and help from it +// https://github.com/scanlime/esp8266-arm-swd + +#define DP_IDCODE 0x00 + +swd::swd(uint8_t swdio, uint8_t swdclk) { + this->swdio_pin = swdio; + this->swdclk_pin = swdclk; + this->swd_Begin(); +} +void swd::swd_Begin() { + pinMode(this->swdclk_pin, OUTPUT); + pinMode(this->swdio_pin, INPUT_PULLUP); +} +uint32_t swd::swd_Init() { // Returns the ID + swd_Write(0xffffffff, 32); + swd_Write(0xffffffff, 32); + swd_Write(0xe79e, 16); + swd_Write(0xffffffff, 32); + swd_Write(0xffffffff, 32); + swd_Write(0, 32); + swd_Write(0, 32); + + DP_Read(DP_IDCODE, this->idCode); + return this->idCode; +} +bool swd::AP_Write(unsigned addr, uint32_t data) { + uint8_t retry = 15; + while (retry--) { + bool state = swd_Transfer(addr, 1, 0, data); + if (state) + return true; + } + return false; +} +bool swd::AP_Read(unsigned addr, uint32_t &data) { + uint8_t retry = 15; + while (retry--) { + bool state = swd_Transfer(addr, 1, 1, data); + if (state) + return true; + } + return false; +} +bool swd::DP_Write(unsigned addr, uint32_t data) { + uint8_t retry = 15; + while (retry--) { + bool state = swd_Transfer(addr, 0, 0, data); + if (state) + return true; + } + return false; +} +bool swd::DP_Read(unsigned addr, uint32_t &data) { + uint8_t retry = 15; + while (retry--) { + bool state = swd_Transfer(addr, 0, 1, data); + if (state) + return true; + } + return false; +} +bool swd::swd_Transfer(unsigned port_address, bool APorDP, bool RorW, uint32_t &data) { + bool parity = APorDP ^ RorW ^ ((port_address >> 2) & 1) ^ ((port_address >> 3) & 1); + uint8_t filled_address = (1 << 0) | (APorDP << 1) | (RorW << 2) | ((port_address & 0xC) << 1) | (parity << 5) | (1 << 7); + swd_Write(filled_address, 8); + if (swd_Read(3) == 1) { + if (RorW) { // Reading 32 bits from SWD + data = swd_Read(32); + if (swd_Read(1) == calculate_Parity(data)) { + swd_Write(0, 1); + return true; + } + } else { // Writing 32bits to SWD + swd_Write(data, 32); + swd_Write(calculate_Parity(data), 1); + swd_Write(0, 1); + return true; + } + } + swd_Write(0, 32); + return false; +} +bool swd::calculate_Parity(uint32_t in_data) { + in_data = (in_data & 0xFFFF) ^ (in_data >> 16); + in_data = (in_data & 0xFF) ^ (in_data >> 8); + in_data = (in_data & 0xF) ^ (in_data >> 4); + in_data = (in_data & 0x3) ^ (in_data >> 2); + in_data = (in_data & 0x1) ^ (in_data >> 1); + return in_data; +} +void swd::swd_Write(uint32_t in_data, uint8_t bits) { + if (cur_swd_direction == 0) + swd_Direction(1); + while (bits--) { + digitalWrite(this->swdio_pin, in_data & 1); + digitalWrite(this->swdclk_pin, LOW); + delayMicroseconds(2); + in_data >>= 1; + digitalWrite(this->swdclk_pin, HIGH); + delayMicroseconds(2); + } +} +uint32_t swd::swd_Read(uint8_t bits) { + uint32_t out_data = 0; + uint32_t input_bit = 1; + if (cur_swd_direction == 1) + swd_Direction(0); + while (bits--) { + if (digitalRead(this->swdio_pin)) { + out_data |= input_bit; + } + digitalWrite(this->swdclk_pin, LOW); + delayMicroseconds(2); + input_bit <<= 1; + digitalWrite(this->swdclk_pin, HIGH); + delayMicroseconds(2); + } + return out_data; +} +void swd::swd_Direction(bool WorR) { // 1 = Write 0 = Read + digitalWrite(this->swdio_pin, HIGH); + pinMode(this->swdio_pin, INPUT_PULLUP); + digitalWrite(this->swdclk_pin, LOW); + delayMicroseconds(2); + digitalWrite(this->swdclk_pin, HIGH); + delayMicroseconds(2); + if (WorR) + pinMode(this->swdio_pin, OUTPUT); + cur_swd_direction = WorR; +} + +#define PORT_AP true +#define PORT_DP false + +#define AP_NRF_RESET 0x00 +#define AP_NRF_ERASEALL 0x04 +#define AP_NRF_ERASEALLSTATUS 0x08 +#define AP_NRF_ERASEALL 0x04 +#define AP_NRF_APPROTECTSTATUS 0x0c +#define AP_NRF_IDR 0xfc + +#define AP_CSW 0x00 +#define AP_TAR 0x04 +#define AP_DRW 0x0c +#define AP_BD0 0x10 +#define AP_BD1 0x14 +#define AP_BD2 0x18 +#define AP_BD3 0x1c +#define AP_DBGDRAR 0xf8 +#define AP_IDR 0xfc + +#define DP_ABORT 0x00 +#define DP_CTRLSTAT 0x04 +#define DP_SELECT 0x08 +#define DP_RDBUFF 0x0c + +nrfswd::nrfswd(uint8_t swdio, uint8_t swdclk) : swd(swdio, swdclk) { +} +bool nrfswd::init() { + uint32_t temp = swd_Init(); + nrf_abort_all(); + if (temp == 0x2ba01477) { // if core id is readable the connection is working + if (showDebug) Serial.printf("Connected to nRF\n"); + isConnected = true; + if (nrf_read_lock_state()) { // nRF is unlocked so we can talk to the debugging interface + if (showDebug) Serial.printf("nRF is unlocked!\n"); + isLocked = false; + nrf_halt(); + nrf_read_ufcr(); + return true; + } else { + if (showDebug) Serial.printf("nRF is locked ;_;\n"); + isLocked = true; + } + } else { + isConnected = false; + isLocked = true; + } + return false; +} +uint32_t nrfswd::nrf_read_port(bool APorDP, uint8_t address) { + uint32_t temp = 0; + if (APorDP == PORT_AP) + AP_Read(address, temp); + else + DP_Read(address, temp); + DP_Read(DP_RDBUFF, temp); + DP_Read(DP_RDBUFF, temp); + Serial.printf("%s Read reg: 0x%02x : 0x%08x\r\n", APorDP ? "AP" : "DP", address, temp); + return temp; +} +void nrfswd::nrf_write_port(bool APorDP, uint8_t address, uint32_t value) { + uint32_t temp = 0; + if (APorDP == PORT_AP) + AP_Write(address, value); + else + DP_Write(address, value); + DP_Read(DP_RDBUFF, temp); + DP_Read(DP_RDBUFF, temp); + Serial.printf("%s Write reg: 0x%02x : 0x%08x\r\n", APorDP ? "AP" : "DP", address, value); +} +void nrfswd::nrf_abort_all() { + nrf_write_port(0, DP_ABORT, 0x1e); + nrf_write_port(0, DP_CTRLSTAT, 0x50000000); +} +void nrfswd::nrf_halt() { + AP_Write(AP_CSW, 0xa2000002); + AP_Write(AP_TAR, 0xe000edf0); + uint32_t retry = 500; + while (retry--) { + AP_Write(AP_DRW, 0xA05F0003); + } +} +void nrfswd::nrf_port_selection(bool new_port) { + DP_Write(DP_SELECT, new_port ? 0x01000000 : 0x00); // Select AP +} +bool nrfswd::nrf_read_lock_state() { + uint32_t temp; + nrf_port_selection(1); + temp = nrf_read_port(1, AP_NRF_APPROTECTSTATUS); + nrf_port_selection(0); + return temp & 1; +} +void nrfswd::nrf_read_ufcr() { + nrf_info.codepage_size = read_register(0x10000010); + nrf_info.codesize = read_register(0x10000014); + nrf_info.flash_size = nrf_info.codepage_size * nrf_info.codesize; + nrf_info.config_id = read_register(0x1000005c); + nrf_info.device_id0 = read_register(0x10000060); + nrf_info.device_id1 = read_register(0x10000064); + nrf_info.info_part = read_register(0x10000100); + nrf_info.info_variant = read_register(0x10000104); + nrf_info.info_package = read_register(0x10000108); + nrf_info.sd_info_area = read_register(0x0000300C) & 0xffff; + nrf_info.ucir_lock = read_register(0x10001208); + + if (showDebug) { + Serial.printf("Device: nRF%8X\n", nrf_info.info_part); + Serial.printf("Flash size: %i\r\n", nrf_info.flash_size); + } +} +uint32_t nrfswd::read_register(uint32_t address) { + uint32_t temp = 0; + bool state1 = AP_Write(AP_TAR, address); + bool state2 = AP_Read(AP_DRW, temp); + bool state3 = DP_Read(DP_RDBUFF, temp); + bool state4 = DP_Read(DP_RDBUFF, temp); + if (showDebug) + Serial.printf("%i%i%i%i Read Register: 0x%08x : 0x%08x\r\n", state1, state2, state3, state4, address, temp); + return temp; +} +void nrfswd::write_register(uint32_t address, uint32_t value) { + uint32_t temp = 0; + bool state1 = AP_Write(AP_TAR, address); + bool state2 = AP_Write(AP_DRW, value); + bool state3 = DP_Read(DP_RDBUFF, temp); + if (showDebug) + Serial.printf("%i%i%i Write Register: 0x%08x : 0x%08x\r\n", state1, state2, state3, address, value); +} + +uint8_t nrfswd::erase_all_flash() { + write_register(0x4001e504, 2); + long timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 1; + } + write_register(0x4001e50c, 1); + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 1000) return 1; + } + write_register(0x4001e504, 0); + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 1; + } + return 0; +} + +uint8_t nrfswd::erase_uicr() { + write_register(0x4001e504, 2); + long timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 1; + } + write_register(0x4001e514, 1); + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 1000) return 1; + } + write_register(0x4001e504, 0); + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 1; + } + return 0; +} + +uint8_t nrfswd::erase_page(uint32_t page) { + write_register(0x4001e504, 2); + long timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 1; + } + write_register(0x4001e508, page); + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 500) return 1; + } + write_register(0x4001e504, 0); + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 1; + } + return 0; +} + +void nrfswd::nrf_soft_reset(){ + nrf_port_selection(1); + nrf_write_port(1, AP_NRF_RESET, 1); + delay(100); + nrf_write_port(1, AP_NRF_RESET, 0); + nrf_port_selection(0); +} + +uint8_t nrfswd::nrf_write_bank(uint32_t address, uint32_t buffer[], int size) { + if (!isConnected) + return 1; // not connected to an nRF + + if (size > 4096) + return 2; // buffer bigger then a bank + + uint32_t temp; + + write_register(0x4001e504, 1); // NVIC Enable writing + long timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 3; + } + + AP_Write(AP_CSW, 0xa2000012); + AP_Write(AP_TAR, address); + + for (int posi = 0; posi < size; posi += 4) { + long end_micros = micros() + 400; // wait till writing of nRF memory is done without asking for ready state + AP_Write(AP_DRW, buffer[posi / 4]); + while (micros() < end_micros) { + }; + } + + AP_Write(AP_CSW, 0xa2000002); + DP_Read(DP_RDBUFF, temp); + DP_Read(DP_RDBUFF, temp); + + write_register(0x4001e504, 0); // NVIC Diasble writing + timeout = millis(); + while (read_register(0x4001e400) != 1) { + if (millis() - timeout > 100) return 3; + } + + return 0; +} +uint8_t nrfswd::nrf_read_bank(uint32_t address, uint32_t buffer[], int size) { + if (!isConnected) + return 1; // not connected to an nRF + + uint32_t temp; + + AP_Write(AP_CSW, 0xa2000012); + AP_Write(AP_TAR, address); + AP_Read(AP_DRW, temp); + + uint32_t curr_word = 0; + for (int posi = 0; posi < size; posi += 4) { + AP_Read(AP_DRW, curr_word); + buffer[posi / 4] = curr_word; + } + + AP_Write(AP_CSW, 0xa2000002); + DP_Read(DP_RDBUFF, temp); + DP_Read(DP_RDBUFF, temp); + + return 0; +} \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/src/usbflasher.cpp b/Tag_Flasher/ESP32_Flasher/src/usbflasher.cpp new file mode 100644 index 00000000..ab39ed8b --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/usbflasher.cpp @@ -0,0 +1,582 @@ +#include + +#include "USB.h" +#include "flasher.h" +#include "leds.h" +#include "powermgt.h" +#include "settings.h" +#include "swd.h" +#include "zbs_interface.h" + +USBCDC USBSerial; + +QueueHandle_t flasherCmdQueue; + +uint32_t usbConnectedStartTime = 0; +bool serialPassthroughState = false; + +#define FLASHER_WAIT_A 0 +#define FLASHER_WAIT_T 1 +#define FLASHER_WAIT_CMD 2 +#define FLASHER_WAIT_LEN 3 +#define FLASHER_WAIT_DATA 4 +#define FLASHER_WAIT_CRCH 5 +#define FLASHER_WAIT_CRCL 6 + +struct flasherCommand { + uint8_t command = 0; + uint32_t len = 0; + uint8_t* data = nullptr; +}; + +int8_t powerPins2[] = FLASHER_EXT_POWER; + +bool autoFlash(flasher* f) { + f->getFirmwareMD5(); + + if (f->findTagByMD5()) { + // this tag currently contains original firmware, found its fingerprint + USBSerial.printf("Found original firmware tag, recognized its fingerprint (%s)\n", f->md5char); + f->readInfoBlock(); + f->getFirmwareMac(); + f->prepareInfoBlock(); + f->writeInfoBlock(); + USBSerial.printf("Attempting to perform a flash...\n"); + if (f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype)) { + USBSerial.printf("Successfully flashed the tag!\n"); + return true; + } else { + USBSerial.printf("Couldn't flash the tag, for some reason...\n"); + } + } else if (f->getInfoBlockMD5()) { + // did find an infoblock MD5 that looks valid + if (f->findTagByMD5()) { + // did find the md5 in the database + USBSerial.printf("Found an already-flashed tag, recognized its fingerprint (%s)\n", f->md5char); + f->getInfoBlockMac(); + f->getInfoBlockType(); + f->readInfoBlock(); + USBSerial.printf("Attempting to perform a flash...\n"); + if (f->writeFlashFromPack("/Tag_FW_Pack.bin", f->tagtype)) { + USBSerial.printf("Successfully flashed the tag!\n"); + return true; + } else { + USBSerial.printf("Couldn't flash the tag, for some reason...\n"); + } + } else { + // couldn't find the md5 from the infoblock + USBSerial.printf("Found an already-flashed tag, but we couldn't find its fingerprint (%s) in the database\n", f->md5char); + return false; + } + } else { + // We couldn't recognize the tag from it's fingerprint... + USBSerial.printf("Found a tag but didn't recognize its fingerprint\n", f->md5char); + f->backupFlash(); + USBSerial.printf("Saved this MD5 binary to filesystem\n"); + } + return false; +} + +void sendFlasherAnswer(uint8_t answer_cmd, uint8_t* ans_buff, uint32_t len) { + uint8_t* answer_buffer = (uint8_t*)calloc(3 + 2 + 2 + len + 2 + 13, 1); + if (answer_buffer == nullptr) return; + uint32_t CRC_value = 0xAB34; + answer_buffer[0] = 'A'; + answer_buffer[1] = 'T'; + answer_buffer[2] = answer_cmd; + CRC_value += answer_cmd; + + for (uint8_t c = 0; c < 4; c++) { + answer_buffer[3 + c] = (uint8_t)(len >> (24 - (c * 8))); + CRC_value += answer_buffer[3 + c]; + } + + for (int i = 0; i < len; i++) { + answer_buffer[7 + i] = ans_buff[i]; + CRC_value += ans_buff[i]; + } + + answer_buffer[3 + 2 + 2 + len] = CRC_value >> 8; + answer_buffer[3 + 2 + 2 + len + 1] = CRC_value; + USBSerial.write(answer_buffer, 3 + 2 + 2 + len + 2); + // for(uint16_t c = 0; c< 3+2+2+len+2; c++){ + + //} + free(answer_buffer); +} + +void flasherUartHandler(uint8_t* data, uint8_t len) { + static struct flasherCommand* cmd; + static uint8_t flasherSerialState = FLASHER_WAIT_A; + static uint32_t flasherCmdDataIndex = 0; + static uint16_t flasherCRC = 0xAB34; + static uint32_t flasherLastCmd = 0; + static uint8_t curLenIndex = 0; + + if ((flasherSerialState != FLASHER_WAIT_A) && (millis() - flasherLastCmd >= 225)) { + flasherSerialState = FLASHER_WAIT_A; + // we should probably do something with stale commands containing data (data leak!) + } + + while (len--) { + uint8_t usbbyte = *(data++); + switch (flasherSerialState) { + case FLASHER_WAIT_A: + if (usbbyte == 'A') { + flasherSerialState = FLASHER_WAIT_T; + flasherLastCmd = millis(); + } else { + // enterConsoleMode(); + } + break; + case FLASHER_WAIT_T: + if (usbbyte == 'T') { + flasherSerialState = FLASHER_WAIT_CMD; + cmd = new flasherCommand; + flasherCRC = 0xAB34; + flasherCmdDataIndex = 0; + } else { + flasherSerialState = FLASHER_WAIT_A; + } + break; + case FLASHER_WAIT_CMD: + cmd->command = usbbyte; + flasherCRC += usbbyte; + flasherSerialState = FLASHER_WAIT_LEN; + curLenIndex = 0; + break; + case FLASHER_WAIT_LEN: + flasherCRC += usbbyte; + cmd->len |= ((uint32_t)usbbyte) << (24 - (8 * curLenIndex)); + curLenIndex++; + if (curLenIndex == sizeof(cmd->len)) { + if (cmd->len) { + // not 0 + cmd->data = (uint8_t*)calloc(cmd->len, 1); + if (cmd->data == nullptr) { + delete cmd; + flasherSerialState = FLASHER_WAIT_A; + } + flasherSerialState = FLASHER_WAIT_DATA; + } else { + // 0 len, so skip to CRC immediately + flasherSerialState = FLASHER_WAIT_CRCH; + } + } + break; + case FLASHER_WAIT_DATA: + flasherCRC += usbbyte; + cmd->data[flasherCmdDataIndex++] = usbbyte; + if (flasherCmdDataIndex == cmd->len) { + flasherSerialState = FLASHER_WAIT_CRCH; + } + break; + case FLASHER_WAIT_CRCH: + flasherCRC -= ((uint16_t)usbbyte << 8); + flasherSerialState = FLASHER_WAIT_CRCL; + break; + case FLASHER_WAIT_CRCL: + flasherCRC -= ((uint16_t)usbbyte); + if (flasherCRC) { + cmd = nullptr; + // we should probably delete the cmd and associated data here (data leak) + } else { + BaseType_t queuestatus = xQueueSend(flasherCmdQueue, &cmd, 0); + if (queuestatus == pdFALSE) { + if (cmd->data != nullptr) free(cmd->data); + delete cmd; + } + cmd = nullptr; + } + flasherSerialState = FLASHER_WAIT_A; + break; + } + } +} + +void resetFlasherState() { + if (serialPassthroughState) { + Serial1.end(); + } + serialPassthroughState = false; +} + +static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + if (event_base == ARDUINO_USB_EVENTS) { + arduino_usb_event_data_t* data = (arduino_usb_event_data_t*)event_data; + switch (event_id) { + case ARDUINO_USB_STARTED_EVENT: + // Serial.println("USB PLUGGED"); + resetFlasherState(); + break; + case ARDUINO_USB_STOPPED_EVENT: + // Serial.println("USB UNPLUGGED"); + resetFlasherState(); + break; + case ARDUINO_USB_SUSPEND_EVENT: + // Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en); + break; + case ARDUINO_USB_RESUME_EVENT: + // Serial.println("USB RESUMED"); + break; + default: + ets_printf("other USB event %d\n", event_id); + break; + } + } else if (event_base == ARDUINO_USB_CDC_EVENTS) { + arduino_usb_cdc_event_data_t* data = (arduino_usb_cdc_event_data_t*)event_data; + switch (event_id) { + case ARDUINO_USB_CDC_CONNECTED_EVENT: + ets_printf("CDC CONNECTED\n"); + resetFlasherState(); + usbConnectedStartTime = millis(); + break; + case ARDUINO_USB_CDC_DISCONNECTED_EVENT: + ets_printf("CDC DISCONNECTED\n"); + resetFlasherState(); + break; + case ARDUINO_USB_CDC_LINE_STATE_EVENT: + ets_printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts); + if (data->line_state.dtr == 0) resetFlasherState(); + break; + case ARDUINO_USB_CDC_LINE_CODING_EVENT: + ets_printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity); + resetFlasherState(); + break; + case ARDUINO_USB_CDC_RX_EVENT: + // Serial.printf("CDC RX [%u]:", data->rx.len); + { + uint8_t buf[data->rx.len]; + size_t len = USBSerial.read(buf, data->rx.len); + if (serialPassthroughState) { + Serial0.printf("IN-size=%d\n", len); + Serial1.write(buf, len); + quickBlink(1); + + } else { + flasherUartHandler(buf, len); + } + } + break; + case ARDUINO_USB_CDC_RX_OVERFLOW_EVENT: + // Serial.printf("CDC RX Overflow of %d bytes", data->rx_overflow.dropped_bytes); + break; + + default: + break; + } + } +} + +typedef enum { + CMD_GET_VERSION = 1, + CMD_RESET_ESP = 2, + CMD_RESET = 11, + CMD_SET_POWER = 13, + + CMD_ERASE_FLASH = 26, + CMD_ERASE_INFOPAGE = 27, + CMD_SAVE_MAC_FROM_FW = 40, + CMD_PASS_THROUGH = 50, + + CMD_SELECT_ZBS243 = 60, + CMD_SELECT_NRF82511 = 61, + + CMD_SELECT_PORT = 70, + + CMD_READ_INFOPAGE = 80, + CMD_READ_FLASH = 81, + CMD_WRITE_INFOPAGE = 82, + CMD_WRITE_FLASH = 83, + CMD_AUTOFLASH = 87, + CMD_COMPLETE = 88, + +} ZBS_UART_PROTO; +uint32_t FLASHER_VERSION = 0x00000030; + +#define CONTROLLER_ZBS243 0 +#define CONTROLLER_NRF82511 1 +uint8_t selectedController = 0; +uint8_t selectedFlasherPort; +uint32_t currentFlasherOffset; +flasher* zbsflasherp; +nrfswd* nrfflasherp; + +void processFlasherCommand(struct flasherCommand* cmd) { + uint8_t* tempbuffer; + uint8_t temp_buff[16]; + uint32_t spi_speed = 0; + uint8_t powerPinCount = 1; + static uint32_t curspeed = 0; + + switch (cmd->command) { + case CMD_GET_VERSION: + temp_buff[0] = FLASHER_VERSION >> 24; + temp_buff[1] = FLASHER_VERSION >> 16; + temp_buff[2] = FLASHER_VERSION >> 8; + temp_buff[3] = FLASHER_VERSION; + sendFlasherAnswer(cmd->command, temp_buff, 4); + break; + case CMD_RESET_ESP: + sendFlasherAnswer(cmd->command, NULL, 0); + delay(100); + ESP.restart(); + break; + case CMD_SET_POWER: + + powerControl(cmd->data[0], (uint8_t*)powerPins2, 4); + + sendFlasherAnswer(CMD_SET_POWER, NULL, 0); + break; + case CMD_RESET: + if (zbsflasherp != nullptr) { + zbsflasherp->zbs->reset(); + delete zbsflasherp; + zbsflasherp = nullptr; + } else if (nrfflasherp != nullptr) { + nrfflasherp->nrf_soft_reset(); + delete nrfflasherp; + nrfflasherp = nullptr; + } else { + pinMode(FLASHER_EXT_RESET, OUTPUT); + digitalWrite(FLASHER_EXT_RESET, LOW); + vTaskDelay(200 / portTICK_PERIOD_MS); + digitalWrite(FLASHER_EXT_RESET, HIGH); + } + sendFlasherAnswer(CMD_RESET, NULL, 0); + break; + case CMD_ERASE_FLASH: + if (selectedController == CONTROLLER_NRF82511) { + } else if (selectedController == CONTROLLER_ZBS243) { + if (zbsflasherp == nullptr) return; + zbsflasherp->zbs->erase_flash(); + } + sendFlasherAnswer(CMD_ERASE_FLASH, NULL, 0); + break; + case CMD_ERASE_INFOPAGE: + if (selectedController == CONTROLLER_NRF82511) { + nrfflasherp->erase_uicr(); + } else if (selectedController == CONTROLLER_ZBS243) { + if (zbsflasherp == nullptr) return; + zbsflasherp->zbs->erase_infoblock(); + } + sendFlasherAnswer(CMD_ERASE_INFOPAGE, NULL, 0); + break; + case CMD_SELECT_PORT: + selectedFlasherPort = cmd->data[0]; + // Serial.printf("Port selected = %d\n", cmd->data[0]); + break; + case CMD_SELECT_ZBS243: + zbsflasherp = new flasher; + temp_buff[0] = zbsflasherp->connectTag(selectedFlasherPort); + sendFlasherAnswer(CMD_SELECT_ZBS243, temp_buff, 1); + currentFlasherOffset = 0; + selectedController = CONTROLLER_ZBS243; + break; + case CMD_SELECT_NRF82511: + + powerControl(true, (uint8_t*)powerPins2, 4); + nrfflasherp = new nrfswd(FLASHER_EXT_MISO, FLASHER_EXT_CLK); + + nrfflasherp->init(); + temp_buff[0] = (nrfflasherp->isConnected && !nrfflasherp->isLocked); + sendFlasherAnswer(CMD_SELECT_NRF82511, temp_buff, 1); + currentFlasherOffset = 0; + selectedController = CONTROLLER_NRF82511; + break; + case CMD_READ_FLASH: + uint8_t* bufferp; + uint32_t cur_len; + if (selectedController == CONTROLLER_NRF82511) { + if (nrfflasherp == nullptr) return; + if (currentFlasherOffset >= nrfflasherp->nrf_info.flash_size) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + bufferp = (uint8_t*)malloc(256); + if (bufferp == nullptr) return; + cur_len = (nrfflasherp->nrf_info.flash_size - currentFlasherOffset >= 256) ? 256 : nrfflasherp->nrf_info.flash_size - currentFlasherOffset; + nrfflasherp->nrf_read_bank(currentFlasherOffset, (uint32_t*)bufferp, cur_len); + currentFlasherOffset += cur_len; + sendFlasherAnswer(CMD_READ_FLASH, bufferp, cur_len); + if (bufferp != nullptr) free(bufferp); + } + } else if (selectedController == CONTROLLER_ZBS243) { + if (zbsflasherp == nullptr) return; + if (currentFlasherOffset >= 65536) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + bufferp = (uint8_t*)malloc(256); + if (bufferp == nullptr) return; + cur_len = (65536 - currentFlasherOffset >= 256) ? 256 : 65536 - currentFlasherOffset; + zbsflasherp->readBlock(currentFlasherOffset, bufferp, cur_len, false); + currentFlasherOffset += cur_len; + sendFlasherAnswer(CMD_READ_FLASH, bufferp, cur_len); + if (bufferp != nullptr) free(bufferp); + } + } else { + Serial0.printf("No controller type selected\n"); + } + break; + case CMD_READ_INFOPAGE: + uint8_t* ibufferp; + uint32_t icur_len; + if (selectedController == CONTROLLER_NRF82511) { + if (nrfflasherp == nullptr) return; + if (currentFlasherOffset >= 4096) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + ibufferp = (uint8_t*)malloc(256); + if (ibufferp == nullptr) return; + icur_len = (4096 - currentFlasherOffset >= 256) ? 256 : 4096 - currentFlasherOffset; + nrfflasherp->nrf_read_bank(0x10001000 + currentFlasherOffset, (uint32_t*)ibufferp, icur_len); + currentFlasherOffset += icur_len; + sendFlasherAnswer(CMD_READ_INFOPAGE, ibufferp, icur_len); + if (ibufferp != nullptr) free(ibufferp); + } + } else if (selectedController == CONTROLLER_ZBS243) { + if (zbsflasherp == nullptr) return; + if (currentFlasherOffset >= 256) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + ibufferp = (uint8_t*)malloc(256); + if (ibufferp == nullptr) return; + icur_len = (1024 - currentFlasherOffset >= 256) ? 256 : 1024 - currentFlasherOffset; + zbsflasherp->readBlock(currentFlasherOffset, ibufferp, icur_len, true); + currentFlasherOffset += icur_len; + sendFlasherAnswer(CMD_READ_INFOPAGE, ibufferp, icur_len); + if (ibufferp != nullptr) free(ibufferp); + } + } + break; + case CMD_WRITE_FLASH: + if (selectedController == CONTROLLER_NRF82511) { + if (nrfflasherp == nullptr) return; + if (currentFlasherOffset >= nrfflasherp->nrf_info.flash_size) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + for (uint32_t c = currentFlasherOffset; c < (currentFlasherOffset + cmd->len);) { + // very ugly and naive way to find out what page we're in, and erase all relevant pages before writing + if (c % nrfflasherp->nrf_info.codepage_size == 0) { + nrfflasherp->erase_page(c); + // Serial.printf("Erasing page %lu\n", c); + c += nrfflasherp->nrf_info.codepage_size; + } else { + c++; + } + } + nrfflasherp->nrf_write_bank(currentFlasherOffset, (uint32_t*)cmd->data, cmd->len); + // Serial.printf("wrote page to nrf\n"); + currentFlasherOffset += cmd->len; + sendFlasherAnswer(CMD_WRITE_FLASH, NULL, 0); + } + } else if (selectedController == CONTROLLER_ZBS243) { + if (zbsflasherp == nullptr) return; + if (currentFlasherOffset >= 65536) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + zbsflasherp->writeBlock(currentFlasherOffset, cmd->data, cmd->len, false); + currentFlasherOffset += cmd->len; + sendFlasherAnswer(CMD_WRITE_FLASH, NULL, 0); + } + } + break; + case CMD_WRITE_INFOPAGE: + if (selectedController == CONTROLLER_NRF82511) { + if (nrfflasherp == nullptr) return; + if (currentFlasherOffset >= 4096) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + nrfflasherp->nrf_write_bank(0x10001000 + currentFlasherOffset, (uint32_t*)cmd->data, cmd->len); + // Serial.printf("wrote page to nrf\n"); + currentFlasherOffset += cmd->len; + sendFlasherAnswer(CMD_WRITE_INFOPAGE, NULL, 0); + } + } else if (selectedController == CONTROLLER_ZBS243) { + if (zbsflasherp == nullptr) return; + if (currentFlasherOffset >= 1024) { + sendFlasherAnswer(CMD_COMPLETE, temp_buff, 1); + } else { + zbsflasherp->writeBlock(currentFlasherOffset, cmd->data, cmd->len, true); + currentFlasherOffset += cmd->len; + sendFlasherAnswer(CMD_WRITE_INFOPAGE, NULL, 0); + } + } + break; + case CMD_PASS_THROUGH: + Serial1.begin(115200, SERIAL_8N1, FLASHER_EXT_RXD, FLASHER_EXT_TXD); + USBSerial.println(">>>"); + serialPassthroughState = true; + break; + case CMD_AUTOFLASH: + if (selectedController == CONTROLLER_ZBS243) { + autoFlash(zbsflasherp); + zbsflasherp->zbs->reset(); + delete zbsflasherp; + zbsflasherp = 0; + USBSerial.write(0x04); + } else { + USBSerial.println("Not yet implemented!"); + } + break; + } +} + +uint32_t lastCmdTimeStamp = 0; +#define USBFLASHER_CONNECTION_TIMEOUT 5000 +void flasherCommandTimeout() { + // this is triggered if no command has been received for a couple of seconds; this makes sure everything is cleaned up when the USB connection is lost + if (zbsflasherp != nullptr) { + delete zbsflasherp; + zbsflasherp = nullptr; + } + if (nrfflasherp != nullptr) { + delete nrfflasherp; + nrfflasherp = nullptr; + } + lastCmdTimeStamp = 0; +} + +void tagDebugPassthrough() { + uint16_t len = Serial1.available(); + if (len > 0) { + Serial0.printf("OUT-size=%d\n", len); + uint8_t* buf = (uint8_t*)malloc(len); + Serial1.read(buf, len); + USBSerial.write(buf, len); + free(buf); + } +} + +void usbFlasherTask(void* parameter) { + flasherCmdQueue = xQueueCreate(10, sizeof(struct flasherCommand*)); +#if ARDUINO_USB_MODEflash +#warning Wrong USB mode is in use, check settings in platformio.ini +#endif + USB.onEvent(usbEventCallback); + USBSerial.onEvent(usbEventCallback); + USBSerial.setTimeout(1000); + USB.begin(); + USBSerial.begin(); + + struct flasherCommand* cmd; + while (true) { + while (serialPassthroughState) { + tagDebugPassthrough(); + vTaskDelay(1 / portTICK_PERIOD_MS); + } + BaseType_t queuereceive = xQueueReceive(flasherCmdQueue, &cmd, 1000 / portTICK_PERIOD_MS); // timeout every second to make sure the timeout gets triggered after a while + if (queuereceive == pdTRUE) { + processFlasherCommand(cmd); + lastCmdTimeStamp = millis(); + if (cmd->data != nullptr) { + free(cmd->data); + } + delete cmd; + } else { + if (lastCmdTimeStamp) { + if (millis() - lastCmdTimeStamp > USBFLASHER_CONNECTION_TIMEOUT) + flasherCommandTimeout(); + } + } + } +} \ No newline at end of file diff --git a/Tag_Flasher/ESP32_Flasher/src/zbs_interface.cpp b/Tag_Flasher/ESP32_Flasher/src/zbs_interface.cpp new file mode 100644 index 00000000..d4022545 --- /dev/null +++ b/Tag_Flasher/ESP32_Flasher/src/zbs_interface.cpp @@ -0,0 +1,222 @@ + +/* Autor: Aaron Christophel ATCnetz.de */ +#include "zbs_interface.h" + +#include +#include +#include +#include + +#ifdef USE_SOFTSPI +#include +#endif + +#include "powermgt.h" + +uint8_t ZBS_interface::begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t* POWER, uint8_t powerPins, uint32_t spi_speed) { + _SS_PIN = SS; + _CLK_PIN = CLK; + _MOSI_PIN = MOSI; + _MISO_PIN = MISO; + _RESET_PIN = RESET; + if (powerPins > 0) + _POWER_PIN = POWER; + else + _POWER_PIN = nullptr; + pinMode(_SS_PIN, OUTPUT); + pinMode(_RESET_PIN, OUTPUT); + digitalWrite(_SS_PIN, HIGH); + digitalWrite(_RESET_PIN, HIGH); + set_power(ZBS_ON); + pinMode(_CLK_PIN, OUTPUT); + pinMode(_MOSI_PIN, OUTPUT); + pinMode(_MISO_PIN, INPUT); + digitalWrite(_CLK_PIN, LOW); + digitalWrite(_MOSI_PIN, HIGH); + +#ifdef USE_SOFTSPI + if (!spi) spi = new SoftSPI(_MOSI_PIN, _MISO_PIN, _CLK_PIN); +#else + if (!spi) spi = new SPIClass(HSPI); +#endif + + spiSettings = SPISettings(spi_speed, MSBFIRST, SPI_MODE0); + spi_ready = 0; + + if (spi_speed != 8000000) { + after_byte_delay = 10; + } else { + after_byte_delay = 10; + } + enable_debug(); + return check_connection(); +} + +void ZBS_interface::setSpeed(uint32_t speed) { + spiSettings = SPISettings(speed, MSBFIRST, SPI_MODE0); +} + +ZBS_interface::~ZBS_interface() { + if (spi) delete spi; +} +void ZBS_interface::set_power(uint8_t state) { + powerControl(state, _POWER_PIN, _POWER_PINS); +} + +void ZBS_interface::enable_debug() { + digitalWrite(_RESET_PIN, HIGH); + digitalWrite(_SS_PIN, HIGH); + digitalWrite(_CLK_PIN, LOW); + digitalWrite(_MOSI_PIN, HIGH); + delay(30); + digitalWrite(_RESET_PIN, LOW); + delay(33); + digitalWrite(_CLK_PIN, HIGH); + delay(1); + digitalWrite(_CLK_PIN, LOW); + delay(1); + digitalWrite(_CLK_PIN, HIGH); + delay(1); + digitalWrite(_CLK_PIN, LOW); + delay(1); + digitalWrite(_CLK_PIN, HIGH); + delay(1); + digitalWrite(_CLK_PIN, LOW); + delay(1); + digitalWrite(_CLK_PIN, HIGH); + delay(1); + digitalWrite(_CLK_PIN, LOW); + delay(9); + digitalWrite(_RESET_PIN, HIGH); + delay(100); +} + +void ZBS_interface::reset() { + if (spi) { + spi->end(); + delete spi; + spi = nullptr; + } + pinMode(_SS_PIN, INPUT); + pinMode(_CLK_PIN, INPUT); + pinMode(_MOSI_PIN, INPUT); + pinMode(_MISO_PIN, INPUT); + digitalWrite(_RESET_PIN, LOW); + set_power(ZBS_OFF); + delay(500); + set_power(ZBS_ON); + delay(500); + digitalWrite(_RESET_PIN, HIGH); + pinMode(_RESET_PIN, INPUT); +} + +void ZBS_interface::send_byte(uint8_t data) { + digitalWrite(_SS_PIN, LOW); + delayMicroseconds(5); + if (!spi_ready) { + spi_ready = 1; + spi->begin(_CLK_PIN, _MISO_PIN, _MOSI_PIN); + } + spi->beginTransaction(spiSettings); + spi->transfer(data); + spi->endTransaction(); + + delayMicroseconds(2); + digitalWrite(_SS_PIN, HIGH); +} + +uint8_t ZBS_interface::read_byte() { + uint8_t data = 0x00; + digitalWrite(_SS_PIN, LOW); + delayMicroseconds(5); + if (!spi_ready) { + spi_ready = 1; + spi->begin(_CLK_PIN, _MISO_PIN, _MOSI_PIN); + } + spi->beginTransaction(spiSettings); + data = spi->transfer(0xff); + spi->endTransaction(); + delayMicroseconds(2); + digitalWrite(_SS_PIN, HIGH); + return data; +} + +void ZBS_interface::write_byte(uint8_t cmd, uint8_t addr, uint8_t data) { + send_byte(cmd); + send_byte(addr); + send_byte(data); + delayMicroseconds(after_byte_delay); +} + +uint8_t ZBS_interface::read_byte(uint8_t cmd, uint8_t addr) { + uint8_t data = 0x00; + send_byte(cmd); + send_byte(addr); + data = read_byte(); + delayMicroseconds(after_byte_delay); + return data; +} + +void ZBS_interface::write_flash(uint16_t addr, uint8_t data) { + send_byte(ZBS_CMD_W_FLASH); + send_byte(addr >> 8); + send_byte(addr); + send_byte(data); + delayMicroseconds(after_byte_delay); +} + +uint8_t ZBS_interface::read_flash(uint16_t addr) { + uint8_t data = 0x00; + send_byte(ZBS_CMD_R_FLASH); + send_byte(addr >> 8); + send_byte(addr); + data = read_byte(); + delayMicroseconds(after_byte_delay); + return data; +} + +void ZBS_interface::write_ram(uint8_t addr, uint8_t data) { + write_byte(ZBS_CMD_W_RAM, addr, data); +} + +uint8_t ZBS_interface::read_ram(uint8_t addr) { + return read_byte(ZBS_CMD_R_RAM, addr); +} + +void ZBS_interface::write_sfr(uint8_t addr, uint8_t data) { + write_byte(ZBS_CMD_W_SFR, addr, data); +} + +uint8_t ZBS_interface::read_sfr(uint8_t addr) { + return read_byte(ZBS_CMD_R_SFR, addr); +} + +uint8_t ZBS_interface::check_connection() { + uint8_t test_byte = 0xA5; + write_ram(0xba, test_byte); + delay(1); + return read_ram(0xba) == test_byte; +} + +uint8_t ZBS_interface::select_flash(uint8_t page) { + uint8_t sfr_low_bank = page ? 0x80 : 0x00; + write_sfr(0xd8, sfr_low_bank); + delay(1); + return read_sfr(0xd8) == sfr_low_bank; +} + +void ZBS_interface::erase_flash() { + send_byte(ZBS_CMD_ERASE_FLASH); + send_byte(0x00); + send_byte(0x00); + send_byte(0x00); + delay(100); +} + +void ZBS_interface::erase_infoblock() { + send_byte(ZBS_CMD_ERASE_INFOBLOCK); + send_byte(0x00); + send_byte(0x00); + send_byte(0x00); + delay(100); +} diff --git a/Tag_Flasher/OEPL-Flasher.py b/Tag_Flasher/OEPL-Flasher.py index 31591929..b34ae0c1 100644 --- a/Tag_Flasher/OEPL-Flasher.py +++ b/Tag_Flasher/OEPL-Flasher.py @@ -106,15 +106,16 @@ def validate_arguments(args): return False if args.command == "write" and not os.path.isfile(args.filename): print("Couldn't find the specified file!") - return False; + return False if args.command in ["read", "write"] and not args.filename: print("Please specify a file to save read data") - return False; + return False if args.command == "read" and len(args.filename) < 2: print("Please specify a file to save read data") - return False; + return False return True + def read_from_serial(port, filename, flash): if flash: print( @@ -145,6 +146,7 @@ def read_from_serial(port, filename, flash): else: print("Failed reading block, timeout?") + def write_to_serial(port, filename, flash): if flash: @@ -167,7 +169,7 @@ def write_to_serial(port, filename, flash): else: print(f"Writing info page data from file: {filename}\n") - chunk_size = 1024 + chunk_size = 256 if filename.endswith('.bin'): file_data = read_binary_file(filename) @@ -192,6 +194,7 @@ def write_to_serial(port, filename, flash): return print('\rAll done writing! ', end='', flush=True) + def short_passthough(period_time): start_time = time.time() while time.time() - start_time < period_time: @@ -201,6 +204,7 @@ def short_passthough(period_time): if chr(0x04) in data.decode(): break + def main(): try: parser = argparse.ArgumentParser( @@ -224,7 +228,7 @@ def main(): help="Selects the external(side) port") parser.add_argument("--altradio", action="store_true", help="Selects the alternate radio port") - parser.add_argument("--pt","--passthrough", action="store_true", + parser.add_argument("--pt", "--passthrough", action="store_true", help="Enters serial passthrough for debug output after flashing") args = parser.parse_args() @@ -237,7 +241,7 @@ def main(): return global ser - ser = serial.Serial(args.port, baudrate=500000) + ser = serial.Serial(args.port, baudrate=115200) time.sleep(1) # Flush serial data while (ser.inWaiting() > 0): data_str = ser.read(ser.inWaiting()) @@ -251,8 +255,9 @@ def main(): print( "Couldn't establish a connection with the flasher, did you select the correct serial port?") exit(0) - - if (args.command!="debug"): + send_cmd(CMD_SET_POWER, bytearray([1])) + cmd, answer = wait_for_command() + if (args.command != "debug"): if args.internalap: send_cmd(CMD_SELECT_PORT, bytearray([0])) if args.external: @@ -264,8 +269,9 @@ def main(): send_cmd(CMD_SELECT_NRF82511, bytearray([])) if args.zbs243: send_cmd(CMD_SELECT_ZBS243, bytearray([])) - cmd, answer = wait_for_command() +# send_cmd(CMD_SET_POWER, bytearray([1])) +# cmd, answer = wait_for_command() if (answer[0] == 1): print("Connection established to microcontroller") else: @@ -278,22 +284,20 @@ def main(): write_to_serial(ser, args.filename, args.flash) elif args.command == "autoflash": print("Starting automatic tag flash") - send_cmd(CMD_AUTOFLASH , bytearray([])) + send_cmd(CMD_AUTOFLASH, bytearray([])) short_passthough(30) else: print("Invalid command!") send_cmd(CMD_RESET, bytearray([])) cmd, answer = wait_for_command() - send_cmd(CMD_SET_POWER, bytearray([1])) - cmd, answer = wait_for_command() - - if (args.pt or args.command=="debug"): + if (args.pt or args.command == "debug"): # enter passthrough mode - send_cmd(CMD_PASS_THROUGH , bytearray([])) - print("Now showing debug output from the tag - CTRL+C to quit"); - print("---------------------------------------------------------------------------------"); + send_cmd(CMD_PASS_THROUGH, bytearray([])) + print("Now showing debug output from the tag - CTRL+C to quit") + print( + "---------------------------------------------------------------------------------") while True: try: data = ser.read() @@ -307,7 +311,6 @@ def main(): if chr(0x04) in data.decode(): break - except KeyboardInterrupt: print("\nBye!") ser.close()