This commit is contained in:
Jelmer
2022-12-26 22:33:49 +01:00
commit f34586ecae
153 changed files with 19383 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

25
.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
*.asm
*.lst
*.rst
*.sym
*.ihx
*.map
*.mem
*.adb
*.cdb
*.omf
*.bak
*.rel
*.asm
*.lst
*.rst
*.sym
*.map
*.mem
*.ihx
*.adb
*.cdb
*.omf
*.bin
*.lk
*.o

76
README.md Normal file
View File

@@ -0,0 +1,76 @@
# solum-esl-alternative-proto
## ⚠THIS IS NOT PRODUCTION READY!⚠️⚠️⚠️
This is not a final, polished codebase. Not by a long shot. It's a complicated system, and it is not unlikely to be a dead end. There are drawbacks to a slotted protocol, it's hard to debug, and it might not work all that well in unfavourable RF conditions. You'll need some knowledge on the use of these tags. A very good place to start is here: https://github.com/atc1441/ZBS_Flasher. You'll need to fix issues yourself, troubleshoot stuff. Once again: this is not for everyone.
This is an alternative protocol for the ZBS243-based Electronic Shelf Labels - ESL / price tags by Solum / Samsung.
### Compatibility
It is currently compatible with the following tags:
* 4.2"
* 2.9"
* 1.54"
### Aims
- Low power (currently around 8µA with 30 second latency)
- Low latency (tags can check for new data every 30 seconds)
- High transfer speeds - It can do about 5kbyte/s in favorable RF conditions. This allows for lower power
- RF-spectrum friendly - We don't need to acknowledge EVERY packet, and we don't need to transfer data we already have
The entire setup requires a few tags, and an ESP32. A (preferably, but not necessarily) broken tag is used as an 802.15.4 radio for the ESP32. You'll need a ZBS_Flasher in order to flash both the AP with its firmware, and the tags. Using the 'mac' option on ZBS_Flasher makes sure a tag flashed with a custom firmware has a valid mac address; it used the stock mac address assigned to the tag if it hasn't been flashed before. If you want to set it yourself, you can edit the mac address in the infopage. The AP expects a tag with a mac that starts with 00:00, followed by 6 bytes. The MAC-address also needs to be set on the AP-tag.
Once flashed, you can hook the AP tag up to the ESP32 by connecting the tags serial lines to some free pins. Make sure you set the pins in settings.h, so that the ESP32 can communicate with it. This can be validated by checking the ESP32 debug output; you should see 'sync burst' displayed every 30 seconds
You can access the ESP32 with any web browser after connecting it to your WiFi Network. The file browser is located at <ip>/edit. For sending data to tags, you'll need to upload the information in 'data' to the ESP32's filesystem. After uploading, you can access the status screen at <ip>/index.html. If everything is working, you should be able to see tags synchronising to the network. After uploading a suitable .bmp file to the filesystem, this file can be sent to the tag by entering it's 6-byte mac address and filename.
## The synchronized/slotted protocol explained, kinda. This is simplified, the reality is even more convoluted
- Every 30 seconds, the AP sends a 250ms long 'burst' of packets, each marked with a sequential 802.15.4 sequence number. The tag, when synchronized to the network, listens for a short while every 30 seconds, to receive exactly one packet. Based on the sequence number received by the tag, the tag can calculate clock drift and window-offset to the 30 seconds interval, in order to remain synchronized.
- At bootup, the tag will try to find an access point, and get timing information. It will start a calibration cycle in order to characterize the difference between the AP's clock and the internal (RC) oscillator. This allows for a fairly precise reception of the sync burst. After initial calibration, the tag will slowly adjust its calibration value to compensate for drift in the RC oscillator frequency.
- When synced, the tag only listens for a -very- short period of time, which allows for both low power use and low latency.
- The ESP32 can send a 'pendingData' message to the AP, indicating it wants a specific tag to check-in to the AP.
- AP adds this pendingData struc to a queue, and adds these tags to the sync-burst
- The sync-burst packets contain up to (currently) 2 mac addresses of tags that the AP wants to talk to, and an 'offset'. Tag receives a MAC address, see if it matches its own, and when it does, it sleeps for [offset] ms. This allows for minimal power consumption and maximum throughput, as the AP determines which tags are allowed to talk.
- When addressed by AP in the sync-burst, the tag wakes up after the offset period, and requests information from the AP. The AP responds with some metadata, such as size, MD5 checksum, and data type.
- The tag checks if this information is already downloaded to EEPROM, or is already displayed. If this is the case, the transfer is immediately cancelled by issuing a 'transfer complete' packet to the AP.
- The tag then proceeds to request data in 'blocks' of 4096 bytes. The AP responds with an ACK on the request, and specifies how long it will spend to gather the data. The tag sleeps until the AP will send the data
- The AP requests its block-buffer to be filled by the ESP32, specifying MD5 and blockID
- Datablock is sent by the AP, which will take about 42 packets for a full block. The tag will keep track of which blocks it has seen
- After a block has been received with missing parts, the tag will request the same block, with a bitmask of blocks that are missing
- The AP responds with the parts as requested by the tag. If there aren't too many blocks requested, the AP will fill the block with duplicate parts, to increase the chance of a successful transmission
- After a full block has been received, the tag will do a simple checksum over all received data. If the checksum matches, the 4K of data is stored in EEPROM storage for later use
- The tag will now either request the next block, or do a full re-request if the checksum failed
- If all blocks are received, the tag will send a 'transfer complete'.
- The AP will report the completed transfer to the ESP32, and removes the pendingData for this transfer from the queue
## Todo:
- 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:
- More reliable serial comms (sometimes bytes are dropped)
- Include source mac with blockrequest struct
### ESP32:
- Do more with status info as sent by the tags
## Known issues:
- The RC oscillator has some jitter, especially on longer sleep times. I find it difficult to have the tag sleep for much longer than 30s without losing synchronization to the network
- For some reason, the screen needs to be reset and put to sleep -EVERY TIME- the tag wakes up. This is a relatively slow process; it would really help if we could find out what causes this. Some glitch on the reset line of the EPD would be my guess...
- The ZBS CPU should be able to sleep during the EPD-draw command; however, this currently (for some reason) increases the sleep-current-draw
- Some tags work better as AP's than others. Your range may suck. The boards on these tags are tiny and fragile. For instance, a dab of hot-glue on a board is enough to warp it pretty severely, and will damage the components that are soldered on there. Reportedly, segmented-display solum tags work well.
## Hints and excuses:
- I'm sorry if reading this spaghetti code makes you lose your mind. <sub>'Of all the things I've lost, I miss my mind the most'</sub> I know it is pretty unreadable. I could blame SDCC for a lot of things, but it's mostly me.
- There is no warranty whatsoever. Nothing. Not implied or otherwise suggested. This code isn't fit for anything. Please don't use this code to do nasty things.
- Do a ```make clean``` between building for different boards. It really helps!
- This repo builds on SDCC 4.2.0 for Linux. Different SDCC versions can behave VERY differently.
- I am happy and honored to accept pull requests! But please, no code/indent style changes :)
## Credits
### Large parts of this repo are based on code written by, and wouldn't be possible without the hard work of:
- dmitry.gr
- atc1441
Hats off to these legends!

25
ap_fw/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
*.asm
*.lst
*.rst
*.sym
*.ihx
*.map
*.mem
*.adb
*.cdb
*.omf
*.bak
*.rel
*.asm
*.lst
*.rst
*.sym
*.map
*.mem
*.ihx
*.adb
*.cdb
*.omf
*.bin
*.lk
*.o

7
ap_fw/LICENSE.txt Normal file
View File

@@ -0,0 +1,7 @@
This E-Paper firmware mod is based on Dmitry Grinbergs firmware which is available here:
http://dmitry.gr/?r=05.Projects&proj=29.%20eInk%20Price%20Tags
Many thanks to Dmitry for sharing such a wonderful work !
The license is simple:
This code/data/waveforms are free for use in hobby and other non-commercial products. For commercial use, Dmitry: licensing@dmitry.gr

55
ap_fw/Makefile Normal file
View File

@@ -0,0 +1,55 @@
BUILD ?= zbs29v033
#file containing main() must be first!
SOURCES += main.c eeprom.c
SOURCES += comms.c
all: #make sure it is the first target
include board/$(BUILD)/make.mk
include soc/$(SOC)/make.mk
include cpu/$(CPU)/make.mk
FLAGS += -Iboard/$(BUILD)
FLAGS += -Isoc/$(SOC)
FLAGS += -Icpu/$(CPU)
SOURCES += cpu/$(CPU)/cpu.c
SOURCES += board/$(BUILD)/board.c
FLAGS += -I.
OBJS = $(patsubst %.c,%.$(OBJFILEEXT),$(SOURCES))
all: $(TARGETS)
%.$(OBJFILEEXT): %.c
$(CC) -c $^ $(FLAGS) -o $@
main.ihx: $(OBJS)
rm -f $@
$(CC) $^ $(FLAGS)
main.elf: $(OBJS)
$(CC) $(FLAGS) -o $@ $^
%.bin: %.ihx
objcopy $^ $@ -I ihex -O binary
.PHONY: clean
clean:
rm -f $(patsubst %.c,%.asm,$(SOURCES))
rm -f $(patsubst %.c,%.lst,$(SOURCES))
rm -f $(patsubst %.c,%.rst,$(SOURCES))
rm -f $(patsubst %.c,%.sym,$(SOURCES))
rm -f $(patsubst %.c,%.map,$(SOURCES))
rm -f $(patsubst %.c,%.mem,$(SOURCES))
rm -f $(patsubst %.c,%.ihx,$(SOURCES))
rm -f $(patsubst %.c,%.adb,$(SOURCES))
rm -f $(patsubst %.c,%.adb,$(SOURCES))
rm -f $(patsubst %.c,%.omf,$(SOURCES))
rm -f $(patsubst %.c,%.bin,$(SOURCES))
rm -f $(OBJS)

16
ap_fw/adc.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _ADC_H_
#define _ADC_H_
#include <stdint.h>
extern uint16_t __xdata mAdcSlope;
extern uint16_t __xdata mAdcIntercept;
uint16_t adcSampleBattery(void); //in mV
int8_t adcSampleTemperature(void); //in degrees C
#endif

114
ap_fw/asmUtil.h Normal file
View File

@@ -0,0 +1,114 @@
#ifndef _ASM_UTIL_H_
#define _ASM_UTIL_H_
#include <stdint.h>
#include "cpu.h"
//SDCC may have uint64_t support, but it is so shitty, we're better off not using it
//sdcc is brain dead when compiling multiplication, so we write our own asm code to make it better...le sigh...
//SDCC also has issues managing xdata memory ops, we write our own
#pragma callee_saves u64_copy
#pragma callee_saves u64_isLt
#pragma callee_saves u64_isEq
#pragma callee_saves u64_sub
#pragma callee_saves u64_add
#pragma callee_saves u64_inc
#pragma callee_saves u64_dec
#pragma callee_saves xMemSet
#pragma callee_saves xMemEqual
#pragma callee_saves xMemEqual4
#pragma callee_saves xMemCopy
#pragma callee_saves xMemCopyShort
#pragma callee_saves xMemCopy8
#pragma callee_saves xStrLen
#pragma callee_saves rngGen
#pragma callee_saves mathPrvMul8x8
#pragma callee_saves mathPrvMul16x8
#pragma callee_saves mathPrvMul16x16
#pragma callee_saves mathPrvMul32x8
#pragma callee_saves mathPrvDiv32x8
#pragma callee_saves mathPrvDiv32x16
#pragma callee_saves mathPrvMod32x16
#pragma callee_saves mathPrvDiv16x8
#pragma callee_saves mathPrvMod16x8
#pragma callee_saves mathPrvCopyPostinc
#pragma callee_saves mathPrvI16Asr1
#pragma callee_saves mathPrvU8bitswap
#pragma callee_saves charsPrvDerefAndIncGenericPtr //return *(*generic_charPtr)++
//it saddens me that i need these....but i do
#pragma callee_saves mathPrvU16from2xU8
#pragma callee_saves mathPrvU32from4xU8
#pragma callee_saves u32minusU16
#pragma callee_saves u32plusU16
#pragma callee_saves u32Nonzero
#pragma callee_saves i32Negative
__bit u32minusU16(uint32_t __xdata *u32, uint16_t u16) __reentrant; //sets carry
__bit u32plusU16(uint32_t __xdata *u32, uint16_t u16) __reentrant; //sets carry
uint8_t u32Nonzero(uint32_t __xdata *u32) __reentrant;
__bit i32Negative(uint32_t __xdata *u32) __reentrant;
void u64_copy(uint64_t __xdata *dst, const uint64_t __xdata *src) __reentrant;
void u64_copyFromCode(uint64_t __xdata *dst, const uint64_t __code *src) __reentrant;
__bit u64_isLt(const uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
__bit u64_isEq(const uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_sub(uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_add(uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_and(uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_inc(uint64_t __xdata *dst) __reentrant;
void u64_dec(uint64_t __xdata *dst) __reentrant;
#define U64FMT "%04x%04x%04x%04x"
#define U64CVT(v) ((uint16_t __xdata*)&v)[3], ((uint16_t __xdata*)&v)[2], ((uint16_t __xdata*)&v)[1], ((uint16_t __xdata*)&v)[0]
int16_t mathPrvI16Asr1(int16_t val) __reentrant;
uint16_t mathPrvMul8x8(uint8_t a, uint8_t b) __reentrant;
uint32_t mathPrvMul16x8(uint16_t a, uint8_t b) __reentrant;
uint32_t mathPrvMul16x16(uint16_t a, uint16_t b) __reentrant;
uint32_t mathPrvMul32x8(uint32_t a, uint8_t b) __reentrant;
uint32_t mathPrvDiv32x8(uint32_t num, uint8_t denom) __reentrant;
uint32_t mathPrvDiv32x16(uint32_t num, uint16_t denom) __reentrant;
uint16_t mathPrvMod32x16(uint32_t num, uint16_t denom) __reentrant;
uint16_t mathPrvDiv16x8(uint16_t num, uint8_t denom) __reentrant;
uint8_t mathPrvMod16x8(uint16_t num, uint8_t denom) __reentrant;
uint8_t mathPrvU8bitswap(uint8_t val) __reentrant;
uint16_t mathPrvU16from2xU8(uint8_t hi, uint8_t lo) __reentrant;
uint32_t mathPrvU32from4xU8(uint8_t hi, uint8_t midhi, uint8_t midlo, uint8_t lo) __reentrant;
char charsPrvDerefAndIncGenericPtr(const char * __xdata* __xdata str);
void xMemSet(void __xdata* mem, uint8_t val, uint16_t num) __reentrant;
__bit xMemEqual(const void __xdata* memA, const void __xdata* memB, uint8_t num) __reentrant;
__bit xMemEqual4(const void __xdata* memA, const void __xdata* memB) __reentrant;
void xMemCopy(void __xdata* dst, const void __xdata* src, uint16_t num) __reentrant;
void xMemCopyShort(void __xdata* dst, const void __xdata* src, uint8_t num) __reentrant;
uint16_t xStrLen(const char __xdata *str) __reentrant;
#define xMemCopy8(_dst, _src) u64_copy((uint64_t __xdata*)(_dst), (const uint64_t __xdata*)(_src))
void mathPrvCopyPostinc(uint32_t __xdata *dst, uint32_t __xdata *src) __reentrant; //*dst = (*src)++
//private
void mathPrvSwapDptrR1R0(void) __reentrant;
#endif

20
ap_fw/board/boardCommon.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef _BOARD_COMMON_H_
#define _BOARD_COMMON_H_
#include <stdint.h>
#pragma callee_saves powerPortsDownForSleep
void powerPortsDownForSleep(void);
#pragma callee_saves boardInit
void boardInit(void);
//early - before most things
#pragma callee_saves boardInitStage2
void boardInitStage2(void);
//late, after eeprom
#pragma callee_saves boardInit
__bit boardGetOwnMac(uint8_t __xdata *mac);
#endif

View File

@@ -0,0 +1,165 @@
#include <stdbool.h>
#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 "adc.h"
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)
{
wdtOff();
//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.7(eink_nCS)
P1FUNC &=~ ((1 << 1) | (1 << 2) | (1 << 7));
P1DIR &= ~((1 << 1) | (1 << 2) | (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 = 0;
uartInit();
spiInit();
}
void boardInitStage2(void)
{
//nothing yet
}
__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 = mScreenRow;
for (i = len; i ; i--)
*dst++ = *src++;
if (!flashWrite(0xfc00, mScreenRow, 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"
);
}

View File

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

View File

@@ -0,0 +1,53 @@
#ifndef _BOARD_H_
#define _BOARD_H_
#include <stdint.h>
#include "spi.h"
#include "uart.h"
//colors for ui messages
#define UI_MSG_MAGNIFY1 1
#define UI_MSG_MAGNIFY2 1
#define UI_MSG_MAGNIFY3 1
#define UI_MSG_BACK_COLOR 4
#define UI_MSG_FORE_COLOR_1 0
#define UI_MSG_FORE_COLOR_2 5
#define UI_MSG_FORE_COLOR_3 5
#define UI_BARCODE_VERTICAL
#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)
//debug uart (enable only when needed, on some boards it inhibits eeprom access)
#define dbgUartOn()
#define dbgUartOff()
#define dbgUartByte uartTx
//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)
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (1)
//hw types
#define HW_TYPE_NORMAL HW_TYPE_29_INCH_ZBS_026
#define HW_TYPE_CYCLING HW_TYPE_29_INCH_ZBS_026_FRAME_MODE
#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,379 @@
#include <stdbool.h>
#include "asmUtil.h"
#include "screen.h"
#include "printf.h"
#include "board.h"
#include "timer.h"
#include "sleep.h"
#include "adc.h"
#include "cpu.h"
#include "spi.h"
uint8_t __xdata mScreenRow[320];
static __bit mInited = false, mPartial;
static uint8_t __xdata mPassNo;
#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
static const uint8_t __code mColorMap[][6] =
{
// colors are: B, DG, G, LG, W, R
// phase 0 (LUTS: B:W:R:G, purpose: BWR, prepare greys)
{
1, 1, 1, 1, 0, 0, // lo plane (B)
},
{
0, 0, 0, 0, 0, 1, // hi plane (R)
}};
#define einkPrvSelect() \
do \
{ \
P1_7 = 0; \
} while (0)
#define einkPrvDeselect() \
do \
{ \
P1_7 = 1; \
} while (0)
// urx pin
#define einkPrvMarkCommand() \
do \
{ \
P2_2 = 0; \
} while (0)
#define einkPrvMarkData() \
do \
{ \
P2_2 = 1; \
} while (0)
#pragma callee_saves einkPrvCmd
static void einkPrvCmd(uint8_t cmd) // sets chip select
{
einkPrvSelect();
einkPrvMarkCommand();
spiByte(cmd);
}
#pragma callee_saves einkPrvData
static void einkPrvData(uint8_t byte)
{
einkPrvMarkData();
spiByte(byte);
}
#pragma callee_saves einkPrvCmdWithOneByte
static void einkPrvCmdWithOneByte(uint16_t vals) // passing in one u16 is better than two params cause SDCC sucks
{
einkPrvCmd(vals >> 8);
einkPrvData(vals);
einkPrvDeselect();
}
#pragma callee_saves einkPrvWaitWithTimeout
static void einkPrvWaitWithTimeout(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)
;
}
#pragma callee_saves einkPrvWaitWithTimeout
static void einkPrvWaitWithTimeoutSleep(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);
P2CHSTA &= 0xfd;
P2INTEN &= 0xfd;
P2FUNC = tmp_P2FUNC;
P2DIR = tmp_P2DIR;
P2PULL = tmp_P2PULL;
P2LVLSEL = tmp_P2LVLSEL;
/*if (!P2_1)
return;
pr("screen timeout\n");
while(1);*/
}
#pragma callee_saves einkPrvReadByte
static uint8_t einkPrvReadByte(void)
{
uint8_t val = 0, i;
P0DIR = (P0DIR & ~(1 << 0)) | (1 << 1);
P0 &= ~(1 << 0);
P0FUNC &= ~((1 << 0) | (1 << 1));
P2_2 = 1;
for (i = 0; i < 8; i++)
{
P0_0 = 1;
__asm__("nop\nnop\nnop\nnop\nnop\n");
val <<= 1;
if (P0_1)
val++;
P0_0 = 0;
__asm__("nop\nnop\nnop\nnop\nnop\n");
}
// set up pins for spi (0.0,0.1,0.2)
P0FUNC |= (1 << 0) | (1 << 1);
return val;
}
#pragma callee_saves einkPrvReadStatus
static uint8_t einkPrvReadStatus(void)
{
uint8_t sta;
einkPrvCmd(0x2f);
sta = einkPrvReadByte();
einkPrvDeselect();
return sta;
}
#pragma callee_saves screenPrvStartSubPhase
static void screenPrvStartSubPhase(__bit redSubphase)
{
einkPrvCmd(0x4e);
einkPrvData(0);
einkPrvDeselect();
einkPrvCmd(0x4f);
einkPrvData(0x00);
einkPrvData(0x00);
einkPrvDeselect();
einkPrvCmd(redSubphase ? 0x26 : 0x24);
einkPrvDeselect();
}
#pragma callee_saves screenInitIfNeeded
static void screenInitIfNeeded(__bit forPartial)
{
if (mInited)
return;
mInited = true;
mPartial = forPartial;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 0;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 1;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
einkPrvCmd(0x12); // software reset
einkPrvDeselect();
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
einkPrvCmdWithOneByte(0x7454);
einkPrvCmdWithOneByte(0x7e3b);
einkPrvCmd(0x2b);
einkPrvData(0x04);
einkPrvData(0x63);
einkPrvDeselect();
einkPrvCmd(0x0c); // they send 8f 8f 8f 3f
einkPrvData(0x8f);
einkPrvData(0x8f);
einkPrvData(0x8f);
einkPrvData(0x3f);
einkPrvDeselect();
einkPrvCmd(0x01);
einkPrvData((SCREEN_HEIGHT - 1) & 0xff);
einkPrvData((SCREEN_HEIGHT - 1) >> 8);
einkPrvData(0x00);
einkPrvDeselect();
einkPrvCmdWithOneByte(0x1103);
einkPrvCmd(0x44);
einkPrvData(0x00);
einkPrvData(SCREEN_WIDTH / 8 - 1);
einkPrvDeselect();
einkPrvCmd(0x45);
einkPrvData(0x00);
einkPrvData(0x00);
einkPrvData((SCREEN_HEIGHT - 1) & 0xff);
einkPrvData((SCREEN_HEIGHT - 1) >> 8);
einkPrvDeselect();
einkPrvCmdWithOneByte(0x3cc0); // border will be HiZ
einkPrvCmdWithOneByte(0x1880); // internal temp sensor
einkPrvCmdWithOneByte(0x2108);
// turn on clock & analog
einkPrvCmdWithOneByte(0x22B1);
einkPrvCmd(0x20); // do action
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
}
#pragma callee_saves screenPrvDraw
static void screenPrvDraw(void)
{
einkPrvCmdWithOneByte(0x2200 | SCREEN_CMD_REFRESH);
einkPrvCmd(0x20); // do actions
if (1)
{
einkPrvWaitWithTimeoutSleep(1000 * 60UL);
screenSleep();
}
else
{
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND * 60UL);
}
}
__bit screenTxStart(__bit forPartial)
{
screenInitIfNeeded(forPartial);
mPassNo = 0;
screenPrvStartSubPhase(false);
return true;
}
void screenEndPass(void)
{
switch (mPassNo)
{
case 0:
screenPrvStartSubPhase(true);
break;
default:
return;
}
mPassNo++;
}
void screenTxEnd(void)
{
screenPrvDraw();
screenShutdown();
}
void screenShutdown(void)
{
if (!mInited)
return;
mInited = false;
einkPrvCmdWithOneByte(0x1003); // shut down
}
void screenSleep(void)
{
P2_0 = 0;
timerDelay(TIMER_TICKS_PER_SECOND / 250);
P2_0 = 1;
timerDelay(TIMER_TICKS_PER_SECOND / 250);
einkPrvCmd(0x12); // software reset
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
einkPrvCmdWithOneByte(0x1003); // shut down
}
#pragma callee_saves screenByteTx
void screenByteTx(uint8_t byte)
{
static uint8_t __xdata prev, step = 0;
prev <<= 2;
prev |= (mColorMap[mPassNo][byte >> 4] << 1) | mColorMap[mPassNo][byte & 0x0f];
if (++step == 4)
{
step = 0;
einkPrvSelect();
einkPrvData(prev);
einkPrvDeselect();
}
}
// yes this is here...
uint16_t adcSampleBattery(void)
{
__bit wasInited = mInited;
uint16_t voltage = 2600;
if (!mInited)
screenInitIfNeeded(false);
uint8_t val;
einkPrvCmdWithOneByte(0x2200 | SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON);
einkPrvCmd(0x20); // do action
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
for (val = 3; val < 8; val++)
{
einkPrvCmdWithOneByte(0x1500 + val);
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
if (einkPrvReadStatus() & 0x10)
{ // set if voltage is less than threshold ( == 1.9 + val / 10)
voltage = 1850 + mathPrvMul8x8(val, 100);
break;
}
}
einkPrvCmdWithOneByte(0x22B1);
einkPrvCmd(0x20); // do action
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
if (!wasInited)
screenShutdown();
return voltage;
}

View File

@@ -0,0 +1,48 @@
#ifndef _SCREEN_H_
#define _SCREEN_H_
#include <stdbool.h>
#include <stdint.h>
//i hate globals, but for 8051 this makes life a lot easier, sorry :(
extern uint8_t __xdata mScreenVcom;
extern int8_t __xdata mCurTemperature;
#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
void screenShutdown(void);
void screenTest(void);
__bit screenTxStart(__bit forPartial);
void screenEndPass(void); //at end of each pass
#pragma callee_saves screenByteTx
void screenByteTx(uint8_t byte);
void screenTxEnd(void);
void screenSleep(void);
extern uint8_t __xdata mScreenRow[]; //320 bytes used as temp by many on cc where memory is tight
#endif

47
ap_fw/builder.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
function checkmem(){
$mem = exec("cat main.mem | grep available | grep Stack");
list($blaat, $mem) = explode("with ", $mem);
list($mem,) = explode(" ", $mem, 2);
return $mem;
}
function bell(){
echo exec("echo \"\007\"");
}
function bells(){
for($c = 0; $c<5; $c++){
bell();
// usleep(220000);
}
}
$stackdisturbed = false;
$mem = checkmem();
while(1){
$errlist = array();
exec("make BUILD=zbs29v033 CPU=8051 SOC=zbs243 2>&1 | grep error | grep -v make", $errlist);
if(checkmem()!=$mem){
$stackdisturbed = true;
echo "Stack size was $mem, is now ".checkmem()." !!!\n";
bells();
} else{
if($stackdisturbed){
bell();
echo "stack back to $mem\n";
$stackdisturbed = false;
}
}
if(count($errlist)){
echo "-------------------------------\n";
foreach($errlist as $err){
echo " - $err\n";
}
bells();
}
sleep(5);
}

51
ap_fw/comms.c Normal file
View File

@@ -0,0 +1,51 @@
#define __packed
#include "comms.h"
#include <string.h>
#include "asmUtil.h"
#include "printf.h"
#include "proto.h"
#include "radio.h"
static uint8_t __xdata mCommsBuf[127];
uint8_t commsGetLastPacketLQI(void) {
return mLastLqi;
}
int8_t commsGetLastPacketRSSI(void) {
return mLastRSSI;
}
int8_t commsRxUnencrypted(void __xdata *data) {
uint8_t __xdata *dstData = (uint8_t __xdata *)data;
uint8_t __xdata *__xdata rxedBuf;
int8_t ret = COMMS_RX_ERR_INVALID_PACKET;
int8_t rxedLen = radioRxDequeuePktGet((void __xdata *__xdata) & rxedBuf, &mLastLqi, &mLastRSSI);
if (rxedLen < 0)
return COMMS_RX_ERR_NO_PACKETS;
xMemCopyShort(dstData, rxedBuf, rxedLen);
radioRxDequeuedPktRelease();
return rxedLen;
}
bool commsTxUnencrypted(const void __xdata *packetP, uint8_t len) {
const uint8_t __xdata *packet = (const uint8_t __xdata *)packetP;
if (len > COMMS_MAX_PACKET_SZ)
return false;
memset(mCommsBuf, 0, COMMS_MAX_PACKET_SZ);
xMemCopyShort(mCommsBuf + 1, packet, len);
mCommsBuf[0] = len + RADIO_PAD_LEN_BY;
return radioTx(mCommsBuf);;
radioTx(mCommsBuf);;
}
bool commsTxNoCpy(const void __xdata *packetp) {
return radioTx(packetp);
}

30
ap_fw/comms.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef _COMMS_H_
#define _COMMS_H_
#include <stdint.h>
#define COMMS_MAX_RADIO_WAIT_MSEC 200
#define COMMS_IV_SIZE (4) //zeroes except these 4 counter bytes
#define COMMS_RX_ERR_NO_PACKETS (-1)
#define COMMS_RX_ERR_INVALID_PACKET (-2)
#define COMMS_RX_ERR_MIC_FAIL (-3)
#define COMMS_MAX_PACKET_SZ (127)
int8_t commsRxUnencrypted(void __xdata *data);
bool commsTxUnencrypted(const void __xdata *packetP, uint8_t len);
bool commsTxNoCpy(const void __xdata *packetp);
static uint8_t __xdata mLastLqi;
static int8_t __xdata mLastRSSI;
#pragma callee_saves commsGetLastPacketLQI
uint8_t commsGetLastPacketLQI(void);
#pragma callee_saves commsGetLastPacketRSSI
int8_t commsGetLastPacketRSSI(void);
#endif

1502
ap_fw/cpu/8051/asmUtil.c Normal file

File diff suppressed because it is too large Load Diff

1
ap_fw/cpu/8051/cpu.c Normal file
View File

@@ -0,0 +1 @@
//nothing

18
ap_fw/cpu/8051/cpu.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef _CPUi_H_
#define _CPUi_H_
#include <stdint.h>
typedef uint16_t uintptr_near_t;
#define VERSIONMARKER __at (0x008b)
#define irqsOn() IEN_EA = 1
#include "soc.h"
#endif

8
ap_fw/cpu/8051/make.mk Normal file
View File

@@ -0,0 +1,8 @@
FLAGS += -Icpu/8051
FLAGS += -mmcs51 --std-sdcc2x --opt-code-size --peep-file cpu/8051/peep.def --fomit-frame-pointer
SOURCES += cpu/8051/asmUtil.c
CC = /usr/local/bin/sdcc
TARGETS = main.ihx main.bin
OBJFILEEXT = rel

115
ap_fw/cpu/8051/peep.def Normal file
View File

@@ -0,0 +1,115 @@
replace restart {
movx a,@dptr
mov %1,a
inc dptr
movx a,@dptr
mov %2,a
inc dptr
movx a,@dptr
mov %3,a
inc dptr
movx a,@dptr
mov %4,a
clr c
mov a,%1
subb a,#%5
mov a,%2
subb a,#%6
mov a,%3
subb a,#%7
mov a,%4
subb a,#%8
DISABLED XXX
} by {
clr c
movx a,@dptr
mov %1,a
subb a,#%5
inc dptr
movx a,@dptr
mov %2,a
subb a,#%6
inc dptr
movx a,@dptr
mov %3,a
subb a,#%7
inc dptr
movx a,@dptr
mov %4,a
subb a,#%8 ;dmitrygr - optimize dumb xdata handling
} if notSame(%1 %2),notSame(%1 %3),notSame(%1 %4),notSame(%2 %3),notSame(%3 %4),notSame(%3 %4)
replace {
mov b,%1
mov a,%2
div ab
mov %3,b
mov b,%1
mov a,%2
div ab
} by {
mov b,%1
mov a,%2
div ab
mov %3,b ; Peephole dmitrygr.0006 - why divide twice?
} if notSame(%1 %3),notSame(%2 %3)
replace {
movx @dptr,a
movx a,@dptr
} by {
movx @dptr,a ; Peephole dmitrygr.0005 - SRSLY, WTF?
}
replace restart {
mov r%1,%2
mov r%3,%4
mov r%5,%6
mov r%7,%8
push %9
push %10
mov %2,r%1
mov %4,r%3
mov %6,r%5
mov %8,r%7
} by {
mov r%1,%2
mov r%3,%4
mov r%5,%6
mov r%7,%8
push %9
push %10; Peephole dmitrygr.000 - pointless shuffling when passing 32 from return to param
} if notSame(%1 %3),notSame(%1 %5),notSame(%1 %7),notSame(%3 %5),notSame(%3 %7),notSame(%5 %7),notSame(%2 %4),notSame(%2 %6),notSame(%2 %8),notSame(%4 %6),notSame(%4 %8),notSame(%6 %8)
replace restart {
%1:
mov dptr,#%2
movx a,@dptr
mov %3,a
cjne %3,#%4,%1
} by {
mov dptr,#%2
%1:
movx a,@dptr
cjne a,#%4,%1 ; Peephole dmitrygr.001 - tighter loop, write extra var just once (i wish we knew if it were dead so we could write it zero times)
mov %3,a
} if labelRefCount(%1 1)
replace restart {
mov a,#%1
add a,#0x00
mov %2,a
mov a,%3
} by {
mov %2,#%1
mov a,%3
clr c ; Peephole dmitrygr.002 - remove pointless A-machinations
}
replace restart {
add a,#0x00
} by {
clr c ; Peephole dmitrygr.003 - adding zero is pointless, just clears C
}

795
ap_fw/cpu/8051/printf.c Normal file
View File

@@ -0,0 +1,795 @@
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include "printf.h"
#include "zbs243.h"
#include "board.h"
typedef void (*StrFormatOutputFunc)(uint32_t param /* low byte is data, bits 24..31 is char */) __reentrant;
static __idata __at (0x00) unsigned char R0;
static __idata __at (0x01) unsigned char R1;
static __idata __at (0x02) unsigned char R2;
static __idata __at (0x03) unsigned char R3;
static __idata __at (0x04) unsigned char R4;
static __idata __at (0x05) unsigned char R5;
static __idata __at (0x06) unsigned char R6;
static __idata __at (0x07) unsigned char R7;
static uint8_t __xdata mCvtBuf[18];
//callback must be reentrant and callee_saves
#pragma callee_saves prvPrintFormat
void prvPrintFormat(StrFormatOutputFunc formatF, uint16_t formatD, const char __code *fmt, va_list vl) __reentrant __naked
{
//formatF is in DPTR
//sp[0..-1] is return addr
//sp[-2..-3] is formatD
//sp[-4..-5] is fmt
//sp[-6] is vl
__asm__ (
" push _R7 \n"
" push DPH \n" //push formatF
" push DPL \n"
" mov _R7, sp \n" //save place on stack where we stashed it so we can call it easily
" push _R4 \n"
" push _R3 \n"
" push _R2 \n"
" push _R1 \n"
" push _R0 \n"
" mov A, #-12 \n"
" add A, sp \n"
" mov R0, A \n"
//R0 now points to pushed params, for large values, we see high bytes first
// to get next byte, we need to DECEREMENT R0
" mov DPH, @R0 \n"
" dec R0 \n"
" mov DPL, @R0 \n"
" dec R0 \n"
" mov _R0, @R0 \n"
" dec R0 \n"
//now format string is in DPTR, and R0 points to the top byte of whatever was in the first param
//main loop: get a byte of the format string
"00001$: \n"
" clr A \n"
" movc A, @A + DPTR \n"
" inc DPTR \n"
//if zero, we're done
" jz 00098$ \n"
//if not '%', print it
" cjne A, #'%', 00097$ \n"
//we got a percent sign - init state for format processing
" mov R4, #0 \n" //bit flags:
// 0x01 = '*' = pointer provided instead of value (integers only)
// 0x02 = '0' = zero-pad (for numbers only)
// 0x04 = have pad-to length
// 0x08 = long
// 0x10 = long long
// 0x20 = signed print requested. also: need to print a negative (used to reuse hex printing for decimal once converted to bcd)
" mov R2, #0 \n" //padLen
//loop for format string ingestion
"00002$: \n"
" clr A \n"
" movc A, @A + DPTR \n"
" inc DPTR \n"
//if zero, we're done
" jz 00098$ \n"
//check for percent sign
" cjne A, #'%', 00003$ \n"
//fallthrough to print it and go read next non-format byte
//print a char in A, go read next format byte
"00097$: \n"
" lcall 00060$ \n"
" sjmp 00001$ \n"
//exit label - placed for easy jumping to
"00098$: \n"
" pop _R0 \n"
" pop _R1 \n"
" pop _R2 \n"
" pop _R3 \n"
" pop _R4 \n"
" pop DPL \n"
" pop DPH \n"
" pop _R7 \n"
" ret \n"
//continue to process format string - handle %c
"00003$: \n"
" cjne A, #'c', 00004$ \n"
" dec R0 \n" //param is pushed as int (16 bits)
" mov A, @R0 \n"
" dec R0 \n"
" sjmp 00097$ \n" //print and go read next non-format byte
//continue to process format string - handle %m
"00004$: \n"
" mov R3, A \n"
" orl A, #0x20 \n"
" cjne A, #'m', 00008$ \n"
//sort out which hexch charset to use
" mov A, R3 \n"
" anl A, #0x20 \n"
" rr A \n"
" mov R1, A \n"
//go, do
" push DPH \n"
" push DPL \n"
" lcall 00090$ \n" //read the short (__xdata) pointer - >DPTR
" mov R4, #8 \n" //byteSel
"00005$: \n"
" push DPH \n"
" push DPL \n"
" mov A, R4 \n"
" dec A \n"
" add A, DPL \n"
" mov DPL, A \n"
" mov A, DPH \n"
" addc A, #0 \n"
" mov DPH, A \n"
" movx A, @DPTR \n"
" mov R2, A \n"
" swap A \n"
" mov R3, #2 \n"
"00006$: \n"
" anl A, #0x0f \n"
" add A, R1 \n"
" mov DPTR, #00099$ \n"
" movc A, @A + DPTR \n"
" lcall 00060$ \n"
" mov A, R2 \n"
" djnz R3, 00006$ \n"
" pop DPL \n"
" pop DPH \n"
" djnz R4, 00007$ \n"
//done with mac addr
"00055$: \n"
" pop DPL \n"
" pop DPH \n"
" sjmp 00001$ \n"
//print colon and contimue mac addr printing
"00007$: \n"
" mov A, #':' \n"
" lcall 00060$ \n"
" sjmp 00005$ \n"
//continue to process format string - handle '*'
"00008$: \n"
" mov A, R3 \n"
" cjne A, #'*', 00009$ \n"
" cjne R2, #0, 00097$ \n" //only valid when no length/padding has been specified yet, else invalid specifier
" mov A, #0x01 \n" //"pointer mode"
"00010$: \n"
" orl A, R4 \n"
" mov R4, A \n"
" sjmp 00002$ \n" //get next format specifier now
//continue to process format string - handle '0'
"00009$: \n"
" cjne A, #'0', 00011$ \n"
" cjne R2, #0, 00011$ \n" //setting "zero pad" is only valid when pad length is zero
" mov A, #0x06 \n" //"have pad length" | "zero-pad"
" sjmp 00010$ \n" //orr A into R4, get next format specifier now
//continue to process format string - handle '1'...'9'
"00011$: \n"
" mov R3, A \n"
" add A, #-'0' \n"
" jnc 00012$ \n" //now 0..9 are valid
" add A, #-10 \n"
" jc 00012$ \n"
" add A, #10 \n" //get it back into 1..9 range
" mov R3, A \n"
" mov A, #10 \n"
" mov B, R2 \n"
" mul AB \n"
" add A, R3 \n"
" mov R2, A \n"
" mov A, #0x04 \n" //"have pad length"
" sjmp 00010$ \n" //orr A into R4, get next format specifier now
//continue to process format string - handle 'l'
"00012$: \n"
" cjne R3, #'l', 00014$ \n"
" mov A, R4 \n"
" anl A, #0x08 \n"
" jz 00013$ \n" //no "long" yet? set that
//have long - set long log
" mov A, #0x10 \n" //"long long"
" sjmp 00010$ \n" //orr A into R4, get next format specifier now
//first 'l' - set long
"00013$: \n"
" mov A, #0x08 \n" //"long"
" sjmp 00010$ \n" //orr A into R4, get next format specifier now
//continue to process format string - handle 's'
"00014$: \n"
" cjne R3, #'s', 00025$ \n"
" mov A, R4 \n"
" anl A, #0x08 \n"
" push DPH \n"
" push DPL \n"
" jnz 00015$ \n"
" lcall 00091$ \n" //get and resolve generic pointer into DPTR
" sjmp 00016$ \n"
"00015$: \n" //get short pointer into DPTR, record that it is to XRAM
" clr PSW.5 \n"
" clr PSW.1 \n"
" lcall 00090$ \n"
"00016$: \n" //pointer to string now in DPTR
//we have the string pointer in {DPTR,PSW}, let's see if we have padding to do
" mov A, R4 \n"
" anl A, #0x04 \n"
" jnz 00018$ \n"
//print string with no length restrictions
"00017$: \n"
" lcall 00095$ \n"
" jz 00055$ \n"
" lcall 00060$ \n"
" sjmp 00017$ \n"
//print string with length restrictions and/or padding
"00018$: \n"
" cjne R2, #0, 00019$ \n" //verify reqested len was not zero
" sjmp 00055$ \n"
"00019$: \n"
" lcall 00095$ \n"
" jz 00020$ \n"
" lcall 00060$ \n"
" djnz R2, 00019$ \n"
//we get here if we ran out of allowable bytes - we're done then
" ljmp 00055$ \n"
//just a trampoline for range issues
"00035$: \n"
" ljmp 00036$ \n"
//we need to pad with spaces
"00020$: \n"
" mov A, #' ' \n"
" lcall 00060$ \n"
" djnz R2, 00020$ \n"
" ljmp 00055$ \n"
//continue to process format string - handle 'x'/'X'
"00025$: \n"
" mov A, R3 \n"
" orl A, #0x20 \n"
" cjne A, #'x', 00035$ \n"
" push DPH \n"
" push DPL \n"
" lcall 00080$ \n" //get pointer to the number in DPTR, length in bytes in B
//save it
"00070$: \n"
" push DPH \n"
" push DPL \n"
//sort out how long it would be if printed, first get a pointer to the highest
" mov A, B \n"
" rl A \n"
" mov R1, A \n"
" rr A \n"
" add A, #0xff \n"
" add A, DPL \n"
" mov DPL, A \n"
" mov A, DPH \n"
" addc A, #0x00 \n"
" mov DPH, A \n"
"00026$: \n"
" lcall 00079$ \n"
" anl A, #0xf0 \n"
" jnz 00028$ \n"
" dec R1 \n"
" lcall 00079$ \n"
" jnz 00028$ \n"
" dec R1 \n"
//dec DPTR
" dec DPL \n"
" mov A, DPL \n"
" cjne A, #0xff, 00027$ \n"
" dec DPH \n"
"00027$: \n"
" djnz B, 00026$ \n"
//we now know how many digits the number is (in R1), except that it has "0" if the number if zero, we cannot have that
"00028$: \n"
" cjne R1, #0, 00029$ \n"
" inc R1 \n"
"00029$: \n" //we now finally have the full length of the digits
//if the number is negative (happens when we're printing decimals)
// the length of it is one more, also in case of zero-padding, we need to print the minus sign here now
" mov A, R4 \n"
" anl A, #0x20 \n"
" jz 00051$ \n"
" inc R1 \n" //the length is one more
" mov A, R4 \n"
" anl A, #02 \n" //if zero-padding, the negative comes now
" jz 00051$ \n"
" mov A, #'-' \n"
" lcall 00060$ \n"
"00051$: \n"
//sort out if we need padding at all and if there is space
" mov A, R4 \n"
" anl A, #0x04 \n"
" jz 00031$ \n" //no padding requested
//padding was requested len is in R2
" mov A, R2 \n"
" clr C \n"
" subb A, R1 \n"
" jc 00031$ \n" //pad-to len < number_len -> no padding needed
" jz 00031$ \n" //pad-to len == number_len -> no padding needed
" mov R2, A \n"
//sort out which character to use -> DPL
" mov A, R4 \n" //fancy way to create space/zero as needed
" anl A, #0x02 \n"
" swap A \n"
" rr A \n"
" add A, #0x20 \n"
" mov DPL, A \n"
//pad!
"00030$: \n"
" mov A, DPL \n"
" lcall 00060$ \n"
" djnz R2, 00030$ \n"
"00031$: \n"
//if the number is negative (happens when we're printing decimals)
// we made the length of it is one more, which we need to undo
// also in case of space-padding, we need to print the minus sign here now
" mov A, R4 \n"
" anl A, #0x20 \n"
" jz 00052$ \n"
" dec R1 \n" //the length is one less than we had increased it to
" mov A, R4 \n"
" anl A, #02 \n" //if space-padding, the negative comes now
" jnz 00052$ \n"
" mov A, #'-' \n"
" lcall 00060$ \n"
"00052$: \n"
//time to print the number itself
//sort out which hexch charset to use -> R2
" mov A, R3 \n"
" anl A, #0x20 \n"
" rr A \n"
" mov R2, A \n"
//re-get the number pointer
" pop DPL \n"
" pop DPH \n"
//currently DPTR points to the number low byte, R1 is now many digits we expect to print, R2 is the charset selection, R4 and R3 are free
//let's calculate how many bytes we expect to process -> R4
" mov A, R1 \n"
" inc A \n"
" clr C \n"
" rrc A \n"
" mov R4, A \n"
//let's repoint DPTR to the first byte we'll print in (remember we print 2 digits per byte)
" dec A \n"
" add A, DPL \n"
" mov DPL, A \n"
" mov A, DPH \n"
" addc A, #0x00 \n"
" mov DPH, A \n"
//decide if we need to print just a nibble of the high byte or the whole thing. Free up R1
" mov A, R1 \n"
" anl A, #0x01 \n"
" jz 00032$ \n"
//we're printing just the low nibble of the first byte - set up for it
" lcall 00079$ \n"
" mov R1, #1 \n"
" sjmp 00033$ \n"
//print loop
"00032$: \n"
" lcall 00079$ \n"
" mov R1, #2 \n"
" mov R3, A \n"
" swap A \n"
"00033$: \n"
" anl A, #0x0f \n"
" add A, R2 \n"
" push DPH \n"
" push DPL \n"
" mov DPTR, #00099$ \n"
" movc A, @A + DPTR \n"
" pop DPL \n"
" pop DPH \n"
" lcall 00060$ \n"
" mov A, R3 \n"
" djnz R1, 00033$ \n"
//dec DPTR
" dec DPL \n"
" mov A, DPL \n"
" cjne A, #0xff, 00034$ \n"
" dec DPH \n"
"00034$: \n"
" djnz R4, 00032$ \n"
//done!
" ljmp 00055$ \n"
//continue to process format string - handle 'd'
"00036$: \n"
" cjne R3, #'d', 00037$ \n"
" mov A, #0x20 \n"
" orl A, R4 \n"
" mov R4, A \n"
" sjmp 00040$ \n"
//continue to process format string - handle 'u'
"00037$: \n"
" cjne R3, #'u', 00038$ \n"
" sjmp 00040$ \n"
//no more format strings exist that we can handle - bail
"00038$: \n"
" ljmp 00001$ \n"
//handle decimal printing
"00040$: \n"
" push DPH \n"
" push DPL \n"
" lcall 00080$ \n" //get pointer to the number in DPTR, length in bytes in B
" push B \n"
//copy the number to the double-dabble storage at proper offset (0 for u64, 4 for u32, 6 for u16)
//we do this so that the dabble area always starts at the same place...
" mov A, #8 \n"
" clr C \n"
" subb A, B \n"
" add A, #_mCvtBuf \n"
" mov R1, A \n"
" clr A \n"
" addc A, #(_mCvtBuf >> 8) \n"
" mov R3, A \n"
"00041$: \n"
" lcall 00079$ \n"
" inc DPTR \n"
" lcall 00086$ \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" lcall 00086$ \n"
" djnz B, 00041$ \n"
//leave DPTR pointing to dabble storage, past the number
" lcall 00086$ \n"
//we now have the top byte of the number in A, good time to check for negatives, if needed
" mov B, A \n"
" mov A, R4 \n"
" anl A, #0x20 \n"
" jz 00050$ \n" //unsigned printing requested
" mov A, B \n"
" anl A, #0x80 \n"
" jnz 00043$ \n" //is negative - we need to invert, 0x20 bit in R1 stays
//positive - 0x20 bit in R1 needs to go
" mov A, R4 \n"
" anl A, #~0x20 \n"
" mov R4, A \n"
" sjmp 00050$ \n"
//we need to negate the number
// but first we need a pointer to it, and its size
"00043$: \n"
" pop B \n"
" push B \n"
" mov A, #8 \n"
" clr C \n"
" subb A, B \n"
" add A, #_mCvtBuf \n"
" mov DPL, A \n"
" clr A \n"
" addc A, #(_mCvtBuf >> 8) \n"
" mov DPH, A \n"
//ok, now we are ready to negate it
" clr C \n"
"00049$: \n"
" movx A, @DPTR \n"
" mov R1, A \n"
" clr A \n"
" subb A, R1 \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" djnz B, 00049$ \n"
//zero out the rest of the storage (10 bytes)
"00050$: \n"
" mov B, #10 \n"
" clr A \n"
"00042$: \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" djnz B, 00042$ \n"
//calculate number of dabble steps
" pop A \n"
" swap A \n"
" rr A \n"
" mov R3, A \n"
//do the thing
"00044$: \n"
//dabble (10 iters for simplicity)
" mov DPTR, #(_mCvtBuf + 8) \n"
" mov B, #10 \n"
"00046$: \n"
" movx A, @DPTR \n"
" mov R1, A \n"
" anl A, #0x0f \n"
" add A,#-0x05 \n"
" mov A, R1 \n"
" jnc 00047$ \n"
" add A, #0x03 \n"
"00047$: \n"
" mov R1, A \n"
" anl A, #0xf0 \n"
" add A,#-0x50 \n"
" mov A, R1 \n"
" jnc 00048$ \n"
" add A, #0x30 \n"
"00048$: \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" djnz B, 00046$ \n"
//double (18 iters for simplicity)
" mov DPTR, #_mCvtBuf \n"
" clr C \n"
" mov B, #18 \n"
"00045$: \n"
" movx A, @DPTR \n"
" rlc A \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" djnz B, 00045$ \n"
" djnz R3, 00044$ \n"
//dabbling is done, print it now using hex routine
" mov DPTR, #(_mCvtBuf + 8) \n"
" mov B, #10 \n"
" clr PSW.5 \n" //it is now for sure in XRAM
" ljmp 00070$ \n"
//read short pointer from param stack
"00090$: \n"
" mov DPH, @R0 \n"
"00093$: \n"
" dec R0 \n"
" mov DPL, @R0 \n"
" dec R0 \n"
" ret \n"
//read and increment pointer of the type provided by 00091$ (in {DPTR,PSW}) into A. clobber nothing
"00095$: \n"
" jb PSW.5, 00066$ \n"
" jb PSW.1, 00067$ \n"
//XRAM
" movx A, @DPTR \n"
" inc DPTR \n"
" ret \n"
//CODE
"00066$: \n"
" clr A \n"
" movc A, @A+DPTR \n"
" inc DPTR \n"
" ret \n"
//IRAM
"00067$: \n"
" mov DPH, R0 \n"
" mov R0, DPL \n"
" mov A, @R0 \n"
" mov R0, DPH \n"
" inc DPL \n"
" ret \n"
//resolve generic pointer on param stack to an pointer in DPTR and flags in PSW.5 and PSW.1
//PSW.5 will be 0 and PSW.1 will be 0 for XRAM (PDATA goes here too)
//PSW.5 will be 1 and PSW.1 will be 0 for CODE
//PSW.5 will be 0 and PSW.1 will be 1 for IRAM
"00091$: \n"
" clr PSW.5 \n"
" clr PSW.1 \n"
" mov A, @R0 \n"
" dec R0 \n"
" jz 00090$ \n" //0x00: pointer type: xdata
" xrl A, #0x80 \n"
" jz 00094$ \n" //0x80: pointer type: code
" xrl A, #0xc0 \n"
" jz 00092$ \n" //0x40: pointer type: idata
//pdata
" mov DPH, _XPAGE \n"
" sjmp 00093$ \n"
//idata
"00092$: \n"
" setb PSW.1 \n"
" sjmp 00093$ \n"
//code
"00094$: \n"
" setb PSW.5 \n"
" sjmp 00090$ \n"
//read the pointer of the type that 00080$ returns (in DPTR) into A. clobber nothing
"00079$: \n"
" jnb PSW.5, 00078$ \n"
" push _R0 \n"
" mov R0, DPL \n"
" mov A, @R0 \n"
" pop _R0 \n"
" ret \n"
"00078$: \n"
" movx A, @DPTR \n"
" ret \n"
//get pointer to a number, might be pushed or might be pointed to, size might vary. return pointer to number's LOW byte in DPTR
"00080$: \n"
" mov A, R4 \n"
" anl A, #0x01 \n"
" jnz 00083$ \n"
//param is itself on stack - now we care about size, but either way, PSW.5 will be 1
" setb PSW.5 \n"
" mov B, #0 \n"
" mov A, R4 \n"
" anl A, #0x18 \n"
" jz 00081$ \n"
" anl A, #0x10 \n"
" jz 00082$ \n"
//long long (8 bytes) \n"
" setb B.2 \n"
" dec R0 \n"
" dec R0 \n"
" dec R0 \n"
" dec R0 \n"
//long (4 bytes)
"00082$: \n"
" setb B.1 \n"
" dec R0 \n"
" dec R0 \n"
//int (2 bytes) \n"
"00081$: \n"
" setb B.0 \n"
" dec R0 \n"
" mov DPL, R0 \n"
" dec R0 \n"
" inc B \n"
" ret \n"
//pointer it on stack itself, number is in xram, but we still need to provide the length
"00083$: \n"
" clr PSW.5 \n" //mark as "in xram"
" mov A, R4 \n"
" anl A, #0x18 \n"
" jz 00084$ \n"
" anl A, #0x10 \n"
" jz 00085$ \n"
//long long
" mov B, #8 \n"
" ljmp 00090$ \n"
//long
"00085$: \n"
" mov B, #4 \n"
" ljmp 00090$ \n"
//int
"00084$: \n"
" mov B, #2 \n"
" ljmp 00090$ \n"
//swap R3:R1 <-> DPH:DPL
"00086$: \n"
" xch A, DPH \n"
" xch A, R3 \n"
" xch A, DPH \n"
" xch A, DPL \n"
" xch A, R1 \n"
" xch A, DPL \n"
" ret \n"
/* putchar func
called via call. char is in A, R7 has pointer to stack as needed
can clobber B, CANNOT clobber DPTR
a mess because...8051
*/
"00060$: \n"
" push DPH \n"
" push DPL \n"
" push _R1 \n"
" push _R0 \n"
" mov _R0, R7 \n"
" mov DPL, @R0 \n"
" dec R0 \n"
" mov DPH, @R0 \n" //DPTR is now func ptr
" dec R0 \n"
" dec R0 \n"
" dec R0 \n"
" dec R0 \n"
" mov _R1, @R0 \n"
" dec R0 \n"
" mov _R0, @R0 \n" //R1:R0 is now "formatD"
" lcall 00061$ \n" //to set ret addr
" pop _R0 \n"
" pop _R1 \n"
" pop DPL \n"
" pop DPH \n"
" ret \n"
"00061$: \n"
" push DPL \n"
" push DPH \n"
" mov DPL, _R0 \n"
" mov DPH, _R1 \n"
" ret \n"
"00099$: \n"
" .ascii \"01234567\" \n"
" .ascii \"89ABCDEF\" \n"
" .ascii \"01234567\" \n"
" .ascii \"89abcdef\" \n"
);
(void)fmt;
(void)vl;
(void)formatF;
(void)formatD;
}
#pragma callee_saves prPrvPutchar
static void prPrvPutchar(uint32_t data) __reentrant
{
char ch = data >> 24;
if (ch == '\n')
dbgUartByte('\r');
dbgUartByte(ch);
}
void pr(const char __code *fmt, ...) __reentrant
{
va_list vl;
va_start(vl, fmt);
dbgUartOn();
prvPrintFormat(prPrvPutchar, 0, fmt, vl);
dbgUartOff();
va_end(vl);
}
#pragma callee_saves prPrvPutS
static void prPrvPutS(uint32_t data) __reentrant
{
char __xdata * __idata *strPP = (char __xdata * __idata *)data;
char ch = data >> 24;
*(*strPP)++ = ch;
}
void spr(char __xdata* out, const char __code *fmt, ...) __reentrant
{
char __xdata* outStart = out;
va_list vl;
va_start(vl, fmt);
prvPrintFormat(prPrvPutS, (uint16_t)&out, fmt, vl);
va_end(vl);
*out = 0;
}

261
ap_fw/cpu/8051/random.c Normal file
View File

@@ -0,0 +1,261 @@
#include "cpu.h"
struct RngState {
uint64_t a, b;
uint32_t c;
};
static struct RngState mState = {0, };
void rndSeed(uint8_t seedA, uint8_t seedB)
{
mState.a = seedA;
mState.b = seedB;
}
#pragma callee_saves rndPrvGen32
static uint32_t rndPrvGen32(struct RngState __xdata *state) __naked
{
// //xorshift128p with a small midification ( " retval ^= ++ c" )
//
// state->a ^= state->a << 23;
// state->a ^= state->a >> 17;
// state->a ^= state->b;
// t = state->a;
// state->a = state->b;
// state->b >>= 26
// state->b = t ^ state->b;
// state->c++;
//
// return (state->a + state->b) >> 32 + state->c;
__asm__(
" push _R0 \n"
//state->a ^= state->a << 23
" mov A, #5 \n" //point to a[5]
" lcall 00091$ \n"
" movx A, @DPTR \n"
" mov PSW.5, A.0 \n"
" mov B, #5 \n"
" mov A, #-1 \n"
"00001$: \n"
" lcall 00089$ \n"
" movx A, @DPTR \n"
" mov C, PSW.5 \n"
" rrc A \n"
" mov PSW.5, C \n"
" inc DPTR \n"
" inc DPTR \n"
" inc DPTR \n"
" mov _R0, A \n"
" movx A, @DPTR \n"
" xrl A, R0 \n"
" movx @DPTR, A \n"
" mov A, #-4 \n"
" djnz B, 00001$ \n"
//repoint DPTR to state->a.8
" mov A, #5 \n"
" lcall 00091$ \n"
//state->a ^= state->a >> 17
" clr PSW.5 \n"
" mov B, #6 \n"
"00002$: \n"
" mov A, #-1 \n"
" lcall 00089$ \n"
" movx A, @DPTR \n"
" mov C, PSW.5 \n"
" rrc A \n"
" mov PSW.5, C \n"
" push A \n"
" djnz B, 00002$ \n"
" mov A, #-2 \n"
" lcall 00089$ \n"
" mov B, #6 \n"
"00003$: \n"
" movx A, @DPTR \n"
" pop _R0 \n"
" xrl A, R0 \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" djnz B, 00003$ \n"
" inc DPTR \n" //point to B
" inc DPTR \n"
// pushed_t = state->a ^ state->b //last pushe di nhigh byte
// state->a = state->b;
" mov B, #8 \n"
"00004$: \n"
" movx A, @DPTR \n"
" mov _R0, A \n" //r0 = b[i]
" mov A, #-8 \n"
" lcall 00089$ \n"
" movx A, @DPTR \n" //a = a[i]
" xrl A, R0 \n" //a = a[i] ^ b[i]
" push A \n"
" mov A, R0 \n"
" movx @DPTR, A \n"
" mov A, #9 \n"
" lcall 00091$ \n"
" djnz B, 00004$ \n"
//repoint DPTR to state->b.3
" mov A, #-5 \n"
" lcall 00089$ \n"
//state->b >>= 24 (top 3 bytes are garbage)
" mov B, #5 \n"
"00005$: \n"
" movx A, @DPTR \n"
" mov _R0, A \n"
" mov A, #-3 \n"
" lcall 00089$ \n"
" mov A, R0 \n"
" movx @DPTR, A \n"
" mov A, #4 \n"
" lcall 00091$ \n"
" djnz B, 00005$ \n"
//state->b >>= 2
" mov A, #-4 \n"
" lcall 00089$ \n"
" mov B, #2 \n"
"00006$: \n"
" mov _R0, #5 \n"
" clr PSW.5 \n"
"00007$: \n"
" movx A, @DPTR \n"
" mov C, PSW.5 \n"
" rrc A \n"
" mov PSW.5, C \n"
" movx @DPTR, A \n"
" mov A, #-1 \n"
" lcall 00089$ \n"
" djnz _R0, 00007$ \n"
" mov A, #5 \n"
" lcall 00091$ \n"
" djnz B, 00006$ \n"
//reset DPTR to end of state->b
" mov A, #3 \n"
" lcall 00091$ \n"
//state->b = t ^ state->b
" mov B, #3 \n"
"00008$: \n"
" pop A \n"
" movx @DPTR, A \n"
" mov A, #-1 \n"
" lcall 00089$ \n"
" djnz B, 00008$ \n"
" mov B, #5 \n"
"00009$: \n"
" movx A, @DPTR \n"
" pop _R0 \n"
" xrl A, R0 \n"
" movx @DPTR, A \n"
" mov A, #-1 \n"
" lcall 00089$ \n"
" djnz B, 00009$ \n"
" mov A, #9 \n"
" lcall 00091$ \n"
//state->c++
" mov B, #4 \n"
" setb C \n"
"00010$: \n"
" movx A, @DPTR \n"
" addc A, #0 \n"
" movx @DPTR, A \n"
" inc DPTR \n"
" djnz B, 00010$ \n"
" mov A, #-16 \n" //point to top 32 bits of A
" lcall 00089$ \n"
// push (state->a + state->b) >> 32 ^ state->c
" mov B, #4 \n"
" clr PSW.5 \n"
"00011$: \n"
" movx A, @DPTR \n"
" mov _R0, A \n"
" mov A, #8 \n"
" lcall 00091$ \n"
" movx A, @DPTR \n"
" mov C, PSW.5 \n"
" addc A, R0 \n"
" mov PSW.5, C \n"
" mov _R0, A \n"
" mov A, #4 \n"
" lcall 00091$ \n"
" movx A, @DPTR \n"
" xrl A, R0 \n"
" push A \n"
" mov A, #-11 \n"
" lcall 00089$ \n"
" djnz B, 00011$ \n"
//pop result (pop it large to small)
" pop A \n"
" pop B \n"
" pop DPH \n"
" pop DPL \n"
" pop _R0 \n"
" ret \n"
//sub from DPTR
"00089$: \n"
" add A, DPL \n"
" mov DPL, A \n"
" mov A, #0xff \n"
" addc A, DPH \n"
" mov DPH, A \n"
" ret \n"
//add to DPTR
"00091$: \n"
" add A, DPL \n"
" mov DPL, A \n"
" clr A \n"
" addc A, DPH \n"
" mov DPH, A \n"
" ret \n"
);
(void)state;
}
uint32_t rndGen32(void) __naked
{
__asm__(
" mov DPTR, #_mState \n"
" ljmp _rndPrvGen32 \n"
);
}
uint8_t rndGen8(void) __naked
{
__asm__(
" lcall _rndGen32 \n"
" rr A \n"
" xrl A, B \n"
" rr A \n"
" xrl A, DPH \n"
" rr A \n"
" xrl DPL, A \n"
" ret \n"
);
}

322
ap_fw/eeprom.c Normal file
View File

@@ -0,0 +1,322 @@
#include "asmUtil.h"
#include "screen.h"
#include "eeprom.h"
#include "printf.h"
#include "board.h"
#include "cpu.h"
static uint32_t __xdata mEepromSize;
static uint8_t __xdata mOpcodeErz4K = 0, mOpcodeErz32K = 0, mOpcodeErz64K = 0;
uint8_t mScreenRow[320];
uint32_t eepromGetSize(void)
{
return mEepromSize;
}
void eepromReadStart(uint32_t addr) __reentrant
{
eepromPrvSelect();
eepromByte(0x03);
eepromByte(addr >> 16);
eepromByte(addr >> 8);
eepromByte(addr & 0xff);
}
void eepromRead(uint32_t addr, void __xdata *dstP, uint16_t len) __reentrant
{
uint8_t __xdata *dst = (uint8_t __xdata*)dstP;
eepromPrvSelect();
eepromByte(0x03);
eepromByte(addr >> 16);
eepromByte(addr >> 8);
eepromByte(addr & 0xff);
while (len--)
*dst++ = eepromByte(0);
eepromPrvDeselect();
}
static void eepromPrvSimpleCmd(uint8_t cmd)
{
eepromPrvSelect();
eepromByte(cmd);
eepromPrvDeselect();
}
static bool eepromPrvBusyWait(void)
{
uint8_t val;
eepromPrvSelect();
eepromByte(0x05);
while ((val = eepromByte(0x00)) & 1);
eepromPrvDeselect();
return true;
}
static bool eepromWriteLL(uint32_t addr, const void __xdata *srcP, uint16_t len)
{
const uint8_t __xdata *src = (const uint8_t __xdata*)srcP;
eepromPrvSimpleCmd(0x06);
eepromPrvSelect();
eepromByte(0x02);
eepromByte(addr >> 16);
eepromByte(addr >> 8);
eepromByte(addr & 0xff);
while (len--)
eepromByte(*src++);
eepromPrvDeselect();
return eepromPrvBusyWait();
}
void eepromDeepPowerDown(void)
{
eepromPrvSimpleCmd(0xb9);
}
static void eepromPrvWakeFromPowerdown(void)
{
eepromPrvSimpleCmd(0xab);
}
#pragma callee_saves eepromPrvSfdpRead
static void eepromPrvSfdpRead(uint16_t ofst, uint8_t __xdata *dst, uint8_t len)
{
eepromPrvSelect();
eepromByte(0x5a); //cmd
eepromByte(0); //addr
eepromByte(ofst >> 8);
eepromByte(ofst);
eepromByte(0x00); //dummy
while(len--)
*dst++ = eepromByte(0);
eepromPrvDeselect();
}
__bit eepromInit(void)
{
uint8_t __xdata buf[8];
uint8_t i, nParamHdrs;
eepromPrvWakeFromPowerdown();
//process SFDP
eepromPrvSfdpRead(0, buf, 8);
if (buf[0] != 0x53 || buf[1] != 0x46 || buf[2] != 0x44 || buf[3] != 0x50 || buf[7] != 0xff) {
pr("SFDP: header not found\n");
__bit valid = false;
//try manual ID for chips we know of
eepromPrvSelect();
eepromByte(0x90);
eepromByte(0x00);
eepromByte(0x00);
eepromByte(0x00);
if (eepromByte(0) == 0xc2) { //old macronix chips
valid = true;
mOpcodeErz4K = 0x20;
switch (eepromByte(0)) {
case 0x05: //MX25V512
mEepromSize = 0x00010000ul;
break;
case 0x12: //MX25V4005
mEepromSize = 0x00080000ul;
break;
default:
valid = false;
break;
}
}
eepromPrvDeselect();
return valid;
}
if (buf[5] != 0x01) {
pr("SFDP: version wrong: %u.%d\n", buf[5], buf[4]);
return false;
}
nParamHdrs = buf[6];
if (nParamHdrs == 0xff) //that case is very unlikely and we just do not care
nParamHdrs--;
//now we need to find the JEDEC parameter table header
for (i = 0; i <= nParamHdrs; i++) {
eepromPrvSfdpRead(mathPrvMul8x8(i, 8) + 8, buf, 8);
if (buf[0] == 0x00 && buf[2] == 0x01 && buf[3] >= 9) {
uint8_t j;
eepromPrvSfdpRead(*(uint16_t __xdata*)(buf + 4), mScreenRow, 9 * 4);
if ((mScreenRow[0] & 3) != 1) {
pr("SFDP: no 4K ERZ\n");
break;
}
if (!(mScreenRow[0] & 0x04)) {
pr("SFDP: no large write buf\n");
break;
}
if ((mScreenRow[2] & 0x06)) {
pr("SFDP: addr.len != 3\n");
break;
}
if (!mScreenRow[1] || mScreenRow[1] == 0xff) {
pr("SFDP: 4K ERZ opcode invalid\n");
break;
}
mOpcodeErz4K = mScreenRow[1];
if (mScreenRow[7] & 0x80) {
pr("SFDP: device too big\n");
break;
}
else {
uint8_t t;
if (t = mScreenRow[7])
mEepromSize = 0x00200000UL;
else if (t = mScreenRow[6])
mEepromSize = 0x00002000UL;
else if (t = mScreenRow[5])
mEepromSize = 0x00000020UL;
else {
pr("SFDP: device so small?!\n");
break;
}
while (t) {
mEepromSize <<= 1;
t >>= 1;
}
}
//get erase opcodes
for (j = 0x1c; j < 0x24; j += 2) {
uint8_t instr = mScreenRow[j + 1];
if (!instr || instr == 0xff)
continue;
switch (mScreenRow[j]) {
case 0x0c:
if (mOpcodeErz4K != instr) {
pr("4K ERZ opcode disagreement\n");
return false;
}
break;
case 0x0f: //32K erase
mOpcodeErz32K = instr;
break;
case 0x10: //64K erase
mOpcodeErz64K = instr;
break;
}
}
/*
pr("EEPROM accepted\n");
pr(" ERZ opcodes: \n");
if (mOpcodeErz4K)
pr(" 4K: %02xh\n", mOpcodeErz4K);
if (mOpcodeErz32K)
pr(" 32K: %02xh\n", mOpcodeErz32K);
if (mOpcodeErz64K)
pr(" 64K: %02xh\n", mOpcodeErz64K);
pr(" Size: 0x%*08lx\n", (uint16_t)&mEepromSize);
*/
return true;
}
}
pr("SFDP: no JEDEC table of expected version found\n");
return false;
}
bool eepromWrite(uint32_t addr, const void __xdata *srcP, uint16_t len) __reentrant
{
const uint8_t __xdata *src = (const uint8_t __xdata*)srcP;
while (len) {
uint16_t lenNow = EEPROM_WRITE_PAGE_SZ - (addr & (EEPROM_WRITE_PAGE_SZ - 1));
if (lenNow > len)
lenNow = len;
if (!eepromWriteLL(addr, src, lenNow))
return false;
addr += lenNow;
src += lenNow;
len -= lenNow;
}
return true;
}
bool eepromErase(uint32_t addr, uint16_t nSec) __reentrant
{
uint8_t now;
if (((uint16_t)addr) & 0x0fff)
return false;
for (;nSec; nSec -= now) {
eepromPrvSimpleCmd(0x06);
eepromPrvSelect();
if (nSec >= 16 && !(uint16_t)addr && mOpcodeErz64K) { //erase 64K
eepromByte(mOpcodeErz64K);
now = 16;
}
else if (nSec >= 8 && !(((uint16_t)addr) & 0x7fff) && mOpcodeErz32K) { //erase 32K
eepromByte(mOpcodeErz32K);
now = 8;
}
else { //erase 4K
eepromByte(mOpcodeErz4K);
now = 1;
}
eepromByte(addr >> 16);
eepromByte(addr >> 8);
eepromByte(addr);
eepromPrvDeselect();
if (!eepromPrvBusyWait())
return false;
addr += mathPrvMul16x8(EEPROM_ERZ_SECTOR_SZ, now);
}
return true;
}
void eepromOtpModeEnter(void)
{
eepromPrvSimpleCmd(0xb1);
}
void eepromOtpModeExit(void)
{
eepromPrvSimpleCmd(0xc1);
}

54
ap_fw/eeprom.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef _EEPROM_H_
#define _EEPROM_H_
#include <stdbool.h>
#include <stdint.h>
#define EEPROM_WRITE_PAGE_SZ 256 //max write size & alignment
#define EEPROM_ERZ_SECTOR_SZ 4096 //erase size and alignment
//device has 256 sectors, so eepromErase() cannot erase thw whole device...i can live with that
__bit eepromInit(void);
void eepromOtpModeEnter(void);
void eepromOtpModeExit(void);
#pragma callee_saves eepromRead
void eepromRead(uint32_t addr, void __xdata *dst, uint16_t len) __reentrant;
#pragma callee_saves eepromWrite
bool eepromWrite(uint32_t addr, const void __xdata *src, uint16_t len) __reentrant;
#pragma callee_saves eepromErase
bool eepromErase(uint32_t addr, uint16_t numSectors) __reentrant;
void eepromDeepPowerDown(void);
#pragma callee_saves eepromGetSize
uint32_t eepromGetSize(void);
//this is for firmware update use
void eepromReadStart(uint32_t addr) __reentrant;
//structures
#define EEPROM_IMG_INPROGRESS (0x7fffffffUL)
#define EEPROM_IMG_VALID (0x494d4722UL)
#include "board.h"
#define EEPROM_PIECE_SZ (88)
struct EepromImageHeader { //each image space is 0x17000 bytes, we have space for ten of them
uint64_t version;
uint32_t validMarker;
uint32_t size;
uint32_t rfu[8]; //zero-filled for now
uint8_t piecesMissing[EEPROM_PROGRESS_BYTES]; //each bit represents a EEPROM_PIECE_SZ-byte piece
uint32_t id;
//image data here
//we pre-erase so progress can be calculated by finding the first non-0xff byte
};
#endif

891
ap_fw/main.c Normal file
View File

@@ -0,0 +1,891 @@
#define __packed
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "asmUtil.h"
#include "board.h"
#include "comms.h"
#include "cpu.h"
#include "eeprom.h"
#include "printf.h"
#include "proto.h"
#include "radio.h"
#include "timer.h"
#include "wdt.h"
struct MacFrameFromMaster {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint16_t from;
} __packed;
struct MacFrameNormal {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
} __packed;
struct MacFrameBcast {
struct MacFcs fcs;
uint8_t seq;
uint16_t dstPan;
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
} __packed;
#define PKT_TIMING_REQ 0xE0
#define PKT_TIMING_RESPONSE 0xE1
#define PKT_AVAIL_DATA_REQ 0xE5
#define PKT_AVAIL_DATA_INFO 0xE6
#define PKT_BLOCK_PARTIAL_REQUEST 0xE7
#define PKT_BLOCK_REQUEST_ACK 0xE9
#define PKT_BLOCK_REQUEST 0xE4
#define PKT_BLOCK_PART 0xE8
#define PKT_XFER_COMPLETE 0xEA
#define PKT_XFER_COMPLETE_ACK 0xEB
#define PKT_SYNC_BURST 0xEF
struct timingResponse {
uint8_t checksum;
uint32_t burstInterval; // time between burst-start
uint8_t burstLength; // in packets; due to use of sequence field, limited to a 256-packet burst
uint16_t burstLengthMs; // burst length in ms
uint32_t timerValue; // current timer value (used to sync up other RC timers/oscillators)
uint32_t burstIntervalRemaining; // time until the next sync burst
uint8_t dataAvailable;
} __packed;
struct AvailDataReq {
uint8_t checksum;
uint8_t lastPacketLQI; // zero if not reported/not supported to be reported
int8_t lastPacketRSSI; // zero if not reported/not supported to be reported
uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint16_t batteryMv;
uint8_t softVer;
uint8_t hwType;
uint8_t protoVer;
} __packed;
#define DATATYPE_NOUPDATE 0
#define DATATYPE_IMG 1
#define DATATYPE_IMGRAW 2
#define DATATYPE_UPDATE 3
struct AvailDataInfo {
uint8_t checksum;
uint64_t dataVer;
uint32_t dataSize;
uint8_t dataType;
} __packed;
struct blockPart {
uint8_t checksum;
uint8_t blockId;
uint8_t blockPart;
uint8_t data[];
} __packed;
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} __packed;
struct pendingData {
struct AvailDataInfo availdatainfo;
uint8_t attemptsLeft;
uint8_t targetMac[8];
uint8_t includedThisBurst : 1;
} __packed;
struct burstMacData {
uint16_t offset;
uint8_t targetMac[8];
} __packed;
#define BLOCK_PART_DATA_SIZE 99
#define BLOCK_MAX_PARTS 42
#define BLOCK_DATA_SIZE 4096
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
// #define BLOCK_XFER_BUFFER_SIZE 4096 + 4
#define BLOCK_REQ_PARTS_BYTES 6 // BLOCK_MAX_PARTS / 8 + 1
#define MAX_MACS_PER_SYNC 2
#define MAX_PENDING_MACS 10
#define SYNC_BURST_INTERVAL 30UL
struct pendingData __xdata pendingDataArr[MAX_PENDING_MACS];
struct blockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t type;
uint8_t requestedParts[BLOCK_REQ_PARTS_BYTES];
} __packed;
struct blockRequestAck {
uint8_t checksum;
uint16_t blockSizeMs;
uint16_t pleaseWaitMs;
uint8_t cancelXfer;
} __packed;
struct espPendingData {
uint8_t checksum;
struct pendingData pending;
} __packed;
struct espBlockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
} __packed;
struct espXferComplete {
uint8_t checksum;
uint8_t src[8];
} __packed;
struct espJoinNetwork {
uint8_t checksum;
uint8_t src[8];
} __packed;
// used to transmit AP update information - flashing the firmware
struct espSaveUpdateBlock {
uint8_t checksum;
uint8_t blockId;
uint16_t blockChecksum;
} __packed;
#define TIMER_TICKS_PER_MS 1333UL
uint16_t __xdata version = 0x0015;
#define RAW_PKT_PADDING 2
static uint8_t __xdata mRxBuf[COMMS_MAX_PACKET_SZ];
uint8_t __xdata radiotxbuffer[128];
uint8_t __xdata radiorxbuffer[128];
uint32_t __xdata burstLengthMs; // stores how fast we were able to send a syncburst
uint8_t __xdata mSelfMac[8];
// serial stuff
uint8_t __xdata cmdbuffer[4];
uint8_t __xdata RXState = 0;
uint8_t __xdata serialbuffer[48];
uint8_t *__xdata serialbufferp;
uint8_t __xdata bytesRemain = 0;
static uint32_t __xdata burstIntervalTimer;
bool __xdata blockRequestInProgress = false; // set if we get a CRC error, or should do a full request for other reasons
struct blockRequest __xdata requestedData = {0};
uint8_t __xdata dstMac[8];
uint16_t __xdata dstPan;
static uint32_t __xdata blockStartTimer = 0;
uint8_t seq = 0; // holds current sequence number for transmission
uint8_t __xdata blockbuffer[BLOCK_XFER_BUFFER_SIZE];
#define SYNC_BURST_LENGTH 142 //(about 250ms)
void sendXferCompleteAck(uint8_t *dst);
// tools
void addCRC(void *p, uint8_t len) {
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++) {
total += ((uint8_t *)p)[c];
}
((uint8_t *)p)[0] = total;
// pr("%d",total);
}
bool checkCRC(void *p, uint8_t len) {
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++) {
total += ((uint8_t *)p)[c];
}
return ((uint8_t *)p)[0] == total;
}
void dump(uint8_t *__xdata a, uint16_t __xdata l) {
pr(" ");
#define ROWS 16
for (uint8_t c = 0; c < ROWS; c++) {
pr(" %02X", c);
timerDelay(1333);
}
pr("\n--------");
for (uint8_t c = 0; c < ROWS; c++) {
pr("---");
timerDelay(1333);
}
for (uint16_t c = 0; c < l; c++) {
timerDelay(1333);
if ((c % ROWS) == 0) {
pr("\n0x%04X | ", c);
}
pr("%02X ", a[c]);
}
pr("\n--------");
for (uint8_t c = 0; c < ROWS; c++) {
pr("---");
timerDelay(1333);
}
pr("\n");
}
uint8_t getBlockDataLength() {
uint8_t partNo = 0;
uint8_t rounds = 0;
while (partNo < BLOCK_MAX_PARTS) {
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) {
if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) {
partNo++;
}
}
rounds++;
if (rounds == 4) {
return partNo;
}
}
return partNo;
}
uint8_t __xdata getPacketType(void *__xdata buffer) {
struct MacFcs *__xdata fcs = buffer;
if ((fcs->frameType == 1) && (fcs->destAddrType == 2) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 0)) {
// broadcast frame
uint8_t __xdata type = ((uint8_t *)buffer)[sizeof(struct MacFrameBcast)];
return type;
} else if ((fcs->frameType == 1) && (fcs->destAddrType == 3) && (fcs->srcAddrType == 3) && (fcs->panIdCompressed == 1)) {
// normal frame
uint8_t __xdata type = ((uint8_t *)buffer)[sizeof(struct MacFrameNormal)];
return type;
}
return 0;
}
uint16_t averageXmitDelay(uint16_t xfersize) {
// returns the about maximum time the base should spend on the transfer; about 6 seconds (6000ms) for a 8000 byte transfer. This is * 3 / 4, pretty conservative
// this includes a lot of retransmissions.
return (xfersize * 3) / 5;
}
// serial update
void eraseUpdateBlock() {
eepromErase(EEPROM_UPDATA_AREA_START, EEPROM_UPDATE_AREA_LEN / EEPROM_ERZ_SECTOR_SZ);
}
bool validateBlockData() {
struct blockData *bd = (struct blockData *)blockbuffer;
// pr("expected len = %04X, checksum=%04X\n", bd->size, bd->checksum);
uint16_t t = 0;
for (uint16_t c = 0; c < bd->size; c++) {
t += bd->data[c];
}
return bd->checksum == t;
}
void saveBlock(uint8_t blockId) {
if (!eepromWrite(EEPROM_UPDATA_AREA_START + (blockId * BLOCK_DATA_SIZE), blockbuffer + sizeof(struct blockData), BLOCK_DATA_SIZE))
pr("EEPROM write failed\n");
}
void performUpdate() {
eepromReadStart(EEPROM_UPDATA_AREA_START);
selfUpdate();
}
uint16_t getBlockChecksum() {
struct blockData *bd = (struct blockData *)blockbuffer;
return bd->checksum;
}
// pendingdata slot stuff
int8_t findSlotForMac(const uint8_t *mac) {
for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) {
if (u64_isEq((uint64_t __xdata *)mac, (uint64_t __xdata *)&(pendingDataArr[c].targetMac))) { // this costs 1 sloc :(
return c;
}
}
return -1;
}
int8_t findFreeSlot() {
for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) {
if (pendingDataArr[c].attemptsLeft == 0) {
return c;
}
}
return -1;
}
// processing serial data
#define ZBS_RX_WAIT_HEADER 0
#define ZBS_RX_WAIT_SDA 1
#define ZBS_RX_WAIT_UPDBLOCK 2
void processSerial(uint8_t lastchar) {
// uartTx(lastchar); echo
switch (RXState) {
case ZBS_RX_WAIT_HEADER:
// shift characters in
for (uint8_t c = 0; c < 3; c++) {
cmdbuffer[c] = cmdbuffer[c + 1];
}
cmdbuffer[3] = lastchar;
if (strncmp(cmdbuffer, "SDA>", 4) == 0) {
RXState = ZBS_RX_WAIT_SDA;
bytesRemain = sizeof(struct pendingData);
serialbufferp = serialbuffer;
break;
}
if (strncmp(cmdbuffer, "VER?", 4) == 0) {
pr("VER>%04X\n", version);
}
if (strncmp(cmdbuffer, "RDY?", 4) == 0) {
pr("RDY>");
}
if (strncmp(cmdbuffer, "RSET", 4) == 0) {
wdtDeviceReset();
}
if (strncmp(cmdbuffer, "ERAS", 4) == 0) {
// erase update space
eraseUpdateBlock();
pr("EROK\n");
}
if (strncmp(cmdbuffer, "UPDA", 4) == 0) {
// perform update!
pr("OK>>\n");
performUpdate();
}
if (strncmp(cmdbuffer, "SUBL", 4) == 0) {
// save update block
RXState = ZBS_RX_WAIT_UPDBLOCK;
bytesRemain = sizeof(struct espSaveUpdateBlock);
serialbufferp = serialbuffer;
break;
}
break;
case ZBS_RX_WAIT_SDA:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0) {
if (checkCRC(serialbuffer, sizeof(struct pendingData))) {
int8_t slot = findFreeSlot();
if (slot != -1) {
xMemCopyShort(&(pendingDataArr[slot]), serialbuffer, sizeof(struct pendingData));
pr("ACK>\n");
} else {
pr("NOK>\n");
}
} else {
pr("NOK>\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_UPDBLOCK:
*serialbufferp = lastchar;
serialbufferp++;
bytesRemain--;
if (bytesRemain == 0) {
if (checkCRC(serialbuffer, sizeof(struct espSaveUpdateBlock))) {
if (validateBlockData()) {
const struct espSaveUpdateBlock *updb = (struct espSaveUpdateBlock *)serialbuffer;
if (updb->blockChecksum == getBlockChecksum()) {
saveBlock(updb->blockId);
pr("BLOK\n");
} else {
pr("BLFL> - block checksum doesn't match\n");
}
} else {
pr("BLFL> - block doesn't validate\n");
}
} else {
// block failed download
pr("BLFL> - update block data checksum failed\n");
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
}
}
// sending data to the ESP
void espBlockRequest(const struct blockRequest *br) {
struct espBlockRequest *__xdata ebr = (struct espBlockRequest *)blockbuffer;
uartTx('R');
uartTx('Q');
uartTx('B');
uartTx('>');
// u64_copy(ebr->ver, br->ver);
xMemCopy8(&(ebr->ver), &(br->ver));
ebr->blockId = br->blockId;
addCRC(ebr, sizeof(struct espBlockRequest));
for (uint8_t c = 0; c < sizeof(struct espBlockRequest); c++) {
uartTx(((uint8_t *)ebr)[c]);
}
// pr("req ebr ver: %02X%02X%02X%02X%02X%02X%02X%02X\n", ((uint8_t *)&(ebr->ver))[0], ((uint8_t *)&(ebr->ver))[1], ((uint8_t *)&(ebr->ver))[2], ((uint8_t *)&(ebr->ver))[3], ((uint8_t *)&(ebr->ver))[4], ((uint8_t *)&(ebr->ver))[5], ((uint8_t *)&(ebr->ver))[6], ((uint8_t *)&(ebr->ver))[7]);
// pr("req br ver: %02X%02X%02X%02X%02X%02X%02X%02X\n", ((uint8_t *)&(br->ver))[0], ((uint8_t *)&(br->ver))[1], ((uint8_t *)&(br->ver))[2], ((uint8_t *)&(br->ver))[3], ((uint8_t *)&(br->ver))[4], ((uint8_t *)&(br->ver))[5], ((uint8_t *)&(br->ver))[6], ((uint8_t *)&(br->ver))[7]);
}
void espNotifyAvailDataReq(const struct AvailDataReq *adr) {
uartTx('A');
uartTx('D');
uartTx('R');
uartTx('>');
for (uint8_t c = 0; c < sizeof(struct AvailDataReq); c++) {
uartTx(((uint8_t *)adr)[c]);
}
}
void espNotifyXferComplete(const uint8_t *src) {
struct espXferComplete exfc;
xMemCopy8(&exfc.src, src);
uartTx('X');
uartTx('F');
uartTx('C');
uartTx('>');
addCRC(&exfc, sizeof(exfc));
for (uint8_t c = 0; c < sizeof(exfc); c++) {
uartTx(((uint8_t *)exfc)[c]);
}
}
void espNotifyTimeOut() {
}
void espNotifyJoinNetwork(const uint8_t *src) {
struct espJoinNetwork ejn;
xMemCopy8(&ejn.src, src);
uartTx('T');
uartTx('J');
uartTx('N');
uartTx('>');
addCRC(&ejn, sizeof(ejn));
for (uint8_t c = 0; c < sizeof(ejn); c++) {
uartTx(((uint8_t *)ejn)[c]);
}
}
// process data from tag
void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
struct MacFrameNormal *__xdata rxHeader = (struct MacFrameNormal *)buffer;
struct blockRequest *__xdata blockReq = (struct blockRequest *)(buffer + sizeof(struct MacFrameNormal) + 1);
if (!checkCRC(blockReq, sizeof(struct blockRequest))) return;
// todo: actually do something with the block request
// uint32_t __xdata curTimerValue = *t;
bool __xdata requestDataDownload = false;
// if ((blockReq->blockId != requestedData.blockId) || (blockReq->ver != requestedData.ver)) {
if ((blockReq->blockId != requestedData.blockId) || (!u64_isEq((const uint64_t __xdata *)&blockReq->ver, (const uint64_t __xdata *)&requestedData.ver))) {
// requested block isn't already in the buffer
requestDataDownload = true;
} else {
// requested block is already in the buffer
if (forceBlockDownload) {
// force a download anyway; probably some error in the transfer between ESP32->AP-tag
if (!blockRequestInProgress) {
// block download from ESP32 not in progress
blockRequestInProgress = true;
requestDataDownload = true;
} else {
// block download from ESP32 requested, but already in progress. Maybe the transfer stalled for some reason; have the ESP32 send us some bytes.
uartTx('R');
uartTx('Q');
uartTx('Q');
uartTx('>');
}
}
}
memcpy(&requestedData, blockReq, sizeof(struct blockRequest));
struct MacFrameNormal *txHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct blockRequestAck *blockRequestAck = (struct blockRequestAck *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct blockRequestAck) + RAW_PKT_PADDING;
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_REQUEST_ACK;
// TODO: get this data from somnewhere, dynamically. Depending on cache status we might need to return a longer or shorter wait period
if (blockStartTimer == 0) {
if (requestDataDownload) {
// check if we need to download the first block; we need to give the ESP32 some additional time to cache the file
if (blockReq->blockId == 0) {
blockRequestAck->pleaseWaitMs = 200;
} else {
blockRequestAck->pleaseWaitMs = 100;
}
blockStartTimer = timerGet() + blockRequestAck->pleaseWaitMs * TIMER_TICKS_PER_MS;
} else {
blockRequestAck->pleaseWaitMs = 30;
blockStartTimer = timerGet() + blockRequestAck->pleaseWaitMs * TIMER_TICKS_PER_MS;
}
} else {
blockRequestAck->pleaseWaitMs = (blockStartTimer - timerGet()) / TIMER_TICKS_PER_MS;
if (blockRequestAck->pleaseWaitMs < 30) {
blockRequestAck->pleaseWaitMs = 30;
blockStartTimer = timerGet() + blockRequestAck->pleaseWaitMs * TIMER_TICKS_PER_MS;
}
}
blockRequestAck->blockSizeMs = 15 + 15 + (getBlockDataLength() * 245) / BLOCK_MAX_PARTS;
blockRequestAck->cancelXfer = 0;
// pr("s=%d\n", blockRequestAck->blockSizeMs);
memcpy(txHeader->src, mSelfMac, 8);
memcpy(txHeader->dst, rxHeader->src, 8);
memcpy(dstMac, rxHeader->src, 8);
dstPan = rxHeader->pan;
txHeader->pan = rxHeader->pan;
txHeader->fcs.frameType = 1;
txHeader->fcs.panIdCompressed = 1;
txHeader->fcs.destAddrType = 3;
txHeader->fcs.srcAddrType = 3;
txHeader->seq = seq++;
addCRC((void *)blockRequestAck, sizeof(struct blockRequestAck));
radioTx(radiotxbuffer);
radioTx(radiotxbuffer);
radioTx(radiotxbuffer);
// pr("req blockreq: %02X%02X%02X%02X%02X%02X%02X%02X\n", ((uint8_t *)&(blockReq->ver))[0], ((uint8_t *)&(blockReq->ver))[1], ((uint8_t *)&(blockReq->ver))[2], ((uint8_t *)&(blockReq->ver))[3], ((uint8_t *)&(blockReq->ver))[4], ((uint8_t *)&(blockReq->ver))[5], ((uint8_t *)&(blockReq->ver))[6], ((uint8_t *)&(blockReq->ver))[7]);
if (requestDataDownload) {
// espBlockRequest(blockReq);
espBlockRequest(&requestedData);
}
/*
pr("Req: %d [", blockReq->blockId);
for (uint8_t c = 0; c < BLOCK_MAX_PARTS; c++) {
if ((c != 0) && (c % 8 == 0)) pr("][");
if (blockReq->requestedParts[c / 8] & (1 << (c % 8))) {
pr("R");
} else {
pr(".");
}
}
pr("]\n");
*/
}
void processAvailDataReq(uint8_t *buffer) {
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer;
struct AvailDataReq *availDataReq = (struct AvailDataReq *)(buffer + sizeof(struct MacFrameNormal) + 1);
if (!checkCRC(availDataReq, sizeof(struct AvailDataReq))) return;
// prepare tx buffer to send a response
memset(radiotxbuffer, 0, 120);
struct MacFrameNormal *txHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct AvailDataInfo *availDataInfo = (struct AvailDataInfo *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + sizeof(struct AvailDataInfo) + RAW_PKT_PADDING;
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_AVAIL_DATA_INFO;
// check to see if we were addressing this mac in this burst, and if yes, copy availdatainfo to the tx buffer
bool haveData = false;
for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) {
if (pendingDataArr[c].includedThisBurst == 1) {
if (memcmp(pendingDataArr[c].targetMac, rxHeader->src, 8) == 0) {
haveData = true;
xMemCopyShort((void *__xdata)availDataInfo, &(pendingDataArr[c].availdatainfo), sizeof(struct AvailDataInfo));
break;
}
}
}
if (!haveData) return;
xMemCopy8(txHeader->src, mSelfMac);
xMemCopy8(txHeader->dst, rxHeader->src);
// memcpy(txHeader->src, mSelfMac, 8);
// memcpy(txHeader->dst, rxHeader->src, 8);
txHeader->pan = rxHeader->pan;
txHeader->fcs.frameType = 1;
txHeader->fcs.panIdCompressed = 1;
txHeader->fcs.destAddrType = 3;
txHeader->fcs.srcAddrType = 3;
txHeader->seq = seq++;
addCRC(availDataInfo, sizeof(struct AvailDataInfo));
radioTx(radiotxbuffer);
espNotifyAvailDataReq(availDataReq);
}
void processXferComplete(uint8_t *buffer) {
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer;
sendXferCompleteAck(rxHeader->src);
espNotifyXferComplete(rxHeader->src);
uint8_t slot = findSlotForMac(rxHeader->src);
pendingDataArr[slot].attemptsLeft = 0;
}
// send crap to the tag
void prepareMacForSyncBurst() {
// mark all pending macs as 'not included'
for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) {
pendingDataArr[c].includedThisBurst = 0;
}
memset(radiotxbuffer, 0, sizeof(struct MacFrameBcast) + 1 + 20 + 2); // TODO, optimize
struct burstMacData *__xdata macdata = (struct burstMacData * __xdata)(((uint8_t *)radiotxbuffer) + sizeof(struct MacFrameBcast) + 3); // total len, pkt type sync, mac-count
uint8_t __xdata count = 0;
uint16_t __xdata currOffset = 500;
while (count < MAX_MACS_PER_SYNC) {
uint8_t __xdata tempmax = 0;
int8_t __xdata maxid = -1;
for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) {
// check if this mac is already included in the planned sync burst
if (pendingDataArr[c].includedThisBurst == 0) {
// check if this current amount of 'attemptsleft' is the current maximum
if (pendingDataArr[c].attemptsLeft && (pendingDataArr[c].attemptsLeft > tempmax)) {
uint16_t timeoffset = SYNC_BURST_INTERVAL * 1000;
timeoffset -= 1000;
// check if the estimated transmission would fit in the remaining time
if (currOffset + averageXmitDelay(pendingDataArr[c].availdatainfo.dataSize) < timeoffset) {
tempmax = pendingDataArr[c].attemptsLeft;
maxid = c;
}
}
}
}
if (maxid == -1) {
// didn't find any valid mac's to add to the syncburst
return;
} else {
// found a pending-data info struct with the highest amount of attemptsLeft. Add this to the sync burst
// make sure we don't add this pending data mac twice
pendingDataArr[maxid].includedThisBurst = 1;
pendingDataArr[maxid].attemptsLeft--;
xMemCopyShort(macdata[count].targetMac, pendingDataArr[maxid].targetMac, 8);
macdata[count].offset = currOffset;
currOffset += averageXmitDelay(pendingDataArr[maxid].availdatainfo.dataSize);
count++;
*((uint8_t *)radiotxbuffer + sizeof(struct MacFrameBcast) + 2) = count;
}
}
}
void sendSyncBurst() {
struct MacFrameBcast *txframe = (struct MacFrameBcast *)(radiotxbuffer + 1);
memcpy(txframe->src, mSelfMac, 8);
*((uint8_t *)txframe + sizeof(struct MacFrameBcast)) = PKT_SYNC_BURST;
txframe->fcs.frameType = 1;
txframe->fcs.secure = 0;
txframe->fcs.framePending = 0;
txframe->fcs.ackReqd = 0;
txframe->fcs.panIdCompressed = 0;
txframe->fcs.destAddrType = 2;
txframe->fcs.frameVer = 0;
txframe->fcs.srcAddrType = 3;
txframe->seq = 0;
txframe->dstPan = 0xFFFF;
txframe->dstAddr = 0xFFFF;
txframe->srcPan = 0x4447;
radiotxbuffer[0] = sizeof(struct MacFrameBcast) + 1 + (MAX_MACS_PER_SYNC * sizeof(struct burstMacData)) + 2;
pr("BST>\n");
burstLengthMs = timerGet();
for (uint16_t c = 0; c < SYNC_BURST_LENGTH; c++) {
radioTx(radiotxbuffer);
txframe->seq++;
}
burstLengthMs = (timerGet() - burstLengthMs) / 1333;
// pr("atxc in %lu,\n", burstLengthMs);
}
void sendTimingReply(void *__xdata buf) {
struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buf;
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct timingResponse *response = (struct timingResponse *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_TIMING_RESPONSE;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct timingResponse) + 1 + RAW_PKT_PADDING;
memset(response, 0, sizeof(struct timingResponse) + sizeof(struct MacFrameNormal) + 1);
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, rxframe->src, 8);
response->timerValue = timerGet();
response->burstInterval = SYNC_BURST_INTERVAL * 1000;
response->burstLength = SYNC_BURST_LENGTH; // in packets; due to use of sequence field, limited to a 256-packet burst
response->burstLengthMs = burstLengthMs; // burst length in ms
response->burstIntervalRemaining = (TIMER_TICKS_PER_SECOND * SYNC_BURST_INTERVAL) - (timerGet() - burstIntervalTimer);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = rxframe->srcPan;
addCRC(response, sizeof(struct timingResponse));
radioTx(radiotxbuffer);
espNotifyJoinNetwork(rxframe->src);
}
void sendPart(uint8_t partNo) {
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
struct blockPart *blockPart = (struct blockPart *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_BLOCK_PART;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dstMac, 8);
blockPart->blockId = requestedData.blockId;
blockPart->blockPart = partNo;
memcpy(&(blockPart->data), blockbuffer + (partNo * BLOCK_PART_DATA_SIZE), BLOCK_PART_DATA_SIZE);
addCRC(blockPart, sizeof(struct blockPart) + BLOCK_PART_DATA_SIZE);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
void sendBlockData() {
uint8_t partNo = 0;
uint8_t rounds = 0;
while (partNo < BLOCK_MAX_PARTS) {
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) {
if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) {
sendPart(c);
partNo++;
}
}
rounds++;
if (rounds == 4) {
return;
}
}
// TODO: not sure if we need this, probably not. Not sure why I added it in the first place
commsRxUnencrypted(radiorxbuffer);
commsRxUnencrypted(radiorxbuffer);
commsRxUnencrypted(radiorxbuffer);
commsRxUnencrypted(radiorxbuffer);
commsRxUnencrypted(radiorxbuffer);
commsRxUnencrypted(radiorxbuffer);
}
void sendXferCompleteAck(uint8_t *dst) {
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE_ACK;
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
memcpy(frameHeader->src, mSelfMac, 8);
memcpy(frameHeader->dst, dst, 8);
frameHeader->fcs.frameType = 1;
frameHeader->fcs.panIdCompressed = 1;
frameHeader->fcs.destAddrType = 3;
frameHeader->fcs.srcAddrType = 3;
frameHeader->seq = seq++;
frameHeader->pan = dstPan;
radioTx(radiotxbuffer);
}
// main loop
void main(void) {
clockingAndIntsInit();
timerInit();
boardInit();
P0FUNC = 0b11001111; // enable uart tx/rx and SPI bus functions
irqsOn();
boardInitStage2();
requestedData.blockId = 0xFF;
if (!boardGetOwnMac(mSelfMac)) {
pr("failed to get MAC. Aborting\n");
while (1)
;
}
for (uint8_t c = 0; c < 8; c++) {
// mSelfMac[c] = c;
}
if (!eepromInit()) { // we'll need the eeprom here, init it.
pr("failed to init eeprom\n");
return;
}
// clear the array with pending information
memset(pendingDataArr, 0, sizeof(pendingDataArr));
radioInit();
radioRxFilterCfg(mSelfMac, 0x10000, PROTO_PAN_ID);
// init the "random" number generation unit
rndSeed(mSelfMac[0] ^ (uint8_t)timerGetLowBits(), mSelfMac[1]);
// wdtSetResetVal(0xFD0DCF);
// wdtOn();
radioSetChannel(RADIO_FIRST_CHANNEL);
radioSetTxPower(10);
radioRxEnable(true, true);
// uint8_t __xdata fromMac[8];
pr("RDY>\n");
// send first burst, used to characterize the packet TX speed
burstIntervalTimer = timerGet(); // + (TIMER_TICKS_PER_SECOND * SYNC_BURST_INTERVAL);
sendSyncBurst();
// really... if I do the call below, it'll cost me 8 bytes IRAM. Not the kind of 'optimization' I ever dreamed of doing
// pr("MAC>%02X%02X%02X%02X%02X%02X%02X%02X\n", mSelfMac[0], mSelfMac[1], mSelfMac[2], mSelfMac[3], mSelfMac[4], mSelfMac[5], mSelfMac[6], mSelfMac[7]);
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]);
pr("VER>%04X\n", version);
while (1) {
radioRxFlush();
// spend about 30 seconds - 100ms in this while loop. The last 100ms are for preparing the sync burst
while ((timerGet() - burstIntervalTimer) < ((TIMER_TICKS_PER_SECOND * SYNC_BURST_INTERVAL) - 100 * TIMER_TICKS_PER_MS)) {
int8_t ret = commsRxUnencrypted(radiorxbuffer);
if (ret > 1) {
// received a packet, lets see what it is
switch (getPacketType(radiorxbuffer)) {
case PKT_TIMING_REQ:
sendTimingReply(radiorxbuffer);
break;
case PKT_AVAIL_DATA_REQ:
processAvailDataReq(radiorxbuffer);
break;
case PKT_BLOCK_REQUEST:
processBlockRequest(radiorxbuffer, 1);
break;
case PKT_BLOCK_PARTIAL_REQUEST:
processBlockRequest(radiorxbuffer, 0);
break;
case PKT_XFER_COMPLETE:
processXferComplete(radiorxbuffer);
break;
//
default:
// pr("other packet...type = %02X\n", getPacketType(radiorxbuffer));
// dump(radiorxbuffer, 128);
break;
}
}
while (uartBytesAvail()) {
processSerial(uartRx());
}
if (blockStartTimer) {
// BUG: uint32 overflowing; this will break every once in a while. Don't know how to fix this other than ugly global variables
if (timerGet() > blockStartTimer) {
sendBlockData();
blockStartTimer = 0;
}
}
}
for (uint8_t __xdata c = 0; c < MAX_PENDING_MACS; c++) {
if (pendingDataArr[c].attemptsLeft == 1) {
espNotifyTimeOut();
pendingDataArr[c].attemptsLeft = 0;
}
}
prepareMacForSyncBurst();
while ((timerGet() - burstIntervalTimer) < (TIMER_TICKS_PER_SECOND * SYNC_BURST_INTERVAL)) {
// wait here for maximum burst-start accuracy
}
burstIntervalTimer = timerGet();
sendSyncBurst();
}
}

27
ap_fw/printf.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef _PRINTF_H_
#define _PRINTF_H_
//our printf has some special abilities
//for example "*" will modify the param to be a __xdata pointer to whatever it would have been instead
//it must then be paramed as "(uintptr_near_t)&value"
//for cc1110 (but not for ZBS) code and xdata addrs are the same, so __code pointers will also work!
//"%s" param takes a generic pointer, but assumes it is an xdata/code (no string support in pdata/idata)
//"%ls" takes an xdata(/code in cc111x) pointer instead :)
//"%m/%M" will print a mac, an __xdata pointer to which has been provided
//no support for passing NULL to %s
//not re-entrant if %d/%u are used
#pragma callee_saves pr
void pr(const char __code *fmt, ...) __reentrant;
#pragma callee_saves spr
void spr(char __xdata* out, const char __code *fmt, ...) __reentrant;
#endif

182
ap_fw/proto.h Normal file
View File

@@ -0,0 +1,182 @@
#ifndef _PROTO_H_
#define _PROTO_H_
#include <stdint.h>
/*
All communications are direct from tag to station, EXCEPT association (tag will broadcast).
All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware.
Master shall provision new key at association. All non-bcast packets shall have pan id compression.
Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply.
Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used
T = tag, S = station
PACKET TYPE USE PAYLOAD STRUCT NOTES
ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key)
ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key)
CHECKIN T2S CheckinInfo tag checking in occasionally
CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it
CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data
CHUNK_RESP S2T ChunkInfo station provides chunk
*/
#define PROTO_PRESHARED_KEY {0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081}
#define PROTO_PAN_ID (0x4447) //PAN ID compression shall be used
#define PKT_ASSOC_REQ (0xF0)
#define PKT_ASSOC_RESP (0xF1)
#define PKT_CHECKIN (0xF2)
#define PKT_CHECKOUT (0xF3)
#define PKT_CHUNK_REQ (0xF4)
#define PKT_CHUNK_RESP (0xF5)
#define PROTO_VER_0 (0)
#define PROTO_VER_CURRENT (PROTO_VER_0)
#define PROTO_COMPR_TYPE_LZ (0x0001)
#define PROTO_COMPR_TYPE_BITPACK (0x0002)
#define PROTO_MAX_DL_LEN (88)
enum TagScreenType {
TagScreenEink_BW_1bpp,
TagScreenEink_BW_2bpp,
TagScreenEink_BW_4bpp,
TagScreenEink_BWY_only, //2bpp, but only 3 colors (BW?Y)
TagScreenEink_BWY_2bpp,
TagScreenEink_BWY_4bpp,
TagScreenEink_BWR_only, //2bpp, but only 3 colors (BW?R)
TagScreenEink_BWR_2bpp,
TagScreenEink_BWR_4bpp,
TagScreenEink_BWY_3bpp,
TagScreenEink_BWR_3bpp,
TagScreenEink_BW_3bpp,
TagScreenPersistentLcd_1bpp,
TagScreenEink_BWY_5colors,
TagScreenEink_BWR_5colors,
TagScreenEink_BWY_6colors,
TagScreenEink_BWR_6colors,
TagScreenTypeOther = 0x7f,
};
#ifndef __packed
#define __packed __attribute__((packed))
#endif
struct TagState {
uint64_t swVer;
uint16_t hwType;
uint16_t batteryMv;
} __packed;
struct TagInfo {
uint8_t protoVer; //PROTO_VER_*
struct TagState state;
uint8_t rfu1[1]; //shall be ignored for now
uint16_t screenPixWidth;
uint16_t screenPixHeight;
uint16_t screenMmWidth;
uint16_t screenMmHeight;
uint16_t compressionsSupported; //COMPR_TYPE_* bitfield
uint16_t maxWaitMsec; //how long tag will wait for packets before going to sleep
uint8_t screenType; //enum TagScreenType
uint8_t rfu[11]; //shall be zero for now
} __packed;
struct AssocInfo {
uint32_t checkinDelay; //space between checkins, in msec
uint32_t retryDelay; //if download fails mid-way wait thi smany msec to retry (IFF progress was made)
uint16_t failedCheckinsTillBlank; //how many fails till we go blank
uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate
uint32_t newKey[4];
uint8_t rfu[8]; //shall be zero for now
} __packed;
#define CHECKIN_TEMP_OFFSET 0x7f
struct CheckinInfo {
struct TagState state;
uint8_t lastPacketLQI; //zero if not reported/not supported to be reported
int8_t lastPacketRSSI; //zero if not reported/not supported to be reported
uint8_t temperature; //zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint8_t rfu[6]; //shall be zero for now
} __packed;
struct PendingInfo {
uint64_t imgUpdateVer;
uint32_t imgUpdateSize;
uint64_t osUpdateVer; //version of OS update avail
uint32_t osUpdateSize;
uint8_t rfu[8]; //shall be zero for now
} __packed;
struct ChunkReqInfo {
uint64_t versionRequested;
uint32_t offset;
uint8_t len;
uint8_t osUpdatePlz : 1;
uint8_t rfu[6]; //shall be zero for now
} __packed;
struct ChunkInfo {
uint32_t offset;
uint8_t osUpdatePlz : 1;
uint8_t rfu; //shall be zero for now
uint8_t data[]; //no data means request is out of bounds of this version no longer exists
} __packed;
#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0]
#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull)
#define HW_TYPE_42_INCH_SAMSUNG (1)
#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8)
#define HW_TYPE_74_INCH_DISPDATA (2)
#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3)
#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP50 (4)
#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP900 (5)
#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_DISPDATA (6)
#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7)
#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_ZBS_026 (8)
#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9)
#define HW_TYPE_154_INCH_ZBS_033 (18)
#define HW_TYPE_154_INCH_ZBS_033_FRAME_MODE (19)
#define HW_TYPE_42_INCH_ZBS_026 (28)
#define HW_TYPE_42_INCH_ZBS_026_FRAME_MODE (29)
#define HW_TYPE_29_INCH_ZBS_025 (10)
#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11)
#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b)
#endif

10
ap_fw/sleep.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef _SLEEP_H_
#define _SLEEP_H_
#include <stdint.h>
void sleepForMsec(uint32_t msec);
void sleepTillInt(void); //assumes you left only one int enabled!
#endif

67
ap_fw/soc/radioCommon.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef _RADIO_COMMON_H_
#define _RADIO_COMMON_H_
#include <stdbool.h>
#include <stdint.h>
#define RADIO_MAX_PACKET_LEN (125) //useful payload, not including the crc
#define ADDR_MODE_NONE (0)
#define ADDR_MODE_SHORT (2)
#define ADDR_MODE_LONG (3)
#define FRAME_TYPE_BEACON (0)
#define FRAME_TYPE_DATA (1)
#define FRAME_TYPE_ACK (2)
#define FRAME_TYPE_MAC_CMD (3)
#define SHORT_MAC_UNUSED (0x10000000UL) //for radioRxFilterCfg's myShortMac
struct MacFcs {
uint8_t frameType : 3;
uint8_t secure : 1;
uint8_t framePending : 1;
uint8_t ackReqd : 1;
uint8_t panIdCompressed : 1;
uint8_t rfu1 : 1;
uint8_t rfu2 : 2;
uint8_t destAddrType : 2;
uint8_t frameVer : 2;
uint8_t srcAddrType : 2;
};
void radioInit(void);
bool radioTx(const void __xdata* packet); //waits for tx end
#pragma callee_saves radioRxAckReset
void radioRxAckReset(void);
#pragma callee_saves radioRxAckGetLast
int16_t radioRxAckGetLast(void); //get seq of lask ack we got or -1 if none
void radioRxFilterCfg(const uint8_t __xdata *filterForLong, uint32_t myShortMac, uint16_t myPan);
void radioRxEnable(__bit on, __bit autoAck);
#pragma callee_saves radioSetTxPower
void radioSetTxPower(int8_t dBm); //-30..+10 dBm
#pragma callee_saves radioSetChannel
void radioSetChannel(uint8_t ch);
void radioRxFlush(void);
int8_t radioRxDequeuePktGet(const void __xdata * __xdata *dstBufP, uint8_t __xdata *lqiP, int8_t __xdata *rssiP);
void radioRxDequeuedPktRelease(void);
#endif

182
ap_fw/soc/zbs243/flash.c Normal file
View File

@@ -0,0 +1,182 @@
#include <stdbool.h>
#include "asmUtil.h"
#include "printf.h"
#include "flash.h"
#include "cpu.h"
#pragma callee_saves flashAddrCheck
static uint8_t flashAddrCheck(uint32_t flashAddr, uint16_t len) __reentrant /* to save ram space */ // return 0xff for error, pgNo
{
uint16_t dstOfst;
uint8_t pgNo;
if (!len)
return 0xff;
//verify address
if ((uint8_t)(flashAddr >> 24))
return 0xff;
switch ((uint8_t)(flashAddr >> 16)) {
case 0x00:
pgNo = ((uint8_t)(flashAddr >> 8)) >> 2;
break;
case 0x80:
if (pgNo)
return 0xff;
pgNo = 0x80;
break;
default:
return 0xff;
}
dstOfst = flashAddr & 0x3ff;
//verify no boundary crossing
if (((uint8_t)((dstOfst + len - 1) >> 8)) >> 2) //yeah...sdcc
return 0xff;
return pgNo;
}
//info page is "at" 0x10000
#pragma callee_saves flashWrite
__bit flashWrite(uint32_t dstAddr, const void __xdata *src, uint16_t len, __bit alsoErase /*whole page */)
{
uint8_t pgNo, cfgPg, speed;
__bit irq;
pgNo = flashAddrCheck(dstAddr, len);
if (pgNo == 0xff)
return false;
irq = IEN_EA;
IEN_EA = false;
speed = CLKSPEED;
CLKSPEED = 0x21; //flash ops only work at this speed
cfgPg = CFGPAGE;
CFGPAGE = 4;
if (alsoErase)
SETTINGS |= 0x38;
else
SETTINGS = 0x18;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = (uint8_t)dstAddr;
FWRDSTH = (((uint16_t)dstAddr) >> 8) & 0x03;
FWRLENL = (uint8_t)(len - 1);
FWRLENH = (len - 1) >> 8;
FWRSRCL = (uint8_t)src;
FWRSRCH = ((uint16_t)src) >> 8;
TRIGGER |= 8; //start
while (!(TCON2 & 0x08)); //wait
TCON2 &=~ 0x48;
SETTINGS &=~ 0x10;
CFGPAGE = cfgPg;
CLKSPEED = speed;
IEN_EA = irq;
return true;
}
#pragma callee_saves flashRead
__bit flashRead(uint32_t srcAddr, void __xdata *dst, uint16_t len)
{
uint8_t pgNo, cfgPg, speed;
__bit irq;
pgNo = flashAddrCheck(srcAddr, len);
if (pgNo == 0xff)
return false;
irq = IEN_EA;
IEN_EA = false;
speed = CLKSPEED;
CLKSPEED = 0x21; //flash ops only work at this speed
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS = 0x8;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = (uint8_t)dst;
FWRDSTH = ((uint16_t)dst) >> 8;
FWRSRCL = (uint8_t)srcAddr;
FWRSRCH = (((uint16_t)srcAddr) >> 8) & 0x03;
FWRLENL = (uint8_t)(len - 1);
FWRLENH = (len - 1) >> 8;
TRIGGER |= 8; //start
while (!(TCON2 & 0x08)); //wait
TCON2 &=~ 0x48;
SETTINGS &=~ 0x10;
CFGPAGE = cfgPg;
CLKSPEED = speed;
IEN_EA = irq;
return true;
}
#pragma callee_saves flashErase
__bit flashErase(uint32_t dstAddr)
{
uint8_t __xdata dummyByte = 0xff;
uint8_t pgNo, cfgPg, speed;
__bit irq;
pgNo = flashAddrCheck(dstAddr, 1);
if (pgNo == 0xff)
return false;
irq = IEN_EA;
IEN_EA = false;
speed = CLKSPEED;
CLKSPEED = 0x21; //flash ops only work at this speed
cfgPg = CFGPAGE;
CFGPAGE = 4;
//this command does an erase AND a write (erase of page, write up to a page at given offset and len)
// i found no way to JUST erase. As len is encoded at "minus one", we cannot ask the hardware to write
// zeor bytes, so we write one - a 0xFF dummy
SETTINGS |= 0x38;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = 0;
FWRDSTH = 0;
FWRLENL = 0;
FWRLENH = 0;
FWRSRCL = (uint8_t)&dummyByte;
FWRSRCH = ((uint16_t)&dummyByte) >> 8;
TRIGGER |= 8; //start
while (!(TCON2 & 0x08)); //wait
TCON2 &=~ 0x48;
SETTINGS &=~ 0x10;
CFGPAGE = cfgPg;
CLKSPEED = speed;
IEN_EA = irq;
return true;
}

23
ap_fw/soc/zbs243/flash.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef _FLASH_ZBS_H_
#define _FLASH_ZBS_H_
#include <stdint.h>
#define FLASH_PAGE_SHIFT (10)
#define FLASH_PAGE_SIZE (1 << FLASH_PAGE_SHIFT)
#define FLASH_NUM_PAGES (64)
#define FLASH_INFOPAGE_ADDR (0x00800000ul)
#pragma callee_saves flashErase
__bit flashErase(uint32_t dstAddr);
#pragma callee_saves flashRead
__bit flashRead(uint32_t srcAddr, void __xdata *dst, uint16_t len);
#pragma callee_saves flashWrite
__bit flashWrite(uint32_t dstAddr, const void __xdata *src, uint16_t len, __bit alsoErase /*whole page */);
#endif

128
ap_fw/soc/zbs243/i2c.c Normal file
View File

@@ -0,0 +1,128 @@
#include "zbs243.h"
#include "i2c.h"
static volatile struct I2cTransaction __xdata * __xdata mCurTrans;
static volatile uint8_t __xdata mNumTrans;
static volatile uint8_t __xdata mResult;
#pragma callee_saves i2cInit
void i2cInit(void)
{
uint8_t bkp;
CLKEN |= 0x10;
bkp = CFGPAGE;
CFGPAGE = 0;
IEN1 |= 4; //int on
I2CUNKNOWN |= 4;
I2CCTL = 0x43; //master mode
I2CSPEED = 0x1a; //100KHz
I2CCTL |= 0x80; //irq on
CFGPAGE = bkp;
}
//this code assumes HW stat emachine acts as it should. for this chip that seems to be true
//this is an 8051 so we value speed & code size over defensive programming. So sue me!
void I2C_IRQ(void) __interrupt (8)
{
uint8_t bkp, sta;
bkp = CFGPAGE;
CFGPAGE = 0;
sta = I2CSTATE;
switch (sta >> 3) {
case 0x08 / 8: //start completed
case 0x10 / 8: //restart completed
I2CBUF = mCurTrans->deviceAddr;
break;
case 0x30 / 8: //byte write was NAKed
if (mCurTrans->numBytes) { //we still had more bytes to send?
I2CCTL |= 0x10; //stop
mResult = I2cNonLastDataByteNAKed;
break;
}
//fallthough since it is ok to NAK last written byte
case 0x28 / 8: //byte write was ACKed
case 0x18 / 8: //addr ACKED in write mode
if (mCurTrans->numBytes) {
mCurTrans->numBytes--;
I2CBUF = *mCurTrans->bytes++;
break;
}
transaction_over:
if (!--mNumTrans) { //byte(s)written and no more transactions? STOP
mResult = I2cOK;
I2CCTL |= 0x10; //stop
}
else { //we have another transaction? RESTART
mCurTrans++;
I2CCTL |= 0x20; //do a restart
}
break;
case 0x20 / 8: //addr NAKEd in write mode
mResult = I2cWrAddrNAKed;
I2CCTL |= 0x10; //stop
break;
case 0x48 / 8: //addr NACKed in read mode
mResult = I2cRdAddrNAKed;
I2CCTL |= 0x10; //stop
break;
case 0x58 / 8: //NAK to RXed byte sent (we still have th ebyte we got in buffer)
case 0x50 / 8: //got byte in read mode
*mCurTrans->bytes++ = I2CBUF;
mCurTrans->numBytes--;
//fallthrough
case 0x40 / 8: //addr ACKED in read mode
if (!mCurTrans->numBytes)
goto transaction_over;
else if (mCurTrans->numBytes == 1)
I2CCTL &=~ 0x04; //NAK
else
I2CCTL |= 0x04; //ACK
break;
default:
mResult = I2cInternalError;
I2CCTL |= 0x10; //stop
mNumTrans = 0;
break;
}
I2CCTL &=~ 0x08;
CFGPAGE = bkp;
}
#pragma callee_saves i2cTransact
enum I2cResult i2cTransact(struct I2cTransaction __xdata *trans, uint8_t nTrans)
{
mCurTrans = trans;
mNumTrans = nTrans;
mResult = I2cOK;
if (nTrans) {
I2CCTL &=~ 0x10; //clear stop
I2CCTL |= 0x20; //issue start
while (mNumTrans); //wait
}
return mResult;
}

34
ap_fw/soc/zbs243/i2c.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef _I2C_H_
#define _I2C_H_
#include <stdint.h>
enum I2cResult {
I2cOK,
I2cWrAddrNAKed,
I2cRdAddrNAKed,
I2cNonLastDataByteNAKed,
I2cInternalError,
};
struct I2cTransaction {
uint8_t deviceAddr;
uint8_t numBytes; //will be updated as we go along so you can figure out what was NAKed
uint8_t __xdata *bytes;
};
#pragma callee_saves i2cInit
void i2cInit(void);
#pragma callee_saves i2cTransact
uint8_t i2cTransact(struct I2cTransaction __xdata *trans, uint8_t nTrans); //returns enum I2cResult
void I2C_IRQ(void) __interrupt (8);
#endif

8
ap_fw/soc/zbs243/make.mk Normal file
View File

@@ -0,0 +1,8 @@
FLAGS += -Isoc/zbs243
FLAGS += -DSOC_ZBS243 --xram-loc 0xe000 --xram-size 0x2000 --model-large
SOURCES += soc/zbs243/soc.c soc/zbs243/wdt.c soc/zbs243/sleep.c soc/zbs243/spi.c soc/zbs243/uart.c soc/zbs243/timer.c soc/zbs243/radio.c
SOURCES += soc/zbs243/flash.c soc/zbs243/temperature.c cpu/8051/random.c cpu/8051/printf.c
CPU = 8051

336
ap_fw/soc/zbs243/radio.c Normal file
View File

@@ -0,0 +1,336 @@
#include "radio.h"
#include "asmUtil.h"
#include "board.h"
#include "cpu.h"
#include "printf.h"
#include "timer.h"
#define RX_BUFFER_SIZE (RADIO_MAX_PACKET_LEN + 1 /* len byte */ + 2 /* RSSI & LQI */)
#define RX_BUFFER_NUM 3
static volatile uint8_t __xdata mRxBufs[RX_BUFFER_NUM][RX_BUFFER_SIZE];
static volatile uint8_t __xdata mLastRSSI, mLastTxedSeq, mRxOn, mRxBufNextR, mRxBufNextW, mRxBufNumFree;
static volatile __bit mAckTimePassed, mGotAck;
// some things look like: https://www.ti.com/lit/ds/symlink/cc2430.pdf
// maybe a licensed and heavily modified version?
// maybe rx on and tx cal?
// see segmented_ota.<<code:00006D7B radioRxEnable>>
void RF_IRQ1(void) __interrupt(4) {
uint8_t cause = RADIO_IRQ4_pending;
static uint8_t __xdata lastRSSI;
RADIO_IRQ4_pending = 0;
if (cause & 0x40) { // ack time happened
// radio will report ACK if we (1) got an ack or (2) sent a packet that did not require it
mAckTimePassed = true;
mGotAck = !!(cause & 0x10);
}
if (cause & 0x20) { // radio has RXed a packet into its internal buffer. vet it quickly and set up DMA
uint8_t len = RADIO_GOTLEN;
if (len < 3 || len >= 0x80 || RADIO_rxFirstByte + 1 != len || !mRxOn || !mRxBufNumFree) { // detect invalid packets right away, or RX being off
RADIO_command = RADIO_CMD_FLUSH_RX_FIFO;
} else {
uint8_t __xdata *buf = mRxBufs[mRxBufNextW];
uint8_t bkp;
buf[0] = len;
bkp = CFGPAGE;
CFGPAGE = 4;
RADIO_RXLEN = len - 1;
RADIO_RXPTRL = ((uint16_t)(buf + 1)) & 0xff;
RADIO_RXPTRH = ((uint16_t)(buf + 1)) >> 8;
TRIGGER |= 4; // start rx dma
TCON2 &= ~4; // without this we trigger next irq too fast and get garbage
CFGPAGE = bkp;
}
}
if ((cause & 0x10) && !(RADIO_curRfState & 0x20)) { // radio got a valid preamble and is RXing a packet. this is our chance to sample some RSSI
uint8_t i;
// we get here if radio is RXing a packet - tells us to capture some RSSI vals.
// Seems there is an offset. Value is signed and offset by 56
for (i = 0; i < 0x3c; i++)
mLastRSSI = RADIO_currentRSSI;
}
}
void RF_IRQ2(void) __interrupt(5) {
uint8_t bck = CFGPAGE;
CFGPAGE = 4;
if (TCON2 & 0x04) { // RX dma over - time to check packet for valid CRC
uint8_t __xdata *buf = mRxBufs[mRxBufNextW];
uint8_t len;
TCON2 &= ~0x04;
RADIO_command = RADIO_CMD_FLUSH_RX_FIFO;
// last byte we got DMAed to us has top bit as flags for "crc ok"
len = buf[0];
if (!(buf[len] & 0x80)) {
// CRC failed on packet
} else {
buf[len - 1] = mLastRSSI - 56;
if (++mRxBufNextW == RX_BUFFER_NUM)
mRxBufNextW = 0;
mRxBufNumFree--;
}
}
if (TCON2 & 0x02) { // TX DMA completed
TCON2 &= ~2;
// nothing to do here
}
if (TCON2 & 0x08) { // radio init over
TCON2 &= ~0x48;
SETTINGS &= ~0x10;
}
CFGPAGE = bck;
}
bool radioTx(const void __xdata *packetP) // waits for tx end
{
const uint8_t __xdata *packet = (const uint8_t __xdata *)packetP;
uint16_t bkp, wait;
__bit irqs;
// this is how to do CCA. we do not bother because fuck it
// this is how WE do CCA. 'Fuck it' still somewhat applies if we don't get a clear channel in a reasonable amount of time
// okay fuck it.
/*
for (uint8_t i = 0; i < 0x80; i++) {
if (!(RADIO_curRfState & 1)) {
//pr("radio CCA fail\n");
timerDelay(TIMER_TICKS_PER_SECOND / 2000);
//return;
}
}
*/
mAckTimePassed = false;
mGotAck = false;
mLastTxedSeq = packet[3];
bkp = CFGPAGE;
CFGPAGE = 4;
irqs = IEN_EA;
IEN_EA = 0;
RADIO_TXLEN = packet[0] - 2;
RADIO_TXPTRL = ((uint16_t)packet) & 0xff;
RADIO_TXPTRH = ((uint16_t)packet) >> 8;
RADIO_command = RADIO_CMD_LOAD_TX_FIFO;
TRIGGER |= 2; // start TX fifo DMA
IEN_EA = irqs;
CFGPAGE = bkp;
//RADIO_unk_C8 = 0xff; /// stock fw does this but seems unnecessary
// wait for tx to start
wait = 0;
wait--;
do {
if (RADIO_curRfState & 0x80)
break;
} while (--wait);
// wait for tx to end
if (wait) {
while (!mAckTimePassed)
;
return true;
} else {
return false;
}
//RADIO_unk_C8 = 0x7f; /// stock fw does this but seems unnecessary
}
void radioRxAckReset(void) {
mGotAck = false;
}
int16_t radioRxAckGetLast(void) {
if (mGotAck)
return (uint16_t)mLastTxedSeq;
else
return -1;
}
void radioRxFilterCfg(const uint8_t __xdata *filterForLong, uint32_t myShortMac, uint16_t myPan) {
uint16_t shortMac = (myShortMac == SHORT_MAC_UNUSED) ? 0xffff : myShortMac;
uint8_t i;
RADIO_PANID_Hi = myPan >> 8;
RADIO_PANID_Lo = myPan;
RADIO_ownShortAddress_Hi = shortMac >> 8;
RADIO_ownShortAddress_Lo = shortMac;
for (i = 0; i < 8; i++)
((volatile uint8_t __xdata *)&RADIO_ownMac_7)[i] = filterForLong[(uint8_t)((uint8_t)7 - (uint8_t)i)];
}
void radioRxEnable(__bit on, __bit autoAck) {
if (!autoAck) {
pr("auto ack forced for now\n");
while (1)
;
}
mRxOn = on;
}
void radioSetTxPower(int8_t dBm) {
if (dBm < -27)
dBm = -27;
else if (dBm > 8)
dBm = 8;
dBm += 27;
RADIO_txPower = (uint8_t)(((uint8_t)dBm) + 3) / 5;
}
void radioSetChannel(uint8_t ch) {
static const uint8_t perChannelSetting1[] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33, 0x33};
static const uint8_t perChannelSetting2[] = {4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2};
if (ch < RADIO_FIRST_CHANNEL || ch >= RADIO_FIRST_CHANNEL + RADIO_NUM_CHANNELS)
return;
RADIO_channel = ch; // configmed to be at least RX channel
RADIO_command = RADIO_CMD_RECEIVE;
RADIO_perChannelSetting1 = perChannelSetting1[ch - 11];
RADIO_perChannelSetting2 = perChannelSetting2[ch - 11];
}
void radioRxFlush(void) {
mRxBufNumFree = RX_BUFFER_NUM;
}
int8_t radioRxDequeuePktGet(const void __xdata *__xdata *dstBufP, uint8_t __xdata *lqiP, int8_t __xdata *rssiP) {
const uint8_t __xdata *buf = mRxBufs[mRxBufNextR];
uint8_t lqi, len = buf[0];
if (mRxBufNumFree == RX_BUFFER_NUM)
return -1;
lqi = (buf[len] & 0x7f);
*lqiP = lqi;
*rssiP = buf[len - 1];
*dstBufP = buf + 1;
return len - 2;
}
void radioRxDequeuedPktRelease(void) {
if (++mRxBufNextR == RX_BUFFER_NUM)
mRxBufNextR = 0;
__critical {
mRxBufNumFree++;
}
}
void radioInit(void) {
uint8_t bkp;
mRxBufNextW = 0;
mRxBufNextR = 0;
mRxBufNumFree = RX_BUFFER_NUM;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
RADIO_unk_F0 |= 0x80;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
CFGPAGE = 0;
RESET &= ~4;
RESET |= 4;
RESET &= ~4;
timerDelay(TIMER_TICKS_PER_SECOND / 10000);
RADIO_RadioPowerCtl |= 4;
bkp = CFGPAGE;
CFGPAGE = 4;
SETTINGS |= 2;
RADIO_INITSEQ0 = 2;
RADIO_INITSEQ1 = 0xFA;
RADIO_INITSEQ2 = 0xDD;
SETTINGS |= 4;
RADIO_INITSEQ3 = 1;
RADIO_INITSEQ4 = 0xFA;
RADIO_INITSEQ5 = 0xDD;
IEN_RF2 = 1;
CFGPAGE = bkp;
RADIO_command = 0xC1;
RADIO_unk_C1 = 0x02;
RADIO_calibration_C2 = 0xf7; // mdmctrl0L?
RADIO_calibration_C3 = 0x05;
RADIO_calibration_C4 = 0x35;
RADIO_calibration_C5 = 0x24;
RADIO_calibration_C6 = 0x33;
RADIO_calibration_C7 = 0x70;
RADIO_unk_CA = 0x58;
RADIO_perChannelSetting2 = 0x02;
RADIO_unk_CD = (RADIO_unk_CD & ~7) | (0x11 & 7);
RADIO_txPower = 0;
RADIO_calibration_CF = 0x30;
RADIO_calibration_D0 = 0x00;
RADIO_calibration_D1 = 0x49;
RADIO_calibration_D2 = 0x06;
RADIO_unk_D7 = 0x43;
RADIO_unk_E2 = 0x08; // setting bit 0x40 breaks rx entirely
RADIO_unk_83 = 0xe5; // maybe sync word?
RADIO_unk_C8 = 0x7f; // setting bit 0x80 breaks rx entirely
RADIO_calibration_81 = 0xf0; // removing bit 0x20 breask rx and tx
RADIO_FLAGS |= 0x08;
RADIO_unk_D8 = 0;
RADIO_calibration_9D = 0x3f; // firts untpouched calib val
RADIO_calibration_A1 = 0x04;
RADIO_calibration_94 = 0x7f; // IOCFG0 ?
RADIO_unk_CE = 0x55;
RADIO_calibration_D3 = 0x30;
RADIO_calibration_D4 = 0xcc;
RADIO_calibration_D5 = 0xf6;
RADIO_calibration_86 = 0x40; // setting this to values like 0x20, 0x80, and 0x60 breaks auto-ack and maybe tx
RADIO_calibration_95 = 0x08;
RADIO_calibration_96 = 0xd3;
RADIO_calibration_70 = 0x01;
RADIO_calibration_71 = 0x40;
RADIO_unk_A6 = 0x00;
RADIO_command = 0xC8;
RADIO_command = 0xC7;
RADIO_command = 0xC6;
RADIO_unk_AF = 0x60; // int enable (we need 0x60)
RADIO_unk_AF |= 0x10;
IEN_RF1 = 1;
RADIO_FLAGS |= 8;
}

22
ap_fw/soc/zbs243/radio.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef _RADIO_H_
#define _RADIO_H_
#include <stdbool.h>
#include <stdint.h>
void RF_IRQ1(void) __interrupt (4);
void RF_IRQ2(void) __interrupt (5);
#define RADIO_PAD_LEN_BY 2
#include "../radioCommon.h"
#endif

66
ap_fw/soc/zbs243/sleep.c Normal file
View File

@@ -0,0 +1,66 @@
#include "sleep.h"
#include "cpu.h"
void sleepForMsec(uint32_t length)
{
__bit irqEn = IEN_EA;
uint8_t prescaler, cfgPg;
IEN_EA = 0;
if (!length)
length = 0xfffffffful;
RADIO_IRQ4_pending = 0;
UNK_C1 &=~ 0x81;
TCON &=~ 0x20;
cfgPg = CFGPAGE;
CFGPAGE = 4;
RADIO_command = 0xCA;
RADIO_command = 0xC5;
if (length <= 0x00008000ul) {
length <<= 5;
prescaler = 0x56; //0x56 = one tick is 1/32k of sec
}
else {
if (length != 0xfffffffful)
length += 500;
length /= 1000;
prescaler = 0x16; //0x16 = one tick is 1 second
}
if (length > 0x000fffff) {
RADIO_SleepTimerLo = 0xff;
RADIO_SleepTimerMid = 0xff;
RADIO_SleepTimerHi = 0x0f;
}
else {
RADIO_SleepTimerLo = length;
RADIO_SleepTimerMid = length >> 8;
RADIO_SleepTimerHi = ((uint8_t)(length >> 16)) & 0x0f;
}
__asm__("nop");
RADIO_SleepTimerSettings = prescaler;
__asm__("nop\nnop\nnop\nnop\n");
RADIO_SleepTimerSettings |= 0x80;
__asm__("nop\nnop\n");
RADIO_RadioPowerCtl = 0x44;
__asm__("nop\nnop\n");
CFGPAGE = cfgPg;
//make sure time does not run backwards
TL0 = 0x0;
TH0 = 0xFF;
while (TH0 == 0xFF);
UNK_C1 |= 0x81;
TCON |= 0x20;
IEN_EA = irqEn;
}

12
ap_fw/soc/zbs243/soc.c Normal file
View File

@@ -0,0 +1,12 @@
#include "asmUtil.h"
#include "printf.h"
#include "screen.h"
#include "cpu.h"
void clockingAndIntsInit(void)
{
IEN0 = 0;
CLKEN = 0x00; //timers only for now
CLKSPEED = 0x01; //fast crystal
}

29
ap_fw/soc/zbs243/soc.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _SOCi_H_
#define _SOCi_H_
#define PDATA
#include "zbs243.h"
#include <stdint.h>
#pragma callee_saves clockingAndIntsInit
void clockingAndIntsInit(void);
#pragma callee_saves rndGen8
uint8_t rndGen8(void);
#pragma callee_saves rndGen32
uint32_t rndGen32(void);
#pragma callee_saves rndSeed
void rndSeed(uint8_t seedA, uint8_t seedB);
#pragma callee_saves selfUpdate
void selfUpdate(void);
void TEMP_ISR(void) __interrupt (10);
#endif

32
ap_fw/soc/zbs243/spi.c Normal file
View File

@@ -0,0 +1,32 @@
#include "spi.h"
#include "cpu.h"
void spiInit(void)
{
uint8_t bcp;
//clock it up
CLKEN |= 0x08;
//enable the unit
bcp = CFGPAGE;
CFGPAGE = 4;
SPIENA = 0x81;
CFGPAGE = bcp;
}
uint8_t spiByte(uint8_t val)
{
uint8_t bcp = CFGPAGE;
CFGPAGE = 4;
SPITX = val;
SPICFG = 0xa0; //spi at 4mhz, mode 0
while(SPICFG & 0x20);
val = SPIRX;
CFGPAGE = bcp;
return val;
}

16
ap_fw/soc/zbs243/spi.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _SPI_ZBS_H_
#define _SPI_ZBS_H_
#include <stdint.h>
//pre-configured for 4mhz mode 0, but can be changed
#pragma callee_saves spiInit
void spiInit(void);
#pragma callee_saves spiByte
uint8_t spiByte(uint8_t val);
#endif

View File

@@ -0,0 +1,113 @@
#include "asmUtil.h"
#include "flash.h"
#include "timer.h"
#include "adc.h"
#include "cpu.h"
static volatile uint8_t __xdata mTempRet[4];
void TEMP_ISR(void) __interrupt (10)
{
uint8_t i;
i = CFGPAGE;
CFGPAGE = 4;
mTempRet[0] = TEMPRETH;
mTempRet[1] = TEMPRETL;
CFGPAGE = i;
IEN1 &=~ 0x10;
}
int8_t adcSampleTemperature(void)
{
uint16_t sum = 0;
uint8_t i;
CLKEN |= 0x80;
//adcConfig
i = CFGPAGE;
CFGPAGE = 4;
TEMPCFG = 0x81; //0x08 set when done
TEMPCAL2 = 0x22;
TEMPCAL1 = 0x55;
TEMPCAL4 = 0;
TEMPCAL3 = 0;
TEMPCAL6 = 3;
TEMPCAL5 = 0xff;
TEMPCFG &=~ 0x08;
CFGPAGE = i;
IEN1 &=~ 0x10;
//adcConfig over
/*
TEMPCAL2: (seemingly analog settings: gain + ???)
removing 0x02 lowers vals a little (9%) \__ these stack just as you'd expect
setting 0x04 moves the values up a bit (18%) /
setting 0x08 adds 2.5% or so
0x30 is ADC gain/bitness. (x4 for each increment)
0x80 seems to make no difference
TEMPCAL1: (seemingly analog settings)
removing 1 decreases value by 5.7% \__ these stack
removing 4 decreases by 22% /
removing 0x10 increments value by 1%
removnig 0x40 increases by 5%
using 0xaa increases width but decreases average
other TEMPCALx values seem to have no effect
*/
for (i = 0; i < 9; i++) {
//int on
IEN1 |= 0x10;
//wait for it to self-turn-off
while (IEN1 & 0x10);
if (i) { //skip first
sum += ((uint16_t)mathPrvU8bitswap(mTempRet[0])) << 2;
if (mTempRet[1] & 1)
sum += 2;
if (mTempRet[1] & 2)
sum += 1;
}
timerDelay(TIMER_TICKS_PER_SECOND / 10000);
}
//turn it off
CLKEN &=~ 0x80;
//reuse tempRet to get calib data
if (!flashRead(FLASH_INFOPAGE_ADDR + 0x0b, mTempRet, 4) || (mTempRet[0] == 0xff && mTempRet[1] == 0xff) || (mTempRet[2] == 0xff && mTempRet[3] == 0xff)) {
//no calibration data - reporting 20 degrees, same as stock firmware
return 20;
}
else {
uint16_t __xdata valB = mathPrvU16from2xU8(mTempRet[0], mTempRet[1]); //temp sensor reading at 30 degrees C
uint16_t __xdata valD = mathPrvU16from2xU8(mTempRet[2], mTempRet[3]); //temp sensor reading at 45 degrees C
uint16_t __xdata slope = valD - valB;
sum = (sum + 4) / 8; //average it
//we can get 1/10 degree accuracy as this is well calibrated, but our api only need degrees so let's do that
/*
//high-precision code is this:
if (sum > valB) //our math routines are unsigned
temp = (uint16_t)mathPrvDiv32x16(mathPrvMul16x16(sum - valB, 150) + (slope / 2), slope) + 300;
else
temp = 300 - (uint16_t)mathPrvDiv32x16(mathPrvMul16x16(valB - sum, 150) + (slope / 2), slope);
*/
if (sum > valB) //our math routines are unsigned
return mathPrvDiv16x8(5 + (uint16_t)mathPrvDiv32x16(mathPrvMul16x16(sum - valB, 150) + (slope / 2), slope), 10) + 30;
else
return 30 - mathPrvDiv16x8(5 + (uint16_t)mathPrvDiv32x16(mathPrvMul16x16(valB - sum, 150) + (slope / 2), slope), 10);
}
}

65
ap_fw/soc/zbs243/timer.c Normal file
View File

@@ -0,0 +1,65 @@
#include "timer.h"
#include "cpu.h"
static volatile uint16_t mTmrHi;
void T0_ISR(void) __interrupt (1)
{
TCON &=~ 0x20; //clear flag
mTmrHi++;
}
uint32_t timerGet(void)
{
union {
struct {
uint8_t tL;
uint8_t tH;
uint16_t hi;
};
uint32_t ret;
} val;
do {
val.hi = mTmrHi;
val.tH = TH0;
val.tL = TL0; //read order is important due ot hardware buffering
} while (val.hi != mTmrHi || val.tH != TH0);
return val.ret;
}
uint8_t timerGetLowBits(void)
{
return TL0;
}
void timerInit(void)
{
//clock up timers
CLKEN |= 0x01;
//stop and clear int flags
TCON &=~ 0xf0;
//reset
mTmrHi = 0;
T0 = 0;
//timer 0 in 16 bit mode, timer 1 off
TMOD = 0x31;
//start
TCON |= 0x10;
//int on
IEN_TMR0 = 1;
}
void timerDelay(uint32_t ticks)
{
uint32_t start = timerGet();
while (timerGet() - start <= ticks);
}

24
ap_fw/soc/zbs243/timer.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef _TIMER_H_
#define _TIMER_H_
#include <stdint.h>
#define TIMER_TICKS_PER_SECOND (16000000 / 12) //overflows every 53 minutes
//this is a requirement by SDCC. is this prototype is missing when compiling main(), we get no irq handler
void T0_ISR(void) __interrupt (1);
void timerInit(void);
#pragma callee_saves timerGet
uint32_t timerGet(void);
#pragma callee_saves timerGetLowBits
uint8_t timerGetLowBits(void); //probaly only useful for random seeds
void timerDelay(uint32_t ticks);
#endif

97
ap_fw/soc/zbs243/uart.c Normal file
View File

@@ -0,0 +1,97 @@
#include "uart.h"
#include "cpu.h"
#include "stdbool.h"
#include "string.h"
void uartInit(void) {
// clock it up
CLKEN |= 0x20;
// configure
UARTBRGH = 0x00; // at the speeds we want, this reg is always 0
//UARTBRGL = 0x8A; // 115200
UARTBRGL = 69; // nice. 230400 baud
// UARTBRGL = 0x1F; // 500kbaud, unreliable RX here...
// UARTBRGL = 39; //400kbaud? also unreliable...
// UARTBRGL = 49; // 320kbaud? nope. bummer.
UARTSTA = 0x12; // also set the "empty" bit else we wait forever for it to go up
IEN_UART0 = 1;
}
extern uint8_t __xdata blockbuffer[];
extern bool __xdata blockRequestInProgress;
volatile uint8_t txtail = 0;
volatile uint8_t txhead = 0;
uint8_t __xdata txbuf[256] = {0};
volatile uint8_t __idata rxtail = 0;
volatile uint8_t __idata rxhead = 0;
uint8_t __xdata rxbuf[256] = {0};
void uartTx(uint8_t val) {
__critical {
txbuf[txhead] = val;
if (txhead == txtail) {
UARTBUF = val;
}
txhead++;
}
}
uint8_t uartRx() {
if (rxhead == rxtail) {
return 0;
} else {
uint8_t ret = rxbuf[rxtail];
rxtail++;
return ret;
}
}
uint8_t uartBytesAvail() {
return rxhead - rxtail;
}
uint8_t* __idata blockp;
uint8_t __idata cmd[3];
bool __idata bypass = false;
void checkcommand(uint8_t rx) {
for (uint8_t c = 0; c < 2; c++) {
cmd[c] = cmd[c + 1];
}
cmd[2] = rx;
if (strncmp(cmd, ">D>", 3) == 0) {
blockp = blockbuffer;
bypass = true;
}
}
void UART_IRQ1(void) __interrupt(0) {
if (UARTSTA & 1) { // RXC
UARTSTA &= 0xfe;
if (bypass) {
*blockp++ = UARTBUF;
if (blockp == (blockbuffer+4100)) {
bypass = false;
blockRequestInProgress = false;
}
} else {
rxbuf[rxhead] = UARTBUF;
rxhead++;
checkcommand(UARTBUF);
}
}
if (UARTSTA & 2) { // TXC
UARTSTA &= 0xfd;
txtail++;
if (txhead != txtail) {
UARTBUF = txbuf[txtail];
}
}
}

25
ap_fw/soc/zbs243/uart.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef _UART_ZBS_H_
#define _UART_ZBS_H_
#include <stdint.h>
//pre-configured for 115200 8n1
//RX can be done but i do not need it
#pragma callee_saves uartInit
void uartInit(void);
#pragma callee_saves uartTx
void uartTx(uint8_t val);
void UART_IRQ1(void) __interrupt (0);
#pragma callee_saves uartBytesAvail
uint8_t uartBytesAvail(void);
#pragma callee_saves uartRX
uint8_t uartRx();
#endif

59
ap_fw/soc/zbs243/wdt.c Normal file
View File

@@ -0,0 +1,59 @@
#include "cpu.h"
#include "wdt.h"
void wdtOn(void)
{
uint8_t cfgPageBck;
cfgPageBck = CFGPAGE;
CFGPAGE = 4;
WDTCONF |= 0x80;
WDTENA = 1;
CFGPAGE = cfgPageBck;
}
void wdtOff(void)
{
uint8_t cfgPageBck;
cfgPageBck = CFGPAGE;
CFGPAGE = 4;
WDTENA = 0;
WDTCONF &=~ 0x80;
CFGPAGE = cfgPageBck;
}
void wdtPet(void)
{
uint8_t cfgPageBck;
cfgPageBck = CFGPAGE;
CFGPAGE = 4;
WDTPET = 0;
CFGPAGE = cfgPageBck;
}
void wdtSetResetVal(uint32_t val) //also pets it
{
uint8_t cfgPageBck;
cfgPageBck = CFGPAGE;
CFGPAGE = 4;
WDTPET = 0;
WDTRSTVALH = val >> 16;
WDTRSTVALM = val >> 8;
WDTRSTVALL = val;
CFGPAGE = cfgPageBck;
}
void wdtDeviceReset(void)
{
CFGPAGE = 4;
WDTCONF = 0x80;
WDTENA = 1;
WDTRSTVALH = 0xff;
WDTRSTVALM = 0xff;
WDTRSTVALL = 0xff;
while(1);
}

289
ap_fw/soc/zbs243/zbs243.h Normal file
View File

@@ -0,0 +1,289 @@
#ifndef _ZBS243_H_
#define _ZBS243_H_
#include <stdint.h>
//P2 selects XPAGE which sucks for us
//IRAM is not at all mapped into XRAM
//id page is not in xram either
static __idata __at (0x00) unsigned char R0;
static __idata __at (0x01) unsigned char R1;
static __idata __at (0x02) unsigned char R2;
static __idata __at (0x03) unsigned char R3;
static __idata __at (0x04) unsigned char R4;
static __idata __at (0x05) unsigned char R5;
static __idata __at (0x06) unsigned char R6;
static __idata __at (0x07) unsigned char R7;
__sfr __at (0xf0) B;
__sfr __at (0xe0) ACC;
__sfr __at (0x82) DPL;
__sfr __at (0x83) DPH;
__sfr __at (0x84) DPL1;
__sfr __at (0x85) DPH1;
__sfr __at (0x92) DPS; //low bit switched DPTR
__sfr __at (0x87) PCON; //lower 4 bits as per 8051 spec, bit4 set disables xram
__sfr __at (0xb2) PERFMON0;
__sfr __at (0xb3) PERFMON1;
__sfr __at (0xb4) PCH; //current PC...yes...
__sfr __at (0xb5) PCL;
__sfr __at (0xb6) PERFMON4;
__sbit __at (0x80) P0_0;
__sbit __at (0x81) P0_1;
__sbit __at (0x82) P0_2;
__sbit __at (0x83) P0_3;
__sbit __at (0x84) P0_4;
__sbit __at (0x85) P0_5;
__sbit __at (0x86) P0_6;
__sbit __at (0x87) P0_7;
__sbit __at (0x90) P1_0;
__sbit __at (0x91) P1_1;
__sbit __at (0x92) P1_2;
__sbit __at (0x93) P1_3;
__sbit __at (0x94) P1_4;
__sbit __at (0x95) P1_5;
__sbit __at (0x96) P1_6;
__sbit __at (0x97) P1_7;
__sbit __at (0xa0) P2_0;
__sbit __at (0xa1) P2_1;
__sbit __at (0xa2) P2_2;
__sbit __at (0xa3) P2_3;
__sbit __at (0xa4) P2_4;
__sbit __at (0xa5) P2_5;
__sbit __at (0xa6) P2_6;
__sbit __at (0xa7) P2_7;
__sbit __at (0xA8) IEN_UART0;
__sbit __at (0xA9) IEN_TMR0;
__sbit __at (0xAB) IEN_TMR1;
__sbit __at (0xAC) IEN_RF1; //enable for RF interrupt #1 (irq #2)
__sbit __at (0xAD) IEN_RF2; //enable for RF interrupt #2 (irq #5)
__sbit __at (0xAF) IEN_EA;
//gpio configs (all in cfg page 0 only)
__sfr __at (0x80) P0; //RO for input pins, WO for output pins (written state not read back)
__sfr __at (0x90) P1;
__sfr __at (0xA0) P2; //disabled ot avoid accidental use
__sfr __at (0xA3) P0LVLSEL; //pin change interrupt edge desired. 0 - rising 1 - falling
__sfr __at (0xA4) P1LVLSEL;
__sfr __at (0xA5) P2LVLSEL;
__sfr __at (0xA6) P0INTEN; //pin change interrupt enable (1 = enabled). will wake device from sleep
__sfr __at (0xA7) P1INTEN;
__sfr __at (0xA9) P2INTEN;
__sfr __at (0xAA) P0CHSTA; //pin change status 1 = changed (remember to clear in ISR & before enabling ISR)
__sfr __at (0xAB) P1CHSTA;
__sfr __at (0xAC) P2CHSTA;
__sfr __at (0xAD) P0FUNC; //1 = func, 0 = gpio
__sfr __at (0xAE) P1FUNC;
__sfr __at (0xAF) P2FUNC;
__sfr __at (0xB9) P0DIR; //1 = in, 0 = out
__sfr __at (0xBA) P1DIR;
__sfr __at (0xBB) P2DIR;
__sfr __at (0xBC) P0PULL; //1 = pull up
__sfr __at (0xBD) P1PULL;
__sfr __at (0xBE) P2PULL;
__sfr __at (0xA0) XPAGE; //sadly this is accurate! same as P2
//these are both avail in both config pages
__sfr __at (0xA8) IEN0;
__sfr __at (0xA1) IEN1; //low 1, bits unknown, top 7 bits eable irqs (7..13)
__sfr __at (0x8E) CLKSPEED; //top bit likely selects osc (1 = crystal??), next 3 set divisor FOR CPU, not periphs, bottom 4 unknown
__sfr __at (0xB7) CLKEN; //one bit per periph to enable clocks. 0x08 - spi, 0x10 - i2c, 0x20 - uart, 0x80 temp sensor, 0x01 - in-cpu-timers(T0 & T1)
__sfr __at (0x8F) RESET; //resets periphs AND stores reset cause. bit 0x01 - we were reset due to WDT, 0x04 - resets radio (Active high), 0x02 resets cpu core
//wdt (accessible in CFG page 4), runs at about 62KHz. counter counts up, overflow causes reset
__sfr __at (0xBA) WDTENA; //low bit is used for WDT off. same SFR addr used as P1DIR when cfg page 0
__sfr __at (0xBB) WDTPET; //write 0 to pet, write 0xff to reset immediately. same SFR addr used as P2DIR when cfg page 0
__sfr __at (0xBC) WDTRSTVALL; //wdt reset value low
__sfr __at (0xBD) WDTRSTVALM; //wdt reset value mid
__sfr __at (0xBE) WDTRSTVALH; //wdt reset value high
__sfr __at (0xBF) WDTCONF; //top bit enable, 0x08 to hang instead of reset. top bit only accessible in CFG page 4
//SPI (accessible in CFG page 4)
__sfr __at (0xEB) SPIUNKNOWN; //existing code sets 0x08 after a byte tx. no idea why. not doing it works
__sfr __at (0xEC) SPICFG; //0x80 - 1, 0x40 - 0, 0x20 - do TX (cleared on done), {0x10,0x08}) - clock div {4mhz,2mhz,1mhz,500khz}, 0x04 - CPHA, 0x02 - CPOL, 0x01 ?? (breaks rx, maybe bidirectional or rx off)
__sfr __at (0xED) SPIENA; //0x80 - 1, 0x01 - unit enable. stock code also enables/disables after every byte but i do not think we need to
__sfr __at (0xEE) SPITX; //tx buffer
__sfr __at (0xEF) SPIRX; //rx buffer
//I2C (accessible in CFG page 0)
//it is in theory possible to drive it without IRQs but there seem to be hw bugs in figuring out when handling is needed, so i wouldn't. stock also doesn't
__sfr __at (0x91) I2CSTATE; //top 5 bits are current state: {???, START_SENT, RESTART_SENT, WR_ADDR_ACKED, WR_ADDR_NAKED, TX_BYTE_ACKED, TX_BYTE_NAKED, unused, RD_ADDR_ACKED, RD_ADDR_NAKED, RXED_BYTE}
//0x93 written by 0xe0 by stock but seems not needed
__sfr __at (0x94) I2CBUF; //data for RX/TX
__sfr __at (0x95) I2CCTL; //0x80 = irqOn (#8), 0x40 = master(?), 0x20 = start/restart, 0x10 = stop, 0x08 = irqReqd, 0x04 = sendAck (to RXed btes)
__sfr __at (0x96) I2CSPEED; //bottom 3 bits = dividerA. next 4 are dividerB. clock = 16MHz / ((dividerB ? 10 *(1 + dividerB) : 12) << dividerA)
__sfr __at (0xA2) I2CUNKNOWN; //4 ORRED at init or it will not work
//uart is basically hard wired to be a uart. clock is divided from 16MHz. only bottom 4 bits of BRGH are used. divisor is (BRGH:BRGL + 1)
__sbit __at (0x98) UART_RXF;
__sbit __at (0x99) UART_TXE;
__sfr __at (0x98) UARTSTA; //0x10 needed. 0x02 - have tx space, 0x01 - have rx byte. both can cause interrupts. both need to be cleared in irq. flag bits do NOT work if accessed using "jbc"
__sfr __at (0x99) UARTBUF; //fifo access
__sfr __at (0x9A) UARTBRGL;
__sfr __at (0x9B) UARTBRGH; //divisor for uart
//timers (same as 8051-classic)
__sfr __at (0x88) TCON;
__sfr __at (0x89) TMOD;
__sfr __at (0x8A) TL0;
__sfr __at (0x8B) TL1;
__sfr __at (0x8C) TH0;
__sfr __at (0x8D) TH1;
__sfr16 __at (0x8C8A) T0;
__sfr16 __at (0x8D8B) T1; //used by timer code for storage
//flash writing (must be done with CFGPAGE == 4). each page is 0x400 bytes
__sfr __at (0xD8) FPGNO; //page number (0x00..0x3f for main flash, 0x80 for info block)
__sfr __at (0xD9) FWRSRCL; //(flash source data for write).lo for write, (flash start offset in page).lo for read
__sfr __at (0xDA) FWRSRCH; //(flash source data for write).hi for write, (flash start offset in page).hi for read
__sfr __at (0xDB) FWRDSTL; //(flash start offset in page).lo for write, (dest xram addr).lo for read
__sfr __at (0xDC) FWRDSTH; //(flash start offset in page).hi for write, (dest xram addr).hi for read
__sfr __at (0xDD) FWRLENL; //(num bytes to write minus 1).lo
__sfr __at (0xDE) FWRLENH; //(num bytes to write minus 1).hi
__sfr __at (0xDF) FWRTHREE; //always written to three
//temp sensor (CLKEN bit 0x80). these only accessible in config page 4
__sfr __at (0xE6) TEMPCAL1; //purpose guessed
__sfr __at (0xE7) TEMPCAL2; //purpose guessed
__sfr __at (0xF7) TEMPCFG; //purpose guessed
__sfr __at (0xF8) TEMPRETH; //has high 8 bits of result. bitswapped
__sfr __at (0xF9) TEMPRETL; //has low two bits of result in lower 2 bits. bitswapped
__sfr __at (0xFB) TEMPCAL3; //purpose guessed
__sfr __at (0xFC) TEMPCAL4; //purpose guessed
__sfr __at (0xFD) TEMPCAL5; //purpose guessed
__sfr __at (0xFE) TEMPCAL6; //purpose guessed
//radio regs (cfg page 4)
__sfr __at (0xC9) RADIO_TXPTRL; //xram pointer to tx buffer's "length" byte. low byte
__sfr __at (0xCA) RADIO_TXPTRH; //xram pointer to tx buffer's "length" byte. high byte
__sfr __at (0xCB) RADIO_INITSEQ1;
__sfr __at (0xCC) RADIO_INITSEQ2;
__sfr __at (0xCD) RADIO_TXLEN; //data bytes to send (not incl "len" byte or crc)
__sfr __at (0xCE) RADIO_INITSEQ0;
__sfr __at (0xD1) RADIO_INITSEQ4;
__sfr __at (0xD2) RADIO_INITSEQ5;
__sfr __at (0xD3) RADIO_RXPTRL; //xram pointer to tx buffer's "length" byte. low byte
__sfr __at (0xD4) RADIO_RXPTRH; //xram pointer to tx buffer's "length" byte. high byte
__sfr __at (0xD5) RADIO_RXLEN; //data bytes to RX (not incl "len" byte or crc) 2 more than this will be RXed
__sfr __at (0xD6) RADIO_INITSEQ3;
//radio regs (any cfg page)
__sfr __at (0xFA) RADIO_GOTLEN; //size packet we GOT (incl len byte)
//shared regs
__sfr __at (0xC1) UNK_C1; //in cfg page 4 only, purpose unknown
__sfr __at (0xD7) TRIGGER; //in both cfg page, seach bit starts something. 0x08 starts flash op, 0x04 - start radio rx fifo dma, 0x02 - start radio tx fifo dma
//in cfg page 4
__sfr __at (0xC7) SETTINGS; //misc settings incl flasg access. 0x18 is flash write, 0x08 is flash read,
__sfr __at (0xCF) TCON2; //interrupt flags (0x08 is flash write)
__sfr __at (0xFF) CFGPAGE; //partially swaps SFR set to another. only 0x04 bit is used
/*
84 85 92 B2 B3 B4 B5 B6 B8 are visible on both pages
93 98 9D 9E 9F B9 C1-C7 E0-E9 are swapped
86 9C B0 B1 C0 C8 EA F1-F6 ??????
*/
//radio MMIO
static __xdata __at (0xdf48) unsigned char RADIO_command;
static __xdata __at (0xdf70) unsigned char RADIO_calibration_70;
static __xdata __at (0xdf71) unsigned char RADIO_calibration_71;
static __xdata __at (0xdf80) unsigned char RADIO_FLAGS;
static __xdata __at (0xdf81) unsigned char RADIO_calibration_81;
static __xdata __at (0xdf83) unsigned char RADIO_unk_83;
static __xdata __at (0xdf84) unsigned char RADIO_currentRSSI;
static __xdata __at (0xdf86) unsigned char RADIO_calibration_86;
static __xdata __at (0xdf88) unsigned char RADIO_ownMac_7;
static __xdata __at (0xdf89) unsigned char RADIO_ownMac_6;
static __xdata __at (0xdf8a) unsigned char RADIO_ownMac_5;
static __xdata __at (0xdf8b) unsigned char RADIO_ownMac_4;
static __xdata __at (0xdf8c) unsigned char RADIO_ownMac_3;
static __xdata __at (0xdf8d) unsigned char RADIO_ownMac_2;
static __xdata __at (0xdf8e) unsigned char RADIO_ownMac_1;
static __xdata __at (0xdf8f) unsigned char RADIO_ownMac_0;
static __xdata __at (0xdf90) unsigned char RADIO_PANID_Hi;
static __xdata __at (0xdf91) unsigned char RADIO_PANID_Lo;
static __xdata __at (0xdf92) unsigned char RADIO_ownShortAddress_Hi;
static __xdata __at (0xdf93) unsigned char RADIO_ownShortAddress_Lo;
static __xdata __at (0xdf94) unsigned char RADIO_calibration_94;
static __xdata __at (0xdf95) unsigned char RADIO_calibration_95;
static __xdata __at (0xdf96) unsigned char RADIO_calibration_96;
static __xdata __at (0xdf98) unsigned char RADIO_rxFirstByte; // first byte of the message actualyl RXed
static __xdata __at (0xdf9b) unsigned char RADIO_curRfState; // RFSTATUS ?
static __xdata __at (0xdf9d) unsigned char RADIO_calibration_9D;
static __xdata __at (0xdfa1) unsigned char RADIO_calibration_A1;
static __xdata __at (0xdfa5) unsigned char RADIO_unk_rxAckSta; //in irqh4, when RADIO_IRQ4_pending has 0x40 set, this is 0x04 if we got ack, and 0x02 if not
static __xdata __at (0xdfa6) unsigned char RADIO_unk_A6;
static __xdata __at (0xdfad) unsigned char RADIO_IRQ4_pending;
static __xdata __at (0xdfaf) unsigned char RADIO_unk_AF;
static __xdata __at (0xdfc0) unsigned char RADIO_channel;
static __xdata __at (0xdfc1) unsigned char RADIO_unk_C1;
static __xdata __at (0xdfc2) unsigned char RADIO_calibration_C2;
static __xdata __at (0xdfc3) unsigned char RADIO_calibration_C3;
static __xdata __at (0xdfc4) unsigned char RADIO_calibration_C4;
static __xdata __at (0xdfc5) unsigned char RADIO_calibration_C5;
static __xdata __at (0xdfc6) unsigned char RADIO_calibration_C6;
static __xdata __at (0xdfc7) unsigned char RADIO_calibration_C7;
static __xdata __at (0xdfc8) unsigned char RADIO_unk_C8;
static __xdata __at (0xdfc9) unsigned char RADIO_txPower; // 0..7 for -5..+8 dBm
static __xdata __at (0xdfca) unsigned char RADIO_unk_CA;
static __xdata __at (0xdfcb) unsigned char RADIO_perChannelSetting2; //relevant for tx
static __xdata __at (0xdfcd) unsigned char RADIO_unk_CD;
static __xdata __at (0xdfce) unsigned char RADIO_unk_CE;
static __xdata __at (0xdfcf) unsigned char RADIO_calibration_CF;
static __xdata __at (0xdfd0) unsigned char RADIO_calibration_D0;
static __xdata __at (0xdfd1) unsigned char RADIO_calibration_D1;
static __xdata __at (0xdfd2) unsigned char RADIO_calibration_D2;
static __xdata __at (0xdfd3) unsigned char RADIO_calibration_D3;
static __xdata __at (0xdfd4) unsigned char RADIO_calibration_D4;
static __xdata __at (0xdfd5) unsigned char RADIO_calibration_D5;
static __xdata __at (0xdfd7) unsigned char RADIO_unk_D7;
static __xdata __at (0xdfd8) unsigned char RADIO_unk_D8;
static __xdata __at (0xdfd9) unsigned char RADIO_SleepTimerHi; //only bottom 4 bits implemented, counts down to wake
static __xdata __at (0xdfda) unsigned char RADIO_SleepTimerMid;
static __xdata __at (0xdfdb) unsigned char RADIO_SleepTimerLo;
static __xdata __at (0xdfe2) unsigned char RADIO_unk_E2;
static __xdata __at (0xdff0) unsigned char RADIO_unk_F0;
static __xdata __at (0xdff3) unsigned char RADIO_SleepTimerSettings; //0x16 for one second tick, 0x56 for 1/32k second tick
static __xdata __at (0xdff4) unsigned char RADIO_RadioPowerCtl;
static __xdata __at (0xdffd) unsigned char RADIO_perChannelSetting1; //relevant fo rRX
#define RADIO_CMD_RECEIVE 0xc2 //tx always goes to RX anyways
#define RADIO_CMD_FLUSH_RX_FIFO 0xc6
#define RADIO_CMD_FLUSH_TX_FIFO 0xc7
#define RADIO_CMD_LOAD_TX_FIFO 0xcb
#endif

23
ap_fw/wdt.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef _WDT_H_
#define _WDT_H_
#include <stdint.h>
#pragma callee_saves wdtOn
void wdtOn(void);
#pragma callee_saves wdtOff
void wdtOff(void);
#pragma callee_saves wdtPet
void wdtPet(void);
#pragma callee_saves wdtSetResetVal
void wdtSetResetVal(uint32_t val); //speed is CPU-specific. On ZBS it is 62KHz or so
#pragma callee_saves wdtDeviceReset
void wdtDeviceReset(void);
#endif

4
esp32_fw/.clang-format Normal file
View File

@@ -0,0 +1,4 @@
Language: Cpp
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 0

5
esp32_fw/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
esp32_fw/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

3
esp32_fw/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}"
}

65
esp32_fw/data/index.html Normal file
View File

@@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<title>Solum - alternative proto AP</title>
<link rel="stylesheet" href="main.css" type="text/css"/>
</head>
<body>
<header>
<div class="logo">Solum - alternative proto AP</div>
</header>
<div class="container">
<div class="blocks"><ul></ul></div>
<div class="window">
<iframe name="empty" style="border:1px solid darkgrey; width:100%; height:4em;"></iframe>
<div class="actionbox">
<form action="send_image" method="POST" target="empty">
DST:<input name="dst" class="dst" size=12 maxlength=12 type="text" placeholder="001122334455">
Filename:<input name="filename" type="text" placeholder="default.bmp">
<input type="submit" value="Send Image">
</form>
</div>
<div class="actionbox">
<form action="send_fw" method="POST" target="empty">
DST:<input name="dst" class="dst" size=12 maxlength=12 type="text" placeholder="001122334455">
Filename:<input name="filename" type="text" placeholder="update.bin">
<input type="submit" value="Send FW Update">
</form>
</div>
<div class="actionbox">
<form action="req_checkin" method="POST" target="empty">
DST:<input name="dst" class="dst" size=12 maxlength=12 type="text" placeholder="001122334455">
<input type="submit" value="Request Check-in">
</form>
</div>
<div id="messages">
<ul class="messages">
</ul>
</div>
</div>
</div>
<script src="jquery.js"></script>
<script src="main.js"></script>
</body>
</html>

6
esp32_fw/data/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

203
esp32_fw/data/main.css Normal file
View File

@@ -0,0 +1,203 @@
*{
margin:0;
padding:0;
border:0;
list-style-type: none;
-webkit-appearance: none;
outline: none;
font-weight: 400;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-smooth: auto;
-webkit-font-smoothing: antialiased;
}
html, body {
height: 100%;
}
body {
font-size: 12px;
font-family: Helvetica, Arial, Verdana, sans-serif;
line-height: 1.5;
background-color: #e8f1f9;
background-color: #f1f1f1;
/*background-image: linear-gradient(315deg, #dde1ee 0%, #e8f1f9 100%);*/
}
header {
height: 50px;
background-color: #666;
}
.logo {
margin: 0 auto;
width: 50px;
height: 50px;
text-indent: 100px;
overflow:hidden;
background: url('p2000.svg') center center no-repeat;
background-size: 50px 50px;
}
.container {
}
.blocks {
padding: 21px 0;
overflow-x: auto;
text-align:center;
}
.blocks ul {
display:table;
margin: 0 auto;
}
.blocks ul li {
display:table-cell;
vertical-align:top;
padding: 0 4px;
position: relative;
}
.blocks ul li:nth-child(6) div.b:before, .blocks ul li:nth-child(6) div.b:after {
content:"";
position:absolute;
margin-left: -5px;
left: 50%;
width: 0;
height: 0;
}
.blocks ul li:nth-child(6) div.b:before {
top: -5px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid #c30;
}
.blocks ul li:nth-child(6) div.b:after {
bottom: -5px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #c30;
}
.blocks ul li, .blocks ul li div {
}
.blocks ul li div {
width: 80px;
padding: 0 4px;
}
.blocks ul li div.b {
position:relative;
padding: 6px 0;
background: #fff;
}
.blocks ul li div.rxt {
background-color: #e5f9ea;
}
.blocks ul li div.txt {
background-color: #f9eae5;
}
.blocks ul li div.b {
}
.window {
margin: 0 auto;
max-width: 94%;
background: #fff;
}
form {
padding: 3px;
}
.actionbox .dst {
size:12em;
}
.actionbox input {
border: solid 1px black;
padding: 2px;
}
input.actionbox {
border: solid 1px black;
}
ul.messages {
padding: 12px 0;
}
ul.messages li {
line-height: 22px;
position: relative;
padding: 0 12px;
}
ul.messages li.new {
-webkit-animation-name: new;
-webkit-animation-duration: 1400ms;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease-in-out;
}
ul.messages li div.message {
padding-left: 58px;
}
ul.messages li div.date {
position: absolute;
left: 12px
}
span.pending {
padding-left: 28px;
}
span.pending:before {
content:"";
position:absolute;
display:inline-block;
top: 0;
left: 12px;
width: 22px;
height: 22px;
background: url('roll.svg') center center no-repeat;
background-size: 22px 22px;
}
@media(max-width: 460px) {
.messages li div, ul.messages li div.date, ul.messages li div.message {
display:block;
position:relative;
padding: 0;
left: auto;
}
.messages li div.message, li.pending {
margin-bottom: 8px;
}
ul.messages {
padding-bottom: 4px;
}
}
@-webkit-keyframes new {
0% {
background-color: rgba(255, 255, 204, 1);
}
50% {
background-color: rgba(255, 255, 204, .5);
}
100% {
background-color: rgba(255, 255, 204, 0);
}
}

23
esp32_fw/data/main.js Normal file
View File

@@ -0,0 +1,23 @@
$.fn.htmlTo = function(elem) {
return this.each(function() {
$(elem).html($(this).html());
});
}
let socket = new WebSocket("ws://" + location.host + "/ws");
socket.onmessage = function(event) {
let incomingMessage = event.data;
showMessage(incomingMessage);
};
socket.onclose = event => console.log(`Closed ${event.code}`);
function showMessage(message) {
$( "<li/>", {
"class": "new",
html: message
}).prependTo( "ul.messages" );
}

BIN
esp32_fw/data/test154.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
esp32_fw/data/test29.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
esp32_fw/data/test42.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,65 @@
#include <Arduino.h>
#pragma pack(push, 1)
struct espBlockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
} __packed;
struct espXferComplete {
uint8_t checksum;
uint8_t src[8];
} __packed;
struct espJoinNetwork {
uint8_t checksum;
uint8_t src[8];
} __packed;
struct espSaveUpdateBlock {
uint8_t checksum;
uint8_t blockId;
uint16_t blockChecksum;
} __packed;
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} __packed;
struct AvailDataReq {
uint8_t checksum;
uint8_t lastPacketLQI; // zero if not reported/not supported to be reported
int8_t lastPacketRSSI; // zero if not reported/not supported to be reported
uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint16_t batteryMv;
uint8_t softVer;
uint8_t hwType;
uint8_t protoVer;
} __packed;
#define DATATYPE_NOUPDATE 0
#define DATATYPE_IMG 1
#define DATATYPE_IMGRAW 2
#define DATATYPE_UPDATE 3
struct AvailDataInfo {
uint8_t checksum;
uint64_t dataVer;
uint32_t dataSize;
uint8_t dataType;
} __packed;
struct pendingData {
struct AvailDataInfo availdatainfo;
uint8_t attemptsLeft;
uint8_t targetMac[8];
uint8_t includedThisBurst : 1;
} __packed;
#define BLOCK_DATA_SIZE 4096
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)

View File

@@ -0,0 +1,4 @@
#include <Arduino.h>
String lookupFirmwareFile(uint16_t &version);
void performDeviceFlash();

4
esp32_fw/include/http.h Normal file
View File

@@ -0,0 +1,4 @@
#include <Arduino.h>
uint32_t getDataFromHTTP(uint8_t** data, String filename);
String postDataToHTTP(String json);

View File

@@ -0,0 +1,10 @@
#include <Arduino.h>
extern void addCRC(void* p, uint8_t len);
extern bool checkCRC(void* p, uint8_t len);
extern void processBlockRequest(struct espBlockRequest* br);
extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst);
extern void processJoinNetwork(struct espJoinNetwork* xjn);
extern void processXferComplete(struct espXferComplete* xfc);
extern void processDataReq(struct AvailDataReq* adr);

View File

@@ -0,0 +1,23 @@
#include <Arduino.h>
#include <vector>
#pragma pack(push, 1)
#pragma once
class pendingdata {
public:
String filename;
//uint8_t dst[8];
uint64_t ver;
String md5;
uint16_t timeout;
uint8_t datatimeout;
uint8_t* data = nullptr;
uint32_t len;
static pendingdata* findByVer(uint64_t ver);
static void garbageCollection();
};
void garbageCollection(void* parameter);
extern std::vector<pendingdata*> pendingfiles;

View File

@@ -0,0 +1,6 @@
#include <Arduino.h>
void zbsTx(uint8_t* packetdata, uint8_t len);
void zbsRxTask(void* parameter);
void sendDataAvail(struct pendingData* pending);

View File

@@ -0,0 +1,45 @@
#include <Arduino.h>
//#define NETWORK_MODE // comment me in to enable network-mode. I'm ready to go in, coach, just give me a chance!
#ifndef NETWORK_MODE
#define STANDALONE_MODE
#endif
#ifdef NETWORK_MODE
#define WEBSERVER_PATH "http://your-webserver/esl/"
#define CHECK_IN_URL "http://your-webserver/esl/check-in.php"
#endif
#ifdef STANDALONE_MODE
#define CHECK_IN_DELAY 900000
#define RETRY_DELAY 1000
#define FAILED_TILL_BLANK 2
#define FAILED_TILL_REASSOCIATE 1
#endif
// how long the we should keep the transfer metadata
#define PENDING_TIMEOUT 300
// this determines how long images will be cached;
#define PENDING_DATA_TIMEOUT 60
// flasher options
#define CUSTOM_MAC_HDR 0x0000
// connections to the tag
//#define RXD1 16 //was 16 - 13
//#define TXD1 17 // was 17 - 12
#define RXD1 13 // 1st
#define TXD1 12 // 2nd
#define ZBS_SS 21
#define ZBS_CLK 18
#define ZBS_MoSi 22
#define ZBS_MiSo 19
#define ZBS_Reset 5
#define ZBS_POWER1 15
#define ZBS_POWER2 2
#define MAX_WRITE_ATTEMPTS 5

13
esp32_fw/include/web.h Normal file
View File

@@ -0,0 +1,13 @@
#include <Arduino.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
void init_web();
extern void webSocketSendProcess(void *parameter);
extern void wsString(String text);
extern uint64_t swap64(uint64_t x);
extern AsyncWebSocket ws;//("/ws");
extern SemaphoreHandle_t wsMutex;
extern TaskHandle_t websocketUpdater;

View File

@@ -0,0 +1,57 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
/* Autor: Aaron Christophel ATCnetz.de */
#include <Arduino.h>
void simplePowerOn();
class ZBS_interface {
public:
uint8_t begin();
void set_power(uint8_t state);
void enable_debug();
void reset();
void send_byte(uint8_t data);
uint8_t read_byte();
void write_byte(uint8_t cmd, uint8_t addr, uint8_t data);
uint8_t read_byte(uint8_t cmd, uint8_t addr);
void write_flash(uint16_t addr, uint8_t data);
uint8_t read_flash(uint16_t addr);
void write_ram(uint8_t addr, uint8_t data);
uint8_t read_ram(uint8_t addr);
void write_sfr(uint8_t addr, uint8_t data);
uint8_t read_sfr(uint8_t addr);
uint8_t check_connection();
uint8_t select_flash(uint8_t page);
void erase_flash();
void erase_infoblock();
private:
uint8_t _SS_PIN = -1;
uint8_t _CLK_PIN = -1;
uint8_t _MOSI_PIN = -1;
uint8_t _MISO_PIN = -1;
uint8_t _RESET_PIN = -1;
uint8_t _POWER_PIN = -1;
int ZBS_spi_delay = 1;
typedef enum {
ZBS_CMD_W_RAM = 0x02,
ZBS_CMD_R_RAM = 0x03,
ZBS_CMD_W_FLASH = 0x08,
ZBS_CMD_R_FLASH = 0x09,
ZBS_CMD_W_SFR = 0x12,
ZBS_CMD_R_SFR = 0x13,
ZBS_CMD_ERASE_FLASH = 0x88,
ZBS_CMD_ERASE_INFOBLOCK = 0x48,
} ZBS_CMD_LIST;
typedef enum {
ZBS_ON = 1,
ZBS_OFF = 0,
} ZBS_POWER_STATE;
};
extern ZBS_interface zbs;

46
esp32_fw/lib/README Normal file
View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

27
esp32_fw/platformio.ini Normal file
View File

@@ -0,0 +1,27 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32cam]
platform = espressif32
board = esp32cam
framework = arduino
;board_build.partitions = min_spiffs.csv
board_build.partitions = no_ota.csv
platform_packages =
monitor_filters = esp32_exception_decoder
monitor_speed = 115200
board_build.f_cpu = 240000000L
lib_deps =
https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/tzapu/WiFiManager.git#feature_asyncwebserver
bblanchon/ArduinoJson
upload_port = COM12
monitor_port = COM12

View File

@@ -0,0 +1,561 @@
#include "SPIFFSEditor.h"
#include <FS.h>
// File: edit.htm.gz, Size: 4151
#define edit_htm_gz_len 4151
const uint8_t edit_htm_gz[] PROGMEM = {
0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED,
0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6,
0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB,
0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A,
0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61,
0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7,
0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02,
0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C,
0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A,
0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89,
0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76,
0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D,
0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9,
0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B,
0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91,
0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78,
0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78,
0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98,
0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E,
0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41,
0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21,
0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F,
0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74,
0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C,
0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0,
0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C,
0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30,
0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9,
0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61,
0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B,
0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9,
0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B,
0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD,
0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3,
0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77,
0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83,
0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF,
0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3,
0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55,
0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3,
0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF,
0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF,
0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60,
0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1,
0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE,
0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F,
0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0,
0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9,
0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5,
0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15,
0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74,
0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D,
0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD,
0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A,
0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6,
0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2,
0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF,
0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53,
0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2,
0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A,
0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83,
0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26,
0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0,
0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0,
0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84,
0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99,
0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5,
0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9,
0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87,
0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F,
0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6,
0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B,
0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D,
0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25,
0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3,
0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F,
0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35,
0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A,
0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6,
0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7,
0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A,
0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9,
0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97,
0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36,
0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C,
0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A,
0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C,
0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F,
0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11,
0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16,
0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA,
0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB,
0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A,
0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6,
0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28,
0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1,
0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E,
0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E,
0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92,
0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05,
0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8,
0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0,
0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85,
0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40,
0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56,
0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47,
0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA,
0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7,
0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD,
0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61,
0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58,
0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D,
0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8,
0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C,
0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA,
0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49,
0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51,
0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00,
0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A,
0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A,
0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35,
0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F,
0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E,
0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C,
0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64,
0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C,
0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1,
0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B,
0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC,
0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42,
0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B,
0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71,
0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F,
0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28,
0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9,
0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD,
0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6,
0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F,
0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5,
0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8,
0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF,
0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62,
0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C,
0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7,
0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89,
0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29,
0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95,
0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7,
0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB,
0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09,
0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F,
0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60,
0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35,
0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6,
0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B,
0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66,
0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25,
0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E,
0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97,
0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC,
0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE,
0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7,
0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13,
0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0,
0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A,
0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93,
0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E,
0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9,
0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78,
0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5,
0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12,
0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E,
0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35,
0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98,
0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58,
0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3,
0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64,
0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39,
0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D,
0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62,
0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48,
0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D,
0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8,
0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9,
0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30,
0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6,
0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1,
0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56,
0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84,
0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0,
0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC,
0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E,
0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39,
0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B,
0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA,
0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1,
0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1,
0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88,
0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4,
0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC,
0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98,
0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97,
0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8,
0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30,
0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA,
0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B,
0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC,
0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45,
0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD,
0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76,
0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD,
0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76,
0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4,
0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF,
0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4,
0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42,
0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43,
0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B,
0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97,
0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73,
0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B,
0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50,
0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51,
0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25,
0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26,
0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80,
0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9,
0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0,
0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74,
0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA,
0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D,
0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F,
0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2,
0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1,
0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99,
0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D,
0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B,
0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD,
0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F,
0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB,
0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47,
0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59,
0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D,
0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD,
0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94,
0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35,
0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81,
0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D,
0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20,
0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB,
0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B,
0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6,
0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17,
0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8,
0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26,
0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57,
0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36,
0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53,
0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00};
#define SPIFFS_MAXLENGTH_FILEPATH 32
const char *excludeListFile = "/.exclude.files";
typedef struct ExcludeListS {
char *item;
ExcludeListS *next;
} ExcludeList;
static ExcludeList *excludes = NULL;
static bool matchWild(const char *pattern, const char *testee) {
const char *nxPat = NULL;
const char *nxTst = NULL;
while (*testee) {
if ((*pattern == '?') || (*pattern == *testee)) {
++pattern;
++testee;
continue;
}
if (*pattern == '*') {
nxPat = pattern++;
nxTst = testee;
continue;
}
if (nxPat) {
pattern = nxPat + 1;
testee = ++nxTst;
continue;
}
return false;
}
while (*pattern == '*') {
++pattern;
}
return (*pattern == 0);
}
static bool addExclude(const char *item) {
size_t len = strlen(item);
if (!len) {
return false;
}
ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
if (!e) {
return false;
}
e->item = (char *)malloc(len + 1);
if (!e->item) {
free(e);
return false;
}
memcpy(e->item, item, len + 1);
e->next = excludes;
excludes = e;
return true;
}
static void loadExcludeList(fs::FS &_fs, const char *filename) {
static char linebuf[SPIFFS_MAXLENGTH_FILEPATH];
fs::File excludeFile;
if (filename[0] != '/') {
excludeFile = _fs.open("/" + ((String)filename), "r");
} else {
excludeFile = _fs.open(filename, "r");
}
if (!excludeFile) {
// addExclude("/*.js.gz");
return;
}
#ifdef ESP32
if (excludeFile.isDirectory()) {
excludeFile.close();
return;
}
#endif
if (excludeFile.size() > 0) {
uint8_t idx;
bool isOverflowed = false;
while (excludeFile.available()) {
linebuf[0] = '\0';
idx = 0;
int lastChar;
do {
lastChar = excludeFile.read();
if (lastChar != '\r') {
linebuf[idx++] = (char)lastChar;
}
} while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH));
if (isOverflowed) {
isOverflowed = (lastChar != '\n');
continue;
}
isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH);
linebuf[idx - 1] = '\0';
if (!addExclude(linebuf)) {
excludeFile.close();
return;
}
}
}
excludeFile.close();
}
static bool isExcluded(fs::FS &_fs, const char *filename) {
if (excludes == NULL) {
loadExcludeList(_fs, excludeListFile);
}
ExcludeList *e = excludes;
while (e) {
if (matchWild(e->item, filename)) {
return true;
}
e = e->next;
}
return false;
}
// WEB HANDLER IMPLEMENTATION
#ifdef ESP32
SPIFFSEditor::SPIFFSEditor(const fs::FS &fs, const String &username, const String &password)
#else
SPIFFSEditor::SPIFFSEditor(const String &username, const String &password, const fs::FS &fs)
#endif
: _fs(fs), _username(username), _password(password), _authenticated(false), _startTime(0) {
}
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) {
if (request->url().equalsIgnoreCase("/edit")) {
if (request->method() == HTTP_GET) {
if (request->hasParam("list")) {
return true;
}
if (request->hasParam("edit")) {
request->_tempFile = _fs.open("/" + request->arg("edit"), "r");
if (!request->_tempFile) {
return false;
}
#ifdef ESP32
if (request->_tempFile.isDirectory()) {
request->_tempFile.close();
return false;
}
#endif
}
if (request->hasParam("download")) {
request->_tempFile = _fs.open("/" + request->arg("download"), "r");
if (!request->_tempFile) {
return false;
}
#ifdef ESP32
if (request->_tempFile.isDirectory()) {
request->_tempFile.close();
return false;
}
#endif
}
request->addInterestingHeader("If-Modified-Since");
return true;
} else if (request->method() == HTTP_POST || request->method() == HTTP_DELETE || request->method() == HTTP_PUT) {
return true;
}
}
return false;
}
void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
if (_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str())) {
return request->requestAuthentication();
}
if (request->method() == HTTP_GET) {
if (request->hasParam("list")) {
const String path = request->getParam("list")->value();
#ifdef ESP32
File dir = _fs.open(path);
#else
Dir dir = _fs.openDir(path);
#endif
String output = "[";
#ifdef ESP32
File file = dir.openNextFile();
while (file) {
#else
while (dir.next()) {
fs::File entry = dir.openFile("r");
#endif
if (isExcluded(_fs, file.name())) {
#ifdef ESP32
file = dir.openNextFile();
#endif
continue;
}
if (output != "[") {
output += ',';
}
output += "{\"type\":\"";
output += "file";
output += "\",\"name\":\"";
output += file.name();
output += "\",\"size\":";
output += file.size();
output += ",\"ver\":";
output += file.getLastWrite();
output += "}";
#ifdef ESP32
file = dir.openNextFile();
#else
file.close();
#endif
}
#ifdef ESP32
dir.close();
#endif
output += "]";
request->send(200, "application/json", output);
} else if (request->hasParam("edit") || request->hasParam("download")) {
request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
} else {
const char *buildTime = __DATE__ " " __TIME__ " GMT";
if (request->header("If-Modified-Since").equals(buildTime)) {
request->send(304);
} else {
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len);
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Last-Modified", buildTime);
request->send(response);
}
}
} else if (request->method() == HTTP_DELETE) {
if (request->hasParam("path", true)) {
_fs.remove("/" + request->getParam("path", true)->value());
request->send(200, "", "DELETE: " + request->getParam("path", true)->value());
} else {
request->send(404);
}
} else if (request->method() == HTTP_POST) {
String filename = request->getParam("data", true, true)->value();
if (filename.c_str()[0] != '/') {
filename = "/" + filename;
}
if (request->hasParam("data", true, true) && _fs.exists(filename)) {
request->send(200, "", "UPLOADED: " + request->getParam("data", true, true)->value());
} else {
request->send(500);
}
} else if (request->method() == HTTP_PUT) {
if (request->hasParam("path", true)) {
String filename = request->getParam("path", true)->value();
if (_fs.exists(filename)) {
request->send(200);
} else {
if (filename.c_str()[0] != '/') {
filename = "/" + filename;
}
fs::File f = _fs.open(filename, "w");
if (f) {
f.write((uint8_t)0x00);
f.close();
request->send(200, "", "CREATE: " + filename);
} else {
request->send(500);
}
}
} else {
request->send(400);
}
}
}
void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
if (!_username.length() || request->authenticate(_username.c_str(), _password.c_str())) {
_authenticated = true;
if (filename.c_str()[0] != '/') {
request->_tempFile = _fs.open("/" + filename, "w");
} else {
request->_tempFile = _fs.open(filename, "w");
}
_startTime = millis();
}
}
if (_authenticated && request->_tempFile) {
if (len) {
request->_tempFile.write(data, len);
}
if (final) {
request->_tempFile.close();
}
}
}

224
esp32_fw/src/flasher.cpp Normal file
View File

@@ -0,0 +1,224 @@
#include "flasher.h"
#include <Arduino.h>
#include <LittleFS.h>
#include "settings.h"
#include "time.h"
#include "zbs_interface.h"
typedef enum {
CMD_GET_VERSION = 1,
CMD_RESET_ESP = 2,
CMD_ZBS_BEGIN = 10,
CMD_RESET_ZBS = 11,
CMD_SELECT_PAGE = 12,
CMD_SET_POWER = 13,
CMD_READ_RAM = 20,
CMD_WRITE_RAM = 21,
CMD_READ_FLASH = 22,
CMD_WRITE_FLASH = 23,
CMD_READ_SFR = 24,
CMD_WRITE_SFR = 25,
CMD_ERASE_FLASH = 26,
CMD_ERASE_INFOBLOCK = 27,
} ZBS_UART_PROTO;
uint8_t *infoblock = nullptr;
uint8_t *flashbuffer = nullptr;
// look for the latest version of the firmware file... It's supposed to be something like zigbeebase0003.bin
String lookupFirmwareFile(uint16_t &version) {
String filename;
File root = LittleFS.open("/");
File file = root.openNextFile();
while (file) {
if (strncmp(file.name(), "zigbeebase", 10) == 0) {
filename = "/" + ((String)file.name());
}
file.close();
file = root.openNextFile();
}
if (filename.length()) {
char buffer[16];
memset(buffer, 0, 16);
strncpy(buffer, filename.c_str() + 11, 4);
version = strtoul(buffer, NULL, 16);
root.close();
return filename;
} else {
version = 0;
root.close();
return "";
}
}
// guess device type based on flash memory contents
uint16_t getDeviceType() {
uint8_t type29[8] = {0x7d, 0x22, 0xff, 0x02, 0xa4, 0x58, 0xf0, 0x90};
uint8_t type154[8] = {0xa1, 0x23, 0x22, 0x02, 0xa4, 0xc3, 0xe4, 0xf0};
uint8_t buffer[8] = {0};
zbs.select_flash(0);
for (uint8_t c = 0; c < 8; c++) {
buffer[c] = zbs.read_flash(0x08 + c);
}
if (memcmp(buffer, type29, 8) == 0) {
return 0x3B10;
}
if (memcmp(buffer, type154, 8) == 0) {
return 0x3410;
}
return 0xFFF0;
}
// extract original mac from firmware and make it 2 bytes longer based on info in settings.h
uint64_t getOriginalTagMac() {
zbs.select_flash(0);
uint8_t mac[8] = {0};
for (uint8_t c = 0; c < 6; c++) {
mac[c + 2] = zbs.read_flash(0xFC06 + c);
}
mac[0] = (uint8_t)(CUSTOM_MAC_HDR >> 8);
mac[1] = (uint8_t)CUSTOM_MAC_HDR;
uint16_t type = getDeviceType();
mac[6] = (uint8_t)(type >> 8);
mac[7] = (uint8_t)(type & 0xFF);
uint8_t xorchk = 0;
for (uint8_t c = 2; c < 8; c++) {
xorchk ^= (mac[c] & 0x0F);
xorchk ^= (mac[c] >> 4);
}
mac[7] |= xorchk;
return *((uint64_t *)(mac));
}
// extract custom firmware mac from Infoblock
uint64_t getInfoBlockMac() {
zbs.select_flash(1);
uint8_t mac[8] = {0};
for (uint8_t c = 0; c < 8; c++) {
mac[c] = zbs.read_flash(0x10 + c);
}
return *((uint64_t *)(mac));
}
// get info from infoblock (eeprom flash, kinda)
void readInfoBlock() {
if (infoblock == nullptr) {
// allocate room for infopage
infoblock = (uint8_t *)calloc(1024, 1);
}
zbs.select_flash(1); // select info page
for (uint16_t c = 0; c < 1024; c++) {
infoblock[c] = zbs.read_flash(c);
}
}
// write info to infoblock
void writeInfoBlock() {
if (infoblock == nullptr) {
return;
}
zbs.select_flash(1);
zbs.erase_infoblock();
zbs.select_flash(1); // select info page
for (uint16_t c = 0; c < 1024; c++) {
for (uint8_t i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
zbs.write_flash(c, infoblock[c]);
if (zbs.read_flash(c) == infoblock[c]) {
break;
}
}
}
}
// erase flash and program from flash buffer
void writeFlashBlock(uint16_t size) {
if (flashbuffer == nullptr) {
return;
}
zbs.select_flash(0);
zbs.erase_flash();
zbs.select_flash(0);
Serial.printf("Starting flash, size=%d\n", size);
uint8_t i = 0;
for (uint16_t c = 0; c < size; c++) {
for (i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
zbs.write_flash(c, flashbuffer[c]);
if (zbs.read_flash(c) == flashbuffer[c]) {
break;
}
}
if (i == MAX_WRITE_ATTEMPTS) {
Serial.printf("\nFailed to write byte at c\n");
}
if (c % 256 == 0) {
Serial.printf("\rNow flashing, %d/%d...", c, size);
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}
}
// perform device flash, save mac, everything
void performDeviceFlash() {
uint8_t interfaceWorking = 0;
Serial.printf("Power cycling to get everything up and running...\n");
zbs.set_power(0);
vTaskDelay(500 / portTICK_PERIOD_MS);
zbs.set_power(1);
vTaskDelay(500 / portTICK_PERIOD_MS);
interfaceWorking = zbs.begin();
if (!interfaceWorking) {
Serial.print("I wasn't able to connect to a ZBS tag, please check wiring and definitions in the settings.h file.\n");
return;
}
readInfoBlock();
uint8_t mac[8] = {0};
*((uint64_t *)(mac)) = getInfoBlockMac();
// check if the mac has been set at all, 0xFF- is not allowed
if (*((uint64_t *)(mac)) == 0xFFFFFFFFFFFFFFFF) {
// mac not set in infopage, get it from the original firmware
*((uint64_t *)(mac)) = getOriginalTagMac();
zbs.select_flash(1);
for (uint8_t c = 0; c < 8; c++) {
infoblock[0x17 - c] = mac[c];
// write mac directly to infoblock without erasing; the bytes should all be 0xFF anyway
zbs.write_flash(0x17 - c, mac[c]);
}
}
uint16_t version = 0;
File file = LittleFS.open(lookupFirmwareFile(version));
if (!file) {
// couldn't find a valid firmware version
return;
} else {
Serial.printf("Preparing to flash version %04X (%d bytes) to the tag\n", version, file.size());
}
// load buffer with zbs firmware
uint16_t flashbuffersize = file.size();
flashbuffer = (uint8_t *)calloc(flashbuffersize, 1);
uint32_t index = 0;
while (file.available()) {
flashbuffer[index] = file.read();
index++;
portYIELD();
}
// write firmware from buffer and finish by rewriting the info block again
writeFlashBlock(flashbuffersize);
writeInfoBlock();
Serial.print("All done with flashing\n");
file.close();
free(infoblock);
infoblock = nullptr;
free(flashbuffer);
flashbuffer = nullptr;
zbs.reset();
zbs.set_power(1);
}

41
esp32_fw/src/http.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "http.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include "settings.h"
#ifdef NETWORK_MODE
uint32_t getDataFromHTTP(uint8_t** data, String filename) {
HTTPClient http;
uint16_t ret = 0;
http.begin(WEBSERVER_PATH + filename);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
ret = http.getSize();
*data = (uint8_t*)calloc(ret, 1);
memcpy(*data, payload.begin(), ret);
} else {
data = 0;
Serial.printf("Error on HTTP request! Tried to get %s but something went TERRIBLY wrong.\n", filename);
}
http.end(); // Free the resources
return ret;
}
String postDataToHTTP(String json) {
HTTPClient http;
http.begin(CHECK_IN_URL);
int httpCode = http.POST(json);
String ret = "";
if (httpCode > 0) {
ret = http.getString();
}
http.end(); // Free the resources
return ret;
}
#endif

54
esp32_fw/src/main.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WifiManager.h>
#include <time.h>
#include "flasher.h"
#include "pendingdata.h"
#include "serial.h"
#include "soc/rtc_wdt.h"
#include "web.h"
void freeHeapTask(void* parameter) {
while (1) {
//Serial.printf("Free heap=%d\n", ESP.getFreeHeap());
vTaskDelay(30000 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
Serial.print(">\n");
init_web();
long timezone = 2;
byte daysavetime = 1;
configTime(0, 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct;
delay(2000);
tmstruct.tm_year = 0;
getLocalTime(&tmstruct, 5000);
Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
Serial.println("");
// WiFiManager wm;
xTaskCreate(freeHeapTask, "print free heap", 10000, NULL, 2, NULL);
xTaskCreate(zbsRxTask, "zbsRX Process", 10000, NULL, 2, NULL);
xTaskCreate(garbageCollection, "pending-data cleanup", 5000, NULL, 1, NULL);
xTaskCreate(webSocketSendProcess, "ws", 5000, NULL,configMAX_PRIORITIES-10, NULL);
/*
wm.setWiFiAutoReconnect(true);
wm.setConfigPortalTimeout(180);
bool res = wm.autoConnect("ESP32ZigbeeBase", "password"); // password protected ap
if (!res) {
Serial.println("Failed to connect");
ESP.restart();
}
wm.setWiFiAutoReconnect(true);
*/
}
void loop() {
vTaskDelay(30000 / portTICK_PERIOD_MS);
}

154
esp32_fw/src/newproto.cpp Normal file
View File

@@ -0,0 +1,154 @@
#pragma pack(push, 1)
#include "newproto.h"
#include <Arduino.h>
#include <MD5Builder.h>
#include "LittleFS.h"
#include "commstructs.h"
#include "pendingdata.h"
#include "serial.h"
#include "settings.h"
#include "web.h"
extern void sendBlock(const void* data, const uint16_t len);
void addCRC(void* p, uint8_t len) {
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++) {
total += ((uint8_t*)p)[c];
}
((uint8_t*)p)[0] = total;
// pr("%d",total);
}
bool checkCRC(void* p, uint8_t len) {
uint8_t total = 0;
for (uint8_t c = 1; c < len; c++) {
total += ((uint8_t*)p)[c];
}
return ((uint8_t*)p)[0] == total;
}
uint8_t* getDataForFile(File* file) {
uint8_t* ret = nullptr;
ret = (uint8_t*)malloc(file->size());
if (ret) {
file->seek(0);
file->readBytes((char*)ret, file->size());
} else {
Serial.print("malloc failed for file...\n");
}
return ret;
}
bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst) {
*filename = "/" + *filename;
if (!LittleFS.exists(*filename)) return false;
File file = LittleFS.open(*filename);
if (file.size() == 0) {
Serial.print("opened a file with size 0??\n");
return false;
}
uint8_t md5bytes[16];
{
MD5Builder md5;
md5.begin();
md5.addStream(file, file.size());
md5.calculate();
md5.getBytes(md5bytes);
}
// the message that will be sent to the AP to tell the tag there is data pending
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = dataType;
pending.availdatainfo.dataVer = *((uint64_t*)md5bytes);
pending.availdatainfo.dataSize = file.size();
pending.attemptsLeft = 10;
sendDataAvail(&pending);
// data for the cache on the esp32; needs to hold the data longer than the maximum timeout on the AP
pendingdata* pendinginfo = nullptr;
pendinginfo = new pendingdata;
pendinginfo->filename = *filename;
pendinginfo->ver = pending.availdatainfo.dataVer;
pendinginfo->len = pending.availdatainfo.dataSize;
pendinginfo->data = nullptr;
pendinginfo->timeout = PENDING_TIMEOUT;
// pendinginfo->data = getDataForFile(&file);
file.close();
pendinginfo->timeout = 1800;
pendingfiles.push_back(pendinginfo);
return true;
}
void processBlockRequest(struct espBlockRequest* br) {
if (!checkCRC(br, sizeof(struct espBlockRequest))) {
Serial.print("Failed CRC on a blockrequest received by the AP");
return;
}
pendingdata* pd = pendingdata::findByVer(br->ver);
if (pd == nullptr) {
Serial.printf("Couldn't find pendingdata info for ver %llu", br->ver);
return;
} else {
if (pd->data == nullptr) {
// not cached. open file, cache the data
File file = LittleFS.open(pd->filename);
if (!file) {
Serial.print("Dunno how this happened... File pending but deleted in the meantime?\n");
}
pd->data = getDataForFile(&file);
pd->datatimeout = PENDING_DATA_TIMEOUT;
file.close();
} else {
// file is already cached, refresh the timeout
pd->datatimeout = PENDING_DATA_TIMEOUT;
}
}
// check if we're not exceeding max blocks (to prevent sendBlock from exceeding its boundary)
uint8_t totalblocks = (pd->len / BLOCK_DATA_SIZE);
if (pd->len % BLOCK_DATA_SIZE) totalblocks++;
if (br->blockId >= totalblocks) {
br->blockId = totalblocks - 1;
}
uint32_t len = pd->len - (BLOCK_DATA_SIZE * br->blockId);
if (len > BLOCK_DATA_SIZE) len = BLOCK_DATA_SIZE;
sendBlock(pd->data + (br->blockId * BLOCK_DATA_SIZE), len);
char buffer[64];
sprintf(buffer, "< Block Request received for MD5 %llu, block %d\n\0", br->ver, br->blockId);
wsString((String)buffer);
Serial.printf("<BlockId=%d\n", br->blockId);
}
void processJoinNetwork(struct espJoinNetwork* xjn) {
char buffer[64];
uint8_t src[8];
*((uint64_t*)src) = swap64(*((uint64_t*)xjn->src));
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X joined the network\n\0", src[2], src[3], src[4], src[5], src[6], src[7]);
wsString((String)buffer);
Serial.print(buffer);
}
void processXferComplete(struct espXferComplete* xfc) {
char buffer[64];
uint8_t src[8];
*((uint64_t*)src) = swap64(*((uint64_t*)xfc->src));
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X reports xfer complete\n\0", src[2], src[3], src[4], src[5], src[6], src[7]);
wsString((String)buffer);
Serial.print(buffer);
}
void processDataReq(struct AvailDataReq* adr) {
char buffer[64];
uint8_t src[8];
// *((uint64_t*)src) = swap64(*((uint64_t*)adr->));
// sprintf(buffer, "< %02X%02X%02X%02X%02X%02X reports xfer complete\n\0", src[2], src[3], src[4], src[5], src[6], src[7]);
sprintf(buffer, "<DATA REQ - version=%d, voltage=%d\n", adr->softVer, adr->batteryMv);
wsString((String)buffer);
Serial.print(buffer);
}

View File

@@ -0,0 +1,53 @@
#include "pendingdata.h"
#include <Arduino.h>
#include <vector>
#include "settings.h"
std::vector<pendingdata*> pendingfiles;
void garbageCollection(void* parameter) {
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
pendingdata::garbageCollection();
}
}
pendingdata* pendingdata::findByVer(uint64_t ver) {
for (int16_t c = 0; c < pendingfiles.size(); c++) {
pendingdata* pending = nullptr;
pending = pendingfiles.at(c);
if (pending->ver == ver) {
return pending;
}
}
return nullptr;
}
void pendingdata::garbageCollection() {
for (int16_t c = 0; c < pendingfiles.size(); c++) {
pendingdata* pending = pendingfiles.at(c);
if (pending->datatimeout > 1) {
pending->datatimeout--;
} else if (pending->datatimeout == 1) {
if (pending->data != nullptr) {
free(pending->data);
}
pending->data = nullptr;
pending->datatimeout == 0;
}
if (pending->timeout) {
pending->timeout--;
} else {
if (pending->data != nullptr) {
free(pending->data);
}
pending->data = nullptr;
delete pending;
pendingfiles.erase(pendingfiles.begin() + c);
}
}
}

352
esp32_fw/src/serial.cpp Normal file
View File

@@ -0,0 +1,352 @@
#pragma pack(push, 1)
#include <Arduino.h>
#include <HardwareSerial.h>
#include <LittleFS.h>
#include "commstructs.h"
#include "flasher.h"
#include "newproto.h"
#include "settings.h"
#include "web.h"
#include "zbs_interface.h"
#define ZBS_RX_WAIT_HEADER 0
#define ZBS_RX_WAIT_PKT_LEN 1
#define ZBS_RX_WAIT_PKT_RX 2
#define ZBS_RX_WAIT_SEP1 3
#define ZBS_RX_WAIT_SEP2 4
#define ZBS_RX_WAIT_VER 6
#define ZBS_RX_BLOCK_REQUEST 7
#define ZBS_RX_WAIT_XFERCOMPLETE 8
#define ZBS_RX_WAIT_DATA_REQ 9
#define ZBS_RX_WAIT_JOINNETWORK 10
uint8_t restartBlockRequest = 0;
uint16_t sendBlock(const void* data, const uint16_t len) {
Serial1.print(">D>");
delay(10);
uint8_t blockbuffer[sizeof(struct blockData)];
struct blockData* bd = (struct blockData*)blockbuffer;
bd->size = len;
bd->checksum = 0;
// calculate checksum
for (uint16_t c = 0; c < len; c++) {
bd->checksum += ((uint8_t*)data)[c];
}
// send blockData header
for (uint8_t c = 0; c < sizeof(struct blockData); c++) {
Serial1.write(blockbuffer[c]);
}
// send an entire block of data
uint16_t c;
for (c = 0; c < len; c++) {
Serial1.write(((uint8_t*)data)[c]);
}
for (; c < BLOCK_DATA_SIZE; c++) {
Serial1.write(0);
}
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
return bd->checksum;
}
void sendDataAvail(struct pendingData* pending) {
addCRC(pending, sizeof(struct pendingData));
Serial1.print("SDA>");
for (uint8_t c = 0; c < sizeof(struct pendingData); c++) {
Serial1.write(((uint8_t*)pending)[c]);
}
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
}
uint8_t RXState = ZBS_RX_WAIT_HEADER;
char cmdbuffer[4] = {0};
uint8_t* packetp = nullptr;
uint8_t pktlen = 0;
uint8_t pktindex = 0;
char lastchar = 0;
uint8_t charindex = 0;
bool waitingForVersion = true;
uint16_t version;
void ShortRXWaitLoop() {
if (Serial1.available()) {
lastchar = Serial1.read();
Serial.write(lastchar);
// shift characters in
for (uint8_t c = 0; c < 3; c++) {
cmdbuffer[c] = cmdbuffer[c + 1];
}
cmdbuffer[3] = lastchar;
}
}
void SerialRXLoop() {
if (Serial1.available()) {
lastchar = Serial1.read();
Serial.write(lastchar);
switch (RXState) {
case ZBS_RX_WAIT_HEADER:
// shift characters in
for (uint8_t c = 0; c < 3; c++) {
cmdbuffer[c] = cmdbuffer[c + 1];
}
cmdbuffer[3] = lastchar;
if ((strncmp(cmdbuffer, "VER>", 4) == 0) && waitingForVersion) {
waitingForVersion = false;
pktindex = 0;
RXState = ZBS_RX_WAIT_VER;
charindex = 0;
memset(cmdbuffer, 0x00, 4);
}
if (strncmp(cmdbuffer, "RQB>", 4) == 0) {
RXState = ZBS_RX_BLOCK_REQUEST;
charindex = 0;
pktindex = 0;
packetp = (uint8_t*)calloc(sizeof(struct espBlockRequest) + 8, 1);
memset(cmdbuffer, 0x00, 4);
restartBlockRequest = 0;
}
if (strncmp(cmdbuffer, "RQQ>", 4) == 0) {
/*
Serial.print("BYTES REQUESTED!\n");
for (uint8_t c = 0; c < 50; c++) {
Serial1.write(0x00);
}
restartBlockRequest++;
if (restartBlockRequest > 10) {
restartBlockRequest = 0;
for (uint8_t c = 0; c < 200; c++) {
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
}
}
RXState = ZBS_RX_WAIT_HEADER;
*/
}
if (strncmp(cmdbuffer, "ADR>", 4) == 0) {
RXState = ZBS_RX_WAIT_DATA_REQ;
charindex = 0;
pktindex = 0;
packetp = (uint8_t*)calloc(sizeof(struct espBlockRequest) + 8, 1);
memset(cmdbuffer, 0x00, 4);
}
if (strncmp(cmdbuffer, "BST>", 4) == 0) {
Serial.print(">SYNC BURST\n");
wsString(">SYNC BURST");
RXState = ZBS_RX_WAIT_HEADER;
}
if (strncmp(cmdbuffer, "XFC>", 4) == 0) {
RXState = ZBS_RX_WAIT_XFERCOMPLETE;
pktindex = 0;
packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1);
memset(cmdbuffer, 0x00, 4);
}
if (strncmp(cmdbuffer, "TJN>", 4) == 0) {
RXState = ZBS_RX_WAIT_JOINNETWORK;
pktindex = 0;
packetp = (uint8_t*)calloc(sizeof(struct espJoinNetwork) + 8, 1);
memset(cmdbuffer, 0x00, 4);
}
break;
case ZBS_RX_BLOCK_REQUEST:
packetp[pktindex] = lastchar;
pktindex++;
if (pktindex == sizeof(struct espBlockRequest)) {
processBlockRequest((struct espBlockRequest*)packetp);
free(packetp);
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_XFERCOMPLETE:
packetp[pktindex] = lastchar;
pktindex++;
if (pktindex == sizeof(struct espXferComplete)) {
struct espXferComplete* xfc = (struct espXferComplete*)packetp;
processXferComplete(xfc);
free(packetp);
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_JOINNETWORK:
packetp[pktindex] = lastchar;
pktindex++;
if (pktindex == sizeof(struct espJoinNetwork)) {
struct espJoinNetwork* ejn = (struct espJoinNetwork*)packetp;
processJoinNetwork(ejn);
free(packetp);
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_DATA_REQ:
packetp[pktindex] = lastchar;
pktindex++;
if (pktindex == sizeof(struct AvailDataReq)) {
struct AvailDataReq* adr = (struct AvailDataReq*)packetp;
processDataReq(adr);
free(packetp);
RXState = ZBS_RX_WAIT_HEADER;
}
break;
case ZBS_RX_WAIT_VER:
cmdbuffer[charindex] = lastchar;
charindex++;
if (charindex == 4) {
charindex = 0;
version = (uint16_t)strtoul(cmdbuffer, NULL, 16);
uint16_t fsversion; // BREAK here! break;
lookupFirmwareFile(fsversion);
if ((fsversion) && (version != fsversion)) {
Serial.printf("ZBS/Zigbee FW version: %04X, version on SPIFFS: %04X\n", version, fsversion);
Serial.printf("Performing flash update in about 30 seconds");
vTaskDelay(30000 / portTICK_PERIOD_MS);
performDeviceFlash();
} else if (!fsversion) {
Serial.println("No ZBS/Zigbee FW binary found on SPIFFS, please upload a zigbeebase000X.bin - format binary to enable flashing");
} else {
Serial.printf("ZBS/Zigbee FW version: %04X\n", version);
}
RXState = ZBS_RX_WAIT_HEADER;
}
break;
}
}
}
void sendSaveUpdateBlock(struct espSaveUpdateBlock* esub) {
addCRC(esub, sizeof(struct espSaveUpdateBlock));
Serial1.print("SUBL");
for (uint8_t c = 0; c < sizeof(struct espSaveUpdateBlock); c++) {
Serial1.write(((uint8_t*)esub)[c]);
}
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
Serial1.write(0x00);
}
extern uint8_t* getDataForFile(File* file);
#define STATE_TIMEOUT 0
#define STATE_PASS 1
#define STATE_FAIL 2
void doAPUpdate() {
uint8_t curBlockId = 0;
uint8_t maxBlocks = 16;
uint16_t len = 0;
uint16_t lenRemaining = 65535;
uint8_t state = true;
uint8_t* updateData = nullptr;
// open firmware file
// do AP update block erase
// 1
// TODO: some other source for the updates
File file = LittleFS.open("/main.bin");
lenRemaining = file.size();
updateData = getDataForFile(&file);
// determine how many blocks we're gonna use for this update
maxBlocks = lenRemaining / BLOCK_DATA_SIZE;
if (lenRemaining % BLOCK_DATA_SIZE) maxBlocks++;
uint32_t masterTimeout = millis();
uint32_t timeout = millis();
// begin with the process by erasing the update block on the AP's EEPROM
state = STATE_TIMEOUT;
Serial1.print("ERAS\n");
while (millis() - timeout < 5000UL) {
ShortRXWaitLoop();
// wait for 'erase okay'
if (strncmp(cmdbuffer, "EROK", 4) == 0) {
state = STATE_PASS;
break;
}
}
if (state == STATE_TIMEOUT) {
// Tag didn't respond properly to the 'erase' command, bail out
Serial.print("Failed to erase AP EEPROM data in preparation of FW update, bailing\n");
return;
}
// Begin transmitting AP update block data
state = STATE_TIMEOUT;
while (curBlockId < maxBlocks && (millis() - masterTimeout < 30000UL)) {
Serial.printf("Now doing block %d\n", curBlockId);
len = lenRemaining;
if (len > BLOCK_DATA_SIZE) len = BLOCK_DATA_SIZE;
struct espSaveUpdateBlock esub;
esub.blockId = curBlockId;
// send the block data (to the raw buffer)
esub.blockChecksum = sendBlock(updateData + (curBlockId * BLOCK_DATA_SIZE), len);
// send the update block-struct; This tells the AP which block it is and double-checks the checksum.
sendSaveUpdateBlock(&esub);
uint32_t timeout = millis();
state = STATE_TIMEOUT;
// we told the AP to save the block in a specific spot, now wait for the reply
while (millis() - timeout < 5000UL) {
ShortRXWaitLoop();
if (strncmp(cmdbuffer, "BLOK", 4) == 0) {
// all good, next block please
state = STATE_PASS;
curBlockId++;
lenRemaining -= BLOCK_DATA_SIZE;
break;
}
if (strncmp(cmdbuffer, "BLFL", 4) == 0) {
state = STATE_FAIL;
break;
}
}
}
if (curBlockId == maxBlocks) {
// update complete! (in theory) - Kick the tag to start the update process
Serial1.print("UPDA\n");
Serial1.print("UPDA\n");
Serial1.print("UPDA\n");
}
// parts left?
}
void zbsRxTask(void* parameter) {
Serial1.begin(230400, SERIAL_8N1, RXD1, TXD1);
simplePowerOn();
while (1) {
SerialRXLoop();
if (Serial.available()) {
Serial1.write(Serial.read());
}
vTaskDelay(1 / portTICK_PERIOD_MS);
if (waitingForVersion) {
if (millis() > 30000) {
waitingForVersion = false;
Serial.printf("We've been waiting for communication from the tag, but got nothing. This is expected if this tag hasn't been flashed yet. We'll try to flash it.\n");
// doAPUpdate();
// performDeviceFlash();
// SDAtest();
}
}
}
}

406
esp32_fw/src/web.cpp Normal file
View File

@@ -0,0 +1,406 @@
#include "web.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ESPmDNS.h>
#include <FS.h>
#include <LittleFS.h>
#include <SPIFFSEditor.h>
#include <WiFi.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager/tree/feature_asyncwebserver
#include "commstructs.h"
#include "newproto.h"
#include "settings.h"
extern uint8_t data_to_send[];
const char *http_username = "admin";
const char *http_password = "admin";
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
SemaphoreHandle_t wsMutex;
TaskHandle_t websocketUpdater;
uint64_t swap64(uint64_t x) {
uint64_t byte1 = x & 0xff00000000000000;
uint64_t byte2 = x & 0x00ff000000000000;
uint64_t byte3 = x & 0x0000ff0000000000;
uint64_t byte4 = x & 0x000000ff00000000;
uint64_t byte5 = x & 0x00000000ff000000;
uint64_t byte6 = x & 0x0000000000ff0000;
uint64_t byte7 = x & 0x000000000000ff00;
uint64_t byte8 = x & 0x00000000000000ff;
return (uint64_t)(byte1 >> 56 | byte2 >> 40 | byte3 >> 24 | byte4 >> 8 |
byte5 << 8 | byte6 << 24 | byte7 << 40 | byte8 << 56);
}
void webSocketSendProcess(void *parameter) {
uint32_t ulNotificationValue;
Serial.print("websocket thread started\n");
websocketUpdater = xTaskGetCurrentTaskHandle();
wsMutex = xSemaphoreCreateMutex();
while (true) {
ulNotificationValue = ulTaskNotifyTake(pdTRUE, 1000 / portTICK_RATE_MS);
if (ulNotificationValue == 0) { // timeout, so every 1s
ws.cleanupClients();
} else {
// if (ws.count())
// sendStatus(STATUS_WIFI_ACTIVITY);
DynamicJsonDocument doc(1500);
if (ulNotificationValue & 2) { // WS_SEND_MODE_STATUS) {
/* doc["rxActive"] = status.rxActive;
doc["txActive"] = status.txActive;
doc["freq"] = status.freq;
doc["txMode"] = status.currentmode;
*/
}
/*
JsonArray statusframes = doc.createNestedArray("frames");
for (uint8_t c = 0; c < STATUSFRAMELISTSIZE; c++) {
if (statusframearr[c]) {
JsonObject statusframe = statusframes.createNestedObject();
statusframe["frame"] = statusframearr[c]->frameno;
statusframe["isTX"] = statusframearr[c]->isTX;
statusframe["freq"] = statusframearr[c]->freq;
statusframe["txSkipped"] = statusframearr[c]->txCancelled;
switch (statusframearr[c]->rxtype) {
case flexsynctype::SYNC_FLEX_1600:
statusframe["rxType"] = "FLEX_1600";
break;
case flexsynctype::SYNC_FLEX_3200_2:
statusframe["rxType"] = "FLEX_3200_2";
break;
case flexsynctype::SYNC_FLEX_3200_4:
statusframe["rxType"] = "FLEX_3200_4";
break;
case flexsynctype::SYNC_FLEX_6400:
statusframe["rxType"] = "FLEX_3200_4";
break;
default:
break;
}
switch (statusframearr[c]->txformat) {
case txframe::FORMAT_FLEX:
statusframe["txType"] = "FLEX";
break;
case txframe::FORMAT_POCSAG:
statusframe["txType"] = "POCSAG";
break;
case txframe::FORMAT_IDLE:
statusframe["txType"] = "IDLE";
break;
case txframe::FORMAT_BLOCKED:
statusframe["txType"] = "BLOCKED";
break;
default:
break;
}
}
}
}*/
size_t len = measureJson(doc);
xSemaphoreTake(wsMutex, portMAX_DELAY);
auto buffer = std::make_shared<std::vector<uint8_t>>(len);
serializeJson(doc, buffer->data(), len);
// ws.textAll((char*)buffer->data());
ws.textAll("ohai");
xSemaphoreGive(wsMutex);
}
}
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
// client connected
ets_printf("ws[%s][%u] connect\n", server->url(), client->id());
xTaskNotify(websocketUpdater, 2, eSetBits);
// client->ping();
break;
case WS_EVT_DISCONNECT:
// client disconnected
ets_printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id());
break;
case WS_EVT_ERROR:
// error was received from the other end
ets_printf("WS Error received :(\n\n");
// ets_printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data);
break;
case WS_EVT_PONG:
// pong message was received (in response to a ping request maybe)
ets_printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : "");
break;
case WS_EVT_DATA:
// data packet
AwsFrameInfo *info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len) {
// the whole message is in a single frame and we got all of it's data
ets_printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT) ? "text" : "binary", info->len);
if (info->opcode == WS_TEXT) {
data[len] = 0;
ets_printf("%s\n", (char *)data);
} else {
for (size_t i = 0; i < info->len; i++) {
ets_printf("%02x ", data[i]);
}
ets_printf("\n");
}
if (info->opcode == WS_TEXT)
client->text("{\"status\":\"received\"}");
else
client->binary("{\"status\":\"received\"}");
} else {
// message is comprised of multiple frames or the frame is split into multiple packets
if (info->index == 0) {
if (info->num == 0)
ets_printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary");
ets_printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
}
ets_printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT) ? "text" : "binary", info->index, info->index + len);
if (info->message_opcode == WS_TEXT) {
data[len] = 0;
ets_printf("%s\n", (char *)data);
} else {
for (size_t i = 0; i < len; i++) {
ets_printf("%02x ", data[i]);
}
ets_printf("\n");
}
if ((info->index + len) == info->len) {
ets_printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
if (info->final) {
ets_printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT) ? "text" : "binary");
if (info->message_opcode == WS_TEXT)
client->text("{\"status\":\"received\"}");
else
client->binary("{\"status\":\"received\"}");
}
}
}
break;
}
}
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
void wsString(String text) {
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(text);
xSemaphoreGive(wsMutex);
}
void init_web() {
LittleFS.begin(true);
WiFi.mode(WIFI_STA);
WiFiManager wm;
bool res;
res = wm.autoConnect("AutoConnectAP");
if (!res) {
Serial.println("Failed to connect");
ESP.restart();
}
Serial.print("Connected! IP address: ");
Serial.println(WiFi.localIP());
server.addHandler(new SPIFFSEditor(LittleFS, http_username, http_password));
ws.onEvent(onEvent);
server.addHandler(&ws);
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", String(ESP.getFreeHeap()));
});
server.on("/reboot", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "OK Reboot");
ESP.restart();
});
server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm");
server.on(
"/imgupload", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200);
},
doImageUpload);
server.on("/send_image", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
if (request->hasParam("filename", true) && request->hasParam("dst", true)) {
filename = request->getParam("filename", true)->value();
dst = request->getParam("dst", true)->value();
uint8_t mac_addr[12]; // I expected this to return like 8 values, but if I make the array 8 bytes long, things die.
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X",
&mac_addr[2],
&mac_addr[3],
&mac_addr[4],
&mac_addr[5],
&mac_addr[6],
&mac_addr[7]) != 6) {
request->send(200, "text/plain", "Something went wrong trying to parse the mac address");
} else {
*((uint64_t *)mac_addr) = swap64(*((uint64_t *)mac_addr));
if (prepareDataAvail(&filename, DATATYPE_IMGRAW, mac_addr)) {
request->send(200, "text/plain", "Sending to " + dst);
} else {
request->send(200, "text/plain", "Couldn't find filename :(");
}
}
return;
}
request->send(200, "text/plain", "Didn't get the required filename + dst");
return;
});
server.on("/send_fw", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
if (request->hasParam("filename", true) && request->hasParam("dst", true)) {
filename = request->getParam("filename", true)->value();
dst = request->getParam("dst", true)->value();
uint8_t mac_addr[12]; // I expected this to return like 8 values, but if I make the array 8 bytes long, things die.
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X",
&mac_addr[2],
&mac_addr[3],
&mac_addr[4],
&mac_addr[5],
&mac_addr[6],
&mac_addr[7]) != 6) {
request->send(200, "text/plain", "Something went wrong trying to parse the mac address");
} else {
*((uint64_t *)mac_addr) = swap64(*((uint64_t *)mac_addr));
if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac_addr)) {
request->send(200, "text/plain", "Sending FW to " + dst);
} else {
request->send(200, "text/plain", "Couldn't find filename :(");
}
}
return;
}
request->send(200, "text/plain", "Didn't get the required filename + dst");
return;
});
server.on("/req_checkin", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
if (request->hasParam("dst", true)) {
dst = request->getParam("dst", true)->value();
uint8_t mac_addr[12]; // I expected this to return like 8 values, but if I make the array 8 bytes long, things die.
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X",
&mac_addr[2],
&mac_addr[3],
&mac_addr[4],
&mac_addr[5],
&mac_addr[6],
&mac_addr[7]) != 6) {
request->send(200, "text/plain", "Something went wrong trying to parse the mac address");
} else {
*((uint64_t *)mac_addr) = swap64(*((uint64_t *)mac_addr));
if (prepareDataAvail(&filename, DATATYPE_NOUPDATE, mac_addr)) {
request->send(200, "text/plain", "Sending check-in request to " + dst);
}
}
return;
}
request->send(200, "text/plain", "Didn't get the required params");
return;
});
server.onNotFound([](AsyncWebServerRequest *request) {
if (request->url() == "/" || request->url() == "index.htm") {
request->send(200, "text/html", "-");
return;
}
Serial.printf("NOT_FOUND: ");
switch (request->method()) {
case HTTP_GET:
Serial.printf("GET");
break;
case HTTP_POST:
Serial.printf("POST");
break;
case HTTP_DELETE:
Serial.printf("DELETE");
break;
case HTTP_PUT:
Serial.printf("PUT");
break;
case HTTP_PATCH:
Serial.printf("PATCH");
break;
case HTTP_HEAD:
Serial.printf("HEAD");
break;
case HTTP_OPTIONS:
Serial.printf("OPTIONS");
break;
default:
Serial.printf("UNKNOWN");
break;
}
Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
if (request->contentLength()) {
Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
}
for (int i = 0; i < request->headers(); i++) {
AsyncWebHeader *h = request->getHeader(i);
Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
}
for (int i = 0; i < request->params(); i++) {
AsyncWebParameter *p = request->getParam(i);
if (p->isFile()) {
Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
} else if (p->isPost()) {
Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
} else {
Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
}
}
request->send(404);
});
server.begin();
}
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
Serial.print((String) "UploadStart: " + filename);
// open the file on first call and store the file handle in the request object
request->_tempFile = LittleFS.open("/" + filename, "w");
}
if (len) {
// stream the incoming chunk to the opened file
request->_tempFile.write(data, len);
}
if (final) {
Serial.print((String) "UploadEnd: " + filename + "," + index + len);
// close the file handle as the upload is now done
request->_tempFile.close();
request->send(200, "text/plain", "File Uploaded !");
/*
sscanf() if (request->hasParam("id") && request->hasParam("file")) {
id = request->getParam("id")->value().toInt();
filename = request->getParam("file")->value();
}
*/
}
}

View File

@@ -0,0 +1,214 @@
/* Autor: Aaron Christophel ATCnetz.de */
#include "zbs_interface.h"
#include <Arduino.h>
#include <stdint.h>
#include <stdio.h>
#include "settings.h"
void simplePowerOn() {
pinMode(ZBS_SS, INPUT);
pinMode(ZBS_CLK, INPUT);
pinMode(ZBS_MoSi, INPUT);
pinMode(ZBS_MiSo, INPUT);
pinMode(ZBS_Reset, OUTPUT);
digitalWrite(ZBS_Reset, HIGH);
zbs.set_power(0);
delay(500);
zbs.set_power(1);
}
uint8_t ZBS_interface::begin() {
_SS_PIN = ZBS_SS;
_CLK_PIN = ZBS_CLK;
_MOSI_PIN = ZBS_MoSi;
_MISO_PIN = ZBS_MiSo;
_RESET_PIN = ZBS_Reset;
pinMode(_SS_PIN, OUTPUT);
pinMode(_CLK_PIN, OUTPUT);
pinMode(_MOSI_PIN, OUTPUT);
pinMode(_MISO_PIN, INPUT);
pinMode(_RESET_PIN, OUTPUT);
digitalWrite(_SS_PIN, HIGH);
digitalWrite(_CLK_PIN, LOW);
digitalWrite(_MOSI_PIN, HIGH);
digitalWrite(_RESET_PIN, HIGH);
set_power(ZBS_ON);
enable_debug();
return check_connection();
}
void ZBS_interface::set_power(uint8_t state) {
pinMode(ZBS_POWER1, INPUT);
pinMode(ZBS_POWER2, INPUT);
digitalWrite(ZBS_POWER1, state);
digitalWrite(ZBS_POWER2, state);
pinMode(ZBS_POWER1, OUTPUT);
pinMode(ZBS_POWER2, OUTPUT);
}
void ZBS_interface::enable_debug() {
digitalWrite(_RESET_PIN, HIGH);
digitalWrite(_SS_PIN, HIGH);
digitalWrite(_CLK_PIN, LOW);
digitalWrite(_MOSI_PIN, HIGH);
delay(30);
digitalWrite(_RESET_PIN, LOW);
delay(33);
digitalWrite(_CLK_PIN, HIGH);
delay(1);
digitalWrite(_CLK_PIN, LOW);
delay(1);
digitalWrite(_CLK_PIN, HIGH);
delay(1);
digitalWrite(_CLK_PIN, LOW);
delay(1);
digitalWrite(_CLK_PIN, HIGH);
delay(1);
digitalWrite(_CLK_PIN, LOW);
delay(1);
digitalWrite(_CLK_PIN, HIGH);
delay(1);
digitalWrite(_CLK_PIN, LOW);
delay(9);
digitalWrite(_RESET_PIN, HIGH);
delay(100);
}
void ZBS_interface::reset() {
pinMode(_SS_PIN, INPUT);
pinMode(_CLK_PIN, INPUT);
pinMode(_MOSI_PIN, INPUT);
pinMode(_MISO_PIN, INPUT);
digitalWrite(_RESET_PIN, LOW);
set_power(ZBS_OFF);
delay(500);
digitalWrite(_RESET_PIN, HIGH);
set_power(ZBS_ON);
pinMode(_RESET_PIN, INPUT);
}
void ZBS_interface::send_byte(uint8_t data) {
digitalWrite(_SS_PIN, LOW);
delayMicroseconds(5);
for (int i = 0; i < 8; i++) {
if (data & 0x80) {
digitalWrite(_MOSI_PIN, HIGH);
} else {
digitalWrite(_MOSI_PIN, LOW);
}
delayMicroseconds(ZBS_spi_delay);
digitalWrite(_CLK_PIN, HIGH);
delayMicroseconds(ZBS_spi_delay);
digitalWrite(_CLK_PIN, LOW);
data <<= 1;
}
delayMicroseconds(2);
digitalWrite(_SS_PIN, HIGH);
}
uint8_t ZBS_interface::read_byte() {
uint8_t data = 0x00;
digitalWrite(_SS_PIN, LOW);
delayMicroseconds(5);
for (int i = 0; i < 8; i++) {
data <<= 1;
if (digitalRead(_MISO_PIN)) {
data |= 1;
}
delayMicroseconds(ZBS_spi_delay);
digitalWrite(_CLK_PIN, HIGH);
delayMicroseconds(ZBS_spi_delay);
digitalWrite(_CLK_PIN, LOW);
}
delayMicroseconds(2);
digitalWrite(_SS_PIN, HIGH);
return data;
}
void ZBS_interface::write_byte(uint8_t cmd, uint8_t addr, uint8_t data) {
send_byte(cmd);
send_byte(addr);
send_byte(data);
delay(1);
}
uint8_t ZBS_interface::read_byte(uint8_t cmd, uint8_t addr) {
uint8_t data = 0x00;
send_byte(cmd);
send_byte(addr);
data = read_byte();
delay(1);
return data;
}
void ZBS_interface::write_flash(uint16_t addr, uint8_t data) {
send_byte(ZBS_CMD_W_FLASH);
send_byte(addr >> 8);
send_byte(addr);
send_byte(data);
delay(1);
}
uint8_t ZBS_interface::read_flash(uint16_t addr) {
uint8_t data = 0x00;
send_byte(ZBS_CMD_R_FLASH);
send_byte(addr >> 8);
send_byte(addr);
data = read_byte();
delay(1);
return data;
}
void ZBS_interface::write_ram(uint8_t addr, uint8_t data) {
write_byte(ZBS_CMD_W_RAM, addr, data);
}
uint8_t ZBS_interface::read_ram(uint8_t addr) {
return read_byte(ZBS_CMD_R_RAM, addr);
}
void ZBS_interface::write_sfr(uint8_t addr, uint8_t data) {
write_byte(ZBS_CMD_W_SFR, addr, data);
}
uint8_t ZBS_interface::read_sfr(uint8_t addr) {
return read_byte(ZBS_CMD_R_SFR, addr);
}
uint8_t ZBS_interface::check_connection() {
uint8_t test_byte = 0xA5;
write_ram(0xba, test_byte);
delay(1);
return read_ram(0xba) == test_byte;
}
uint8_t ZBS_interface::select_flash(uint8_t page) {
uint8_t sfr_low_bank = page ? 0x80 : 0x00;
write_sfr(0xd8, sfr_low_bank);
delay(1);
return read_sfr(0xd8) == sfr_low_bank;
}
void ZBS_interface::erase_flash() {
send_byte(ZBS_CMD_ERASE_FLASH);
send_byte(0x00);
send_byte(0x00);
send_byte(0x00);
delay(100);
}
void ZBS_interface::erase_infoblock() {
send_byte(ZBS_CMD_ERASE_INFOBLOCK);
send_byte(0x00);
send_byte(0x00);
send_byte(0x00);
delay(100);
}
ZBS_interface zbs;

11
esp32_fw/test/README Normal file
View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html

25
tag_fw/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
*.asm
*.lst
*.rst
*.sym
*.ihx
*.map
*.mem
*.adb
*.cdb
*.omf
*.bak
*.rel
*.asm
*.lst
*.rst
*.sym
*.map
*.mem
*.ihx
*.adb
*.cdb
*.omf
*.bin
*.lk
*.o

7
tag_fw/LICENSE.txt Normal file
View File

@@ -0,0 +1,7 @@
This E-Paper firmware mod is based on Dmitry Grinbergs firmware which is available here:
http://dmitry.gr/?r=05.Projects&proj=29.%20eInk%20Price%20Tags
Many thanks to Dmitry for sharing such a wonderful work !
The license is simple:
This code/data/waveforms are free for use in hobby and other non-commercial products. For commercial use, Dmitry: licensing@dmitry.gr

77
tag_fw/Makefile Normal file
View File

@@ -0,0 +1,77 @@
BUILD ?= zbs29v033
#file containing main() must be first!
SOURCES += main.c eeprom.c drawing.c
SOURCES += comms.c chars.c
SOURCES += syncedproto.c
all: #make sure it is the first target
include board/$(BUILD)/make.mk
include soc/$(SOC)/make.mk
include cpu/$(CPU)/make.mk
FLAGS += -Iboard/$(BUILD)
FLAGS += -Isoc/$(SOC)
FLAGS += -Icpu/$(CPU)
SOURCES += cpu/$(CPU)/cpu.c
SOURCES += board/$(BUILD)/board.c
SOURCES += board/$(BUILD)/screen.c
ifeq ($(BARCODE),barcode)
FLAGS += -DBARCODE
SOURCES += barcode.c
else ifeq ($(BARCODE),datamatrix)
# FLAGS += -DDATAMATRIX
# SOURCES += datamatrix.c
#i might release this later, but for now, just use barcodes :P
FLAGS += -DBARCODE
SOURCES += barcode.c
else
SOURCES += $(warning "no barcode type set")
endif
EEPROMDRV ?= eeprom.c
SOURCES += $(EEPROMDRV)
FLAGS += -I.
OBJS = $(patsubst %.c,%.$(OBJFILEEXT),$(SOURCES))
all: $(TARGETS)
%.$(OBJFILEEXT): %.c
$(CC) -c $^ $(FLAGS) -o $@
main.ihx: $(OBJS)
rm -f $@
$(CC) $^ $(FLAGS)
main.elf: $(OBJS)
$(CC) $(FLAGS) -o $@ $^
%.bin: %.ihx
objcopy $^ $@ -I ihex -O binary
.PHONY: clean
clean:
rm -f $(patsubst %.c,%.asm,$(SOURCES))
rm -f $(patsubst %.c,%.lst,$(SOURCES))
rm -f $(patsubst %.c,%.rst,$(SOURCES))
rm -f $(patsubst %.c,%.sym,$(SOURCES))
rm -f $(patsubst %.c,%.map,$(SOURCES))
rm -f $(patsubst %.c,%.mem,$(SOURCES))
rm -f $(patsubst %.c,%.ihx,$(SOURCES))
rm -f $(patsubst %.c,%.adb,$(SOURCES))
rm -f $(patsubst %.c,%.adb,$(SOURCES))
rm -f $(patsubst %.c,%.omf,$(SOURCES))
rm -f $(patsubst %.c,%.bin,$(SOURCES))
rm -f $(OBJS)

16
tag_fw/adc.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _ADC_H_
#define _ADC_H_
#include <stdint.h>
extern uint16_t __xdata mAdcSlope;
extern uint16_t __xdata mAdcIntercept;
uint16_t adcSampleBattery(void); //in mV
int8_t adcSampleTemperature(void); //in degrees C
#endif

114
tag_fw/asmUtil.h Normal file
View File

@@ -0,0 +1,114 @@
#ifndef _ASM_UTIL_H_
#define _ASM_UTIL_H_
#include <stdint.h>
#include "cpu.h"
//SDCC may have uint64_t support, but it is so shitty, we're better off not using it
//sdcc is brain dead when compiling multiplication, so we write our own asm code to make it better...le sigh...
//SDCC also has issues managing xdata memory ops, we write our own
#pragma callee_saves u64_copy
#pragma callee_saves u64_isLt
#pragma callee_saves u64_isEq
#pragma callee_saves u64_sub
#pragma callee_saves u64_add
#pragma callee_saves u64_inc
#pragma callee_saves u64_dec
#pragma callee_saves xMemSet
#pragma callee_saves xMemEqual
#pragma callee_saves xMemEqual4
#pragma callee_saves xMemCopy
#pragma callee_saves xMemCopyShort
#pragma callee_saves xMemCopy8
#pragma callee_saves xStrLen
#pragma callee_saves rngGen
#pragma callee_saves mathPrvMul8x8
#pragma callee_saves mathPrvMul16x8
#pragma callee_saves mathPrvMul16x16
#pragma callee_saves mathPrvMul32x8
#pragma callee_saves mathPrvDiv32x8
#pragma callee_saves mathPrvDiv32x16
#pragma callee_saves mathPrvMod32x16
#pragma callee_saves mathPrvDiv16x8
#pragma callee_saves mathPrvMod16x8
#pragma callee_saves mathPrvCopyPostinc
#pragma callee_saves mathPrvI16Asr1
#pragma callee_saves mathPrvU8bitswap
#pragma callee_saves charsPrvDerefAndIncGenericPtr //return *(*generic_charPtr)++
//it saddens me that i need these....but i do
#pragma callee_saves mathPrvU16from2xU8
#pragma callee_saves mathPrvU32from4xU8
#pragma callee_saves u32minusU16
#pragma callee_saves u32plusU16
#pragma callee_saves u32Nonzero
#pragma callee_saves i32Negative
__bit u32minusU16(uint32_t __xdata *u32, uint16_t u16) __reentrant; //sets carry
__bit u32plusU16(uint32_t __xdata *u32, uint16_t u16) __reentrant; //sets carry
uint8_t u32Nonzero(uint32_t __xdata *u32) __reentrant;
__bit i32Negative(uint32_t __xdata *u32) __reentrant;
void u64_copy(uint64_t __xdata *dst, const uint64_t __xdata *src) __reentrant;
void u64_copyFromCode(uint64_t __xdata *dst, const uint64_t __code *src) __reentrant;
__bit u64_isLt(const uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
__bit u64_isEq(const uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_sub(uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_add(uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_and(uint64_t __xdata *lhs, const uint64_t __xdata *rhs) __reentrant;
void u64_inc(uint64_t __xdata *dst) __reentrant;
void u64_dec(uint64_t __xdata *dst) __reentrant;
#define U64FMT "%04x%04x%04x%04x"
#define U64CVT(v) ((uint16_t __xdata*)&v)[3], ((uint16_t __xdata*)&v)[2], ((uint16_t __xdata*)&v)[1], ((uint16_t __xdata*)&v)[0]
int16_t mathPrvI16Asr1(int16_t val) __reentrant;
uint16_t mathPrvMul8x8(uint8_t a, uint8_t b) __reentrant;
uint32_t mathPrvMul16x8(uint16_t a, uint8_t b) __reentrant;
uint32_t mathPrvMul16x16(uint16_t a, uint16_t b) __reentrant;
uint32_t mathPrvMul32x8(uint32_t a, uint8_t b) __reentrant;
uint32_t mathPrvDiv32x8(uint32_t num, uint8_t denom) __reentrant;
uint32_t mathPrvDiv32x16(uint32_t num, uint16_t denom) __reentrant;
uint16_t mathPrvMod32x16(uint32_t num, uint16_t denom) __reentrant;
uint16_t mathPrvDiv16x8(uint16_t num, uint8_t denom) __reentrant;
uint8_t mathPrvMod16x8(uint16_t num, uint8_t denom) __reentrant;
uint8_t mathPrvU8bitswap(uint8_t val) __reentrant;
uint16_t mathPrvU16from2xU8(uint8_t hi, uint8_t lo) __reentrant;
uint32_t mathPrvU32from4xU8(uint8_t hi, uint8_t midhi, uint8_t midlo, uint8_t lo) __reentrant;
char charsPrvDerefAndIncGenericPtr(const char * __xdata* __xdata str);
void xMemSet(void __xdata* mem, uint8_t val, uint16_t num) __reentrant;
__bit xMemEqual(const void __xdata* memA, const void __xdata* memB, uint8_t num) __reentrant;
__bit xMemEqual4(const void __xdata* memA, const void __xdata* memB) __reentrant;
void xMemCopy(void __xdata* dst, const void __xdata* src, uint16_t num) __reentrant;
void xMemCopyShort(void __xdata* dst, const void __xdata* src, uint8_t num) __reentrant;
uint16_t xStrLen(const char __xdata *str) __reentrant;
#define xMemCopy8(_dst, _src) u64_copy((uint64_t __xdata*)(_dst), (const uint64_t __xdata*)(_src))
void mathPrvCopyPostinc(uint32_t __xdata *dst, uint32_t __xdata *src) __reentrant; //*dst = (*src)++
//private
void mathPrvSwapDptrR1R0(void) __reentrant;
#endif

101
tag_fw/barcode.c Normal file
View File

@@ -0,0 +1,101 @@
#include <stdbool.h>
#include "barcode.h"
#include "asmUtil.h"
//code128 generator (c) 2021 Dmitry Grinberg https://dmitry.gr
//non-commercial use only, contact licensing@dmitry.gr for other options
#pragma nogcse
static const uint16_t __code mCode128[] = {
0b00110011011, 0b00110110011, 0b01100110011, 0b00011001001, 0b00110001001, 0b00110010001, 0b00010011001, 0b00100011001,
0b00100110001, 0b00010010011, 0b00100010011, 0b00100100011, 0b00111001101, 0b00111011001, 0b01110011001, 0b00110011101,
0b00110111001, 0b01100111001, 0b01001110011, 0b00111010011, 0b01110010011, 0b00100111011, 0b00101110011, 0b01110110111,
0b00110010111, 0b00110100111, 0b01100100111, 0b00100110111, 0b00101100111, 0b01001100111, 0b00011011011, 0b01100011011,
0b01101100011, 0b00011000101, 0b00011010001, 0b01100010001, 0b00010001101, 0b00010110001, 0b01000110001, 0b00010001011,
0b00010100011, 0b01000100011, 0b00011101101, 0b01110001101, 0b01110110001, 0b00011011101, 0b01100011101, 0b01101110001,
0b01101110111, 0b01110001011, 0b01110100011, 0b00010111011, 0b01000111011, 0b01110111011, 0b00011010111, 0b01100010111,
0b01101000111, 0b00010110111, 0b01000110111, 0b01011000111, 0b01011110111, 0b01000010011, 0b01010001111, 0b00001100101,
0b00110000101, 0b00001101001, 0b01100001001, 0b00110100001, 0b01100100001, 0b00001001101, 0b00100001101, 0b00001011001,
0b01000011001, 0b00101100001, 0b01001100001, 0b01001000011, 0b00001010011, 0b01011101111, 0b00101000011, 0b01011110001,
0b00111100101, 0b00111101001, 0b01111001001, 0b00100111101, 0b00101111001, 0b01001111001, 0b00100101111, 0b00101001111,
0b01001001111, 0b01111011011, 0b01101111011, 0b01101101111, 0b00011110101, 0b01111000101, 0b01111010001, 0b00010111101,
0b01000111101, 0b00010101111, 0b01000101111, 0b01111011101, 0b01110111101, 0b01111010111, 0b01110101111
};
#define CODE128_START_B (0b00001001011)
#define CODE128_STOP (0b1101011100011)
#define CODE128_IDX_START_A (103)
#define CODE128_IDX_START_B (104)
#define CODE128_IDX_START_C (105)
#define CODE128_IDX_CODE_STOP (106)
enum BarCodeState {
BarCodeInit,
BarCodeEmittingChar,
BarCodeEmittingChecksum,
BarCodeEmittingStop,
BarCodeDone,
};
__bit barcodeIsDone(struct BarcodeInfo __xdata *bci)
{
return bci->state == BarCodeDone;
}
__bit barcodeNextBar(struct BarcodeInfo __xdata *bci)
{
uint8_t t;
__bit ret;
if (!bci->barsLeft) switch (bci->state) {
case BarCodeInit:
bci->curBars = CODE128_START_B;
bci->barsLeft = 11;
bci->state = BarCodeEmittingChar;
bci->csum = CODE128_IDX_START_B;
break;
case BarCodeEmittingChar:
t = charsPrvDerefAndIncGenericPtr(&bci->str);
if (t) {
t -= 0x20;
if (t >= 0x60)
t = '?' - 0x20;
bci->csum = mathPrvMod16x8(mathPrvMul8x8(++bci->csumMul, t) + bci->csum, 103);
}
else {
bci->state = BarCodeEmittingChecksum;
t = bci->csum;
}
bci->curBars = mCode128[t];
bci->barsLeft = 11;
break;
case BarCodeEmittingChecksum:
bci->state = BarCodeEmittingStop;
bci->curBars = CODE128_STOP;
bci->barsLeft = 13;
break;
case BarCodeEmittingStop:
bci->state = BarCodeDone;
//fallthrough
case BarCodeDone:
default:
return false;
}
ret = bci->curBars & 1;
bci->curBars >>= 1;
bci->barsLeft--;
return ret;
}

29
tag_fw/barcode.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _BARCODE_H_
#define _BARCODE_H_
#include <stdint.h>
//code128 generator (c) 2021 Dmitry Grinberg https://dmitry.gr
//non-commercial use only, contact licensing@dmitry.gr for other options
struct BarcodeInfo { //zero-init this except the string ptr
const char *str;
uint16_t curBars;
uint8_t barsLeft;
uint8_t state;
uint8_t csum;
uint8_t csumMul;
};
#define barcodeWidth(nChars) (11 * (nChars) + 11 /*start */+ 11 /*check sum */ + 13 /* stop */)
#pragma callee_saves barcodeIsDone
__bit barcodeIsDone(struct BarcodeInfo __xdata *bci);
#pragma callee_saves barcodeNextBar
__bit barcodeNextBar(struct BarcodeInfo __xdata *bci);
#endif

View File

@@ -0,0 +1,58 @@
#ifndef _BOARD_COMMON_H_
#define _BOARD_COMMON_H_
#include <stdint.h>
#pragma callee_saves powerPortsDownForSleep
void powerPortsDownForSleep(void);
#pragma callee_saves boardInit
void boardInit(void);
//early - before most things
#pragma callee_saves boardInitStage2
void boardInitStage2(void);
//late, after eeprom
#pragma callee_saves boardInit
__bit boardGetOwnMac(uint8_t __xdata *mac);
//some sanity checks
#include "eeprom.h"
#if !EEPROM_SETTINGS_AREA_START
#error "settings cannot be at address 0"
#endif
#if (EEPROM_SETTINGS_AREA_LEN % EEPROM_ERZ_SECTOR_SZ) != 0
#error "settings area must be an integer number of eeprom blocks"
#endif
#if (EEPROM_SETTINGS_AREA_START % EEPROM_ERZ_SECTOR_SZ) != 0
#error "settings must begin at an integer number of eeprom blocks"
#endif
#if (EEPROM_IMG_EACH % EEPROM_ERZ_SECTOR_SZ) != 0
#error "each image must be an integer number of eeprom blocks"
#endif
#if (EEPROM_IMG_START % EEPROM_ERZ_SECTOR_SZ) != 0
#error "images must begin at an integer number of eeprom blocks"
#endif
#if (EEPROM_UPDATE_AREA_LEN % EEPROM_ERZ_SECTOR_SZ) != 0
#error "update must be an integer number of eeprom blocks"
#endif
#if (EEPROM_UPDATA_AREA_START % EEPROM_ERZ_SECTOR_SZ) != 0
#error "images must begin at an integer number of eeprom blocks"
#endif
#endif

View File

@@ -0,0 +1,166 @@
#include <stdbool.h>
#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 "adc.h"
#include "i2c.h"
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)
{
wdtOff();
//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.7(eink_nCS)
P1FUNC &=~ ((1 << 1) | (1 << 2) | (1 << 7));
P1DIR &= ~((1 << 1) | (1 << 2) | (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 = 0;
uartInit();
spiInit();
}
void boardInitStage2(void)
{
//nothing yet
}
__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 = mScreenRow;
for (i = len; i ; i--)
*dst++ = *src++;
if (!flashWrite(0xfc00, mScreenRow, 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"
);
}

View File

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

View File

@@ -0,0 +1,53 @@
#ifndef _BOARD_H_
#define _BOARD_H_
#include <stdint.h>
#include "spi.h"
#include "uart.h"
//colors for ui messages
#define UI_MSG_MAGNIFY1 1
#define UI_MSG_MAGNIFY2 1
#define UI_MSG_MAGNIFY3 1
#define UI_MSG_BACK_COLOR 4
#define UI_MSG_FORE_COLOR_1 0
#define UI_MSG_FORE_COLOR_2 5
#define UI_MSG_FORE_COLOR_3 5
#define UI_BARCODE_VERTICAL
#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)
//debug uart (enable only when needed, on some boards it inhibits eeprom access)
#define dbgUartOn()
#define dbgUartOff()
#define dbgUartByte uartTx
//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 (0x02000UL)
//till end of eeprom really. do not put anything after - it will be erased at pairing time!!!
#define EEPROM_PROGRESS_BYTES (128)
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (1)
//hw types
#define HW_TYPE_NORMAL HW_TYPE_154_INCH_ZBS_033
#define HW_TYPE_CYCLING HW_TYPE_154_INCH_ZBS_033_FRAME_MODE
#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,475 @@
#include "screen.h"
#include <stdbool.h>
#include "adc.h"
#include "asmUtil.h"
#include "board.h"
#include "cpu.h"
#include "printf.h"
#include "sleep.h"
#include "spi.h"
#include "timer.h"
uint8_t __xdata mScreenRow[320];
static __bit mInited = false, mPartial;
static uint8_t __xdata mPassNo;
#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
static const uint8_t __code mColorMap[][6] =
{
// colors are: B, DG, G, LG, W, R
// phase 0 (LUTS: B:W:R:G, purpose: BWR, prepare greys)
{
1, 1, 1, 1, 0, 0, // lo plane (B)
},
{
0, 0, 0, 0, 0, 1, // hi plane (R)
}};
static const uint8_t __code partial_lut[] = {
// lut0 (KEEP) voltages
0x40,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
// lut1 (W2B) voltages
0x80,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
// lut2 (B2W) voltages
0x40,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
// lut3 (unused) voltages
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
// lut4 (vcom) voltages
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
// group0 phase lengths and repeat count
45,
0x00,
0x00,
0x00,
0x00,
// group1 not used
0x00,
0x00,
0x00,
0x00,
0x00,
// group2 not used
0x00,
0x00,
0x00,
0x00,
0x00,
// group3 phase lengths and repeat count
0x00,
0x00,
0x00,
0x00,
0x00,
// group4 phase lengths and repeat count
0x00,
0x00,
0x00,
0x00,
0x00,
// group5 phase lengths and repeat count
0x00,
0x00,
0x00,
0x00,
0x00,
// group6 phase lengths and repeat count
0x00,
0x00,
0x00,
0x00,
0x00,
};
#define einkPrvSelect() \
do { \
P1_7 = 0; \
} while (0)
#define einkPrvDeselect() \
do { \
P1_7 = 1; \
} while (0)
// urx pin
#define einkPrvMarkCommand() \
do { \
P2_2 = 0; \
} while (0)
#define einkPrvMarkData() \
do { \
P2_2 = 1; \
} while (0)
#pragma callee_saves einkPrvCmd
static void einkPrvCmd(uint8_t cmd) // sets chip select
{
einkPrvSelect();
einkPrvMarkCommand();
spiByte(cmd);
}
#pragma callee_saves einkPrvData
static void einkPrvData(uint8_t byte) {
einkPrvMarkData();
spiByte(byte);
}
#pragma callee_saves einkPrvCmdWithOneByte
static void einkPrvCmdWithOneByte(uint16_t vals) // passing in one u16 is better than two params cause SDCC sucks
{
einkPrvCmd(vals >> 8);
einkPrvData(vals);
einkPrvDeselect();
}
#pragma callee_saves einkPrvWaitWithTimeout
static void einkPrvWaitWithTimeout(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)
;
}
#pragma callee_saves einkPrvWaitWithTimeout
static void einkPrvWaitWithTimeoutSleep(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);
P2CHSTA &= 0xfd;
P2INTEN &= 0xfd;
P2FUNC = tmp_P2FUNC;
P2DIR = tmp_P2DIR;
P2PULL = tmp_P2PULL;
P2LVLSEL = tmp_P2LVLSEL;
/*if (!P2_1)
return;
pr("screen timeout\n");
while(1);*/
}
#pragma callee_saves einkPrvReadByte
static uint8_t einkPrvReadByte(void) {
uint8_t val = 0, i;
P0DIR = (P0DIR & ~(1 << 0)) | (1 << 1);
P0 &= ~(1 << 0);
P0FUNC &= ~((1 << 0) | (1 << 1));
P2_2 = 1;
for (i = 0; i < 8; i++) {
P0_0 = 1;
__asm__("nop\nnop\nnop\nnop\nnop\n");
val <<= 1;
if (P0_1)
val++;
P0_0 = 0;
__asm__("nop\nnop\nnop\nnop\nnop\n");
}
// set up pins for spi (0.0,0.1,0.2)
P0FUNC |= (1 << 0) | (1 << 1);
return val;
}
#pragma callee_saves einkPrvReadStatus
static uint8_t einkPrvReadStatus(void) {
uint8_t sta;
einkPrvCmd(0x2f);
sta = einkPrvReadByte();
einkPrvDeselect();
return sta;
}
#pragma callee_saves screenPrvStartSubPhase
static void screenPrvStartSubPhase(__bit redSubphase) {
einkPrvCmd(0x4e);
einkPrvData(0);
einkPrvDeselect();
einkPrvCmd(0x4f);
einkPrvData(0x00);
einkPrvData(0x00);
einkPrvDeselect();
einkPrvCmd(redSubphase ? 0x26 : 0x24);
einkPrvDeselect();
}
void moveToXY(uint8_t x, uint16_t y, bool color) {
einkPrvCmd(0x4e);
einkPrvData(x);
einkPrvDeselect();
einkPrvCmd(0x4f);
einkPrvData((uint8_t)(y & 0xFF));
einkPrvData((uint8_t)(y >> 8));
einkPrvDeselect();
einkPrvCmd(color ? 0x26 : 0x24);
einkPrvDeselect();
}
#pragma callee_saves screenInitIfNeeded
static void screenInitIfNeeded(__bit forPartial) {
if (mInited)
return;
mInited = true;
mPartial = forPartial;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 0;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
P2_0 = 1;
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
einkPrvCmd(0x12); // software reset
einkPrvDeselect();
timerDelay(TIMER_TICKS_PER_SECOND / 1000);
einkPrvCmdWithOneByte(0x7454);
einkPrvCmdWithOneByte(0x7e3b);
einkPrvCmd(0x2b);
einkPrvData(0x04);
einkPrvData(0x63);
einkPrvDeselect();
einkPrvCmd(0x0c); // they send 8f 8f 8f 3f
einkPrvData(0x8f);
einkPrvData(0x8f);
einkPrvData(0x8f);
einkPrvData(0x3f);
einkPrvDeselect();
einkPrvCmd(0x01);
einkPrvData(0x97);
einkPrvData(0x00);
einkPrvData(0x00);
einkPrvDeselect();
einkPrvCmdWithOneByte(0x1103);
einkPrvDeselect();
einkPrvCmdWithOneByte(0x2200 | SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON);
einkPrvCmd(0x20);
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND * 10);
einkPrvCmd(0x44);
einkPrvData(0x00);
einkPrvData(SCREEN_WIDTH / 8 - 1);
einkPrvDeselect();
einkPrvCmd(0x45);
einkPrvData(0x00);
einkPrvData(0x00);
einkPrvData(0x97);
einkPrvData(0x00);
einkPrvDeselect();
einkPrvCmdWithOneByte(0x3c01); // border will be HiZ
einkPrvDeselect();
einkPrvCmdWithOneByte(0x1880); // internal temp sensor
einkPrvCmdWithOneByte(0x2108);
// turn on clock & analog
einkPrvCmdWithOneByte(0x22B1);
einkPrvCmd(0x20); // do action
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
if (forPartial) {
einkPrvCmd(0x32);
for (int i = 0; i < sizeof(partial_lut); i++)
einkPrvData(partial_lut[i]);
}
einkPrvDeselect();
}
#pragma callee_saves screenPrvDraw
static void screenPrvDraw(void) {
einkPrvCmdWithOneByte(0x2200 | SCREEN_CMD_REFRESH);
einkPrvCmd(0x20); // do actions
if (0) {
einkPrvWaitWithTimeoutSleep(1000 * 60UL);
screenSleep();
} else {
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND * 60UL);
}
}
__bit screenTxStart(__bit forPartial) {
screenInitIfNeeded(forPartial);
mPassNo = 0;
screenPrvStartSubPhase(false);
return true;
}
void screenEndPass(void) {
switch (mPassNo) {
case 0:
screenPrvStartSubPhase(true);
break;
default:
return;
}
mPassNo++;
}
void screenTxEnd(void) {
screenPrvDraw();
screenShutdown();
}
void screenShutdown(void) {
if (!mInited)
return;
mInited = false;
einkPrvCmdWithOneByte(0x1003); // shut down
}
void screenSleep(void) {
P2_0 = 0;
timerDelay(10);
P2_0 = 1;
timerDelay(50);
einkPrvCmd(0x12); // software reset
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND / 250);
einkPrvCmdWithOneByte(0x1003); // shut down
}
#pragma callee_saves screenByteTx
void screenByteTx(uint8_t byte) {
static uint8_t __xdata prev, step = 0;
prev <<= 2;
prev |= (mColorMap[mPassNo][byte >> 4] << 1) | mColorMap[mPassNo][byte & 0x0f];
if (++step == 4) {
step = 0;
einkPrvSelect();
einkPrvData(prev);
einkPrvDeselect();
}
}
void screenByteRawTx(uint8_t byte) {
einkPrvSelect();
einkPrvData(byte);
einkPrvDeselect();
}
// yes this is here...
uint16_t adcSampleBattery(void) {
__bit wasInited = mInited;
uint16_t voltage = 2600;
if (!mInited)
screenInitIfNeeded(false);
uint8_t val;
einkPrvCmdWithOneByte(0x2200 | SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON);
einkPrvCmd(0x20); // do action
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
for (val = 3; val < 8; val++) {
einkPrvCmdWithOneByte(0x1500 + val);
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
if (einkPrvReadStatus() & 0x10) { // set if voltage is less than threshold ( == 1.9 + val / 10)
voltage = 1850 + mathPrvMul8x8(val, 100);
break;
}
}
einkPrvCmdWithOneByte(0x22B1);
einkPrvCmd(0x20); // do action
einkPrvDeselect();
einkPrvWaitWithTimeout(TIMER_TICKS_PER_SECOND);
if (!wasInited)
screenShutdown();
return voltage;
}

View File

@@ -0,0 +1,50 @@
#ifndef _SCREEN_H_
#define _SCREEN_H_
#include <stdbool.h>
#include <stdint.h>
//i hate globals, but for 8051 this makes life a lot easier, sorry :(
extern uint8_t __xdata mScreenVcom;
extern int8_t __xdata mCurTemperature;
#define SCREEN_WIDTH 152
#define SCREEN_HEIGHT 152
#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 27
#define SCREEN_HEIGHT_MM 27
#define SCREEN_BYTE_FILL 0x44 //white
#define SCREEN_TYPE TagScreenEink_BWR_6colors
#define SCREEN_DATA_PASSES 2
void screenShutdown(void);
void screenTest(void);
__bit screenTxStart(__bit forPartial);
void screenEndPass(void); //at end of each pass
void moveToXY(uint8_t x, uint16_t y, bool red);
#pragma callee_saves screenByteTx
void screenByteTx(uint8_t byte);
void screenByteRawTx(uint8_t byte);
void screenTxEnd(void);
void screenSleep(void);
extern uint8_t __xdata mScreenRow[]; //320 bytes used as temp by many on cc where memory is tight
#endif

View File

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

View File

@@ -0,0 +1,53 @@
#ifndef _BOARD_H_
#define _BOARD_H_
#include <stdint.h>
#include "spi.h"
#include "uart.h"
//colors for ui messages
#define UI_MSG_MAGNIFY1 1
#define UI_MSG_MAGNIFY2 1
#define UI_MSG_MAGNIFY3 1
#define UI_MSG_BACK_COLOR 4
#define UI_MSG_FORE_COLOR_1 0
#define UI_MSG_FORE_COLOR_2 5
#define UI_MSG_FORE_COLOR_3 5
#define UI_BARCODE_VERTICAL
#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)
//debug uart (enable only when needed, on some boards it inhibits eeprom access)
#define dbgUartOn()
#define dbgUartOff()
#define dbgUartByte uartTx
//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)
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (1)
//hw types
#define HW_TYPE_NORMAL HW_TYPE_29_INCH_ZBS_026
#define HW_TYPE_CYCLING HW_TYPE_29_INCH_ZBS_026_FRAME_MODE
#include "../boardCommon.h"
#endif

Some files were not shown because too many files have changed in this diff Show More