Espruino on your watch!

Posted on
Page
of 10
  • Espruino and Bluetooth should be fine battery-wise, but it's the serial console that'll get you. That could take as much as 4mA.

  • @fanoush, great legwork with all this reverse-engineering!

  • Thanks guys.

    I figured out the touch button - pin30 reversed (low when pressed) and charging/power status. Those were the easy ones. I put my current board diff here https://github.com/fanoush/ds-d6/wiki/Es­pruino I an confused about the pinutils.findpin section, what it is need for? What are correct names for pins and interfaces there? Pins are not Dxx but PDxx and names like UART or USART look random across different boards.

    Also how do I tell that the DFU bootloader should use negated touch button on pin 30?

    I also tried to scan I2C but basically any address returns some data. I used I2C2.setup({scl:13, sda:14, bitrate:400000}) and when using I2C2.readFrom(address, 1) with random adresses it always returns something - mostly array with [232] value. What am I doing wrong? How to scan i2c for devices in espruino?

  • I an confused about the pinutils.findpin section, what it is need for?

    It's not that useful on nRF52 as I2C/SPI/Serial can be on any bit - mainly it was there for STM32.
    The bits you need are the ones for ADC since they say what channel/pin the ADC is on.

    You can safely ignore anything to do with SPI/I2C/USART - they only exist on nRF52 so that they appear in the pinout diagram, eg. https://www.espruino.com/MDBT42Q#pinout

    What are correct names for pins and interfaces there? Pins are not Dxx but PDxx

    It's based on STM32 but is too much of a pain to explain here. Just use https://github.com/espruino/Espruino/blo­b/master/boards/MDBT42Q.py as the basis.

    Also how do I tell that the DFU bootloader should use negated touch button on pin 30?

    'BTN1' : { 'pin' : 'D30', 'pinstate' : 'IN_PULLDOWN' },
    ...
    pinutils.findpin(pins, "PD30", True)["functions"]["NEGATED"]=0;
    

    should work and should invert it in Espruino too.

    I2C

    Honestly, not sure why it's doing that, but why not just look at the PCB (or even the manufacturer's docs) to try and find out what the I2C parts are then try and look at those addresses?

  • mostly array with [232] value

    looks like scl:13 or/and sda:14 is not used for I2C on this device. Try to check the pins with an LED and a resistor and command PIN.toggle();

    Check i2cdetect for a simple scanner


    1 Attachment

    • Resistor_LED.jpg
  • looks like scl:13 or/and sda:14 is not used for I2C on this device.

    could be some mistake on my part, will double check but this is what I got from register dump when running the firmware and stopping it in gdb over swd https://github.com/fanoush/ds-d6/wiki/Ha­rdware

    0x40004500: 0x00000005
    0x40004508: 0x0000000d
    0x4000450c: 0x0000000e
    0x40004524: 0x06680000

    which I guessed is TWI1 with scl 13 sda 14 speed 400kbps
    as per https://infocenter.nordicsemi.com/index.­jsp?topic=%2Fcom.nordic.infocenter.nrf52­832.ps.v1.1%2Ftwi.html&cp=2_1_0_48_7_5&a­nchor=register.PSELSCL

    Check i2cdetect for a simple scanner

    Thanks a lot, this is what I was looking for :-)

  • It's not that useful on nRF52 as I2C/SPI/Serial can be on any bit

    Yes it can be on any pin but in this watch it is on specific pins so if it would set some predefined values out of box so one could just use it without setup in code it would make some sense.

    'BTN1' : { 'pin' : 'D30', 'pinstate' : 'IN_PULLDOWN' },
    

    Is it pull up or down? Since when the button is not touched the value is high and goes low when touched shouldn't this be pull up? or is there just 'INPUT' with no pull as it is always connected and not left floating

  • Did you try it?

    It's pull down because it's also inverted. Check some of the other .py files in the boards directory.

  • Since when the button is not touched the value is high and goes low when touched shouldn't this be pull up?

    This sound like a INPUT_PULLUP, please check pinMode.

  • Did you try it?

    Not yet, I better asked.

    It's pull down because it's also inverted. Check some of the other .py files in the boards directory.

    Yes, I've seen it some board files, just was not sure. I was thinking inverted means the bit is just flipped in software before returning it back to js code while the pinstate should set pull up/down right in the HW registers. OK thanks for explanation, will try.

  • Honestly, not sure why it's doing that

    As for i2c for some reason it did not work with I2C2. It did work however with I2C1. So I2C2.setup({scl:13, sda:14, bitrate:400000}) did nothing, I did also break into debugger after that and HW registers were still at default values for both TWI ports. I2C1.setup({scl:13, sda:14, bitrate:400000}) however did work and when using I2C1 for scannig the bus I got exceptions except 0x1F address which was expected. There should be KX022 accelometer. BTW is there some Espruino module for this one? I see it was mentioned in this thread but I don't see it here https://github.com/espruino/EspruinoDocs­/tree/master/devices

    but why not just look at the PCB (or even the manufacturer's docs) to try and find out what the I2C parts are then try and look at those addresses?

    Because I wanted to test i2c functionality - scanning the bus looks like good test.

    As for the negated button - it doesn't work. It is still as before - returning false when pressed and true otherwise and BTN1 gives

    >BTN1.getInfo()
    ={ 
      port: "D", 
      num: 30, channel: 6, 
      functions: {  }
     }
    >BTN1.getMode()
    ="input_pulldown"
    

    so the line

      pinutils.findpin(pins, "PD30", True)["functions"]["NEGATED"]=0;
    

    seems to have no effect. I did find it used in boards/NRF52832DK.py in same way.

    gen/platform_config.h has

    [#define](https://forum.espruino.com/sea­rch/?q=%23define) BTN1_PININDEX 30/* D30 */
    [#define](https://forum.espruino.com/sea­rch/?q=%23define) BTN1_ONSTATE 1
    [#define](https://forum.espruino.com/sea­rch/?q=%23define) BTN1_PINSTATE JSHPINSTATE_GPIO_IN_PULLDOWN
    
    

    EDIT:
    oh, there is scripts/build_pininfo.py which is using NEGATED and then there is build_platform_config.py which tests for "inverted". And no code to automagicaly negate pulldown to pullup.

    EDIT2:
    however gen/jspininfo.c contains

    /* PD30 */ { JSH_PORTD|JSH_PIN_NEGATED, JSH_PIN0+30, JSH_ANALOG_NONE, { 0 } },
    

    so the 'NEGATED" works after all? but it still returned true when not pressed

  • I2C2.setup({scl:13, sda:14, bitrate:400000}) did nothing

    I don't think I2C2 is supported on Espruino since you can't have two SPI and two I2C. That's probably your issue.

    Maybe just try using software I2C.

    pinutils.findpin(pins, "PD30", True)["functions"]["NEGATED"]=0;

    Did you make clean before rebuilding?

  • And no code to automagicaly negate pulldown to pullup.

    What's this then? https://github.com/espruino/Espruino/blo­b/master/targets/nrf5x/jshardware.c#L466­

    NEGATED adds a flag in gen/jspininfo.c - it's different to the BTN1_* defines.

  • I don't think I2C2 is supported on Espruino since you can't have two SPI and two I2C.

    oh, I see, I was confused by the board file having

      'spi' : 3,
      'i2c' : 2,
      'adc' : 1,
    

    and I2C2 being defined. But since there are 3 SPIs too and the i2c/spi resources are shared it clearly cannot work for all five at the same time even if those objects are all defined in the interpreter, however I did not use SPI yet so in theory both i2c interfaces should be free to use.

    Thanks for pointing the inverting code (would not have guessed it is done later at runtime), also will try with 'make clean', thanks.
    EDIT: oh, checked the file you linked and the answer to SPI/I2c is there too
    https://github.com/espruino/Espruino/blo­b/master/targets/nrf5x/jshardware.c#L75
    so currently just one SPI and one I2c. So perhaps the board file should reflect that if those numbers have any efffect on generating those SPIx and I2Cx objects at runtime.
    EDIT2: and indeed they have, I have reduced i2c and spi to 1 and jswrapper.c no longer has those not supported ones generated.

  • Thanks - Honestly I don't know why those are set the way they are. Looking at Git Blame it seems it was just because that's what was put in initially.

    I believe the SPI_COUNT constant in platform_config.h may conflict with what's in targetlibs/nrf5x_12/components/device/nr­f52832_peripherals.h which is why it's still 3, but if it compiles then it'd be a good thing for me to switch back on all nRF52s - it seems that SPI_COUNT in the Nordic SDK isn't used anyway.

  • How complicated is to add support for two NRF52 I2C buses? Looks like this watch uses one for accelerometer and second one for HR sensor.It uses TWI0, TWI1 and SPI2 (for OLED). TWI0 uses scl 7 sda 8.

  • So I got the display working with the SSD1306 module :-)

    SPI1.setup({mosi:6,sck:5,baud:8000000})
    g=require("SSD1306").connectSPI(SPI1,D28­,D4,function(){g.drawString("Hello",0,0)­;g.flip()},{cs:D29,height:32})
    

    Looks like I have most of the hardware pinout covered. I have updated HW summary here DS-D6 Hardware I still guess on some analog input there could be real battery voltage (NRF.getBattery() returns 3.3 which s already regulated)

    So now let's see how far can I go with Espruino to do some watch interface - showing time, setting alarms etc. So far it looks pretty usable.

    BTW I noticed some strange issue, the HR sensor needs to be enabled by pin 26. When it is not done any i2c read on its bus hangs (forever?) and ctrl+c does not work, is there a way to break it? Or maybe some timeout?

  • So I got the display working with the SSD1306 module :-)

    I spoke too soon. I see the text but every other 32pixel line is empty so text takes up double width. This is with height set to 32. I found this thread http://forum.espruino.com/conversations/­269330/ which discusses the issue but it looks like current SSD1306 module I use is already fixed like that. But when I set height to 32 it is wrong. And when leaving the default 64 it works a bit better. When I draw to normal coordinates 0,0 with 64 height set and call flip() I see nothing. But when I connectSPI to it again now with 32 height I see previously written data correctly how it should be. But any new drawings written with 32 height set are wrong again. Also when in 64 height mode I can write text to y=32 and it shows with correct size as if y=0, however only few (8?) 128pixel lines are working so when I write to e.g. y=36 the bottom of text is cut off so I cannot access lower part of display. I guess the initCmds and flipCmds arrays are not correctly set for my type of display but I don't know how to fix it. Any tips?

    EDIT: Oh, got it. For some reason with my display the initCmds[15] should be 0x12 like for 64 height and not 0x02. When I keep 0x12 it works perfectly. Well it is mirrored so I need also setRotation(0,true) but then it is fine.

  • That's great!

    Potentially I2C2/SPI2/etc could be added but in reality the chip is sitting there waiting for the hardware, so there's actually very little reason not to just use software I2C... In fact it may draw less power because I2C hardware doesn't need to be kept powered.

  • Thanks for insight. Btw is it possible to build firmware hex file so that something already starts at boot time without calling save? I am currently putting my stuff to DSD6 module in libs/js for now but that of course does not start. so is there better way than loading hex, running it, calling save or create .boot(0,1,2,3) files and then making .hex from it? I did read https://www.espruino.com/Saving but still not sure what is possible. Can I e.g. prepare .boot1 file and put it to libs/js so it is build as part of firmware?

    Also what is best way to store relatively big data like bitmaps, fonts so it is not copied to RAM but could be read directly from flash as constants? I guess e.g. the initCmds array in SSD1306 module get allocated in RAM as it is now? I am looking at the fonts e.g. Font4x4Numeric.js and see E.toString() and also atob() but still not sure what get stored where. the module source get stored in flash as (possibly minifed) source code and when it is being run it creates another copy of data in ram/flash?

    Checked https://www.espruino.com/Reference#l_E_t­oString and https://www.espruino.com/Reference#l__gl­obal_atob but it does not tell.

  • You might be better creating a new jswrap_... C file and then using the _init callback on it to run stuff at boot. Or you can use the Espruino CLI to make a HEX file for .boot0 which you can then merge with the hex file?

    Also what is best way to store relatively big data like bitmaps, fonts so it is not copied to RAM but could be read directly from flash as constants?

    You could add them as files in Storage, or could put them inside a function written in a module (or just code file) in Storage. That way they stay in flash, but just get loaded into RAM when used.

  • But yeah, the whole atob('...') gets stored as-is, as text. So if you want it stored as binary you need to do Storage.write(... , atob('...'))...

  • So there is currently no build step to prepopulate storage area from files in some folder and merge it with the hex file? Possibly because I may often not want that as the memory is retained between flashing new espruino version?

    I checked gen/jswrapper.c how modules work and what happens with JSMODULESOURCES+=libs/js/SSD1306.min.js part and now I understand.

    const char *jswGetBuiltInJSLibrary(const char *name) {
      if (!strcmp(name,"SSD1306")) return "function k(b){b&&(b.he............
    

    So the module is simple string that gets parsed when require(string) is called. And then some function inside the module is just a variable pointing to its javascript source stored directly inside that module string in flash. And when called it is interpreted directly from flash memory.

    BTW, maybe I have figured our battery voltage level. Looks like the D2 pin that gets high when charging can be read as analog value. And D3 too. So when I do vmult=5/analogRead(D2) when charging, then with vmult*analogRead(D3) any time later I get some sensible number like 4.2 that goes down a bit with time. Over night it went down to 4.0 with serial console atteched.

    Also the Web IDE is very cool, I opened it on android tablet directly from the link (with Samsung Internet browser) and I could connect to the watch over bluetooth. The console is a bit slower than serial when going up in history and editing the line but it is still quite usable :-) This is so polished. Really good stuff.

    And I got very simple watch like functionality (press button to show time for 5 seconds) with short code like

    function mydrawfunction(){
    if (BTN1.read()) {
      g.on();g.drawText(new Date,0,12);g.flip();
      setTimeout(function(){g.off()},5000);
    }
    }
    setWatch(mydrawfuntion,BTN1,true);
    

    coded directly in the console. I wonder how long it would take to do this with Arduino C code :-)

  • So there is currently no build step to prepopulate storage area from files in some folder and merge it with the hex file?

    No, there isn't. It's just a few lines in a Bash file to do though.

    However, you could just do what's done for the Nordic Thingy: https://github.com/espruino/Espruino/blo­b/master/libs/nordic_thingy/jswrap_thing­y.c#L123

    So custom JS module, and some code to include it on initialisation.

  • Just to let you know that I also received Lenovo HX06 and sadly it is not Nordic chip despite having exactly same firmware, app, manufacturer, SWD pins in FCC ID photos as DS-D6. It is Dialog DA14585 - ARM Cortex M0 with 96KB SRAM and SPI flash. So just beware and don't buy it if you prefer Nordic chip.

    Otherwise it is quite hackable, I dumped ROM and SRAM (app is mirrored from SPI flash into 96KB sram at power on or resume from deep sleep) over SWD, there is also serial port on USB data pins, I poked around in HW registers so can see how it is setup, there is datasheet and tools and support forum on Dialog site. However I am not sure how good it is target for Espruino with 96KB SRAM and non memory mapped SPI flash. Spi flash is good for storage but cannot be used for executing native code from it without some paging/overlays. So the core interpreter code should fit into e.g. 64KB and the rest must be loaded from flash storage and run in 32kb ram. Is any other architecture similar to this? I noticed there was some effort at minimizing RAM usage at the expense of (memory mapped, executable) flash however this somehow goes into opposite direction. Perhaps if native code is minimized and rest is javascript parsed from SPI flash into ram it could somehow work and be usable for smaller/medium stuff.

    And BTW the bluetooth LE stack is in directly mapped 120KB ROM - a bit similar to nordic soft device so it does not take SRAM for code.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Espruino on your watch!

Posted by Avatar for Gordon @Gordon

Actions