Avatar for Kaoron

Kaoron

Member since Aug 2023 • Last active Sep 2023
  • 1 conversations
  • 15 comments

Kospet ROCK owner. All-purpose tinkerer.

Most recent activity

    • 17 comments
    • 1,621 views
  • in Projects
    Avatar for Kaoron

    I do use the watchdog already, a lot of my early code comes from your demo and jeffmer's repo.

    However what I'd like to do is to create a few graphics routines in C to avoid the overhead from the interpreter. At the moment I'm drawing overlays upon overlays to achieve masking, which is pretty suboptimal.

    Gotta put my head into custom builds but that's a lot of foreign stuff to take in :).

    Edit : That jswrap_graphics.c file is brutal.

  • in Projects
    Avatar for Kaoron

    A few updates :

    • Added some device drivers (accel, touch, buzzer)
    • Added a context manager and a few utils to Graphics
    • Started implementing an execution manager module, the objective is to have transition management between apps
    • Added two simple clock apps (one is a reimplementation of slopeclock, not yet animated)

    I'm not trying to be compatible with Bangle.js, maybe I'll end up implementing something very similar, maybe not.

    I'd like to tinker a bit with low-level graphics eventually in order to implement some efficient masking, but I'm kind of afraid to brick my device in the process.

  • in Projects
    Avatar for Kaoron

    One weird thing (to me, maybe it's normal) is that the I2C device needs to be 'primed' by a first read (write?) to its registers to have them initialized. The first read seems to be garbage.

    Here's attached a dump of the registers after priming, with annotations.

  • in Projects
    Avatar for Kaoron

    Le led driver current control implementation is described like this in the 3603 datasheet :

    LED current range 3.2 to 204.8 mA
    LED current resolution 6 Bits
    ...
    The device has one internal current DAC 3bits output current control.
    The output current control though register 0x0Bbit2:bit0for phase1
    to phase3.

    And in the driver :

    // LED Driver current
    HRS_LEDDR_MSB : 3,
    PS1_LEDDR_MSB : 7,
    LEDDR_LSB : 7,
    ...
    dev.writeByte(0x0b, c.PS1_LEDDR_MSB<<4 | c.HRS_LEDDR_MSB);
    dev.writeByte(0xc4, c.EXTPD_SEL<<7 | (c.LEDDR_LSB&0x07)<<4 | 0x0f);
    

    Look vaguely consistent. Shame that the 0xC4 register isn't documented (among others).

  • in Projects
    Avatar for Kaoron

    This is the datasheet for what seems to be an earlier iteration of the same chip architecture. Register addresses are not accurate and some are missing, but it looks overall closer to the 3603 than the 3605, 3300 or 3313 and provides a bit more info.

    Edit : scratch that, it's basically the same as the 3313.

    http://www.synercontech.com/Public/uploa­ds/file/2019_10/20191020152311_81180.pdf­

  • in Projects
    Avatar for Kaoron

    Here's my "driver" at this point, if anyone wants to give a shot at understanding what's missing.

    // Driver for HX3603, see https://github.com/moyitai/5.26-sdk-1.0.­2--integrate/blob/master/apps/common/dev­ice/hr_sensor/hrs3603.c
    
    var dev = wOS.i2c.bind(0x44); // device I2C address
    
    functionHX3603 (){
    var conf_hrs = {
      // Clock
      SAMPLE_RATE : 25, // Hz
      PRF_CLK_NUM : 32000/SAMPLE_RATE,
      // PS1 PRF [0-255]
      PS1_INTERVAL_I2C : 64, // NOTE : 3600 datasheet reco
    
      // Phase Enable
      HRS_ENABLE : 1,
      PS0_ENABLE : 0,
      PS1_ENABLE : 1,
      TS_ENABLE : 0,
    
      // Oversampling rate 0:128 1:256 2:512 3:1024
      HRS_PS0_TS_OSR : 3,
      PS1_OSR : 3,
    
      // LED & pulse delay
      HRS_LED : 1,
      HRS_CKAFE : 1,
      HRS_CKAFE_CYCLE : 50,
    
      PS1_LED : 1,
      PS1_CKAFE : 1,
      PS1_CKAFE_CYCLE : 50,
    
      // LED Driver current
      HRS_LEDDR_MSB : 3,
      PS1_LEDDR_MSB : 7,
      LEDDR_LSB : 7,
    
      // LES SEL ?
      LEDSEL_HRS : 1,
      LEDSEL_PS1 : 2,
      FORCE_LEDSEL : 0,
    
      RESET_CYCLE : 5,
    
      // DC signal canceling
      DCCANCEL_HRS_IDAC : 0,
      DCCANCEL_PS1_IDAC : 0,
    
      // EXT PD (power delivery) // Undocumented
      EXTPD_SEL : 1,
      EXTPD_PS1 : 1,
      EXTPD_HRS : 1,
    
      // TIA 1 | Integrator 0
      TIA_PS1 : 1,
      TIA_HRS : 1,
      // TIA feedback resistor
      RFSEL_HRS : 6,
      RFSEL_PS1 : 1,
      // Integrator Capacitor ?
      HRS_PS0_TS_INT_CAP : 15,
      PS1_INT_CAP : 15,
    
      // Clock polatiry ?
      ADC_DATA_CLK_POL : 3,
    };
    var mode_hrs = 1;
    var hrs = {
      dev:dev,
      disable: ()=>{
        dev.writeByte(0x01, 0x01); 
        dev.writeByte(0x02, 0x01); 
        delay(50);
        dev.writeByte(0x1a, 0x13); 
      },
      enable: ()=>{
        var c = conf_hrs;
        dev.writeByte(0x1e, 0x00); 
        // Clock config
        dev.writeByte(0x10, c.PRF_CLK_NUM & 0xff); 
        dev.writeByte(0x11, (c.PRF_CLK_NUM & 0x0f00)>>8); 
    
        // Phase enable and OSR
        dev.writeByte(0x01, c.TS_ENABLE<<4 | c.PS0_ENABLE <<3 | c.HRS_ENABLE<<2 | c.HRS_PS0_TS_OSR);
        dev.writeByte(0x02, c.PS1_ENABLE<<2 | c.PS1_OSR);
        
        // LED enable and AFE clock enable
        dev.writeByte(0x03, c.HRS_LED<<3 | c.PS1_LED<<2 | c.HRS_CKAFE<<1 | c.PS1_CKAFE); // 00001111 // ?:0000|HRS_LED:1|PS1_LED:1|HRS_CKAFE:1|P­S1_CKAFE:1
        dev.writeByte(0x08, c.HRS_CKAFE_CYCLE); // hrs ledontime, 0~255,  led on time = 4*hrsckafe_cycle*0.382us
        dev.writeByte(0x09, c.PS1_CKAFE_CYCLE); // ps1 ledontime, 0~255,  led on time = 4*ps1_ckafe_cycle*0.382us
    
        // clock reset config 
        dev.writeByte(0x04, c.RESET_CYCLE); // 00000101 // 3 bit RESET CYCLE sel //  (2^(reset_cycle+1))-1*/
    
        // PRF
        dev.writeByte(0x1c, c.PS1_INTERVAL_I2C); // PS1 PRF 0-255 - 25 mean 1s
        dev.writeByte(0x05, c.DCCANCEL_PS1_IDAC); // offset DCCANCEL PS1 
        dev.writeByte(0x06, c.DCCANCEL_HRS_IDAC); // offset DCCANCEL HRS/TS/PS0
    
        dev.writeByte(0x07, c.LEDSEL_PS1<<4 | c.FORCE_LEDSEL<<3 | LEDSEL_HRS); // 00100010 // ?:0|LEDSEL_PS1:010|FORCE_LEDSEL:0|LEDSEL­_HRS:001 
        dev.writeByte(0x0a, c.PS1_INT_CAP<<4 | c.HRS_PS0_TS_INT_CAP); // 11111111 // INTCAPSEL Integrator gain ?
    
        dev.writeByte(0x0b, c.PS1_LEDDR_MSB<<4 | c.HRS_LEDDR_MSB); // 01110011 // PS1:0111|HRS:0011 LEDDR [0-7]
    
        dev.writeByte(0xc4, c.EXTPD_SEL<<7 | (c.LEDDR_LSB&0x07)<<4 | 0x0f); // 11111111 // extpd_sel | leddr_lsb | 0x0f
    
        dev.writeByte(0x0d, c.RFSEL_HRS <<4 | c.RFSEL_PS1); // 01100001 // rfsel_hrs | rfsel_ps1
        dev.writeByte(0x0c, c.RFSEL_PS1); // 00000001 // rfsel_ps1 feedback resistor ?
        dev.writeByte(0x0f, 0x00); // 
        dev.writeByte(0x0e, c.EXTPD_PS1<<3 | c.EXTPD_HRS<<2 | c.TIA_PS1<<1 | c.TIA_HRS); // 00001111 extpd_ps1:1 | extpd_hrs:1 | tia_ps1:1 | tia_hrs:1
    
        dev.writeByte(0x1b, 0x7f); //
        dev.writeByte(0xc2, 0x10); // Self test
        dev.writeByte(0xc3, 0xff); //
        dev.writeByte(0x18, c.ADC_DATA_CLK_POL); // adc_data_clk_pol
    
        dev.writeByte(0x1a, 0x12); // 
        dev.writeByte(0x13, 0x02); // enable mode (ps1|hrs)
        dev.writeByte(0x12, 0x50); //
        dev.writeByte(0x20, 0x20); // close fifo int
      },
      read_hrs:()=>{
        var data = dev.readBytes(0xa0,12);
        var p0 = (data[0]) | (data[1]<<8) | (data[2]<<16);
        var p1 = (data[3]) | (data[4]<<8) | (data[5]<<16);
        return { als:[p0, p1], hrs: (p0>p1)?p0-p1:0};
      }
    };
    return hrs;
    }
    

    And here's the module I use it with

    var correlator = function(cspan) {
      var CMIN = 7; var CMAX = 37; var NSLOT = 128;
      var buffer = new Array(NSLOT); var next=0;
      cspan = cspan || 1;
      return {
        put: (v)=>{ buffer[next] = v; next = (next+1)%NSLOT; },
        get bpm(){
          var minCorr=0x7FFFFFFF;
          var span=0;
          for (var c=CMIN; c<CMAX; c++) {
            var s = 0; var a = (next-c*cspan) % NSLOT; var b = next;
            for (var i=0; i<(NSLOT-CMAX);i++){
              var d = buffer[b]-buffer[a];
              b = (b+1)%NSLOT; a = (a+1)%NSLOT;
              s += d*d;
            }
            if (s<minCorr) {minCorr = s; span = c;}
          }
          return span == 0 ? 0:(60000/(span*40));
        }
      };
    };
    
    function hrm(){
      var corr = correlator();
      var iv;
      var hrm = {
        read:()=>{
          var v = wOS.hrs.read_hrs();
          corr.put(v.hrs);
        },
        start: (record)=>{
          if (iv) return;
          wOS.hrs.enable();
          iv = setInterval(hrm.read, 40);
        },
        stop: ()=>{
          if (!iv) return;
          iv = clearInterval(iv);
          wOS.hrs.disable();
        },
        get bpm() { return corr.bpm(); },
      };
      return hrm;
    }
    
  • in Projects
    Avatar for Kaoron

    I guess I'm an idiot. Readings were all over the place because I was reading noise. Led doesn't seem to turn on.

  • in Projects
    Avatar for Kaoron

    Thanks!

    Edit: deleted wild guesswork, misread the datasheet. I don't know what I'm doing.

    On the algorithm part, I didn't find the code using that driver (it seems to be dead code, replaced by a driver for the HX3605), but I expect to be able to use what jeffmer has for processing the signal.

  • in Projects
    Avatar for Kaoron

    I managed to translate part of the driver mentioned above to js and get some readings. BPM values are all over the place, but that's a start...

Actions