From 57748d825b3c54c4fc1213e96e6acdccc5630d50 Mon Sep 17 00:00:00 2001 From: jjwbruijn Date: Tue, 13 Feb 2024 22:38:36 +0100 Subject: [PATCH] Added compression to M3, moved stuff around --- .gitmodules | 3 + .../Newton_M3_nRF52811/include/compression.h | 7 + .../Newton_M3_nRF52811/include/drawing.h | 113 +- ARM_Tag_FW/Newton_M3_nRF52811/include/font.h | 6 +- .../Newton_M3_nRF52811/include/settings.h | 11 +- .../Newton_M3_nRF52811/include/syncedproto.h | 22 +- ARM_Tag_FW/Newton_M3_nRF52811/platformio.ini | 1 + .../Newton_M3_nRF52811/src/compression.cpp | 25 + ARM_Tag_FW/Newton_M3_nRF52811/src/drawing.cpp | 631 +--------- .../src/epd_driver/uc8159.cpp | 41 +- .../Newton_M3_nRF52811/src/settings.cpp | 3 +- .../Newton_M3_nRF52811/src/syncedproto.cpp | 1024 +--------------- .../Newton_M3_nRF52811/src/userinterface.cpp | 2 +- ARM_Tag_FW/common/QRCode | 1 + ARM_Tag_FW/common/compression.cpp | 37 +- ARM_Tag_FW/common/drawing.cpp | 940 +++++++++++++++ ARM_Tag_FW/common/drawing.h | 152 +++ ARM_Tag_FW/common/oepl-protocol.cpp | 1043 +++++++++++++++++ ARM_Tag_FW/common/oepl-protocol.h | 21 + 19 files changed, 2281 insertions(+), 1802 deletions(-) create mode 100644 ARM_Tag_FW/Newton_M3_nRF52811/include/compression.h create mode 100644 ARM_Tag_FW/Newton_M3_nRF52811/src/compression.cpp create mode 160000 ARM_Tag_FW/common/QRCode create mode 100644 ARM_Tag_FW/common/drawing.cpp create mode 100644 ARM_Tag_FW/common/drawing.h create mode 100644 ARM_Tag_FW/common/oepl-protocol.cpp create mode 100644 ARM_Tag_FW/common/oepl-protocol.h diff --git a/.gitmodules b/.gitmodules index 997df955..b14f97ee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ARM_Tag_FW/common/uzlib"] path = ARM_Tag_FW/common/uzlib url = https://github.com/pfalcon/uzlib.git +[submodule "ARM_Tag_FW/common/QRCode"] + path = ARM_Tag_FW/common/QRCode + url = https://github.com/ricmoo/QRCode diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/include/compression.h b/ARM_Tag_FW/Newton_M3_nRF52811/include/compression.h new file mode 100644 index 00000000..b08c51e9 --- /dev/null +++ b/ARM_Tag_FW/Newton_M3_nRF52811/include/compression.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef ENABLE_OEPLFS +#include "oepl_fs.h" +#endif + +#include "../../common/compression.h" \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/include/drawing.h b/ARM_Tag_FW/Newton_M3_nRF52811/include/drawing.h index 54dbbb82..d52b6d93 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/include/drawing.h +++ b/ARM_Tag_FW/Newton_M3_nRF52811/include/drawing.h @@ -4,115 +4,6 @@ #include #include -#define COLOR_RED 1 -#define COLOR_BLACK 0 +#include "../../common/drawing.h" -#define IMAGE_OR 1 -#define IMAGE_REPLACE 0 - -#define DRAW_INVERTED 1 -#define DRAW_NORMAL 0 - -typedef struct { - uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap - uint8_t width; ///< Bitmap dimensions in pixels - uint8_t height; ///< Bitmap dimensions in pixels - uint8_t xAdvance; ///< Distance to advance cursor (x axis) - int8_t xOffset; ///< X dist from cursor pos to UL corner - int8_t yOffset; ///< Y dist from cursor pos to UL corner -} GFXglyph; - -/// Data stored for FONT AS A WHOLE -typedef struct { - uint8_t *bitmap; ///< Glyph bitmaps, concatenated - GFXglyph *glyph; ///< Glyph array - uint16_t first; ///< ASCII extents (first char) - uint16_t last; ///< ASCII extents (last char) - uint8_t yAdvance; ///< Newline distance (y axis) -} GFXfont; - -enum rotation { - ROTATE_0, - ROTATE_90, - ROTATE_180, - ROTATE_270 -}; - -void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask); -void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image); -void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...); -void drawImageAtAddress(uint32_t addr, uint8_t lut); -void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color); -void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color); - -class drawItem { - public: - drawItem(); - ~drawItem(); - void setRotation(enum rotation ro); - void addItem(uint8_t *data, uint16_t width, uint16_t height); - bool addToList(); - - static void shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len); - static void renderDrawLine(uint8_t *line, uint16_t number, uint8_t c); - static void flushDrawItems(); - - // these are also used for rotated screens - static void reverseBytes(uint8_t *src, uint8_t src_len); - static uint8_t bitReverse(uint8_t byte); - - - enum drawType { - DRAW_FONT, - DRAW_BUFFERED_1BPP, - DRAW_MASK, - DRAW_EEPROM_1BPP, - DRAW_EEPROM_2BPP, - } type; - - int16_t xpos; - int16_t ypos; - - enum rotation rotate = ROTATE_0; - - uint8_t color = 0; - - bool direction = false; - - bool mirrorH = false; - bool mirrorV = false; - uint16_t width; - uint16_t height; - - // if this is true, clean up the reference (free memory). - bool cleanUp = true; - - protected: - void copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset); - - - void getDrawLine(uint8_t *line, uint16_t number, uint8_t c); - void getXLine(uint8_t *line, uint16_t yPos, uint8_t color); - void getYLine(uint8_t *line, uint16_t xPos, uint8_t color); - uint8_t widthBytes = 0; - uint8_t *buffer; -}; - -class fontrender { - public: - void epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...); - fontrender(const GFXfont *font); - void setFont(const GFXfont *font); - - protected: - GFXfont *gfxFont; // = &FreeSansBold18pt7b; - uint16_t bufferByteWidth = 0; - uint8_t *fb = nullptr; - uint16_t Xpixels; - uint8_t drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size); - uint8_t getCharWidth(uint16_t c); - void drawFastHLine(uint16_t x, uint16_t y, uint16_t w); - void fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h); -}; - -#endif +#endif \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/include/font.h b/ARM_Tag_FW/Newton_M3_nRF52811/include/font.h index 49f4901e..c55c22a7 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/include/font.h +++ b/ARM_Tag_FW/Newton_M3_nRF52811/include/font.h @@ -1,5 +1,5 @@ #pragma once #include -#include "FreeSans9pt7b.h" -#include "FreeSansBold18pt7b.h" -#include "FreeSansBold24pt7b.h" \ No newline at end of file +#include "../../common/fonts/FreeSans9pt7b.h" +#include "../../common/fonts/FreeSansBold18pt7b.h" +#include "../../common/fonts/FreeSansBold24pt7b.h" \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/include/settings.h b/ARM_Tag_FW/Newton_M3_nRF52811/include/settings.h index bc487b21..a5f6104d 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/include/settings.h +++ b/ARM_Tag_FW/Newton_M3_nRF52811/include/settings.h @@ -3,11 +3,16 @@ #include -#define FW_VERSION 0x0026 // version number (max 2.5.5 :) ) -#define FW_VERSION_SUFFIX "LED2" // suffix, like RC1 or whatever. -//#define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers +#define FW_VERSION 0x0027 // version number (max 2.5.5 :) ) +#define FW_VERSION_SUFFIX "ZLIB" // suffix, like RC1 or whatever. +#define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers #endif + +#define DEBUG_BUILD +#define DEBUG_DRAWING + + #define SETTINGS_STRUCT_VERSION 0x01 #define DEFAULT_SETTING_FASTBOOT 0 diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/include/syncedproto.h b/ARM_Tag_FW/Newton_M3_nRF52811/include/syncedproto.h index b9e2e635..4c0be514 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/include/syncedproto.h +++ b/ARM_Tag_FW/Newton_M3_nRF52811/include/syncedproto.h @@ -3,24 +3,6 @@ #include #include "settings.h" -extern uint8_t mSelfMac[]; -extern uint8_t currentChannel; -extern uint8_t APmac[]; +#define PERSISTENTVAR -extern uint8_t curImgSlot; - -extern void setupRadio(void); -extern void killRadio(void); - -extern uint8_t findSlotDataTypeArg(uint8_t arg); -extern uint8_t findNextSlideshowImage(uint8_t start); -extern uint8_t getEepromImageDataArgument(const uint8_t slot); - -extern struct AvailDataInfo *getAvailDataInfo(); -extern struct AvailDataInfo *getShortAvailDataInfo(); - -extern void drawImageFromEeprom(const uint8_t imgSlot, uint8_t lut); -extern void eraseImageBlocks(); -extern bool processAvailDataInfo(struct AvailDataInfo *avail); -extern void initializeProto(); -extern uint8_t detectAP(const uint8_t channel); +#include "../../common/oepl-protocol.h" \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/platformio.ini b/ARM_Tag_FW/Newton_M3_nRF52811/platformio.ini index 6fd701a0..09f65133 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/platformio.ini +++ b/ARM_Tag_FW/Newton_M3_nRF52811/platformio.ini @@ -12,6 +12,7 @@ lib_deps = stevemarple/AsyncDelay @ ^1.1.2 https://github.com/calebstewart/md5 https://github.com/ricmoo/QRCode + https://github.com/pfalcon/uzlib extra_scripts = post:preparefiles.py [env:Newton_M3_Universal] diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/src/compression.cpp b/ARM_Tag_FW/Newton_M3_nRF52811/src/compression.cpp new file mode 100644 index 00000000..79b1458c --- /dev/null +++ b/ARM_Tag_FW/Newton_M3_nRF52811/src/compression.cpp @@ -0,0 +1,25 @@ +#include +#include +#include "eeprom.h" +#include "comms.h" +#include "powermgt.h" + +#include "syncedproto.h" +#include "hal.h" + +#include "compression.h" + +#include "../../../oepl-definitions.h" +#include "../../../oepl-proto.h" + +#include "settings.h" + +#define MAX_WINDOW_SIZE 8192 +#define ZLIB_CACHE_SIZE 256 + +uint32_t __attribute__((always_inline)) inline HAL_flashRead(uint32_t address, uint8_t *buffer, uint16_t numbytes) { + eepromRead(address, (void *)buffer, numbytes); + return numbytes;; +} + +#include "../../common/compression.cpp" \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/src/drawing.cpp b/ARM_Tag_FW/Newton_M3_nRF52811/src/drawing.cpp index ff66b505..8ea4242e 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/src/drawing.cpp +++ b/ARM_Tag_FW/Newton_M3_nRF52811/src/drawing.cpp @@ -18,632 +18,13 @@ #include "epd_driver/epd_interface.h" -#define EEPROM_XFER_BLOCKSIZE 512 // shouldn't be any less than 256 bytes probably -#define DRAWITEM_LIST_SIZE 24 +#include "settings.h" -static drawItem *drawItems[DRAWITEM_LIST_SIZE] = {0}; +#include "compression.h" -void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask) { - drawItem *di = new drawItem; - - di->setRotation(ro); - if (di->direction ^ epd->drawDirectionRight) { - int16_t temp = x; - x = y; - y = temp; - } - - uint16_t originalWidthBytes = (((uint16_t *)image)[0]) / 8; - uint16_t size = 0; - uint16_t width = ((uint16_t *)image)[0]; - - // find out if the original data was aligned in one byte; if not, add a byte - if (((uint16_t *)image)[0] % 8) originalWidthBytes++; - - // if we're drawing in X direction, we shift the content here. Add extra space for shifting! - if (!di->direction) { - width += x % 8; - } - - // check if the size is aligned in bytes; if not, add an extra for good measure; - if (width % 8) { - width /= 8; - width++; - } else { - width /= 8; - } - - size = width * ((uint16_t *)image)[1]; - size += 2; // not needed - - uint8_t *im = (uint8_t *)calloc(size, 1); - - for (uint16_t copyY = 0; copyY < ((uint16_t *)image)[1]; copyY++) { - memcpy(im + (copyY * width), image + 4 + (copyY * originalWidthBytes), originalWidthBytes); - - // if we draw in X direction, we need to shift bytes in the array - if (!di->direction && (x % 8)) { - drawItem::shiftBytesRight(im + (copyY * width), x % 8, width); - } - } - - di->addItem(im, width * 8, ((uint16_t *)image)[1]); - - di->xpos = x; - di->ypos = y; - di->color = color; - if (mask) - di->type = drawItem::drawType::DRAW_MASK; - else - di->type = drawItem::drawType::DRAW_BUFFERED_1BPP; - - di->addToList(); +uint32_t __attribute__((always_inline)) inline HAL_flashRead(uint32_t address, uint8_t *buffer, uint32_t num){ + eepromRead(address, buffer, num); + return num; } -void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image) { - drawItem *di = new drawItem; - - di->setRotation(ro); - - if (di->direction ^ epd->drawDirectionRight) { - int16_t temp = x; - x = y; - y = temp; - } - - di->addItem((uint8_t *)(image + 4), ((uint16_t *)image)[0], ((uint16_t *)image)[1]); - - di->xpos = x; - di->ypos = y; - di->color = color; - di->cleanUp = false; - di->type = drawItem::drawType::DRAW_BUFFERED_1BPP; - di->addToList(); -} - -void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...) { - char out_buffer[256]; - va_list lst; - va_start(lst, c); - vsnprintf(out_buffer, 255, c, lst); - va_end(lst); - - QRCode qrcode; - // Allocate a chunk of memory to store the QR code - uint8_t qrcodeBytes[qrcode_getBufferSize(version)]; - qrcode_initText(&qrcode, qrcodeBytes, version, ECC_LOW, out_buffer); - - drawItem *di = new drawItem; - di->setRotation(rotation::ROTATE_0); - - uint8_t xbytes = (qrcode.size * scale) / 8; - if (qrcode.size % 8) xbytes++; - - uint16_t size = xbytes * (qrcode.size * scale); - uint8_t *im = (uint8_t *)calloc(size + 1, 1); - - uint8_t *qrbuf = im; - - for (uint8_t qry = 0; qry < qrcode.size; qry++) { - for (uint8_t scale_y = 0; scale_y < scale; scale_y++) { - for (uint8_t qrx = 0; qrx < qrcode.size; qrx++) { - for (uint8_t scale_x = 0; scale_x < scale; scale_x++) { - if (qrcode_getModule(&qrcode, qrx, qry)) { - // Calculate the position in the framebuffer for the scaled pixel - uint8_t scaled_qrx = (qrx * scale) + scale_x; - uint8_t scaled_qry = (qry * scale) + scale_y; - - // Calculate the byte and bit positions in the framebuffer - uint8_t fb_byte = scaled_qrx / 8; - uint8_t fb_bit = 7 - (scaled_qrx % 8); - - // Set the bit in the framebuffer - qrbuf[fb_byte + (scaled_qry * xbytes)] |= (1 << fb_bit); - } - } - } - } - } - - di->addItem(im, xbytes * 8, qrcode.size * scale); - - di->xpos = x; - di->ypos = y; - di->color = 0; - di->type = drawItem::drawType::DRAW_BUFFERED_1BPP; - di->addToList(); -} - -void drawImageAtAddress(uint32_t addr, uint8_t lut) { - powerUp(INIT_EEPROM); - uint8_t *xferbuffer = (uint8_t *)malloc(EEPROM_XFER_BLOCKSIZE); - struct EepromImageHeader *eih = (struct EepromImageHeader *)xferbuffer; - eepromRead(addr, xferbuffer, sizeof(struct EepromImageHeader)); - switch (eih->dataType) { - case DATATYPE_IMG_RAW_1BPP: { - drawItem *di = new drawItem; - // di->setRotation(ro); - di->xpos = 0; - di->ypos = 0; - di->color = 0; - di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes); - di->type = drawItem::drawType::DRAW_EEPROM_1BPP; - di->direction = false; - di->cleanUp = false; - di->addToList(); - } break; - case DATATYPE_IMG_RAW_2BPP: { - drawItem *di = new drawItem; - // di->setRotation(ro); - di->xpos = 0; - di->ypos = 0; - di->color = 0; - di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes); - di->type = drawItem::drawType::DRAW_EEPROM_2BPP; - di->direction = false; - di->cleanUp = false; - di->addToList(); - } break; - } - free(xferbuffer); - addOverlay(); - draw(); -} - -void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) { - uint16_t widthBytes = width / 8; - if (width % 8) widthBytes++; - uint32_t framebufferSize = widthBytes * height; - uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1); - if (framebuffer == NULL) { - return; - } - - ((uint16_t *)framebuffer)[0] = width; - ((uint16_t *)framebuffer)[1] = height; - - framebuffer += 4; - - uint16_t w = width - 1; - uint16_t x = 1; - while (w--) { - framebuffer[(x / 8)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); - x++; - } - for (uint16_t curY = 1; curY < (height - 1); curY++) { - framebuffer[widthBytes * curY] = 0x80; - if (width % 8) - framebuffer[(widthBytes * curY) + widthBytes - 1] = (uint8_t)(1 << (7 - ((uint8_t)width % 8))); - else - framebuffer[(widthBytes * curY) + widthBytes - 1] = 0x01; - } - w = width - 1; - x = 1; - while (w--) { - framebuffer[(x / 8) + ((height - 1) * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); - x++; - } - framebuffer -= 4; - addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_NORMAL); - free(framebuffer); -} - -void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) { - uint16_t widthBytes = width / 8; - if (width % 8) widthBytes++; - uint32_t framebufferSize = widthBytes * height; - uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1); - if (framebuffer == NULL) { - return; - } - - ((uint16_t *)framebuffer)[0] = width; - ((uint16_t *)framebuffer)[1] = height; - - framebuffer += 4; - - for (uint16_t curY = 0; curY < height; curY++) { - uint16_t w = width; - uint16_t x = 0; - while (w--) { - framebuffer[(x / 8) + (curY * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); - x++; - } - } - - framebuffer -= 4; - addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_INVERTED); - free(framebuffer); -} - -// drawItem (sprite) functions -void drawItem::shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len) { - // Ensure the shift value is within bounds (0 to 7) - shift = shift % 8; - - // Handle the case where shift is 0 or len is 0 - if (shift == 0 || len == 0) { - return; - } - - // Loop through the array from right to left - for (int i = len - 1; i > 0; i--) { - // Perform the shift by combining bits from the current byte - // and the next byte to its right - data[i] = (data[i] >> shift) | (data[i - 1] << (8 - shift)); - } - - // For the leftmost byte, simply shift it to the right - data[0] >>= shift; -} - -uint8_t drawItem::bitReverse(uint8_t byte) { - byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xAA); - byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xCC); - byte = (byte >> 4) | (byte << 4); - return byte; -} - -void drawItem::reverseBytes(uint8_t *src, uint8_t src_len) { - // Check for valid input - if (src == NULL || src_len == 0) { - return; - } - - // Reverse the entire source array - for (uint8_t i = 0; i < src_len / 2; i++) { - uint8_t temp = src[i]; - src[i] = src[src_len - i - 1]; - src[src_len - i - 1] = temp; - } - // Reverse the bits within the bytes - for (uint8_t i = 0; i < src_len; i++) { - src[i] = bitReverse(src[i]); - } -} - -void drawItem::copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset) { - switch (type) { - case DRAW_MASK: - for (uint8_t i = 0; i < src_len; i++) { - dst[i + offset] &= ~(src[i]); - } - break; - default: - for (uint8_t i = 0; i < src_len; i++) { - dst[i + offset] |= src[i]; - } - break; - } -} - -void drawItem::renderDrawLine(uint8_t *line, uint16_t number, uint8_t c) { - drawItem *curDrawItem; - for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) { - curDrawItem = drawItems[i]; - if (curDrawItem != nullptr) { - curDrawItem->getDrawLine(line, number, c); - } - } -} - -void drawItem::flushDrawItems() { - drawItem *curDrawItem; - for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) { - curDrawItem = drawItems[i]; - if (curDrawItem != nullptr) { - delete curDrawItem; - drawItems[i] = nullptr; - } - } -} - -void drawItem::getXLine(uint8_t *line, uint16_t y, uint8_t c) { - switch (type) { - case DRAW_FONT: - case DRAW_BUFFERED_1BPP: - case DRAW_MASK: - if (c != color) return; - if ((y >= ypos) && (y < height + ypos)) { // was y > ypos, not >= - // y = height-y; - if (mirrorV) { - if (mirrorH) { - reverseBytes(&buffer[((height - (y - ypos)) * widthBytes)], widthBytes); - // reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes); - } else { - reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes); - } - } - if (mirrorH) { - copyWithByteShift(line, &buffer[((height - (y - ypos)) * widthBytes)], widthBytes, xpos / 8); - } else { - copyWithByteShift(line, &buffer[((y - ypos) * widthBytes)], widthBytes, xpos / 8); - } - } - break; - case DRAW_EEPROM_1BPP: - if (c != color) return; - if (epd->drawDirectionRight) - y = epd->effectiveYRes - 1 - y; - eepromRead((uint32_t)buffer + sizeof(struct EepromImageHeader) + (y * (epd->effectiveXRes / 8)), line, (epd->effectiveXRes / 8)); - break; - case DRAW_EEPROM_2BPP: - if (epd->drawDirectionRight) - y = epd->effectiveYRes - 1 - y; - eepromRead((uint32_t)buffer + sizeof(struct EepromImageHeader) + ((y + (c * epd->effectiveYRes)) * (epd->effectiveXRes / 8)), line, (epd->effectiveXRes / 8)); - break; - } -} - -void drawItem::getYLine(uint8_t *line, uint16_t x, uint8_t c) { - switch (type) { - case DRAW_FONT: - case DRAW_BUFFERED_1BPP: - if (c != color) return; - if ((x >= xpos) && (x < width + xpos)) { - x -= xpos; - for (uint16_t curY = 0; curY < height; curY++) { - uint16_t curYMirrored = curY; - if (!mirrorH) curYMirrored = height - 1 - curY; - if (mirrorV) { - if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) { - line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); - } - } else { - if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) { - line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); - } - } - } - } - break; - case DRAW_MASK: - if (c != color) return; - if ((x >= xpos) && (x < width + xpos)) { - x -= xpos; - for (uint16_t curY = 0; curY < height; curY++) { - uint16_t curYMirrored = curY; - if (!mirrorH) curYMirrored = height - 1 - curY; - if (mirrorV) { - if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) { - line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8))); - } - } else { - if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) { - line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8))); - } - } - } - } - break; - default: - break; - } -} - -void drawItem::getDrawLine(uint8_t *line, uint16_t number, uint8_t c) { - if (direction) { - getYLine(line, number, c); - } else { - getXLine(line, number, c); - } -} - -void drawItem::addItem(uint8_t *data, uint16_t w, uint16_t h) { - width = w; - height = h; - widthBytes = w / 8; - if (w % 8) widthBytes++; - buffer = data; -} - -bool drawItem::addToList() { - for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) { - if (drawItems[i] == nullptr) { - drawItems[i] = this; - return true; - }; - } - return false; -} - -drawItem::~drawItem() { - if (cleanUp) { - free(buffer); - } -} - -drawItem::drawItem() { - if (epd->drawDirectionRight) { - direction = true; - mirrorH = true; - } -} - -void drawItem::setRotation(enum rotation ro) { - if (epd->drawDirectionRight) { - direction = true; - mirrorH = true; - } - - switch (ro) { - case ROTATE_0: - break; - case ROTATE_270: - direction = !direction; - mirrorH = !mirrorH; - mirrorV = !mirrorV; - break; - case ROTATE_180: - mirrorH = !mirrorH; - mirrorV = !mirrorV; - break; - case ROTATE_90: - direction = !direction; - break; - }; -} - -// font rendering functions -fontrender::fontrender(const GFXfont *font) { - gfxFont = (GFXfont *)font; -} - -void fontrender::setFont(const GFXfont *font) { - gfxFont = (GFXfont *)font; -} - -void fontrender::drawFastHLine(uint16_t x, uint16_t y, uint16_t w) { - while (w--) { - fb[(x / 8) + (y * bufferByteWidth)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); - x++; - } -} - -void fontrender::fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { - for (uint16_t curY = y; curY < y + h; curY++) { - drawFastHLine(x, curY, w); - } -} - -uint8_t fontrender::getCharWidth(uint16_t c) { - if ((c >= gfxFont->first) && (c <= gfxFont->last)) { - c -= gfxFont->first; - // GFXglyph *glyph = &(((GFXglyph *)pgm_read_dword(&gfxFont->glyph))[c]); - GFXglyph *glyph = &(gfxFont->glyph[c]); - return glyph->xAdvance; - } - return 0; -} - -uint8_t fontrender::drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size) { - // Filter out bad characters not present in font - if ((c >= gfxFont->first) && (c <= gfxFont->last)) { - c -= gfxFont->first; - GFXglyph *glyph = &(gfxFont->glyph[c]); - uint8_t *bitmap = (uint8_t *)gfxFont->bitmap; - uint32_t bo = glyph->bitmapOffset; - uint8_t w = glyph->width, - h = glyph->height; - int8_t xo = glyph->xOffset, - yo = glyph->yOffset; - uint8_t xx, yy, bits = 0, bit = 0; - int16_t xo16 = 0, yo16 = 0; - - if (size > 1) { - xo16 = xo; - yo16 = yo; - } - - // GFXFF rendering speed up - uint16_t hpc = 0; // Horizontal foreground pixel count - for (yy = 0; yy < h; yy++) { - for (xx = 0; xx < w; xx++) { - if (bit == 0) { - bits = bitmap[bo++]; - bit = 0x80; - } - if (bits & bit) - hpc++; - else { - if (hpc) { - if (size == 1) - drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc); - else - fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size); - hpc = 0; - } - } - bit >>= 1; - } - // Draw pixels for this line as we are about to increment yy - if (hpc) { - if (size == 1) - drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc); - else - fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size); - hpc = 0; - } - } - return glyph->xAdvance; - } - return 0; -} - -void fontrender::epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...) { - drawItem *di = new drawItem; - if (di == nullptr) return; - di->setRotation(ro); - - // prepare a drawItem, exchange x/y if necessary. - if (di->direction ^ epd->drawDirectionRight) { - int16_t temp = x; - x = y; - y = temp; - } - - // output string using vsnprintf - char out_buffer[256]; - va_list lst; - va_start(lst, c); - uint8_t len = vsnprintf(out_buffer, 255, c, lst); - va_end(lst); - - // account for offset in font rendering - if (!di->direction) { - Xpixels = x % 8; // total drawing width increased by x%8 - } else { - Xpixels = 0; - } - - // find out the total length of the string - for (uint8_t c = 0; c < len; c++) { - Xpixels += (uint16_t)getCharWidth(out_buffer[c]); - } - - // find out the high and low points for given font - int8_t high = 0; - int8_t low = 0; - for (uint8_t curchar = 0; curchar < len; curchar++) { - uint8_t c = out_buffer[curchar]; - if ((c >= gfxFont->first) && (c <= gfxFont->last)) { - c -= gfxFont->first; - int8_t glyphUL = gfxFont->glyph[c].yOffset; - if (glyphUL < high) high = glyphUL; - int8_t glyphHeight = gfxFont->glyph[c].height; - if ((glyphUL + glyphHeight) > low) low = glyphUL + glyphHeight; - } - } - // Actual font height (reduces memory footprint) - int8_t height = -1 * (high - low) + 1; - - // determine actual width - bufferByteWidth = Xpixels / 8; - if (Xpixels % 8) bufferByteWidth++; - - // allocate framebuffer - fb = (uint8_t *)calloc(bufferByteWidth * height, 1); - - if (!fb) { - printf("Failed to allocate buffer for drawitem, we can't render this text!\n"); - delete di; - return; - } - - uint16_t curX; - if (!di->direction) { - curX = x % 8; // start drawing at x%8s - } else { - curX = 0; - } - for (uint8_t c = 0; c < len; c++) { - curX += (uint16_t)drawChar(curX, height - low, out_buffer[c], 1); - } - - di->addItem(fb, curX, height); - di->ypos = y; - di->xpos = x; - di->color = color; - di->type = drawItem::drawType::DRAW_FONT; - di->addToList(); -} +#include "../../common/drawing.cpp" \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/src/epd_driver/uc8159.cpp b/ARM_Tag_FW/Newton_M3_nRF52811/src/epd_driver/uc8159.cpp index 5226a970..1ecc6b7c 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/src/epd_driver/uc8159.cpp +++ b/ARM_Tag_FW/Newton_M3_nRF52811/src/epd_driver/uc8159.cpp @@ -197,27 +197,38 @@ void uc8159::selectLUT(uint8_t lut) { } void uc8159::epdWriteDisplayData() { - uint8_t screenrow_bw[epd->effectiveXRes / 8]; - uint8_t screenrow_r[epd->effectiveXRes / 8]; - uint8_t screenrowInterleaved[epd->effectiveXRes / 8 * 4]; + uint8_t blocksize = 16; + uint16_t byteWidth = this->effectiveXRes / 8; + uint8_t screenrow_bw[byteWidth * blocksize]; + uint8_t screenrow_r[byteWidth * blocksize]; + uint8_t screenrowInterleaved[byteWidth * 4]; epd_cmd(CMD_DISPLAY_START_TRANSMISSION_DTM1); markData(); epdSelect(); - for (uint16_t curY = 0; curY < epd->effectiveYRes; curY++) { - memset(screenrow_bw, 0, epd->effectiveXRes / 8); - memset(screenrow_r, 0, epd->effectiveXRes / 8); - drawItem::renderDrawLine(screenrow_bw, curY, 0); - drawItem::renderDrawLine(screenrow_r, curY, 1); - if (curY != 0) { + + for (uint16_t curY = 0; curY < this->effectiveYRes; curY += blocksize) { // + wdt30s(); + memset(screenrow_bw, 0, byteWidth * blocksize); + memset(screenrow_r, 0, byteWidth * blocksize); + + for (uint8_t bcount = 0; bcount < blocksize; bcount++) { + drawItem::renderDrawLine(screenrow_bw + (byteWidth * bcount), curY + bcount, 0); + } + for (uint8_t bcount = 0; bcount < blocksize; bcount++) { + drawItem::renderDrawLine(screenrow_r + (byteWidth * bcount), curY + bcount, 1); + } + + for (uint8_t bcount = 0; bcount < blocksize; bcount++) { + for (uint16_t curX = 0; curX < (byteWidth); curX++) { + interleaveColorToBuffer(screenrowInterleaved + (curX * 4), screenrow_bw[curX + (byteWidth * bcount)], screenrow_r[curX + (byteWidth * bcount)]); + } + + epdSPIAsyncWrite(screenrowInterleaved, byteWidth * 4); epdSPIWait(); epdDeselect(); epdSelect(); } - for (uint16_t curX = 0; curX < (epd->effectiveXRes / 8); curX++) { - interleaveColorToBuffer(screenrowInterleaved + (curX * 4), screenrow_bw[curX], screenrow_r[curX]); - } - epdSPIAsyncWrite(screenrowInterleaved, epd->effectiveXRes / 8 * 4); } epdSPIWait(); @@ -230,7 +241,7 @@ void uc8159::epdWriteDisplayData() { void uc8159::draw() { delay(1); drawNoWait(); - epdBusyWaitRising(25000); + epdBusyWaitRising(30000); } void uc8159::drawNoWait() { epdWriteDisplayData(); @@ -238,5 +249,5 @@ void uc8159::drawNoWait() { epdWrite(CMD_DISPLAY_REFRESH, 0); } void uc8159::epdWaitRdy() { - epdBusyWaitRising(25000); + epdBusyWaitRising(30000); } \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/src/settings.cpp b/ARM_Tag_FW/Newton_M3_nRF52811/src/settings.cpp index f4243de2..91f028ba 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/src/settings.cpp +++ b/ARM_Tag_FW/Newton_M3_nRF52811/src/settings.cpp @@ -20,8 +20,6 @@ #define SETTINGS_MAGIC 0xABBA5AA5 struct tagsettings __xdata tagSettings = {0}; -extern uint8_t __xdata blockbuffer[]; -uint8_t* __xdata settingsTempBuffer = 1024 + blockbuffer; void loadDefaultSettings() { tagSettings.settingsVer = SETTINGS_STRUCT_VERSION; @@ -57,6 +55,7 @@ static void upgradeSettings() { } void loadSettings() { + uint8_t settingsTempBuffer[sizeof(struct tagsettings)]; eepromRead(EEPROM_SETTINGS_AREA_START+4, (void*)settingsTempBuffer, sizeof(struct tagsettings)); memcpy((void*)&tagSettings, (void*)settingsTempBuffer, sizeof(struct tagsettings)); uint32_t valid = 0; diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/src/syncedproto.cpp b/ARM_Tag_FW/Newton_M3_nRF52811/src/syncedproto.cpp index 6b34e91f..b5a10570 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/src/syncedproto.cpp +++ b/ARM_Tag_FW/Newton_M3_nRF52811/src/syncedproto.cpp @@ -25,411 +25,28 @@ struct fwmetadata { #define EEPROM_SETTINGS_SIZE 4096 -#define BLOCKSIZE_MS 225 // was 270 +#define BLOCKSIZE_MS 240 // was 270 -// download-stuff -uint8_t blockbuffer[BLOCK_XFER_BUFFER_SIZE] = {0}; -static struct blockRequest curBlock = {0}; // used by the block-requester, contains the next request that we'll send -static uint64_t curDispDataVer = 0; -static struct AvailDataInfo xferDataInfo = {0}; // last 'AvailDataInfo' we received from the AP -static bool requestPartialBlock = false; // if we should ask the AP to get this block from the host or not -#define BLOCK_TRANSFER_ATTEMPTS 5 +#define FWNRF +#define LEDSENABLED +#define PERSISTENTVAR +#define HAL_TIMER_TICK 1 +#define EEPROM_IMG_START 0 -uint8_t xferImgSlot = 0xFF; -uint8_t curImgSlot = 0xFF; -static uint32_t curHighSlotId = 0; -static uint8_t nextImgSlot = 0; -static uint8_t imgSlots = 0; -// stuff we need to keep track of related to the network/AP -uint8_t APmac[8] = {0}; -uint16_t APsrcPan = 0; -uint8_t mSelfMac[8] = {0}; -static uint8_t seq = 0; -uint8_t currentChannel = 0; +#define HAL_PacketRX commsRxUnencrypted +#define HAL_msDelay delay -// buffer we use to prepare/read packets -static uint8_t inBuffer[128] = {0}; -static uint8_t outBuffer[128] = {0}; +void dump(const uint8_t *a, const uint16_t l); -extern void executeCommand(uint8_t cmd); // this is defined in main.c - -// tools -static uint8_t getPacketType(const void *buffer) { - const struct MacFcs *fcs = (MacFcs *)buffer; - if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { - // broadcast frame - uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameBcast)]; - return type; - } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { - // normal frame - uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameNormal)]; - return type; - } - return 0; -} -static bool pktIsUnicast(const void *buffer) { - const struct MacFcs *fcs = (MacFcs *)buffer; - if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { - return false; - } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { - // normal frame - return true; - } - // unknown type... - return false; -} -void dump(const uint8_t *a, const uint16_t l) { - printf("\n "); -#define ROWS 16 - for (uint8_t c = 0; c < ROWS; c++) { - printf(" %02X", c); - } - printf("\n--------"); - for (uint8_t c = 0; c < ROWS; c++) { - printf("---"); - } - for (uint16_t c = 0; c < l; c++) { - if ((c % ROWS) == 0) { - printf("\n0x%04X | ", c); - } - printf("%02X ", a[c]); - } - printf("\n--------"); - for (uint8_t c = 0; c < ROWS; c++) { - printf("---"); - } - printf("\n"); -} -static bool checkCRC(const void *p, const uint8_t len) { - uint8_t total = 0; - for (uint8_t c = 1; c < len; c++) { - total += ((uint8_t *)p)[c]; - } - // printf("CRC: rx %d, calc %d\n", ((uint8_t *)p)[0], total); - return ((uint8_t *)p)[0] == total; -} -static void addCRC(void *p, const uint8_t len) { - uint8_t total = 0; - for (uint8_t c = 1; c < len; c++) { - total += ((uint8_t *)p)[c]; - } - ((uint8_t *)p)[0] = total; +static void saveUpdateMetadata(uint32_t size) { + struct fwmetadata metadata; + metadata.magic1 = MAGIC1; + metadata.magic2 = MAGIC2; + metadata.fwsize = size; + eepromWrite(FW_METADATA_LOC, &metadata, sizeof(struct fwmetadata)); } -// radio stuff -static void sendPing() { - struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); - memset(outBuffer, 0, sizeof(struct MacFrameBcast) + 2 + 4); - outBuffer[0] = sizeof(struct MacFrameBcast) + 1 + 2; - outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_PING; - memcpy(txframe->src, mSelfMac, 8); - txframe->fcs.frameType = 1; - txframe->fcs.ackReqd = 1; - txframe->fcs.destAddrType = 2; - txframe->fcs.srcAddrType = 3; - txframe->seq = seq++; - txframe->dstPan = PROTO_PAN_ID; - txframe->dstAddr = 0xFFFF; - txframe->srcPan = PROTO_PAN_ID; - commsTxNoCpy(outBuffer); -} -uint8_t detectAP(const uint8_t channel) { - uint32_t t; - radioRxEnable(false); - radioSetChannel(channel); - radioRxFlush(); - radioRxEnable(true); - for (uint8_t c = 1; c <= MAXIMUM_PING_ATTEMPTS; c++) { - sendPing(); - t = millis() + (PING_REPLY_WINDOW); - while (millis() < t) { - int8_t ret = commsRxUnencrypted(inBuffer); - if (ret > 1) { - // dump(inBuffer+sizeof(struct MacFrameNormal),32); - if ((inBuffer[sizeof(struct MacFrameNormal) + 1] == channel) && (getPacketType(inBuffer) == PKT_PONG)) { - if (pktIsUnicast(inBuffer)) { - struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; - memcpy(APmac, f->src, 8); - APsrcPan = f->pan; - return c; - } - } - } - delay(1); - } - } - return 0; -} - -// data xfer stuff -static void sendShortAvailDataReq() { - struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); - outBuffer[0] = sizeof(struct MacFrameBcast) + 1 + 2; - outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_AVAIL_DATA_SHORTREQ; - memcpy(txframe->src, mSelfMac, 8); - outBuffer[1] = 0x21; - outBuffer[2] = 0xC8; // quickly set txframe fcs structure for broadcast packet - txframe->seq = seq++; - txframe->dstPan = PROTO_PAN_ID; - txframe->dstAddr = 0xFFFF; - txframe->srcPan = PROTO_PAN_ID; - commsTxNoCpy(outBuffer); -} -static void sendAvailDataReq() { - struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); - memset(outBuffer, 0, sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 4); - struct AvailDataReq *availreq = (struct AvailDataReq *)(outBuffer + 2 + sizeof(struct MacFrameBcast)); - outBuffer[0] = sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 2; - outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_AVAIL_DATA_REQ; - memcpy(txframe->src, mSelfMac, 8); - txframe->fcs.frameType = 1; - txframe->fcs.ackReqd = 1; - txframe->fcs.destAddrType = 2; - txframe->fcs.srcAddrType = 3; - txframe->seq = seq++; - txframe->dstPan = PROTO_PAN_ID; - txframe->dstAddr = 0xFFFF; - txframe->srcPan = PROTO_PAN_ID; - // TODO: send some (more) meaningful data - availreq->hwType = tag.OEPLtype; - availreq->wakeupReason = wakeUpReason; - availreq->lastPacketRSSI = mLastRSSI; - availreq->lastPacketLQI = mLastLqi; - availreq->temperature = temperature; - availreq->batteryMv = batteryVoltage; - availreq->capabilities = capabilities; - availreq->currentChannel = currentChannel; - availreq->tagSoftwareVersion = FW_VERSION; - addCRC(availreq, sizeof(struct AvailDataReq)); - commsTxNoCpy(outBuffer); -} -struct AvailDataInfo *getAvailDataInfo() { - radioRxEnable(true); - uint32_t t; - for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { - sendAvailDataReq(); - t = millis() + (DATA_REQ_RX_WINDOW_SIZE); - while (millis() < t) { - int8_t ret = commsRxUnencrypted(inBuffer); - if (ret > 1) { - if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) { - if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) { - struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; - memcpy(APmac, f->src, 8); - APsrcPan = f->pan; - dataReqLastAttempt = c; - return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - } - } - } - } - } - dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; - return NULL; -} -struct AvailDataInfo *getShortAvailDataInfo() { - radioRxEnable(true); - uint32_t t; - for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { - sendShortAvailDataReq(); - // sendAvailDataReq(); - t = millis() + (DATA_REQ_RX_WINDOW_SIZE); - while (millis() < t) { - int8_t ret = commsRxUnencrypted(inBuffer); - if (ret > 1) { - if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) { - if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) { - struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; - memcpy(APmac, f->src, 8); - APsrcPan = f->pan; - dataReqLastAttempt = c; - return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - } - } - } - } - } - dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; - return NULL; -} -static bool processBlockPart(const struct blockPart *bp) { - uint16_t start = bp->blockPart * BLOCK_PART_DATA_SIZE; - uint16_t size = BLOCK_PART_DATA_SIZE; - // validate if it's okay to copy data - if (bp->blockId != curBlock.blockId) { - // printf("got a packet for block %02X\n", bp->blockId); - return false; - } - if (start >= (sizeof(blockbuffer) - 1)) - return false; - if (bp->blockPart > BLOCK_MAX_PARTS) - return false; - if ((start + size) > sizeof(blockbuffer)) { - size = sizeof(blockbuffer) - start; - } - if (checkCRC(bp, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE)) { - // copy block data to buffer - memcpy((void *)(blockbuffer + start), (const void *)bp->data, size); - // we don't need this block anymore, set bit to 0 so we don't request it again - curBlock.requestedParts[bp->blockPart / 8] &= ~(1 << (bp->blockPart % 8)); - return true; - } else { - return false; - } -} -static bool blockRxLoop(const uint32_t timeout) { - uint32_t t; - bool success = false; - radioRxEnable(true); - t = millis() + ((timeout + 20)); - - bool blockComplete = false; - - while (millis() < t) { - int8_t ret = commsRxUnencrypted(inBuffer); - if (ret > 1) { - if ((getPacketType(inBuffer) == PKT_BLOCK_PART) && pktIsUnicast(inBuffer)) { - struct blockPart *bp = (struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - success = processBlockPart(bp); - } - blockComplete = true; - for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { - if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) - blockComplete = false; - } - if (blockComplete) { - radioRxEnable(false); - radioRxFlush(); - } - } - } - if (blockComplete == false) { - radioRxEnable(false); - radioRxFlush(); - } - return success; -} -static struct blockRequestAck *continueToRX() { - struct blockRequestAck *ack = (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - ack->pleaseWaitMs = 0; - return ack; -} -static void sendBlockRequest() { - memset(outBuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2); - struct MacFrameNormal *f = (struct MacFrameNormal *)(outBuffer + 1); - struct blockRequest *blockreq = (struct blockRequest *)(outBuffer + 2 + sizeof(struct MacFrameNormal)); - outBuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2; - if (requestPartialBlock) { - ; - outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PARTIAL_REQUEST; - } else { - outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST; - } - memcpy(f->src, mSelfMac, 8); - memcpy(f->dst, APmac, 8); - f->fcs.frameType = 1; - f->fcs.secure = 0; - f->fcs.framePending = 0; - f->fcs.ackReqd = 0; - f->fcs.panIdCompressed = 1; - f->fcs.destAddrType = 3; - f->fcs.frameVer = 0; - f->fcs.srcAddrType = 3; - f->seq = seq++; - f->pan = APsrcPan; - memcpy(blockreq, &curBlock, sizeof(struct blockRequest)); - // printf("req ver: %02X%02X%02X%02X%02X%02X%02X%02X\n", ((uint8_t*)&blockreq->ver)[0],((uint8_t*)&blockreq->ver)[1],((uint8_t*)&blockreq->ver)[2],((uint8_t*)&blockreq->ver)[3],((uint8_t*)&blockreq->ver)[4],((uint8_t*)&blockreq->ver)[5],((uint8_t*)&blockreq->ver)[6],((uint8_t*)&blockreq->ver)[7]); - addCRC(blockreq, sizeof(struct blockRequest)); - commsTxNoCpy(outBuffer); -} -static struct blockRequestAck *performBlockRequest() { - uint32_t t; - radioRxEnable(true); - radioRxFlush(); - for (uint8_t c = 0; c < 30; c++) { - sendBlockRequest(); - t = millis() + ((7UL + c / 10)); - do { - int8_t ret = commsRxUnencrypted(inBuffer); - if (ret > 1) { - switch (getPacketType(inBuffer)) { - case PKT_BLOCK_REQUEST_ACK: - if (checkCRC((inBuffer + sizeof(struct MacFrameNormal) + 1), sizeof(struct blockRequestAck))) - return (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); - break; - case PKT_BLOCK_PART: - // block already started while we were waiting for a get block reply - // printf("!"); - // processBlockPart((struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1)); - return continueToRX(); - break; - case PKT_CANCEL_XFER: - return NULL; - default: - printf("pkt w/type %02X\n", getPacketType(inBuffer)); - break; - } - } - - } while (millis() < t); - } - return continueToRX(); - // return NULL; -} -static void sendXferCompletePacket() { - memset(outBuffer, 0, sizeof(struct MacFrameNormal) + 2 + 4); - struct MacFrameNormal *f = (struct MacFrameNormal *)(outBuffer + 1); - outBuffer[0] = sizeof(struct MacFrameNormal) + 2 + 2; - outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE; - memcpy(f->src, mSelfMac, 8); - memcpy(f->dst, APmac, 8); - f->fcs.frameType = 1; - f->fcs.secure = 0; - f->fcs.framePending = 0; - f->fcs.ackReqd = 0; - f->fcs.panIdCompressed = 1; - f->fcs.destAddrType = 3; - f->fcs.frameVer = 0; - f->fcs.srcAddrType = 3; - f->pan = APsrcPan; - f->seq = seq++; - commsTxNoCpy(outBuffer); -} -static void sendXferComplete() { - radioRxEnable(true); - - for (uint8_t c = 0; c < 16; c++) { - sendXferCompletePacket(); - uint32_t start = millis(); - while ((millis() - start) < (6UL)) { - int8_t ret = commsRxUnencrypted(inBuffer); - if (ret > 1) { - if (getPacketType(inBuffer) == PKT_XFER_COMPLETE_ACK) { - printf("XFC ACK\n"); - return; - } - } - } - } - printf("XFC NACK!\n"); - return; -} -static bool validateBlockData() { - struct blockData *bd = (struct blockData *)blockbuffer; - // printf("expected len = %04X, checksum=%04X\n", bd->size, bd->checksum); - if (bd->size > BLOCK_XFER_BUFFER_SIZE - sizeof(blockData)) { - printf("Impossible data size; size = %d\n"); - return false; - } - uint16_t t = 0; - for (uint16_t c = 0; c < bd->size; c++) { - t += bd->data[c]; - } - return bd->checksum == t; -} - -// EEprom related stuff static bool validateEepromMD5(uint64_t ver, uint32_t eepromstart, uint32_t flen) { unsigned char hash[16]; char chunk[512]; @@ -450,617 +67,12 @@ static bool validateEepromMD5(uint64_t ver, uint32_t eepromstart, uint32_t flen) bool isValid = ver == *((uint64_t *)hash); if (!isValid) { printf("MD5 failed check! This is what we should get:\n"); - dump((const uint8_t *)&(xferDataInfo.dataVer), 8); + dump((const uint8_t *)&(ver), 8); printf("This is what we got:\n"); dump(hash, 16); } + return true; return isValid; } -static uint32_t getAddressForSlot(const uint8_t s) { - return (tag.imageSize * s); -} -static void getNumSlots() { - uint32_t eeSize = eepromGetSize(); - uint16_t nSlots = (eeSize - EEPROM_SETTINGS_SIZE) / (tag.imageSize >> 8) >> 8; - if (!nSlots) { - printf("eeprom is too small\n"); - while (1) - ; - } else if (nSlots >> 8) { - printf("eeprom is too big, some will be unused\n"); - imgSlots = 254; - } else - imgSlots = nSlots; - printf("EEPROM reported size = %lu, %d slots\n", eeSize, imgSlots); -} -static uint8_t findSlotVer(uint64_t ver) { -#ifdef DEBUGBLOCKS - return 0xFF; // remove me! This forces the tag to re-download each and every upload without checking if it's already in the eeprom somewhere -#endif - uint32_t markerValid = EEPROM_IMG_VALID; - for (uint8_t c = 0; c < imgSlots; c++) { - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - eepromRead(getAddressForSlot(c), eih, sizeof(struct EepromImageHeader)); - if (!memcmp(&eih->validMarker, &markerValid, 4)) { - if (eih->version == ver) { - return c; - } - } - } - return 0xFF; -} - -uint8_t findSlotDataTypeArg(uint8_t arg) { - arg &= (0xF8); // unmatch with the 'preload' bit and LUT bits - for (uint8_t c = 0; c < imgSlots; c++) { - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - eepromRead(getAddressForSlot(c), eih, sizeof(struct EepromImageHeader)); - if (eih->validMarker == EEPROM_IMG_VALID) { - if ((eih->argument & 0xF8) == arg) { - return c; - } - } - } - return 0xFF; -} -uint8_t getEepromImageDataArgument(const uint8_t slot) { - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - eepromRead(getAddressForSlot(slot), eih, sizeof(struct EepromImageHeader)); - return eih->argument; -} -uint8_t findNextSlideshowImage(uint8_t start) { - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - uint8_t c = start; - while (1) { - c++; - if (c > imgSlots) c = 0; - if (c == start) return c; - eepromRead(getAddressForSlot(c), eih, sizeof(struct EepromImageHeader)); - if (eih->validMarker == EEPROM_IMG_VALID) { - if ((eih->argument & 0xF8) == (CUSTOM_IMAGE_SLIDESHOW << 3)) { - return c; - } - } - } -} - -static void eraseUpdateBlock() { - eepromErase(FW_LOC, (FW_METADATA_LOC + EEPROM_ERZ_SECTOR_SZ) / EEPROM_ERZ_SECTOR_SZ); -} -static void eraseImageBlock(const uint8_t c) { - eepromErase(getAddressForSlot(c), tag.imageSize / EEPROM_ERZ_SECTOR_SZ); -} -static void saveUpdateBlockData(uint8_t blockId) { - if (!eepromWrite(FW_LOC + (blockId * BLOCK_DATA_SIZE), blockbuffer + sizeof(struct blockData), BLOCK_DATA_SIZE)) - printf("EEPROM write failed\n"); -} -static void saveUpdateMetadata(uint32_t size) { - struct fwmetadata metadata; - metadata.magic1 = MAGIC1; - metadata.magic2 = MAGIC2; - metadata.fwsize = size; - eepromWrite(FW_METADATA_LOC, &metadata, sizeof(struct fwmetadata)); -} -static void saveImgBlockData(const uint8_t imgSlot, const uint8_t blockId) { - uint32_t length = tag.imageSize - (sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE)); - if (length > 4096) - length = 4096; - - if (!eepromWrite(getAddressForSlot(imgSlot) + sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE), blockbuffer + sizeof(struct blockData), length)) - printf("EEPROM write failed\n"); -} -void eraseImageBlocks() { - for (uint8_t c = 0; c < imgSlots; c++) { - eraseImageBlock(c); - } -} -void drawImageFromEeprom(const uint8_t imgSlot, uint8_t lut) { - drawImageAtAddress(getAddressForSlot(imgSlot), lut); -} -static uint32_t getHighSlotId() { - uint32_t temp = 0; - uint32_t markerValid = EEPROM_IMG_VALID; - for (uint8_t c = 0; c < imgSlots; c++) { - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - eepromRead(getAddressForSlot(c), eih, sizeof(struct EepromImageHeader)); - if (!memcmp(&eih->validMarker, &markerValid, 4)) { - if (temp < eih->id) { - temp = eih->id; - nextImgSlot = c; - } - } - } - printf("found high id=%lu in slot %d\n", temp, nextImgSlot); - return temp; -} - -static uint8_t partsThisBlock = 0; -static uint8_t blockAttempts = 0; // these CAN be local to the function, but for some reason, they won't survive sleep? - // they get overwritten with 7F 32 44 20 00 00 00 00 11, I don't know why. - -static bool getDataBlock(const uint16_t blockSize) { - blockAttempts = BLOCK_TRANSFER_ATTEMPTS; - if (blockSize == BLOCK_DATA_SIZE) { - partsThisBlock = BLOCK_MAX_PARTS; - memset(curBlock.requestedParts, 0xFF, BLOCK_REQ_PARTS_BYTES); - } else { - partsThisBlock = (sizeof(struct blockData) + blockSize) / BLOCK_PART_DATA_SIZE; - if (blockSize % BLOCK_PART_DATA_SIZE) - partsThisBlock++; - memset(curBlock.requestedParts, 0x00, BLOCK_REQ_PARTS_BYTES); - for (uint8_t c = 0; c < partsThisBlock; c++) { - curBlock.requestedParts[c / 8] |= (1 << (c % 8)); - } - } - - requestPartialBlock = false; // this forces the AP to request the block data from the host - - while (blockAttempts--) { -#ifndef DEBUGBLOCKS - printf("REQ %d ", curBlock.blockId); -#else - printf("REQ %d[", curBlock.blockId); - for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { - if ((c != 0) && (c % 8 == 0)) - printf("]["); - if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) { - printf("R"); - } else { - printf("_"); - } - } - printf("]\n"); -#endif - powerUp(INIT_RADIO); - struct blockRequestAck *ack = performBlockRequest(); - if (ack == NULL) { - printf("Cancelled request\n"); - return false; - } - - doSleep(ack->pleaseWaitMs + 50); - powerUp(INIT_UART | INIT_RADIO); - radioRxEnable(true); - - blockRxLoop(BLOCKSIZE_MS); // BLOCK RX LOOP - receive a block, until the timeout has passed - powerDown(INIT_RADIO); - -#ifdef DEBUGBLOCKS - printf("RX %d[", curBlock.blockId); - for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { - if ((c != 0) && (c % 8 == 0)) - printf("]["); - if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) { - printf("."); - } else { - printf("R"); - } - } - printf("]\n"); -#endif - // check if we got all the parts we needed, e.g: has the block been completed? - bool blockComplete = true; - for (uint8_t c = 0; c < partsThisBlock; c++) { - if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) - blockComplete = false; - } - doSleep(30); - - if (blockComplete) { -#ifndef DEBUGBLOCKS - printf("- COMPLETE\n"); -#endif - if (validateBlockData()) { - // printf("- Validated\n"); - // block download complete, validated - return true; - } else { - for (uint8_t c = 0; c < partsThisBlock; c++) { - curBlock.requestedParts[c / 8] |= (1 << (c % 8)); - } - requestPartialBlock = false; - printf("blk failed validation!\n"); - } - } else { -#ifndef DEBUGBLOCKS - printf("- INCOMPLETE\n"); -#endif - // block incomplete, re-request a partial block - requestPartialBlock = true; - } - } - printf("failed getting block\n"); - return false; -} - -uint16_t dataRequestSize = 0; -uint32_t curXferSize = 0; - -static bool downloadFWUpdate(const struct AvailDataInfo *avail) { - // check if we already started the transfer of this information & haven't completed it - if (!memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8) && xferDataInfo.dataSize) { - // looks like we did. We'll carry on where we left off. - } else { - // start, or restart the transfer from 0. Copy data from the AvailDataInfo struct, and the struct intself. This forces a new transfer - curBlock.blockId = 0; - memcpy(&(curBlock.ver), &(avail->dataVer), 8); - curBlock.type = avail->dataType; - memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - curXferSize = avail->dataSize; - eraseUpdateBlock(); - } - - while (xferDataInfo.dataSize) { - wdt10s(); - if (xferDataInfo.dataSize > BLOCK_DATA_SIZE) { - // more than one block remaining - dataRequestSize = BLOCK_DATA_SIZE; - } else { - // only one block remains - dataRequestSize = xferDataInfo.dataSize; - } - if (getDataBlock(dataRequestSize)) { - // succesfully downloaded datablock, save to eeprom - powerUp(INIT_EEPROM); - saveUpdateBlockData(curBlock.blockId); - powerDown(INIT_EEPROM); - curBlock.blockId++; - xferDataInfo.dataSize -= dataRequestSize; - } else { - // failed to get the block we wanted, we'll stop for now, maybe resume later - return false; - } - } - - // no more data, download complete - powerUp(INIT_EEPROM); - if (validateEepromMD5(xferDataInfo.dataVer, FW_LOC, curXferSize)) { - // md5 matches - powerDown(INIT_EEPROM); - return true; - } else { - // md5 does not match, invalidate current transfer result, forcing a restart of the transfer - memset((void *)&xferDataInfo, 0, sizeof(struct AvailDataInfo)); - powerDown(INIT_EEPROM); - return false; - } -} - -uint32_t imageSize = 0; -static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) { - powerUp(INIT_EEPROM); - // check if we already started the transfer of this information & haven't completed it - if (!memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8) && xferDataInfo.dataSize) { - // looks like we did. We'll carry on where we left off. - printf("restarting image download"); - // curImgSlot = nextImgSlot; // hmmm - } else { - // new transfer - uint8_t startingSlot = nextImgSlot; - while (1) { - nextImgSlot++; - if (nextImgSlot >= imgSlots) nextImgSlot = 0; - if (nextImgSlot == startingSlot) { - // looped - powerDown(INIT_EEPROM); - printf("no slot available...\n"); - return true; - } - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - eepromRead(getAddressForSlot(nextImgSlot), eih, sizeof(struct EepromImageHeader)); - // checked if the marker is valid - if (eih->validMarker == EEPROM_IMG_VALID) { - struct imageDataTypeArgStruct *eepromDataArgument = (struct imageDataTypeArgStruct *)&(eih->argument); - // if this is a normal type, we can replace it - if (eepromDataArgument->specialType == 0x00) break; - } else { - // invalid header, so safe to overwrite anyway - break; - } - } - - xferImgSlot = nextImgSlot; - - uint8_t attempt = 5; - while (attempt--) { - if (eepromErase(getAddressForSlot(xferImgSlot), tag.imageSize / EEPROM_ERZ_SECTOR_SZ)) goto eraseSuccess; - } - eepromFail: - powerDown(INIT_RADIO); - powerUp(INIT_EPD); - showNoEEPROM(); - powerDown(INIT_EEPROM | INIT_EPD); - doSleep(-1); - NVIC_SystemReset(); - eraseSuccess: - printf("new download, writing to slot %d\n", xferImgSlot); - // start, or restart the transfer. Copy data from the AvailDataInfo struct, and the struct intself. This forces a new transfer - curBlock.blockId = 0; - memcpy(&(curBlock.ver), &(avail->dataVer), 8); - curBlock.type = avail->dataType; - memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - imageSize = xferDataInfo.dataSize; - } - - while (xferDataInfo.dataSize) { - wdt10s(); - if (xferDataInfo.dataSize > BLOCK_DATA_SIZE) { - // more than one block remaining - dataRequestSize = BLOCK_DATA_SIZE; - } else { - // only one block remains - dataRequestSize = xferDataInfo.dataSize; - } - if (getDataBlock(dataRequestSize)) { - // succesfully downloaded datablock, save to eeprom - powerUp(INIT_EEPROM); -#ifdef DEBUGBLOCKS - printf("Saving block %d to slot %d\n", curBlock.blockId, xferImgSlot); -#endif - saveImgBlockData(xferImgSlot, curBlock.blockId); - powerDown(INIT_EEPROM); - curBlock.blockId++; - xferDataInfo.dataSize -= dataRequestSize; - } else { - // failed to get the block we wanted, we'll stop for now, probably resume later - return false; - } - } - // no more data, download complete - - // borrow the blockbuffer temporarily - struct EepromImageHeader *eih = (struct EepromImageHeader *)blockbuffer; - memcpy(&eih->version, &xferDataInfo.dataVer, 8); - eih->validMarker = EEPROM_IMG_VALID; - eih->id = ++curHighSlotId; - eih->size = curXferSize; - eih->dataType = xferDataInfo.dataType; - eih->argument = xferDataInfo.dataTypeArgument; - -#ifdef DEBUGBLOCKS - printf("Now writing datatype 0x%02X to slot %d\n", xferDataInfo.dataType, xferImgSlot); -#endif - - powerUp(INIT_EEPROM); - if (validateEepromMD5(xferDataInfo.dataVer, getAddressForSlot(xferImgSlot) + sizeof(struct EepromImageHeader), imageSize)) { - // md5 matches - eepromWrite(getAddressForSlot(xferImgSlot), eih, sizeof(struct EepromImageHeader)); - powerDown(INIT_EEPROM); - return true; - } else { - // md5 does not match, invalidate current transfer result, forcing a restart of the transfer - memset((void *)&xferDataInfo, 0, sizeof(struct AvailDataInfo)); - powerDown(INIT_EEPROM); - return false; - } -} - -bool processImageDataAvail(struct AvailDataInfo *avail) { - struct imageDataTypeArgStruct arg = *((struct imageDataTypeArgStruct *)avail->dataTypeArgument); - - if (arg.preloadImage) { - printf("Preloading image with type 0x%02X from arg 0x%02X\n", arg.specialType, avail->dataTypeArgument); - powerUp(INIT_EEPROM); - switch (arg.specialType) { - // check if a slot with this argument is already set; if so, erase. Only one of each arg type should exist - default: { - uint8_t slot = findSlotDataTypeArg(avail->dataTypeArgument); - if (slot != 0xFF) { - eepromErase(getAddressForSlot(slot), tag.imageSize / EEPROM_ERZ_SECTOR_SZ); - } - } break; - // regular image preload, there can be multiple of this type in the EEPROM - case CUSTOM_IMAGE_NOCUSTOM: { - // check if a version of this already exists - uint8_t slot = findSlotVer(avail->dataVer); - if (slot != 0xFF) { - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } - } break; - case CUSTOM_IMAGE_SLIDESHOW: - break; - } - powerDown(INIT_EEPROM); - - printf("downloading preload image...\n"); - if (downloadImageDataToEEPROM(avail)) { - // sets xferImgSlot to the right slot - printf("preload complete!\n"); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } else { - return false; - } - - } else { - // check if we're currently displaying this data payload - if (avail->dataVer == curDispDataVer) { - // currently displayed, not doing anything except for sending an XFC - - printf("currently shown image, send xfc\n"); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - - } else { - // currently not displayed - - // try to find the data in the SPI EEPROM - powerUp(INIT_EEPROM); - uint8_t findImgSlot = findSlotVer(avail->dataVer); - powerDown(INIT_EEPROM); - - // Is this image already in a slot somewhere - if (findImgSlot != 0xFF) { - // found a (complete)valid image slot for this version - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - - // mark as completed and draw from EEPROM - memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - xferDataInfo.dataSize = 0; // mark as transfer not pending - - wdt60s(); - curImgSlot = findImgSlot; - powerUp(INIT_EPD | INIT_EEPROM); - drawImageFromEeprom(findImgSlot, arg.lut); - powerDown(INIT_EPD | INIT_EEPROM); - - } else { - // not found in cache, prepare to download - printf("downloading image...\n"); - if (downloadImageDataToEEPROM(avail)) { - // sets xferImgSlot to the right slot - printf("download complete!\n"); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - - // not preload, draw now - wdt60s(); - curImgSlot = xferImgSlot; - powerUp(INIT_EPD | INIT_EEPROM); - drawImageFromEeprom(xferImgSlot, arg.lut); - powerDown(INIT_EPD | INIT_EEPROM); - - } else { - return false; - } - } - - // keep track on what is currently displayed - curDispDataVer = xferDataInfo.dataVer; - return true; - } - } -} - -bool processAvailDataInfo(struct AvailDataInfo *avail) { - switch (avail->dataType) { - case DATATYPE_IMG_RAW_1BPP: - case DATATYPE_IMG_RAW_2BPP: - processImageDataAvail(avail); - break; - case DATATYPE_FW_UPDATE: - powerUp(INIT_EEPROM); - if (downloadFWUpdate(avail)) { - printf("firmware download complete, doing update.\n"); - - powerUp(INIT_EPD); - // showApplyUpdate(); - - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - - powerUp(INIT_EEPROM); - wdt60s(); - saveUpdateMetadata(curXferSize); - NVIC_SystemReset(); - } else { - return false; - } - break; - case DATATYPE_NFC_URL_DIRECT: - case DATATYPE_NFC_RAW_CONTENT: { - // Handle data for the NFC IC (if we have it) - - // check if we actually have the capability to do NFC - if (!(capabilities & CAPABILITY_HAS_NFC)) { - // looks like we don't. mark as complete and then bail! - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } - - printf("NFC URL received\n"); - if (xferDataInfo.dataSize == 0 && !memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8)) { - // we've already downloaded this NFC data, disregard and send XFC - printf("this was the same as the last transfer, disregard\n"); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } - curBlock.blockId = 0; - memcpy(&(curBlock.ver), &(avail->dataVer), 8); - curBlock.type = avail->dataType; - memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - uint16_t nfcsize = avail->dataSize; - wdt10s(); - if (getDataBlock(avail->dataSize)) { - xferDataInfo.dataSize = 0; // mark as transfer not pending - powerUp(INIT_I2C); - if (avail->dataType == DATATYPE_NFC_URL_DIRECT) { - // only one URL (handle NDEF records on the tag) - // loadURLtoNTag(); - } else { - // raw NFC data upload to the NFC IC - // loadRawNTag(nfcsize); - } - delay(13330); - powerDown(INIT_I2C); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } - return false; - break; - } - case DATATYPE_COMMAND_DATA: - memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - if (avail->dataTypeArgument == 4) { - Serial.println("LED CMD"); - setled(xferDataInfo.dataVer, xferDataInfo.dataSize); - } - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - if (avail->dataTypeArgument != 4) { - executeCommand(xferDataInfo.dataTypeArgument); - } - return true; - break; - case DATATYPE_TAG_CONFIG_DATA: - if (xferDataInfo.dataSize == 0 && memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8) == 0) { - printf("this was the same as the last transfer, disregard\n"); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } - curBlock.blockId = 0; - memcpy(&(curBlock.ver), &(avail->dataVer), sizeof(curBlock.ver)); - curBlock.type = avail->dataType; - memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); - wdt10s(); - if (getDataBlock(avail->dataSize)) { - xferDataInfo.dataSize = 0; // mark as transfer not pending - powerUp(INIT_EEPROM); - loadSettingsFromBuffer(sizeof(struct blockData) + blockbuffer); - powerDown(INIT_EEPROM); - powerUp(INIT_RADIO); - sendXferComplete(); - powerDown(INIT_RADIO); - return true; - } - return false; - break; - } - return true; -} - -void initializeProto() { - getNumSlots(); - curHighSlotId = getHighSlotId(); -} +#include "../../common/oepl-protocol.cpp" \ No newline at end of file diff --git a/ARM_Tag_FW/Newton_M3_nRF52811/src/userinterface.cpp b/ARM_Tag_FW/Newton_M3_nRF52811/src/userinterface.cpp index da32efe8..17280d7d 100644 --- a/ARM_Tag_FW/Newton_M3_nRF52811/src/userinterface.cpp +++ b/ARM_Tag_FW/Newton_M3_nRF52811/src/userinterface.cpp @@ -46,7 +46,7 @@ void addOverlay() { } else { lowBatteryShown = false; } -#ifdef DEBUGBLOCKS +#ifdef DEBUG_BUILD fontrender fr(&FreeSansBold18pt7b); drawMask(15, epd->Yres - 53, 129, 33, COLOR_BLACK); drawMask(15, epd->Yres - 53, 129, 33, COLOR_RED); diff --git a/ARM_Tag_FW/common/QRCode b/ARM_Tag_FW/common/QRCode new file mode 160000 index 00000000..eafbde49 --- /dev/null +++ b/ARM_Tag_FW/common/QRCode @@ -0,0 +1 @@ +Subproject commit eafbde494979abc2445c363cc2602230bcbe299c diff --git a/ARM_Tag_FW/common/compression.cpp b/ARM_Tag_FW/common/compression.cpp index 5644cbd7..8074f73e 100644 --- a/ARM_Tag_FW/common/compression.cpp +++ b/ARM_Tag_FW/common/compression.cpp @@ -1,4 +1,4 @@ -#include +// #include std::vector decompContexts; @@ -16,7 +16,7 @@ int decompCallback(TINF_DATA *d) { return dec->getNextCompressedBlockFromFlash(); } } - printf("Couldn't find callback...\n"); + printf("FS: Couldn't find callback...\n"); return -1; } @@ -33,7 +33,7 @@ bool decompress::readHeader() { // read the window size from the zlib header int res = uzlib_zlib_parse_header(this->ctx); if (res < 0) { - printf("FS: Invalid zlib header\n"); + printf("FS: Invalid zlib header\n"); return false; } @@ -45,11 +45,14 @@ bool decompress::readHeader() { printf("FS: Asked to decompress a file with a specified window size of %d, I don't see that happening\n", window); return false; } else { - // printf("FS: Opened compressed file with dictionary size %d\n", window); + //printf("FS: Opened compressed file with dictionary size %d\n", window); } + window = 8192; + // allocate dict/window if not already allocated if (!this->dictionary) this->dictionary = (uint8_t *)malloc(window); + if (!this->dictionary) printf("FS: window malloc failed\n"); uzlib_uncompress_init(this->ctx, this->dictionary, window); return true; @@ -134,28 +137,30 @@ uint32_t decompress::getBlock(uint32_t address, uint8_t *target, uint32_t len) { this->ctx->source = this->compBuffer; compressedPos = 0; decompressedPos = 0; +#ifdef ENABLE_OEPLFS if (this->fromFile) this->getNextCompressedBlockFromFile(); else +#endif this->getNextCompressedBlockFromFlash(); this->ctx->source = this->compBuffer; this->readHeader(); } // skip to the next part of the output stream - uint8_t *temp = nullptr; - if (address != decompressedPos) temp = (uint8_t *)malloc(ZLIB_CACHE_SIZE); - while (this->decompressedPos < address) { - uint32_t readBytes = address - decompressedPos; - if (readBytes > ZLIB_CACHE_SIZE) readBytes = ZLIB_CACHE_SIZE; - decompressedPos += readBytes; - this->ctx->dest = temp; - ctx->dest_start = ctx->dest; - ctx->dest_limit = ctx->dest + readBytes; - uzlib_uncompress(ctx); - } - if (temp) free(temp); + if (address != decompressedPos) { + uint8_t temp[ZLIB_CACHE_SIZE]; + while (this->decompressedPos < address) { + uint32_t readBytes = address - decompressedPos; + if (readBytes > ZLIB_CACHE_SIZE) readBytes = ZLIB_CACHE_SIZE; + decompressedPos += readBytes; + this->ctx->dest = temp; + ctx->dest_start = ctx->dest; + ctx->dest_limit = ctx->dest + readBytes; + uzlib_uncompress(ctx); + } + } uint32_t bytesLeft = this->decompressedSize - this->decompressedPos; if (len > bytesLeft) len = bytesLeft; diff --git a/ARM_Tag_FW/common/drawing.cpp b/ARM_Tag_FW/common/drawing.cpp new file mode 100644 index 00000000..c772489b --- /dev/null +++ b/ARM_Tag_FW/common/drawing.cpp @@ -0,0 +1,940 @@ +#define EEPROM_XFER_BLOCKSIZE 512 // shouldn't be any less than 256 bytes probably +#define DRAWITEM_LIST_SIZE 24 + +struct __attribute__((packed)) imageHeader { + uint16_t width; + uint16_t height; + uint8_t bpp : 4; + uint8_t reserved : 4; +}; + +static drawItem *drawItems[DRAWITEM_LIST_SIZE] = {0}; + +void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask) { + drawItem *di = new drawItem; + + di->setRotation(ro); + if (di->direction ^ epd->drawDirectionRight) { + int16_t temp = x; + x = y; + y = temp; + } + + uint16_t originalWidthBytes = (((uint16_t *)image)[0]) / 8; + uint16_t size = 0; + uint16_t width = ((uint16_t *)image)[0]; + + // find out if the original data was aligned in one byte; if not, add a byte + if (((uint16_t *)image)[0] % 8) originalWidthBytes++; + + // if we're drawing in X direction, we shift the content here. Add extra space for shifting! + if (!di->direction) { + width += x % 8; + } + + // check if the size is aligned in bytes; if not, add an extra for good measure; + if (width % 8) { + width /= 8; + width++; + } else { + width /= 8; + } + + size = width * ((uint16_t *)image)[1]; + size += 2; // not needed + + uint8_t *im = (uint8_t *)calloc(size, 1); + + for (uint16_t copyY = 0; copyY < ((uint16_t *)image)[1]; copyY++) { + memcpy(im + (copyY * width), image + 4 + (copyY * originalWidthBytes), originalWidthBytes); + + // if we draw in X direction, we need to shift bytes in the array + if (!di->direction && (x % 8)) { + drawItem::shiftBytesRight(im + (copyY * width), x % 8, width); + } + } + + di->addItem(im, width * 8, ((uint16_t *)image)[1]); + + di->xpos = x; + di->ypos = y; + di->color = color; + if (mask) + di->type = drawItem::drawType::DRAW_MASK; + else + di->type = drawItem::drawType::DRAW_BUFFERED_1BPP; + + di->addToList(); +} + +void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image) { + drawItem *di = new drawItem; + + di->setRotation(ro); + + if (di->direction ^ epd->drawDirectionRight) { + int16_t temp = x; + x = y; + y = temp; + } + + di->addItem((uint8_t *)(image + 4), ((uint16_t *)image)[0], ((uint16_t *)image)[1]); + + di->xpos = x; + di->ypos = y; + di->color = color; + di->cleanUp = false; + di->type = drawItem::drawType::DRAW_BUFFERED_1BPP; + di->addToList(); +} +#ifdef ENABLE_OEPLFS +void addFSImage(uint16_t x, uint16_t y, uint8_t color, enum rotation ro, char *name) { + drawItem *di = new drawItem; + + di->setRotation(ro); + + if (di->direction ^ epd->drawDirectionRight) { + int16_t temp = x; + x = y; + y = temp; + } + + OEPLFile *file = fs->getFile(name); + if (!file) { + delete di; + return; + } + uint16_t width, height; + + file->getBlock(0, (uint8_t *)&width, 2); + file->getBlock(2, (uint8_t *)&height, 2); + + di->addItem((uint8_t *)file, width, height); + di->xpos = x; + di->ypos = y; + di->color = color; + di->cleanUp = true; + if (color == 2) { + di->type = drawItem::drawType::DRAW_OEPLFS_2BPP; + } else { + di->type = drawItem::drawType::DRAW_OEPLFS_1BPP; + } + di->addToList(); +} + +void addCompressedFSImage(uint16_t x, uint16_t y, enum rotation ro, char *name) { + drawItem *di = new drawItem; + di->type = drawItem::drawType::DRAW_COMPRESSED; + + di->setRotation(ro); + + if (di->direction ^ epd->drawDirectionRight) { + int16_t temp = x; + x = y; + y = temp; + } + + decompress *decomp = new decompress; + if (!decomp->openFromFile(name)) { + delete di; + delete decomp; + return; + } + + di->imageHeaderOffset = decomp->readByte(0); + + struct imageHeader imgheader; + decomp->getBlock(1, (uint8_t *)&imgheader, sizeof(struct imageHeader)); + + di->addItem((uint8_t *)decomp, imgheader.width, imgheader.height); + + di->xpos = x; + di->ypos = y; + di->color = imgheader.bpp; + if (di->color == 1) di->color = 0; + di->cleanUp = true; + di->addToList(); +} +#endif + +void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...) { + char out_buffer[256]; + va_list lst; + va_start(lst, c); + vsnprintf(out_buffer, 255, c, lst); + va_end(lst); + + QRCode qrcode; + // Allocate a chunk of memory to store the QR code + uint8_t qrcodeBytes[qrcode_getBufferSize(version)]; + qrcode_initText(&qrcode, qrcodeBytes, version, ECC_LOW, out_buffer); + + drawItem *di = new drawItem; + di->setRotation(rotation::ROTATE_0); + + uint8_t xbytes = (qrcode.size * scale) / 8; + if (qrcode.size % 8) xbytes++; + + uint16_t size = xbytes * (qrcode.size * scale); + uint8_t *im = (uint8_t *)calloc(size + 1, 1); + + uint8_t *qrbuf = im; + + for (uint8_t qry = 0; qry < qrcode.size; qry++) { + for (uint8_t scale_y = 0; scale_y < scale; scale_y++) { + for (uint8_t qrx = 0; qrx < qrcode.size; qrx++) { + for (uint8_t scale_x = 0; scale_x < scale; scale_x++) { + if (qrcode_getModule(&qrcode, qrx, qry)) { + // Calculate the position in the framebuffer for the scaled pixel + uint8_t scaled_qrx = (qrx * scale) + scale_x; + uint8_t scaled_qry = (qry * scale) + scale_y; + + // Calculate the byte and bit positions in the framebuffer + uint8_t fb_byte = scaled_qrx / 8; + uint8_t fb_bit = 7 - (scaled_qrx % 8); + + // Set the bit in the framebuffer + qrbuf[fb_byte + (scaled_qry * xbytes)] |= (1 << fb_bit); + } + } + } + } + } + + di->addItem(im, xbytes * 8, qrcode.size * scale); + + di->xpos = x; + di->ypos = y; + di->color = 0; + di->type = drawItem::drawType::DRAW_BUFFERED_1BPP; + di->addToList(); +} + +void drawImageAtAddressWrap(uint32_t addr, uint8_t lut) { + // powerUp(INIT_EEPROM); + // epdSetup(); + selectLUT(lut); + struct EepromImageHeader eih; + HAL_flashRead(addr, (uint8_t *)&eih, sizeof(struct EepromImageHeader)); +#ifdef DEBUG_DRAWING + printf("Drawing image of type 0x%02X from location 0x%08X\n", eih.dataType, addr); +#endif + switch (eih.dataType) { + case DATATYPE_IMG_RAW_1BPP: { + drawItem *di = new drawItem; + // di->setRotation(ro); + di->xpos = 0; + di->ypos = 0; + di->color = 0; + di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes); + di->type = drawItem::drawType::DRAW_EEPROM_1BPP; + di->direction = false; + if (di->mirrorH) di->mirrorV = !di->mirrorV; + di->cleanUp = false; + di->addToList(); + } break; + case DATATYPE_IMG_RAW_2BPP: { + drawItem *di = new drawItem; + // di->setRotation(ro); + di->xpos = 0; + di->ypos = 0; + di->color = 0; + di->addItem((uint8_t *)addr, epd->effectiveXRes, epd->effectiveYRes); + di->type = drawItem::drawType::DRAW_EEPROM_2BPP; + di->direction = false; + if (di->mirrorH) di->mirrorV = !di->mirrorV; + di->cleanUp = false; + di->addToList(); + } break; + case DATATYPE_IMG_ZLIB: { +#ifdef DEBUG_DRAWING + printf("DRAW: drawing compressed image\n"); +#endif + drawItem *di = new drawItem; + decompress *decomp = new decompress; + di->type = drawItem::drawType::DRAW_COMPRESSED; + + addr += sizeof(struct EepromImageHeader); + + if (!decomp->openFromFlash(addr, eih.size)) { + printf("DRAW: failed to open\n"); + delete di; + delete decomp; + return; + } + + di->imageHeaderOffset = decomp->readByte(0); + + struct imageHeader imgheader; + decomp->getBlock(1, (uint8_t *)&imgheader, sizeof(struct imageHeader)); + + di->addItem((uint8_t *)decomp, imgheader.width, imgheader.height); + + di->xpos = 0; + di->ypos = 0; + di->direction = false; + if (di->mirrorH) di->mirrorV = !di->mirrorV; + if (imgheader.bpp == 1) di->color = 0; + if (imgheader.bpp == 2) di->color = 2; + di->cleanUp = true; + di->addToList(); + } break; + } + addOverlay(); + + draw(); +} + +extern "C" { +void drawImageAtAddress(uint32_t addr, uint8_t lut) { + drawImageAtAddressWrap(addr, lut); +} +} + +void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) { + uint16_t widthBytes = width / 8; + if (width % 8) widthBytes++; + uint32_t framebufferSize = (widthBytes + 1) * height; + uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1); + if (framebuffer == NULL) { + return; + } + + ((uint16_t *)framebuffer)[0] = width + 1; + ((uint16_t *)framebuffer)[1] = height; + + framebuffer += 4; + + uint16_t w = width - 1; + uint16_t x = 1; + while (w--) { + framebuffer[(x / 8)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); + x++; + } + for (uint16_t curY = 1; curY < (height - 1); curY++) { + framebuffer[widthBytes * curY] = 0x80; + if (width % 8) + framebuffer[(widthBytes * curY) + widthBytes - 1] = (uint8_t)(1 << (7 - ((uint8_t)width % 8))); + else + framebuffer[(widthBytes * curY) + widthBytes - 1] = 0x01; + } + w = width - 1; + x = 1; + while (w--) { + framebuffer[(x / 8) + ((height - 1) * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); + x++; + } + framebuffer -= 4; + addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_NORMAL); + free(framebuffer); +} + +void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color) { + uint16_t widthBytes = width / 8; + if (width % 8) widthBytes++; + uint32_t framebufferSize = widthBytes * height; + uint8_t *framebuffer = (uint8_t *)calloc(framebufferSize + 4, 1); + if (framebuffer == NULL) { + return; + } + + ((uint16_t *)framebuffer)[0] = width; + ((uint16_t *)framebuffer)[1] = height; + + framebuffer += 4; + + for (uint16_t curY = 0; curY < height; curY++) { + uint16_t w = width; + uint16_t x = 0; + while (w--) { + framebuffer[(x / 8) + (curY * widthBytes)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); + x++; + } + } + + framebuffer -= 4; + addBufferedImage(xpos, ypos, color, rotation::ROTATE_0, framebuffer, DRAW_INVERTED); + free(framebuffer); +} + +// drawItem (sprite) functions +void drawItem::shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len) { + // Ensure the shift value is within bounds (0 to 7) + shift = shift % 8; + + // Handle the case where shift is 0 or len is 0 + if (shift == 0 || len == 0) { + return; + } + + // Loop through the array from right to left + for (int i = len - 1; i > 0; i--) { + // Perform the shift by combining bits from the current byte + // and the next byte to its right + data[i] = (data[i] >> shift) | (data[i - 1] << (8 - shift)); + } + + // For the leftmost byte, simply shift it to the right + data[0] >>= shift; +} + +uint8_t drawItem::bitReverse(uint8_t byte) { + byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xAA); + byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xCC); + byte = (byte >> 4) | (byte << 4); + return byte; +} + +void drawItem::reverseBytes(uint8_t *src, uint8_t src_len) { + // Check for valid input + if (src == NULL || src_len == 0) { + return; + } + + // Reverse the entire source array + for (uint8_t i = 0; i < src_len / 2; i++) { + uint8_t temp = src[i]; + src[i] = src[src_len - i - 1]; + src[src_len - i - 1] = temp; + } + // Reverse the bits within the bytes + for (uint8_t i = 0; i < src_len; i++) { + src[i] = bitReverse(src[i]); + } +} + +void drawItem::copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset) { + switch (type) { + case DRAW_MASK: + for (uint8_t i = 0; i < src_len; i++) { + dst[i + offset] &= ~(src[i]); + } + break; + default: + for (uint8_t i = 0; i < src_len; i++) { + dst[i + offset] |= src[i]; + } + break; + } +} + +void drawItem::renderDrawLine(uint8_t *line, uint16_t number, uint8_t c) { + drawItem *curDrawItem; + for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) { + curDrawItem = drawItems[i]; + if (curDrawItem != nullptr) { + curDrawItem->getDrawLine(line, number, c); + } + } +} + +void drawItem::flushDrawItems() { + drawItem *curDrawItem; + for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) { + curDrawItem = drawItems[i]; + if (curDrawItem != nullptr) { + delete curDrawItem; + drawItems[i] = nullptr; + } + } +} + +void drawItem::getXLine(uint8_t *line, uint16_t y, uint8_t c) { + switch (type) { + case DRAW_FONT: + case DRAW_BUFFERED_1BPP: + case DRAW_MASK: + if (c != color) return; + if ((y >= ypos) && (y < height + ypos)) { // was y > ypos, not >= + // y = height-y; + if (mirrorV) { + if (mirrorH) { + reverseBytes(&buffer[((height - (y - ypos)) * widthBytes)], widthBytes); + // reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes); + } else { + reverseBytes(&buffer[((y - ypos) * widthBytes)], widthBytes); + } + } + if (mirrorH) { + copyWithByteShift(line, &buffer[((height - (y - ypos)) * widthBytes)], widthBytes, xpos / 8); + } else { + copyWithByteShift(line, &buffer[((y - ypos) * widthBytes)], widthBytes, xpos / 8); + } + } + break; +#ifdef ENABLE_OEPLFS + case DRAW_OEPLFS_1BPP: + case DRAW_OEPLFS_2BPP: + if ((color < 2) && (c != color)) return; + if ((y >= ypos) && (y < height + ypos)) { // was y > ypos, not >= + uint32_t offset = 4; + offset += c * height * widthBytes; + OEPLFile *file = (OEPLFile *)this->buffer; + uint8_t *dbuffer = (uint8_t *)malloc(widthBytes); + if (mirrorH) { + file->getBlock(offset + (height - (y - ypos)) * widthBytes, dbuffer, widthBytes); + } else { + file->getBlock(offset + (y - ypos) * widthBytes, dbuffer, widthBytes); + } + if (mirrorV) { + reverseBytes(dbuffer, widthBytes); + } + copyWithByteShift(line, dbuffer, widthBytes, xpos / 8); + free(dbuffer); + } + break; +#endif + case DRAW_COMPRESSED: + if ((color < 2) && (c != color)) return; + if ((y >= ypos) && (y < height + ypos)) { + uint32_t offset = this->imageHeaderOffset; + offset += c * height * widthBytes; + decompress *decomp = (decompress *)this->buffer; + uint8_t *dbuffer = (uint8_t *)malloc(widthBytes); + if (mirrorH) { + decomp->getBlock(offset + (height - (y - ypos)) * widthBytes, dbuffer, widthBytes); + } else { + decomp->getBlock(offset + (y - ypos) * widthBytes, dbuffer, widthBytes); + } + if (mirrorV) { + reverseBytes(dbuffer, widthBytes); + } + copyWithByteShift(line, dbuffer, widthBytes, xpos / 8); + free(dbuffer); + } + break; + case DRAW_EEPROM_1BPP: + if (c != color) return; + if (epd->drawDirectionRight) + y = epd->effectiveYRes - 1 - y; + HAL_flashRead((uint32_t)buffer + sizeof(struct EepromImageHeader) + (y * (epd->effectiveXRes / 8)), line, (epd->effectiveXRes / 8)); + break; + case DRAW_EEPROM_2BPP: + if (epd->drawDirectionRight) + y = epd->effectiveYRes - 1 - y; + HAL_flashRead((uint32_t)(buffer + sizeof(struct EepromImageHeader) + ((y + (c * epd->effectiveYRes)) * (epd->effectiveXRes / 8))), line, (epd->effectiveXRes / 8)); + break; + default: + printf("DRAW: Not supported mode!\n"); + break; + } +} + +void drawItem::getYLine(uint8_t *line, uint16_t x, uint8_t c) { + switch (type) { + case DRAW_FONT: + case DRAW_BUFFERED_1BPP: + if (c != color) return; + if ((x >= xpos) && (x < width + xpos)) { + x -= xpos; + for (uint16_t curY = 0; curY < height; curY++) { + uint16_t curYMirrored = curY; + if (!mirrorH) curYMirrored = height - 1 - curY; + if (mirrorV) { + if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) { + line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); + } + } else { + if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) { + line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); + } + } + } + } + break; + case DRAW_MASK: + if (c != color) return; + if ((x >= xpos) && (x < width + xpos)) { + x -= xpos; + for (uint16_t curY = 0; curY < height; curY++) { + uint16_t curYMirrored = curY; + if (!mirrorH) curYMirrored = height - 1 - curY; + if (mirrorV) { + if (buffer[((width - x) / 8) + (curYMirrored * widthBytes)] & (1 << (7 - ((width - x) % 8)))) { + line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8))); + } + } else { + if (buffer[(x / 8) + (curYMirrored * widthBytes)] & (1 << (7 - (x % 8)))) { + line[(curY + ypos) / 8] &= ~(1 << (7 - ((curY + ypos) % 8))); + } + } + } + } + break; +#ifdef ENABLE_OEPLFS + case DRAW_OEPLFS_1BPP: + case DRAW_OEPLFS_2BPP: + // this is incredibly slow. For larger images, it'll probably read 128 bytes of eeprom for every -bit- in the image. + if ((color < 2) && (c != color)) return; + if ((x >= xpos) && (x < width + xpos)) { + uint32_t offset = 4; + offset += c * height * widthBytes; + OEPLFile *file = (OEPLFile *)this->buffer; + x -= xpos; + for (uint16_t curY = 0; curY < height; curY++) { + uint16_t curYMirrored = curY; + if (!mirrorH) curYMirrored = height - 1 - curY; + if (mirrorV) { + if (file->readByte(offset + ((width - x) / 8) + (curYMirrored * widthBytes)) & (1 << (7 - ((width - x) % 8)))) { + line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); + } + } else { + if (file->readByte(offset + (x / 8) + (curYMirrored * widthBytes)) & (1 << (7 - (x % 8)))) { + line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); + } + } + } + } + break; +#endif + case DRAW_COMPRESSED: + // this is incredibly slow, and very naive. Just don't. Maybe if you want to test if everything works. It'll decompress about 50% of the file *PER PIXEL*. Good for load testing + if ((color < 2) && (c != color)) return; + if ((x >= xpos) && (x < width + xpos)) { + uint32_t offset = this->imageHeaderOffset; + offset += c * height * widthBytes; + decompress *decomp = (decompress *)this->buffer; + x -= xpos; + for (uint16_t curY = 0; curY < height; curY++) { + uint16_t curYMirrored = curY; + if (!mirrorH) curYMirrored = height - 1 - curY; + if (mirrorV) { + if (decomp->readByte(offset + ((width - x) / 8) + (curYMirrored * widthBytes)) & (1 << (7 - ((width - x) % 8)))) { + line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); + } + } else { + if (decomp->readByte(offset + (x / 8) + (curYMirrored * widthBytes)) & (1 << (7 - (x % 8)))) { + line[(curY + ypos) / 8] |= (1 << (7 - ((curY + ypos) % 8))); + } + } + } + } + break; + + default: + break; + } +} + +void drawItem::getDrawLine(uint8_t *line, uint16_t number, uint8_t c) { + if (direction) { + getYLine(line, number, c); + } else { + getXLine(line, number, c); + } +} + +void drawItem::addItem(uint8_t *data, uint16_t w, uint16_t h) { + width = w; + height = h; + widthBytes = w / 8; + if (w % 8) widthBytes++; + buffer = data; +} + +bool drawItem::addToList() { + for (uint8_t i = 0; i < DRAWITEM_LIST_SIZE; i++) { + if (drawItems[i] == nullptr) { + drawItems[i] = this; + return true; + }; + } + return false; +} + +drawItem::~drawItem() { + if (cleanUp) { + switch (this->type) { +#ifdef ENABLE_OEPLFS + case drawItem::drawType::DRAW_OEPLFS_1BPP: + case drawItem::drawType::DRAW_OEPLFS_2BPP: { + OEPLFile *file = (OEPLFile *)this->buffer; + if (file) delete file; + } break; +#endif + case drawItem::drawType::DRAW_COMPRESSED: { + decompress *dec = (decompress *)this->buffer; + if (dec) delete dec; + } break; + default: + free(buffer); + break; + } + } +} + +drawItem::drawItem() { + if (epd->drawDirectionRight) { + direction = true; + mirrorH = true; + } +} + +void drawItem::setRotation(enum rotation ro) { + if (epd->drawDirectionRight) { + direction = true; + mirrorH = true; + } + + switch (ro) { + case ROTATE_0: + break; + case ROTATE_270: + direction = !direction; + mirrorH = !mirrorH; + mirrorV = !mirrorV; + break; + case ROTATE_180: + mirrorH = !mirrorH; + mirrorV = !mirrorV; + break; + case ROTATE_90: + direction = !direction; + break; + }; +} + +void fontrender::drawFastHLine(uint16_t x, uint16_t y, uint16_t w) { + while (w--) { + fb[(x / 8) + (y * bufferByteWidth)] |= (uint8_t)(1 << (7 - ((uint8_t)x % 8))); + x++; + } +} + +void fontrender::fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { + for (uint16_t curY = y; curY < y + h; curY++) { + drawFastHLine(x, curY, w); + } +} + +#ifdef ENABLE_OEPLFS +// font rendering functions for OEPLFS +fontrender::fontrender(char *name) { + this->setFont(name); +} + +void fontrender::setFont(char *name) { + if (this->glyphFile) delete this->glyphFile; + if (this->bitmapFile) delete this->bitmapFile; + OEPLFile *font = fs->getFile(name); + if (!font) { +#ifdef DEBUG_DRAWING + printf("DRAW: Couldn't open font file %s\n", name); +#endif + } + font->getBlock(0, (uint8_t *)&this->gfxFont, sizeof(GFXFontOEPL)); + this->glyphFile = fs->getFile(this->gfxFont.glyphFile); + this->bitmapFile = fs->getFile(this->gfxFont.bitmapFile); + + if (!this->glyphFile) printf("DRAW: Couldn't open font glyph file %s\n", name); + if (!this->bitmapFile) printf("DRAW: Couldn't open font bitmap file %s\n", name); + if (font) delete (font); +} + +fontrender::~fontrender() { + if (this->glyphFile) delete this->glyphFile; + if (this->bitmapFile) delete this->bitmapFile; +} + +uint8_t fontrender::getCharWidth(uint16_t c) { + if ((c >= gfxFont.first) && (c <= gfxFont.last)) { + c -= gfxFont.first; + GFXglyph glyph = this->getGlyph(c); + return glyph.xAdvance; + } + return 0; +} + +GFXglyph fontrender::getGlyph(uint16_t c) { + GFXglyph g; + this->glyphFile->getBlock(sizeof(GFXglyph) * c, (uint8_t *)&g, sizeof(GFXglyph)); + return g; +} + +#else + +// 'regular' font rendering functions +uint8_t fontrender::getCharWidth(uint16_t c) { + if ((c >= gfxFont->first) && (c <= gfxFont->last)) { + c -= gfxFont->first; + GFXglyph *glyph = &(gfxFont->glyph[c]); + return glyph->xAdvance; + } + return 0; +} + +fontrender::fontrender(const GFXfont *font) { + gfxFont = (GFXfont *)font; +} + +void fontrender::setFont(const GFXfont *font) { + gfxFont = (GFXfont *)font; +} + +#endif + +uint8_t fontrender::drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size) { + // Filter out bad characters not present in font + +#ifdef ENABLE_OEPLFS + if ((c >= gfxFont.first) && (c <= gfxFont.last)) { + c -= gfxFont.first; + GFXglyph glyph = this->getGlyph(c); + // uint8_t *bitmap = (uint8_t *)gfxFont->bitmap; + uint32_t bo = glyph.bitmapOffset; + uint8_t w = glyph.width, + h = glyph.height; + int8_t xo = glyph.xOffset, + yo = glyph.yOffset; +#else + if ((c >= gfxFont->first) && (c <= gfxFont->last)) { + c -= gfxFont->first; + GFXglyph *glyph = &(gfxFont->glyph[c]); + uint8_t *bitmap = (uint8_t *)gfxFont->bitmap; + uint32_t bo = glyph->bitmapOffset; + uint8_t w = glyph->width, + h = glyph->height; + int8_t xo = glyph->xOffset, + yo = glyph->yOffset; +#endif + + uint8_t xx, yy, bits = 0, bit = 0; + int16_t xo16 = 0, yo16 = 0; + + if (size > 1) { + xo16 = xo; + yo16 = yo; + } + + // GFXFF rendering speed up + uint16_t hpc = 0; // Horizontal foreground pixel count + for (yy = 0; yy < h; yy++) { + for (xx = 0; xx < w; xx++) { + if (bit == 0) { +#ifdef ENABLE_OEPLFS + bits = (*this->bitmapFile)(bo++); +#else + bits = bitmap[bo++]; +#endif + + bit = 0x80; + } + if (bits & bit) + hpc++; + else { + if (hpc) { + if (size == 1) + drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc); + else + fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size); + hpc = 0; + } + } + bit >>= 1; + } + // Draw pixels for this line as we are about to increment yy + if (hpc) { + if (size == 1) + drawFastHLine(x + xo + xx - hpc, y + yo + yy, hpc); + else + fillRect(x + (xo16 + xx - hpc) * size, y + (yo16 + yy) * size, size * hpc, size); + hpc = 0; + } + } +#ifdef ENABLE_OEPLFS + return glyph.xAdvance; +#else + return glyph->xAdvance; +#endif + } + return 0; +} + +void fontrender::epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...) { + drawItem *di = new drawItem; + if (di == nullptr) return; + di->setRotation(ro); + + // prepare a drawItem, exchange x/y if necessary. + if (di->direction ^ epd->drawDirectionRight) { + int16_t temp = x; + x = y; + y = temp; + } + + // output string using vsnprintf + char out_buffer[256]; + va_list lst; + va_start(lst, c); + uint8_t len = vsnprintf(out_buffer, 255, c, lst); + va_end(lst); + + // account for offset in font rendering + if (!di->direction) { + Xpixels = x % 8; // total drawing width increased by x%8 + } else { + Xpixels = 0; + } + + // find out the total length of the string + for (uint8_t c = 0; c < len; c++) { + Xpixels += (uint16_t)getCharWidth(out_buffer[c]); + } + + // find out the high and low points for given font + int8_t high = 0; + int8_t low = 0; + for (uint8_t curchar = 0; curchar < len; curchar++) { + uint8_t c = out_buffer[curchar]; + +#ifdef ENABLE_OEPLFS + if ((c >= gfxFont.first) && (c <= gfxFont.last)) { + c -= gfxFont.first; + GFXglyph g = this->getGlyph(c); + int8_t glyphUL = g.yOffset; + if (glyphUL < high) high = glyphUL; + int8_t glyphHeight = g.height; +#else + if ((c >= gfxFont->first) && (c <= gfxFont->last)) { + c -= gfxFont->first; + int8_t glyphUL = gfxFont->glyph[c].yOffset; + if (glyphUL < high) high = glyphUL; + int8_t glyphHeight = gfxFont->glyph[c].height; +#endif + + if ((glyphUL + glyphHeight) > low) low = glyphUL + glyphHeight; + } + } + // Actual font height (reduces memory footprint) + int8_t height = -1 * (high - low) + 1; + + // determine actual width + bufferByteWidth = Xpixels / 8; + if (Xpixels % 8) bufferByteWidth++; + + // allocate framebuffer + fb = (uint8_t *)calloc(bufferByteWidth * height, 1); + + if (!fb) { + printf("DRAW: Tried to allocate a buffer %d x %d\n and failed...", bufferByteWidth, height); + delete di; + return; + } + + uint16_t curX; + if (!di->direction) { + curX = x % 8; // start drawing at x%8s + } else { + curX = 0; + } + for (uint8_t c = 0; c < len; c++) { + curX += (uint16_t)drawChar(curX, height - low, out_buffer[c], 1); + } + + di->addItem(fb, curX, height); + di->ypos = y; + di->xpos = x; + di->color = color; + di->type = drawItem::drawType::DRAW_FONT; + di->addToList(); +} diff --git a/ARM_Tag_FW/common/drawing.h b/ARM_Tag_FW/common/drawing.h new file mode 100644 index 00000000..7fcb1d33 --- /dev/null +++ b/ARM_Tag_FW/common/drawing.h @@ -0,0 +1,152 @@ +#define COLOR_RED 1 +#define COLOR_BLACK 0 +#define COLOR_DUAL 2 + +#define IMAGE_OR 1 +#define IMAGE_REPLACE 0 + +#define DRAW_INVERTED 1 +#define DRAW_NORMAL 0 + +#define FILENAME_LENGTH 32 + +typedef struct __attribute__((packed)) { + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner +} GFXglyph; + +enum rotation { + ROTATE_0, + ROTATE_90, + ROTATE_180, + ROTATE_270 +}; + +#ifdef ENABLE_OEPLFS +typedef struct __attribute__((packed)) { + uint16_t first; + uint16_t last; + uint8_t yAdvance; + char glyphFile[FILENAME_LENGTH]; + char bitmapFile[FILENAME_LENGTH]; +} GFXFontOEPL; +#else +/// Data stored for FONT AS A WHOLE +typedef struct { + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint16_t first; ///< ASCII extents (first char) + uint16_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) +} GFXfont; +#endif + +extern "C" { +void drawImageAtAddress(uint32_t addr, uint8_t lut); +} + +void addBufferedImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image, bool mask); +void addFlashImage(uint16_t x, uint16_t y, bool color, enum rotation ro, const uint8_t *image); +void addQR(uint16_t x, uint16_t y, uint8_t version, uint8_t scale, const char *c, ...); +void drawRoundedRectangle(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color); +void drawMask(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, bool color); + +#ifdef ENABLE_OEPLFS +void addFSImage(uint16_t x, uint16_t y, uint8_t color, enum rotation ro, char *name); +void addCompressedFSImage(uint16_t x, uint16_t y, enum rotation ro, char *name); +#endif + +class drawItem { + public: + drawItem(); + ~drawItem(); + void setRotation(enum rotation ro); + void addItem(uint8_t *data, uint16_t width, uint16_t height); + bool addToList(); + + static void shiftBytesRight(uint8_t *data, uint8_t shift, uint8_t len); + static void renderDrawLine(uint8_t *line, uint16_t number, uint8_t c); + static void flushDrawItems(); + + // these are also used for rotated screens + static void reverseBytes(uint8_t *src, uint8_t src_len); + static uint8_t bitReverse(uint8_t byte); + + enum drawType { + DRAW_FONT, + DRAW_BUFFERED_1BPP, + DRAW_MASK, + DRAW_EEPROM_1BPP, + DRAW_EEPROM_2BPP, + DRAW_COMPRESSED, + DRAW_OEPLFS_1BPP, + DRAW_OEPLFS_2BPP + } type; + + int16_t xpos; + int16_t ypos; + + enum rotation rotate = ROTATE_0; + + uint8_t color = 0; + + bool direction = false; + + bool mirrorH = false; + bool mirrorV = false; + uint16_t width; + uint16_t height; + + uint8_t imageHeaderOffset = 0; + + // if this is true, clean up the reference (free memory). + bool cleanUp = true; + + protected: + void copyWithByteShift(uint8_t *dst, uint8_t *src, uint8_t src_len, uint8_t offset); + + void getDrawLine(uint8_t *line, uint16_t number, uint8_t c); + void getXLine(uint8_t *line, uint16_t yPos, uint8_t color); + void getYLine(uint8_t *line, uint16_t xPos, uint8_t color); + uint8_t widthBytes = 0; + uint8_t *buffer = nullptr; +}; + +class fontrender { + public: + void epdPrintf(uint16_t x, uint16_t y, bool color, enum rotation ro, const char *c, ...); + +#ifdef ENABLE_OEPLFS + fontrender(char *name); + ~fontrender(); + void setFont(char *name); +#else + fontrender(const GFXfont *font); + void setFont(const GFXfont *font); +#endif + protected: +#ifdef ENABLE_OEPLFS + GFXFontOEPL gfxFont; +#else + GFXfont *gfxFont; // = &FreeSansBold18pt7b; +#endif + + uint16_t bufferByteWidth = 0; + uint8_t *fb = nullptr; + uint16_t Xpixels; + uint8_t drawChar(int32_t x, int32_t y, uint16_t c, uint8_t size); + uint8_t getCharWidth(uint16_t c); + void drawFastHLine(uint16_t x, uint16_t y, uint16_t w); + void fillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + +#ifdef ENABLE_OEPLFS + // The OEPLFS version needs some additional pointers to the file objects + GFXglyph getGlyph(uint16_t c); + OEPLFile *glyphFile = nullptr; + OEPLFile *bitmapFile = nullptr; +#endif +}; \ No newline at end of file diff --git a/ARM_Tag_FW/common/oepl-protocol.cpp b/ARM_Tag_FW/common/oepl-protocol.cpp new file mode 100644 index 00000000..4c0d3da7 --- /dev/null +++ b/ARM_Tag_FW/common/oepl-protocol.cpp @@ -0,0 +1,1043 @@ + +// download-stuff +static PERSISTENTVAR struct blockRequest curBlock = {0}; // used by the block-requester, contains the next request that we'll send +static PERSISTENTVAR uint64_t curDispDataVer = 0; +static PERSISTENTVAR struct AvailDataInfo xferDataInfo = {0}; // last 'AvailDataInfo' we received from the AP +static PERSISTENTVAR bool requestPartialBlock = false; // if we should ask the AP to get this block from the host or not +#define BLOCK_TRANSFER_ATTEMPTS 5 + +PERSISTENTVAR uint8_t xferImgSlot = 0xFF; +PERSISTENTVAR uint8_t curImgSlot = 0xFF; +static PERSISTENTVAR uint32_t curHighSlotId = 0; +static PERSISTENTVAR uint8_t nextImgSlot = 0; +static PERSISTENTVAR uint8_t imgSlots = 0; + +// stuff we need to keep track of related to the network/AP +uint8_t APmac[8] = {0}; +uint16_t APsrcPan = 0; +PERSISTENTVAR uint8_t mSelfMac[8] = {0}; +static PERSISTENTVAR uint8_t seq = 0; +volatile uint8_t PERSISTENTVAR currentChannel = 0; + +// buffer we use to prepare/read packets +static uint8_t inBuffer[128] = {0}; +static uint8_t outBuffer[128] = {0}; + +extern void executeCommand(uint8_t cmd); // this is defined in main.c + +// tools +static uint8_t getPacketType(const void *buffer) { + const struct MacFcs *fcs = (MacFcs *)buffer; + if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { + // broadcast frame + uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameBcast)]; + return type; + } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { + // normal frame + uint8_t type = ((uint8_t *)buffer)[sizeof(struct MacFrameNormal)]; + return type; + } + return 0; +} +static bool pktIsUnicast(const void *buffer) { + const struct MacFcs *fcs = (MacFcs *)buffer; + if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) { + return false; + } else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) { + // normal frame + return true; + } + // unknown type... + return false; +} +void dump(const uint8_t *a, const uint16_t l) { + printf("\n "); +#define ROWS 16 + for (uint8_t c = 0; c < ROWS; c++) { + printf(" %02X", c); + } + printf("\n--------"); + for (uint8_t c = 0; c < ROWS; c++) { + printf("---"); + } + for (uint16_t c = 0; c < l; c++) { + if ((c % ROWS) == 0) { + printf("\n0x%04X | ", c); + } + printf("%02X ", a[c]); + } + printf("\n--------"); + for (uint8_t c = 0; c < ROWS; c++) { + printf("---"); + } + printf("\n"); +} +static bool checkCRC(const void *p, const uint8_t len) { + uint8_t total = 0; + for (uint8_t c = 1; c < len; c++) { + total += ((uint8_t *)p)[c]; + } + // printf("CRC: rx %d, calc %d\n", ((uint8_t *)p)[0], total); + return ((uint8_t *)p)[0] == total; +} +static void addCRC(void *p, const uint8_t len) { + uint8_t total = 0; + for (uint8_t c = 1; c < len; c++) { + total += ((uint8_t *)p)[c]; + } + ((uint8_t *)p)[0] = total; +} + +// radio stuff +static void sendPing() { + struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); + memset(outBuffer, 0, sizeof(struct MacFrameBcast) + 2 + 4); + outBuffer[0] = sizeof(struct MacFrameBcast) + 1 + 2; + outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_PING; + memcpy(txframe->src, mSelfMac, 8); + txframe->fcs.frameType = 1; + txframe->fcs.ackReqd = 1; + txframe->fcs.destAddrType = 2; + txframe->fcs.srcAddrType = 3; + txframe->seq = seq++; + txframe->dstPan = PROTO_PAN_ID; + txframe->dstAddr = 0xFFFF; + txframe->srcPan = PROTO_PAN_ID; + commsTxNoCpy(outBuffer); +} +uint8_t detectAP(const uint8_t channel) { + uint32_t t; + radioRxEnable(false); + radioSetChannel(channel); + radioRxFlush(); + radioRxEnable(true); + for (uint8_t c = 1; c <= MAXIMUM_PING_ATTEMPTS; c++) { + sendPing(); + t = millis() + (HAL_TIMER_TICK * (PING_REPLY_WINDOW)); + while (millis() < t) { + int8_t ret = HAL_PacketRX(inBuffer); + if (ret > 1) { + // dump(inBuffer+sizeof(struct MacFrameNormal),32); + if ((inBuffer[sizeof(struct MacFrameNormal) + 1] == channel) && (getPacketType(inBuffer) == PKT_PONG)) { + if (pktIsUnicast(inBuffer)) { + struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; + memcpy(APmac, f->src, 8); + APsrcPan = f->pan; + return c; + } + } + } + HAL_msDelay(1); + } + } + return 0; +} + +// data xfer stuff +static void sendShortAvailDataReq() { + struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); + outBuffer[0] = sizeof(struct MacFrameBcast) + 1 + 2; + outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_AVAIL_DATA_SHORTREQ; + memcpy(txframe->src, mSelfMac, 8); + outBuffer[1] = 0x21; + outBuffer[2] = 0xC8; // quickly set txframe fcs structure for broadcast packet + txframe->seq = seq++; + txframe->dstPan = PROTO_PAN_ID; + txframe->dstAddr = 0xFFFF; + txframe->srcPan = PROTO_PAN_ID; + commsTxNoCpy(outBuffer); +} +static void sendAvailDataReq() { + struct MacFrameBcast *txframe = (struct MacFrameBcast *)(outBuffer + 1); + memset(outBuffer, 0, sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 4); + struct AvailDataReq *availreq = (struct AvailDataReq *)(outBuffer + 2 + sizeof(struct MacFrameBcast)); + outBuffer[0] = sizeof(struct MacFrameBcast) + sizeof(struct AvailDataReq) + 2 + 2; + outBuffer[sizeof(struct MacFrameBcast) + 1] = PKT_AVAIL_DATA_REQ; + memcpy(txframe->src, mSelfMac, 8); + txframe->fcs.frameType = 1; + txframe->fcs.ackReqd = 1; + txframe->fcs.destAddrType = 2; + txframe->fcs.srcAddrType = 3; + txframe->seq = seq++; + txframe->dstPan = PROTO_PAN_ID; + txframe->dstAddr = 0xFFFF; + txframe->srcPan = PROTO_PAN_ID; + // TODO: send some (more) meaningful data + availreq->hwType = tag.OEPLtype; + availreq->wakeupReason = wakeUpReason; + availreq->lastPacketRSSI = mLastRSSI; + availreq->lastPacketLQI = mLastLqi; + availreq->temperature = temperature; + availreq->batteryMv = batteryVoltage; + availreq->capabilities = capabilities; + availreq->currentChannel = currentChannel; + availreq->tagSoftwareVersion = FW_VERSION; + addCRC(availreq, sizeof(struct AvailDataReq)); + commsTxNoCpy(outBuffer); +} +struct AvailDataInfo *getAvailDataInfo() { + radioRxEnable(true); + uint32_t t; + for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { + sendAvailDataReq(); + t = millis() + (HAL_TIMER_TICK * (DATA_REQ_RX_WINDOW_SIZE)); + while (millis() < t) { + int8_t ret = HAL_PacketRX(inBuffer); + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) { + if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) { + struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; + memcpy(APmac, f->src, 8); + APsrcPan = f->pan; + dataReqLastAttempt = c; + return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); + } + } + } + } + } + dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; + return NULL; +} +struct AvailDataInfo *getShortAvailDataInfo() { + radioRxEnable(true); + uint32_t t; + for (uint8_t c = 0; c < DATA_REQ_MAX_ATTEMPTS; c++) { + sendShortAvailDataReq(); + // sendAvailDataReq(); + t = millis() + (HAL_TIMER_TICK * (DATA_REQ_RX_WINDOW_SIZE)); + while (millis() < t) { + int8_t ret = HAL_PacketRX(inBuffer); + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_AVAIL_DATA_INFO) { + if (checkCRC(inBuffer + sizeof(struct MacFrameNormal) + 1, sizeof(struct AvailDataInfo))) { + struct MacFrameNormal *f = (struct MacFrameNormal *)inBuffer; + memcpy(APmac, f->src, 8); + APsrcPan = f->pan; + dataReqLastAttempt = c; + return (struct AvailDataInfo *)(inBuffer + sizeof(struct MacFrameNormal) + 1); + } + } + } + } + } + dataReqLastAttempt = DATA_REQ_MAX_ATTEMPTS; + return NULL; +} +static bool processBlockPart(const struct blockPart *bp, uint8_t *blockbuffer) { + uint16_t start = bp->blockPart * BLOCK_PART_DATA_SIZE; + uint16_t size = BLOCK_PART_DATA_SIZE; + // validate if it's okay to copy data + if (bp->blockId != curBlock.blockId) { + // printf("got a packet for block %02X\n", bp->blockId); + return false; + } + if (start >= BLOCK_XFER_BUFFER_SIZE - 1) + return false; + if (bp->blockPart > BLOCK_MAX_PARTS) + return false; + if ((start + size) > BLOCK_XFER_BUFFER_SIZE) { + size = BLOCK_XFER_BUFFER_SIZE - start; + } + if (checkCRC(bp, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE)) { + // copy block data to buffer + memcpy((void *)(blockbuffer + start), (const void *)bp->data, size); + // we don't need this block anymore, set bit to 0 so we don't request it again + curBlock.requestedParts[bp->blockPart / 8] &= ~(1 << (bp->blockPart % 8)); + return true; + } else { + return false; + } +} +static bool blockRxLoop(const uint32_t timeout, uint8_t *blockbuffer) { + uint32_t t; + bool success = false; + radioRxEnable(true); + t = millis() + (HAL_TIMER_TICK * (timeout + 20)); + + bool blockComplete = false; + + while (millis() < t) { + int8_t ret = HAL_PacketRX(inBuffer); + if (ret > 1) { + if ((getPacketType(inBuffer) == PKT_BLOCK_PART) && pktIsUnicast(inBuffer)) { + struct blockPart *bp = (struct blockPart *)(inBuffer + sizeof(struct MacFrameNormal) + 1); + success = processBlockPart(bp, blockbuffer); + } + blockComplete = true; + for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { + if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) + blockComplete = false; + } + if (blockComplete) { + radioRxEnable(false); + radioRxFlush(); + } + } + } + if (blockComplete == false) { + radioRxEnable(false); + radioRxFlush(); + } + return success; +} +static struct blockRequestAck *continueToRX() { + struct blockRequestAck *ack = (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); + ack->pleaseWaitMs = 0; + return ack; +} +static void sendBlockRequest() { + memset(outBuffer, 0, sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2); + struct MacFrameNormal *f = (struct MacFrameNormal *)(outBuffer + 1); + struct blockRequest *blockreq = (struct blockRequest *)(outBuffer + 2 + sizeof(struct MacFrameNormal)); + outBuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockRequest) + 2 + 2; + if (requestPartialBlock) { + ; + outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PARTIAL_REQUEST; + } else { + outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST; + } + memcpy(f->src, mSelfMac, 8); + memcpy(f->dst, APmac, 8); + f->fcs.frameType = 1; + f->fcs.secure = 0; + f->fcs.framePending = 0; + f->fcs.ackReqd = 0; + f->fcs.panIdCompressed = 1; + f->fcs.destAddrType = 3; + f->fcs.frameVer = 0; + f->fcs.srcAddrType = 3; + f->seq = seq++; + f->pan = APsrcPan; + memcpy(blockreq, &curBlock, sizeof(struct blockRequest)); + // printf("req ver: %02X%02X%02X%02X%02X%02X%02X%02X\n", ((uint8_t*)&blockreq->ver)[0],((uint8_t*)&blockreq->ver)[1],((uint8_t*)&blockreq->ver)[2],((uint8_t*)&blockreq->ver)[3],((uint8_t*)&blockreq->ver)[4],((uint8_t*)&blockreq->ver)[5],((uint8_t*)&blockreq->ver)[6],((uint8_t*)&blockreq->ver)[7]); + addCRC(blockreq, sizeof(struct blockRequest)); + commsTxNoCpy(outBuffer); +} +static struct blockRequestAck *performBlockRequest() { + uint32_t t; + radioRxEnable(true); + radioRxFlush(); + for (uint8_t c = 0; c < 30; c++) { + sendBlockRequest(); + t = millis() + (HAL_TIMER_TICK * ((7UL + c / 10))); + do { + int8_t ret = HAL_PacketRX(inBuffer); + if (ret > 1) { + switch (getPacketType(inBuffer)) { + case PKT_BLOCK_REQUEST_ACK: + if (checkCRC((inBuffer + sizeof(struct MacFrameNormal) + 1), sizeof(struct blockRequestAck))) + return (struct blockRequestAck *)(inBuffer + sizeof(struct MacFrameNormal) + 1); + break; + case PKT_BLOCK_PART: + return continueToRX(); + break; + case PKT_CANCEL_XFER: + return NULL; + default: + printf("pkt w/type %02X\n", getPacketType(inBuffer)); + break; + } + } + + } while (millis() < t); + } + return continueToRX(); + // return NULL; +} +static void sendXferCompletePacket() { + memset(outBuffer, 0, sizeof(struct MacFrameNormal) + 2 + 4); + struct MacFrameNormal *f = (struct MacFrameNormal *)(outBuffer + 1); + outBuffer[0] = sizeof(struct MacFrameNormal) + 2 + 2; + outBuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE; + memcpy(f->src, mSelfMac, 8); + memcpy(f->dst, APmac, 8); + f->fcs.frameType = 1; + f->fcs.secure = 0; + f->fcs.framePending = 0; + f->fcs.ackReqd = 0; + f->fcs.panIdCompressed = 1; + f->fcs.destAddrType = 3; + f->fcs.frameVer = 0; + f->fcs.srcAddrType = 3; + f->pan = APsrcPan; + f->seq = seq++; + commsTxNoCpy(outBuffer); +} +static void sendXferComplete() { + radioRxEnable(true); + + for (uint8_t c = 0; c < 16; c++) { + sendXferCompletePacket(); + uint32_t start = millis(); + while ((millis() - start) < (HAL_TIMER_TICK * (6UL))) { + int8_t ret = HAL_PacketRX(inBuffer); + if (ret > 1) { + if (getPacketType(inBuffer) == PKT_XFER_COMPLETE_ACK) { + printf("XFC ACK\n"); + return; + } + } + } + } + printf("XFC NACK!\n"); + return; +} +static bool validateBlockData(uint8_t *blockbuffer) { + struct blockData *bd = (struct blockData *)blockbuffer; + printf("expected len = %d, checksum=%d\n", bd->size, bd->checksum); + if (bd->size > BLOCK_XFER_BUFFER_SIZE - sizeof(blockData)) { + printf("Impossible data size; size = %d\n", bd->size); + return false; + } + uint16_t t = 0; + for (uint16_t c = 0; c < bd->size; c++) { + t += bd->data[c]; + } + printf("calculated checksum = %04X, %d\n", t, t); + if (t != bd->checksum) dump(blockbuffer, BLOCK_XFER_BUFFER_SIZE + sizeof(blockData)); + return true; + return bd->checksum == t; +} + +// EEprom related stuff +static uint32_t getAddressForSlot(const uint8_t s) { + return EEPROM_IMG_START + (tag.imageSize * s); +} +static void getNumSlots() { + uint32_t eeSize = eepromGetSize(); + printf("eeSize = %lu, image = %lu\n", eeSize, tag.imageSize); + + uint16_t nSlots = (eeSize - EEPROM_SETTINGS_SIZE) / (tag.imageSize >> 8) >> 8; + if (!nSlots) { + printf("eeprom is too small\n"); + while (1) + ; + } else if (nSlots >> 8) { + printf("eeprom is too big, some will be unused\n"); + imgSlots = 254; + } else + imgSlots = nSlots; + printf("EEPROM reported size = %lu, %d slots\n", eeSize, imgSlots); +} +static uint8_t findSlotVer(uint64_t ver) { +#ifdef DEBUGBLOCKS + return 0xFF; // remove me! This forces the tag to re-download each and every upload without checking if it's already in the eeprom somewhere +#endif + uint32_t markerValid = EEPROM_IMG_VALID; + for (uint8_t c = 0; c < imgSlots; c++) { + struct EepromImageHeader eih; + eepromRead(getAddressForSlot(c), &eih, sizeof(struct EepromImageHeader)); + if (!memcmp(&eih.validMarker, &markerValid, 4)) { + if (eih.version == ver) { + return c; + } + } + } + return 0xFF; +} + +uint8_t findSlotDataTypeArg(uint8_t arg) { + arg &= (0xF8); // unmatch with the 'preload' bit and LUT bits + for (uint8_t c = 0; c < imgSlots; c++) { + struct EepromImageHeader eih; + eepromRead(getAddressForSlot(c), &eih, sizeof(struct EepromImageHeader)); + if (eih.validMarker == EEPROM_IMG_VALID) { + if ((eih.argument & 0xF8) == arg) { + return c; + } + } + } + return 0xFF; +} +uint8_t getEepromImageDataArgument(const uint8_t slot) { + struct EepromImageHeader eih; + eepromRead(getAddressForSlot(slot), &eih, sizeof(struct EepromImageHeader)); + return eih.argument; +} +uint8_t findNextSlideshowImage(uint8_t start) { + struct EepromImageHeader eih; + ; + uint8_t c = start; + while (1) { + c++; + if (c > imgSlots) c = 0; + if (c == start) return c; + eepromRead(getAddressForSlot(c), &eih, sizeof(struct EepromImageHeader)); + if (eih.validMarker == EEPROM_IMG_VALID) { + if ((eih.argument & 0xF8) == (CUSTOM_IMAGE_SLIDESHOW << 3)) { + return c; + } + } + } +} + +static void eraseUpdateBlock(uint32_t fwsize) { +#ifdef FWNRF + eepromErase(FW_LOC, (FW_METADATA_LOC + EEPROM_ERZ_SECTOR_SZ) / EEPROM_ERZ_SECTOR_SZ); +#endif +#ifdef FW88MZ100 + eepromErase(FW_LOC, flashRoundUp(fwsize) / EEPROM_ERZ_SECTOR_SZ); +#endif +} +static void eraseImageBlock(const uint8_t c) { + eepromErase(getAddressForSlot(c), tag.imageSize / EEPROM_ERZ_SECTOR_SZ); +} +static void saveUpdateBlockData(uint8_t blockId, uint8_t *blockbuffer) { + if (!eepromWrite(FW_LOC + (blockId * BLOCK_DATA_SIZE), blockbuffer + sizeof(struct blockData), BLOCK_DATA_SIZE)) + printf("EEPROM write failed\n"); +} + +static void saveImgBlockData(const uint8_t imgSlot, const uint8_t blockId, uint8_t *blockbuffer) { + uint32_t length = tag.imageSize - (sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE)); + if (length > 4096) + length = 4096; + + if (!eepromWrite(getAddressForSlot(imgSlot) + sizeof(struct EepromImageHeader) + (blockId * BLOCK_DATA_SIZE), blockbuffer + sizeof(struct blockData), length)) + printf("EEPROM write failed\n"); +} +void eraseImageBlocks() { + for (uint8_t c = 0; c < imgSlots; c++) { + eraseImageBlock(c); + } +} +void drawImageFromEeprom(const uint8_t imgSlot, uint8_t lut) { + drawImageAtAddress(getAddressForSlot(imgSlot), lut); +} +static uint32_t getHighSlotId() { + uint32_t temp = 0; + uint32_t markerValid = EEPROM_IMG_VALID; + for (uint8_t c = 0; c < imgSlots; c++) { + struct EepromImageHeader eih; + eepromRead(getAddressForSlot(c), &eih, sizeof(struct EepromImageHeader)); + if (!memcmp(&eih.validMarker, &markerValid, 4)) { + if (temp < eih.id) { + temp = eih.id; + nextImgSlot = c; + } + } + } + printf("found high id=%lu in slot %d\n", temp, nextImgSlot); + return temp; +} + +static uint8_t *getDataBlock(const uint16_t blockSize) { + uint8_t partsThisBlock = 0; + uint8_t blockAttempts = 0; + + uint8_t *blockbuffer = (uint8_t *)malloc(BLOCK_XFER_BUFFER_SIZE); + if (!blockbuffer) { + printf("failed to allocate block buffer\n"); + return nullptr; + } + + blockAttempts = BLOCK_TRANSFER_ATTEMPTS; + if (blockSize == BLOCK_DATA_SIZE) { + partsThisBlock = BLOCK_MAX_PARTS; + memset(curBlock.requestedParts, 0xFF, BLOCK_REQ_PARTS_BYTES); + } else { + partsThisBlock = (sizeof(struct blockData) + blockSize) / BLOCK_PART_DATA_SIZE; + if (blockSize % BLOCK_PART_DATA_SIZE) + partsThisBlock++; + memset(curBlock.requestedParts, 0x00, BLOCK_REQ_PARTS_BYTES); + for (uint8_t c = 0; c < partsThisBlock; c++) { + curBlock.requestedParts[c / 8] |= (1 << (c % 8)); + } + } + + requestPartialBlock = false; // this forces the AP to request the block data from the host + + while (blockAttempts--) { +#ifndef DEBUGBLOCKS + printf("REQ %d ", curBlock.blockId); +#else + printf("REQ %d[", curBlock.blockId); + for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { + if ((c != 0) && (c % 8 == 0)) + printf("]["); + if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) { + printf("R"); + } else { + printf("_"); + } + } + printf("]\n"); +#endif + powerUp(INIT_RADIO); + struct blockRequestAck *ack = performBlockRequest(); + if (ack == NULL) { + printf("Cancelled request\n"); + free(blockbuffer); + return nullptr; + } + +#ifdef FWNRF + doSleep(ack->pleaseWaitMs + 40); + powerUp(INIT_UART | INIT_RADIO); + radioRxEnable(true); +#endif +#ifdef FW88MZ100 + if (ack->pleaseWaitMs) { // SLEEP - until the AP is ready with the data + timerDelay(TIMER_TICKS_PER_MSEC * ack->pleaseWaitMs); + } else { + // immediately start with the reception of the block data + } +#endif + + blockRxLoop(BLOCKSIZE_MS, blockbuffer); // BLOCK RX LOOP - receive a block, until the timeout has passed + powerDown(INIT_RADIO); + +#ifdef DEBUGBLOCKS + printf("RX %d[", curBlock.blockId); + for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) { + if ((c != 0) && (c % 8 == 0)) + printf("]["); + if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) { + printf("."); + } else { + printf("R"); + } + } + printf("]\n"); +#endif + // check if we got all the parts we needed, e.g: has the block been completed? + bool blockComplete = true; + for (uint8_t c = 0; c < partsThisBlock; c++) { + if (curBlock.requestedParts[c / 8] & (1 << (c % 8))) + blockComplete = false; + } + doSleep(30); + + if (blockComplete) { +#ifndef DEBUGBLOCKS + printf("- COMPLETE\n"); +#endif + if (validateBlockData(blockbuffer)) { + // printf("- Validated\n"); + // block download complete, validated + return blockbuffer; + } else { + for (uint8_t c = 0; c < partsThisBlock; c++) { + curBlock.requestedParts[c / 8] |= (1 << (c % 8)); + } + requestPartialBlock = false; + printf("blk failed validation!\n"); + } + } else { +#ifndef DEBUGBLOCKS + printf("- INCOMPLETE\n"); +#endif + // block incomplete, re-request a partial block + requestPartialBlock = true; + } + } + printf("failed getting block\n"); + free(blockbuffer); + return nullptr; +} + +static uint32_t downloadFWUpdate(const struct AvailDataInfo *avail) { + uint16_t dataRequestSize = 0; + uint32_t curXferSize = 0; + // check if we already started the transfer of this information & haven't completed it + if (!memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8) && xferDataInfo.dataSize) { + // looks like we did. We'll carry on where we left off. + } else { + // start, or restart the transfer from 0. Copy data from the AvailDataInfo struct, and the struct intself. This forces a new transfer + curBlock.blockId = 0; + memcpy(&(curBlock.ver), &(avail->dataVer), 8); + curBlock.type = avail->dataType; + memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + curXferSize = avail->dataSize; + eraseUpdateBlock(curXferSize); + } + + while (xferDataInfo.dataSize) { + wdt10s(); + if (xferDataInfo.dataSize > BLOCK_DATA_SIZE) { + // more than one block remaining + dataRequestSize = BLOCK_DATA_SIZE; + } else { + // only one block remains + dataRequestSize = xferDataInfo.dataSize; + } + if (uint8_t *blockbuffer = getDataBlock(dataRequestSize)) { + // succesfully downloaded datablock, save to eeprom + powerUp(INIT_EEPROM); + saveUpdateBlockData(curBlock.blockId, blockbuffer); + powerDown(INIT_EEPROM); + free(blockbuffer); + curBlock.blockId++; + xferDataInfo.dataSize -= dataRequestSize; + } else { + // failed to get the block we wanted, we'll stop for now, maybe resume later + return 0; + } + } + + // no more data, download complete + powerUp(INIT_EEPROM); + if (validateEepromMD5(xferDataInfo.dataVer, FW_LOC, curXferSize)) { + // md5 matches + powerDown(INIT_EEPROM); + return curXferSize; + } else { + // md5 does not match, invalidate current transfer result, forcing a restart of the transfer + memset((void *)&xferDataInfo, 0, sizeof(struct AvailDataInfo)); + powerDown(INIT_EEPROM); + return 0; + } +} + +static bool downloadImageDataToEEPROM(const struct AvailDataInfo *avail) { + uint16_t dataRequestSize = 0; + uint32_t curXferSize = 0; + powerUp(INIT_EEPROM); + // check if we already started the transfer of this information & haven't completed it + if (!memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8) && xferDataInfo.dataSize) { + // looks like we did. We'll carry on where we left off. + printf("restarting image download"); + // curImgSlot = nextImgSlot; // hmmm + } else { + // new transfer + uint8_t startingSlot = nextImgSlot; + while (1) { + nextImgSlot++; + if (nextImgSlot >= imgSlots) nextImgSlot = 0; + if (nextImgSlot == startingSlot) { + // looped + powerDown(INIT_EEPROM); + printf("no slot available...\n"); + return true; + } + struct EepromImageHeader eih; + eepromRead(getAddressForSlot(nextImgSlot), &eih, sizeof(struct EepromImageHeader)); + // checked if the marker is valid + if (eih.validMarker == EEPROM_IMG_VALID) { + struct imageDataTypeArgStruct *eepromDataArgument = (struct imageDataTypeArgStruct *)&(eih.argument); + // if this is a normal type, we can replace it + if (eepromDataArgument->specialType == 0x00) break; + } else { + // invalid header, so safe to overwrite anyway + break; + } + } + + xferImgSlot = nextImgSlot; + + uint8_t attempt = 5; + while (attempt--) { + if (eepromErase(getAddressForSlot(xferImgSlot), tag.imageSize / EEPROM_ERZ_SECTOR_SZ)) goto eraseSuccess; + } + eepromFail: + powerDown(INIT_RADIO); +#ifdef FWNRF + powerUp(INIT_EPD); + showNoEEPROM(); + powerDown(INIT_EEPROM | INIT_EPD); + doSleep(-1); +#endif + NVIC_SystemReset(); + eraseSuccess: + printf("new download, writing to slot %d\n", xferImgSlot); + // start, or restart the transfer. Copy data from the AvailDataInfo struct, and the struct intself. This forces a new transfer + curBlock.blockId = 0; + memcpy(&(curBlock.ver), &(avail->dataVer), 8); + curBlock.type = avail->dataType; + memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + curXferSize = xferDataInfo.dataSize; + } + + while (xferDataInfo.dataSize) { + wdt10s(); + if (xferDataInfo.dataSize > BLOCK_DATA_SIZE) { + // more than one block remaining + dataRequestSize = BLOCK_DATA_SIZE; + } else { + // only one block remains + dataRequestSize = xferDataInfo.dataSize; + } + if (uint8_t *blockbuffer = getDataBlock(dataRequestSize)) { + // succesfully downloaded datablock, save to eeprom + powerUp(INIT_EEPROM); +#ifdef DEBUGBLOCKS + printf("Saving block %d to slot %d\n", curBlock.blockId, xferImgSlot); +#endif + saveImgBlockData(xferImgSlot, curBlock.blockId, blockbuffer); + powerDown(INIT_EEPROM); + free(blockbuffer); + curBlock.blockId++; + xferDataInfo.dataSize -= dataRequestSize; + } else { + // failed to get the block we wanted, we'll stop for now, probably resume later + return false; + } + } + // no more data, download complete + + struct EepromImageHeader eih; + memcpy(&eih.version, &xferDataInfo.dataVer, 8); + eih.validMarker = EEPROM_IMG_VALID; + eih.id = ++curHighSlotId; + eih.size = curXferSize; + eih.dataType = xferDataInfo.dataType; + eih.argument = xferDataInfo.dataTypeArgument; + +#ifdef DEBUGBLOCKS + printf("Now writing datatype 0x%02X to slot %d\n", xferDataInfo.dataType, xferImgSlot); +#endif + + powerUp(INIT_EEPROM); + if (validateEepromMD5(xferDataInfo.dataVer, getAddressForSlot(xferImgSlot) + sizeof(struct EepromImageHeader), curXferSize)) { + // md5 matches + eepromWrite(getAddressForSlot(xferImgSlot), &eih, sizeof(struct EepromImageHeader)); + powerDown(INIT_EEPROM); + + } else { + // md5 does not match, invalidate current transfer result, forcing a restart of the transfer + memset((void *)&xferDataInfo, 0, sizeof(struct AvailDataInfo)); + powerDown(INIT_EEPROM); + return false; + } + return true; +} + +bool processImageDataAvail(struct AvailDataInfo *avail) { + struct imageDataTypeArgStruct arg = *((struct imageDataTypeArgStruct *)avail->dataTypeArgument); + + if (arg.preloadImage) { + printf("Preloading image with type 0x%02X from arg 0x%02X\n", arg.specialType, avail->dataTypeArgument); + powerUp(INIT_EEPROM); + switch (arg.specialType) { + // check if a slot with this argument is already set; if so, erase. Only one of each arg type should exist + default: { + uint8_t slot = findSlotDataTypeArg(avail->dataTypeArgument); + if (slot != 0xFF) { + eepromErase(getAddressForSlot(slot), tag.imageSize / EEPROM_ERZ_SECTOR_SZ); + } + } break; + // regular image preload, there can be multiple of this type in the EEPROM + case CUSTOM_IMAGE_NOCUSTOM: { + // check if a version of this already exists + uint8_t slot = findSlotVer(avail->dataVer); + if (slot != 0xFF) { + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } + } break; + case CUSTOM_IMAGE_SLIDESHOW: + break; + } + powerDown(INIT_EEPROM); + + printf("downloading preload image...\n"); + if (downloadImageDataToEEPROM(avail)) { + // sets xferImgSlot to the right slot + printf("preload complete!\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } else { + return false; + } + + } else { + // check if we're currently displaying this data payload + if (avail->dataVer == curDispDataVer) { + // currently displayed, not doing anything except for sending an XFC + + printf("currently shown image, send xfc\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + + } else { + // currently not displayed + + // try to find the data in the SPI EEPROM + powerUp(INIT_EEPROM); + uint8_t findImgSlot = findSlotVer(avail->dataVer); + powerDown(INIT_EEPROM); + + // Is this image already in a slot somewhere + if (findImgSlot != 0xFF) { + // found a (complete)valid image slot for this version + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + + // mark as completed and draw from EEPROM + memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + xferDataInfo.dataSize = 0; // mark as transfer not pending + + wdt60s(); + curImgSlot = findImgSlot; + powerUp(INIT_EPD | INIT_EEPROM); + drawImageFromEeprom(findImgSlot, arg.lut); + powerDown(INIT_EPD | INIT_EEPROM); + + } else { + // not found in cache, prepare to download + printf("downloading image...\n"); + if (downloadImageDataToEEPROM(avail)) { + // sets xferImgSlot to the right slot + printf("download complete!\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + + // not preload, draw now + wdt60s(); + curImgSlot = xferImgSlot; + powerUp(INIT_EPD | INIT_EEPROM); + drawImageFromEeprom(xferImgSlot, arg.lut); + powerDown(INIT_EPD | INIT_EEPROM); + + } else { + return false; + } + } + + // keep track on what is currently displayed + curDispDataVer = xferDataInfo.dataVer; + return true; + } + } +} + +bool processAvailDataInfo(struct AvailDataInfo *avail) { + switch (avail->dataType) { + case DATATYPE_IMG_RAW_1BPP: + case DATATYPE_IMG_RAW_2BPP: + case DATATYPE_IMG_ZLIB: + processImageDataAvail(avail); + break; + case DATATYPE_FW_UPDATE: + powerUp(INIT_EEPROM); + if (uint32_t fwsize = downloadFWUpdate(avail)) { + printf("firmware download complete, doing update.\n"); + + powerUp(INIT_EPD); + // showApplyUpdate(); + + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + + powerUp(INIT_EEPROM); + wdt60s(); +#ifdef FWNRF + saveUpdateMetadata(fwsize); +#endif + +#ifdef FW88MZ100 + applyUpdate(fwsize); +#endif + NVIC_SystemReset(); + } else { + return false; + } + break; + case DATATYPE_NFC_URL_DIRECT: + case DATATYPE_NFC_RAW_CONTENT: { + // Handle data for the NFC IC (if we have it) + + // check if we actually have the capability to do NFC + if (!(capabilities & CAPABILITY_HAS_NFC)) { + // looks like we don't. mark as complete and then bail! + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } + + printf("NFC URL received\n"); + if (xferDataInfo.dataSize == 0 && !memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8)) { + // we've already downloaded this NFC data, disregard and send XFC + printf("this was the same as the last transfer, disregard\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } + curBlock.blockId = 0; + memcpy(&(curBlock.ver), &(avail->dataVer), 8); + curBlock.type = avail->dataType; + memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + uint16_t nfcsize = avail->dataSize; + wdt10s(); + if (uint8_t *blockbuffer = getDataBlock(avail->dataSize)) { + xferDataInfo.dataSize = 0; // mark as transfer not pending + powerUp(INIT_I2C); + if (avail->dataType == DATATYPE_NFC_URL_DIRECT) { + // only one URL (handle NDEF records on the tag) + // loadURLtoNTag(); + } else { + // raw NFC data upload to the NFC IC + // loadRawNTag(nfcsize); + } + delay(13330); + free(blockbuffer); + powerDown(INIT_I2C); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } + return false; + break; + } + case DATATYPE_COMMAND_DATA: + memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); +#ifdef LEDSENABLED + if (avail->dataTypeArgument == 4) { + printf("LED CMD"); + setled(xferDataInfo.dataVer, xferDataInfo.dataSize); + } +#endif + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + if (avail->dataTypeArgument != 4) { + executeCommand(xferDataInfo.dataTypeArgument); + } + return true; + break; + case DATATYPE_TAG_CONFIG_DATA: + if (xferDataInfo.dataSize == 0 && memcmp((const void *)&avail->dataVer, (const void *)&xferDataInfo.dataVer, 8) == 0) { + printf("this was the same as the last transfer, disregard\n"); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } + curBlock.blockId = 0; + memcpy(&(curBlock.ver), &(avail->dataVer), sizeof(curBlock.ver)); + curBlock.type = avail->dataType; + memcpy(&xferDataInfo, (void *)avail, sizeof(struct AvailDataInfo)); + wdt10s(); + if (uint8_t *blockbuffer = getDataBlock(avail->dataSize)) { + xferDataInfo.dataSize = 0; // mark as transfer not pending + powerUp(INIT_EEPROM); + loadSettingsFromBuffer(sizeof(struct blockData) + blockbuffer); + free(blockbuffer); + powerDown(INIT_EEPROM); + powerUp(INIT_RADIO); + sendXferComplete(); + powerDown(INIT_RADIO); + return true; + } + return false; + break; + } + return true; +} + +void initializeProto() { + getNumSlots(); + curHighSlotId = getHighSlotId(); +} \ No newline at end of file diff --git a/ARM_Tag_FW/common/oepl-protocol.h b/ARM_Tag_FW/common/oepl-protocol.h new file mode 100644 index 00000000..6b8541ae --- /dev/null +++ b/ARM_Tag_FW/common/oepl-protocol.h @@ -0,0 +1,21 @@ +extern PERSISTENTVAR uint8_t mSelfMac[]; +extern PERSISTENTVAR volatile uint8_t currentChannel; +extern PERSISTENTVAR uint8_t APmac[]; + +extern PERSISTENTVAR uint8_t curImgSlot; + +extern void setupRadio(void); +extern void killRadio(void); + +extern uint8_t findSlotDataTypeArg(uint8_t arg); +extern uint8_t findNextSlideshowImage(uint8_t start); +extern uint8_t getEepromImageDataArgument(const uint8_t slot); + +extern struct AvailDataInfo *getAvailDataInfo(); +extern struct AvailDataInfo *getShortAvailDataInfo(); + +extern void drawImageFromEeprom(const uint8_t imgSlot, uint8_t lut); +extern void eraseImageBlocks(); +extern bool processAvailDataInfo(struct AvailDataInfo *avail); +extern void initializeProto(); +extern uint8_t detectAP(const uint8_t channel);