From b9329ce555858116b109fe608fdfeeaa457aa844 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sat, 12 Sep 2020 19:38:29 +0200 Subject: [PATCH] Add Waveshare support (bitmap ok, RGB maybe broken?) --- CHANGELOG.md | 3 +- client/CMakeLists.txt | 1 + client/Makefile | 3 +- client/android/CMakeLists.txt | 1 + client/src/cmdhf.c | 4 +- client/src/cmdhfwaveshare.c | 878 ++++++++++++++++++++++++++++++++++ client/src/cmdhfwaveshare.h | 10 + client/src/ui.h | 1 + 8 files changed, 897 insertions(+), 4 deletions(-) create mode 100644 client/src/cmdhfwaveshare.c create mode 100644 client/src/cmdhfwaveshare.h diff --git a/CHANGELOG.md b/CHANGELOG.md index eccad5f86..55d26c791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] - - Add `hf 14a config` to deal with badly configured cards: invalid ATQA/BCC/SAK (@doegox)" + - Add `hf waveshare` to upload picture to Waveshare NFC-Powered e-Paper (@doegox) + - Add `hf 14a config` to deal with badly configured cards: invalid ATQA/BCC/SAK (@doegox) - Mikron JSC Russia Ultralight EV1 41 pages tag type support (@McEloff) - Add test for Ultralight gen2 magic 'hf search' (@McEloff) - Add test for Ultralight EV1 gen2 magic 'hf search' (@McEloff) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index ab8f20c74..fc56b594a 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -241,6 +241,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhfst.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c + ${PM3_ROOT}/client/src/cmdhfwaveshare.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c diff --git a/client/Makefile b/client/Makefile index 5af53463d..ebf07c4b4 100644 --- a/client/Makefile +++ b/client/Makefile @@ -432,9 +432,10 @@ SRCS = aidsearch.c \ cmdhfmfhard.c \ cmdhfmfu.c \ cmdhfmfp.c \ + cmdhfst.c \ cmdhfthinfilm.c \ cmdhftopaz.c \ - cmdhfst.c \ + cmdhfwaveshare.c \ cmdhw.c \ cmdlf.c \ cmdlfawid.c \ diff --git a/client/android/CMakeLists.txt b/client/android/CMakeLists.txt index cc827d143..a55055770 100644 --- a/client/android/CMakeLists.txt +++ b/client/android/CMakeLists.txt @@ -119,6 +119,7 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdhfmfu.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c + ${PM3_ROOT}/client/src/cmdhfwaveshare.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 4d89cca62..dcd85c5d5 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -36,6 +36,7 @@ #include "cmdhflto.h" // LTO-CM #include "cmdhfcryptorf.h" // CryptoRF #include "cmdhfst.h" // ST rothult +#include "cmdhfwaveshare.h" // Waveshare #include "cmdtrace.h" // trace list #include "ui.h" #include "proxgui.h" @@ -85,8 +86,6 @@ static int usage_hf_tune(void) { return PM3_SUCCESS; } -#define PROMPT_CLEARLINE PrintAndLogEx(INPLACE, " \r") - int CmdHFSearch(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); @@ -369,6 +368,7 @@ static command_t CommandTable[] = { {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, {"plot", CmdHFPlot, IfPm3Hfplot, "Plot signal"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, diff --git a/client/src/cmdhfwaveshare.c b/client/src/cmdhfwaveshare.c new file mode 100644 index 000000000..8494f9f48 --- /dev/null +++ b/client/src/cmdhfwaveshare.c @@ -0,0 +1,878 @@ +//----------------------------------------------------------------------------- +// Waveshare commands +//----------------------------------------------------------------------------- +// from ST25R3911B-NFC-Demo source code by Waveshare team + +#include "cmdhfwaveshare.h" + +#include +#include +#include "comms.h" +#include "cmdparser.h" +#include "ui.h" +#include "util.h" +#include "fileutils.h" +#include "util_posix.h" // msleep + +// Currently the largest pixel 880*528 only needs 58.08K bytes +#define WSMAPSIZE 60000 + +#pragma pack(1) /* Mandatory to remove any padding */ +typedef struct { + uint8_t B; + uint8_t M; + uint32_t fsize; + uint16_t res1; + uint16_t res2; + uint32_t offset; + uint32_t Bit_Pixel; + uint32_t BMP_Width; + uint32_t BMP_Height; + uint16_t planes; + uint16_t bpp; + uint32_t ctype; + uint32_t dsize; + uint32_t hppm; + uint32_t vppm; + uint32_t colorsused; + uint32_t colorreq; + uint32_t Color_1; //Color palette + uint32_t Color_2; +} PACKED BMP_HEADER; + +#define EPD_1IN54B 0 +#define EPD_1IN54C 1 +#define EPD_1IN54V2 2 +#define EPD_1IN54BCV2 3 +#define EPD_2IN13V2 4 +#define EPD_2IN13BC 5 +#define EPD_2IN13D 6 +#define EPD_2IN9 7 +#define EPD_2IN9BC 8 +#define EPD_2IN9D 9 +#define EPD_4IN2 10 +#define EPD_4IN2BC 11 +#define EPD_7IN5 12 +#define EPD_7IN5BC 13 +#define EPD_7IN5V2 14 +#define EPD_7IN5BCV2 15 +#define EPD_2IN7 16 +#define EPD_7IN5HD 17 + +typedef struct model_s { + const char *desc; + uint8_t len; // The data sent in one time shall not be greater than 128-3 +} model_t; + +typedef enum { + M2in13 = 0, + M2in9, + M4in2, + M7in5, + M2in7, + M2in13B, + M1in54B, + M7in5HD, + MEND +} model_enum_t; + +static model_t models[] = { + {"2.13 inch e-paper", 16}, + {"2.9 inch e-paper", 16}, + {"4.2 inch e-paper", 100}, + {"7.5 inch e-paper", 120}, + {"2.7 inch e-paper", 121}, + {"2.13 inch e-paper B", 106}, + {"1.54 inch e-paper B", 100}, + {"7.5 inch e-paper HD", 120}, +}; + +static int CmdHelp(const char *Cmd); + +static int usage_hf_waveshare_loadbmp(void) { + PrintAndLogEx(NORMAL, "Load BMP file to Waveshare NFC ePaper."); + PrintAndLogEx(NORMAL, "Usage: hf waveshare loadbmp [h] f m "); + PrintAndLogEx(NORMAL, " Options :"); + PrintAndLogEx(NORMAL, " f : " _YELLOW_("filename[.bmp]") " to upload to tag"); + PrintAndLogEx(NORMAL, " m : " _YELLOW_("model number") " of your tag"); + for (uint8_t i=0; i< MEND; i++) { + PrintAndLogEx(NORMAL, " m %2i : %s", i, models[i].desc); + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf waveshare loadbmp m 0 f myfile")); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize) { + if (bmpsize < sizeof(BMP_HEADER)) + return PM3_ESOFT; + BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp; + PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused); + PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp); + return pbmpheader->bpp; +} + + +static int read_bmp_bitmap(const uint8_t *bmp, const size_t bmpsize, uint8_t **black) { + BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp; + // check file is bitmap + if (pbmpheader->bpp != 1) { + return PM3_ESOFT; + } + if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42 + PrintAndLogEx(WARNING, "The file is not a BMP!"); + return PM3_ESOFT; + } + PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize); + PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset); + if (pbmpheader->fsize > bmpsize) { + PrintAndLogEx(WARNING, "The file is truncated!"); + return PM3_ESOFT; + } + uint8_t color_flag = pbmpheader->Color_1; + // Get BMP file data pointer + uint32_t offset = pbmpheader->offset; + + uint16_t X, Y; + uint16_t Image_Width_Byte = (pbmpheader->BMP_Width % 8 == 0) ? (pbmpheader->BMP_Width / 8) : (pbmpheader->BMP_Width / 8 + 1); + uint16_t Bmp_Width_Byte = (Image_Width_Byte % 4 == 0) ? Image_Width_Byte : ((Image_Width_Byte / 4 + 1) * 4); + + *black = calloc(WSMAPSIZE, sizeof(uint8_t)); + if (*black == NULL) { + return PM3_EMALLOC; + } + // Write data into RAM + for (Y = 0; Y < pbmpheader->BMP_Height; Y++) { // columns + for (X = 0; X < Bmp_Width_Byte; X++) { // lines + if ((X < Image_Width_Byte) && ((X + (pbmpheader->BMP_Height - Y - 1) * Image_Width_Byte) < WSMAPSIZE)) { + (*black)[X + (pbmpheader->BMP_Height - Y - 1) * Image_Width_Byte] = color_flag ? bmp[offset] : ~bmp[offset]; + offset++; + } + } + } + return PM3_SUCCESS; +} + +static int read_bmp_rgb(const uint8_t *bmp, const size_t bmpsize, uint8_t **black, uint8_t **red) { + BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp; + // check file is full color + if (pbmpheader->bpp != 24) { + return PM3_ESOFT; + } + if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42 + PrintAndLogEx(WARNING, "The file is not a BMP!"); + return PM3_ESOFT; + } + PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize); + PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset); + if (pbmpheader->fsize > bmpsize) { + PrintAndLogEx(WARNING, "The file is truncated!"); + return PM3_ESOFT; + } + // Get BMP file data pointer + uint32_t offset = pbmpheader->offset; + + uint16_t X, Y; + uint16_t Image_Width_Byte = (pbmpheader->BMP_Width % 8 == 0) ? (pbmpheader->BMP_Width / 8) : (pbmpheader->BMP_Width / 8 + 1); + + *black = calloc(WSMAPSIZE, sizeof(uint8_t)); + if (*black == NULL) { + return PM3_EMALLOC; + } + *red = calloc(WSMAPSIZE, sizeof(uint8_t)); + if (*red == NULL) { + free(*black); + return PM3_EMALLOC; + } + + uint8_t R = 0, G = 0, B = 0; + uint8_t Black_data = 0; + uint8_t Red_data = 0; + uint8_t count = 0; + // Write data into RAM + for (Y = 0; Y < pbmpheader->BMP_Height; Y++) { // columns + for (X = 0; X < pbmpheader->BMP_Width; X++) { // lines + B = bmp[offset++]; + G = bmp[offset++]; + G = bmp[offset++]; + if (R < 30 && G < 30 && B < 30) { + Black_data = Black_data | (1); + } else if (R > 190 && G < 90 && B < 90) { + Red_data = Red_data | (1); + } + count++; + if (count >= 8) { + (*black)[X / 8 + (pbmpheader->BMP_Height - Y - 1) * Image_Width_Byte] = ~Black_data; + (*red)[X / 8 + (pbmpheader->BMP_Height - Y - 1) * Image_Width_Byte] = ~Red_data; + count = 0; + Black_data = 0; + Red_data = 0; + } + Black_data = Black_data << 1; + Red_data = Red_data << 1; + } + } + return PM3_SUCCESS; +} + +static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, uint8_t *black) { + for (uint8_t j = 0; j < models[model_nr].len; j++) { + l[3 + j] = black[i * models[model_nr].len + j]; + } +} +static void read_red(uint32_t i, uint8_t *l, uint8_t model_nr, uint8_t *red) { + for (uint8_t j = 0; j < models[model_nr].len; j++) { + if (model_nr == M1in54B) { + //1.54B needs to flip the red picture data, other screens do not need to flip data + l[3 + j] = ~red[i * models[model_nr].len + j]; + } else { + l[3 + j] = red[i * models[model_nr].len + j]; + } + } +} + +static void transceive_blocking( uint8_t* txBuf, uint16_t txBufLen, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* actLen, uint32_t fwt ){ + *actLen = 2; + (void) fwt; + PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, txBufLen, 0, txBuf, txBufLen); + WaitForResponse(CMD_ACK, &resp); + if (resp.oldarg[0] > rxBufLen) { + PrintAndLogEx(WARNING, "Received % bytes, rxBuf too small (%)", resp.oldarg[0], rxBufLen); + memcpy(rxBuf, resp.data.asBytes, rxBufLen); + *actLen = rxBufLen; + return; + } + memcpy(rxBuf, resp.data.asBytes, resp.oldarg[0]); + *actLen = resp.oldarg[0]; +} + +// 1.54B Keychain +// 1.54B does not share the common base and requires specific handling +static int start_drawing_1in54B(uint8_t model_nr, uint8_t *black, uint8_t *red, uint8_t fail_num) { + uint8_t step = 5; + uint8_t step_5[128] = {0xcd, 0x05, 100}; + uint8_t step_4[2] = {0xcd, 0x04}; + uint8_t step_6[2] = {0xcd, 0x06}; + uint8_t rx[20] = {0}; + uint16_t actrxlen[20], i = 0, progress = 0; + + if (model_nr == M1in54B) { + step_5[2] = 100; + } + while (1) { + if (step == 5) { + PrintAndLogEx(INFO, "1.54_Step9: e-paper config2 (black)"); + if (model_nr == M1in54B) { //1.54inch B Keychain + for (i = 0; i < 50; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step_5, model_nr, black); + transceive_blocking(step_5, 103, rx, 20, actrxlen, 2157 + 2048); // cd 05 + progress = i * 100 / 100; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } + PROMPT_CLEARLINE; + step = 6; + } else if (step == 6) { + PrintAndLogEx(INFO, "1.54_Step6: e-paper power on"); + transceive_blocking(step_4, 2, rx, 20, actrxlen, 2157 + 2048); //cd 04 + step = 7; + if (rx[0] == 0 && rx[1] == 0) { + step = 7; + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please press any key to exit and try again."); + step = 14; + fail_num = 0; + msleep(200); + } + } + } else if (step == 7) { + PrintAndLogEx(INFO, "1.54_Step7: e-paper config2 (red)"); + if (model_nr == M1in54B) { //1.54inch B Keychain + for (i = 0; i < 50; i++) { + rx[0] = 1; + rx[1] = 1; + read_red(i, step_5, model_nr, red); + transceive_blocking(step_5, 103, rx, 20, actrxlen, 2157 + 2048); // cd 05 + progress = i * 100 / 100 + 50; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } + PROMPT_CLEARLINE; + step = 8; + rx[0] = 1; + rx[1] = 1; + } else if (step == 8) { + // Send update instructions + PrintAndLogEx(INFO, "1.54_Step8: EDP load to main"); + transceive_blocking(step_6, 2, rx, 20, actrxlen, 2157 + 2048); //cd 06 + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 9; + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please press any key to exit and try again."); + step = 14; + fail_num = 0; + msleep(200); + } + } + } else if (step == 9) { + PrintAndLogEx(INFO, "1.54_Step9"); + return PM3_SUCCESS; + } else if (step == 14) { + return PM3_ESOFT; + } + } +} + +static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { + uint8_t fail_num = 0; + uint8_t step = 0, progress = 0; + uint8_t step0[2] = {0xcd, 0x0d}; + uint8_t step1[3] = {0xcd, 0x00, 10}; //select e-paper type and reset e-paper 4:2.13inch e-Paper 7:2.9inch e-Paper 10:4.2inch e-Paper 14:7.5inch e-Paper + uint8_t step2[2] = {0xcd, 0x01}; //e-paper normal mode type: + uint8_t step3[2] = {0xcd, 0x02}; //e-paper config1 + uint8_t step4[2] = {0xcd, 0x03}; //e-paper power on + uint8_t step5[2] = {0xcd, 0x05}; //e-paper config2 + uint8_t step6[2] = {0xcd, 0x06}; //EDP load to main + uint8_t step7[2] = {0xcd, 0x07}; //Data preparation + uint8_t step8[123] = {0xcd, 0x08, 0x64}; //Data start command 2.13inch(0x10:Send 16 data at a time) 2.9inch(0x10:Send 16 data at a time) 4.2inch(0x64:Send 100 data at a time) 7.5inch(0x78:Send 120 data at a time) + uint8_t step9[2] = {0xcd, 0x18}; //e-paper power on + uint8_t step10[2] = {0xcd, 0x09}; //Refresh e-paper + uint8_t step11[2] = {0xcd, 0x0a}; //wait for ready + uint8_t step12[2] = {0xcd, 0x04}; //e-paper power off command + uint8_t step13[124] = {0xcd, 0x19, 121}; +// uint8_t step13[2]={0xcd,0x0b}; //Judge whether the power supply is turned off successfully +// uint8_t step14[2]={0xcd,0x0c}; //The end of the transmission + uint8_t rx[20]; + uint16_t actrxlen[20], i = 0; + + + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + PrintAndLogEx(ERR, "No tag found"); + DropField(); + return PM3_ETIMEOUT; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.oldarg[0]; + + if (select_status == 0) { + PrintAndLogEx(ERR, "Tag select error"); + DropField(); + return PM3_ERFTRANS; + } else if (select_status == 3) { + PrintAndLogEx(WARNING, "Card doesn't support standard iso14443-3 anticollision, doesn't look like Waveshare tag"); + DropField(); + return PM3_ESOFT; + } + + if ((card.uidlen != 7) || (memcmp(card.uid, "WSDZ10m", 7) != 0)) { + PrintAndLogEx(WARNING, "Card doesn't look like Waveshare tag"); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(DEBUG, "model_nr = %d", model_nr); + while (1) { + if (step == 0) { + PrintAndLogEx(INFO, "Step0"); + transceive_blocking(step0, 2, rx, 20, actrxlen, 2157 + 2048); //cd 0d + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 1; + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + } else if (step == 1) { + PrintAndLogEx(INFO, "Step1: e-paper config"); + //step1[2] screen model + //step8[2] nr of bytes sent at once + //step13[2] nr of bytes sent for the second time + // generally, step8 sends a black image, step13 sends a red image + if (model_nr == M2in13) { //2.13inch + step1[2] = EPD_2IN13V2; + step8[2] = 16; + step13[2] = 0; + } else if (model_nr == M2in9) { //2.9inch + step1[2] = EPD_2IN9; + step8[2] = 16; + step13[2] = 0; + } else if (model_nr == M4in2) { //4.2inch + step1[2] = EPD_4IN2; + step8[2] = 100; + step13[2] = 0; + } else if (model_nr == M7in5) { //7.5inch + step1[2] = EPD_7IN5V2; + step8[2] = 120; + step13[2] = 0; + } else if (model_nr == M2in7) { //2.7inch + step1[2] = EPD_2IN7; + step8[2] = 121; + // Send blank data for the first time, and send other data to 0xff without processing the bottom layer + step13[2] = 121; + //Sending the second data is the real image data. If the previous 0xff is not sent, the last output image is abnormally black + } else if (model_nr == M2in13B) { //2.13inch B + step1[2] = EPD_2IN13BC; + step8[2] = 106; + step13[2] = 106; + } else if (model_nr == M7in5HD) { + step1[2] = EPD_7IN5HD; + step8[2] = 120; + step13[2] = 0; + } + + if (model_nr == M1in54B) { + transceive_blocking(step1, 2, rx, 20, actrxlen, 2157 + 2048); //cd 00 + } else { + transceive_blocking(step1, 3, rx, 20, actrxlen, 2157 + 2048); + } + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 2; + fail_num = 0; + msleep(100); + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + msleep(10); + } else if (step == 2) { + PrintAndLogEx(INFO, "Step2: e-paper normal mode type"); + transceive_blocking(step2, 2, rx, 20, actrxlen, 2157 + 2048); //cd 01 + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 3; + fail_num = 0; + msleep(100); + } else { + fail_num++; + if (fail_num > 50) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + msleep(100); + } else if (step == 3) { + PrintAndLogEx(INFO, "Step3: e-paper config1"); + transceive_blocking(step3, 2, rx, 20, actrxlen, 2157 + 2048); //cd 02 + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 4; + fail_num = 0; + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + msleep(200); + } else if (step == 4) { + PrintAndLogEx(INFO, "Step4: e-paper power on"); + transceive_blocking(step4, 2, rx, 20, actrxlen, 2157 + 2048); //cd 03 + if (model_nr == M1in54B) { + // 1.54B Keychain handler + PrintAndLogEx(DEBUG, "Start_Drawing_1in54B"); + char t = start_drawing_1in54B(model_nr, black, red, fail_num); + if (t == 0) { + step = 11; + //1.54B Data transfer is complete and wait for refresh + } else if (t == 1) { + step = 14; + //1.54B Data transmission error + } + // 1.54B Keychain handler end + } + if (rx[0] == 0 && rx[1] == 0) { + fail_num = 0; + step = 5; + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + } else if (step == 5) { + PrintAndLogEx(INFO, "Step5: e-paper config2"); + transceive_blocking(step5, 2, rx, 20, actrxlen, 2157 + 2048); //cd 05 + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 6; + fail_num = 0; + msleep(100); + } else { + fail_num++; + if (fail_num > 30) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + msleep(10); + } else if (step == 6) { + PrintAndLogEx(INFO, "Step6: EDP load to main") ; + transceive_blocking(step6, 2, rx, 20, actrxlen, 2157 + 2048); //cd 06 + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 7; + fail_num = 0; + msleep(100); + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + } else if (step == 7) { + PrintAndLogEx(INFO, "Step7: Data preparation"); + transceive_blocking(step7, 2, rx, 20, actrxlen, 2157 + 2048); //cd 07 + if (rx[0] == 0 && rx[1] == 0) { + rx[0] = 1; + rx[1] = 1; + step = 8; + fail_num = 0; + } else { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } + } else if (step == 8) { //cd 08 + PrintAndLogEx(INFO, "Step8: Start data transfer"); + if (model_nr == M2in13) { //2.13inch + for (i = 0; i < 250; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step8, model_nr, black); + transceive_blocking(step8, 19, rx, 20, actrxlen, 2157 + 2048); + progress = i * 100 / 250; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } else if (model_nr == M2in9) { + for (i = 0; i < 296; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step8, model_nr, black); + transceive_blocking(step8, 19, rx, 20, actrxlen, 2157 + 2048); + progress = i * 100 / 296; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } else if (model_nr == M4in2) { //4.2inch + for (i = 0; i < 150; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step8, model_nr, black); + transceive_blocking(step8, 103, rx, 20, actrxlen, 2157 + 2048); + progress = i * 100 / 150; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } else if (model_nr == M7in5) { //7.5inch + for (i = 0; i < 400; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step8, model_nr, black); + transceive_blocking(step8, 123, rx, 20, actrxlen, 2157 + 2048); + progress = i * 100 / 400; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + msleep(6); + } + } else if (model_nr == M2in13B) { //2.13inch B + for (i = 0; i < 26; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step8, model_nr, black); + transceive_blocking(step8, 109, rx, 20, actrxlen, 2157 + 2048); + progress = i * 50 / 26; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } else if (model_nr == M7in5HD) { //7.5HD + + for (i = 0; i < 484; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step8, model_nr, black); + //memset(&step8[3], 0xf0, 120); + transceive_blocking(step8, 123, rx, 20, actrxlen, 2157 + 2048); + progress = i * 100 / 484; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + memset(&step8[3], 0xff, 120); + transceive_blocking(step8, 110 + 3, rx, 20, actrxlen, 2157 + 2048); + + + } else if (model_nr == M2in7) { //2.7inch + for (i = 0; i < 48; i++) { + rx[0] = 1; + rx[1] = 1; + //read_black(i,step8, model_nr, black); + memset(&step8[3], 0xFF, sizeof(step8)-3); + transceive_blocking(step8, 124, rx, 20, actrxlen, 2157 + 2048); + progress = i * 50 / 48; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } + PROMPT_CLEARLINE; + step = 9; + } else if (step == 9) { + PrintAndLogEx(INFO, "Step9: e-paper power on"); + if (model_nr == M2in13 || model_nr == M2in9 || model_nr == M4in2 || model_nr == M7in5 || model_nr == M7in5HD) { + transceive_blocking(step9, 2, rx, 20, actrxlen, 2157 + 2048); //cd 18 + // The black-and-white screen sending backplane is also shielded, with no effect. Except 2.7 + if (rx[0] != 0 || rx[1] != 0) { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } else + fail_num = 0; + + rx[0] = 1; + rx[1] = 1; + step = 10; + } else if (model_nr == M2in13B || model_nr == M2in7) { + transceive_blocking(step9, 2, rx, 20, actrxlen, 2157 + 2048); //cd 18 + //rx[0]=1;rx[1]=1; + step = 19; + } + } else if (step == 10) { + PrintAndLogEx(INFO, "Step10: Refresh e-paper"); + transceive_blocking(step10, 2, rx, 20, actrxlen, 2157 + 2048); //cd 09 refresh command + if (rx[0] != 0 || rx[1] != 0) { + fail_num++; + if (fail_num > 10) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } + } else + fail_num = 0; + rx[0] = 1; + rx[1] = 1; + step = 11; + msleep(200); + } else if (step == 11) { + PrintAndLogEx(INFO, "Step11: Wait tag to be ready"); + if (model_nr == M2in13B || model_nr == M1in54B) { // Black, white and red screen refresh time is longer, wait first + msleep(9000); + } else if (model_nr == M7in5HD) { + msleep(1000); + } + while (1) { + rx[0] = 1; + rx[1] = 1; + if (model_nr == M1in54B) { + // send 0xcd 0x08 with 1.54B + transceive_blocking(step8, 2, rx, 20, actrxlen, 2157 + 2048); + } else { + transceive_blocking(step11, 2, rx, 20, actrxlen, 2157 + 2048); //cd 0a + } + if (rx[0] == 0xff && rx[1] == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "E-paper Reflash OK"); + fail_num = 0; + step = 12; + msleep(200); + break; + } else { + if (fail_num > 50) { + PrintAndLogEx(WARNING, "Update failed, please try again."); + DropField(); + return PM3_ESOFT; + } else { + fail_num++; + PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting"); + msleep(100); + } + } + } + + } else if (step == 12) { + PrintAndLogEx(INFO, "Step12: e-paper power off command"); + transceive_blocking(step12, 2, rx, 20, actrxlen, 2157 + 2048); //cd 04 + rx[0] = 1; + rx[1] = 1; + step = 13; + msleep(200); + } else if (step == 13) { + PrintAndLogEx(SUCCESS, "E-paper Update OK"); + rx[0] = 1; + rx[1] = 1; + msleep(200); + DropField(); + return PM3_SUCCESS; + } else if (step == 19) { + PrintAndLogEx(INFO, "Step9b"); + if (model_nr == M2in7) { + for (i = 0; i < 48; i++) { + rx[0] = 1; + rx[1] = 1; + read_black(i, step13, model_nr, black); + transceive_blocking(step13, 124, rx, 20, actrxlen, 2157 + 2048); //CD 19 + progress = i * 50 / 48 + 50; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } else if (model_nr == M2in13B) { + for (i = 0; i < 26; i++) { + rx[0] = 1; + rx[1] = 1; + read_red(i, step13, model_nr, red); + //memset(&step13[3], 0xfE, 106); + transceive_blocking(step13, 109, rx, 20, actrxlen, 2157 + 2048); + progress = i * 50 / 26 + 50; + PrintAndLogEx(INPLACE, "Progress: %d %%", progress); + } + } + PROMPT_CLEARLINE; + rx[0] = 1; + rx[1] = 1; + step = 10; + } + } +} + + +static int CmdHF14AWSLoadBmp(const char *Cmd) { + + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = false; + size_t filenamelen = 0; + uint8_t model_nr = 0xff; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_hf_waveshare_loadbmp(); + case 'f': + filenamelen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + if (filenamelen > FILE_PATH_SIZE - 5) + filenamelen = FILE_PATH_SIZE - 5; + cmdp += 2; + break; + case 'm': + model_nr = param_get8(Cmd, cmdp + 1); + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter: " _RED_("'%c'"), param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (filenamelen < 1) { + PrintAndLogEx(WARNING, "Missing filename"); + errors = true; + } + if (model_nr == 0xff) { + PrintAndLogEx(WARNING, "Missing model"); + errors = true; + } else if (model_nr >= MEND) { + PrintAndLogEx(WARNING, "Unknown model"); + errors = true; + } + if (errors || cmdp == 0) return usage_hf_waveshare_loadbmp(); + + uint8_t *bmp = NULL; + uint8_t *black = NULL; + uint8_t *red = NULL; + size_t bytes_read = 0; + if (loadFile_safe(filename, ".bmp", (void **)&bmp, &bytes_read) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename); + return PM3_EIO; + } + + int depth = picture_bit_depth(bmp, bytes_read); + if (depth == PM3_ESOFT) { + PrintAndLogEx(ERR, "Error, BMP file is too small"); + free(bmp); + return PM3_ESOFT; + } else if (depth == 1) { + PrintAndLogEx(DEBUG, "BMP file is a bitmap"); + if (read_bmp_bitmap(bmp, bytes_read, &black) != PM3_SUCCESS) { + free(bmp); + return PM3_ESOFT; + } + } else if (depth == 24) { + PrintAndLogEx(DEBUG, "BMP file is a RGB"); + if (read_bmp_rgb(bmp, bytes_read, &black, &red) != PM3_SUCCESS) { + free(bmp); + return PM3_ESOFT; + } + } else if (depth == 32) { + PrintAndLogEx(ERR, "Error, BMP color depth %i not supported. Remove alpha channel.", depth); + free(bmp); + return PM3_ESOFT; + } else { + PrintAndLogEx(ERR, "Error, BMP color depth %i not supported", depth); + free(bmp); + return PM3_ESOFT; + } + free(bmp); + + start_drawing(model_nr, black, red); + free(black); + if (red != NULL) { + free(red); + } + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"loadbmp", CmdHF14AWSLoadBmp, IfPm3Iso14443a, "Load BMP file to Waveshare NFC ePaper"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFWaveshare(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhfwaveshare.h b/client/src/cmdhfwaveshare.h new file mode 100644 index 000000000..bae8f239a --- /dev/null +++ b/client/src/cmdhfwaveshare.h @@ -0,0 +1,10 @@ +//----------------------------------------------------------------------------- +// Waveshare commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFWQVESHARE_H__ +#define CMDHFWAVESHARE_H__ + +int CmdHFWaveshare(const char *Cmd); + +#endif diff --git a/client/src/ui.h b/client/src/ui.h index 7a5b7911d..f19f2024a 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -53,6 +53,7 @@ extern bool showDemod; #endif #define MAX_PRINT_BUFFER 2048 +#define PROMPT_CLEARLINE PrintAndLogEx(INPLACE, " \r") void PrintAndLogOptions(const char *str[][2], size_t size, size_t space); void PrintAndLogEx(logLevel_t level, const char *fmt, ...); void SetFlushAfterWrite(bool value);