Merging Dickens-specific changes into master

This commit is contained in:
Gordon Williams 2023-09-27 14:57:47 +01:00
parent 61effcaffd
commit 138452fb65
72 changed files with 5344 additions and 196 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
import time
import serial

View File

@ -35,7 +35,7 @@ import pinutils;
# D42(btn) GND
# unfitted big flash-ish chip
# o D14 NC?
# o D14 VCC
# D15 D17
# D2 D19
# GND D18
@ -61,7 +61,7 @@ info = {
'espruino_page_link' : '',
'default_console' : "EV_BLUETOOTH",
'variables' : 5000, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile.
'io_buffer_size' : 512, # How big is the input buffer (in 4 byte words). Default on nRF52 is 256
'io_buffer_size' : 512,
'bootloader' : 1,
'binary_name' : 'espruino_%v_dickens.hex',
'build' : {
@ -69,14 +69,27 @@ info = {
'libraries' : [
'BLUETOOTH',
'GRAPHICS',
'LCD_SPI'
'LCD_SPI',
'JIT'
],
'makefile' : [
# 'DEFINES += -DNRF_LOG_ENABLED=1 -DNRF_LOG_FILTERS_ENABLED=0',
'DEFINES += -DCONFIG_NFCT_PINS_AS_GPIOS', # Allow the reset pin to work
'DEFINES += -DCONFIG_NFCT_PINS_AS_GPIOS', # Allow us to use NFC pins as GPIO
'DEFINES += -DESPR_LSE_ENABLE=1', # Ensure low speed external osc enabled
'DEFINES += -DNRF_SDH_BLE_GATT_MAX_MTU_SIZE=131', # 23+x*27 rule as per https://devzone.nordicsemi.com/f/nordic-q-a/44825/ios-mtu-size-why-only-185-bytes
'LDFLAGS += -Xlinker --defsym=LD_APP_RAM_BASE=0x2ec0', # set RAM base to match MTU
# 'DEFINES += -DESPR_REGOUT0_1_8V=1', # Leave REGOUT0 as 1.8v (not 3.3v) - seems to be what original watch firmware did
'DEFINES += -DESPR_DCDC_ENABLE=1', # Use DC/DC converter
'ESPR_BLUETOOTH_ANCS=1', # Enable ANCS (Apple notifications) support
'DEFINES += -DSPIFLASH_SLEEP_CMD', # SPI flash needs to be explicitly slept and woken up
'DEFINES += -DSPIFLASH_READ2X', # Read SPI flash at 2x speed using MISO and MOSI for IO
'DEFINES += -DESPR_USE_SPI3=1', # Use SPI3 (even though it has errata 195) as it's much faster
'DEFINES += -DESPR_BACKLIGHT_FADE=1', # Smoothly fade backlight on and off
'DEFINES += -DNRF_BL_DFU_ENTER_METHOD_BUTTON=1 -DNRF_BL_DFU_ENTER_METHOD_BUTTON_PIN=29',
'DEFINES += -DNRF_BOOTLOADER_NO_WRITE_PROTECT=1', # By default the bootloader protects flash. Avoid this (a patch for NRF_BOOTLOADER_NO_WRITE_PROTECT must be applied first)
'DEFINES += -DBUTTONPRESS_TO_REBOOT_BOOTLOADER',
'DEFINES += -DESPR_BOOTLOADER_SPIFLASH', # Allow bootloader to flash direct from SPI flash
'DEFINES += -DAPP_TIMER_OP_QUEUE_SIZE=6', # Bangle.js accelerometer poll handler needs something else in queue size
'BOOTLOADER_SETTINGS_FAMILY = NRF52840',
'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem',
@ -85,16 +98,24 @@ info = {
'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"Dickens"\'',
'DEFINES+=-DCUSTOM_GETBATTERY=jswrap_banglejs_getBattery',
'DEFINES+=-DDUMP_IGNORE_VARIABLES=\'"g\\0"\'',
'DEFINES+=-DESPR_GRAPHICS_INTERNAL=1',
'DEFINES+=-DUSE_FONT_6X8 -DGRAPHICS_PALETTED_IMAGES -DESPR_GRAPHICS_12BIT -DGRAPHICS_ANTIALIAS',
'DEFINES+=-DNO_DUMP_HARDWARE_INITIALISATION', # don't dump hardware init - not used and saves 1k of flash
'INCLUDE += -I$(ROOT)/libs/banglejs -I$(ROOT)/libs/misc',
'INCLUDE += -I$(ROOT)/libs/banglejs -I$(ROOT)/libs/dickens -I$(ROOT)/libs/misc',
'WRAPPERSOURCES += libs/banglejs/jswrap_bangle.c',
'WRAPPERSOURCES += libs/dickens/jswrap_dickens.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_architekt10.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_architekt12.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_architekt15.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_architekt35.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_grotesk14.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_grotesk16.c',
'WRAPPERSOURCES += libs/graphics/jswrap_font_grotesk20.c',
'SOURCES += libs/graphics/line_font.c',
'SOURCES += libs/misc/stepcount.c',
'JSMODULESOURCES += libs/js/banglejs/locale.min.js',
'DEFINES += -DBANGLEJS',
'DEFINES += -DESPR_NO_LOADING_SCREEN', # disable 'loading...' message when switching apps
'NRF_SDK15=1'
]
@ -115,10 +136,10 @@ chip = {
'adc' : 1,
'dac' : 0,
'saved_code' : {
# 'address' : ((246 - 10) * 4096), # Bootloader takes pages 248-255, FS takes 246-247
# 'page_size' : 4096,
# 'pages' : 10,
# 'flash_available' : 1024 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb.
# 'address' : ((246 - 10) * 4096), # Bootloader takes pages 248-255, FS takes 246-247
# 'page_size' : 4096,
# 'pages' : 10,
# 'flash_available' : 1024 - ((31 + 8 + 2 + 10)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2, code 10. Each page is 4 kb.
'address' : 0x60000000, # put this in external spiflash (see below)
'page_size' : 4096,
'pages' : 768, # 3MB of 4MB flash
@ -143,7 +164,7 @@ devices = {
'pin_mosi' : 'D5',
'pin_miso' : 'D27',
'pin_en' : 'D43',
'pin_bl' : 'D33', # TESTED!
'pin_bl' : 'D33', # Also enables the power supply for the vibration motor
'bitrate' : 32000000
},
'BAT' : {

File diff suppressed because it is too large Load Diff

View File

@ -639,6 +639,10 @@ JshI2CInfo i2cInternal;
#define HOME_BTN 2
#define DEFAULT_BTN_LOAD_TIMEOUT 4000
#define DEFAULT_LCD_POWER_TIMEOUT 20000
#define DEFAULT_TWIST_THRESHOLD 600
#define DEFAULT_TWIST_MAXY 0
#define WAKE_FROM_OFF_TIME 1000
#define MAG_MAX_RANGE 400 // maximum range of readings allowed between magmin/magmax. In the UK at ~20uT 250 is ok, and the max field strength us ~40uT
#endif
#ifdef ID205
@ -681,6 +685,15 @@ JshI2CInfo i2cInternal;
#ifndef DEFAULT_LOCK_TIMEOUT
#define DEFAULT_LOCK_TIMEOUT 30000 // in msec - default for lockTimeout
#endif
#ifndef DEFAULT_TWIST_THRESHOLD
#define DEFAULT_TWIST_THRESHOLD 800
#endif
#ifndef DEFAULT_TWIST_MAXY
#define DEFAULT_TWIST_MAXY -800
#endif
#ifndef WAKE_FROM_OFF_TIME
#define WAKE_FROM_OFF_TIME 200
#endif
#ifdef TOUCH_DEVICE
short touchX, touchY; ///< current touch event coordinates
@ -833,8 +846,10 @@ bool lcdFadeHandlerActive;
Vector3 mag, magmin, magmax;
bool magOnWhenCharging;
#define MAG_CHARGE_TIMEOUT 3000 // time after charging when magnetometer value gets automatically reset
#ifndef MAG_MAX_RANGE
#define MAG_MAX_RANGE 500 // maximum range of readings allowed between magmin/magmax. In the UK at ~20uT 250 is ok, and the max field strength us ~40uT
#endif
#endif // MAG_I2C
#ifdef MAG_DEVICE_GMC303
uint8_t magCalib[3]; // Magnetic Coefficient Registers - used to rescale the magnetometer values
#endif
@ -869,9 +884,9 @@ int accelGestureInactiveCount = 4;
/// how many samples must a gesture have before we notify about it?
int accelGestureMinLength = 10;
/// How much acceleration to register a twist of the watch strap?
int twistThreshold = 800;
int twistThreshold = DEFAULT_TWIST_THRESHOLD;
/// Maximum acceleration in Y to trigger a twist (low Y means watch is facing the right way up)
int twistMaxY = -800;
int twistMaxY = DEFAULT_TWIST_MAXY;
/// How little time (in ms) must a twist take from low->high acceleration?
int twistTimeout = 1000;
@ -1171,8 +1186,14 @@ void peripheralPollHandler() {
if (homeBtnTimer < TIMER_MAX) {
homeBtnTimer += pollInterval;
if (btnLoadTimeout && (homeBtnTimer >= btnLoadTimeout)) {
bangleTasks |= JSBT_RESET;
jshHadEvent();
#ifdef DICKENS
if (!jsfIsStorageEmpty()) { // Only reset if there's something in flash storage
#else
{
#endif
bangleTasks |= JSBT_RESET;
jshHadEvent();
}
homeBtnTimer = TIMER_MAX;
// Allow home button to break out of debugger
if (jsiStatus & JSIS_IN_DEBUGGER) {
@ -1410,7 +1431,26 @@ void peripheralPollHandler() {
if (tapType) {
// report tap
tapInfo = buf[0] | (tapType<<6);
bangleTasks |= JSBT_ACCEL_TAPPED;
bool handled = false;
// wake on tap, for front (for Dickens)
#ifdef DICKENS
if ((bangleFlags&JSBF_WAKEON_TOUCH) && (tapInfo&1)) {
if (!(bangleFlags&JSBF_LCD_ON)) {
bangleTasks |= JSBT_LCD_ON;
handled = true;
}
if (!(bangleFlags&JSBF_LCD_BL_ON)) {
bangleTasks |= JSBT_LCD_BL_ON;
handled = true;
}
if (bangleFlags&JSBF_LOCKED) {
bangleTasks |= JSBT_UNLOCK;
handled = true;
}
}
#endif
if (!handled)
bangleTasks |= JSBT_ACCEL_TAPPED;
jshHadEvent();
}
// clear the IRQ flags
@ -1437,6 +1477,15 @@ void peripheralPollHandler() {
#endif
#ifdef ACCEL_DEVICE_KX126
newy = -newy;
#endif
#ifdef LCD_ROTATION
#if LCD_ROTATION == 180
newy = -newy;
#elif LCD_ROTATION == 0
newx = -newx; //consistent directions with Bangle
#else
#error "LCD rotation is only implemented for 180 and 0 degrees"
#endif
#endif
// if the graphics instance is rotated, also rotate accelerometer values
if (graphicsInternal.data.flags & JSGRAPHICSFLAGS_INVERT_X) newx = -newx;
@ -1504,8 +1553,8 @@ void peripheralPollHandler() {
bangleTasks |= JSBT_ACCEL_DATA;
jshHadEvent();
}
// check for 'face up'
faceUp = (acc.z<-6700) && (acc.z>-9000) && abs(acc.x)<2048 && abs(acc.y)<2048;
// check for 'face up' (or tilted towards the viewer, which reduces the Y value)
faceUp = (acc.z<-5700) && (acc.z>-9000) && abs(acc.x)<2048 && abs(acc.y+4096)<2048;
if (faceUp!=wasFaceUp) {
faceUpTimer = 0;
faceUpSent = false;
@ -1980,12 +2029,12 @@ static void jswrap_banglejs_setLCDPowerController(bool isOn) {
// TODO: LCD_CONTROLLER_GC9A01 - has an enable/power pin
if (isOn) { // wake
lcdCmd_SPILCD(0x11, 0, NULL); // SLPOUT
jshDelayMicroseconds(20);
jshDelayMicroseconds(5000); // For GC9A01, we should wait 5ms after SLPOUT for power supply and clocks to stabilise before sending next command
lcdCmd_SPILCD(0x29, 0, NULL); // DISPON
} else { // sleep
lcdCmd_SPILCD(0x28, 0, NULL); // DISPOFF
jshDelayMicroseconds(20);
lcdCmd_SPILCD(0x10, 0, NULL); // SLPIN
lcdCmd_SPILCD(0x10, 0, NULL); // SLPIN - for GC9A01, it takeds 120ms to get into sleep mode after sending SPLIN
}
#endif
#ifdef LCD_EN
@ -2133,7 +2182,6 @@ void jswrap_banglejs_setLCDPower(bool isOn) {
#ifdef ESPR_BACKLIGHT_FADE
if (isOn) jswrap_banglejs_setLCDPowerController(1);
else jswrap_banglejs_setLCDPowerBacklight(0); // RB: don't turn on the backlight here if fading is enabled
jswrap_banglejs_setLCDPowerBacklight(isOn);
#else
jswrap_banglejs_setLCDPowerController(isOn);
jswrap_banglejs_setLCDPowerBacklight(isOn);
@ -2457,6 +2505,9 @@ which the touchscreen/buttons start being ignored). To set both separately, use
`Bangle.setOptions`
*/
void jswrap_banglejs_setLCDTimeout(JsVarFloat timeout) {
#ifdef DICKENS
inactivityTimer = 0; // reset the LCD timeout timer
#endif
if (!isfinite(timeout))
timeout=0;
else if (timeout<0) timeout=0;
@ -3625,7 +3676,10 @@ NO_INLINE void jswrap_banglejs_init() {
/* If first run and button is held down, enter recovery mode. During this
we will try not to access storage */
#ifndef DICKENS
#ifdef DICKENS
if (jshPinGetValue(BTN1_PININDEX) && jshPinGetValue(BTN4_PININDEX))
recoveryMode = true;
#else
if (jshPinGetValue(HOME_BTN_PININDEX))
recoveryMode = true;
#endif
@ -3653,6 +3707,14 @@ NO_INLINE void jswrap_banglejs_init() {
if (!recoveryMode) {
JsVar *settingsFN = jsvNewFromString("setting.json");
JsVar *settings = jswrap_storage_readJSON(settingsFN,true);
#ifdef DICKENS
// Remove the whitelist from settings, in case it was set when the watch
// was running the original factory firmware
if (jsvIsObject(settings)) {
jsvObjectRemoveChild(settings, "whitelist");
jswrap_storage_writeJSON(settingsFN, settings);
}
#endif
jsvUnLock(settingsFN);
JsVar *v;
v = jsvIsObject(settings) ? jsvObjectGetChildIfExists(settings,"beep") : 0;
@ -3809,6 +3871,22 @@ NO_INLINE void jswrap_banglejs_init() {
}
w = (int)(unsigned char)jsvGetCharInString(img, 0);
h = (int)(unsigned char)jsvGetCharInString(img, 1);
char addrStr[20];
#ifndef EMULATED
JsVar *addr = jswrap_ble_getAddress(); // Write MAC address in bottom right
#else
JsVar *addr = jsvNewFromString("Emulated");
#endif
jsvGetString(addr, addrStr, sizeof(addrStr));
jsvUnLock(addr);
#if (defined(DICKENS) || defined(EMSCRIPTEN_DICKENS))
int y=111;
jswrap_graphics_drawCString(&graphicsInternal,20,y-10,"---------------");
jswrap_graphics_drawCString(&graphicsInternal,20,y,"PROJECT DICKENS");
jswrap_graphics_drawCString(&graphicsInternal,20,y+10,"---------------");
jswrap_graphics_drawCString(&graphicsInternal,20,y+30,JS_VERSION);
jswrap_graphics_drawCString(&graphicsInternal,20,y+40,addrStr);
#else // not DICKENS
int y=(LCD_HEIGHT-h)/2;
jsvUnLock2(jswrap_graphics_drawImage(graphics,img,(LCD_WIDTH-w)/2,y,NULL),img);
if (drawInfo) {
@ -3826,8 +3904,13 @@ NO_INLINE void jswrap_banglejs_init() {
jswrap_graphics_drawCString(&graphicsInternal,8,y+10,addrStr);
jswrap_graphics_drawCString(&graphicsInternal,8,y+20,"Copyright 2021 G.Williams");
}
#endif // DICKENS
}
graphicsInternalFlip();
#ifdef DICKENS
if (showSplashScreen)
#endif
graphicsInternalFlip();
graphicsStructResetState(&graphicsInternal);
// no need to unlock graphics as we stored it in 'graphicsVar'
#endif
@ -3880,17 +3963,20 @@ NO_INLINE void jswrap_banglejs_init() {
jshDelayMicroseconds(2000);
jswrap_banglejs_accelWr(KX126_CNTL3,KX126_CNTL3_OTP_12P5|KX126_CNTL3_OTDT_400|KX126_CNTL3_OWUF_0P781); // CNTL3 12.5Hz tilt, 400Hz tap, 0.781Hz motion detection
jswrap_banglejs_accelWr(KX126_ODCNTL,KX126_ODCNTL_OSA_12P5); // ODCNTL - 12.5Hz output data rate (ODR), with low-pass filter set to ODR/9
jswrap_banglejs_accelWr(KX126_INC1,0); // INC1 - interrupt output pin INT1 disabled
jswrap_banglejs_accelWr(KX126_INC2,0); // INC2 - wake-up & back-to-sleep ignores all 3 axes
jswrap_banglejs_accelWr(KX126_INC3,0); // INC3 - tap detection ignores all 3 axes
jswrap_banglejs_accelWr(KX126_INC4,0); // INC4 - no routing of interrupt reporting to pin INT1
jswrap_banglejs_accelWr(KX126_INC5,0); // INC5 - interrupt output pin INT2 disabled
jswrap_banglejs_accelWr(KX126_INC6,0); // INC6 - no routing of interrupt reporting to pin INT2
jswrap_banglejs_accelWr(KX126_INC7,0); // INC7 - no step counter interrupts reported on INT1 or INT2
jswrap_banglejs_accelWr(KX126_INC1,0); // INC1 - interrupt output pin INT1 disabled
jswrap_banglejs_accelWr(KX126_INC2,0); // INC2 - wake-up & back-to-sleep ignores all 3 axes
jswrap_banglejs_accelWr(KX126_INC3,0x3F); // INC3 - enable tap detection in all 6 directions
jswrap_banglejs_accelWr(KX126_INC4,0); // INC4 - no routing of interrupt reporting to pin INT1
jswrap_banglejs_accelWr(KX126_INC5,0); // INC5 - interrupt output pin INT2 disabled
jswrap_banglejs_accelWr(KX126_INC6,0); // INC6 - no routing of interrupt reporting to pin INT2
jswrap_banglejs_accelWr(KX126_INC7,0); // INC7 - no step counter interrupts reported on INT1 or INT2
jswrap_banglejs_accelWr(KX126_TDTRC,3); // TDTRC - enable interrupts on single and double taps
jswrap_banglejs_accelWr(KX126_TDTC, 0x78); // TDTC - tap detect double tap (0x78 default)
jswrap_banglejs_accelWr(KX126_TTH, 0xCB); // TTH - tap detect threshold high (0xCB default)
jswrap_banglejs_accelWr(KX126_TTL, 0x22); // TTL - tap detect threshold low (0x1A default)
jswrap_banglejs_accelWr(KX126_BUF_CLEAR,0); // clear the buffer
jswrap_banglejs_accelWr(KX126_CNTL1,KX126_CNTL1_DRDYE|KX126_CNTL1_GSEL_4G); // CNTL1 - standby mode, low power, enable "data ready" interrupt, 4g, disable tap, tilt & pedometer (for now)
jswrap_banglejs_accelWr(KX126_CNTL1,KX126_CNTL1_DRDYE|KX126_CNTL1_GSEL_4G|KX126_CNTL1_PC1); // CNTL1 - same as above but change from standby to operating mode
jswrap_banglejs_accelWr(KX126_CNTL1,KX126_CNTL1_DRDYE|KX126_CNTL1_GSEL_4G|KX126_CNTL1_TDTE); // CNTL1 - standby mode, low power, enable "data ready" interrupt, 4g, enable tap, disable tilt & pedometer (for now)
jswrap_banglejs_accelWr(KX126_CNTL1,KX126_CNTL1_DRDYE|KX126_CNTL1_GSEL_4G|KX126_CNTL1_TDTE|KX126_CNTL1_PC1); // CNTL1 - same as above but change from standby to operating mode
#endif
#ifdef PRESSURE_DEVICE
@ -4140,6 +4226,14 @@ bool jswrap_banglejs_idle() {
JsVar *o = jsvNewObject();
if (o) {
const char *string="";
#ifdef DICKENS
if (tapInfo&1) string="front";
if (tapInfo&2) string="back";
if (tapInfo&4) string="top";
if (tapInfo&8) string="bottom";
if (tapInfo&16) string="left";
if (tapInfo&32) string="right";
#else
#ifdef BANGLEJS_Q3
if (tapInfo&2) string="front";
if (tapInfo&1) string="back";
@ -4154,6 +4248,7 @@ bool jswrap_banglejs_idle() {
if (tapInfo&8) string="bottom";
if (tapInfo&16) string="left";
if (tapInfo&32) string="right";
#endif
#endif
int n = (tapInfo&0x80)?2:1;
jsvObjectSetChildAndUnLock(o, "dir", jsvNewFromString(string));
@ -5229,8 +5324,10 @@ static void jswrap_banglejs_periph_off() {
nrf_gpio_cfg_sense_set(pinInfo[BTN4_PININDEX].pin, NRF_GPIO_PIN_NOSENSE);
#endif
#ifndef DICKENS // RB: the call to jswrap_banglejs_kill via jswInit can cause increased power draw
jsiKill();
jsvKill();
#endif
jshKill();
/* The low power pin watch code (nrf_drv_gpiote_in_init) somehow causes
@ -5239,12 +5336,25 @@ static void jswrap_banglejs_periph_off() {
to re-enable everything. */
jshPinWatch(BTN1_PININDEX, true, JSPW_NONE);
nrf_gpio_cfg_sense_set(pinInfo[BTN1_PININDEX].pin, NRF_GPIO_PIN_SENSE_LOW);
#ifdef DICKENS
jshPinWatch(BAT_PIN_CHARGING, true, JSPW_NONE); // watch for when power applied
nrf_gpio_cfg_sense_set(pinInfo[BAT_PIN_CHARGING].pin, NRF_GPIO_PIN_SENSE_LOW); // falling -> on charge
#endif
#else
jsExceptionHere(JSET_ERROR, ".off not implemented on emulator");
#endif
}
// True if a button/charge input/etc should wake the Bangle from being off
static bool _jswrap_banglejs_shouldWake() {
return jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE
#ifdef DICKENS
|| jshPinGetValue(BAT_PIN_CHARGING)==0/*charging*/
#endif
;
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
@ -5257,7 +5367,7 @@ Turn Bangle.js off. It can only be woken by pressing BTN1.
void jswrap_banglejs_off() {
#ifndef EMULATED
// If BTN1 is pressed wait until it is released
while (jshPinGetValue(BTN1_PININDEX));
while (_jswrap_banglejs_shouldWake());
// turn peripherals off
jswrap_banglejs_periph_off();
// system off
@ -5291,17 +5401,17 @@ void jswrap_banglejs_softOff() {
// keep sleeping until a button is pressed
jshKickWatchDog();
do {
// sleep until BTN1 pressed
while (!jshPinGetValue(BTN1_PININDEX)) {
jshKickWatchDog();
jshSleep(jshGetTimeFromMilliseconds(4*1000));
}
// wait for button to be pressed for at least 200ms
int timeout = 200;
while (jshPinGetValue(BTN1_PININDEX) && timeout--)
// sleep until BTN1 pressed
while (!_jswrap_banglejs_shouldWake()) {
jshKickWatchDog();
jshSleep(jshGetTimeFromMilliseconds(4*1000));
}
// wait for button to be pressed for at least WAKE_FROM_OFF_TIME (200ms usually)
int timeout = WAKE_FROM_OFF_TIME;
while (_jswrap_banglejs_shouldWake() && timeout--)
nrf_delay_ms(1);
// if button not pressed, keep sleeping
} while (!jshPinGetValue(BTN1_PININDEX));
} while (!_jswrap_banglejs_shouldWake());
// restart
jshReboot();
@ -5995,8 +6105,11 @@ other issues when switching apps. Please see http://www.espruino.com/Bangle.js+F
"type" : "staticmethod",
"class" : "Bangle",
"name" : "factoryReset",
"params" : [
["noReboot","bool","Do not reboot the watch when done (default false, so will reboot)"]
],
"generate" : "jswrap_banglejs_factoryReset",
"#if" : "defined(BANGLEJS_Q3) || defined(EMULATED)"
"#if" : "defined(BANGLEJS_Q3) || defined(EMULATED) || defined(DICKENS)"
}
Erase all storage and reload it with the default contents.
@ -6005,9 +6118,9 @@ This is only available on Bangle.js 2.0. On Bangle.js 1.0 you need to use
`Install Default Apps` under the `More...` tab of http://banglejs.com/apps
*/
extern void ble_app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name);
void jswrap_banglejs_factoryReset() {
void jswrap_banglejs_factoryReset(bool noReboot) {
jsfResetStorage();
jsiStatus |= JSIS_TODO_FLASH_LOAD;
if (!noReboot) jsiStatus |= JSIS_TODO_FLASH_LOAD;
}
/*JSON{
@ -6086,4 +6199,4 @@ void jsbangle_push_event(JsBangleEvent type, uint16_t value) {
evt.data.chars[1] = (char)((value>>8) & 0xFF);
evt.data.chars[2] = (char)(value & 0xFF);
jshPushEvent(&evt);
}
}

View File

@ -73,7 +73,7 @@ JsVar *jswrap_banglejs_buzz(int time, JsVarFloat amt);
void jswrap_banglejs_off();
void jswrap_banglejs_softOff();
JsVar *jswrap_banglejs_getLogo();
void jswrap_banglejs_factoryReset();
void jswrap_banglejs_factoryReset(bool noReboot);
JsVar *jswrap_banglejs_appRect();

View File

@ -175,6 +175,8 @@ typedef enum {
BLEP_TASK_CHARACTERISTIC_NOTIFY, //< Central: Started requesting notifications
BLEP_CENTRAL_NOTIFICATION, //< A characteristic we were watching has changed
BLEP_CENTRAL_DISCONNECTED, //< Central: Disconnected (reason as data low byte, index in m_central_conn_handles as high byte )
#endif
#if PEER_MANAGER_ENABLED
BLEP_BONDING_STATUS, //< Bonding negotiation status (data is one of BLEBondingStatus)
#endif
BLEP_WRITE, //< One of our characteristics written by someone else

8
libs/dickens/README.md Normal file
View File

@ -0,0 +1,8 @@
Dickens Smartwatch
------------------
This directory contains extra files used by the Dickens smartwatch manufactured by The Wand Company.
The watch's board description file is in `boards/DICKENS.py`
**However** because this watch uses some fonts that aren't licensed for redistribution, it is not possible to fully build the firmware as-is. This is fully allowed by the MPLv2 license.

View File

@ -0,0 +1,200 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* This file is designed to be parsed during the build process
*
* Contains JavaScript function just for the Dickens smartwatch
* ----------------------------------------------------------------------------
*/
#include "jswrap_dickens.h"
#include "jswrap_graphics.h"
#include "jswrap_math.h"
#include "jsinteractive.h"
#include "lcd_spilcd.h"
/*JSON{
"type": "class",
"class" : "Dickens",
"ifdef" : "BANGLEJS"
}
*/
/*JSON{
"type" : "staticmethod", "class" : "Bangle", "name" : "drawWidgets", "patch":true,
"generate_js" : "libs/js/dickens/Bangle_drawWidgets_DICKENS.min.js",
"#if" : "defined(BANGLEJS) && defined(DICKENS)"
}
*/
/*JSON{
"type" : "staticmethod", "class" : "E", "name" : "showMenu", "patch":true,
"generate_js" : "libs/js/dickens/E_showMenu_DICKENS.min.js",
"#if" : "defined(BANGLEJS) && defined(DICKENS)"
}
*/
/*JSON{
"type" : "staticmethod", "class" : "E", "name" : "showPrompt", "patch":true,
"generate_js" : "libs/js/dickens/E_showPrompt_DICKENS.min.js",
"#if" : "defined(BANGLEJS) && defined(DICKENS)"
}
*/
/*JSON{
"type" : "staticmethod", "class" : "E", "name" : "showMessage", "patch":true,
"generate_js" : "libs/js/dickens/E_showMessage_DICKENS.min.js",
"#if" : "defined(BANGLEJS) && defined(DICKENS)"
}
*/
/*JSON{
"type" : "staticmethod", "class" : "Bangle", "name" : "setUI", "patch":true,
"generate_js" : "libs/js/dickens/Bangle_setUI_DICKENS.min.js",
"#if" : "defined(BANGLEJS) && defined(DICKENS)"
}
*/
// add a radial point to an array
void addRadialPoint(JsVar *arr, double r, double a) {
jsvArrayPushAndUnLock(arr, jsvNewFromFloat(119.0 + (r*jswrap_math_sin(a))));
jsvArrayPushAndUnLock(arr, jsvNewFromFloat(119.0 - (r*jswrap_math_cos(a))));
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "fillArc",
"ifdef" : "DICKENS",
"generate" : "jswrap_graphics_fillArc",
"params" : [
["a1","float","Angle 1 (radians)"],
["a2","float","Angle 2 (radians)"],
["r","float","Radius"]
],
"return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"],
"return_object" : "Graphics"
}
Draw a filled arc between two angles
*/
JsVar *jswrap_graphics_fillArc(JsVar *parent, double a1, double a2, double r) {
if (a2<a1) return jsvLockAgain(parent);
if ((a2-a1)>6.28) a2=a1+6.28; // No need for more than a full circle - if the polygon has too many points, a memory leak will happen
double a;
const int res = 8;
JsVar *poly = jsvNewEmptyArray();
for (double i=a1*res;i<a2*res;i++) {
a = i/res;
addRadialPoint(poly, r, a);
}
addRadialPoint(poly, r, a2);
jswrap_graphics_fillPoly_X(parent, poly, true/*antialias*/);
jsvUnLock(poly);
return parent; // jswrap_graphics_fillPoly_X clready locked it
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "fillSeg",
"ifdef" : "DICKENS",
"generate" : "jswrap_graphics_fillSeg",
"params" : [
["a","float","Angle (radians)"],
["ar","float","Angle either side (radians)"],
["r1","float","Radius"],
["r2","float","Radius"]
],
"return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"],
"return_object" : "Graphics"
}
Draw rectangle between angles a-ar and a+ar, and radius r1/r2
*/
JsVar *jswrap_graphics_fillSeg(JsVar *parent, double a, double ar, double r1, double r2) {
double a1 = a-ar;
double a2 = a+ar;
JsVar *poly = jsvNewEmptyArray();
addRadialPoint(poly, r1, a1);
addRadialPoint(poly, r1, a2);
addRadialPoint(poly, r2, a2);
addRadialPoint(poly, r2, a1);
jswrap_graphics_fillPoly_X(parent, poly, true/*antialias*/);
jsvUnLock(poly);
return parent; // jswrap_graphics_fillPoly_X clready locked it
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "drawSeg",
"ifdef" : "DICKENS",
"generate" : "jswrap_graphics_drawSeg",
"params" : [
["a","float","Angle (radians)"],
["ar","float","Angle either side (radians)"],
["r","float","Radius"]
],
"return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"],
"return_object" : "Graphics"
}
Draw A line between angles a-ar and a+ar at radius r
*/
JsVar *jswrap_graphics_drawSeg(JsVar *parent, double a, double ar, double r) {
double a1 = a-ar;
double a2 = a+ar;
return jswrap_graphics_drawLineAA(parent,
119.0 + (r*jswrap_math_sin(a1)),
119.0 - (r*jswrap_math_cos(a1)),
119.0 + (r*jswrap_math_sin(a2)),
119.0 - (r*jswrap_math_cos(a2))
);
}
#ifdef DICKENS
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "setLCDRotation",
"generate" : "jswrap_banglejs_setLCDRotation",
"params" : [
["d","int","The number of degrees to the LCD display (0, 90, 180 or 270)"]
],
"ifdef" : "BANGLEJS"
}
Sets the rotation of the LCD display (relative to its nominal orientation)
*/
void jswrap_banglejs_setLCDRotation(int d) {
#ifdef LCD_ROTATION
// If the LCD is already rotated in the board definition file, add this
d += LCD_ROTATION;
if (d>=360) d-=360;
#endif
uint8_t regValue;
// Register values are OK for GC9A01 on Dickens, but will need to be different on other hardware.
switch (d) {
case 0:
regValue = 0x88;
break;
case 90:
regValue = 0x78;
break;
case 180:
regValue = 0x48;
break;
case 270:
regValue = 0xB8;
break;
default:
jsExceptionHere(JSET_ERROR, "setLCDRotation expects a rotation value of 0, 90, 180 or 270");
}
lcdCmd_SPILCD(0x36, 1, (const uint8_t *)&regValue);
}
#endif

View File

@ -0,0 +1,21 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Contains JavaScript function just for the Dickens smartwatch
* ----------------------------------------------------------------------------
*/
#include "jsvar.h"
JsVar *jswrap_graphics_fillArc(JsVar *parent, double a1, double a2, double r);
JsVar *jswrap_graphics_fillSeg(JsVar *parent, double a, double ar, double r1, double r2);
JsVar *jswrap_graphics_drawSeg(JsVar *parent, double a, double ar, double r);
void jswrap_banglejs_setLCDRotation(int d);

View File

@ -127,7 +127,7 @@ const unsigned short LCD_FONT_3X5[] IN_FLASH_MEMORY = { // from 33 up to 127
PACK_5_TO_16( X_X , X_X , X__ , _X_ , X_X ),
PACK_5_TO_16( X_X , XX_ , _X_ , _X_ , X_X ),
PACK_5_TO_16( X_X , X_X , __X , _X_ , X_X ),
PACK_5_TO_16( _XX , X_X , XX_ , _X_ , _X_ ),
PACK_5_TO_16( _XX , X_X , XX_ , _X_ , XXX ),
PACK_5_TO_16( ___ , ___ , ___ , X_X , ___ ), // vwxyz
PACK_5_TO_16( ___ , X_X , X_X , X_X , ___ ),

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
/* This is a super hacky bit of code that takes a specially formatted SVG file
and creates an Espruino vector font from it.

View File

@ -520,7 +520,7 @@ fn : "Light20px.png",
bpp : 2,
height : 20, // actual used height of font map
firstChar : 32,
maxChars : 128-32
maxChars : 256-32
});
f.fullHeight = false;
f.glyphPadX = 0;*/
@ -558,7 +558,7 @@ f.debugPixelsUsed();
console.log(f.outputJS());
// Write a binary PBF file
//require("fs").writeFileSync("font.pbf", Buffer.from(f.outputPBF()))
require("fs").writeFileSync("font.pbf", Buffer.from(f.outputPBF()))
// Write a PBF file as a jswrap_ C file that can be included in the build
// by adding 'WRAPPERSOURCES += libs/graphics/jswrap_font_light20.c' to the BOARD.py file

View File

@ -262,6 +262,7 @@ size_t graphicsGetMemoryRequired(const JsGraphics *gfx) {
// If graphics is flipped or rotated then the coordinates need modifying
void graphicsToDeviceCoordinates(const JsGraphics *gfx, int *x, int *y) {
#ifndef DICKENS // For Dickens, we can use Bangle.lcdWr(0x36, xxx) to set the screen rotation
if (gfx->data.flags & JSGRAPHICSFLAGS_SWAP_XY) {
int t = *x;
*x = *y;
@ -269,6 +270,7 @@ void graphicsToDeviceCoordinates(const JsGraphics *gfx, int *x, int *y) {
}
if (gfx->data.flags & JSGRAPHICSFLAGS_INVERT_X) *x = (int)(gfx->data.width - (*x+1));
if (gfx->data.flags & JSGRAPHICSFLAGS_INVERT_Y) *y = (int)(gfx->data.height - (*y+1));
#endif
}
// If graphics is flipped or rotated then the coordinates need modifying. This is to go back - eg for touchscreens
@ -284,6 +286,7 @@ void deviceToGraphicsCoordinates(const JsGraphics *gfx, int *x, int *y) {
// If graphics is flipped or rotated then the coordinates need modifying
void graphicsToDeviceCoordinates16x(const JsGraphics *gfx, int *x, int *y) {
#ifndef DICKENS // For Dickens, we can use Bangle.lcdWr(0x36, xxx) to set the screen rotation
if (gfx->data.flags & JSGRAPHICSFLAGS_SWAP_XY) {
int t = *x;
*x = *y;
@ -291,6 +294,7 @@ void graphicsToDeviceCoordinates16x(const JsGraphics *gfx, int *x, int *y) {
}
if (gfx->data.flags & JSGRAPHICSFLAGS_INVERT_X) *x = (int)((gfx->data.width-1)*16 - *x);
if (gfx->data.flags & JSGRAPHICSFLAGS_INVERT_Y) *y = (int)((gfx->data.height-1)*16 - *y);
#endif
}
unsigned short graphicsGetWidth(const JsGraphics *gfx) {

View File

@ -32,6 +32,11 @@
#include "jswrap_functions.h" // for asURL
#include "jswrap_object.h" // for getFonts
#ifdef DICKENS
#include "jsflash.h" // for saveScreenshot
#include "lcd_spilcd.h"
#define ESPR_LINE_FONTS
#endif
#include "bitmap_font_4x6.h"
#include "bitmap_font_6x8.h"
@ -39,6 +44,10 @@
#ifdef ESPR_PBF_FONTS
#include "pbf_font.h"
#endif
#ifdef ESPR_LINE_FONTS
#include "line_font.h"
#endif
#ifdef GRAPHICS_PALETTED_IMAGES
#if defined(ESPR_GRAPHICS_12BIT)
@ -1142,6 +1151,32 @@ Draw a filled circle in the Foreground Color
return jswrap_graphics_fillEllipse(parent, x-rad, y-rad, x+rad, y+rad);
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "fillAnnulus",
"#if" : "defined(DICKENS)",
"generate" : "jswrap_graphics_fillAnnulus",
"params" : [
["x","int32","The X axis"],
["y","int32","The Y axis"],
["r1","int32","The annulus inner radius"],
["r2","int32","The annulus outer radius"]
],
"return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"],
"return_object" : "Graphics"
}
Draw a filled annulus in the Foreground Color
*/
// JsVar *jswrap_graphics_fillAnnulus(JsVar *parent, int x, int y, int r1, int r2, unsigned short quadrants) { // Too many arguments!
JsVar *jswrap_graphics_fillAnnulus(JsVar *parent, int x, int y, int r1, int r2) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0;
unsigned short quadrants = 0x0F; // Just do all quadrants for now
graphicsFillAnnulus(&gfx, x,y,r1,r2,quadrants);
graphicsSetVar(&gfx); // gfx data changed because modified area
return jsvLockAgain(parent);
}
/*JSON{
"type" : "method",
"class" : "Graphics",
@ -2696,6 +2731,75 @@ JsVar *jswrap_graphics_getVectorFontPolys(JsGraphics *gfx, JsVar *str, JsVar *op
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "drawLineString",
"#if" : "defined(DICKENS)",
"generate" : "jswrap_graphics_drawLineString",
"params" : [
["str","JsVar","The string"],
["x","int32","The X position of the start of the text string"],
["y","int32","The Y position of the middle of the text string"],
["options","JsVar","Options for drawing this font (see below)"]
],
"return" : ["JsVar","The instance of Graphics this was called on, to allow call chaining"],
"return_object" : "Graphics"
}
Draw a string of text as a fixed-width line font
`options` contains:
* `size`: font size in pixels (char width is half font size) - default 16
* `rotate`: Initial rotation in radians - default 0
* `twist`: Subsequent rotation per character in radians - default 0
*/
#ifdef ESPR_LINE_FONTS
JsVar *jswrap_graphics_drawLineString(JsVar *parent, JsVar *var, int x, int y, JsVar *options) {
JsGraphics gfx; if (!graphicsGetFromVar(&gfx, parent)) return 0;
int fontSize = 16;
double rotate = 0, twist = 0;
jsvConfigObject configs[] = {
{"size", JSV_INTEGER, &fontSize},
{"rotate", JSV_FLOAT, &rotate},
{"twist", JSV_FLOAT, &twist}
};
if (!jsvReadConfigObject(options, configs, sizeof(configs) / sizeof(jsvConfigObject))) {
jsExceptionHere(JSET_ERROR, "Invalid options");
return 0;
}
fontSize *= 16;
x = x*16 - 8;
y = y*16 - 8;
int startx = x;
JsVar *str = jsvAsString(var);
JsvStringIterator it;
jsvStringIteratorNew(&it, str, 0);
while (jsvStringIteratorHasChar(&it)) {
char ch = jsvStringIteratorGetCharAndNext(&it);
if (ch=='\n') {
x = startx;
y += fontSize;
continue;
}
int xdx = (int)(0.5 + fontSize*cos(rotate));
int xdy = (int)(0.5 + fontSize*sin(rotate));
graphicsDrawLineChar(&gfx, x, y, xdx, xdy, ch);
x += xdx * 1 / 2;
y += xdy * 1 / 2;
rotate += twist;
if (jspIsInterrupted()) break;
}
jsvStringIteratorFree(&it);
jsvUnLock(str);
graphicsSetVar(&gfx); // gfx data changed because modified area
return jsvLockAgain(parent);
}
#endif
/*JSON{
"type" : "method",
"class" : "Graphics",
@ -4002,6 +4106,38 @@ void jswrap_graphics_dump(JsVar *parent) {
jsvUnLock(jswrap_graphics_asBMP_X(parent, true/*printBase64*/));
}
/*JSON{
"type" : "method",
"class" : "Graphics",
"name" : "saveScreenshot",
"#if" : "defined(DICKENS)",
"generate" : "jswrap_graphics_saveScreenshot",
"params" : [
["filename","JsVar","If supplied, a file to save, otherwise 'screenshot.img'"]
]
}
*/
void jswrap_graphics_saveScreenshot(JsVar *parent, JsVar *fileNameVar) {
#ifdef DICKENS
JsfFileName fileName = jsfNameFromString("screenshot.img");
if (fileNameVar) fileName = jsfNameFromVar(fileNameVar);
JsVar *v = jsvNewFromString("\xF0\xF0\x10");
jsfWriteFile(fileName, v, JSFF_NONE, 0, sizeof(lcdBuffer)+3);
jsvUnLock(v);
const int chunkSize = 16384;
for (int i=0;i<sizeof(lcdBuffer);i+=chunkSize) {
int s = chunkSize;
if (s+i > sizeof(lcdBuffer))
s=sizeof(lcdBuffer)-i;
JsVar *gfxBufferString = jsvNewNativeString(&lcdBuffer[i], s);
jsfWriteFile(fileName, gfxBufferString, JSFF_NONE, 3+i, 0);
jsvUnLock(gfxBufferString);
}
#endif
}
/*JSON{
"type" : "method",
"class" : "Graphics",

View File

@ -45,6 +45,7 @@ JsVar *jswrap_graphics_drawRect(JsVar *parent, JsVar *opt, int y1, int x2, int y
JsVar *jswrap_graphics_drawCircle(JsVar *parent, int x, int y, int rad);
JsVar *jswrap_graphics_drawCircleAA(JsVar *parent, int x, int y, int r);
JsVar *jswrap_graphics_fillCircle(JsVar *parent, int x, int y, int rad);
JsVar *jswrap_graphics_fillAnnulus(JsVar *parent, int x, int y, int r1, int r2);
JsVar *jswrap_graphics_drawEllipse(JsVar *parent, int x, int y, int x2, int y2);
JsVar *jswrap_graphics_fillEllipse(JsVar *parent, int x, int y, int x2, int y2);
int jswrap_graphics_getPixel(JsVar *parent, int x, int y);
@ -66,6 +67,7 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth);
JsVar *jswrap_graphics_drawString(JsVar *parent, JsVar *str, int x, int y, bool solidBackground);
void jswrap_graphics_drawCString(JsGraphics *gfx, int x, int y, char *str); /// Convenience function for using drawString from C code
JsVarInt jswrap_graphics_stringWidth(JsVar *parent, JsVar *var);
JsVar *jswrap_graphics_drawLineString(JsVar *parent, JsVar *var, int x, int y, JsVar *options);
JsVar* jswrap_graphics_stringMetrics(JsVar *parent, JsVar *var);
JsVar *jswrap_graphics_getVectorFontPolys(JsGraphics *gfx, JsVar *var, JsVar *options);
JsVar *jswrap_graphics_drawLine(JsVar *parent, int x1, int y1, int x2, int y2);
@ -85,6 +87,7 @@ JsVar *jswrap_graphics_blit(JsVar *parent, JsVar *options);
JsVar *jswrap_graphics_asBMP(JsVar *parent);
JsVar *jswrap_graphics_asURL(JsVar *parent);
void jswrap_graphics_dump(JsVar *parent);
void jswrap_graphics_saveScreenshot(JsVar *parent, JsVar *fileNameVar);
JsVar *jswrap_graphics_quadraticBezier(JsVar *parent, JsVar * arr, JsVar *options);
JsVar *jswrap_graphics_transformVertices(JsVar *parent, JsVar *verts, JsVar *transformation);
JsVar *jswrap_graphics_floodFill(JsVar *parent, int x, int y, JsVar *col);

View File

@ -26,7 +26,6 @@
// ======================================================================
#define LCD_STRIDE ((LCD_WIDTH*LCD_BPP+7)>>3)
unsigned char lcdBuffer[LCD_STRIDE*LCD_HEIGHT];
#if LCD_BPP==4
unsigned short lcdPalette[16];
@ -377,9 +376,9 @@ void lcdInit_SPILCD(JsGraphics *gfx) {
jshPinOutput(LCD_SPI_MOSI,1);
#ifdef LCD_SPI_RST
jshPinOutput(LCD_SPI_RST,0);
jshDelayMicroseconds(1000);
jshDelayMicroseconds(50000);
jshPinOutput(LCD_SPI_RST, 1);
jshDelayMicroseconds(2000);
jshDelayMicroseconds(50000);
#endif
JshSPIInfo inf;
jshSPIInitInfo(&inf);

View File

@ -13,6 +13,9 @@
*/
#include "graphics.h"
#define LCD_STRIDE ((LCD_WIDTH*LCD_BPP+7)>>3)
unsigned char lcdBuffer[LCD_STRIDE*LCD_HEIGHT];
void lcdInit_SPILCD(JsGraphics *gfx);
void lcdSetCallbacks_SPILCD(JsGraphics *gfx);
@ -23,4 +26,4 @@ void lcdSetPalette_SPILCD(const char *pal);
#if LCD_BPP==12 || LCD_BPP==16
// Enable overlay mode (to overlay a graphics instance on top of the LCD contents)
void lcdSetOverlay_SPILCD(JsVar *imgVar, int x, int y);
#endif
#endif

View File

@ -107,47 +107,69 @@ static const unsigned char SPILCD_INIT_CODE[] = {
0xfe,0,0,
0xef,0,0,
0xeb,0,1, 0x14,
0x84,0,1, 0x40,
0x84,0,1, 0x60, // 0x40->0x60 0xb5 en 20200924 james
0x85,0,1, 0xFF,
0x86,0,1, 0xFF,
0x87,0,1, 0xFF,
0x8e,0,1, 0xFF,
0x8f,0,1, 0xFF,
0x88,0,1, 10,
0x89,0,1, 0x21,
0x89,0,1, 0x23, // 0x21->0x23 spi 2data reg en
0x8a,0,1, 0,
0x8b,0,1, 0x80,
0x8c,0,1, 1,
0x8d,0,1, 1,
0xb6,0,1, 0x20,
0x36,0,1, 0x88, // Memory Access Control (0x48 flips upside-down)
0x8d,0,1, 3, // 1->3 99 en
0xb5,0,4, 0x08, 0x09, 0x14, 0x08,
0xb6,0,2, 0, 0, // Positive sweep 0x20->0 GS SS 0x20
#ifdef LCD_ROTATION
#if (LCD_ROTATION == 90)
0x36,0,1, 0x78, // Memory Access Control (rotated 90 degrees)
#elif (LCD_ROTATION == 180)
0x36,0,1, 0x48, // Memory Access Control (rotated 180 degrees)
#elif (LCD_ROTATION == 270)
0x36,0,1, 0xB8, // Memory Access Control (rotated 270 degrees)
#else
0x36,0,1, 0x88, // Memory Access Control (no rotation)
#endif
#else
0x36,0,1, 0x88, // Memory Access Control (no rotation)
#endif
0x3a,0,1, 5, // could be 16/12 bit?
0x90,0,4, 8, 8, 8, 8,
0xba,0,1, 1, // TE width
0xbd,0,1, 6,
0xbc,0,1, 0,
0xff,0,3, 0x60, 1, 4,
0xc3,0,1, 0x13,
0xc4,0,1, 0x13,
0xc9,0,1, 0x22,
0xc3,0,1, 0x13, // Power control 2: 0x13->0x1d->0x13 (again)
0xc4,0,1, 0x13, // Power control 3: 0x13->0x1d->0x13 (again)
0xc9,0,1, 0x25, // Power control 4: 0x22->0x25
0xbe,0,1, 0x11,
0xe1,0,2, 0x10, 0xe,
0xdf,0,3, 0x21, 0xc, 2,
0xf0,0,6, 0x45, 9, 8, 8, 0x26, 0x2a,
0xf1,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f,
0xf2,0,6, 0x45, 9, 8, 8, 0x26, 0x2a,
0xf3,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f,
0xf0,0,6, 0x45, 9, 8, 8, 0x26, 0x2a, // Gamma 1
0xf1,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f, // Gamma 2
0xf2,0,6, 0x45, 9, 8, 8, 0x26, 0x2a, // Gamma 3
0xf3,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f, // Gamma 4
0xed,0,2, 0x1b, 0xb,
0xae,0,1, 0x74,
0xcd,0,1, 99,
0x70,0,9, 7, 9, 4, 0xe, 0xf, 9, 7, 8, 3,
0xae,0,1, 0x77, // 0x74->0x77
0xcd,0,1, 0x63,
0x70,0,9, 7, 7, 4, 0xe, 0xf, 9, 7, 8, 3, // 7,9,4... -> 7,7,4...
0xe8,0,1, 0x34,
0x62,0,12, 0x18, 0xd, 0x71, 0xed, 0x70, 0x70, 0x18, 0xf, 0x71, 0xef, 0x70, 0x70,
99,0,12, 0x18, 0x11, 0x71, 0xf1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xf3, 0x70, 0x70,
100,0,7, 0x28, 0x29, 0xf1, 1, 0xf1, 0, 7,
0x60,0,8, 0x38, 0x0b, 0x6d, 0x6d, 0x39, 0xf0, 0x6d, 0x6d,
0x61,0,8, 0x38, 0xf4, 0x6d, 0x6d, 0x38, 0xf7, 0x6d, 0x6d,
0x62,0,12, 0x38, 0xd, 0x71, 0xed, 0x70, 0x70, 0x38, 0xf, 0x71, 0xef, 0x70, 0x70,
0x63,0,12, 0x38, 0x11, 0x71, 0xf1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xf3, 0x70, 0x70,
0x64,0,7, 0x28, 0x29, 0xf1, 1, 0xf1, 0, 7,
0x66,0,10, 0x3c, 0, 0xcd, 0x67, 0x45, 0x45, 0x10, 0, 0, 0,
0x67,0,10, 0, 0x3c, 0, 0, 0, 1, 0x54, 0x10, 0x32, 0x98,
0x74,0,7, 0x10, 0x85, 0x80, 0, 0, 0x4e, 0,
0x74,0,7, 0x10, 0x68, 0x80, 0, 0, 0x4e, 0, // 0x85->0x68
0x98,0,2, 0x3e, 7,
0x35,0,0,
0x21,5,0,
0x11,5,0,
0x29,5,0,
0x2c,0,0,
0x99,0,2, 0x3e, 7, // bvee 2x
0x35,0,1, 0, // Tearing effect (TE) line ON, with V-blanking only
0x21,5,0, // Display inversion ON
0x11,5,0, // Sleep out
0x29,5,0, // Display ON
0x2c,0,0, // Memory write
// End
0, 0, 255/*DATA_LEN = 255 => END*/
};

View File

@ -5,6 +5,7 @@
Bangle.removeAllListeners();
E.removeAllListeners();
NRF.removeAllListeners();
Bangle.setLCDBrightness(1);
E.showMenu({"":{title:"Recovery"},
"Attempt Compact": () => {
E.showMessage("Compacting...\nMay take\n5 min.");
@ -13,7 +14,7 @@
require("Storage").compact();
E.reboot();
},
"Rewrite bootloader": () => {
"Rewrite Bootloader": () => {
setTimeout(load,1000);
eval(require("Storage").read("bootupdate.js"));
},
@ -21,6 +22,7 @@
E.showPrompt("Are you sure?\nThis will remove all data.",{title:"Factory Reset"}).then(ok => {
if (!ok) return Bangle.showRecoveryMenu();
E.showMessage("Resetting");
Bangle.setLCDTimeout(0);
if(!NRF.getSecurityStatus().connected)
Terminal.setConsole();
Bangle.factoryReset();
@ -35,11 +37,15 @@
"Turn Off": () => {
Bangle.off();
},
"Exit": () => {
E.showMessage("Loading...");
if(!NRF.getSecurityStatus().connected)
Terminal.setConsole();
load();
"Exit": () => {
if (require("Storage").list().length>0) {
E.showMessage("Loading...");
if(!NRF.getSecurityStatus().connected)
Terminal.setConsole();
load();
} else {
E.reboot();
}
},
});
})
})

View File

@ -0,0 +1,14 @@
(function() {
if (!global.WIDGETS) return;
var pad = 4, w = -pad, wd;
for (wd of WIDGETS) w += wd.width+pad;
var x = 119-w/2;
g.reset();
for (wd of WIDGETS) {
wd.x = x;
wd.y = 26;
x += wd.width+pad;
wd.draw(wd);
}
g.reset();
})

View File

@ -0,0 +1 @@
(function(){if(global.WIDGETS){var b=-4,a;for(a of WIDGETS)b+=a.width+4;b=119-b/2;g.reset();for(a of WIDGETS)a.x=b,a.y=26,b+=a.width+4,a.draw(a);g.reset()}})

View File

@ -0,0 +1,9 @@
(function(mode, cb) {
if (Bangle.btnWatches) {
Bangle.btnWatches.forEach(w => {
try { clearWatch(w); } catch (e) {}
});
delete Bangle.btnWatches;
}
// this is just a replacement for E.clearWatches for the moment
})

View File

@ -0,0 +1 @@
(function(b,c){Bangle.btnWatches&&(Bangle.btnWatches.forEach(a=>{try{clearWatch(a)}catch(d){}}),delete Bangle.btnWatches)})

View File

@ -0,0 +1,126 @@
(function(items, onCancel) {
if (!global.Dickens) Dickens={}; // for when called with no boot code
Bangle.setUI(); // clear Bangle.btnWatches
g.clear(1);
// clear screen if no menu supplied
if (!items) return;
var cHighlightBg = "#304060";
var cBorderBg = "#305080";
g.setColor(cBorderBg);
g.fillArc(-Math.PI*0.285,Math.PI*0.285,96);
g.fillArc(Math.PI*(1-0.285),Math.PI*1.285,96);
g.fillRect(41,62,195,62);
g.fillRect(41,175,195,175);
var menuItems = Object.keys(items);
var options = items[""];
if (options) menuItems.splice(menuItems.indexOf(""),1);
if (!(options instanceof Object)) options = {};
options.fontHeight=16;
if (options.selected === undefined)
options.selected = 0;
var x = 38;
var x2 = 200;
var y = 65+6;
var y2 = 174;
var cBg = 0; // background col
var cFg = -1; // foreground col
var cHighlightFg = -1;
var loc = require("locale");
var l = {
draw : function() {
g.reset().setColor(cFg).setFontGrotesk16();
if (options.title) {
g.setFontAlign(0,-1,0);
g.setBgColor(cBorderBg).drawString(options.title,119,42);
}
g.setBgColor(0);
var rows = 0|Math.min((y2-y) / options.fontHeight,menuItems.length);
var idx = E.clip(options.selected-(rows>>1),0,menuItems.length-rows);
var iy = y;
var less = idx>0;
g.setColor(idx>0?cHighlightBg:cBorderBg).fillPoly([111,36,127,36,119,28]);
while (rows--) {
var name = menuItems[idx];
var item = items[name];
var hl = (idx==options.selected && !l.selectEdit);
g.setBgColor(hl ? cHighlightBg : cBg);
g.setColor(hl ? cHighlightFg : cFg);
g.clearRect(x,iy-1,x2,iy+options.fontHeight-1);
g.setFontAlign(-1,-1);
g.drawString(loc.translate(name),x+2,iy);
if ("object" == typeof item) {
var xo = x2;
var v = item.value;
if (item.format) v=item.format(v);
if (("number" == typeof v) && item.precision) v=v.toFixed(item.precision);
v = loc.translate(""+v);
if (l.selectEdit && idx==options.selected) {
xo -= 24 + 1;
g.setColor(cHighlightBg);
g.fillRect(xo-(g.stringWidth(v)+8),iy-1,x2,iy+options.fontHeight-1);
g.setColor(cHighlightFg);
g.drawImage("\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",xo,iy+(options.fontHeight-10)/2,{scale:2});
}
g.setFontAlign(1,-1);
g.drawString(v,xo-2,iy);
}
iy += options.fontHeight+1;
idx++;
}
g.setFontAlign(-1,-1);
g.setColor(idx<menuItems.length?cHighlightBg:cBorderBg).fillPoly([110,191,128,191,119,200]);
g.flip();
},
select : function(dir) {
var item = items[menuItems[options.selected]];
if ("function" == typeof item) {
Bangle.setUI(); // clear Bangle.btnWatches
item(l);
}
else if ("object" == typeof item) {
// if a number, go into 'edit mode'
if ("number" == typeof item.value)
l.selectEdit = l.selectEdit?undefined:item;
else { // else just toggle bools
if ("boolean" == typeof item.value) item.value=!item.value;
if (item.onchange) item.onchange(item.value);
}
l.draw();
}
},
move : function(dir) {
if (l.selectEdit) {
var item = l.selectEdit;
item.value -= (dir||1)*(item.step||1);
if (Math.abs(item.value)<1e-10) item.value=0;
if (item.min!==undefined && item.value<item.min) {
if (item.wrap) item.value = item.max;
else item.value = item.min;
}
if (item.max!==undefined && item.value>item.max) {
if (item.wrap) item.value = item.min;
else item.value = item.max;
}
if (item.onchange) item.onchange(item.value);
} else {
options.selected = (dir+options.selected)%menuItems.length;
if (options.selected<0) options.selected += menuItems.length;
}
l.draw();
}
};
Dickens.buttonIcons=['select',null,'down','up'];
if (onCancel) Dickens.buttonIcons[1]='back';
l.draw();
Dickens.loadSurround&&Dickens.loadSurround();
Bangle.btnWatches = [
setWatch(function() { l.move(-1); }, BTN4, {repeat:1}),
setWatch(function() { l.move(1); }, BTN3, {repeat:1}),
setWatch(function() { l.select(); }, BTN1, {repeat:1})
];
if (onCancel) Bangle.btnWatches.push(setWatch(onCancel, BTN2, {repeat:1}))
return l;
})

View File

@ -0,0 +1,5 @@
(function(m,n){global.Dickens||(Dickens={});Bangle.setUI();g.clear(1);if(m){g.setColor("#305080");g.fillArc(.285*-Math.PI,.285*Math.PI,96);g.fillArc(Math.PI*(1-.285),1.285*Math.PI,96);g.fillRect(41,62,195,62);g.fillRect(41,175,195,175);var e=Object.keys(m),b=m[""];b&&e.splice(e.indexOf(""),1);b instanceof Object||(b={});b.fontHeight=16;void 0===b.selected&&(b.selected=0);var p=require("locale"),d={draw:function(){g.reset().setColor(-1).setFontGrotesk16();b.title&&(g.setFontAlign(0,
-1,0),g.setBgColor("#305080").drawString(b.title,119,42));g.setBgColor(0);var c=0|Math.min(103/b.fontHeight,e.length),a=E.clip(b.selected-(c>>1),0,e.length-c),h=71;for(g.setColor(0<a?"#304060":"#305080").fillPoly([111,36,127,36,119,28]);c--;){var k=e[a],l=m[k];g.setBgColor(a!=b.selected||d.selectEdit?0:"#304060");g.setColor(-1);g.clearRect(38,h-1,200,h+b.fontHeight-1);g.setFontAlign(-1,-1);g.drawString(p.translate(k),40,h);if("object"==typeof l){k=200;var f=l.value;l.format&&(f=l.format(f));"number"==
typeof f&&l.precision&&(f=f.toFixed(l.precision));f=p.translate(""+f);d.selectEdit&&a==b.selected&&(k-=25,g.setColor("#304060"),g.fillRect(k-(g.stringWidth(f)+8),h-1,200,h+b.fontHeight-1),g.setColor(-1),g.drawImage("\f\x05\x81\x00 \x07\x00\xf9\xf0\x0e\x00@",k,h+(b.fontHeight-10)/2,{scale:2}));g.setFontAlign(1,-1);g.drawString(f,k-2,h)}h+=b.fontHeight+1;a++}g.setFontAlign(-1,-1);g.setColor(a<e.length?"#304060":"#305080").fillPoly([110,191,128,191,119,200]);g.flip()},select:function(c){c=
m[e[b.selected]];if("function"==typeof c)Bangle.setUI(),c(d);else if("object"==typeof c){if("number"==typeof c.value)d.selectEdit=d.selectEdit?void 0:c;else if("boolean"==typeof c.value&&(c.value=!c.value),c.onchange)c.onchange(c.value);d.draw()}},move:function(c){if(d.selectEdit){var a=d.selectEdit;a.value-=(c||1)*(a.step||1);1E-10>Math.abs(a.value)&&(a.value=0);void 0!==a.min&&a.value<a.min&&(a.value=a.wrap?a.max:a.min);void 0!==a.max&&a.value>a.max&&(a.value=a.wrap?a.min:a.max);if(a.onchange)a.onchange(a.value)}else b.selected=
(c+b.selected)%e.length,0>b.selected&&(b.selected+=e.length);d.draw()}};Dickens.buttonIcons=["select",null,"down","up"];n&&(Dickens.buttonIcons[1]="back");d.draw();Dickens.loadSurround&&Dickens.loadSurround();Bangle.btnWatches=[setWatch(function(){d.move(-1)},BTN4,{repeat:1}),setWatch(function(){d.move(1)},BTN3,{repeat:1}),setWatch(function(){d.select()},BTN1,{repeat:1})];n&&Bangle.btnWatches.push(setWatch(n,BTN2,{repeat:1}));return d}})

View File

@ -0,0 +1,24 @@
(function(msg,title,icon) {
// TODO: Widgets?
g.clear(1).setColor("#305080");
g.fillArc(-Math.PI*0.285,Math.PI*0.285,96);
g.fillArc(Math.PI*(1-0.285),Math.PI*1.285,96);
g.fillRect(41,62,195,62);
g.fillRect(41,175,195,175);
g.setColor(-1);
var loc = require("locale");
var W = g.getWidth();
var H = g.getHeight();
if (title) {
title = loc.translate(title);
g.setFontGrotesk16().setFontAlign(0,-1,0).setBgColor("#305080").drawString(E.decodeUTF8(title),119,42).setBgColor(0);
}
if (icon) {
g.setBgColor(cBorderBg).drawImage(icon.img, icon.x, icon.y);
}
g.setFontGrotesk20().setFontAlign(0,0,0);
var lines = msg.split("\n");
var offset = 11 + (H - lines.length*22)/2;
lines.forEach((line,y)=>g.drawString(E.decodeUTF8(loc.translate(line)),W/2,offset + y*22));
g.flip();
})

View File

@ -0,0 +1,2 @@
(function(a,b,c){g.clear(1).setColor("#305080");g.fillArc(.285*-Math.PI,.285*Math.PI,96);g.fillArc(Math.PI*(1-.285),1.285*Math.PI,96);g.fillRect(41,62,195,62);g.fillRect(41,175,195,175);g.setColor(-1);var d=require("locale"),e=g.getWidth(),f=g.getHeight();b&&(b=d.translate(b),g.setFontGrotesk16().setFontAlign(0,-1,0).setBgColor("#305080").drawString(E.decodeUTF8(b),119,42).setBgColor(0));c&&g.setBgColor(cBorderBg).drawImage(c.img,c.x,c.y);g.setFontGrotesk20().setFontAlign(0,0,0);a=a.split("\n");
var h=11+(f-22*a.length)/2;a.forEach((k,l)=>g.drawString(E.decodeUTF8(d.translate(k)),e/2,h+22*l));g.flip()})

View File

@ -0,0 +1,98 @@
(function(msg,options) {
if (!global.Dickens) Dickens={}; // for when called with no boot code
var cHighlightBg = "#304060";
var cBorderBg = "#305080";
if (!options) options={};
if (!options.buttons)
options.buttons = {"Yes":true,"No":false};
var loc = require("locale");
var btns = Object.keys(options.buttons);
if (!options.selected)
options.selected = 0;
if (options.vstack===undefined)
options.vstack = 1;
function draw() {
g.reset();
g.setColor(cBorderBg);
g.fillArc(-Math.PI*0.285,Math.PI*0.285,96);
g.fillArc(Math.PI*(1-0.285),Math.PI*1.285,96);
g.fillRect(41,62,195,62);
g.fillRect(41,175,195,175);
g.setColor(-1);
var W = g.getWidth();
var H = g.getHeight();
var title = options.title;
if (title) {
title = loc.translate(title);
g.setFontGrotesk16().setFontAlign(0,-1,0).setBgColor(cBorderBg).drawString(title,119,42).setBgColor(0);
}
var i =options.icon;
if (i) {
g.setBgColor(cBorderBg).drawImage(i.img, i.x, i.y);
}
g.setFontGrotesk16().setFontAlign(0,0,0);
var lines = msg.split("\n");
var offset = 105 - lines.length*16/2;
lines.forEach((line,y)=>
g.drawString(loc.translate(line),W/2,offset + y*16));
var buttonWidths = 0;
var buttonPadding = 16;
var x, y, w, bw, poly;
if (options.vstack) {
x = 120;
y = 172-btns.length*18;
btns.forEach((btn,idx)=>{
btn = loc.translate(btn);
bw = 50;
poly = [x-bw-4,y-9,x+bw+4,y-9,x+bw+4,y+9,x-bw-4,y+9];
g.setColor(idx==options.selected ? cHighlightBg : 0).fillPoly(poly).setColor(-1).drawPoly(poly,1).setFontGrotesk14().drawString(btn,x,y+1);
y += 18;
});
} else
{
btns.forEach(btn=>buttonWidths += buttonPadding+g.stringWidth(loc.translate(btn)));
x = (W-buttonWidths)/2;
y = 150;
btns.forEach((btn,idx)=>{
btn = loc.translate(btn);
w = g.stringWidth(btn);
x += (buttonPadding+w)/2;
bw = 2+w/2;
poly = [x-bw-4,y-10,x+bw+4,y-10,x+bw+4,y+10,x-bw-4,y+10];
g.setColor(idx==options.selected ? cHighlightBg : 0).fillPoly(poly).setColor(-1).drawPoly(poly,1).drawString(btn,x,y+1);
x += (buttonPadding+w)/2;
});
}
g.setColor(-1).flip(); // turn screen on
}
Bangle.setUI(); // clear Bangle.btnWatches
// TODO: Widgets?
g.clear(1);
Dickens.buttonIcons=['select',null,'down','up'];
if (!msg) {
return Promise.resolve();
}
draw();
Dickens.loadSurround&&Dickens.loadSurround();
return new Promise(resolve=>{
Bangle.btnWatches = [
setWatch(function() {
if (options.selected>0) {
options.selected--;
draw();
}
}, BTN4, {repeat:1}),
setWatch(function() {
if (options.selected<btns.length-1) {
options.selected++;
draw();
}
}, BTN3, {repeat:1}),
setWatch(function() {
E.showPrompt();
Bangle.setUI(); // clear Bangle.btnWatches
resolve(options.buttons[btns[options.selected]]);
}, BTN1, {repeat:1})
];
});
})

View File

@ -0,0 +1,4 @@
(function(r,a){function q(){g.reset();g.setColor("#305080");g.fillArc(.285*-Math.PI,.285*Math.PI,96);g.fillArc(Math.PI*(1-.285),1.285*Math.PI,96);g.fillRect(41,62,195,62);g.fillRect(41,175,195,175);g.setColor(-1);var n=g.getWidth();g.getHeight();var e=a.title;e&&(e=l.translate(e),g.setFontGrotesk16().setFontAlign(0,-1,0).setBgColor("#305080").drawString(e,119,42).setBgColor(0));(e=a.icon)&&g.setBgColor("#305080").drawImage(e.img,e.x,e.y);g.setFontGrotesk16().setFontAlign(0,
0,0);e=r.split("\n");var u=105-16*e.length/2;e.forEach((c,m)=>g.drawString(l.translate(c),n/2,u+16*m));var t=0,p,f,h;if(a.vstack){var b=120;var d=172-18*k.length;k.forEach((c,m)=>{c=l.translate(c);f=50;h=[b-f-4,d-9,b+f+4,d-9,b+f+4,d+9,b-f-4,d+9];g.setColor(m==a.selected?"#304060":0).fillPoly(h).setColor(-1).drawPoly(h,1).setFontGrotesk14().drawString(c,b,d+1);d+=18})}else k.forEach(c=>t+=16+g.stringWidth(l.translate(c))),b=(n-t)/2,d=150,k.forEach((c,m)=>{c=l.translate(c);p=g.stringWidth(c);b+=(16+
p)/2;f=2+p/2;h=[b-f-4,d-10,b+f+4,d-10,b+f+4,d+10,b-f-4,d+10];g.setColor(m==a.selected?"#304060":0).fillPoly(h).setColor(-1).drawPoly(h,1).drawString(c,b,d+1);b+=(16+p)/2});g.setColor(-1).flip()}global.Dickens||(Dickens={});a||(a={});a.buttons||(a.buttons={Yes:!0,No:!1});var l=require("locale"),k=Object.keys(a.buttons);a.selected||(a.selected=0);void 0===a.vstack&&(a.vstack=1);Bangle.setUI();g.clear(1);Dickens.buttonIcons=["select",null,"down","up"];if(!r)return Promise.resolve();q();Dickens.loadSurround&&
Dickens.loadSurround();return new Promise(n=>{Bangle.btnWatches=[setWatch(function(){0<a.selected&&(a.selected--,q())},BTN4,{repeat:1}),setWatch(function(){a.selected<k.length-1&&(a.selected++,q())},BTN3,{repeat:1}),setWatch(function(){E.showPrompt();Bangle.setUI();n(a.buttons[k[a.selected]])},BTN1,{repeat:1})]})})

View File

@ -18,10 +18,10 @@ wget https://www.espruino.com/modules/MPU9250.min.js -O MPU9250.min.js
wget https://www.espruino.com/modules/LPS22HB.min.js -O LPS22HB.min.js
wget https://www.espruino.com/modules/HTS221.min.js -O HTS221.min.js
wget https://www.espruino.com/modules/CCS811.min.js -O CCS811.min.js
wget https://www.espruino.com/modules/BH1745.min.js -O BH1745.min.js
wget https://www.espruino.com/modules/BH1745.min.js -O BH1745.min.js
wget https://www.espruino.com/modules/SHT3C.min.js -O SHT3C.min.js
#wget https://www.espruino.com/modules/PCA9685.min.js -O PCA9685.min.js
#wget https://www.espruino.com/modules/Smartibot.min.js -O Smartibot.min.js
#wget https://www.espruino.com/modules/PCA9685.min.js -O PCA9685.min.js
#wget https://www.espruino.com/modules/Smartibot.min.js -O Smartibot.min.js
# Other libs
wget https://www.espruino.com/modules/EspruinoWiFi.min.js -O espruino_wifi/Wifi.min.js
@ -54,4 +54,9 @@ node ../../../EspruinoDocs/bin/minify.js banglejs/Bangle_setUI_F18.js banglejs/B
node ../../../EspruinoDocs/bin/minify.js banglejs/Bangle_setUI_Q3.js banglejs/Bangle_setUI_Q3.min.js
node ../../../EspruinoDocs/bin/minify.js banglejs/locale.js banglejs/locale.min.js
node ../../../EspruinoDocs/bin/minify.js dickens/Bangle_setUI_DICKENS.js dickens/Bangle_setUI_DICKENS.min.js
node ../../../EspruinoDocs/bin/minify.js dickens/Bangle_drawWidgets_DICKENS.js dickens/Bangle_drawWidgets_DICKENS.min.js
node ../../../EspruinoDocs/bin/minify.js dickens/E_showMenu_DICKENS.js dickens/E_showMenu_DICKENS.min.js
node ../../../EspruinoDocs/bin/minify.js dickens/E_showPrompt_DICKENS.js dickens/E_showPrompt_DICKENS.min.js
node ../../../EspruinoDocs/bin/minify.js dickens/E_showMessage_DICKENS.js dickens/E_showMessage_DICKENS.min.js

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python2.7
#!/usr/bin/env python2.7
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
/* This builds a js file containing the addresses of a chip's PERIPHERALS and
the values of the bits in each register. Can be used for quickly building
ways to access the underlying hardware from JS.

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
/* This'll take JavaScript source files and convert them
to jswrap files to be included in the interpreter.

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3
#!/usr/bin/env python3
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
// sudo npm install -g marked highlight.js

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
/**
* Add two spaces at the beginning of every line.

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# Checks to see if an Elf file will fit in memory before the
# area reserved for Storage (saved code). This is mainly used
# for nRF5x parts (which don't generate binaries and which

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
if (process.argv.length == 3 && process.argv[2] == "BANGLEJS") {
var EMULATOR = "banglejs1";

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
/** This scans a source code listing file for Thumb assembly
code instructions that are repeated. It scores them according
to `no_of_instructions * times_repeated` and outputs the top

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1,4 +1,4 @@
#!/usr/bin/nodejs
#!/usr/bin/env node
/* Take a hex file and turn it into Espruino commands that write
to external flash. Special flash bootloaders (targets/nrf52_dfu/flash.c)
can then take this and do the firmware update.
@ -18,7 +18,7 @@ var inputFile = process.argv[2];
var hex = require("fs").readFileSync(inputFile).toString().split("\n");
function parseLines(dataCallback) {
var addrHi = 0;
var addrHi = 0;
hex.forEach(function(hexline) {
if (DEBUG) console.log(hexline);
var bytes = hexline.substr(1,2);

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# Copyright (c) 2008,2010,2011,2013,2014,2015 Alexander Belchenko
# All rights reserved.

View File

@ -1,4 +1,4 @@
#!/usr/bin/nodejs
#!/usr/bin/env node
/* This is a big hack - we take a hex file and turn it into
Espruino commands that write the memory directly via JS. Can
be used to do bootloader updates/etc.

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# (c) Alexander Belchenko, 2007, 2009
# [2013/08] NOTE: This file is keeping for historical reasons.

View File

@ -1,4 +1,4 @@
#!/usr/bin/node
#!/usr/bin/env node
/* This was designed to be run once over Espruino
* ... hence it has probably already been run.
*

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#

View File

@ -1370,10 +1370,14 @@ JsVar *jsfGetBootCodeFromFlash(bool isReset) {
}
bool jsfLoadBootCodeFromFlash(bool isReset) {
// Load code in .bootFirst at first boot UNLESS BTN1 IS HELD DOWN
// Load code in .bootFirst at first boot UNLESS BTN1 IS HELD DOWN (BTN3 for Dickens)
#ifndef SAVE_ON_FLASH
#if (defined(BANGLEJS) && !defined(DICKENS))
#if defined(BANGLEJS)
#if defined(DICKENS)
if (!(jshPinGetValue(BTN3_PININDEX)==BTN3_ONSTATE))
#else
if (!(jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE))
#endif
#endif
if (jsiStatus & JSIS_FIRST_BOOT) {
JsVar *code = jsfReadFile(jsfNameFromString(".bootPowerOn"),0,0);
@ -1381,11 +1385,16 @@ bool jsfLoadBootCodeFromFlash(bool isReset) {
jsvUnLock2(jspEvaluateVar(code,0,0), code);
}
#endif
// Load code in .boot0/1/2/3 UNLESS BTN1 IS HELD DOWN FOR BANGLE.JS ON FIRST BOOT
// Load code in .boot0/1/2/3 UNLESS BTN1 IS HELD DOWN FOR BANGLE.JS ON FIRST BOOT (BTN3 for Dickens)
// On an average Bangle.js 2 this takes 0.25 ms (so not worth optimising)
#if (defined(BANGLEJS) && !defined(DICKENS))
#if defined(BANGLEJS)
#if defined(DICKENS)
if (!(jshPinGetValue(BTN3_PININDEX)==BTN3_ONSTATE &&
(jsiStatus & JSIS_FIRST_BOOT)))
#else // not DICKENS
if (!(jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE &&
(jsiStatus & JSIS_FIRST_BOOT)))
#endif
#endif
{
char filename[7] = ".bootX";

View File

@ -860,6 +860,12 @@ void jsiSemiInit(bool autoLoad, JsfFileName *loadedFilename) {
// set up terminal to avoid word wrap
"\e[?7l"
#endif
#if (defined(DICKENS) || defined(EMSCRIPTEN_DICKENS))
"\n"
"------------------------\n"
"PROJECT DICKENS "JS_VERSION"\n"
"© 2023 G.Williams & TWC\n"
#else
// rectangles @ http://www.network-science.de/ascii/
"\n"
" ____ _ \n"
@ -867,7 +873,7 @@ void jsiSemiInit(bool autoLoad, JsfFileName *loadedFilename) {
"| __|_ -| . | _| | | | | . |\n"
"|____|___| _|_| |___|_|_|_|___|\n"
" |_| espruino.com\n"
" "JS_VERSION" (c) 2021 G.Williams\n"
" "JS_VERSION" (c) 2023 G.Williams\n"
// Point out about donations - but don't bug people
// who bought boards that helped Espruino
#if !defined(PICO) && !defined(ESPRUINOBOARD) && !defined(ESPRUINOWIFI) && !defined(PUCKJS) && !defined(PIXLJS) && !defined(BANGLEJS) && !defined(EMSCRIPTEN)
@ -875,6 +881,7 @@ void jsiSemiInit(bool autoLoad, JsfFileName *loadedFilename) {
"Espruino is Open Source. Our work is supported\n"
"only by sales of official boards and donations:\n"
"http://espruino.com/Donate\n"
#endif
#endif
);
#ifdef ESP8266

View File

@ -805,6 +805,7 @@ void vcbprintf(
user_callback(buf,user_data);
break;
}
case 'i': // Added to support some NRF_LOG_INFO calls in, for example, nrf_ble_ancs_c.c
case 'd': itostr(va_arg(argp, int), buf, 10); user_callback(buf,user_data); break;
case 'x': itostr_extra(va_arg(argp, int), buf, false, 16); user_callback(buf,user_data); break;
case 'L': {

View File

@ -423,15 +423,24 @@ void jswrap_storage_debug() {
"ifndef" : "SAVE_ON_FLASH",
"class" : "Storage",
"name" : "getFree",
"params" : [
["checkInternalFlash","bool","Check the internal flash (rather than external SPI flash). Default false, so will check external storage"]
],
"generate" : "jswrap_storage_getFree",
"return" : ["int","The amount of free bytes"]
}
Return the amount of free bytes available in Storage. Due to fragmentation there
may be more bytes available, but this represents the maximum size of file that
can be written.
**NOTE:** `checkInternalFlash` is only useful on DICKENS devices - other devices don't use two different flash banks
*/
int jswrap_storage_getFree() {
return (int)jsfGetStorageStats(0,true).free;
int jswrap_storage_getFree(bool checkInternalFlash) {
uint32_t addr = 0;
#ifdef FLASH_SAVED_CODE2_START
addr = checkInternalFlash ? FLASH_SAVED_CODE_START : FLASH_SAVED_CODE2_START;
#endif
return (int)jsfGetStorageStats(addr,true).free;
}
/*JSON{
@ -439,6 +448,9 @@ int jswrap_storage_getFree() {
"ifndef" : "SAVE_ON_FLASH",
"class" : "Storage",
"name" : "getStats",
"params" : [
["checkInternalFlash","bool","Check the internal flash (rather than external SPI flash). Default false, so will check external storage"]
],
"generate" : "jswrap_storage_getStats",
"return" : ["JsVar","An object containing info about the current Storage system"]
}
@ -454,11 +466,18 @@ Returns:
trashCount // How many trash files do we have? (can be cleared with .compact)
}
```
**NOTE:** `checkInternalFlash` is only useful on DICKENS devices - other devices don't use two different flash banks
*/
JsVar *jswrap_storage_getStats() {
JsVar *jswrap_storage_getStats(bool checkInternalFlash) {
JsVar *o = jsvNewObject();
if (!o) return NULL;
JsfStorageStats stats = jsfGetStorageStats(0, true);
uint32_t addr = 0;
#ifdef FLASH_SAVED_CODE2_START
addr = checkInternalFlash ? FLASH_SAVED_CODE_START : FLASH_SAVED_CODE2_START;
#endif
JsfStorageStats stats = jsfGetStorageStats(addr, true);
jsvObjectSetChildAndUnLock(o, "totalBytes", jsvNewFromInteger((JsVarInt)stats.total));
jsvObjectSetChildAndUnLock(o, "freeBytes", jsvNewFromInteger((JsVarInt)stats.free));
jsvObjectSetChildAndUnLock(o, "fileBytes", jsvNewFromInteger((JsVarInt)stats.fileBytes));

View File

@ -29,8 +29,8 @@ void jswrap_storage_compact(bool showMessage);
JsVar *jswrap_storage_list(JsVar *regex, JsVar *filter);
JsVarInt jswrap_storage_hash(JsVar *regex);
void jswrap_storage_debug();
int jswrap_storage_getFree();
JsVar *jswrap_storage_getStats();
int jswrap_storage_getFree(bool checkInternalFlash);
JsVar *jswrap_storage_getStats(bool checkInternalFlash);
void jswrap_storage_optimise();
JsVar *jswrap_storage_open(JsVar *name, JsVar *mode);

View File

@ -614,6 +614,89 @@ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) {
return res;
}
/*JSON{
"type" : "method",
"class" : "String",
"name" : "removeAccents",
"ifndef" : "SAVE_ON_FLASH",
"generate_full" : "jswrap_string_removeAccents(parent)",
"return" : ["JsVar","This string with the accents/diacritics (such as é, ü) removed from characters in the ISO 8859-1 set"]
}*/
JsVar *jswrap_string_removeAccents(JsVar *parent) {
bool isLowerCase;
JsVar *res = jsvNewFromEmptyString();
if (!res) return 0; // out of memory
JsVar *parentStr = jsvAsString(parent);
JsvStringIterator itsrc, itdst;
jsvStringIteratorNew(&itsrc, parentStr, 0);
jsvStringIteratorNew(&itdst, res, 0);
while (jsvStringIteratorHasChar(&itsrc)) {
char ch = jsvStringIteratorGetCharAndNext(&itsrc);
if (ch >= 0xE0) {
isLowerCase = true;
ch -= 32;
} else {
isLowerCase = false;
}
if (ch >= 0xC0) {
switch (ch) {
case 0xC0 ... 0xC5: // À Á Â Ã Ä Å
ch = 'A';
break;
case 0xC6: // convert Æ to AE
jsvStringIteratorAppend(&itdst, isLowerCase ? 'a' : 'A');
ch = 'E';
break;
case 0xC7: // Ç
ch = 'C';
break;
case 0xC8 ... 0xCB: // È É Ê Ë
ch = 'E';
break;
case 0xCC ... 0xCF: // Ì Í Î Ï
ch = 'I';
break;
case 0xD0: // Ð
ch = 'D';
break;
case 0xD1: // Ñ
ch = 'N';
break;
case 0xD2 ... 0xD6: // Ò Ó Ô Õ Ö
case 0xD8: // Ø
ch = 'O';
break;
case 0xD9 ... 0xDC: // Ù Ú Û Ü
ch = 'U';
break;
case 0xDD: // Ý
ch = 'Y';
break;
case 0xDE: // Þ
ch = 'P';
break;
case 0xDF: // ß to SS or ÿ to y (if lowercase)
if (isLowerCase) {
ch = 'Y';
} else {
jsvStringIteratorAppend(&itdst, 'S');
ch = 'S';
}
break;
}
}
jsvStringIteratorAppend(&itdst, isLowerCase ? ch+32 : ch);
}
jsvStringIteratorFree(&itsrc);
jsvStringIteratorFree(&itdst);
jsvUnLock(parentStr);
return res;
}
/*JSON{
"type" : "method",
"class" : "String",

View File

@ -29,6 +29,7 @@ JsVar *jswrap_string_substr(JsVar *parent, JsVarInt pStart, JsVar *vLen);
JsVar *jswrap_string_slice(JsVar *parent, JsVarInt pStart, JsVar *vEnd);
JsVar *jswrap_string_split(JsVar *parent, JsVar *split);
JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper);
JsVar *jswrap_string_removeAccents(JsVar *parent);
JsVar *jswrap_string_trim(JsVar *parent);
JsVar *jswrap_string_concat(JsVar *parent, JsVar *args);
bool jswrap_string_startsWith(JsVar *parent, JsVar *search, int position);

View File

@ -448,7 +448,7 @@ ret_code_t ble_ams_c_remote_command_write(ble_ams_c_t const * p_ams_c,
}
ret_code_t ble_ams_c_entity_update_write(ble_ams_c_t const * p_ams_c,
ble_ams_c_evt_id_values_t entity_id,
ble_ams_c_entity_id_values_t entity_id,
uint8_t attribute_number,
uint8_t * attribute_list)
{
@ -490,7 +490,7 @@ ret_code_t ble_ams_c_entity_update_write(ble_ams_c_t const * p_ams_c,
}
ret_code_t ble_ams_c_entity_attribute_write(ble_ams_c_t const * p_ams_c,
ble_ams_c_evt_id_values_t entity_id,
ble_ams_c_entity_id_values_t entity_id,
uint8_t attribute_id)
{
VERIFY_PARAM_NOT_NULL(p_ams_c);

View File

@ -164,7 +164,7 @@ typedef enum
BLE_AMS_ENTITY_ID_PLAYER, /**< The iOS notification was added. */
BLE_AMS_ENTITY_ID_QUEUE, /**< The iOS notification was modified. */
BLE_AMS_ENTITY_ID_TRACK /**< The iOS notification was removed. */
} ble_ams_c_evt_id_values_t;
} ble_ams_c_entity_id_values_t;
/**@brief Flags for iOS media (Entity Update). */
typedef struct
@ -420,7 +420,7 @@ ret_code_t ble_ams_c_remote_command_write(ble_ams_c_t const * p_ams_c,
* \param[in] attribute_list List of the desired attributes
*/
ret_code_t ble_ams_c_entity_update_write(ble_ams_c_t const * p_ams_c,
ble_ams_c_evt_id_values_t entity_id,
ble_ams_c_entity_id_values_t entity_id,
uint8_t attribute_number,
uint8_t * attribute_list);
@ -434,7 +434,7 @@ ret_code_t ble_ams_c_entity_update_write(ble_ams_c_t const * p_ams_c,
* \param[in] attribute_id ID of the desired Attribute
*/
ret_code_t ble_ams_c_entity_attribute_write(ble_ams_c_t const * p_ams_c,
ble_ams_c_evt_id_values_t entity_id,
ble_ams_c_entity_id_values_t entity_id,
uint8_t attribute_id);
/**

View File

@ -528,16 +528,21 @@ int jsble_exec_pending(IOEvent *event) {
#ifdef LINK_SECURITY
case BLEP_TASK_PASSKEY_DISPLAY: { // data = connection handle
uint16_t conn_handle = data;
#if CENTRAL_LINK_COUNT>0
/* TODO: yes/no passkey
uint8_t match_request : 1; If 1 requires the application to report the match using @ref sd_ble_gap_auth_key_reply
with either @ref BLE_GAP_AUTH_KEY_TYPE_NONE if there is no match or
@ref BLE_GAP_AUTH_KEY_TYPE_PASSKEY if there is a match. */
int centralIdx = jsble_get_central_connection_idx(conn_handle);
#endif
if (bufferLen==BLE_GAP_PASSKEY_LEN) {
buffer[BLE_GAP_PASSKEY_LEN] = 0;
JsVar *passkey = jsvNewFromString((char*)buffer);
#if CENTRAL_LINK_COUNT>0
if (centralIdx<0) { // it's on the peripheral connection
#endif
bleQueueEventAndUnLock(JS_EVENT_PREFIX"passkey", passkey);
#if CENTRAL_LINK_COUNT>0
} else { // it's on a central connection
JsVar *gattServer = bleGetActiveBluetoothGattServer(centralIdx);
if (gattServer) {
@ -549,6 +554,7 @@ uint8_t match_request : 1; If 1 requires the application to report
jsvUnLock2(bluetoothDevice, gattServer);
}
}
#endif
jsvUnLock(passkey);
}
break;
@ -669,6 +675,8 @@ uint8_t match_request : 1; If 1 requires the application to report
default:
jsWarn("jsble_exec_pending: Unknown enum type %d",(int)blep);
}
if (jspIsInterrupted())
jsWarn("jsble_exec_pending: Interrupted processing event %d",(int)blep);
return eventsHandled;
}
@ -1834,8 +1842,10 @@ static void pm_evt_handler(pm_evt_t const * p_evt) {
case PM_EVT_STORAGE_FULL:
{
jsWarn("PM: PM_EVT_STORAGE_FULL - running garbage collection");
// Run garbage collection on the flash.
err_code = fds_gc();
jsWarn("Garbage collection result: %d", err_code);
if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
{
// Retry.
@ -3379,9 +3389,11 @@ void jsble_set_tx_power(int8_t pwr) {
#if NRF_SD_BLE_API_VERSION > 5
if (m_peripheral_conn_handle != BLE_CONN_HANDLE_INVALID)
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_peripheral_conn_handle, pwr);
#if CENTRAL_LINK_COUNT>0
for (int i=0;i<CENTRAL_LINK_COUNT;i++)
if (m_central_conn_handles[i] != BLE_CONN_HANDLE_INVALID)
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_central_conn_handles[i], pwr);
#endif
if (m_adv_handle != BLE_GAP_ADV_SET_HANDLE_NOT_SET)
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_adv_handle, pwr);
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_SCAN_INIT, 0/*ignored*/, pwr);

View File

@ -286,7 +286,7 @@ void ble_cts_handle_time(BLEPending blep, char *buffer, size_t bufferLen) {
ble_date_time_t time = p_time->exact_time_256.day_date_time.date_time;
CalendarDate date;
date.year = time.year;
date.month = time.month;
date.month = time.month-1; // JS months are 0-11, but CTS uses 1-12
date.day = time.day;
TimeInDay td;
td.daysSinceEpoch = fromCalenderDate(&date);

View File

@ -664,9 +664,12 @@ static NO_INLINE void jshPinSetFunction_int(JshPinFunction func, uint32_t pin) {
case JSH_TIMER2:
case JSH_TIMER3: {
NRF_PWM_Type *pwm = nrf_get_pwm(fType);
pwm->PSEL.OUT[fInfo>>JSH_SHIFT_INFO] = pin;
// FIXME: Only disable if nothing else is using it!
if (pin==0xFFFFFFFF) nrf_pwm_disable(pwm);
if (pin==0xFFFFFFFF) {
nrf_pwm_task_trigger(pwm, NRF_PWM_TASK_STOP);
nrf_pwm_disable(pwm);
}
pwm->PSEL.OUT[fInfo>>JSH_SHIFT_INFO] = pin;
break;
}
#endif

View File

@ -93,7 +93,6 @@ static void flashReset(){
nrf_delay_us(50);
}
// Wake up the SPI Flash from deep power-down mode
static void flashWakeUp() {
unsigned char buf = 0xAB; // SPI Flash release from deep power-down

View File

@ -17,6 +17,7 @@
#include "jspininfo.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "lcd.h"
/// Because Nordic's library functions don't inline on NRF52840!
#ifdef NRF_P1
@ -81,6 +82,15 @@ static bool get_btn2_state() {
return jshPinGetValue(BTN2_PININDEX)==BTN2_ONSTATE;
}
#endif
#ifdef BAT_PIN_CHARGING
static bool get_charging_state() {
return jshPinGetValue(BAT_PIN_CHARGING)==0;
}
#endif
static void print_fw_version(void) {
lcd_println("BL " JS_VERSION "\n");
}
static void hardware_init(void) {
#if defined(PIXLJS)
@ -88,6 +98,15 @@ static void hardware_init(void) {
jshPinOutput(LED1_PININDEX, 0);
#endif
set_led_state(false, false);
#ifdef DICKENS // Simpler setup of BTN1 and BTN2 to save 48 bytes of code space
NRF_GPIO_PIN_CNF(BTN1_PININDEX,0x0000000c); // BTN1 input (with pullup)
NRF_GPIO_PIN_CNF(BTN2_PININDEX,0x0000000c); // BTN2 input (with pullup)
jshPinOutput(LCD_BL, !LCD_BL_ON); // backlight off
// NRF_P1->OUT=0x00000001; // Backlight output high (for off)
#ifdef BAT_PIN_CHARGING
NRF_GPIO_PIN_CNF(BAT_PIN_CHARGING,0x0000000c); // Charge input (with pullup)
#endif
#else // !DICKENS
#ifdef BTN1_PININDEX
bool polarity;
uint32_t pin;
@ -108,7 +127,10 @@ static void hardware_init(void) {
nrf_gpio_cfg_input(pin,
polarity ? NRF_GPIO_PIN_PULLDOWN : NRF_GPIO_PIN_PULLUP);
#endif
#endif // !DICKENS
#ifdef VIBRATE_PIN
jshPinOutput(VIBRATE_PIN,0); // vibrate off
#endif
}

View File

@ -76,7 +76,7 @@ const unsigned short LCD_FONT_3X5[] = { // from 33 up to 127
PACK_5_TO_16( X__ , _X_ , X_X , X_X , X_X ),
PACK_5_TO_16( _X_ , _X_ , X_X , X_X , XXX ),
PACK_5_TO_16( __X , _X_ , X_X , _X_ , XXX ),
PACK_5_TO_16( XX_ , _X_ , _X_ , _X_ , X_X ),
PACK_5_TO_16( XX_ , _X_ , XXX , _X_ , X_X ),
PACK_5_TO_16( X_X , X_X , XXX , _XX , ___ ), // XYZ[\ end
PACK_5_TO_16( X_X , X_X , __X , _X_ , ___ ), // \ is used as .
@ -591,20 +591,20 @@ static const char SPILCD_INIT_CODE[] = {
#endif
#ifdef LCD_CONTROLLER_GC9A01
// CMD,DELAY,DATA_LEN,D0,D1,D2...
0xfe,0,0,
0xef,0,0,
0xeb,0,1, 0x14,
0xfe,0,0,
0xef,0,0,
0xeb,0,1, 0x14,
0x84,0,1, 0x60, // 0x40->0x60 0xb5 en 20200924 james
0x85,0,1, 0xFF,
0x86,0,1, 0xFF,
0x87,0,1, 0xFF,
0x8e,0,1, 0xFF,
0x8f,0,1, 0xFF,
0x88,0,1, 10,
0x88,0,1, 10,
0x89,0,1, 0x23, // 0x21->0x23 spi 2data reg en
0x8a,0,1, 0,
0x8b,0,1, 0x80,
0x8c,0,1, 1,
0x8a,0,1, 0,
0x8b,0,1, 0x80,
0x8c,0,1, 1,
0x8d,0,1, 3, // 1->3 99 en
0xb5,0,4, 0x08, 0x09, 0x14, 0x08,
0xb6,0,2, 0, 0, // Positive sweep 0x20->0 GS SS 0x20
@ -623,43 +623,41 @@ static const char SPILCD_INIT_CODE[] = {
#else
0x36,0,1, 0x88, // Memory Access Control (no rotation)
#endif
0x3a,0,1, 5, // could be 16/12 bit?
0x90,0,4, 8, 8, 8, 8,
0xbd,0,1, 6,
0xbc,0,1, 0,
0xff,0,3, 0x60, 1, 4,
0xc3,0,1, 0x1d, // Power control 2: 0x13->0x1d
0xc4,0,1, 0x1d, // Power control 3: 0x13->0x1d
0x3a,0,1, 5, // could be 16/12 bit?
0x90,0,4, 8, 8, 8, 8,
0xbd,0,1, 6,
0xbc,0,1, 0,
0xff,0,3, 0x60, 1, 4,
0xc3,0,1, 0x13, // Power control 2: 0x13->0x1d->0x13 (again)
0xc4,0,1, 0x13, // Power control 3: 0x13->0x1d->0x13 (again)
0xc9,0,1, 0x25, // Power control 4: 0x22->0x25
0xbe,0,1, 0x11,
0xe1,0,2, 0x10, 0xe,
0xdf,0,3, 0x21, 0xc, 2,
0xf0,0,6, 0x45, 9, 8, 8, 0x26, 0x2a, // Gamma 1
0xf1,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f, // Gamma 2
0xf2,0,6, 0x45, 9, 8, 8, 0x26, 0x2a, // Gamma 3
0xf3,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f, // Gamma 4
0xed,0,2, 0x1b, 0xb,
0xbe,0,1, 0x11,
0xe1,0,2, 0x10, 0xe,
0xdf,0,3, 0x21, 0xc, 2,
// disabled to save flash: 0xf0,0,6, 0x45, 9, 8, 8, 0x26, 0x2a, // Gamma 1
// disabled to save flash: 0xf1,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f, // Gamma 2
// disabled to save flash: 0xf2,0,6, 0x45, 9, 8, 8, 0x26, 0x2a, // Gamma 3
// disabled to save flash: 0xf3,0,6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6f, // Gamma 4
0xed,0,2, 0x1b, 0xb,
0xae,0,1, 0x77, // 0x74->0x77
0xcd,0,1, 99,
0xcd,0,1, 0x63,
0x70,0,9, 7, 7, 4, 0xe, 0xf, 9, 7, 8, 3, // 7,9,4... -> 7,7,4...
0xe8,0,1, 0x34,
0x60,0,4, 0x38, 0x0b, 0x6d, 0x6d,
0x39,0,3, 0xf0, 0x6d, 0x6d,
0x61,0,4, 0x38, 0xf4, 0x6d, 0x6d,
0x38,0,3, 0xf7, 0x6d, 0x6d,
0xe8,0,1, 0x34,
0x60,0,8, 0x38, 0x0b, 0x6d, 0x6d, 0x39, 0xf0, 0x6d, 0x6d,
0x61,0,8, 0x38, 0xf4, 0x6d, 0x6d, 0x38, 0xf7, 0x6d, 0x6d,
0x62,0,12, 0x38, 0xd, 0x71, 0xed, 0x70, 0x70, 0x38, 0xf, 0x71, 0xef, 0x70, 0x70,
0x63,0,12, 0x38, 0x11, 0x71, 0xf1, 0x70, 0x70, 0x38, 0x13, 0x71, 0xf3, 0x70, 0x70,
100,0,7, 0x28, 0x29, 0xf1, 1, 0xf1, 0, 7,
0x66,0,10, 0x3c, 0, 0xcd, 0x67, 0x45, 0x45, 0x10, 0, 0, 0,
0x67,0,10, 0, 0x3c, 0, 0, 0, 1, 0x54, 0x10, 0x32, 0x98,
0x74,0,7, 0x10, 0x85, 0x80, 0, 0, 0x4e, 0,
0x98,0,2, 0x3e, 7,
0x64,0,7, 0x28, 0x29, 0xf1, 1, 0xf1, 0, 7,
0x66,0,10, 0x3c, 0, 0xcd, 0x67, 0x45, 0x45, 0x10, 0, 0, 0,
0x67,0,10, 0, 0x3c, 0, 0, 0, 1, 0x54, 0x10, 0x32, 0x98,
0x74,0,7, 0x10, 0x68, 0x80, 0, 0, 0x4e, 0, // 0x85->0x68
0x98,0,2, 0x3e, 7,
0x99,0,2, 0x3e, 7, // bvee 2x
0x35,0,1, 0,
0x21,5,0,
0x11,5,0,
0x29,5,0,
0x2c,0,0,
0x35,0,1, 0, // Tearing effect (TE) line ON, with V-blanking only
0x21,5,0, // Display inversion ON
0x11,5,0, // Sleep out
0x29,5,0, // Display ON
0x2c,0,0, // Memory write
#endif
// End
0, 0, 255/*DATA_LEN = 255 => END*/
@ -751,9 +749,9 @@ void lcd_init() {
jshPinOutput(LCD_SPI_SCK,1);
jshPinOutput(LCD_SPI_MOSI,1);
jshPinOutput(LCD_SPI_RST,0);
jshDelayMicroseconds(10000);
nrf_delay_ms(50);
jshPinOutput(LCD_SPI_RST, 1);
jshDelayMicroseconds(10000);
nrf_delay_ms(120);
// Send initialization commands
const char *cmd = SPILCD_INIT_CODE;
@ -764,8 +762,9 @@ void lcd_init() {
cmd += 3 + cmd[CMDINDEX_DATALEN];
}
}
void lcd_kill() {
jshPinOutput(LCD_BL,!LCD_BL_ON); // backlight off
jshPinOutput(LCD_BL, !LCD_BL_ON); // backlight off
lcd_cmd(0x28, 0, NULL); // display off
jshDelayMicroseconds(20);
lcd_cmd(0x10, 0, NULL); // SLPIN
@ -774,7 +773,7 @@ void lcd_kill() {
#endif
}
#endif
#endif // LCD_CONTROLLER_ST7735 or LCD_CONTROLLER_GC9A01
#ifdef LCD_CONTROLLER_LPM013M126

View File

@ -80,38 +80,43 @@ void turn_off() {
#if defined(SPIFLASH_SLEEP_CMD) && defined(ESPR_BOOTLOADER_SPIFLASH)
flashPowerDown(); // Put the SPI Flash into deep power-down
#endif
#ifdef VIBRATE_PIN
jshPinOutput(VIBRATE_PIN,1); // vibrate on
#if defined(VIBRATE_PIN) && !defined(DICKENS)
jshPinOutput(VIBRATE_PIN,1); // vibrate whilst waiting for button release
#endif
#if defined(BTN2_PININDEX)
while (get_btn1_state() || get_btn2_state()) {}; // wait for BTN1 and BTN2 to be released
#else
while (get_btn1_state()) {}; // wait for BTN1 and BTN2 to be released
while (get_btn1_state()) {}; // wait for BTN1 to be released
#endif
#ifdef VIBRATE_PIN
jshPinSetValue(VIBRATE_PIN,0); // vibrate off
#endif
#ifdef DICKENS
NRF_P0->OUT=0x03300f04; // 00000011 00110000 00001111 00000100 - high pins: D2, D8, SDA, SCL, LCD_CS, FLASH_CS, FLASH_WP, FLASH_RST, FLASH_SCK
//NRF_P0->OUT=0x03300e00; // 00000011 00110000 00001110 00000000 - high pins: SDA, SCL, LCD_CS, FLASH_CS, FLASH_WP, FLASH_RST, FLASH_SCK
//NRF_P0->OUT=0x03300e00; // 00000011 00110000 00001110 00000000 - high pins: SDA, SCL, LCD_CS, FLASH_CS, FLASH_WP, FLASH_RST, FLASH_SCK
if (pinInfo[LCD_BL].port&JSH_PIN_NEGATED) // if backlight negated
NRF_P1->OUT=0x00000001; // High pins: LCD_BL
NRF_P1->OUT=0x00000101; // High pins: LCD_BL, P1.16 (doesn't exist, but seems to draw around 3 µA extra if this is not set)
else
NRF_P1->OUT=0x00000000;
for (uint8_t pin=0; pin<48; pin++) {
NRF_GPIO_PIN_CNF(pin,0x00000004); // Set all pins as input with pulldown
}
NRF_P1->OUT=0x00000100;
// for (uint8_t pin=0; pin<48; pin++) {
// NRF_GPIO_PIN_CNF(pin,0x00000006); // Set all pins as input disconnect with pulldown
// }
NRF_GPIO_PIN_CNF(BAT_PIN_VOLTAGE,0x00000002); // D4 = battery voltage measurement (no pull, input buffer disconnected)
NRF_GPIO_PIN_CNF(ACCEL_PIN_SDA,0x0000060d); // D9 = SDA open-drain output
NRF_GPIO_PIN_CNF(ACCEL_PIN_SCL,0x0000060d); // D10 = SCL open-drain output
NRF_GPIO_PIN_CNF(LCD_SPI_MISO,0x0000000c); // D27 = LCD_MISO input with pullup
if (pinInfo[LCD_BL].port&JSH_PIN_NEGATED) // if backlight negated
NRF_GPIO_PIN_CNF(LCD_BL,0x00000003); // D32 = LCD backlight pin
#ifdef ACCEL_PIN_INT1
NRF_GPIO_PIN_CNF(ACCEL_PIN_INT1,0x00000002); // D21 = INT1 (no pull, input buffer disconnected)
NRF_GPIO_PIN_CNF(ACCEL_PIN_INT2,0x00000002); // D23 = INT2 (no pull, input buffer disconnected)
#endif
NRF_GPIO_PIN_CNF(LCD_SPI_MISO,0x00000002); // D27 = LCD_MISO (no pull, input buffer disconnected)
//if (pinInfo[LCD_BL].port&JSH_PIN_NEGATED) // if backlight negated
// NRF_GPIO_PIN_CNF(LCD_BL,0x00000003); // D32 = LCD backlight pin
//NRF_GPIO_PIN_CNF(BTN2_PININDEX,0x0003000c); // D28 = BTN2 input (with pullup and low-level sense)
//NRF_GPIO_PIN_CNF(BTN3_PININDEX,0x0003000c); // D29 = BTN3 input (with pullup and low-level sense)
//NRF_GPIO_PIN_CNF(31,0x00000003); // D31 = Debug output pin (brought out to external header on)
//NRF_GPIO_PIN_CNF(BTN4_PININDEX,0x0003000c); // D42 = BTN4 input (with pullup and low-level sense)
NRF_GPIO_PIN_CNF(BTN1_PININDEX,0x0003000c); // D46 = BTN1 input (with pullup and low-level sense)
NRF_GPIO_PIN_CNF(BAT_PIN_CHARGING,0x0000000c); // Charge input (with pullup)
#else // !DICKENS
set_led_state(0,0);
#if defined(BTN2_PININDEX)
@ -187,6 +192,7 @@ bool dfu_enter_check(void) {
#endif
} else {
lcd_clear();
print_fw_version();
lcd_println("DFU START");
}
set_led_state(true, true);
@ -356,24 +362,33 @@ int main(void)
dfuIsColdBoot = (r&0xF)==0;
#if defined(DICKENS) || defined(BANGLEJS)
// On smartwatches, turn on only if BTN1 held for >1 second
// On smartwatches, turn on only if BTN1 held for >1 second (or charging on Dickens)
// This may help in cases where battery is TOTALLY flat
if ((r&0b1011)==0) {
// if not watchdog, lockup, or reset pin...
if ((r&0xF)==0) { // Bangle.softOff causes 'SW RESET' after 1 sec, so r==4
nrf_delay_ms(1000);
}
#ifndef DICKENS
if (!get_btn1_state() && (r&0xF)==0) { // Don't turn off after a SW reset, to avoid user input needed during reflashing
turn_off();
} else {
#ifdef DICKENS
#else // DICKENS
if (!get_btn1_state() && get_charging_state()) {
nrf_delay_ms(3000); // wait 4 secs in total before booting if on charge
}
if (!get_btn1_state() && !get_charging_state() && (r&0xF)==0) { // Don't turn off after a SW reset, to avoid user input needed during reflashing
turn_off();
} else {
// DICKENS: Enter bootloader only if BTN2 held as well
if (!get_btn2_state()) {
// Clear reset reason flags
NRF_POWER->RESETREAS = 0xFFFFFFFF;
#ifdef ESPR_BOOTLOADER_SPIFLASH
lcd_init();
lcd_println("DFU " JS_VERSION "\n");
#ifndef DICKENS
print_fw_version();
#endif
// Check if we should reflash new firmware
flashCheckAndRun();
#endif
@ -400,7 +415,7 @@ int main(void)
}
// Clear reset reason flags
NRF_POWER->RESETREAS = 0xFFFFFFFF;
lcd_println("DFU " JS_VERSION "\n");
print_fw_version();
#ifdef ESPR_BOOTLOADER_SPIFLASH
if (!get_btn1_state()) flashCheckAndRun();
#endif