mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 09:04:24 +01:00
nfc support working on tag, no esp32 yet
This commit is contained in:
@@ -5,7 +5,7 @@ BUILD ?= zbs29v033
|
||||
SOURCES += main.c eeprom.c drawing.c
|
||||
SOURCES += comms.c
|
||||
SOURCES += syncedproto.c userinterface.c
|
||||
SOURCES += powermgt.c barcode.c
|
||||
SOURCES += powermgt.c barcode.c i2cdevices.c
|
||||
|
||||
all: #make sure it is the first target
|
||||
|
||||
|
||||
Binary file not shown.
BIN
tag_fw/fw29.bin
BIN
tag_fw/fw29.bin
Binary file not shown.
BIN
tag_fw/fw42.bin
BIN
tag_fw/fw42.bin
Binary file not shown.
109
tag_fw/i2cdevices.c
Normal file
109
tag_fw/i2cdevices.c
Normal file
@@ -0,0 +1,109 @@
|
||||
// data / _command: 2.2
|
||||
// _select 1.7
|
||||
// busy 2.1
|
||||
// reset 2.0
|
||||
// spi.clk 0.0
|
||||
// spi.mosi 0.1
|
||||
|
||||
#include "i2cdevices.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "i2c.h"
|
||||
#include "printf.h"
|
||||
#include "timer.h"
|
||||
|
||||
extern void dump(uint8_t* __xdata a, uint16_t __xdata l); // remove me when done
|
||||
|
||||
extern uint8_t __xdata blockXferBuffer[];
|
||||
__xdata uint8_t i2cbuffer[18];
|
||||
|
||||
void loadRawNTag(uint16_t blocksize) {
|
||||
struct I2cTransaction __xdata i2ctrans;
|
||||
if (blocksize > 864) blocksize = 864;
|
||||
|
||||
uint8_t trcount = (uint8_t)(blocksize / 16);
|
||||
if (blocksize % 16) trcount++;
|
||||
|
||||
for (uint8_t c = 0; c < trcount; c++) {
|
||||
i2ctrans.numBytes = 17;
|
||||
i2ctrans.deviceAddr = (uint8_t)0x55 << 1;
|
||||
i2ctrans.bytes = i2cbuffer;
|
||||
i2cbuffer[0] = c + 1;
|
||||
memcpy(i2cbuffer + 1, blockXferBuffer + (c * 16), 16);
|
||||
uint8_t res = i2cTransact(&i2ctrans, 1);
|
||||
timerDelay(133300);
|
||||
}
|
||||
}
|
||||
|
||||
void loadURLtoNTag() {
|
||||
// https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef << very helpful
|
||||
|
||||
uint8_t __xdata i2cbuffer[18];
|
||||
__xdata uint8_t* tempbuffer = blockXferBuffer + 2048;
|
||||
|
||||
strncpy(tempbuffer + 7, blockXferBuffer, 245);
|
||||
uint8_t __xdata len = strlen(tempbuffer + 7);
|
||||
struct I2cTransaction __xdata i2ctrans;
|
||||
|
||||
// TLV
|
||||
tempbuffer[0] = 0x03; // NDEF message (TLV type)
|
||||
tempbuffer[1] = 4 + len + 1;
|
||||
|
||||
// ndef record
|
||||
tempbuffer[2] = 0xD1;
|
||||
tempbuffer[3] = 0x01; // well known record type
|
||||
tempbuffer[4] = len + 1; // payload length
|
||||
tempbuffer[5] = 0x55; // payload type (URI record)
|
||||
tempbuffer[6] = 0x00; // URI identifier code (no prepending)
|
||||
|
||||
len = 7 + len;
|
||||
|
||||
tempbuffer[len] = 0xFE;
|
||||
|
||||
uint8_t trcount = len / 16;
|
||||
if (len % 16) trcount++;
|
||||
|
||||
for (uint8_t c = 0; c < trcount; c++) {
|
||||
i2ctrans.numBytes = 17;
|
||||
i2ctrans.deviceAddr = (uint8_t)0x55 << 1;
|
||||
i2ctrans.bytes = i2cbuffer;
|
||||
i2cbuffer[0] = c + 1;
|
||||
memcpy(i2cbuffer + 1, tempbuffer + (c * 16), 16);
|
||||
uint8_t res = i2cTransact(&i2ctrans, 1);
|
||||
timerDelay(133300);
|
||||
}
|
||||
}
|
||||
|
||||
void i2cBusScan() {
|
||||
struct I2cTransaction __xdata iictest;
|
||||
iictest.numBytes = 0;
|
||||
iictest.bytes = NULL;
|
||||
pr("Starting I2C scan...\n");
|
||||
for (uint8_t address = 0x00; address <= 0x7F; address++) {
|
||||
iictest.deviceAddr = address << 1;
|
||||
uint8_t res = i2cTransact(&iictest, 1);
|
||||
if (res == 0) {
|
||||
pr(" - Found i2c device at %02X\n", address);
|
||||
}
|
||||
timerDelay(13330);
|
||||
}
|
||||
pr("I2C scan complete\n");
|
||||
}
|
||||
|
||||
bool i2cCheckDevice(uint8_t address) {
|
||||
struct I2cTransaction __xdata iictest;
|
||||
iictest.numBytes = 0;
|
||||
iictest.deviceAddr = address << 1;
|
||||
uint8_t res = i2cTransact(&iictest, 1);
|
||||
if (res == 0) {
|
||||
pr("Found i2c device at 0x%02X\n", address);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
9
tag_fw/i2cdevices.h
Normal file
9
tag_fw/i2cdevices.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef _I2CDEV_H_
|
||||
#define _I2CDEV_H_
|
||||
#include <stdint.h>
|
||||
|
||||
void loadURLtoNTag();
|
||||
void loadRawNTag(uint16_t blocksize);
|
||||
bool i2cCheckDevice(uint8_t address);
|
||||
|
||||
#endif
|
||||
@@ -8,11 +8,13 @@
|
||||
#include "asmUtil.h"
|
||||
#include "comms.h" // for mLastLqi and mLastRSSI
|
||||
#include "eeprom.h"
|
||||
#include "screen.h"
|
||||
#include "i2c.h"
|
||||
#include "i2cdevices.h"
|
||||
#include "powermgt.h"
|
||||
#include "printf.h"
|
||||
#include "proto.h"
|
||||
#include "radio.h"
|
||||
#include "screen.h"
|
||||
#include "settings.h"
|
||||
#include "syncedproto.h"
|
||||
#include "timer.h"
|
||||
@@ -21,8 +23,10 @@
|
||||
|
||||
// #define DEBUG_MODE
|
||||
|
||||
uint8_t __xdata capabilities = 0;
|
||||
|
||||
void displayLoop() {
|
||||
powerUp(INIT_BASE | INIT_UART | INIT_GPIO);
|
||||
powerUp(INIT_BASE | INIT_UART);
|
||||
|
||||
pr("Splash screen\n");
|
||||
powerUp(INIT_EPD);
|
||||
@@ -178,6 +182,15 @@ void main() {
|
||||
|
||||
pr("BOOTED> %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
|
||||
|
||||
#ifdef HAS_BUTTON
|
||||
capabilities |= CAPABILITY_HAS_WAKE_BUTTON;
|
||||
#endif
|
||||
powerUp(INIT_I2C);
|
||||
if (i2cCheckDevice(0x55)){
|
||||
capabilities |= CAPABILITY_HAS_NFC;
|
||||
}
|
||||
powerDown(INIT_I2C);
|
||||
|
||||
pr("MAC>%02X%02X", mSelfMac[0], mSelfMac[1]);
|
||||
pr("%02X%02X", mSelfMac[2], mSelfMac[3]);
|
||||
pr("%02X%02X", mSelfMac[4], mSelfMac[5]);
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
#include "cpu.h"
|
||||
#include "drawing.h"
|
||||
#include "eeprom.h"
|
||||
#include "screen.h"
|
||||
#include "i2c.h"
|
||||
#include "i2cdevices.h"
|
||||
#include "printf.h"
|
||||
#include "proto.h"
|
||||
#include "radio.h"
|
||||
#include "screen.h"
|
||||
#include "settings.h"
|
||||
#include "sleep.h"
|
||||
#include "syncedproto.h"
|
||||
@@ -41,7 +42,7 @@ uint16_t __xdata voltageCheckCounter = 0;
|
||||
bool __xdata spiActive = false;
|
||||
bool __xdata uartActive = false;
|
||||
bool __xdata eepromActive = false;
|
||||
|
||||
bool __xdata i2cActive = false;
|
||||
extern int8_t adcSampleTemperature(void); // in degrees C
|
||||
|
||||
void setupPortsInitial() {
|
||||
@@ -118,7 +119,27 @@ static void configEEPROM(const bool setup) {
|
||||
} else {
|
||||
P1DIR |= (1 << 1);
|
||||
}
|
||||
setup == eepromActive;
|
||||
setup == eepromActive; // wtf, this does nothing.
|
||||
}
|
||||
|
||||
static void configI2C(const bool setup) {
|
||||
if (setup == i2cActive) return;
|
||||
if (setup) {
|
||||
P1DIR &= ~(1 << 6);
|
||||
P1_6 = 1;
|
||||
P1FUNC |= (1 << 4) | (1 << 5);
|
||||
P1PULL |= (1 << 4) | (1 << 5);
|
||||
i2cInit();
|
||||
i2cCheckDevice(0x50); // first transaction after init fails, this makes sure everything is ready for the first transaction
|
||||
} else {
|
||||
P1DIR |= (1 << 6);
|
||||
P1_6 = 0;
|
||||
P1FUNC &= ~((1 << 4) | (1 << 5));
|
||||
P1PULL &= ~((1 << 4) | (1 << 5));
|
||||
CLKEN &= ~0x10;
|
||||
IEN1 &= ~4;
|
||||
}
|
||||
i2cActive = setup;
|
||||
}
|
||||
|
||||
void powerUp(const uint8_t parts) {
|
||||
@@ -171,13 +192,16 @@ void powerUp(const uint8_t parts) {
|
||||
radioSetChannel(RADIO_FIRST_CHANNEL);
|
||||
}
|
||||
}
|
||||
if (parts & INIT_I2C) {
|
||||
configI2C(true);
|
||||
}
|
||||
}
|
||||
|
||||
void powerDown(const uint8_t parts) {
|
||||
if (parts & INIT_UART) {
|
||||
configUART(false);
|
||||
}
|
||||
if (parts & INIT_RADIO) { // warning; this also touches some stuff about the EEPROM, apparently. Re-init EEPROM afterwards
|
||||
if (parts & INIT_RADIO) { // warning; this also touches some stuff about the EEPROM, apparently. Re-init EEPROM afterwards
|
||||
radioRxEnable(false, true);
|
||||
RADIO_IRQ4_pending = 0;
|
||||
UNK_C1 &= ~0x81;
|
||||
@@ -201,6 +225,9 @@ void powerDown(const uint8_t parts) {
|
||||
if (!eepromActive && !epdGPIOActive) {
|
||||
configSPI(false);
|
||||
}
|
||||
if (parts & INIT_I2C) {
|
||||
configI2C(false);
|
||||
}
|
||||
}
|
||||
|
||||
void doSleep(const uint32_t __xdata t) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#define INIT_EPD_VOLTREADING 0x80
|
||||
#define INIT_RADIO 0x40
|
||||
#define INIT_GPIO 0x20
|
||||
#define INIT_I2C 0x20
|
||||
#define INIT_UART 0x10
|
||||
#define INIT_EPD 0x08
|
||||
#define INIT_EEPROM 0x04
|
||||
|
||||
@@ -111,28 +111,31 @@ struct AvailDataReq {
|
||||
uint16_t batteryMv;
|
||||
uint8_t hwType;
|
||||
uint8_t wakeupReason;
|
||||
uint8_t capabilities; // undefined, as of now
|
||||
uint8_t capabilities; // undefined, as of now
|
||||
} __packed;
|
||||
|
||||
#define CAPABILITY_HAS_WAKE_BUTTON 0x20
|
||||
#define CAPABILITY_HAS_NFC 0x40
|
||||
|
||||
#define DATATYPE_NOUPDATE 0
|
||||
#define DATATYPE_IMG_BMP 2
|
||||
#define DATATYPE_FW_UPDATE 3
|
||||
#define DATATYPE_IMG_DIFF 0x10 // always 1BPP
|
||||
#define DATATYPE_IMG_RAW_1BPP 0x20 // 2888 bytes for 1.54" / 4736 2.9" / 15000 4.2"
|
||||
#define DATATYPE_IMG_RAW_2BPP 0x21 // 5776 bytes for 1.54" / 9472 2.9" / 30000 4.2"
|
||||
#define DATATYPE_IMG_RAW_1BPP_DIRECT 0x3F // only for 1.54", don't write to EEPROM, but straightaway to the EPD
|
||||
#define DATATYPE_IMG_DIFF 0x10 // always 1BPP
|
||||
#define DATATYPE_IMG_RAW_1BPP 0x20 // 2888 bytes for 1.54" / 4736 2.9" / 15000 4.2"
|
||||
#define DATATYPE_IMG_RAW_2BPP 0x21 // 5776 bytes for 1.54" / 9472 2.9" / 30000 4.2"
|
||||
#define DATATYPE_IMG_RAW_1BPP_DIRECT 0x3F // only for 1.54", don't write to EEPROM, but straightaway to the EPD
|
||||
#define DATATYPE_NFC_RAW_CONTENT 0xA0 // raw memory content for the NT3H1101
|
||||
#define DATATYPE_NFC_URL_DIRECT 0xA1 // URL format for NT3H1101
|
||||
|
||||
struct AvailDataInfo {
|
||||
uint8_t checksum;
|
||||
uint64_t dataVer; // MD5 of potential traffic
|
||||
uint32_t dataSize;
|
||||
uint8_t dataType;
|
||||
uint64_t dataVer; // MD5 of potential traffic
|
||||
uint32_t dataSize;
|
||||
uint8_t dataType;
|
||||
uint8_t dataTypeArgument; // extra specification or instruction for the tag (LUT to be used for drawing image)
|
||||
uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes
|
||||
uint16_t nextCheckIn; // when should the tag check-in again? Measured in minutes
|
||||
} __packed;
|
||||
|
||||
|
||||
struct blockPart {
|
||||
uint8_t checksum;
|
||||
uint8_t blockId;
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
#include "cpu.h"
|
||||
#include "drawing.h"
|
||||
#include "eeprom.h"
|
||||
#include "i2c.h"
|
||||
#include "i2cdevices.h"
|
||||
#include "powermgt.h"
|
||||
#include "printf.h"
|
||||
#include "proto.h"
|
||||
#include "radio.h"
|
||||
#include "screen.h"
|
||||
#include "settings.h"
|
||||
#include "sleep.h"
|
||||
#include "timer.h"
|
||||
#include "userinterface.h"
|
||||
#include "wdt.h"
|
||||
#include "screen.h"
|
||||
|
||||
// download-stuff
|
||||
uint8_t __xdata blockXferBuffer[BLOCK_XFER_BUFFER_SIZE] = {0};
|
||||
@@ -50,6 +50,9 @@ uint8_t __xdata currentChannel = 0;
|
||||
static uint8_t __xdata inBuffer[128] = {0};
|
||||
static uint8_t __xdata outBuffer[128] = {0};
|
||||
|
||||
// get capabilities from main.c
|
||||
extern uint8_t __xdata capabilities;
|
||||
|
||||
// tools
|
||||
static uint8_t __xdata getPacketType(const void *__xdata buffer) {
|
||||
const struct MacFcs *__xdata fcs = buffer;
|
||||
@@ -192,6 +195,7 @@ static void sendAvailDataReq() {
|
||||
availreq->lastPacketLQI = mLastLqi;
|
||||
availreq->temperature = temperature;
|
||||
availreq->batteryMv = batteryVoltage;
|
||||
availreq->capabilities = capabilities;
|
||||
addCRC(availreq, sizeof(struct AvailDataReq));
|
||||
commsTxNoCpy(outBuffer);
|
||||
}
|
||||
@@ -479,7 +483,7 @@ static bool getDataBlock(const uint16_t blockSize) {
|
||||
partsThisBlock = BLOCK_MAX_PARTS;
|
||||
memset(curBlock.requestedParts, 0xFF, BLOCK_REQ_PARTS_BYTES);
|
||||
} else {
|
||||
partsThisBlock = (sizeof(struct blockData)+blockSize) / BLOCK_PART_DATA_SIZE;
|
||||
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++) {
|
||||
@@ -698,7 +702,7 @@ bool processAvailDataInfo(struct AvailDataInfo *__xdata avail) {
|
||||
}
|
||||
xMemCopyShort(&curDataInfo, (void *)avail, sizeof(struct AvailDataInfo));
|
||||
if (avail->dataSize > 4096) avail->dataSize = 4096;
|
||||
|
||||
|
||||
if (getDataBlock(avail->dataSize)) {
|
||||
powerUp(INIT_RADIO);
|
||||
sendXferComplete();
|
||||
@@ -792,6 +796,50 @@ bool processAvailDataInfo(struct AvailDataInfo *__xdata avail) {
|
||||
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;
|
||||
}
|
||||
|
||||
pr("NFC URL received\n");
|
||||
if (curDataInfo.dataSize == 0 && xMemEqual((const void *__xdata) & avail->dataVer, (const void *__xdata) & curDataInfo.dataVer, 8)) {
|
||||
// we've already downloaded this NFC data, disregard and send XFC
|
||||
pr("this was the same as the last transfer, disregard\n");
|
||||
powerUp(INIT_RADIO);
|
||||
sendXferComplete();
|
||||
powerDown(INIT_RADIO);
|
||||
return true;
|
||||
}
|
||||
xMemCopyShort(&curDataInfo, (void *)avail, sizeof(struct AvailDataInfo));
|
||||
uint16_t __xdata nfcsize = avail->dataSize;
|
||||
if (getDataBlock(avail->dataSize)) {
|
||||
powerUp(INIT_RADIO);
|
||||
sendXferComplete();
|
||||
powerDown(INIT_RADIO);
|
||||
|
||||
curDataInfo.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);
|
||||
}
|
||||
powerDown(INIT_I2C);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "spi.h"
|
||||
#include "syncedproto.h" // for APmac / Channel
|
||||
#include "timer.h"
|
||||
#include "proto.h"
|
||||
|
||||
// extern uint8_t __xdata mSelfMac[8];
|
||||
// extern uint8_t __xdata currentChannel;
|
||||
@@ -29,9 +30,21 @@
|
||||
const uint8_t __code fwVersion = FW_VERSION;
|
||||
const char __code fwVersionSuffix[] = FW_VERSION_SUFFIX;
|
||||
|
||||
extern uint8_t __xdata capabilities;
|
||||
|
||||
bool __xdata lowBatteryShown = false;
|
||||
bool __xdata noAPShown = false;
|
||||
|
||||
void addCapabilities() {
|
||||
epdpr("Options: ");
|
||||
if (capabilities & CAPABILITY_HAS_NFC) {
|
||||
epdpr("-NFC ");
|
||||
}
|
||||
if (capabilities & CAPABILITY_HAS_WAKE_BUTTON) {
|
||||
epdpr("-WAKE BUTTON" );
|
||||
}
|
||||
}
|
||||
|
||||
void addOverlay() {
|
||||
if (currentChannel == 0) {
|
||||
#if (SCREEN_WIDTH == 152)
|
||||
@@ -83,6 +96,10 @@ void showSplashScreen() {
|
||||
epdpr("%02X%02X", mSelfMac[1], mSelfMac[0]);
|
||||
epdPrintEnd();
|
||||
|
||||
epdPrintBegin(2, 104, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
|
||||
addCapabilities();
|
||||
epdPrintEnd();
|
||||
|
||||
epdPrintBegin(2, 120, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
|
||||
epdpr("zbs154v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
|
||||
epdPrintEnd();
|
||||
@@ -95,6 +112,11 @@ void showSplashScreen() {
|
||||
epdpr("Starting");
|
||||
epdPrintEnd();
|
||||
|
||||
|
||||
epdPrintBegin(64, 295, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
|
||||
addCapabilities();
|
||||
epdPrintEnd();
|
||||
|
||||
epdPrintBegin(80, 295, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
|
||||
epdpr("zbs29v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
|
||||
epdPrintEnd();
|
||||
@@ -106,6 +128,7 @@ void showSplashScreen() {
|
||||
epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
|
||||
epdPrintEnd();
|
||||
|
||||
|
||||
uint8_t __xdata buffer[17];
|
||||
spr(buffer, "%02X%02X", mSelfMac[7], mSelfMac[6]);
|
||||
spr(buffer + 4, "%02X%02X", mSelfMac[5], mSelfMac[4]);
|
||||
@@ -126,6 +149,10 @@ void showSplashScreen() {
|
||||
epdpr("Starting");
|
||||
epdPrintEnd();
|
||||
|
||||
epdPrintBegin(2, 252, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
|
||||
addCapabilities();
|
||||
epdPrintEnd();
|
||||
|
||||
epdPrintBegin(3, 268, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
|
||||
epdpr("zbs42v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
|
||||
epdPrintEnd();
|
||||
@@ -421,7 +448,7 @@ void showNoEEPROM() {
|
||||
epdPrintEnd();
|
||||
#endif
|
||||
#if (SCREEN_WIDTH == 400) // 4.2"
|
||||
epdPrintBegin(50 , 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
|
||||
epdPrintBegin(50, 3, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
|
||||
epdpr("EEPROM FAILED :(");
|
||||
epdPrintEnd();
|
||||
loadRawBitmap(failed, 176, 126, EPD_COLOR_RED);
|
||||
|
||||
Reference in New Issue
Block a user