diff --git a/ESP32_AP-Flasher/data/tagtypes/21.json b/ESP32_AP-Flasher/data/tagtypes/21.json
new file mode 100644
index 00000000..e380a7f4
--- /dev/null
+++ b/ESP32_AP-Flasher/data/tagtypes/21.json
@@ -0,0 +1,74 @@
+{
+ "name": "ST‐GM29XXF 2.9\"",
+ "width": 296,
+ "height": 128,
+ "rotatebuffer": 1,
+ "bpp": 1,
+ "colors": 2,
+ "colortable": {
+ "white": [255, 255, 255],
+ "black": [0, 0, 0]
+ },
+ "shortlut": 0,
+ "options": ["button", "customlut"],
+ "contentids": [ 0, 1, 2, 3, 4, 8, 16, 9, 7, 19, 10, 11, 21 ],
+ "usetemplate": 1,
+ "template": {
+ "1": {
+ "weekday": [148, 10, "fonts/calibrib60"],
+ "date": [148, 73, "fonts/calibrib50"]
+ },
+ "16": {
+ "location": [ 5, 5, "fonts/bahnschrift30" ],
+ "title": [ 247, 11, "glasstown_nbp_tf" ],
+ "cols": [ 1, 125, 12, "glasstown_nbp_tf" ],
+ "bars": [ 5, 111, 10 ]
+ },
+ "2": {
+ "fonts": ["fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100"],
+ "xy": [148, 74]
+ },
+ "4": {
+ "location": [5, 5, "fonts/bahnschrift30"],
+ "wind": [280, 5, "fonts/bahnschrift30"],
+ "temp": [5, 65, "fonts/bahnschrift70"],
+ "icon": [285, 20, 70, 2],
+ "dir": [235, -12, 40],
+ "umbrella": [190, -50, 25]
+ },
+ "8": {
+ "location": [5, 12, "t0_14b_tf"],
+ "column": [5, 59],
+ "day": [30, 18, "fonts/twcondensed20", 41, 108],
+ "icon": [30, 55, 30],
+ "wind": [18, 26],
+ "line": [20, 128]
+ },
+ "9": {
+ "title": [5, 3, "fonts/bahnschrift20"],
+ "items": 8,
+ "line": [5, 34, 13],
+ "font": "glasstown_nbp_tf"
+ },
+ "10": {
+ "title": [10, 5, "fonts/bahnschrift20"],
+ "pos": [149, 25]
+ },
+ "11": {
+ "title": [5, 2, "fonts/bahnschrift20"],
+ "date": [290, 2],
+ "items": 7,
+ "red": [0, 21, 296, 14],
+ "line": [5, 32, 15, "t0_14b_tf", 50]
+ },
+ "21": [
+ { "text": [ 5, 5, "OpenEpaperLink AP", "bahnschrift20", 1, 0, 0 ] },
+ { "text": [ 5, 50, "IP address:", "t0_14b_tf", 1, 0, 0 ] },
+ { "text": [ 120, 50, "{ap_ip}", "t0_14b_tf", 1, 0, 0 ] },
+ { "text": [ 5, 70, "Channel:", "t0_14b_tf", 1, 0, 0 ] },
+ { "text": [ 120, 70, "{ap_ch}", "t0_14b_tf", 1, 0, 0 ] },
+ { "text": [ 5, 90, "Tag count:", "t0_14b_tf", 1, 0, 0 ] },
+ { "text": [ 120, 90, "{ap_tagcount}", "t0_14b_tf", 1, 0, 0 ] }
+ ]
+ }
+}
diff --git a/ESP32_AP-Flasher/data/www/content_cards.json.gz b/ESP32_AP-Flasher/data/www/content_cards.json.gz
deleted file mode 100644
index 51d1555f..00000000
Binary files a/ESP32_AP-Flasher/data/www/content_cards.json.gz and /dev/null differ
diff --git a/ESP32_AP-Flasher/data/www/index.html.gz b/ESP32_AP-Flasher/data/www/index.html.gz
index feffb6d4..e76fb1fa 100644
Binary files a/ESP32_AP-Flasher/data/www/index.html.gz and b/ESP32_AP-Flasher/data/www/index.html.gz differ
diff --git a/ESP32_AP-Flasher/platformio.ini b/ESP32_AP-Flasher/platformio.ini
index 7e4a9ae3..e0759d1c 100644
--- a/ESP32_AP-Flasher/platformio.ini
+++ b/ESP32_AP-Flasher/platformio.ini
@@ -37,8 +37,8 @@ build_flags =
-D ILI9341_DRIVER
-D SMOOTH_FONT
-upload_port = COM11
-monitor_port = COM11
+;upload_port = COM11
+;monitor_port = COM11
; ----------------------------------------------------------------------------------------
; !!! this configuration expects the Mini_AP
;
@@ -325,6 +325,7 @@ build_src_filter =
[env:ESP32_S3_16_8_YELLOW_AP]
board = esp32-s3-devkitc-1
board_build.partitions = large_spiffs_16MB.csv
+
build_unflags =
-std=gnu++11
-D ARDUINO_USB_MODE=1
diff --git a/ESP32_AP-Flasher/src/language.cpp b/ESP32_AP-Flasher/src/language.cpp
index 7d5b9923..61eabf54 100644
--- a/ESP32_AP-Flasher/src/language.cpp
+++ b/ESP32_AP-Flasher/src/language.cpp
@@ -7,7 +7,7 @@
int defaultLanguage = 0;
-String languageList[] = {"EN - English", "NL - Nederlands", "DE - Deutsch", "NO - Norwegian"};
+String languageList[] = {"EN - English", "NL - Nederlands", "DE - Deutsch", "NO - Norwegian", "FR - French"};
/*EN English language section*/
String languageEnDaysShort[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
@@ -33,9 +33,15 @@ String languageNoDays[] = {"Søndag", "Mandag", "Tirsdag", "Onsdag", "Torsdag",
String languageNoMonth[] = {"Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"};
/*END Norwegian language section END*/
-String* languageDaysShort[] = {languageEnDaysShort, languageNlDaysShort, languageDeDaysShort, languageNoDaysShort};
-String* languageDays[] = {languageEnDays, languageNlDays, languageDeDays, languageNoDays};
-String* languageMonth[] = {languageEnMonth, languageNlMonth, languageDeMonth, languageNoMonth};
+/*FR French language section*/
+String languageFrDaysShort[] = {"DI", "LU", "MA", "ME", "JE", "VE", "SA"};
+String languageFrDays[] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};
+String languageFrMonth[] = {"Janvier", "Fevrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Aout", "Septembre", "Octobre", "Novembre", "Decembre"};
+/*END French language section END*/
+
+String* languageDaysShort[] = {languageEnDaysShort, languageNlDaysShort, languageDeDaysShort, languageNoDaysShort, languageFrDaysShort};
+String* languageDays[] = {languageEnDays, languageNlDays, languageDeDays, languageNoDays, languageFrDays};
+String* languageMonth[] = {languageEnMonth, languageNlMonth, languageDeMonth, languageNoMonth, languageFrMonth};
int currentLanguage = defaultLanguage;
diff --git a/ESP32_AP-Flasher/wwwroot/content_cards.json b/ESP32_AP-Flasher/wwwroot/content_cards.json
index db2df8ef..63c889ba 100644
--- a/ESP32_AP-Flasher/wwwroot/content_cards.json
+++ b/ESP32_AP-Flasher/wwwroot/content_cards.json
@@ -10,7 +10,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -90,7 +91,8 @@
17,
49,
51,
- 240
+ 240,
+ 15
],
"param": []
},
@@ -106,7 +108,8 @@
17,
49,
51,
- 240
+ 240,
+ 21
],
"param": [
{
@@ -126,7 +129,8 @@
2,
49,
51,
- 17
+ 17,
+ 15
]
}
]
@@ -143,7 +147,8 @@
17,
49,
51,
- 240
+ 240,
+ 15
],
"param": [
{
@@ -164,7 +169,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
]
}
]
@@ -181,7 +187,8 @@
17,
49,
51,
- 240
+ 240,
+ 15
],
"param": [
{
@@ -224,7 +231,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -265,7 +273,8 @@
1,
49,
51,
- 17
+ 17,
+ 21
],
"param": [
{
@@ -298,7 +307,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -332,7 +342,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -360,7 +371,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -394,7 +406,8 @@
5,
17,
49,
- 51
+ 51,
+ 15
],
"param": [
{
@@ -421,7 +434,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -456,7 +470,8 @@
17,
49,
51,
- 240
+ 240,
+ 15
],
"param": [
{
@@ -548,7 +563,8 @@
17,
49,
51,
- 240
+ 240,
+ 15
],
"param": [
{
@@ -571,7 +587,8 @@
17,
49,
51,
- 240
+ 240,
+ 15
],
"param": [
{
@@ -675,7 +692,8 @@
5,
49,
51,
- 17
+ 17,
+ 15
],
"param": [
{
@@ -692,7 +710,8 @@
"desc": "Displays information about the currently connected access point",
"hwtype": [
0,
- 1
+ 1,
+ 15
]
}
]
\ No newline at end of file
diff --git a/ESP32_AP-Flasher/wwwroot/index.html b/ESP32_AP-Flasher/wwwroot/index.html
index 3d2fa76a..88ea06d1 100644
--- a/ESP32_AP-Flasher/wwwroot/index.html
+++ b/ESP32_AP-Flasher/wwwroot/index.html
@@ -287,6 +287,7 @@
+
diff --git a/oepl-definitions.h b/oepl-definitions.h
index f7dcc705..e0547bf5 100755
--- a/oepl-definitions.h
+++ b/oepl-definitions.h
@@ -1,5 +1,6 @@
#define SOLUM_154_SSD1619 0
#define SOLUM_29_SSD1619 0x01
+#define SOLUM_29_BW_SSD1619 0x21
#define SOLUM_29_UC8151 0x11
#define SOLUM_42_SSD1619 0x02
#define SOLUM_SEG_UK 0xF0
diff --git a/zbs243_Tag_FW/powermgt.c b/zbs243_Tag_FW/powermgt.c
index 77c10a16..574ffa38 100755
--- a/zbs243_Tag_FW/powermgt.c
+++ b/zbs243_Tag_FW/powermgt.c
@@ -133,8 +133,10 @@ static void configUART(const bool setup) {
static void configEEPROM(const bool setup) {
if (setup == eepromActive) return;
if (setup) {
- P1FUNC &= ~(1 << 1);
- P1DIR &= ~(1 << 1);
+ P1FUNC &= ~(1 << 1) | (1 << 2) | (1 << 6);
+ P1DIR &= ~(1 << 1) | (1 << 2) | (1 << 6);
+ P1_6 = 1;
+ P1_2 = 1;
if (!eepromInit()) {
powerDown(INIT_RADIO);
powerUp(INIT_EPD);
diff --git a/zbs243_Tag_FW/syncedproto.c b/zbs243_Tag_FW/syncedproto.c
index 938c9c4c..7b34a29d 100755
--- a/zbs243_Tag_FW/syncedproto.c
+++ b/zbs243_Tag_FW/syncedproto.c
@@ -633,8 +633,8 @@ static bool getDataBlock(const uint16_t blockSize) {
#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;
+ for (uint8_t c1 = 0; c1 < partsThisBlock; c1++) {
+ if (curBlock.requestedParts[c1 / 8] & (1 << (c1 % 8))) blockComplete = false;
}
if (blockComplete) {
diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/board.c b/zbs243_shared/board/zbs29_BW_ssd1619/board.c
new file mode 100644
index 00000000..d2c5ce19
--- /dev/null
+++ b/zbs243_shared/board/zbs29_BW_ssd1619/board.c
@@ -0,0 +1,163 @@
+#include
+#include "printf.h"
+#include "screen.h"
+#include "board.h"
+#include "flash.h"
+#include "uart.h"
+#include "spi.h"
+#include "cpu.h"
+#include "wdt.h"
+#include "i2c.h"
+
+//extern uint8_t __xdata* tempBuffer;
+uint8_t __xdata tempBuffer[320];
+
+void powerPortsDownForSleep(void)
+{
+ P0FUNC = 0;
+ P1FUNC = 0;
+ P2FUNC = 0;
+ P0DIR = 0;
+ P0 = 0;
+ P0PULL = 0;
+ P1DIR = 0;
+ P1 = 2;
+ P1PULL = 0;
+ P2DIR = 2;
+ P2 =1;
+ P2PULL = 0;
+}
+
+void boardInit(void)
+{
+ pr("board init\n");
+ //set up pins for spi(0.0,0.1,0.2), UART (0.6)
+ P0FUNC |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 6);
+ P0DIR = (P0DIR &~ ((1 << 0) | (1 << 1) | (1 << 6))) | (1 << 2);
+
+ //pulls for spi in
+ P0PULL = (P0PULL &~ ((1 << 0) | (1 << 1) | (1 << 6))) | (1 << 2);
+
+ //setup 1.1(eeprom_nCS), 1.2(eink_BS1), 1.6(flash write protect) 1.7(eink_nCS)
+ P1FUNC &=~ ((1 << 1) | (1 << 2) | (1 << 5) | (1 << 6) | (1 << 7));
+ P1DIR &= ~((1 << 1) | (1 << 2) | (1 << 5) | (1 << 6) | (1 << 7));
+
+ //setup 2.0(eink_nRST), 2.1(eink_BUSY), 2.2(eink_D/nC)
+ P2FUNC &= ~((1 << 0) | (1 << 1) | (1 << 2));
+ P2DIR = (P2DIR &~ ((1 << 0) | (1 << 2))) | (1 << 1);
+
+ //raise chip select(s)
+ P1_1 = 1;
+ P1_7 = 1;
+
+ //BS1 = low
+ P1_2 = 1;
+ P1_5 = 0;
+ P1_6 = 1;
+
+ spiInit();
+}
+
+
+__bit boardGetOwnMac(uint8_t __xdata *mac)
+{
+ return flashRead(FLASH_INFOPAGE_ADDR + 0x10, mac, 8);
+}
+
+#pragma callee_saves prvUpdateApplierGet
+static uint32_t prvUpdateApplierGet(void) __naked
+{
+ __asm__(
+ " mov DPTR, #00098$ \n"
+ " mov A, #00099$ \n"
+ " clr C \n"
+ " subb A, DPL \n"
+ " mov B, A \n"
+ " ret \n"
+
+ ///actual updater code
+ "00098$: \n"
+
+
+ //copied to last page of flash for updating, called with ints off and eeprom ready to read update
+ //flashes 63 flash pages, uses xram for buffer. uses combined erase+flash flash op
+
+ " mov _CLKSPEED, #0x21 \n"
+ " mov _CFGPAGE, #0x04 \n"
+ " mov R0, #0 \n"
+
+ "00001$: \n"
+
+ //read a page of update
+ " mov DPTR, #0xe000 \n"
+ " mov R1, #0x04 \n"
+ " mov R2, #0x00 \n"
+ "000010$: \n"
+ " mov _SPITX, #0x00 \n"
+ " mov _SPICFG, #0xa0 \n"
+ "000011$: \n"
+ " mov A, _SPICFG \n"
+ " jb A.5, 000011$ \n"
+ " mov A, _SPIRX \n"
+ " movx @DPTR, A \n"
+ " inc DPTR \n"
+ " djnz R2, 000010$ \n"
+ " djnz R1, 000010$ \n"
+
+ //flash it
+ " clr A \n"
+ " orl _SETTINGS, #0x38 \n"
+ " mov _FWRTHREE, #0x03 \n"
+ " mov _FPGNO, R0 \n"
+ " mov _FWRDSTL, A \n"
+ " mov _FWRDSTH, A \n"
+ " mov _FWRLENL, #0xff \n"
+ " mov _FWRLENH, #0x03 \n"
+ " mov _FWRSRCL, A \n"
+ " mov _FWRSRCH, #0xe0 \n"
+ " orl _TRIGGER, #0x08 \n"
+ "00050$: \n"
+ " mov A, _TCON2 \n"
+ " jnb A.3, 00050$ \n"
+ " anl _TCON2, #~0x48 \n"
+ " anl _SETTINGS, #~0x10 \n"
+
+ //go do next page
+ " inc R0 \n"
+ " cjne R0, #63, 00001$ \n"
+
+ //done? reset
+ " mov _WDTCONF, #0x80 \n"
+ " mov _WDTENA, #0x01 \n"
+ " mov A, #0xff \n"
+ " mov _WDTRSTVALH, A \n"
+ " mov _WDTRSTVALM, A \n"
+ " mov _WDTRSTVALL, A \n"
+ "00090$: \n"
+ " sjmp 00090$ \n"
+
+ "00099$: \n"
+ );
+}
+
+void selfUpdate(void)
+{
+ uint32_t updaterInfo = prvUpdateApplierGet();
+ uint8_t __code *src = (uint8_t __code*)updaterInfo;
+ uint8_t i, len = updaterInfo >> 16;
+ uint8_t __xdata *dst = tempBuffer;
+
+ for (i = len; i ; i--)
+ *dst++ = *src++;
+
+ if (!flashWrite(0xfc00, tempBuffer, len, true))
+ pr("failed to write updater\n");
+
+ IEN_EA = 0; //ints off
+
+ __asm__(
+ " mov dptr, #0xfc00 \n"
+ " clr a \n"
+ " jmp @a+dptr \n"
+ );
+}
\ No newline at end of file
diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/board.h b/zbs243_shared/board/zbs29_BW_ssd1619/board.h
new file mode 100644
index 00000000..4684a303
--- /dev/null
+++ b/zbs243_shared/board/zbs29_BW_ssd1619/board.h
@@ -0,0 +1,36 @@
+#ifndef _BOARD_H_
+#define _BOARD_H_
+
+#include
+
+#define __packed
+
+#include "../oepl-definitions.h"
+#include "spi.h"
+
+#define eepromByte spiByte
+#define eepromPrvSelect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 0; __asm__("nop\nnop\nnop\n"); } while(0)
+#define eepromPrvDeselect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 1; __asm__("nop\nnop\nnop\n"); } while(0)
+
+//eeprom map
+#define EEPROM_SETTINGS_AREA_START (0x01000UL)
+#define EEPROM_SETTINGS_AREA_LEN (0x03000UL)
+#define EEPROM_UPDATA_AREA_START (0x04000UL)
+#define EEPROM_UPDATE_AREA_LEN (0x10000UL)
+#define EEPROM_IMG_START (0x14000UL)
+#define EEPROM_IMG_EACH (0x04000UL)
+//till end of eeprom really. do not put anything after - it will be erased at pairing time!!!
+#define EEPROM_PROGRESS_BYTES (128)
+
+#define HAS_EEPROM 1
+#define HAS_SCREEN 1
+#define NFC_TYPE -1
+#define AP_EMULATE_TAG 1
+
+//hw types
+#define HW_TYPE SOLUM_29_BW_SSD1619
+
+#include "../boardCommon.h"
+
+
+#endif
diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/make.mk b/zbs243_shared/board/zbs29_BW_ssd1619/make.mk
new file mode 100644
index 00000000..d1083181
--- /dev/null
+++ b/zbs243_shared/board/zbs29_BW_ssd1619/make.mk
@@ -0,0 +1,7 @@
+FLAGS += --code-size 0xfc00
+
+SOC = zbs243
+
+BARCODE = datamatrix
+
+# 0xfc00 and not 0x10000 to leave some space for update header and updater in flash
\ No newline at end of file
diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c b/zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c
new file mode 100644
index 00000000..9494884c
--- /dev/null
+++ b/zbs243_shared/board/zbs29_BW_ssd1619/old_screen.c
@@ -0,0 +1 @@
+#include "../ssd1619.c"
diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/screen.c b/zbs243_shared/board/zbs29_BW_ssd1619/screen.c
new file mode 100644
index 00000000..abc290f3
--- /dev/null
+++ b/zbs243_shared/board/zbs29_BW_ssd1619/screen.c
@@ -0,0 +1,834 @@
+#include "../ssd1619.h"
+
+#include
+#include
+
+#include "asmUtil.h"
+#include "barcode.h"
+#include "board.h"
+#include "cpu.h"
+#include "font.h"
+#include "lut.h"
+#include "printf.h"
+#include "screen.h"
+// #include "settings.h"
+#include "sleep.h"
+#include "spi.h"
+#include "timer.h"
+#include "wdt.h"
+
+#define CMD_DRV_OUTPUT_CTRL 0x01
+#define CMD_SOFT_START_CTRL 0x0C
+#define CMD_ENTER_SLEEP 0x10
+#define CMD_DATA_ENTRY_MODE 0x11
+#define CMD_SOFT_RESET 0x12
+#define CMD_SOFT_RESET2 0x13
+#define CMD_SETUP_VOLT_DETECT 0x15
+#define CMD_TEMP_SENSOR_CONTROL 0x18
+#define CMD_ACTIVATION 0x20
+#define CMD_DISP_UPDATE_CTRL 0x21
+#define CMD_DISP_UPDATE_CTRL2 0x22
+#define CMD_WRITE_FB_BW 0x24
+#define CMD_WRITE_FB_RED 0x26
+#define CMD_UNKNOWN_1 0x2B
+#define CMD_LOAD_OTP_LUT 0x31
+#define CMD_WRITE_LUT 0x32
+#define CMD_BORDER_WAVEFORM_CTRL 0x3C
+#define CMD_WINDOW_X_SIZE 0x44
+#define CMD_WINDOW_Y_SIZE 0x45
+#define CMD_WRITE_PATTERN_RED 0x46
+#define CMD_WRITE_PATTERN_BW 0x47
+#define CMD_XSTART_POS 0x4E
+#define CMD_YSTART_POS 0x4F
+#define CMD_ANALOG_BLK_CTRL 0x74
+#define CMD_DIGITAL_BLK_CTRL 0x7E
+// added for OTA LUT-support
+#define CMD_GATE_LEVEL 0x03
+#define CMD_SOURCE_LEVEL 0x04
+#define CMD_DUMMY_PERIOD 0x3A
+#define CMD_GATE_LINE_WIDTH 0x3B
+
+#define SCREEN_CMD_CLOCK_ON 0x80
+#define SCREEN_CMD_CLOCK_OFF 0x01
+#define SCREEN_CMD_ANALOG_ON 0x40
+#define SCREEN_CMD_ANALOG_OFF 0x02
+#define SCREEN_CMD_LATCH_TEMPERATURE_VAL 0x20
+#define SCREEN_CMD_LOAD_LUT 0x10
+#define SCREEN_CMD_USE_MODE_2 0x08 // modified commands 0x10 and 0x04
+#define SCREEN_CMD_REFRESH 0xC7
+
+#define commandEnd() \
+ do { \
+ P1_7 = 1; \
+ } while (0)
+
+#define markCommand() \
+ do { \
+ P2_2 = 0; \
+ } while (0)
+
+#define markData() \
+ do { \
+ P2_2 = 1; \
+ } while (0)
+
+extern void dump(uint8_t* __xdata a, uint16_t __xdata l); // remove me when done
+
+static uint8_t __xdata epdCharSize = 1; // character size, 1 or 2 (doubled)
+static bool __xdata directionY = true; // print direction, X or Y (true)
+static uint8_t __xdata rbuffer[32]; // used to rotate bits around
+static uint16_t __xdata fontCurXpos = 0; // current X value we're working with
+static uint16_t __xdata fontCurYpos = 0; // current Y value we're working with
+static uint8_t __xdata currentLut = 0;
+uint8_t __xdata dispLutSize = 0; // we'll need to expose this in the 'capabilities' flag
+
+static bool __xdata isInited = false;
+
+bool __xdata epdGPIOActive = false;
+
+#define LUT_BUFFER_SIZE 128
+static uint8_t waveformbuffer[LUT_BUFFER_SIZE];
+uint8_t __xdata customLUT[LUT_BUFFER_SIZE] = {0};
+
+struct waveform10* __xdata waveform10 = (struct waveform10*)waveformbuffer; // holds the LUT/waveform
+struct waveform* __xdata waveform7 = (struct waveform*)waveformbuffer; // holds the LUT/waveform
+
+#pragma callee_saves epdBusySleep
+#pragma callee_saves epdBusyWait
+static void epdBusySleep(uint32_t timeout) {
+ uint8_t tmp_P2FUNC = P2FUNC;
+ uint8_t tmp_P2DIR = P2DIR;
+ uint8_t tmp_P2PULL = P2PULL;
+ uint8_t tmp_P2LVLSEL = P2LVLSEL;
+ P2FUNC &= 0xfd;
+ P2DIR |= 2;
+ P2PULL |= 2;
+ P2LVLSEL |= 2;
+
+ P2CHSTA &= 0xfd;
+ P2INTEN |= 2;
+ P2CHSTA &= 0xfd;
+ sleepForMsec(timeout);
+ wdtOn();
+ P2CHSTA &= 0xfd;
+ P2INTEN &= 0xfd;
+
+ P2FUNC = tmp_P2FUNC;
+ P2DIR = tmp_P2DIR;
+ P2PULL = tmp_P2PULL;
+ P2LVLSEL = tmp_P2LVLSEL;
+ eepromPrvDeselect();
+}
+static void epdBusyWait(uint32_t timeout) {
+ uint32_t __xdata start = timerGet();
+
+ while (timerGet() - start < timeout) {
+ if (!P2_1)
+ return;
+ }
+ pr("screen timeout %lu ticks :(\n", timerGet() - start);
+ while (1)
+ ;
+}
+static void commandReadBegin(uint8_t cmd) {
+ epdSelect();
+ markCommand();
+ spiByte(cmd); // dump LUT
+
+ P0DIR = (P0DIR & ~(1 << 0)) | (1 << 1);
+ P0 &= ~(1 << 0);
+ P0FUNC &= ~((1 << 0) | (1 << 1));
+ P2_2 = 1;
+}
+static void commandReadEnd() {
+ // set up pins for spi (0.0,0.1,0.2)
+ P0FUNC |= (1 << 0) | (1 << 1);
+ epdDeselect();
+}
+#pragma callee_saves epdReadByte
+static uint8_t epdReadByte() {
+ uint8_t val = 0, i;
+
+ for (i = 0; i < 8; i++) {
+ P0_0 = 1;
+ __asm__("nop\nnop\nnop\nnop\nnop\nnop\n");
+ val <<= 1;
+ if (P0_1)
+ val++;
+ P0_0 = 0;
+ __asm__("nop\nnop\nnop\nnop\nnop\nnop\n");
+ }
+
+ return val;
+}
+static void shortCommand(uint8_t cmd) {
+ epdSelect();
+ markCommand();
+ spiTXByte(cmd);
+ epdDeselect();
+}
+static void shortCommand1(uint8_t cmd, uint8_t arg) {
+ epdSelect();
+ markCommand();
+ spiTXByte(cmd);
+ markData();
+ spiTXByte(arg);
+ epdDeselect();
+}
+static void shortCommand2(uint8_t cmd, uint8_t arg1, uint8_t arg2) {
+ epdSelect();
+ markCommand();
+ spiTXByte(cmd);
+ markData();
+ spiTXByte(arg1);
+ spiTXByte(arg2);
+ epdDeselect();
+}
+static void commandBegin(uint8_t cmd) {
+ epdSelect();
+ markCommand();
+ spiTXByte(cmd);
+ markData();
+}
+static void epdReset() {
+ timerDelay(TIMER_TICKS_PER_SECOND / 100);
+ P2_0 = 0;
+ timerDelay(TIMER_TICKS_PER_SECOND / 100);
+ P2_0 = 1;
+ timerDelay(TIMER_TICKS_PER_SECOND / 100);
+
+ shortCommand(CMD_SOFT_RESET); // software reset
+ timerDelay(TIMER_TICKS_PER_SECOND / 100);
+ //shortCommand(CMD_SOFT_RESET2);
+ //timerDelay(TIMER_TICKS_PER_SECOND / 100);
+}
+void epdConfigGPIO(bool setup) {
+ // data / _command: 2.2
+ // busy 2.1
+ // reset 2.0
+ // _select 1.7
+ // bs1 1.2
+
+ // GENERIC SPI BUS PINS
+ // spi.clk 0.0
+ // spi.mosi 0.1
+ if (epdGPIOActive == setup) return;
+ if (setup) {
+ P2DIR |= (1 << 1); // busy as input
+ P2DIR &= ~((1 << 2) | (1 << 0)); // D/C and Reset as output
+ P1DIR &= ~((1 << 7) | (1 << 2) | (1 << 5)); // select and bs1 as output
+ //P1_2 = 0; // select 4-wire SPI / BS1 = low
+ P1_5 = 0;
+ P1_7 = 1; // deselect EPD
+ } else {
+ P2DIR |= ((1 << 2) | (1 << 0)); // DC and Reset as input
+ P2 &= ~((1 << 2) | (1 << 0));
+ P1DIR |= ((1 << 7) | (1 << 2)); // Select and BS1 as input
+ P2 &= ~((1 << 7));
+ }
+ epdGPIOActive = setup;
+}
+void epdEnterSleep() {
+ P2_0 = 0;
+ timerDelay(10);
+ P2_0 = 1;
+ timerDelay(50);
+ shortCommand(CMD_SOFT_RESET2);
+ epdBusyWait(TIMER_TICKS_PER_MS * 15);
+ shortCommand1(CMD_ENTER_SLEEP, 0x03);
+ isInited = false;
+}
+void epdSetup() {
+ epdReset();
+ shortCommand1(CMD_ANALOG_BLK_CTRL, 0x54);
+ shortCommand1(CMD_DIGITAL_BLK_CTRL, 0x3B);
+ shortCommand2(CMD_UNKNOWN_1, 0x04, 0x63);
+
+ commandBegin(CMD_SOFT_START_CTRL);
+ epdSend(0x8f);
+ epdSend(0x8f);
+ epdSend(0x8f);
+ epdSend(0x3f);
+ commandEnd();
+
+ commandBegin(CMD_DRV_OUTPUT_CTRL);
+ epdSend((SCREEN_HEIGHT - 1) & 0xff);
+ epdSend((SCREEN_HEIGHT - 1) >> 8);
+ epdSend(0x00);
+ commandEnd();
+
+ // shortCommand1(CMD_DATA_ENTRY_MODE, 0x03);
+ // shortCommand1(CMD_BORDER_WAVEFORM_CTRL, 0xC0); // blurry edges
+ shortCommand1(CMD_BORDER_WAVEFORM_CTRL, 0x01);
+ shortCommand1(CMD_TEMP_SENSOR_CONTROL, 0x80);
+ shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C)
+ // shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB9); // mode 2?
+ shortCommand(CMD_ACTIVATION);
+ epdBusyWait(TIMER_TICKS_PER_SECOND);
+ isInited = true;
+ currentLut = EPD_LUT_DEFAULT;
+}
+static uint8_t epdGetStatus() {
+ uint8_t sta;
+ commandReadBegin(0x2F);
+ sta = epdReadByte();
+ commandReadEnd();
+ return sta;
+}
+uint16_t epdGetBattery(void) {
+ uint16_t voltage = 2600;
+ uint8_t val;
+
+ epdReset();
+
+ shortCommand1(CMD_DISP_UPDATE_CTRL2, SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON);
+ shortCommand(CMD_ACTIVATION);
+ epdBusyWait(TIMER_TICKS_PER_MS * 1000);
+
+ for (val = 3; val < 8; val++) {
+ shortCommand1(CMD_SETUP_VOLT_DETECT, val);
+ epdBusyWait(TIMER_TICKS_PER_MS * 1000);
+ if (epdGetStatus() & 0x10) { // set if voltage is less than threshold ( == 1.9 + val / 10)
+ voltage = 1850 + mathPrvMul8x8(val, 100);
+ break;
+ }
+ }
+ //shortCommand(CMD_SOFT_RESET2);
+ //epdBusyWait(TIMER_TICKS_PER_MS * 1000);
+ shortCommand1(CMD_ENTER_SLEEP, 0x03);
+ return voltage;
+}
+
+void loadFixedTempOTPLUT() {
+ shortCommand1(0x18, 0x48); // external temp sensor
+ shortCommand2(0x1A, 0x05, 0x00); // < temp register
+ shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C)
+ shortCommand(CMD_ACTIVATION);
+ epdBusyWait(TIMER_TICKS_PER_SECOND);
+}
+static void writeLut() {
+ commandBegin(CMD_WRITE_LUT);
+ for (uint8_t i = 0; i < (dispLutSize * 10); i++)
+ epdSend(waveformbuffer[i]);
+ commandEnd();
+}
+static void readLut() {
+ commandReadBegin(0x33);
+ uint16_t checksum = 0;
+ uint16_t ident = 0;
+ uint16_t shortl = 0;
+ for (uint16_t c = 0; c < LUT_BUFFER_SIZE; c++) {
+ waveformbuffer[c] = epdReadByte();
+ }
+ commandReadEnd();
+}
+static uint8_t getLutSize() {
+ uint8_t ref = 0;
+ for (uint8_t c = (LUT_BUFFER_SIZE - 4); c > 16; c--) {
+ uint8_t check = waveformbuffer[c];
+ for (uint8_t d = 1; d < 4; d++) {
+ if (waveformbuffer[c + d] != check) {
+ ref = c;
+ goto end;
+ }
+ }
+ }
+end:;
+ return ref + 1;
+}
+static void lutGroupDisable(uint8_t group) {
+ if (dispLutSize == 7) {
+ memset(&(waveform7->group[group]), 0x00, 5);
+ } else {
+ memset(&(waveform10->group[group]), 0x00, 5);
+ }
+}
+static void lutGroupSpeedup(uint8_t group, uint8_t speed) {
+ if (dispLutSize == 7) {
+ for (uint8_t i = 0; i < 4; i++) {
+ waveform7->group[group].phaselength[i] = 1 + (waveform7->group[group].phaselength[i] / speed);
+ }
+ } else {
+ for (uint8_t i = 0; i < 4; i++) {
+ waveform10->group[group].phaselength[i] = 1 + (waveform10->group[group].phaselength[i] / speed);
+ }
+ }
+}
+static void lutGroupRepeat(uint8_t group, uint8_t repeat) {
+ if (dispLutSize == 7) {
+ waveform7->group[group].repeat = repeat;
+ } else {
+ waveform10->group[group].repeat = repeat;
+ }
+}
+static void lutGroupRepeatReduce(uint8_t group, uint8_t factor) {
+ if (dispLutSize == 7) {
+ waveform7->group[group].repeat = waveform7->group[group].repeat / factor;
+ } else {
+ waveform10->group[group].repeat = waveform10->group[group].repeat / factor;
+ }
+}
+void selectLUT(uint8_t lut) {
+ if (currentLut == lut) {
+ return;
+ }
+
+ if (currentLut != EPD_LUT_DEFAULT) {
+ // load the 'default' LUT for the current temperature in the EPD lut register
+ shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1?
+ shortCommand(CMD_ACTIVATION);
+ epdBusyWait(TIMER_TICKS_PER_SECOND);
+ }
+
+ currentLut = lut;
+
+ // if we're going to be using the default LUT, we're done here.
+ if (lut == EPD_LUT_DEFAULT) {
+ return;
+ }
+
+ // download the current LUT from the waveform buffer
+ readLut();
+
+ if (dispLutSize == 0) {
+ dispLutSize = getLutSize();
+ dispLutSize /= 10;
+ pr("lut size = %d\n", dispLutSize);
+#ifdef PRINT_LUT
+ dump(waveformbuffer, LUT_BUFFER_SIZE);
+#endif
+ memcpy(customLUT, waveformbuffer, dispLutSize * 10);
+ }
+
+ switch (lut) {
+ default:
+ case EPD_LUT_NO_REPEATS:
+ lutGroupDisable(LUTGROUP_NEGATIVE);
+ lutGroupDisable(LUTGROUP_FASTBLINK);
+ lutGroupRepeat(LUTGROUP_SLOWBLINK, 0);
+ lutGroupSpeedup(LUTGROUP_SET, 2);
+ lutGroupSpeedup(LUTGROUP_IMPROVE_SHARPNESS, 2);
+ lutGroupRepeatReduce(LUTGROUP_IMPROVE_SHARPNESS, 2);
+ lutGroupSpeedup(LUTGROUP_IMPROVE_REDS, 2);
+ lutGroupRepeatReduce(LUTGROUP_IMPROVE_REDS, 2);
+ lutGroupDisable(LUTGROUP_UNUSED);
+ break;
+ case EPD_LUT_FAST_NO_REDS:
+ lutGroupDisable(LUTGROUP_NEGATIVE);
+ lutGroupDisable(LUTGROUP_FASTBLINK);
+ lutGroupDisable(LUTGROUP_SLOWBLINK);
+ lutGroupSpeedup(LUTGROUP_SET, 2);
+ lutGroupDisable(LUTGROUP_IMPROVE_REDS);
+ lutGroupDisable(LUTGROUP_IMPROVE_SHARPNESS);
+ lutGroupDisable(LUTGROUP_UNUSED);
+ break;
+ case EPD_LUT_FAST:
+ lutGroupDisable(LUTGROUP_NEGATIVE);
+ lutGroupDisable(LUTGROUP_FASTBLINK);
+ lutGroupDisable(LUTGROUP_SLOWBLINK);
+ lutGroupRepeat(LUTGROUP_SET, 1);
+ lutGroupSpeedup(LUTGROUP_SET, 2);
+ lutGroupDisable(LUTGROUP_IMPROVE_SHARPNESS);
+ lutGroupDisable(LUTGROUP_IMPROVE_REDS);
+ lutGroupDisable(LUTGROUP_UNUSED);
+ break;
+ }
+
+ // Handling if we received an OTA LUT
+ if (lut == EPD_LUT_OTA) {
+ memcpy(waveformbuffer, customLUT, dispLutSize * 10);
+ writeLut();
+ shortCommand1(CMD_GATE_LEVEL, customLUT[70]);
+ commandBegin(CMD_SOURCE_LEVEL);
+ epdSend(customLUT[71]);
+ epdSend(customLUT[72]);
+ epdSend(customLUT[73]);
+ commandEnd();
+ shortCommand1(CMD_DUMMY_PERIOD, customLUT[74]);
+ shortCommand1(CMD_GATE_LINE_WIDTH, customLUT[75]);
+ currentLut = lut;
+ return;
+ }
+
+ if (dispLutSize == 10) {
+ lutGroupDisable(LUTGROUP_UNUSED);
+ lutGroupDisable(LUTGROUP_UNKNOWN);
+ lutGroupDisable(LUTGROUP_UNUSED3);
+ lutGroupDisable(LUTGROUP_UNUSED4);
+ }
+ writeLut();
+}
+
+void setWindowX(uint16_t start, uint16_t end) {
+ shortCommand2(CMD_WINDOW_X_SIZE, start / 8, end / 8 - 1);
+}
+void setWindowY(uint16_t start, uint16_t end) {
+ commandBegin(CMD_WINDOW_Y_SIZE);
+ epdSend((start)&0xff);
+ epdSend((start) >> 8);
+ epdSend((end - 1) & 0xff);
+ epdSend((end - 1) >> 8);
+ commandEnd();
+}
+void setPosXY(uint16_t x, uint16_t y) {
+ shortCommand1(CMD_XSTART_POS, (uint8_t)(x / 8));
+ commandBegin(CMD_YSTART_POS);
+ epdSend((y)&0xff);
+ epdSend((y) >> 8);
+ commandEnd();
+}
+void setColorMode(uint8_t red, uint8_t bw) {
+ shortCommand1(CMD_DISP_UPDATE_CTRL, (red << 4) | bw);
+}
+void fillWindowWithPattern(bool color) {
+ if (color == EPD_COLOR_RED) {
+ shortCommand1(CMD_WRITE_PATTERN_RED, 0x00);
+ } else {
+ shortCommand1(CMD_WRITE_PATTERN_BW, 0x00);
+ }
+}
+void clearWindow(bool color) {
+ if (color == EPD_COLOR_RED) {
+ shortCommand1(CMD_WRITE_PATTERN_RED, 0x66);
+ } else {
+ shortCommand1(CMD_WRITE_PATTERN_BW, 0x66);
+ }
+}
+void clearScreen() {
+ setWindowX(0, SCREEN_WIDTH);
+ setWindowY(0, SCREEN_HEIGHT);
+ setPosXY(0, 0);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 3); // was 3
+ shortCommand1(CMD_WRITE_PATTERN_BW, 0x66);
+ epdBusyWait(TIMER_TICKS_PER_MS * 100);
+ shortCommand1(CMD_WRITE_PATTERN_RED, 0x66);
+ epdBusyWait(TIMER_TICKS_PER_MS * 100);
+}
+void draw() {
+ shortCommand1(0x22, 0xCF);
+ // shortCommand1(0x22, SCREEN_CMD_REFRESH);
+ shortCommand(0x20);
+ epdBusyWait(TIMER_TICKS_PER_SECOND * 120);
+}
+void drawNoWait() {
+ shortCommand1(0x22, 0xCF);
+ // shortCommand1(0x22, SCREEN_CMD_REFRESH);
+ shortCommand(0x20);
+}
+void drawWithSleep() {
+ shortCommand1(0x22, 0xCF);
+ // shortCommand1(0x22, SCREEN_CMD_REFRESH);
+ shortCommand(0x20);
+ uint8_t tmp_P2FUNC = P2FUNC;
+ uint8_t tmp_P2DIR = P2DIR;
+ uint8_t tmp_P2PULL = P2PULL;
+ uint8_t tmp_P2LVLSEL = P2LVLSEL;
+ P2FUNC &= 0xfd;
+ P2DIR |= 2;
+ P2PULL |= 2;
+ P2LVLSEL |= 2;
+
+ P2CHSTA &= 0xfd;
+ P2INTEN |= 2;
+ P2CHSTA &= 0xfd;
+ sleepForMsec(TIMER_TICKS_PER_SECOND * 120);
+ wdtOn();
+ P2CHSTA &= 0xfd;
+ P2INTEN &= 0xfd;
+
+ P2FUNC = tmp_P2FUNC;
+ P2DIR = tmp_P2DIR;
+ P2PULL = tmp_P2PULL;
+ P2LVLSEL = tmp_P2LVLSEL;
+ eepromPrvDeselect();
+}
+void epdWaitRdy() {
+ epdBusyWait(TIMER_TICKS_PER_SECOND * 120);
+}
+void drawLineHorizontal(bool color, uint16_t x1, uint16_t x2, uint16_t y) {
+ setWindowX(x1, x2);
+ setWindowY(y, y + 1);
+ if (color) {
+ shortCommand1(CMD_WRITE_PATTERN_RED, 0xE6);
+ } else {
+ shortCommand1(CMD_WRITE_PATTERN_BW, 0xE6);
+ }
+ epdBusyWait(TIMER_TICKS_PER_MS * 100);
+}
+void drawLineVertical(bool color, uint16_t x, uint16_t y1, uint16_t y2) {
+ setWindowY(y1, y2);
+ setWindowX(x, x + 8);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 3);
+ setPosXY(x, y1);
+ if (color) {
+ commandBegin(CMD_WRITE_FB_RED);
+ } else {
+ commandBegin(CMD_WRITE_FB_BW);
+ }
+ uint8_t __xdata c = 0x80;
+ c >>= (x % 8);
+ for (; y1 < y2; y1++) {
+ epdSend(c);
+ }
+ commandEnd();
+}
+void beginFullscreenImage() {
+ setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
+ setWindowX(0, SCREEN_WIDTH);
+ setWindowY(0, SCREEN_HEIGHT);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 3);
+ setPosXY(0, 0);
+}
+void beginWriteFramebuffer(bool color) {
+ if (color == EPD_COLOR_RED) {
+ commandBegin(CMD_WRITE_FB_RED);
+ } else {
+ commandBegin(CMD_WRITE_FB_BW);
+ }
+ epdDeselect();
+}
+void endWriteFramebuffer() {
+ commandEnd();
+}
+void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color) {
+ uint16_t xsize = bmp[0] / 8;
+ if (bmp[0] % 8) xsize++;
+ uint16_t size = xsize * bmp[1];
+ setWindowX(x, x + (xsize * 8));
+ setWindowY(y, bmp[1] + y);
+ setPosXY(x, y);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 3);
+ if (color) {
+ commandBegin(CMD_WRITE_FB_RED);
+ } else {
+ commandBegin(CMD_WRITE_FB_BW);
+ }
+ bmp += 2;
+ while (size--) {
+ epdSend(*(bmp++));
+ }
+ commandEnd();
+}
+void printBarcode(const uint8_t* string, uint16_t x, uint16_t y) {
+ setWindowY(y, 1);
+ setWindowX(x, x + 8);
+ setPosXY(x, y);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 1);
+ commandBegin(CMD_WRITE_FB_BW);
+ struct BarcodeInfo __xdata bci = {
+ .str = string,
+ };
+ while (!barcodeIsDone(&bci)) {
+ if (barcodeNextBar(&bci)) {
+ epdSend(0xFF);
+ } else {
+ epdSend(0x00);
+ }
+ }
+ commandEnd();
+}
+// stuff for printing text
+static void pushXFontBytesToEPD(uint8_t byte1, uint8_t byte2) {
+ if (epdCharSize == 1) {
+ uint8_t offset = 7 - (fontCurXpos % 8);
+ for (uint8_t c = 0; c < 8; c++) {
+ if (byte2 & (1 << (7 - c))) rbuffer[c] |= (1 << offset);
+ }
+ for (uint8_t c = 0; c < 8; c++) {
+ if (byte1 & (1 << (7 - c))) rbuffer[8 + c] |= (1 << offset);
+ }
+ fontCurXpos++;
+ } else {
+ uint8_t offset = 6 - (fontCurXpos % 8);
+ // double font size
+ for (uint8_t c = 0; c < 8; c++) {
+ if (byte2 & (1 << (7 - c))) {
+ rbuffer[c * 2] |= (3 << offset);
+ rbuffer[(c * 2) + 1] |= (3 << offset);
+ }
+ }
+ for (uint8_t c = 0; c < 8; c++) {
+ if (byte1 & (1 << (7 - c))) {
+ rbuffer[(c * 2) + 16] |= (3 << offset);
+ rbuffer[(c * 2) + 17] |= (3 << offset);
+ }
+ }
+ fontCurXpos += 2;
+ }
+ if (fontCurXpos % 8 == 0) {
+ // next byte, flush current byte to EPD
+ for (uint8_t i = 0; i < (16 * epdCharSize); i++) {
+ epdSend(rbuffer[i]);
+ }
+ memset(rbuffer, 0, 32);
+ }
+}
+static void bufferByteShift(uint8_t byte) {
+ /*
+ rbuffer[0] = 0; // previous value
+ rbuffer[1] = y%8; // offset
+ rbuffer[2] = 0; // current byte counter;
+ rbuffer[3] = 1+(epdCharsize*2);
+ */
+
+ if (rbuffer[1] == 0) {
+ epdSend(byte);
+ } else {
+ uint8_t offset = rbuffer[1];
+ rbuffer[0] |= (byte >> offset);
+ epdSend(rbuffer[0]);
+ // epdSend(byte);
+ rbuffer[0] = (byte << (8 - offset));
+ rbuffer[2]++;
+ if (rbuffer[2] == rbuffer[3]) {
+ epdSend(rbuffer[0]);
+ rbuffer[0] = 0;
+ rbuffer[2] = 0;
+ }
+ }
+}
+static void pushYFontBytesToEPD(uint8_t byte1, uint8_t byte2) {
+ if (epdCharSize == 2) {
+ for (uint8_t j = 0; j < 2; j++) {
+ uint8_t c = 0;
+ for (uint8_t i = 7; i != 255; i--) {
+ if (byte1 & (1 << i)) c |= (0x03 << ((i % 4) * 2));
+ if ((i % 4) == 0) {
+ bufferByteShift(c);
+ c = 0;
+ }
+ }
+ for (uint8_t i = 7; i != 255; i--) {
+ if (byte2 & (1 << i)) c |= (0x03 << ((i % 4) * 2));
+ if ((i % 4) == 0) {
+ bufferByteShift(c);
+ c = 0;
+ }
+ }
+ }
+ } else {
+ bufferByteShift(byte1);
+ bufferByteShift(byte2);
+ }
+}
+void writeCharEPD(uint8_t c) {
+ // Writes a single character to the framebuffer
+ bool empty = true;
+ for (uint8_t i = 0; i < 20; i++) {
+ if (font[c][i]) empty = false;
+ }
+ if (empty) {
+ for (uint8_t i = 0; i < 8; i++) {
+ if (directionY) {
+ pushYFontBytesToEPD(0x00, 0x00);
+ } else {
+ pushXFontBytesToEPD(0x00, 0x00);
+ }
+ }
+ return;
+ }
+
+ uint8_t begin = 0;
+ while (font[c][begin] == 0x00 && font[c][begin + 1] == 0x00) {
+ begin += 2;
+ }
+
+ uint8_t end = 20;
+ while (font[c][end - 1] == 0x00 && font[c][end - 2] == 0x00) {
+ end -= 2;
+ }
+
+ for (uint8_t pos = begin; pos < end; pos += 2) {
+ if (directionY) {
+ pushYFontBytesToEPD(font[c][pos + 1], font[c][pos]);
+ } else {
+ pushXFontBytesToEPD(font[c][pos], font[c][pos + 1]);
+ }
+ }
+
+ // spacing between characters
+ if (directionY) {
+ pushYFontBytesToEPD(0x00, 0x00);
+ } else {
+ pushXFontBytesToEPD(0x00, 0x00);
+ }
+}
+
+// Print text to the EPD. Origin is top-left
+void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool color) {
+ directionY = direction;
+ epdCharSize = 1 + fontsize;
+ if (directionY) {
+ uint8_t extra = 0;
+
+ // provisions for dealing with font in Y direction, byte-unaligned
+ if (x % 8) {
+ extra = 8;
+ rbuffer[0] = 0; // previous value
+ rbuffer[1] = x % 8; // offset
+ rbuffer[2] = 0; // current byte counter;
+ rbuffer[3] = (epdCharSize * 2);
+ } else {
+ rbuffer[1] = 0;
+ }
+
+ setWindowY(y, 1);
+ if (epdCharSize == 2) {
+ setWindowX(x, x + 32 + extra);
+ setPosXY(x, y);
+ } else {
+ setWindowX(x, x + 16 + extra);
+ setPosXY(x, y);
+ }
+ shortCommand1(CMD_DATA_ENTRY_MODE, 1); // was 3
+ } else {
+ if (epdCharSize == 2) {
+ x /= 2;
+ x *= 2;
+ setWindowY(y, y + 32);
+ } else {
+ setWindowY(y, y + 16);
+ }
+ setPosXY(x, y);
+ fontCurXpos = x;
+ setWindowX(x, SCREEN_WIDTH);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 7);
+ memset(rbuffer, 0, 32);
+ }
+
+ if (color) {
+ commandBegin(CMD_WRITE_FB_RED);
+ } else {
+ commandBegin(CMD_WRITE_FB_BW);
+ }
+}
+void epdPrintEnd() {
+ if (!directionY && ((fontCurXpos % 8) != 0)) {
+ for (uint8_t i = 0; i < (16 * epdCharSize); i++) {
+ epdSend(rbuffer[i]);
+ }
+ }
+ commandEnd();
+}
+
+extern uint8_t __xdata blockXferBuffer[];
+
+void readRam() {
+ setWindowY(296, 0);
+ setWindowX(0, 8);
+ setPosXY(0, 296);
+ shortCommand1(CMD_DATA_ENTRY_MODE, 1); // was 3
+ shortCommand1(0x41, 0x00);
+ commandReadBegin(0x27);
+ epdReadByte();
+
+ for (uint16_t c = 0; c < 293; c++) {
+ blockXferBuffer[c] = epdReadByte() | 0x10;
+ }
+ commandReadEnd();
+ commandBegin(CMD_WRITE_FB_BW);
+ for (uint16_t c = 0; c < 296; c++) {
+ epdSend(blockXferBuffer[c]);
+ }
+ commandEnd();
+}
diff --git a/zbs243_shared/board/zbs29_BW_ssd1619/screen.h b/zbs243_shared/board/zbs29_BW_ssd1619/screen.h
new file mode 100644
index 00000000..e724816e
--- /dev/null
+++ b/zbs243_shared/board/zbs29_BW_ssd1619/screen.h
@@ -0,0 +1,26 @@
+#ifndef _SCREEN_H_
+#define _SCREEN_H_
+
+#include
+#include
+#include "../ssd1619.h"
+
+
+#define SCREEN_WIDTH 128
+#define SCREEN_HEIGHT 296
+
+#define SCREEN_NUM_GREYS 5
+#define SCREEN_FIRST_GREY_IDX 0
+#define SCREEN_EXTRA_COLOR_INDEX -1 //set to negative if nonexistent
+#define SCREEN_TX_BPP 4 //in transit
+
+#define SCREEN_WIDTH_MM 29
+#define SCREEN_HEIGHT_MM 67
+
+#define SCREEN_BYTE_FILL 0x44 //white
+
+#define SCREEN_TYPE TagScreenEink_BW_1bpp
+
+#define SCREEN_DATA_PASSES 2
+
+#endif