Merge branch 'master' into raw-buffer

This commit is contained in:
Nic Limper
2023-03-13 13:38:19 +01:00
52 changed files with 9769 additions and 8048 deletions

65
PCB-jig/README.md Normal file
View File

@@ -0,0 +1,65 @@
# The OpenEpaperLink PCB #
The OpenEpaperLink PCB is a set of PCBs that can help you flash the custom OpenEPaperLink firmware to some of the Solum ESL tags.
<img width="600" alt="board" src="gerbv-board.png">
<img width="600" alt="board" src="pcb-photo.jpg">
## Pogo / Interface boards ##
There are pogo interface boards for the following display types:
* 1.54" displays
* 2.9" displays
* 4.2" displays
* Segmented displays
In addition, there are a few loose debug-header footprints that can help to secure pogo pins in 3d-printed programming jigs.
### Parts (per board) ###
* 10x 1mm pogo pin (I'd take the rounded ones, but the pointy ones are fine too)
* 1x 90 degree angle 1.27mm 2x5 male IDC header (search for JTAG connector/header)
* 1x 6-pin 2.54mm angled pinheader
* 1x 1206-sized SMD LED
* 1x 1206 SMD resistor (47 ohm or so, depending on LED color, personal taste, whatever)
### Segmented, soldered board ###
As this board sits flush on the ESL board, it can't use through-hole components. It only uses:
* 1x SMD 2x5 male IDC header
For power consumption / debugging purposes, an optional 2.54mm 90-degree-angled pinheader can be added to connect a power profiler, such as the PPK2. A jumper or solder bridge needs to be added when not used or populated.
## Flasher/ESP32 Board ##
This board expects an ESP32-S3 DevkitC1 board. If you want to use it as an AP and generate pictures on the ESP32, it is probably advisable to get an ESP32 with extra RAM, it'll probably scale better in the future. However, current codebase does not require extra RAM.
### Parts ###
* 1x ESP32-S3 DevkitC1
* 2x SMD 2x5 male IDC header
* 1x 90 degree angle 1.27mm 2x5 male IDC header
* 2x AO3401 P-channel MOSFET
* 2x AO3400 N-channel MOSFET
* 1x 1206-sized SMD LED
* 1x 1206 SMD resistor (47 ohm or so, depending on LED color, personal taste, whatever)
Some 1206 SMD resistors, not critical:
* 4x 100 (suggested) R4, R5, R8, R9
* 2x 4k7 (pull up) R6, R10
* 2x 47 (discharge shunt) R7, R11
### Errata ###:
* The tags have quite a lot of capacitance with a LOT of condensors over the power rail, these can cause the 3v3 rail to sag enough to cause the ESP32 to reset if power to the tag is switched on directly. Use a PWM to switch the tag on, and you're fine. It's also possible to add a small resistor in series with the tag, or add a bunch of extra capacitance to the ESP32.
## Use as Access Point ##
In addition, there is a sub-board that can be used to connect a segmented EPD-ESL to the flasher via a soldered connection. For this to work, you'll need to open the tag up and solder the interface board directly to the PCB. A fine pitched soldering iron tip and thin solder wire make this job easier. If you cut out a small slot in the back of the case, you can close the case up again.
Any tag can be used as an AP, when connected to the AP. The 'internal AP' header is meant for that purpose. This allows you to use the ESL as a 802.15.4 radio, using the ZBS243/SEM9110 over a serial link to the ESP32. The segmented ESL seems to be best suited for that, anecdotal evidence suggests the best range with this type of tag.
Please note that while this works mostly 'fine', these tags aren't built to be used as AP's. Tags are really built down to a price, and it shows. Some tags have a LOT better range than others. The PCB is pretty fragile and bends really easily; this can cause cracks in components and connections reducing range or killing the device altogether. Your milage may vary.
## Other radios ##
While there is no current application or code within the project, it's possible to connect a A7105 or CC1101 to the board in order to connect to other wireless experiments. A small subboard is included for this specific purpose.
## Getting PCB's ##
You can order the boards from your favorite boardhouse, using the zip file in this repository. Some boardhouses don't like the amount of routing/milling this board requires, or add hefty fees depending on how many 'designs' are included in your board. As-is, according to some boardhouses, the PCB contains 9 different designs.
Also, this design uses castellated edges by milling through a debug-header footprint. Some houses don't like that either. I've had success ordering this PCB from DirtyPCBs, but this should not be seen as an endorsement; there's no guarantee they will continue to manufacture this board.
## Disclaimer ##
There is no guarantee or warranty whatsoever, nor is there any promise or insinuation that this board fill fullfill any particular purpose. This board may very well not work for you, set your hair and/or, but not limited to, pants on fire, incite violance or persuade other countries to invade your country. You're on your own, chief!

View File

@@ -3539,6 +3539,19 @@ You are welcome to use this library for commercial purposes. For attribution, we
<wire x1="53.086" y1="70.383" x2="56.261" y2="70.383" width="0.4064" layer="1"/>
<wire x1="56.261" y1="70.383" x2="56.261" y2="72.898" width="0.4064" layer="1"/>
<wire x1="56.261" y1="72.898" x2="56.363" y2="72.898" width="0.4064" layer="1"/>
<wire x1="53.086" y1="70.383" x2="53.086" y2="72.517" width="0.4064" layer="1"/>
<via x="53.086" y="72.517" extent="1-16" drill="0.35"/>
<wire x1="53.086" y1="72.517" x2="53.086" y2="68.707" width="0.4064" layer="16"/>
<wire x1="53.086" y1="68.707" x2="53.848" y2="67.945" width="0.4064" layer="16"/>
<wire x1="53.848" y1="67.945" x2="60.8838" y2="67.945" width="0.4064" layer="16"/>
<wire x1="60.8838" y1="67.945" x2="63.5" y2="67.945" width="0.2032" layer="16"/>
<wire x1="63.5" y1="67.945" x2="72.898" y2="67.945" width="0.4064" layer="16"/>
<wire x1="72.898" y1="67.945" x2="73.66" y2="67.183" width="0.4064" layer="16"/>
<wire x1="73.66" y1="67.183" x2="73.66" y2="47.498" width="0.4064" layer="16"/>
<wire x1="73.66" y1="47.498" x2="82.55" y2="38.608" width="0.4064" layer="16"/>
<via x="82.55" y="38.608" extent="1-16" drill="0.35"/>
<wire x1="82.55" y1="38.608" x2="82.55" y2="38.735" width="0.4064" layer="1"/>
<wire x1="82.55" y1="38.735" x2="87.63" y2="38.735" width="0.4064" layer="1"/>
</signal>
<signal name="N$42">
<contactref element="R4" pad="1"/>
@@ -4335,6 +4348,10 @@ You are welcome to use this library for commercial purposes. For attribution, we
<mfgpreviewcolor name="coppercolor" color="0xFFFFBF00"/>
<mfgpreviewcolor name="substratecolor" color="0xFF786E46"/>
</mfgpreviewcolors>
<errors>
<approved hash="19,1,32684d8eae69d312"/>
<approved hash="23,1,aa050aef08d0a83a"/>
</errors>
</board>
</drawing>
<compatibility>

View File

@@ -3312,6 +3312,19 @@ You are welcome to use this library for commercial purposes. For attribution, we
<wire x1="54.737" y1="70.383" x2="57.912" y2="70.383" width="0.4064" layer="1"/>
<wire x1="57.912" y1="70.383" x2="57.912" y2="72.898" width="0.4064" layer="1"/>
<wire x1="57.912" y1="72.898" x2="58.014" y2="72.898" width="0.4064" layer="1"/>
<contactref element="J9" pad="0"/>
<wire x1="54.737" y1="70.383" x2="54.737" y2="72.263" width="0.4064" layer="1"/>
<via x="54.737" y="72.263" extent="1-16" drill="0.35"/>
<wire x1="54.737" y1="72.263" x2="54.737" y2="68.453" width="0.4064" layer="16"/>
<wire x1="54.737" y1="68.453" x2="55.245" y2="67.945" width="0.4064" layer="16"/>
<wire x1="55.245" y1="67.945" x2="62.5348" y2="67.945" width="0.4064" layer="16"/>
<wire x1="62.5348" y1="67.945" x2="65.151" y2="67.945" width="0.2032" layer="16"/>
<wire x1="65.151" y1="67.945" x2="74.803" y2="67.945" width="0.4064" layer="16"/>
<wire x1="74.803" y1="67.945" x2="75.311" y2="67.437" width="0.4064" layer="16"/>
<wire x1="75.311" y1="67.437" x2="75.311" y2="47.879" width="0.4064" layer="16"/>
<wire x1="75.311" y1="47.879" x2="84.455" y2="38.735" width="0.4064" layer="16"/>
<via x="84.455" y="38.735" extent="1-16" drill="0.35"/>
<wire x1="84.455" y1="38.735" x2="89.281" y2="38.735" width="0.4064" layer="1"/>
</signal>
<signal name="N$42">
<contactref element="R4" pad="1"/>

View File

@@ -8047,6 +8047,12 @@ You are welcome to use this library for commercial purposes. For attribution, we
<wire x1="-7.62" y1="124.46" x2="-10.16" y2="124.46" width="0.1524" layer="91"/>
<wire x1="-10.16" y1="124.46" x2="-10.16" y2="144.78" width="0.1524" layer="91"/>
<junction x="-10.16" y="144.78"/>
<wire x1="-10.16" y1="124.46" x2="-10.16" y2="86.36" width="0.1524" layer="91"/>
<wire x1="-10.16" y1="86.36" x2="132.08" y2="86.36" width="0.1524" layer="91"/>
<junction x="-10.16" y="124.46"/>
<pinref part="J9" gate="G$1" pin="BOOT/0"/>
<wire x1="132.08" y1="86.36" x2="132.08" y2="53.34" width="0.1524" layer="91"/>
<wire x1="132.08" y1="53.34" x2="124.46" y2="53.34" width="0.1524" layer="91"/>
</segment>
</net>
<net name="N$42" class="0">

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@@ -171,14 +171,17 @@ X14805Y26555
X18955Y27455
X20355Y29155
X20355Y29455
X20355Y31655
X21255Y28855
X22305Y26205
X30355Y26055
X20355Y31655
X30355Y28055
X33355Y28055
X30355Y26055
X33355Y26055
X36255Y23605
X33355Y28055
X36855Y28555
X37805Y27905
X37305Y24155
X36255Y23605
X36155Y22055
X35505Y22055
X35955Y21055
@@ -186,6 +189,7 @@ X36455Y20505
X35855Y19605
X36355Y19055
X36355Y17555
X32855Y15505
X37355Y9555
X36055Y6555
X35755Y6555
@@ -213,8 +217,6 @@ X9947Y6855
X6955Y28655
X10255Y31655
X2155Y30905
X36855Y28555
X37805Y27905
T03
X11605Y7305
X12105Y7305

View File

@@ -1,8 +1,8 @@
Generated by EAGLE CAM Processor 9.6.2
Drill Station Info File: C:/Users/jbruijn/Documents/EAGLE/projects/programming jigs/epapertagjig.dri
Drill Station Info File: X:/solum-esl-alternative-proto/PCB-jig/epapertagjig-cam.dri
Date : 20/02/2023 11:19
Date : 10/03/2023 14:14
Drills : generated
Device : Excellon drill station, coordinate format 2.4 inch
@@ -28,15 +28,15 @@ Drills used:
Code Size used
T01 0.0120inch 21
T02 0.0138inch 183
T02 0.0138inch 185
T03 0.0200inch 60
T04 0.0394inch 138
T05 0.0400inch 85
T06 0.0591inch 14
T07 0.0787inch 6
Total number of drills: 507
Total number of drills: 509
Plotfiles:
C:/Users/jbruijn/Documents/EAGLE/projects/programming jigs/epapertagjig.TXT
X:/solum-esl-alternative-proto/PCB-jig/epapertagjig-cam.TXT

View File

@@ -1,9 +1,9 @@
Generated by EAGLE CAM Processor 9.6.2
Photoplotter Info File: C:/Users/jbruijn/Documents/EAGLE/projects/programming jigs/epapertagjig.gpi
Photoplotter Info File: X:/solum-esl-alternative-proto/PCB-jig/epapertagjig-cam.gpi
Date : 20/02/2023 11:19
Plotfile : C:/Users/jbruijn/Documents/EAGLE/projects/programming jigs/epapertagjig.GTL
Date : 10/03/2023 14:14
Plotfile : X:/solum-esl-alternative-proto/PCB-jig/epapertagjig-cam.GTL
Apertures : generated:
Device : Gerber RS-274-X photoplotter, coordinate format 2.5 inch
@@ -46,16 +46,16 @@ Apertures used:
D20 rectangle 0.0945inch x 0.0299inch 20
D21 rectangle 0.0551inch x 0.0472inch 2
D22 rectangle 0.0709inch x 0.0630inch 4
D23 draw 0.0080inch 4751
D23 draw 0.0080inch 4752
D24 draw 0.0500inch 26
D25 draw 0.0400inch 25
D26 round 0.0258inch 96
D26 round 0.0258inch 98
D27 round 0.0886inch 11
D28 round 0.0240inch 21
D29 draw 0.0240inch 100
D30 draw 0.0160inch 191
D30 draw 0.0160inch 194
D31 draw 0.0320inch 5
D32 draw 0.0100inch 1
D33 draw 0.0079inch 4038
D33 draw 0.0079inch 4076
D34 round 0.1181inch 6

View File

@@ -1,49 +0,0 @@
(gerbv-file-version! "2.0A")
(define-layer! 7 (cons 'filename "epapertagjig.GTL")
(cons 'visible #t)
(cons 'color #(17990 17990 17990))
(cons 'alpha #(65535))
)
(define-layer! 6 (cons 'filename "epapertagjig.GTS")
(cons 'visible #t)
(cons 'color #(65535 62723 0))
(cons 'alpha #(65535))
)
(define-layer! 5 (cons 'filename "epapertagjig.GTO")
(cons 'visible #t)
(cons 'color #(65535 65535 65535))
(cons 'alpha #(65535))
)
(define-layer! 4 (cons 'filename "epapertagjig.GBL")
(cons 'visible #f)
(cons 'color #(17990 17990 17990))
)
(define-layer! 3 (cons 'filename "epapertagjig.GBS")
(cons 'visible #f)
(cons 'color #(65535 62595 0))
(cons 'alpha #(65535))
)
(define-layer! 2 (cons 'filename "epapertagjig.GBO")
(cons 'visible #f)
(cons 'color #(65535 65535 65535))
(cons 'alpha #(65535))
)
(define-layer! 1 (cons 'filename "epapertagjig.TXT")
(cons 'visible #t)
(cons 'color #(0 0 0))
(cons 'alpha #(65535))
(cons 'attribs (list
(list 'autodetect 'Boolean 1)
(list 'zero_suppression 'Enum 0)
(list 'units 'Enum 0)
(list 'digits 'Integer 4)
))
)
(define-layer! 0 (cons 'filename "epapertagjig.GML")
(cons 'visible #t)
(cons 'color #(0 50115 50115))
)
(define-layer! -1 (cons 'filename "X:/solum-esl-alternative-proto/PCB-jig/gerbview")
(cons 'color #(0 0 0))
)
(set-render-type! 3)

BIN
PCB-jig/pcb-photo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

View File

@@ -11,6 +11,8 @@ It is currently compatible with the following tags:
* 2.9"
* 1.54"
On the 2.9" tags, both the UC8151 and SSD1619 display variants are supported
### Aims
- Low power (currently around 9µA with a minimum of 40 second latency)
- Even lower power when there's no AP around
@@ -43,8 +45,6 @@ You can access the ESP32 with any web browser after connecting it to your WiFi N
- Code cleanup... Splitting into different files, for instance. It's a mess.
### Tags:
- Implement NFC for URL's
- Implement battery reading
- Implement RSSI/LQI to be sent to the AP
### AP:
- Important! The AP needs to be able to tell a tag to try again later if it's already doing comms with another tag. The AP can't handle concurrent checkins/download due to memory constraints!
- More reliable serial comms (sometimes bytes are dropped)

View File

@@ -4,8 +4,8 @@ BUILD ?= zbs29v033
#file containing main() must be first!
SOURCES += main.c eeprom.c drawing.c
SOURCES += comms.c
SOURCES += syncedproto.c epd.c userinterface.c
SOURCES += powermgt.c barcode.c
SOURCES += syncedproto.c userinterface.c
SOURCES += powermgt.c barcode.c i2cdevices.c
all: #make sure it is the first target
@@ -18,7 +18,7 @@ FLAGS += -Icpu/$(CPU)
SOURCES += cpu/$(CPU)/cpu.c
SOURCES += board/$(BUILD)/board.c
#SOURCES += board/$(BUILD)/screen.c
SOURCES += board/$(BUILD)/screen.c
EEPROMDRV ?= eeprom.c

View File

@@ -1,5 +1,3 @@
#include "epd.h"
#include <stdbool.h>
#include <string.h>
@@ -7,6 +5,7 @@
#include "barcode.h"
#include "board.h"
#include "cpu.h"
#include "ssd1619.h"
#include "font.h"
#include "lut.h"
#include "printf.h"
@@ -205,7 +204,7 @@ void epdConfigGPIO(bool setup) {
// GENERIC SPI BUS PINS
// spi.clk 0.0
// spi.mosi 0.1
if(epdGPIOActive==setup)return;
if (epdGPIOActive == setup) return;
if (setup) {
P2DIR |= (1 << 1); // busy as input
P2DIR &= ~((1 << 2) | (1 << 0)); // D/C and Reset as output
@@ -815,4 +814,4 @@ void readRam() {
epdSend(blockXferBuffer[c]);
}
commandEnd();
}
}

View File

@@ -17,12 +17,11 @@
#define EPD_MODE_INVERT 0x08
#define EPD_MODE_IGNORE 0x04
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_FAST 3
#define epdSelect() \
do { \
P1_7 = 0; \
@@ -43,7 +42,7 @@ extern bool __xdata epdGPIOActive;
void setWindowX(uint16_t start, uint16_t end);
void setWindowY(uint16_t start, uint16_t end);
void setPosXY(uint16_t x, uint16_t y);
void setColorMode(uint8_t red, uint8_t bw) ;
void setColorMode(uint8_t red, uint8_t bw);
void fillWindowWithPattern(bool color);
void clearWindow(bool color);
void clearScreen();
@@ -74,4 +73,4 @@ void lutTest();
// for printf.c
void writeCharEPD(uint8_t c);
#endif
#endif

787
tag_fw/board/uc8151.c Normal file
View File

@@ -0,0 +1,787 @@
#include "uc8151.h"
#include <stdbool.h>
#include <string.h>
#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_PANEL_SETTING 0x00
#define CMD_POWER_SETTING 0x01
#define CMD_POWER_OFF 0x02
#define CMD_POWER_OFF_SEQUENCE 0x03
#define CMD_POWER_ON 0x04
#define CMD_POWER_ON_MEASURE 0x05
#define CMD_BOOSTER_SOFT_START 0x06
#define CMD_DEEP_SLEEP 0x07
#define CMD_DISPLAY_START_TRANSMISSION_DTM1 0x10
#define CMD_DATA_STOP 0x11
#define CMD_DISPLAY_REFRESH 0x12
#define CMD_DISPLAY_START_TRANSMISSION_DTM2 0x13
#define CMD_PLL_CONTROL 0x30
#define CMD_TEMPERATURE_CALIB 0x40
#define CMD_TEMPERATURE_SELECT 0x41
#define CMD_TEMPERATURE_WRITE 0x42
#define CMD_TEMPERATURE_READ 0x43
#define CMD_VCOM_INTERVAL 0x50
#define CMD_LOWER_POWER_DETECT 0x51
#define CMD_TCON_SETTING 0x60
#define CMD_RESOLUTION_SETING 0x61
#define CMD_REVISION 0x70
#define CMD_STATUS 0x71
#define CMD_AUTO_MEASUREMENT_VCOM 0x80
#define CMD_READ_VCOM 0x81
#define CMD_VCOM_DC_SETTING 0x82
#define CMD_PARTIAL_WINDOW 0x90
#define CMD_PARTIAL_IN 0x91
#define CMD_PARTIAL_OUT 0x92
#define CMD_PROGRAM_MODE 0xA0
#define CMD_ACTIVE_PROGRAM 0xA1
#define CMD_READ_OTP 0xA2
#define CMD_CASCADE_SET 0xE0
#define CMD_POWER_SAVING 0xE3
#define CMD_FORCE_TEMPERATURE 0xE5
enum PSR_FLAGS {
RES_96x230 = 0b00000000,
RES_96x252 = 0b01000000,
RES_128x296 = 0b10000000,
RES_160x296 = 0b11000000,
LUT_OTP = 0b00000000,
LUT_REG = 0b00100000,
FORMAT_BWR = 0b00000000,
FORMAT_BW = 0b00010000,
SCAN_DOWN = 0b00000000,
SCAN_UP = 0b00001000,
SHIFT_LEFT = 0b00000000,
SHIFT_RIGHT = 0b00000100,
BOOSTER_OFF = 0b00000000,
BOOSTER_ON = 0b00000010,
RESET_SOFT = 0b00000000,
RESET_NONE = 0b00000001
};
enum PWR_FLAGS_1 {
VDS_EXTERNAL = 0b00000000,
VDS_INTERNAL = 0b00000010,
VDG_EXTERNAL = 0b00000000,
VDG_INTERNAL = 0b00000001
};
enum PWR_FLAGS_2 {
VCOM_VD = 0b00000000,
VCOM_VG = 0b00000100,
VGHL_16V = 0b00000000,
VGHL_15V = 0b00000001,
VGHL_14V = 0b00000010,
VGHL_13V = 0b00000011
};
enum BOOSTER_FLAGS {
START_10MS = 0b00000000,
START_20MS = 0b01000000,
START_30MS = 0b10000000,
START_40MS = 0b11000000,
STRENGTH_1 = 0b00000000,
STRENGTH_2 = 0b00001000,
STRENGTH_3 = 0b00010000,
STRENGTH_4 = 0b00011000,
STRENGTH_5 = 0b00100000,
STRENGTH_6 = 0b00101000,
STRENGTH_7 = 0b00110000,
STRENGTH_8 = 0b00111000,
OFF_0_27US = 0b00000000,
OFF_0_34US = 0b00000001,
OFF_0_40US = 0b00000010,
OFF_0_54US = 0b00000011,
OFF_0_80US = 0b00000100,
OFF_1_54US = 0b00000101,
OFF_3_34US = 0b00000110,
OFF_6_58US = 0b00000111
};
enum PFS_FLAGS {
FRAMES_1 = 0b00000000,
FRAMES_2 = 0b00010000,
FRAMES_3 = 0b00100000,
FRAMES_4 = 0b00110000
};
enum TSE_FLAGS {
TEMP_INTERNAL = 0b00000000,
TEMP_EXTERNAL = 0b10000000,
OFFSET_0 = 0b00000000,
OFFSET_1 = 0b00000001,
OFFSET_2 = 0b00000010,
OFFSET_3 = 0b00000011,
OFFSET_4 = 0b00000100,
OFFSET_5 = 0b00000101,
OFFSET_6 = 0b00000110,
OFFSET_7 = 0b00000111,
OFFSET_MIN_8 = 0b00001000,
OFFSET_MIN_7 = 0b00001001,
OFFSET_MIN_6 = 0b00001010,
OFFSET_MIN_5 = 0b00001011,
OFFSET_MIN_4 = 0b00001100,
OFFSET_MIN_3 = 0b00001101,
OFFSET_MIN_2 = 0b00001110,
OFFSET_MIN_1 = 0b00001111
};
enum PLL_FLAGS {
// other frequency options exist but there doesn't seem to be much
// point in including them - this is a fair range of options...
HZ_29 = 0b00111111,
HZ_33 = 0b00111110,
HZ_40 = 0b00111101,
HZ_50 = 0b00111100,
HZ_67 = 0b00111011,
HZ_100 = 0b00111010,
HZ_200 = 0b00111001
};
#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;
static uint8_t __xdata dispLutSize = 0;
static bool __xdata drawDirection = false;
static bool __xdata isInited = false;
bool __xdata epdGPIOActive = false;
#define LUT_BUFFER_SIZE 128
uint8_t waveformbuffer[LUT_BUFFER_SIZE];
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 / 1000);
P2_0 = 0;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 1;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
}
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)); // select and bs1 as output
P1_2 = 0; // select 4-wire SPI / BS1 = low
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() {
shortCommand1(CMD_VCOM_INTERVAL, 0x17);
shortCommand1(CMD_VCOM_DC_SETTING, 0x00);
// shortCommand(CMD_POWER_OFF);
// epdWaitRdy();
shortCommand1(CMD_DEEP_SLEEP, 0xA5);
isInited = false;
}
static void epdDrawDirection(bool direction) {
if (direction == drawDirection) return;
drawDirection = direction;
uint8_t psr_setting = RES_128x296 | FORMAT_BWR | BOOSTER_ON | RESET_NONE | LUT_OTP | SHIFT_RIGHT;
if (drawDirection) {
psr_setting |= SCAN_DOWN;
} else {
psr_setting |= SCAN_UP;
}
shortCommand1(CMD_PANEL_SETTING, psr_setting);
}
void epdSetup() {
epdReset();
drawDirection = false;
epdDrawDirection(true);
commandBegin(CMD_POWER_SETTING);
epdSend(VDS_INTERNAL | VDG_INTERNAL);
epdSend(VCOM_VD | VGHL_16V);
epdSend(0b101011);
epdSend(0b101011);
epdSend(0b101011);
commandEnd();
shortCommand(CMD_POWER_ON);
epdWaitRdy();
commandBegin(CMD_BOOSTER_SOFT_START);
epdSend(START_10MS | STRENGTH_3 | OFF_6_58US);
epdSend(START_10MS | STRENGTH_3 | OFF_6_58US);
epdSend(START_10MS | STRENGTH_3 | OFF_6_58US);
commandEnd();
commandBegin(CMD_RESOLUTION_SETING);
epdSend(SCREEN_WIDTH);
epdSend(SCREEN_HEIGHT >> 8);
epdSend(SCREEN_HEIGHT & 0xFF);
commandEnd();
shortCommand1(CMD_POWER_OFF_SEQUENCE, FRAMES_1);
shortCommand1(CMD_TEMPERATURE_SELECT, TEMP_INTERNAL | OFFSET_0);
shortCommand1(CMD_TCON_SETTING, 0x22);
shortCommand1(CMD_VCOM_INTERVAL, 0x8d); // 0x87
shortCommand1(CMD_PLL_CONTROL, HZ_200);
epdWaitRdy();
shortCommand(CMD_POWER_ON);
epdWaitRdy();
}
static uint8_t epdGetStatus() {
uint8_t sta;
commandReadBegin(0x2F);
sta = epdReadByte();
commandReadEnd();
return sta;
}
uint16_t epdGetBattery(void) {
return 0;
}
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) {
// implement alternative LUTs here. Currently just reset the watchdog to two minutes,
// to ensure it doesn't reset during the much longer bootup procedure
wdtSetResetVal(0xFF8E797F); // 120 s
wdtOn();
return;
}
void setWindowXY(uint16_t xstart, uint16_t xend, uint16_t ystart, uint16_t yend) {
shortCommand(CMD_PARTIAL_IN);
commandBegin(CMD_PARTIAL_WINDOW);
epdSend((xstart / 8) << 3);
epdSend(((xend / 8 - 1) << 3) | 0x07);
epdSend(ystart >> 8);
epdSend(ystart & 0xFF);
epdSend((yend - 1) >> 8);
epdSend((yend - 1) & 0xff);
epdSend(0x01);
commandEnd();
}
void setColorMode(uint8_t red, uint8_t bw) {
return;
}
void clearScreen() {
shortCommand(CMD_PARTIAL_OUT);
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
for (uint16_t c = 0; c < ((1UL * SCREEN_HEIGHT * SCREEN_WIDTH) / 8); c++) {
epdSend(0x00);
}
commandEnd();
epdWaitRdy();
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
for (uint16_t c = 0; c < ((1UL * SCREEN_HEIGHT * SCREEN_WIDTH) / 8); c++) {
epdSend(0x00);
}
commandEnd();
}
void draw() {
shortCommand(CMD_DISPLAY_REFRESH);
epdWaitRdy();
}
void drawNoWait() {
shortCommand(CMD_DISPLAY_REFRESH);
}
void drawWithSleep() {
shortCommand(CMD_DISPLAY_REFRESH);
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 beginFullscreenImage() {
shortCommand(CMD_PARTIAL_OUT);
epdDrawDirection(false);
// shortCommand1(CMD_DATA_ENTRY_MODE, 3);
// setPosXY(0, 0);
}
void beginWriteFramebuffer(bool color) {
if (color == EPD_COLOR_RED) {
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
} else {
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
}
epdDeselect();
}
void endWriteFramebuffer() {
commandEnd();
}
void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color) __reentrant {
// this function is very badly hurt by the switch to UC8151, taking up LOTS of valuable idata space. Only defining variables
// as static, or the function as reentrant (relegating variables to the stack) seemed to fix the idata issue. Fix me, or put me out of my misery...
uint16_t xsize = bmp[0] / 8;
if (bmp[0] % 8) xsize++;
uint16_t ysize = bmp[1];
uint16_t size = xsize * bmp[1];
// shortCommand1(CMD_DATA_ENTRY_MODE, 3);
bmp += 2;
uint16_t c = 0;
uint16_t curY = y;
while (1) {
if (c % xsize == 0) {
commandEnd();
if (drawDirection) {
setWindowXY(x, x + xsize * 8, SCREEN_HEIGHT - curY - 1, SCREEN_HEIGHT - curY);
} else {
setWindowXY(x, x + xsize * 8, curY - 1, curY);
}
curY++;
if (color) {
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
} else {
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
}
}
epdSend(*(bmp++));
c++;
if (!size--) break;
}
commandEnd();
shortCommand(CMD_PARTIAL_OUT);
}
void printBarcode(const uint8_t* string, uint16_t x, uint16_t y) {
setWindowXY(x, x + 8, SCREEN_HEIGHT - y, SCREEN_HEIGHT);
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
struct BarcodeInfo __xdata bci = {
.str = string,
};
while (!barcodeIsDone(&bci)) {
if (barcodeNextBar(&bci)) {
epdSend(0xFF);
} else {
epdSend(0x00);
}
}
commandEnd();
shortCommand(CMD_PARTIAL_OUT);
}
// 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) {
setWindowXY(x, x + 32 + extra, SCREEN_HEIGHT - y, SCREEN_HEIGHT);
// setPosXY(x, y);
} else {
setWindowXY(x, x + 16 + extra, SCREEN_HEIGHT - y, SCREEN_HEIGHT);
// setPosXY(x, y);
}
// shortCommand1(CMD_DATA_ENTRY_MODE, 1); // was 3
} else {
if (epdCharSize == 2) {
x /= 2;
x *= 2;
setWindowXY(x, SCREEN_WIDTH, y, y + 32);
} else {
setWindowXY(x, SCREEN_WIDTH, y, y + 16);
}
// setPosXY(x, y);
fontCurXpos = x;
// setWindowXY(x, SCREEN_WIDTH);
// shortCommand1(CMD_DATA_ENTRY_MODE, 7);
memset(rbuffer, 0, 32);
}
if (color) {
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM2);
} else {
commandBegin(CMD_DISPLAY_START_TRANSMISSION_DTM1);
}
}
void epdPrintEnd() {
if (!directionY && ((fontCurXpos % 8) != 0)) {
for (uint8_t i = 0; i < (16 * epdCharSize); i++) {
epdSend(rbuffer[i]);
}
}
commandEnd();
shortCommand(CMD_PARTIAL_OUT);
epdDrawDirection(true);
}
extern uint8_t __xdata blockXferBuffer[];

74
tag_fw/board/uc8151.h Normal file
View File

@@ -0,0 +1,74 @@
#ifndef _JSCREEN_H_
#define _JSCREEN_H_
#include <stdbool.h>
#include <stdint.h>
#define epdSend spiTXByte
#define EPD_DIRECTION_X false
#define EPD_DIRECTION_Y true
#define EPD_SIZE_SINGLE false
#define EPD_SIZE_DOUBLE true
#define EPD_COLOR_RED true
#define EPD_COLOR_BLACK false
#define EPD_LOAD_CUSTOM_LUT true
#define EPD_LOAD_OTP_LUT false
#define EPD_MODE_NORMAL 0x00
#define EPD_MODE_INVERT 0x08
#define EPD_MODE_IGNORE 0x04
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_FAST 3
#define epdSelect() \
do { \
P1_7 = 0; \
} while (0)
#define epdDeselect() \
do { \
P1_7 = 1; \
} while (0)
void epdSetup();
void epdEnterSleep();
uint16_t epdGetBattery();
void epdConfigGPIO(bool setup);
extern bool __xdata epdGPIOActive;
void setColorMode(uint8_t red, uint8_t bw) ;
void clearWindow(bool color);
void clearScreen();
void draw();
void drawNoWait();
void drawWithSleep();
void epdWaitRdy();
void beginFullscreenImage();
void beginWriteFramebuffer(bool color);
void endWriteFramebuffer();
void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color) __reentrant;
void printBarcode(const uint8_t* string, uint16_t x, uint16_t y);
void selectLUT(uint8_t lut);
void ByteDecode(uint8_t byte);
void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool red);
void epdPrintEnd();
void beginFullscreenImage();
void beginWriteFramebuffer(bool color);
void lutTest();
void epdTest();
// for printf.c
void writeCharEPD(uint8_t c);
#endif

View File

@@ -0,0 +1 @@
#include "../ssd1619.c"

View File

@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "../ssd1619.h"
#define SCREEN_WIDTH 152
#define SCREEN_HEIGHT 152
@@ -22,4 +23,4 @@
#define SCREEN_DATA_PASSES 2
#endif
#endif

View File

@@ -0,0 +1 @@
#include "../boardZBS29common.c"

View File

@@ -0,0 +1,28 @@
#ifndef _BOARD_H_
#define _BOARD_H_
#include <stdint.h>
#include "proto.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)
//hw types
#define HW_TYPE SOLUM_29_033
#include "../boardCommon.h"
#endif

View File

@@ -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

View File

@@ -0,0 +1 @@
#include "../uc8151.c"

View File

@@ -0,0 +1,25 @@
#ifndef _SCREEN_H_
#define _SCREEN_H_
#include <stdbool.h>
#include <stdint.h>
#include "../uc8151.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 5 //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_BWR_6colors
#define SCREEN_DATA_PASSES 2
#endif

View File

@@ -0,0 +1 @@
#include "../ssd1619.c"

View File

@@ -3,6 +3,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "../ssd1619.h"
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 296
@@ -21,4 +23,4 @@
#define SCREEN_DATA_PASSES 2
#endif
#endif

View File

@@ -0,0 +1 @@
#include "../ssd1619.c"

View File

@@ -3,6 +3,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "../ssd1619.h"
#define SCREEN_WIDTH 400
#define SCREEN_HEIGHT 300
@@ -22,4 +24,4 @@
#define SCREEN_DATA_PASSES 2
#endif
#endif

View File

@@ -1,11 +1,18 @@
#!/bin/bash
make clean
make BUILD=zbs154v033 CPU=8051 SOC=zbs243
mv main.bin fw154.bin
make clean
make BUILD=zbs29v033 CPU=8051 SOC=zbs243
mv main.bin fw29.bin
make clean
make BUILD=zbs42v033 CPU=8051 SOC=zbs243
mv main.bin fw42.bin
make clean
make BUILD=zbs29_uc8151 CPU=8051 SOC=zbs243
mv main.bin fw29-uc8151.bin
make clean

View File

@@ -5,7 +5,7 @@
#include <stdio.h>
#include "board.h"
#include "epd.h"
#include "screen.h"
#include "uart.h"
#include "zbs243.h"

View File

@@ -6,7 +6,7 @@
#include "board.h"
#include "cpu.h"
#include "eeprom.h"
#include "epd.h"
#include "screen.h"
#include "printf.h"
#include "proto.h"
#include "screen.h"
@@ -433,6 +433,9 @@ void drawImageAtAddress(uint32_t addr, uint8_t lut) {
pr(" complete.\n");
break;
default: // prevent drawing from an unknown file image type
pr("Image with type 0x%02X was requested, but we don't know what to do with that currently...\n", eih->dataType);
return;
}
addOverlay();
drawWithSleep();

Binary file not shown.

BIN
tag_fw/fw29-uc8151.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

127
tag_fw/i2cdevices.c Normal file
View File

@@ -0,0 +1,127 @@
// 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];
bool supportsNFCWake() {
P1PULL |= (1 << 3);
timerDelay(33300); // wait 25 ms
uint32_t pcount = 0;
P1PULL &= ~(1 << 3);
while (P1_3 && pcount < 10000) {
pcount++;
}
if (pcount < 10000) {
// P1_3 (Field Detect) dropped to 'low' pretty fast, this means the load on this pin is high
pr("This tag currently does not support NFC wake, load on the FD pin (P1.3) is pretty high.\nOn some boards, a pull-up resistor backpowers the NFC IC. Consider removing it!\n");
return false;
} else {
// No reason to believe this pin is currently loaded down severely
return true;
}
}
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;
}

10
tag_fw/i2cdevices.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef _I2CDEV_H_
#define _I2CDEV_H_
#include <stdint.h>
bool supportsNFCWake();
void loadURLtoNTag();
void loadRawNTag(uint16_t blocksize);
bool i2cCheckDevice(uint8_t address);
#endif

View File

@@ -8,11 +8,13 @@
#include "asmUtil.h"
#include "comms.h" // for mLastLqi and mLastRSSI
#include "eeprom.h"
#include "epd.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"
@@ -22,7 +24,7 @@
// #define DEBUG_MODE
void displayLoop() {
powerUp(INIT_BASE | INIT_UART | INIT_GPIO);
powerUp(INIT_BASE | INIT_UART);
pr("Splash screen\n");
powerUp(INIT_EPD);
@@ -141,7 +143,7 @@ uint8_t channelSelect() { // returns 0 if no accesspoints were found
return highestSlot;
}
void mainProtocolLoop(void) {
void main() {
// displayLoop(); // remove me
setupPortsInitial();
powerUp(INIT_BASE | INIT_UART);
@@ -176,15 +178,33 @@ void mainProtocolLoop(void) {
}
}
pr("BOOTED> %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
pr("BOOTED> %d.%d.%d%s\n", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
#ifdef HAS_BUTTON
capabilities |= CAPABILITY_HAS_WAKE_BUTTON;
#endif
powerUp(INIT_I2C);
if (i2cCheckDevice(0x55)) {
powerDown(INIT_I2C);
capabilities |= CAPABILITY_HAS_NFC;
if (supportsNFCWake()) {
pr("This board supports NFC wake!\n");
capabilities |= CAPABILITY_NFC_WAKE;
}
} else {
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]);
pr("%02X%02X\n", mSelfMac[6], mSelfMac[7]);
powerUp(INIT_EPD_VOLTREADING | INIT_TEMPREADING | INIT_EEPROM);
powerUp(INIT_RADIO); // load down the battery using the radio to get a good voltage reading
powerUp(INIT_EPD_VOLTREADING | INIT_TEMPREADING);
powerDown(INIT_RADIO);
powerUp(INIT_EEPROM);
// get the highest slot number, number of slots
initializeProto();
powerDown(INIT_EEPROM);
@@ -222,7 +242,9 @@ void mainProtocolLoop(void) {
if ((longDataReqCounter > LONG_DATAREQ_INTERVAL) || wakeUpReason != WAKEUP_REASON_TIMED) {
// check if we should do a voltage measurement (those are pretty expensive)
if (voltageCheckCounter == VOLTAGE_CHECK_INTERVAL) {
powerUp(INIT_RADIO); // load down the battery using the radio to get a good reading
powerUp(INIT_TEMPREADING | INIT_EPD_VOLTREADING);
powerDown(INIT_RADIO);
voltageCheckCounter = 0;
} else {
powerUp(INIT_TEMPREADING);
@@ -234,6 +256,7 @@ void mainProtocolLoop(void) {
// Check if we were already displaying an image
if (curImgSlot != 0xFF) {
powerUp(INIT_EEPROM | INIT_EPD);
wdt60s();
drawImageFromEeprom(curImgSlot);
powerDown(INIT_EEPROM | INIT_EPD);
} else {
@@ -296,7 +319,9 @@ void mainProtocolLoop(void) {
} else {
// not associated
if (((scanAttempts != 0) && (scanAttempts % VOLTAGEREADING_DURING_SCAN_INTERVAL == 0)) || (scanAttempts > (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS))) {
powerUp(INIT_RADIO); // load down the battery using the radio to get a good reading
powerUp(INIT_EPD_VOLTREADING);
powerDown(INIT_RADIO);
}
// try to find a working channel
powerUp(INIT_RADIO);
@@ -305,6 +330,7 @@ void mainProtocolLoop(void) {
if ((!currentChannel && !noAPShown) || (lowBattery && !lowBatteryShown) || (scanAttempts == (INTERVAL_1_ATTEMPTS + INTERVAL_2_ATTEMPTS - 1))) {
powerUp(INIT_EPD);
wdt60s();
if (curImgSlot != 0xFF) {
powerUp(INIT_EEPROM);
drawImageFromEeprom(curImgSlot);
@@ -332,7 +358,3 @@ void mainProtocolLoop(void) {
}
}
}
void main(void) {
mainProtocolLoop();
}

View File

@@ -12,11 +12,12 @@
#include "cpu.h"
#include "drawing.h"
#include "eeprom.h"
#include "epd.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"
@@ -38,10 +39,12 @@ bool __xdata lowBattery = false;
uint16_t __xdata longDataReqCounter = 0;
uint16_t __xdata voltageCheckCounter = 0;
uint8_t __xdata capabilities = 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 +121,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 +194,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) {
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 +227,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) {
@@ -232,13 +261,23 @@ void doSleep(const uint32_t __xdata t) {
#ifdef HAS_BUTTON
// Button setup on TEST pin 1.0 (input pullup)
P1FUNC &= ~(1 << 0);
P1DIR |= (1 << 0);
P1PULL |= (1 << 0);
P1LVLSEL |= (1 << 0);
P1INTEN = (1 << 0);
P1CHSTA &= ~(1 << 0);
P1FUNC &= ~(1 << 0);
P1DIR |= (1 << 0);
P1PULL |= (1 << 0);
P1LVLSEL |= (1 << 0);
P1INTEN = (1 << 0);
P1CHSTA &= ~(1 << 0);
#endif
if (capabilities & CAPABILITY_NFC_WAKE) {
P1FUNC &= ~(1 << 3);
P1DIR |= (1 << 3);
P1PULL |= (1 << 3);
P1LVLSEL |= (1 << 3);
P1INTEN = (1 << 3);
P1CHSTA &= ~(1 << 3);
}
// sleepy
sleepForMsec(t);
#ifdef HAS_BUTTON
@@ -247,6 +286,11 @@ void doSleep(const uint32_t __xdata t) {
wakeUpReason = WAKEUP_REASON_GPIO;
P1CHSTA &= ~(1 << 0);
}
if (P1CHSTA && (1 << 3) && capabilities & CAPABILITY_NFC_WAKE) {
wakeUpReason = WAKEUP_REASON_NFC;
P1CHSTA &= ~(1 << 3);
}
#endif
}

View File

@@ -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
@@ -36,6 +36,13 @@
wdtOn(); \
} while (0)
#define wdt120s() \
do { \
wdtSetResetVal(0xFF8E797F); \
wdtOn(); \
} while (0)
// power saving algorithm
#define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA)
#define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current
@@ -75,6 +82,8 @@ extern void initPowerSaving(const uint16_t initialValue);
extern uint8_t __xdata wakeUpReason;
extern uint8_t __xdata capabilities;
extern uint16_t __xdata nextCheckInFromAP;
extern uint8_t __xdata dataReqLastAttempt;
extern int8_t __xdata temperature;

View File

@@ -111,28 +111,32 @@ 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 CAPABILITY_NFC_WAKE 0x80
#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;

View File

@@ -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};
@@ -192,6 +192,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 +480,7 @@ static bool getDataBlock(const uint16_t blockSize) {
partsThisBlock = BLOCK_MAX_PARTS;
memset(curBlock.requestedParts, 0xFF, BLOCK_REQ_PARTS_BYTES);
} else {
partsThisBlock = 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 +699,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 +793,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;
}

View File

@@ -10,7 +10,6 @@
#include "board.h"
#include "comms.h"
#include "cpu.h"
#include "epd.h"
#include "font.h"
#include "lut.h"
#include "powermgt.h"
@@ -21,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;
@@ -30,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)
@@ -84,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();
@@ -96,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();
@@ -107,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]);
@@ -127,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();
@@ -422,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);