fix coverity CID 301400|301401
[RRG-proxmark3.git] / client / src / cmdhfwaveshare.c
blobb4c4183292013905bbaec8e9312f44c13b4a25f5
1 //-----------------------------------------------------------------------------
2 // Waveshare commands
3 //-----------------------------------------------------------------------------
4 // from ST25R3911B-NFC-Demo source code by Waveshare team
6 #include "cmdhfwaveshare.h"
8 #include <stdio.h>
9 #include <ctype.h>
10 #include "comms.h"
11 #include "cmdparser.h"
12 #include "ui.h"
13 #include "util.h"
14 #include "fileutils.h"
15 #include "util_posix.h" // msleep
16 #include "cliparser.h"
18 // Currently the largest pixel 880*528 only needs 58.08K bytes
19 #define WSMAPSIZE 60000
21 typedef struct {
22 uint8_t B;
23 uint8_t M;
24 uint32_t fsize;
25 uint16_t res1;
26 uint16_t res2;
27 uint32_t offset;
28 uint32_t Bit_Pixel;
29 uint32_t BMP_Width;
30 uint32_t BMP_Height;
31 uint16_t planes;
32 uint16_t bpp;
33 uint32_t ctype;
34 uint32_t dsize;
35 uint32_t hppm;
36 uint32_t vppm;
37 uint32_t colorsused;
38 uint32_t colorreq;
39 uint32_t Color_1; //Color palette
40 uint32_t Color_2;
41 } PACKED BMP_HEADER;
43 #define EPD_1IN54B 0
44 #define EPD_1IN54C 1
45 #define EPD_1IN54V2 2
46 #define EPD_1IN54BCV2 3
47 #define EPD_2IN13V2 4
48 #define EPD_2IN13BC 5
49 #define EPD_2IN13D 6
50 #define EPD_2IN9 7
51 #define EPD_2IN9BC 8
52 #define EPD_2IN9D 9
53 #define EPD_4IN2 10
54 #define EPD_4IN2BC 11
55 #define EPD_7IN5 12
56 #define EPD_7IN5BC 13
57 #define EPD_7IN5V2 14
58 #define EPD_7IN5BCV2 15
59 #define EPD_2IN7 16
60 #define EPD_7IN5HD 17
62 typedef struct model_s {
63 const char *desc;
64 uint8_t len; // The data sent in one time shall not be greater than 128-3
65 uint16_t width;
66 uint16_t height;
67 } model_t;
69 typedef enum {
70 M2in13 = 0,
71 M2in9,
72 M4in2,
73 M7in5,
74 M2in7,
75 M2in13B,
76 M1in54B,
77 M7in5HD,
78 MEND
79 } model_enum_t;
81 static model_t models[] = {
82 {"2.13 inch e-paper", 16, 122, 250}, // tested
83 {"2.9 inch e-paper", 16, 296, 128},
84 {"4.2 inch e-paper", 100, 400, 300}, // tested
85 {"7.5 inch e-paper", 120, 800, 480},
86 {"2.7 inch e-paper", 121, 176, 276}, // tested
87 {"2.13 inch e-paper B (with red)", 106, 104, 212}, // tested
88 {"1.54 inch e-paper B (with red)", 100, 200, 200}, // tested
89 {"7.5 inch e-paper HD", 120, 880, 528},
92 static int CmdHelp(const char *Cmd);
94 static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize, const uint8_t model_nr) {
95 if (bmpsize < sizeof(BMP_HEADER)) {
96 return PM3_ESOFT;
99 BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
100 PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused);
101 PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp);
102 if ((pbmpheader->BMP_Width != models[model_nr].width) || (pbmpheader->BMP_Height != models[model_nr].height)) {
103 PrintAndLogEx(WARNING, "Invalid BMP size, expected %ix%i, got %ix%i", models[model_nr].width, models[model_nr].height, pbmpheader->BMP_Width, pbmpheader->BMP_Height);
105 return pbmpheader->bpp;
108 static int read_bmp_bitmap(const uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red) {
109 BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
110 // check file is bitmap
111 if (pbmpheader->bpp != 1) {
112 return PM3_ESOFT;
114 if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42
115 PrintAndLogEx(WARNING, "The file is not a BMP!");
116 return PM3_ESOFT;
118 PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize);
119 PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset);
120 if (pbmpheader->fsize > bmpsize) {
121 PrintAndLogEx(WARNING, "The file is truncated!");
122 return PM3_ESOFT;
124 uint8_t color_flag = pbmpheader->Color_1;
125 // Get BMP file data pointer
126 uint32_t offset = pbmpheader->offset;
127 uint16_t width = pbmpheader->BMP_Width;
128 uint16_t height = pbmpheader->BMP_Height;
129 if ((width + 8) * height > WSMAPSIZE * 8) {
130 PrintAndLogEx(WARNING, "The file is too large, aborting!");
131 return PM3_ESOFT;
134 uint16_t X, Y;
135 uint16_t Image_Width_Byte = (width % 8 == 0) ? (width / 8) : (width / 8 + 1);
136 uint16_t Bmp_Width_Byte = (Image_Width_Byte % 4 == 0) ? Image_Width_Byte : ((Image_Width_Byte / 4 + 1) * 4);
138 *black = calloc(WSMAPSIZE, sizeof(uint8_t));
139 if (*black == NULL) {
140 return PM3_EMALLOC;
142 // Write data into RAM
143 for (Y = 0; Y < height; Y++) { // columns
144 for (X = 0; X < Bmp_Width_Byte; X++) { // lines
145 if ((X < Image_Width_Byte) && ((X + (height - Y - 1) * Image_Width_Byte) < WSMAPSIZE)) {
146 (*black)[X + (height - Y - 1) * Image_Width_Byte] = color_flag ? bmp[offset] : ~bmp[offset];
148 offset++;
151 if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
152 // for BW+Red screens:
153 *red = calloc(WSMAPSIZE, sizeof(uint8_t));
154 if (*red == NULL) {
155 free(*black);
156 return PM3_EMALLOC;
159 return PM3_SUCCESS;
162 static void rgb_to_gray(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, int16_t *chanGrey) {
163 for (uint16_t Y = 0; Y < height; Y++) {
164 for (uint16_t X = 0; X < width; X++) {
165 // greyscale conversion
166 float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width];
167 // Csrgb = 12.92 Clinear when Clinear <= 0.0031308
168 // Csrgb = 1.055 Clinear1/2.4 - 0.055 when Clinear > 0.0031308
169 chanGrey[X + Y * width] = Clinear;
174 // Floyd-Steinberg dithering
175 static void dither_chan_inplace(int16_t *chan, uint16_t width, uint16_t height) {
176 for (uint16_t Y = 0; Y < height; Y++) {
177 for (uint16_t X = 0; X < width; X++) {
178 int16_t oldp = chan[X + Y * width];
179 int16_t newp = oldp > 127 ? 255 : 0;
180 chan[X + Y * width] = newp;
181 int16_t err = oldp - newp;
182 float m[] = {7, 3, 5, 1};
183 if (X < width - 1) {
184 chan[X + 1 + Y * width] = chan[X + 1 + Y * width] + m[0] / 16 * err;
186 if (Y < height - 1) {
187 chan[X - 1 + (Y + 1) * width] = chan[X - 1 + (Y + 1) * width] + m[1] / 16 * err;
188 chan[X + (Y + 1) * width] = chan[X + (Y + 1) * width] + m[2] / 16 * err;
190 if ((X < width - 1) && (Y < height - 1)) {
191 chan[X + 1 + (Y + 1) * width] = chan[X + 1 + (Y + 1) * width] + m[3] / 16 * err;
197 static uint32_t color_compare(int16_t r1, int16_t g1, int16_t b1, int16_t r2, int16_t g2, int16_t b2) {
198 // Compute (square of) distance from oldR/G/B to this color
199 int16_t inR = r1 - r2;
200 int16_t inG = g1 - g2;
201 int16_t inB = b1 - b2;
202 // use RGB-to-grey weighting
203 float dist = 0.2126 * inR * inR + 0.7152 * inG * inG + 0.0722 * inB * inB;
204 return dist;
207 static void nearest_color(int16_t oldR, int16_t oldG, int16_t oldB, uint8_t *palette, uint16_t palettelen, uint8_t *newR, uint8_t *newG, uint8_t *newB) {
208 uint32_t bestdist = 0x7FFFFFFF;
209 for (uint16_t i = 0; i < palettelen; i++) {
210 uint8_t R = palette[i * 3 + 0];
211 uint8_t G = palette[i * 3 + 1];
212 uint8_t B = palette[i * 3 + 2];
213 uint32_t dist = color_compare(oldR, oldG, oldB, R, G, B);
214 if (dist < bestdist) {
215 bestdist = dist;
216 *newR = R;
217 *newG = G;
218 *newB = B;
223 static void dither_rgb_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, uint8_t *palette, uint16_t palettelen) {
224 for (uint16_t Y = 0; Y < height; Y++) {
225 for (uint16_t X = 0; X < width; X++) {
226 // scan odd lines in the opposite direction
227 uint16_t XX = X;
228 if (Y % 2) {
229 XX = width - X - 1;
231 int16_t oldR = chanR[XX + Y * width];
232 int16_t oldG = chanG[XX + Y * width];
233 int16_t oldB = chanB[XX + Y * width];
234 uint8_t newR = 0, newG = 0, newB = 0;
235 nearest_color(oldR, oldG, oldB, palette, palettelen, &newR, &newG, &newB);
236 chanR[XX + Y * width] = newR;
237 chanG[XX + Y * width] = newG;
238 chanB[XX + Y * width] = newB;
239 int16_t errR = oldR - newR;
240 int16_t errG = oldG - newG;
241 int16_t errB = oldB - newB;
242 float m[] = {7, 3, 5, 1};
243 if (Y % 2) {
244 if (XX > 0) {
245 chanR[XX - 1 + Y * width] = (chanR[XX - 1 + Y * width] + m[0] / 16 * errR);
246 chanG[XX - 1 + Y * width] = (chanG[XX - 1 + Y * width] + m[0] / 16 * errG);
247 chanB[XX - 1 + Y * width] = (chanB[XX - 1 + Y * width] + m[0] / 16 * errB);
249 if (Y < height - 1) {
250 chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[3] / 16 * errR);
251 chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[3] / 16 * errG);
252 chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[3] / 16 * errB);
253 chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR);
254 chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG);
255 chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB);
257 if ((XX < width - 1) && (Y < height - 1)) {
258 chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[1] / 16 * errR);
259 chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[1] / 16 * errG);
260 chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[1] / 16 * errB);
262 } else {
263 if (XX < width - 1) {
264 chanR[XX + 1 + Y * width] = (chanR[XX + 1 + Y * width] + m[0] / 16 * errR);
265 chanG[XX + 1 + Y * width] = (chanG[XX + 1 + Y * width] + m[0] / 16 * errG);
266 chanB[XX + 1 + Y * width] = (chanB[XX + 1 + Y * width] + m[0] / 16 * errB);
268 if (Y < height - 1) {
269 chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[1] / 16 * errR);
270 chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[1] / 16 * errG);
271 chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[1] / 16 * errB);
272 chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR);
273 chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG);
274 chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB);
276 if ((XX < width - 1) && (Y < height - 1)) {
277 chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[3] / 16 * errR);
278 chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[3] / 16 * errG);
279 chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[3] / 16 * errB);
286 static void rgb_to_gray_red_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height) {
287 for (uint16_t Y = 0; Y < height; Y++) {
288 for (uint16_t X = 0; X < width; X++) {
289 float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width];
290 if ((chanR[X + Y * width] < chanG[X + Y * width] && chanR[X + Y * width] < chanB[X + Y * width])) {
291 chanR[X + Y * width] = Clinear;
292 chanG[X + Y * width] = Clinear;
293 chanB[X + Y * width] = Clinear;
299 static void threshold_chan(int16_t *colorchan, uint16_t width, uint16_t height, uint8_t threshold, uint8_t *colormap) {
300 for (uint16_t Y = 0; Y < height; Y++) {
301 for (uint16_t X = 0; X < width; X++) {
302 colormap[X + Y * width] = colorchan[X + Y * width] < threshold;
307 static void threshold_rgb_black_red(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, uint8_t threshold_black, uint8_t threshold_red, uint8_t *blackmap, uint8_t *redmap) {
308 for (uint16_t Y = 0; Y < height; Y++) {
309 for (uint16_t X = 0; X < width; X++) {
310 if ((chanR[X + Y * width] < threshold_black) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) {
311 blackmap[X + Y * width] = 1;
312 redmap[X + Y * width] = 0;
313 } else if ((chanR[X + Y * width] > threshold_red) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) {
314 blackmap[X + Y * width] = 0;
315 redmap[X + Y * width] = 1;
316 } else {
317 blackmap[X + Y * width] = 0;
318 redmap[X + Y * width] = 0;
324 static void map8to1(uint8_t *colormap, uint16_t width, uint16_t height, uint8_t *colormap8) {
325 uint16_t width8;
326 if (width % 8 == 0) {
327 width8 = width / 8;
328 } else {
329 width8 = width / 8 + 1;
331 uint8_t data = 0;
332 uint8_t count = 0;
333 for (uint16_t Y = 0; Y < height; Y++) {
334 for (uint16_t X = 0; X < width; X++) {
335 data = data | colormap[X + Y * width];
336 count += 1;
337 if ((count >= 8) || (X == width - 1)) {
338 colormap8[X / 8 + Y * width8] = (~data) & 0xFF;
339 count = 0;
340 data = 0;
342 data = (data << 1) & 0xFF;
347 static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red, char *filename, bool save_conversions) {
348 BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp;
349 // check file is full color
350 if ((pbmpheader->bpp != 24) && (pbmpheader->bpp != 32)) {
351 return PM3_ESOFT;
354 if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42
355 PrintAndLogEx(WARNING, "The file is not a BMP!");
356 return PM3_ESOFT;
359 PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize);
360 PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset);
361 if (pbmpheader->fsize > bmpsize) {
362 PrintAndLogEx(WARNING, "The file is truncated!");
363 return PM3_ESOFT;
366 // Get BMP file data pointer
367 uint32_t offset = pbmpheader->offset;
368 uint16_t width = pbmpheader->BMP_Width;
369 uint16_t height = pbmpheader->BMP_Height;
370 if ((width + 8) * height > WSMAPSIZE * 8) {
371 PrintAndLogEx(WARNING, "The file is too large, aborting!");
372 return PM3_ESOFT;
375 int16_t *chanR = calloc(width * height, sizeof(int16_t));
376 if (chanR == NULL) {
377 return PM3_EMALLOC;
380 int16_t *chanG = calloc(width * height, sizeof(int16_t));
381 if (chanG == NULL) {
382 free(chanR);
383 return PM3_EMALLOC;
386 int16_t *chanB = calloc(width * height, sizeof(int16_t));
387 if (chanB == NULL) {
388 free(chanR);
389 free(chanG);
390 return PM3_EMALLOC;
393 // Extracting BMP chans
394 for (uint16_t Y = 0; Y < height; Y++) {
395 for (uint16_t X = 0; X < width; X++) {
396 chanB[X + (height - Y - 1) * width] = bmp[offset++];
397 chanG[X + (height - Y - 1) * width] = bmp[offset++];
398 chanR[X + (height - Y - 1) * width] = bmp[offset++];
399 if (pbmpheader->bpp == 32) // Skip Alpha chan
400 offset++;
402 // Skip line padding
403 offset += width % 4;
406 if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
407 // for BW+Red screens:
408 uint8_t *mapBlack = calloc((uint32_t)(width * height), sizeof(uint8_t));
409 if (mapBlack == NULL) {
410 free(chanR);
411 free(chanG);
412 free(chanB);
413 return PM3_EMALLOC;
415 uint8_t *mapRed = calloc((uint32_t)(width * height), sizeof(uint8_t));
416 if (mapRed == NULL) {
417 free(chanR);
418 free(chanG);
419 free(chanB);
420 free(mapBlack);
421 return PM3_EMALLOC;
423 rgb_to_gray_red_inplace(chanR, chanG, chanB, width, height);
425 uint8_t palette[] = {0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; // black, white, red
426 dither_rgb_inplace(chanR, chanG, chanB, width, height, palette, sizeof(palette) / 3);
428 threshold_rgb_black_red(chanR, chanG, chanB, width, height, 128, 128, mapBlack, mapRed);
429 if (save_conversions) {
430 // fill BMP chans
431 offset = pbmpheader->offset;
432 for (uint16_t Y = 0; Y < height; Y++) {
433 for (uint16_t X = 0; X < width; X++) {
434 bmp[offset++] = chanB[X + (height - Y - 1) * width] & 0xFF;
435 bmp[offset++] = chanG[X + (height - Y - 1) * width] & 0xFF;
436 bmp[offset++] = chanR[X + (height - Y - 1) * width] & 0xFF;
437 if (pbmpheader->bpp == 32) // Fill Alpha chan
438 bmp[offset++] = 0xFF;
440 // Skip line padding
441 offset += width % 4;
443 PrintAndLogEx(INFO, "Saving red+black dithered version...");
444 if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) {
445 PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename);
446 free(chanR);
447 free(chanG);
448 free(chanB);
449 free(mapBlack);
450 free(mapRed);
451 return PM3_EIO;
454 free(chanR);
455 free(chanG);
456 free(chanB);
457 *black = calloc(WSMAPSIZE, sizeof(uint8_t));
458 if (*black == NULL) {
459 free(mapBlack);
460 free(mapRed);
461 return PM3_EMALLOC;
463 map8to1(mapBlack, width, height, *black);
464 free(mapBlack);
465 *red = calloc(WSMAPSIZE, sizeof(uint8_t));
466 if (*red == NULL) {
467 free(mapRed);
468 free(*black);
469 return PM3_EMALLOC;
471 map8to1(mapRed, width, height, *red);
472 free(mapRed);
473 } else {
474 // for BW-only screens:
475 int16_t *chanGrey = calloc(width * height, sizeof(int16_t));
476 if (chanGrey == NULL) {
477 free(chanR);
478 free(chanG);
479 free(chanB);
480 return PM3_EMALLOC;
482 rgb_to_gray(chanR, chanG, chanB, width, height, chanGrey);
483 dither_chan_inplace(chanGrey, width, height);
485 uint8_t *mapBlack = calloc(width * height, sizeof(uint8_t));
486 if (mapBlack == NULL) {
487 free(chanR);
488 free(chanG);
489 free(chanB);
490 free(chanGrey);
491 return PM3_EMALLOC;
493 threshold_chan(chanGrey, width, height, 128, mapBlack);
495 if (save_conversions) {
496 // fill BMP chans
497 offset = pbmpheader->offset;
498 for (uint16_t Y = 0; Y < height; Y++) {
499 for (uint16_t X = 0; X < width; X++) {
500 bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
501 bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
502 bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF;
503 if (pbmpheader->bpp == 32) // Fill Alpha chan
504 bmp[offset++] = 0xFF;
506 // Skip line padding
507 offset += width % 4;
509 PrintAndLogEx(INFO, "Saving black dithered version...");
510 if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) {
511 PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename);
512 free(chanGrey);
513 free(chanR);
514 free(chanG);
515 free(chanB);
516 free(mapBlack);
517 return PM3_EIO;
520 free(chanGrey);
521 free(chanR);
522 free(chanG);
523 free(chanB);
524 *black = calloc(WSMAPSIZE, sizeof(uint8_t));
525 if (*black == NULL) {
526 free(mapBlack);
527 return PM3_EMALLOC;
529 map8to1(mapBlack, width, height, *black);
530 free(mapBlack);
532 return PM3_SUCCESS;
535 static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, uint8_t *black) {
536 for (uint8_t j = 0; j < models[model_nr].len; j++) {
537 l[3 + j] = black[i * models[model_nr].len + j];
540 static void read_red(uint32_t i, uint8_t *l, uint8_t model_nr, uint8_t *red) {
541 // spurious warning with GCC10 (-Wstringop-overflow) when j is uint8_t, even if all len are < 128
542 for (uint16_t j = 0; j < models[model_nr].len; j++) {
543 if (model_nr == M1in54B) {
544 //1.54B needs to flip the red picture data, other screens do not need to flip data
545 l[3 + j] = ~red[i * models[model_nr].len + j];
546 } else {
547 l[3 + j] = red[i * models[model_nr].len + j];
552 static int transceive_blocking(uint8_t *txBuf, uint16_t txBufLen, uint8_t *rxBuf, uint16_t rxBufLen, uint16_t *actLen, bool retransmit) {
553 uint8_t fail_num = 0;
554 if (rxBufLen < 2) {
555 return PM3_EINVARG;
558 while (1) {
559 PacketResponseNG resp;
560 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, txBufLen, 0, txBuf, txBufLen);
561 rxBuf[0] = 1;
562 if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
563 if (resp.oldarg[0] > rxBufLen) {
564 PrintAndLogEx(WARNING, "Received %"PRIu64 " bytes, rxBuf too small (%u)", resp.oldarg[0], rxBufLen);
565 memcpy(rxBuf, resp.data.asBytes, rxBufLen);
566 *actLen = rxBufLen;
567 return PM3_ESOFT;
569 memcpy(rxBuf, resp.data.asBytes, resp.oldarg[0]);
570 *actLen = resp.oldarg[0];
573 if ((retransmit) && (rxBuf[0] != 0 || rxBuf[1] != 0)) {
574 fail_num++;
575 if (fail_num > 10) {
576 PROMPT_CLEARLINE;
577 PrintAndLogEx(WARNING, "Transmission failed, please try again.");
578 DropField();
579 return PM3_ESOFT;
581 } else {
582 break;
585 return PM3_SUCCESS;
588 // 1.54B Keychain
589 // 1.54B does not share the common base and requires specific handling
590 static int start_drawing_1in54B(uint8_t model_nr, uint8_t *black, uint8_t *red) {
591 int ret;
592 uint8_t step_5[128] = {0xcd, 0x05, 100};
593 uint8_t step_4[2] = {0xcd, 0x04};
594 uint8_t step_6[2] = {0xcd, 0x06};
595 uint8_t rx[20] = {0};
596 uint16_t actrxlen[20], i = 0, progress = 0;
598 if (model_nr == M1in54B) {
599 step_5[2] = 100;
601 PrintAndLogEx(DEBUG, "1.54_Step9: e-paper config2 (black)");
602 if (model_nr == M1in54B) { //1.54inch B Keychain
603 for (i = 0; i < 50; i++) {
604 read_black(i, step_5, model_nr, black);
605 ret = transceive_blocking(step_5, 103, rx, 20, actrxlen, true); // cd 05
606 if (ret != PM3_SUCCESS) {
607 return ret;
609 progress = i * 100 / 100;
610 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
613 PROMPT_CLEARLINE;
614 PrintAndLogEx(DEBUG, "1.54_Step6: e-paper power on");
615 ret = transceive_blocking(step_4, 2, rx, 20, actrxlen, true); //cd 04
616 if (ret != PM3_SUCCESS) {
617 return ret;
619 PrintAndLogEx(DEBUG, "1.54_Step7: e-paper config2 (red)");
620 if (model_nr == M1in54B) { //1.54inch B Keychain
621 for (i = 0; i < 50; i++) {
622 read_red(i, step_5, model_nr, red);
623 ret = transceive_blocking(step_5, 103, rx, 20, actrxlen, true); // cd 05
624 if (ret != PM3_SUCCESS) {
625 return ret;
627 progress = i * 100 / 100 + 50;
628 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
631 PROMPT_CLEARLINE;
632 // Send update instructions
633 PrintAndLogEx(DEBUG, "1.54_Step8: EDP load to main");
634 ret = transceive_blocking(step_6, 2, rx, 20, actrxlen, true); //cd 06
635 if (ret != PM3_SUCCESS) {
636 return ret;
638 PrintAndLogEx(DEBUG, "1.54_Step9");
639 return PM3_SUCCESS;
642 static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) {
643 uint8_t progress = 0;
644 uint8_t step0[2] = {0xcd, 0x0d};
645 uint8_t step1[3] = {0xcd, 0x00, 10}; // select e-paper type and reset e-paper
646 // 4 :2.13inch e-Paper
647 // 7 :2.9inch e-Paper
648 // 10 :4.2inch e-Paper
649 // 14 :7.5inch e-Paper
650 uint8_t step2[2] = {0xcd, 0x01}; // e-paper normal mode type:
651 uint8_t step3[2] = {0xcd, 0x02}; // e-paper config1
652 uint8_t step4[2] = {0xcd, 0x03}; // e-paper power on
653 uint8_t step5[2] = {0xcd, 0x05}; // e-paper config2
654 uint8_t step6[2] = {0xcd, 0x06}; // EDP load to main
655 uint8_t step7[2] = {0xcd, 0x07}; // Data preparation
657 uint8_t step8[123] = {0xcd, 0x08, 0x64}; // Data start command
658 // 2.13inch(0x10:Send 16 data at a time)
659 // 2.9inch(0x10:Send 16 data at a time)
660 // 4.2inch(0x64:Send 100 data at a time)
661 // 7.5inch(0x78:Send 120 data at a time)
662 uint8_t step9[2] = {0xcd, 0x18}; // e-paper power on
663 uint8_t step10[2] = {0xcd, 0x09}; // Refresh e-paper
664 uint8_t step11[2] = {0xcd, 0x0a}; // wait for ready
665 uint8_t step12[2] = {0xcd, 0x04}; // e-paper power off command
666 uint8_t step13[124] = {0xcd, 0x19, 121};
667 // uint8_t step13[2]={0xcd,0x0b}; // Judge whether the power supply is turned off successfully
668 // uint8_t step14[2]={0xcd,0x0c}; // The end of the transmission
669 uint8_t rx[20];
670 uint16_t actrxlen[20], i = 0;
674 clearCommandBuffer();
675 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
676 PacketResponseNG resp;
677 if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
678 PrintAndLogEx(ERR, "No tag found");
679 DropField();
680 return PM3_ETIMEOUT;
683 iso14a_card_select_t card;
684 memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
686 uint64_t select_status = resp.oldarg[0];
688 if (select_status == 0) {
689 PrintAndLogEx(ERR, "Tag select error");
690 DropField();
691 return PM3_ERFTRANS;
692 } else if (select_status == 3) {
693 PrintAndLogEx(WARNING, "Card doesn't support standard iso14443-3 anticollision, doesn't look like Waveshare tag");
694 DropField();
695 return PM3_ESOFT;
698 if ((card.uidlen != 7) || ((memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "WSDZ10m", 7) != 0))) {
699 PrintAndLogEx(WARNING, "Card doesn't look like Waveshare tag");
700 DropField();
701 return PM3_ESOFT;
703 if (((model_nr != M1in54B) && (memcmp(card.uid, "FSTN10m", 7) == 0))) {
704 PrintAndLogEx(WARNING, "Card is a Waveshare tag 1.54\", not %s", models[model_nr].desc);
705 DropField();
706 return PM3_ESOFT;
708 if (((model_nr == M1in54B) && (memcmp(card.uid, "FSTN10m", 7) != 0))) {
709 PrintAndLogEx(WARNING, "Card is not a Waveshare tag 1.54\", check your model number");
710 DropField();
711 return PM3_ESOFT;
713 PrintAndLogEx(DEBUG, "model_nr = %d", model_nr);
714 int ret;
715 PrintAndLogEx(DEBUG, "Step0");
716 ret = transceive_blocking(step0, 2, rx, 20, actrxlen, true); //cd 0d
717 if (ret != PM3_SUCCESS) {
718 return ret;
720 PrintAndLogEx(DEBUG, "Step1: e-paper config");
721 //step1[2] screen model
722 //step8[2] nr of bytes sent at once
723 //step13[2] nr of bytes sent for the second time
724 // generally, step8 sends a black image, step13 sends a red image
725 if (model_nr == M2in13) { //2.13inch
726 step1[2] = EPD_2IN13V2;
727 step8[2] = 16;
728 step13[2] = 0;
729 } else if (model_nr == M2in9) { //2.9inch
730 step1[2] = EPD_2IN9;
731 step8[2] = 16;
732 step13[2] = 0;
733 } else if (model_nr == M4in2) { //4.2inch
734 step1[2] = EPD_4IN2;
735 step8[2] = 100;
736 step13[2] = 0;
737 } else if (model_nr == M7in5) { //7.5inch
738 step1[2] = EPD_7IN5V2;
739 step8[2] = 120;
740 step13[2] = 0;
741 } else if (model_nr == M2in7) { //2.7inch
742 step1[2] = EPD_2IN7;
743 step8[2] = 121;
744 // Send blank data for the first time, and send other data to 0xff without processing the bottom layer
745 step13[2] = 121;
746 //Sending the second data is the real image data. If the previous 0xff is not sent, the last output image is abnormally black
747 } else if (model_nr == M2in13B) { //2.13inch B
748 step1[2] = EPD_2IN13BC;
749 step8[2] = 106;
750 step13[2] = 106;
751 } else if (model_nr == M7in5HD) {
752 step1[2] = EPD_7IN5HD;
753 step8[2] = 120;
754 step13[2] = 0;
757 if (model_nr == M1in54B) {
758 ret = transceive_blocking(step1, 2, rx, 20, actrxlen, true); //cd 00
759 } else {
760 ret = transceive_blocking(step1, 3, rx, 20, actrxlen, true);
762 if (ret != PM3_SUCCESS) {
763 return ret;
765 msleep(100);
766 PrintAndLogEx(DEBUG, "Step2: e-paper normal mode type");
767 ret = transceive_blocking(step2, 2, rx, 20, actrxlen, true); //cd 01
768 if (ret != PM3_SUCCESS) {
769 return ret;
771 msleep(100);
772 PrintAndLogEx(DEBUG, "Step3: e-paper config1");
773 ret = transceive_blocking(step3, 2, rx, 20, actrxlen, true); //cd 02
774 if (ret != PM3_SUCCESS) {
775 return ret;
777 msleep(200);
778 PrintAndLogEx(DEBUG, "Step4: e-paper power on");
779 ret = transceive_blocking(step4, 2, rx, 20, actrxlen, true); //cd 03
780 if (ret != PM3_SUCCESS) {
781 return ret;
783 if (model_nr == M1in54B) {
784 // 1.54B Keychain handler
785 PrintAndLogEx(DEBUG, "Start_Drawing_1in54B");
786 ret = start_drawing_1in54B(model_nr, black, red);
787 if (ret != PM3_SUCCESS) {
788 return ret;
790 //1.54B Data transfer is complete and wait for refresh
791 } else {
792 PrintAndLogEx(DEBUG, "Step5: e-paper config2");
793 ret = transceive_blocking(step5, 2, rx, 20, actrxlen, true); //cd 05
794 if (ret != PM3_SUCCESS) {
795 return ret;
797 msleep(100);
798 PrintAndLogEx(DEBUG, "Step6: EDP load to main") ;
799 ret = transceive_blocking(step6, 2, rx, 20, actrxlen, true); //cd 06
800 if (ret != PM3_SUCCESS) {
801 return ret;
803 msleep(100);
804 PrintAndLogEx(DEBUG, "Step7: Data preparation");
805 ret = transceive_blocking(step7, 2, rx, 20, actrxlen, true); //cd 07
806 if (ret != PM3_SUCCESS) {
807 return ret;
809 PrintAndLogEx(DEBUG, "Step8: Start data transfer");
810 if (model_nr == M2in13) { //2.13inch
811 for (i = 0; i < 250; i++) {
812 read_black(i, step8, model_nr, black);
813 ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08
814 if (ret != PM3_SUCCESS) {
815 return ret;
817 progress = i * 100 / 250;
818 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
820 } else if (model_nr == M2in9) {
821 for (i = 0; i < 296; i++) {
822 read_black(i, step8, model_nr, black);
823 ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08
824 if (ret != PM3_SUCCESS) {
825 return ret;
827 progress = i * 100 / 296;
828 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
830 } else if (model_nr == M4in2) { //4.2inch
831 for (i = 0; i < 150; i++) {
832 read_black(i, step8, model_nr, black);
833 ret = transceive_blocking(step8, 103, rx, 20, actrxlen, true); // cd 08
834 if (ret != PM3_SUCCESS) {
835 return ret;
837 progress = i * 100 / 150;
838 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
840 } else if (model_nr == M7in5) { //7.5inch
841 for (i = 0; i < 400; i++) {
842 read_black(i, step8, model_nr, black);
843 ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08
844 if (ret != PM3_SUCCESS) {
845 return ret;
847 progress = i * 100 / 400;
848 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
849 msleep(6);
851 } else if (model_nr == M2in13B) { //2.13inch B
852 for (i = 0; i < 26; i++) {
853 read_black(i, step8, model_nr, black);
854 ret = transceive_blocking(step8, 109, rx, 20, actrxlen, false); // cd 08
855 if (ret != PM3_SUCCESS) {
856 return ret;
858 progress = i * 50 / 26;
859 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
861 } else if (model_nr == M7in5HD) { //7.5HD
863 for (i = 0; i < 484; i++) {
864 read_black(i, step8, model_nr, black);
865 //memset(&step8[3], 0xf0, 120);
866 ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08
867 if (ret != PM3_SUCCESS) {
868 return ret;
870 progress = i * 100 / 484;
871 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
873 memset(&step8[3], 0xff, 120);
874 ret = transceive_blocking(step8, 110 + 3, rx, 20, actrxlen, true); // cd 08
875 if (ret != PM3_SUCCESS) {
876 return ret;
878 } else if (model_nr == M2in7) { //2.7inch
879 for (i = 0; i < 48; i++) {
880 //read_black(i,step8, model_nr, black);
881 memset(&step8[3], 0xFF, sizeof(step8) - 3);
882 ret = transceive_blocking(step8, 124, rx, 20, actrxlen, true); // cd 08
883 if (ret != PM3_SUCCESS) {
884 return ret;
886 progress = i * 50 / 48;
887 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
890 PROMPT_CLEARLINE;
891 PrintAndLogEx(DEBUG, "Step9: e-paper power on");
892 if (model_nr == M2in13 || model_nr == M2in9 || model_nr == M4in2 || model_nr == M7in5 || model_nr == M7in5HD) {
893 ret = transceive_blocking(step9, 2, rx, 20, actrxlen, true); //cd 18
894 // The black-and-white screen sending backplane is also shielded, with no effect. Except 2.7
895 if (ret != PM3_SUCCESS) {
896 return ret;
898 } else if (model_nr == M2in13B || model_nr == M2in7) {
899 ret = transceive_blocking(step9, 2, rx, 20, actrxlen, true); //cd 18
900 if (ret != PM3_SUCCESS) {
901 return ret;
903 PrintAndLogEx(DEBUG, "Step9b");
904 if (model_nr == M2in7) {
905 for (i = 0; i < 48; i++) {
906 read_black(i, step13, model_nr, black);
907 ret = transceive_blocking(step13, 124, rx, 20, actrxlen, true); //CD 19
908 if (ret != PM3_SUCCESS) {
909 return ret;
911 progress = i * 50 / 48 + 50;
912 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
914 } else if (model_nr == M2in13B) {
915 for (i = 0; i < 26; i++) {
916 read_red(i, step13, model_nr, red);
917 //memset(&step13[3], 0xfE, 106);
918 ret = transceive_blocking(step13, 109, rx, 20, actrxlen, false);
919 if (ret != PM3_SUCCESS) {
920 return ret;
922 progress = i * 50 / 26 + 50;
923 PrintAndLogEx(INPLACE, "Progress: %d %%", progress);
926 PROMPT_CLEARLINE;
928 PrintAndLogEx(DEBUG, "Step10: Refresh e-paper");
929 ret = transceive_blocking(step10, 2, rx, 20, actrxlen, true); //cd 09 refresh command
930 if (ret != PM3_SUCCESS) {
931 return ret;
933 msleep(200);
935 PrintAndLogEx(DEBUG, "Step11: Wait tag to be ready");
936 PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting");
937 if (model_nr == M2in13B || model_nr == M1in54B) { // Black, white and red screen refresh time is longer, wait first
938 msleep(9000);
939 } else if (model_nr == M7in5HD) {
940 msleep(1000);
942 uint8_t fail_num = 0;
943 while (1) {
944 if (model_nr == M1in54B) {
945 // send 0xcd 0x08 with 1.54B
946 ret = transceive_blocking(step8, 2, rx, 20, actrxlen, false); //cd 08
947 } else {
948 ret = transceive_blocking(step11, 2, rx, 20, actrxlen, false); //cd 0a
950 if (ret != PM3_SUCCESS) {
951 return ret;
953 if (rx[0] == 0xff && rx[1] == 0) {
954 PrintAndLogEx(NORMAL, "");
955 PrintAndLogEx(SUCCESS, "E-paper Reflash OK");
956 msleep(200);
957 break;
958 } else {
959 if (fail_num > 50) {
960 PrintAndLogEx(WARNING, "Update failed, please try again.");
961 DropField();
962 return PM3_ESOFT;
963 } else {
964 fail_num++;
965 PrintAndLogEx(INPLACE, "E-paper Reflashing, Waiting");
966 msleep(400);
970 PrintAndLogEx(DEBUG, "Step12: e-paper power off command");
971 ret = transceive_blocking(step12, 2, rx, 20, actrxlen, true); //cd 04
972 if (ret != PM3_SUCCESS) {
973 return ret;
975 msleep(200);
976 PrintAndLogEx(SUCCESS, "E-paper Update OK");
977 msleep(200);
978 DropField();
979 return PM3_SUCCESS;
982 static int CmdHF14AWSLoadBmp(const char *Cmd) {
984 char desc[800] = {0};
985 for (uint8_t i = 0; i < MEND; i++) {
986 snprintf(desc + strlen(desc),
987 sizeof(desc) - strlen(desc),
988 "hf waveshare loadbmp -f myfile -m %2u -> %s ( %u, %u )\n",
990 models[i].desc,
991 models[i].width,
992 models[i].height
996 CLIParserContext *ctx;
997 CLIParserInit(&ctx, "hf waveshare loadbmp",
998 "Load BMP file to Waveshare NFC ePaper.",
999 desc
1002 char modeldesc[40];
1003 snprintf(modeldesc, sizeof(modeldesc), "model number [0 - %d] of your tag", MEND - 1);
1005 void *argtable[] = {
1006 arg_param_begin,
1007 arg_int1("m", NULL, "<nr>", modeldesc),
1008 arg_lit0("s", "save", "save dithered version in filename-[n].bmp, only for RGB BMP"),
1009 arg_str1("f", "file", "<filename>", "filename[.bmp] to upload to tag"),
1010 arg_param_end
1013 CLIExecWithReturn(ctx, Cmd, argtable, false);
1015 int model_nr = arg_get_int_def(ctx, 1, -1);
1016 bool save_conversions = arg_get_lit(ctx, 2);
1018 int fnlen = 0;
1019 char filename[FILE_PATH_SIZE] = {0};
1020 CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
1021 CLIParserFree(ctx);
1023 //Validations
1024 if (fnlen < 1) {
1025 PrintAndLogEx(WARNING, "Missing filename");
1026 return PM3_EINVARG;
1028 if (model_nr == -1) {
1029 PrintAndLogEx(WARNING, "Missing model");
1030 return PM3_EINVARG;
1032 if (model_nr >= MEND) {
1033 PrintAndLogEx(WARNING, "Unknown model");
1034 return PM3_EINVARG;
1037 uint8_t *bmp = NULL;
1038 uint8_t *black = NULL;
1039 uint8_t *red = NULL;
1040 size_t bytes_read = 0;
1041 if (loadFile_safe(filename, ".bmp", (void **)&bmp, &bytes_read) != PM3_SUCCESS) {
1042 PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename);
1043 return PM3_EIO;
1046 int depth = picture_bit_depth(bmp, bytes_read, model_nr);
1047 if (depth == PM3_ESOFT) {
1048 PrintAndLogEx(ERR, "Error, BMP file is too small");
1049 free(bmp);
1050 return PM3_ESOFT;
1051 } else if (depth == 1) {
1052 PrintAndLogEx(DEBUG, "BMP file is a bitmap");
1053 if (read_bmp_bitmap(bmp, bytes_read, model_nr, &black, &red) != PM3_SUCCESS) {
1054 free(bmp);
1055 return PM3_ESOFT;
1057 } else if (depth == 24) {
1058 PrintAndLogEx(DEBUG, "BMP file is a RGB");
1059 if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) {
1060 free(bmp);
1061 return PM3_ESOFT;
1063 } else if (depth == 32) {
1064 PrintAndLogEx(DEBUG, "BMP file is a RGBA, we will ignore the Alpha channel");
1065 if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) {
1066 free(bmp);
1067 return PM3_ESOFT;
1069 } else {
1070 PrintAndLogEx(ERR, "Error, BMP color depth %i not supported. Must be 1 (BW), 24 (RGB) or 32 (RGBA)", depth);
1071 free(bmp);
1072 return PM3_ESOFT;
1074 free(bmp);
1076 start_drawing(model_nr, black, red);
1077 free(black);
1078 if ((model_nr == M1in54B) || (model_nr == M2in13B)) {
1079 free(red);
1081 return PM3_SUCCESS;
1084 static command_t CommandTable[] = {
1085 {"help", CmdHelp, AlwaysAvailable, "This help"},
1086 {"loadbmp", CmdHF14AWSLoadBmp, IfPm3Iso14443a, "Load BMP file to Waveshare NFC ePaper"},
1087 {NULL, NULL, NULL, NULL}
1090 static int CmdHelp(const char *Cmd) {
1091 (void)Cmd; // Cmd is not used so far
1092 CmdsHelp(CommandTable);
1093 return PM3_SUCCESS;
1096 int CmdHFWaveshare(const char *Cmd) {
1097 clearCommandBuffer();
1098 return CmdsParse(CommandTable, Cmd);