fanoush / ds-d6

stuff for Desay/MPOW DS-D6 nRF52832 bracelet
163 stars 28 forks source link

Getting accelerometer and HR sensor working #2

Open 54696d21 opened 5 years ago

54696d21 commented 5 years ago

Hello fanoush,

I followed this repo for quite some time and now wanted to try it out myself. I tried to flash the device (Mpow DS-D6 from gearbest, I have 3 units) with your espurino DFU file but I can't get it to work. I'm on Ubuntu 18.10 with python2 and nrfutils 0.5.3. So far I tried two different USB to serial adapters, I tried to swap the RX and TX pins for flashing. Both serial adapters I tried are recognized by the computer with "ls /dev/ttyUSB*". I tried both DS-D6 devices. I tried 5V and 3V3. I tried with +5V VCC connected to the device at time of flashing and without (the same with 3V3). I measured the wires with a multimeter and built a second adapter (for the other USB to serial). I tried your DFU file and created my own with the provided command from the wiki. As far I can tell I didn't damage the devices in anyway. They still charge fine and run their original firmware and I have an untouched spare one.

I always get the same Error "Serial port could not be opened" (please see the attached image).

FJIMG_20190330_171312 FJIMG_20190330_170250 One on my adapter setups

Thank you for your time and effort of documenting your progress and thank you in advance for your help.

Tim

54696d21 commented 5 years ago

I'm sorry :) I solved the issue. beeing root is important :) so instead of nrfutil dfu serial -pkg D6-DS.zip -p /dev/ttyUSB0 I had to use sudo nrfutil dfu serial -pkg D6-DS.zip -p /dev/ttyUSB0 Could you add that maybe to the wiki?

fanoush commented 5 years ago

I'm sorry :) I solved the issue.

That's great so no need to say sorry :-)

beeing root is important :) so instead of nrfutil dfu serial -pkg D6-DS.zip -p /dev/ttyUSB0 I had to use sudo nrfutil dfu serial -pkg D6-DS.zip -p /dev/ttyUSB0 Could you add that maybe to the wiki?

I see, so your user probably doesn't have proper rights to use serial ports. But that's typical if you never tried using serial ports before so it is good tip to remind to check for this. On some distributions you need to be member of dialout group, just google it - linux cannot access serial port. I'll add it to wiki.

So did you flash Espruino too, did it work?

fanoush commented 5 years ago

btw, I see you connected 3.3V to usb power - red wire, if you want to charge the watch connect 5V there instead of 3.3V, it is supposed to be charged via normal USB port after all. Hovever rx,tx should still use 3.3V logic, if your adapter can switch rx,tx to 5v logic too, don't do that (not sure what the jumper on 5v to vcc does).

54696d21 commented 5 years ago

So did you flash Espruino too, did it work?

Yes, I played a bit with REPL and uploaded your gist. The OLED works. But it is the first time I use espurino and don't really know it yet.

I see, so your user probably doesn't have proper rights to use serial ports.

Mostly when using linux I use the Raspberry Pi and the rights management seems to be a bit different on normal desktop linux. I also didn't thought about it, something wrong with the hardware seemed way more likely.

Concerning the 3V3, I know that USB uses 5V but my adapter is designed (according to the seller) in a way that you are supposed to short the VCC pin with the unused positive voltage pin. So I tried that :)

fanoush commented 5 years ago

That's strange about the adapter. I would guess you select voltage you want by connecting such voltage pin to vcc. Then vcc becomes this selected voltage and drives voltage of rx and tx. I see similar items on ebay - dual output ch340 adapter and the description is not very clear but does not imply what you say. Normally tx is high if not sending data so if you measure tx to gnd and see 5v then you selected 5v voltage levels.

nrf52832 gpio is not 5v tolerant so you may damage it if you use it longer like this even if it appears to work see e.g. https://devzone.nordicsemi.com/f/nordic-q-a/37006/nrf52832-maximum-gpio-input-voltage

54696d21 commented 5 years ago

I just measured it with my multimeter, the RX/TX pins are always at about 3,5V when high. Probably 3,5V is enough to be a logical 1 in most 5V setups. There are also similar usb to serial adapters with a physical switch that might not have that issue, but it works fine for me.

Btw do you have some working code example for retrieving the accelerometer data in espurino? When I use w.accRead(0x06, 6) from your gist the return is new Uint8Array([128, 162, 0, 32, 0, 0]) and all but the first two ints are always identical (yes, I'm moving the device 😄 ) and that seems to result in w.accelCoords() returning nonsense values (y is than always 8192 and z is always 0).

fanoush commented 5 years ago

yes, it needs initialization, otherwise it works only first time after flashing from original firmware which does the initialization, will try now and post more info, it is in the datasheet, also the interrupt needs to be enabled e.g. for detecting taps and doubletaps from various directions via setWatch

54696d21 commented 5 years ago

how far are the kx022 and kx023 identical?

54696d21 commented 5 years ago

because here is a c library for the kx022 that might be quite easy to port https://github.com/goran-mahovlic/nRF51_ID107_libs/tree/master/libraries/KX022

fanoush commented 5 years ago

I guess they are mostly identical, i followed easy guide, google "kionix AN041 getting started" there are described all typical use cases and how to enable it via registers http://www.bdtic.com/download/kionix/Semiconductor/AN04120Getting20Started20with20the20KX02320and20KX022.pdf and http://kionixfs.kionix.com/en/datasheet/KX023-1025%20Specifications%20Rev%2012.0.pdf

fanoush commented 5 years ago

so this worked for me now (according to guide)

w.initAccel()
w.accWrite(0x18,0x40)
w.accWrite(0x1b,0x02)
w.accWrite(0x18,0xc0)

then w.accelCoords() starts to print sensible values

fanoush commented 5 years ago

and to enable interrupt and tap/doubletap features:

w.accWrite(0x1c,0x30) - enable kx023 interrupt, it is wired to pin D15

setWatch(function(s){if (s.state){w.accINSDump();console.log(s,w.accRegDump(0x17));}},D15,true)

this enables watching interrupt and dumping interesting values, then this enables tap,doubletap detection

w.accWrite(0x18,0x44)
w.accWrite(0x1f,0x04)
w.accWrite(0x18,0xc4)

then start hitting watch from various directions and you should see watch being triggered and dumping different values depending on direction you tap or double tap from

54696d21 commented 5 years ago

Thank you very much :) And do you have something similar for the HR sensor (illuminating the LED and reading back the brightness value)?

54696d21 commented 5 years ago

I found a datasheet: https://pdf-datasheet-datasheet.netdna-ssl.com/pdf-down/P/A/H/PAH8001EI-2G-PixArt.pdf I'm highly impressed with the complexity of what I believed to be a phototransistor and a led 😅

fanoush commented 5 years ago

No, nothing working fo HR sensor yet, only what is written in hardware wiki page. You need to set pin 26 high, this enables HR sensor and green led starts to work immediatelly. Also once enabled you can use address 0x6b for communication, for that you need to reconfigure i2c to use different pins. When I tried and forgot to enable device via pin 26 espruino got stuck in i2c read (indefinitely?) so I needed to reset it via reset pin. I'll try again if there is another way to recover from this, I did not know much about espruino too when I first tried this. Also I did not check datasheet how to communicate with it yet.I mainly wanted to have smart watch with alarm clock and notifications so I did not focus on step counting and heartrate. But please go for it if you care.

fanoush commented 5 years ago

Oh thanks for the datasheet. There is a nice list of registers there. Most probably there is also some interrupt pin connected to signal that measurement is finished, there are not that much free pins left , maybe 27? Or 9-12.

54696d21 commented 5 years ago

unfortunately the datasheet doesn't seem to be complete, I found some C code for the sensor on github that writes to 0x27-0x29 and those are not named in the datasheet. That code also seems to access some high frequency modes up to 1000 hz that the datasheet doesn't talk about

fanoush commented 5 years ago

Hi, if you get something working please don't hesitate to share :-)

54696d21 commented 5 years ago

here is some very basic code to simply read something from the hr sensor (simply the first 8 bytes (8 is an arbitrary value) in this exmaple); its written in a way to prevent the device from getting stuck by ensuring the hr sensor has power before reading measurements.

(tip: enable the display when experimenting with the hr sensor, so in case the device gets stuck, the battery gets drained more quickly and don't fully charge the device)


hrIsInit = false; // assume hr sensor wasn't used before

function initHr(){
digitalWrite(D26, HIGH); // switch on the hr sensor on hardware level
i2c = I2C1; //instanciate i2c class
i2c.setup({scl:7, sda:8, bitrate:400000}); //define i2c connection profile
}

function readHr(readLength){
if(hrIsInit == true){ // check if hr sensor was initialized
print(i2c.readFrom(0x6b, readLength)); // read the number of bytes from the devices i2c adress
}
else{ // if the hr sensor hasn't been initialized, initialize it
print("init hr sensor");
initHr(); // initialize hr sensor
hrIsInit = true; //i2c now has been initialized
readHr(readLength); //call this function again to print out the first measurement
}
}

for (i = 0; i<10; i++){ // read hr sensor 10 times
readHr(8); // read 8 bytes from heart rate sensor; 8 bytes is an arbitrary value for testing purposes
}
scientistnobee commented 5 years ago

Hi @54696d21

I flashed your code into the D6. The green Led starts blinking continuously. However, if I touch the watch, instead of blinking it is just brighten continuously. In your code, I didn't find any section that mentions about stopping the blinking. By the way the link to the PDF is broken. So I am attaching here the PDF link. https://www.thaieasyelec.com/downloads/EFDV423/PAH8001EI-2G.pdf

Finally, I want a simple measurement from photodiode when LED is shining. The code in the above post is giving some matrx of values. I didn't understand, how this can be correlated with the reflectance values. See for example, the following array. new Uint8Array([48, 211, 2, 0, 0, 184, 2, 13])

54696d21 commented 5 years ago

Hi, the HR-sensor seems to have an autoexposure feature which is activated by default; that should be the reason for the described behavoir. My problem with my code from above is that the returned array is always constant (even across devices).

fanoush commented 5 years ago

My problem with my code from above is that the returned array is always constant (even across devices).

Sorry if this sounds stupid but in the code I don't see you are writing anything. Typically over i2c you first write register address you want to read/write and then read or write. Like the accelerometer code function accRead(reg,len){i2c.writeTo(0x1f,reg);return i2c.readFrom(0x1f,len);} and then exports.accelCoords=function(){coords=new Int16Array(accRead(0x06,6).buffer);return {x:coords[0],y:coords[1],z:coords[2]}; }; so the method accelCords first writes 0x06 to i2c to let it know you read coordinates from register 0x06 and only then there is read of 6 bytes. Your readHR is just calling i2c.readFrom.

However I didn't study the datasheet too much to see if it makes sense to just read something so sorry if it is just noise. I guess I need to finally try this but as mentioned I don't find heart rate reading that much useful and there is lot of other more interesting stuff.

fanoush commented 5 years ago

And when checking the datasheet there are two banks of registers and by default registers are write protected which can be turned off by writing 'address 0x09 with 0x5A' and banks are switched via writing bank number to 0x7f register.

So to just read e.g. the ID from register 0x00 one should call i2c.writeTo(0x6b,0);return i2c.readFrom(0x6b,1); and it should return 0x30

And if something writable should be written one should unprotect writing via i2c.writeTo(0x6b,0x09);i2c.writeTo(0x6b,0x5a); and then one could write e.g. register 0x05 - operation mode via i2c.writeTo(0x6b,0x05);i2c.writeTo(0x6b,0xb8);

in fact the array [48, 211, 2, 0, 0, 184, 2, 13] looks like sequential read from address 0 because 48=0x30 and 211=0xD3 which is two bytes of ID at addresses 0,1 and 184 is indeed operation mode 0xd8 at address 0x05 and so on.

54696d21 commented 5 years ago

@fanoush yeah, I know about the I2c write thing (found out later; I didn't get it initially), but I haven't had success with that thing either. I think overread the write protection thingy; I'll try that

fanoush commented 5 years ago

the basic communication works for me. with similar functions to accRead/Write/regDump just using 0x6b instead of 0x1f I can unlock writing and write to registers, reset it, power down it, change sleep mode timings, change blinking rate when in sleep2 mode. However I don't understand various Algo_A,B,C registers to get the heartrate. Look like this is job of the blue rectangle called PixArt Algo Library to compute some sensible information from the data. examples reset i2c.writeTo(0x6b,[0x06,0x82]) powerdown i2c.writeTo(0x6b,[0x06,0x0a]) powerup i2c.writeTo(0x6b,[0x06,0x02]) unlock writing i2c.writeTo(0x6b,[0x09,0x5a]) (it sticks until reset) fastest switch to sleep1,2 modes i2c.writeTo(0x6b,[0x0b,0x00]) (default 0x73) shortest blink in sleep2 mode i2c.writeTo(0x6b,[0x0c,0xfc])

fanoush commented 5 years ago

there seems to be pah8001 driver here https://github.com/blockswearables/blocks-module-heart-rate/ https://github.com/blockswearables/blocks-module-heart-rate/blob/master/pah8001/pah8001.c

sebi5361 commented 4 years ago

Based on the PAH8001EI-2G datasheet we need a piece of code called "PixArt Algo Library" to process the data retrieved from the HR sensor. I hope we can get access to this library.

fanoush commented 4 years ago

while this chip and that library may be quite sophisticated, it was figured out by atc1441 that basically one register value can be used to compute good approximation of heart rate, see his code here https://github.com/atc1441/D6-arduino-nRF5/blob/master/libraries/D6Examples/examples/HeartRateTest/HeartRateTest.ino#L236

dariosalvi78 commented 4 years ago

Hello, thanks for the great project!

I may have found the precompiled library for the heart rate sensor here.

dariosalvi78 commented 4 years ago

Hi, it looks like atc1441 in the end uses the proprietary library too, see this video and the associated repo (particularly this Arduino sketch) .

I have found some repos on Github which might contain the right version of the lib. See: here or here or here.

Do you have plans to try to include this lib into your code? I guess it is needed to determine the HR (unless you want to do it from raw PPG). I wouldn't mind helping you, but I am not a very experienced embedded systems programmer.

Thanks!

fanoush commented 4 years ago

Do you have plans to try to include this lib into your code?

I am not sure about linking it directly into espruino binary because of license and floating point linking convention. It is doable but I am not sure it is worth the trouble. I'd better have some open source code for raw PPG data. In fact the bangle.js watch does it from raw data too so some code is already there in Espruino. As fo the closed libraries I'd first try to link and load it dynamically via espruino inline C/E.nativeCall (see e.g. this how it can be done) than linking the library directly into firmware.

sebi5361 commented 4 years ago

Linking and loading the library dynamically would be great indeed. And using a locally hosted compiler with floating point support (rather than the default Espruino server C compiler that does not support it) could be nice as well. I am quite disappointed by the performances of the HR sensor on the Bangle.js and I have great hope on the DS-D6 with this embedded library. I really don't have the expertise to program such a thing but it would be great if you plan to do it.

dariosalvi78 commented 4 years ago

yes, please fanoush, show us the way!

PS: there are many algorithms in literature for heart rate extraction from PPG, some also provide open source implementations, though most are in python or Matlab. I haven't seen anything validated in C and for real time, embedded applications. For the moment, I think it would be just easier to use the pre-compiled stuff, which also has the advantage of making use of accelerometry (maybe to compensate motion?). On a side note, I am seriously thinking of starting an open source project for signal processing for open wearables. We have some code (Java and python) for step counting for the moment.