-
I'd share my code, but I don't feel it's ready.. I'd like to implement a queue for all commands, since it's possible that during a full refresh (which will return to the event loop) another command could be requested at a bad time. But then I can share a simple clock that updates every minute (fast refresh) an updates full every hour. I just have some other work ahead of that... :-/
-
My understanding is you don't get both.. you get one or the other. Mode 1 for slow, but no ghosting and known state (you should always START with this), or Mode 2 for faster, some ghosting. And according to what I've read, make sure you revert to full refresh regularly to keep the screen "healthy"
From the spec, I see parameters B1 and B9 for command 0x22... but I don't understand their use. I use CF for a Mode 2, and if you don't call the command it appears to default to Mode 1 (after reset anyway)
-
I'm pretty sure that's full refresh (mode 1) and partial refresh (mode 2). From the recommendations, it says if you use partial (which looks nicer and is faster), make sure that you occasionally do a full refresh to reset the display to known values. Also, power it off whenever you can.. not only does it save power, but the higher voltage can cause permanent damage.
In my case, I'm setting up my desk clock to do a partial refresh every minute, and full refresh on the hour (like a visual "chime"). But I haven't powered it down (or put it to sleep) in 3 weeks! Guess I'd better get on that...
-
there may be other commands that need to be "waited" for. If you look at C source for these (heltec, waveshare) you'll see the spots where they wait for BUSY to clear. seeing BUSY go hi/lo a lot means your just sending commands, should not be a problem. But if the timing is off, you'll get weird effects like you've been seeing
-
You can run Espruino on both of those. I just flashed the Watchy the other day and it works while plugged in (just drains the battery quickly). Pinetime is running an NRF52832 so it runs Espruino as well, but I think you have to open the case to get to the SWD pins (the default bootloader is not compatible with Espruino OTA update).
-
Glad to hear. You lit a fire under me to get the whole BUSY thing working the way I wanted. In my haste, my original code just used timeouts for calls, which meant you had to be very careful when you called subsequent functions. So thanks for that! I think I'll fallback on timeouts for when the target device is a low-memory MCU (no Promises).
-
-
-
As for the Watchy, I wanted an e-ink watch to develop with. But I eschew the big compile, push full firmware approach for just tweaking watch faces, so I'm a huge fan of Espruino everywhere. Fortunately, it's an ESP32 so I just pushed a existing build to it and it ran OOB. It also ran out of battery in 30 minutes... as ESP32s need a lot of power management to last on a battery. I hope to tweak the firmware to do what the Watchy code does: shutdown completely and wake on RTC or button press. Some day...
-
Here is a gist of what works on my Watchy:
https://gist.github.com/yngv27/aa5f562597d734928c5d5a1b622c2c13it runs through your original demo, then does a partial refresh demo. It needs to be cleaned up before becoming a module.
Not sure if this is compatible with SSD1606 or not, I don't have one to try. I know it's not compatible with IL3829 (an older 1.54 screen).
It's not perfect, no. On my screen the regular update creates faint, but crisp edges for text and shapes, whereas the partial refresh thickens everything up (it's easier to read, just not as precise). I'm still trying to figure this stuff out.
As for the LUT, it's from the Heltec site where my first 1.54 came from:
https://github.com/HelTecAutomation/e-ink/blob/master/src/DEPG0150BxS810FxX_BW.hScreens vary a LOT from what I understand, so maybe try a Waveshare source file too?
-
I've got some code that may be helpful. I'm able to do a partial refresh on my Watchy using this, although the LUT is not quite right (seems very dark compared to regular full refresh). First of all, I extended some of your commands.. I didn't see you calling cmd(0x12) (software reset) so I created a fullReset():
SSD16xx.prototype.fullReset = function() { return new Promise((resolve)=>{ digitalWrite(this.resetPin, 0); setTimeout(()=>{ digitalWrite(this.resetPin, 1); this.sc(0x12); this.partial=false; setTimeout(resolve,this.hwResetTimeOut); },this.hwResetTimeOut); }); };
I also use this to send commands WITH data, then wait (if necessary) for the RST pin (some commands are so fast that a watch on RST falling doesn't register):
SSD16xx.prototype.waitCmdData = function(command, data){ return new Promise((resolve,reject)=>{ console.log("sending a busy command!"); this.sc(command); this.psd(); this.sd(data); if(this.busyPin.read()) setWatch(resolve, this.busyPin, { repeat:false, edge:'falling' }); else resolve(); }); };
This sets up using partial refresh by uploading a special LUT:
SSD16xx.prototype.setFastRefresh = function() { const WF_PARTIAL = new Uint8Array([ 0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xF,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0,0x02,0x17,0x41,0xB0,0x32,0x28, ]); this.fullReset().then(()=>{ this.waitCmdData(0x32, WF_PARTIAL.slice(0,153)).then(()=>{ this.scd(0x3F, WF_PARTIAL[153]); this.scd(0x03, WF_PARTIAL[154]); this.scd(0x04, WF_PARTIAL.slice(155, 158)); this.scd(0x2C, WF_PARTIAL[158]); this.scd(0x37, [0,0,0,0,0,0x40,0,0,0,0]); this.waitCmdData(0x3C, 0x80).then(()=>{ this.partial = true; }); }); }); };
It sets an internal flag (partial) so the flip() command knows what to send:
SSD16xx.prototype.flip = function() { this.scd(0x4E, 0); this.scd(0x4F, [0,0]); this.scd(0x24, this.g.bw.buffer); if(this.display.coloredDisplay){ this.scd(0x4E, 0); this.scd(0x4F, [0,0]); this.scd(0x26, this.g.cw.buffer); } if(this.partial) { print("flip:partial"); this.scd(0x22, 0xcf); } else { print("flip:full"); //this.scd(0x22, 0xF7); } return this.refreshDisplay(); };
That much works for me on the Watchy. Hope it helps. Like I said, LUTs are tricky things, so your results may be better/worse. I'm still trying to figure the out...
-
By partial, do you mean "partial update" (updating a subsection of the screen) or partial (i.e. "fast") refresh? You've got your partialFlip() implementing a little of both. I've never used partial screen refresh as it's a lot of work to save a few milliseconds while the screen still takes 2 sec to display. As for a "partial refresh", you need to set it up first with a few commands (reinitialize, set the partial LUT), then when you flip, you include the extra command (0x22, 0xcf) before update (0x20).
Looks like your partial update is requesting the updated dimensions of the buffer, but it seems to return the entire buffer area. Therefore, you're recreating the entire buffer in JS and sending it, so it takes quite a bit of time.
I'm working on getting partial refresh put into your sample. It seems my Watchy is not honoring the busy pin well...
-
I'm currently working on some e-ink screens, and I have working JS code for the Waveshare 1.54 3-color and a Weact 2.19" (very similar: SSD168x drivers). I think it should work with a watchy screen. LMK how you're progressing and I can share my code (still not a module, just need some time). BTW: I'm using my own board file for an NRF52832 breakout board, so it doesn't have any Bangle code built-in.
-
-
-
-
-
I'm getting these errors during my build:
CC obj/targetlibs/nrf5x_15/components/libraries/atomic/nrf_atomic.o In file included from targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.c:41:0: targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.h:116:13: error: unknown type name 'nrf_uarte_baudrate_t' typedef nrf_uarte_baudrate_t nrf_uart_baudrate_t; ^ targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.h:121:13: error: unknown type name 'nrf_uarte_error_mask_t' typedef nrf_uarte_error_mask_t nrf_uart_error_mask_t; ^ targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.h:124:13: error: unknown type name 'nrf_uarte_hwfc_t' typedef nrf_uarte_hwfc_t nrf_uart_hwfc_t; ^ targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.h:127:13: error: unknown type name 'nrf_uarte_parity_t' typedef nrf_uarte_parity_t nrf_uart_parity_t; ^ targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.h:128:13: error: unknown type name 'nrf_uarte_task_t' typedef nrf_uarte_task_t nrf_uart_task_t; ^ targetlibs/nrf5x_15/integration/nrfx/legacy/nrf_drv_uart.h:129:13: error: unknown type name 'nrf_uarte_event_t' typedef nrf_uarte_event_t nrf_uart_event_t; ^
Here is the relevant (Ihope) portion of my build file:
info = { 'name' : "QY03", 'link' : [ "https://www.kospet.com/products/kospet-magic-3" ], 'espruino_page_link' : 'QY03', 'default_console' : "EV_BLUETOOTH", 'variables' : 14000-300, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile. #'bootloader' : 1, 'binary_name' : 'e_%v.QY03.hex', 'build' : { 'optimizeflags' : '-Os', 'libraries' : [ 'BLUETOOTH', # 'NET', 'GRAPHICS', #'JIT', # 'NEOPIXEL' ], 'makefile' : [ # 'DEFINES += -DCONFIG_GPIO_AS_PINRESET', # Allow the reset pin to work # 'CFLAGS += -D__STARTUP_CLEAR_BSS -D__START=main', # 'LDFLAGS += -D__STARTUP_CLEAR_BSS -D__START=main -nostartfiles', 'DEFINES += -DNRF_SDH_BLE_GATT_MAX_MTU_SIZE=131', #59 77 131 104 'DEFINES += -DBLUETOOTH_NAME_PREFIX=\'"QY03"\'', 'LDFLAGS += -Xlinker --defsym=LD_APP_RAM_BASE=0x2ec0',#2bf0 0x3058#37f8 0x3720 'LDFLAGS += -Xlinker --defsym=LD_NOINIT_SIZE=0x1290',#2bf0 0x3058#37f8 0x3720 'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem', 'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0xa9,0xae,0xb6', [#S140](https://forum.espruino.com/search/?q=%23S140) 6.0.0 'BOOTLOADER_SETTINGS_FAMILY=NRF52840', #'ESPR_BLUETOOTH_ANCS=1', # Enable ANCS (Apple notifications) support # 'BLACKLIST=boards/MAGIC3.blocklist', # force some stuff to be removed to save space 'DEFINES += -DNRF_BL_DFU_INSECURE=1 -DNRF_BOOTLOADER_NO_WRITE_PROTECT=1 -DESPR_DCDC_ENABLE=1', 'DEFINES += -DNO_DUMP_HARDWARE_INITIALISATION', 'DEFINES += -DUSE_FONT_6X8 -DGRAPHICS_PALETTED_IMAGES ', #-DGRAPHICS_ANTIALIAS', # 'DEFINES += -DSAVE_ON_FLASH_SAVE -DSAVE_ON_FLASH_ERRORMSG -DSAVE_ON_FLASH_RANDOM -DSAVE_ON_FLASH_WAVEFORM -DSAVE_ON_FLASH_MATH -DSAVE_ON_FLASH_SWSERIAL -DSAVE_ON_FLASH_FFT -DSAVE_ON_FLASH_DUMP', # 'DEFINES+=-DDUMP_IGNORE_VARIABLES=\'"g\\0"\'', 'DEFINES += -DFDS_VIRTUAL_PAGES=2', [#should](https://forum.espruino.com/search/?q=%23should) match fstorage_pages below 'NRF_SDK15=1' ] } }; save_code_pages = 132; #96; fstorage_pages = 10; # typically 2, 10 reduces risk of brick on first flash from stock FW chip = { 'part' : "NRF52840", 'family' : "NRF52", 'package' : "QFN48", 'ram' : 256, 'flash' : 1024, 'speed' : 64, 'usart' : 0, #2 'spi' : 2, #3 'i2c' : 1, #2 'adc' : 1, 'dac' : 0, 'saved_code' : { 'address' : ((0xf8 - fstorage_pages - save_code_pages) * 4096), # Bootloader at 0xF8000 'page_size' : 4096, 'pages' : save_code_pages, 'flash_available' : 1024 - ((0x26 + (0x100-0xf8) + fstorage_pages + save_code_pages)*4), # Softdevice uses 38 pages of flash (0x26000/0x100), bootloader 0x100-0xe0=0x20, FS 2, code 96. Each page is 4 kb. # TWO PAGES 'address2' : 0x60000000, # put this in external spiflash (see below) 'pages2' : 4096, # Entire 16MB (4096 pages of 4096) of external flash }; devices = { 'BTN1' : { 'pin' : 'D4', 'inverted': True, 'pinstate' : 'IN_PULLUP' }, 'SPIFLASH' : { 'pin_cs' : 'D17', 'pin_sck' : 'D19', 'pin_mosi' : 'D20', 'pin_miso' : 'D21', 'pin_wp' : 'D22', # 'pin_hold' : 'D23', 'pin_rst' : 'D23', # no reset but this is HOLD pin, we want it set to 1 like RST 'size' : 16384*1024, # 16MB 'memmap_base' : 0x60000000, }, };
Not sure why it's happening here and not in other builds (like BANGLEJS2)...?
jv -
Just did a fresh pull and tried to build BANGLEJS2 and it failed with:
CC obj/libs/tensorflow/tensorflow/lite/micro/simple_memory_allocator.cc.o arm-none-eabi-gcc: error: unrecognized command line option '-fmacro-prefix-map=libs/tensorflow/=' make: *** [Makefile:842: obj/libs/tensorflow/tensorflow/lite/micro/simple_memory_allocator.cc.o] Error 1
I commented out TENSORFLOW and it builds fine.
-
As always, thanks for your insights, @fanoush.. great idea.
-
I made sure it was all wired at power up and sure enough, it lit up just fine (@ 38400 too). This will be interesting as my true target for this is battery powered. Maybe I'll add a watch on button hold to enable Serial1 and setConsole...
Either way, thank you both! One step forward.
(BTW: the target board is a C20 smartwatch [https://www.aliexpress.com/item/33025338264.html] which were on sale last year for $10US. One became an experiment and I somehow fried the BT system, so looking for an alternate console. )
-
-
I've done a git pull to get 2v19. I'm seeing this on all NRF52840 boards that previously compiled OK (I'm guessing circa 2v16 or so)...
targets/nrf5x/bluetooth.c: In function 'jsble_exec_pending': targets/nrf5x/bluetooth.c:445:9: error: 'BLEP_BONDING_STATUS' undeclared (first use in this function) case BLEP_BONDING_STATUS: { ^ targets/nrf5x/bluetooth.c:445:9: note: each undeclared identifier is reported only once for each function it appears in targets/nrf5x/bluetooth.c:542:33: error: implicit declaration of function 'bleGetActiveBluetoothGattServer' [-Werror=implicit-function-declaration] JsVar *gattServer = bleGetActiveBluetoothGattServer(centralIdx); ^ targets/nrf5x/bluetooth.c:542:33: warning: initialization makes pointer from integer without a cast [-Wint-conversion] targets/nrf5x/bluetooth.c: In function 'pm_evt_handler': targets/nrf5x/bluetooth.c:1735:31: error: 'BLEP_BONDING_STATUS' undeclared (first use in this function) jsble_queue_pending(BLEP_BONDING_STATUS, BLE_BOND_START); ^ targets/nrf5x/bluetooth.c: In function 'services_init': targets/nrf5x/bluetooth.c:2449:17: error: 'ble_nus_init_t {aka struct <anonymous>}' has no member named mitmProtect' nus_init.mitmProtect = true; ^ targets/nrf5x/bluetooth.c: In function 'jsble_startBonding': targets/nrf5x/bluetooth.c:3223:23: error: 'BLEP_BONDING_STATUS' undeclared (first use in this function) jsble_queue_pending(BLEP_BONDING_STATUS, BLE_BOND_REQUEST); // report that we've requested bonding ^ targets/nrf5x/bluetooth.c: In function 'jsble_set_tx_power': targets/nrf5x/bluetooth.c:3383:9: error: 'm_central_conn_handles' undeclared (first use in this function) if (m_central_conn_handles[i] != BLE_CONN_HANDLE_INVALID)
I can't find BLEP_BONDING_STATUS in any *.h file!
I'm also getting a FLASH overrun error trying to compile BANGLEJS2:
/usr/local/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: bin/bootloader_espruino_2v19.179_banglejs2.elf section `.text' will not fit in region `FLASH' /usr/local/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: region FLASH overflowed with .data and user data /usr/local/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: region `FLASH' overflowed by 1892 bytes collect2: error: ld returned 1 exit status make: *** [make/targets/ARM.make:3: bin/bootloader_espruino_2v19.179_banglejs2.elf] Error 1 In file included from targetlibs/nrf5x_15/modules/nrfx/mdk/nrf_peripherals.h:63:0, from targetlibs/nrf5x_15/modules/nrfx/drivers/nrfx_common.h:49, from targetlibs/nrf5x_15/modules/nrfx/nrfx.h:45, from targetlibs/nrf5x_15/modules/nrfx/hal/nrf_gpio.h:44, from targets/nrf5x_dfu/hardware.h:18, from targets/nrf5x_dfu/lcd.c:15:
No setTimeout(); has to be a check for BUSY going low (or is already low). You can say "don't do anything" while it's doing a full redraw and right now that's exactly what I am doing, but someone else who may use this module may not heed that and see issues and not understand. So it's trying to foolproof the driver. In particular, I would override g.flip()/g.setPartial()/g.reset() so they all get queued and only perform in the intended order and when BUSY is low