-
At the moment it is all working, but I am polling the sensor 10 times a second to look for wrist twist, which is putting a bit of a strain on battery life, even when the watch isn't being worn. I'll look into the 'movement' interrupt, but am unsure if this is specific to BMA423. It won't hurt to try. The datasheet implies you can set a threshold movement in each axis to trigger the interrupt.
-
-
Thanks. I was just reading https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays when you replied! Much neater.
I suspect 103 was redundant, as it is really a pointer assignment, not a copy. -
This is current state of code - just enough to demonstrate working. Various things still need adding, e.g. events and scaling the accel vector.
/* Espruino module for BMA421/BMA423 motion sensor */ //delete the next line for module var exports={}; //create an instance - initialise if necessary (or just reconnect) function BMA421(_i2c) { this.i2c = _i2c; this.enabled = (this.checkstatus()==1); if (!this.enabled) this.initialise(); } BMA421.prototype.initialise = function() { //reset sensor this.writeReg(0x7E,0xB6); //disable power save this.writeReg(0x7C,0x00); this.loadConfig(); //Accl Enable this.writeReg(0x7D, 0x04); //Acc Config this.writeReg(0x40, 0b00101000); //Enable and Reset Step Counter - this will also enable power save this.resetSteps(); this.enabled = (this.checkstatus()==1); } //x and y are swapped - could remap in hardware BMA421.prototype.getAccel = function() { var acc = { x: 0, y: 0, z: 0 }; if (this.enabled) { var data = this.readBytes(0x12,6); acc.x = (data[3] << 8) | data[2]; if (acc.x > 32767) acc.x -= 65536; acc.y = (data[1] << 8) | data[0]; if (acc.y > 32767) acc.y -= 65536; acc.z = (data[5] << 8) | data[4]; if (acc.z > 32767) acc.z -= 65536; } return acc; } BMA421.prototype.getSteps = function() { if (this.enabled) { var steps = this.readBytes(0x1E,4); return (steps[3] << 24) + (steps[2] <<16) + (steps[1] << 8) + steps[0]; } else return 0; } //Temperature always seems to be 25 BMA421.prototype.getTemp = function() { if (this.enabled) { return this.readBytes(0x22,1)[0] +23; } else return -300; } //enables the step counter, disables the step detector BMA421.prototype.resetSteps = function() { this.writeReg(0x7C, 0x00);//Sleep disable var feature_config = new Uint8Array(70); feature_config = this.readFeatures(70); feature_config[0x3A + 1] = 0x34; this.writeFeatures(feature_config, 70); //Sleep Enable this.writeReg(0x7C, 0x03); } //burst write data to a register BMA421.prototype.writeReg = function(r,d) { this.i2c.writeTo(0x18,r,d); } //read a given number of bytes from a register BMA421.prototype.readBytes = function(r,l) { this.i2c.writeTo(0x18,r); return this.i2c.readFrom(0x18,l); } //read a single byte from a register BMA421.prototype.readReg = function(reg) { this.i2c.writeTo(0x18,reg); return this.i2c.readFrom(0x18,1)[0]; } //burst read bytes from the feature config BMA421.prototype.readFeatures = function(l) { this.i2c.writeTo(0x18,0x5E); return this.i2c.readFrom(0x18,l); } //burst write bytes to the feature config BMA421.prototype.writeFeatures = function(config) { this.i2c.writeTo(0x18,0x5E,config); } //Config file as a binary blob - write in chunks BMA421.prototype.loadConfig = function () { var buffer_size = 64; var config = new Uint8Array(buffer_size); //initialise config this.writeReg(0x59,0x00); for (var i=0;i<6144; i+=buffer_size) { config = require("Storage").read("bma421_config.bin",i,buffer_size); this.i2c.writeTo(0x18,0x5B, (i / 2) & 0x0F); this.i2c.writeTo(0x18,0x5C, (i / 2) >> 4); this.i2c.writeTo(0x18,0x5E,config); } //enable sensor features this.writeReg(0x59,0x01); } //LSB of status register is 1 for working BMA421.prototype.checkstatus = function() { return (this.readReg(0x2A) & 0x1F); } exports.connect = function(_i2c) { return new BMA421(_i2c); }
BTW, I have no experience of JavaScript, only C/Pascal/Delphi (and a little python), so I'm not convinced by the object model here at all.
-
-
Thanks. Tried to open whole file with Storage.readArrayBuffer but ran out of heap memory, so chunked instead. I will try a larger chunk to see what works, now I can see if I am successful.
I will upload code when I have tweaked it, and maybe submit somewhere as well. I am trying to write as a proper module, but I have zero JavaScript. It would help Pinetime owners, at least. I think only older P8s have BMA421.
-
Ok, going back to my original question:
I've taken the suggestion from @fanoush and just written a short module in javascript for the BMA421 from scratch. All it does it load the binary code to set up the step counter and then reads steps and xyz acceleration. It seems to all work, for not many lines of code, so I'm happy with that as an approach.
My eventual solution was just to turn the bytes from the config file into a binary file that I upload to Storage. I then read it in chunks (I don't think this really uses memory?) and write each chunk to the sensor. Everything else is very similar to the example code in the ATCWatcH project. The datasheet has several errors / omissions in it, but it helped to get started.
BMA421 doesn't have twist interrupts, so I will have to check that on an interval if I want to turn on the screen that way. -
-
-
-
-
Thanks @fanoush. The flash module appears to work, so I doubt anything is wrong with SPI interface. I know storage module does wear levelling, so I assume it caches its state somewhere, and maybe that has become corrupted. Is there a way to wipe the storage entirely and start again? I tried, but managed to wipe the bootloader instead!
-
I am running the P8 SPIFLASH version of Espruino by @fanoush on a similar smart watch, but I have managed to get the Flash memory into a strange state whereby I cannot write to it with the Storage class.
The Flash is writeable - I can update the bootloader and flash Espruino builds to it. I can also erase, read and write flash pages, but any attempt to write a file fails with a timeout. Rebooting doesn't help. This device has been working well, and I had been reading and writing to Storage, but it seems that something is out of alignment!
process.memory()
={ free: 2490, usage: 75, total: 2565, history: 50,
gc: 0, gctime: 3.69262695312, blocksize: 16, stackEndAddress: 536928984, flash_start: 0,
flash_binary_end: 396828, flash_code_start: 1610612736, flash_length: 524288 }
require("Flash").getFree()
=[
{ addr: 397312, length: 86016 }
]
require("Storage").getFree()
=4194304
require("Flash").read(8,397312)
=new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255])
require("Flash").write("hello",397312)
=undefined
require("Flash").read(8,397312)
=new Uint8Array([104, 101, 108, 108, 111, 255, 255, 255])
require("Storage").list()
=[ ]
require("Storage").write("test.dat","hello")
Uncaught InternalError: Timeout on jshFlashWrite
at line 1 col 44
require("Storage").write("test.dat","hello") -
Thank you both - it looks like that has worked. I think that I accidentally wrote to the register which switched the sensor to SPI mode, which is why I couldn't see it again.
Fortunately, a battery discharge followed by 48 hours in a drawer has reset the sensor, and it now appears to be working again. -
Thanks for the hints - I will explore further. It looks like SPI mode could be the reason. Writing to bit 1 of register 0x70 disables I2C. There are many 0x70 bytes in the config file, so I could easily have done that.
It looks quite hard to overwrite the NVRAM, you need a distinct sequence of bytes (or a byte and a bit) that I can't find anywhere in my config file.
I don't think it has changed I2C address - that would require rewiring.
I have tried to flatten the battery by running a firmware (no sleep or shutdown) until it dies and won't power on. However, it is possible that there is enough power in the battery to keep the sensor alive. I haven't taken the watch apart.
I found a poster on the Borsch forum that had a similar problem, and solve it by a power cycle, so I will try running down the battery again and leaving for a few days (or weeks?) to see if that helps to flatten it totally! -
I have tried... and failed. The watch still works, but my efforts to send config bytes to the BMA421 have somehow put it the chip into an unreponsive state. I suspect I have overwritten its NVRAM somehow, as it won't even respond to a reset command.
Even powering down the watch doesn't seem to bring it back, and any reads from the BMA421 registers just return 0xFF.
Any ideas?
-
It's a bit of mix at the moment. I am using your P8 SDK12 bootloader and the 2.09 firmware from @jeffmer. I update with the DaFlasher, but it does use the nordic bootloader, SD3.0. Is there an Espruino bootloader for this device? Would there be any advantage?
Unfortunately, the M11 button is on D20, not D17, so the P8 bootloader would need rebuilding for this device if I want to use this strategy.
I might spend some time reading the driver as you suggest. I have a background in C, but not much javascript - most of it is definitions and interfaces, there is not much actual code because the magic happens inside the black box. I would be happy to submit a module at the end of the process if it works.
I'm not going to risk rebuilding the firmware unless it becomes essential. -
Thanks for that (sorry for the duplicate DM, thought it better to ask in forum). There is some simpler code from Daniel Thompson which might be easier to include. I think the BMA421 needs priming with a binary blob before initialising, which takes more steps.
I'm more nervous about modifying the 'firmware' Espruino build at the moment, because I have no way to recover a bricked device. Rewriting in JS would be time consuming, and also make it harder to keep up with any changes upstream. -
I have been using Espruino on an Gokoo M11 watch, with broadly similar specs to a Pinetime or older model of Colmi P8.
It includes a BMA421 accelerometer and an HRS3300 heart rate monitor. The former also offers built in step counter as seen at https://www.youtube.com/watch?v=pqnowZd2_KQ
I can use this C++ code library successfully (on the ATCWatch Arduino firmware) to step count and to wake the watch.
Is there any way to implement this for Espruino - or do I need to create my own build and compile the driver into it?
Maybe it would be better to correct the time on the hour (e.g. 00 minutes past), to make sure it happens regardless of the current app load time?