-
As an update to this conversation, I had to make a minor change to the data buffer, changing it from ArrayBuffer to Uint8Array so that I could use buf.set(...) to push the older samples out. I presume that .set is going to be more efficient than anything I could have done programmatically.
So here are the relevant snippets from my working code:
var ELEMENT_SIZE = 10; // 10 bytes per data structure (uint32, 3 x int16) ... // define various setters and getters (could probably put this in a 'required' module DataView.prototype.setInt16i = function(v) { this.setInt16(this.idx,v); this.idx+=2; }; DataView.prototype.setUint32i = function(v) { this.setInt32(this.idx,v); this.idx+=4; }; DataView.prototype.getInt16i = function() { var v = this.getInt16(this.idx); this.idx+=2; return v; }; DataView.prototype.getUint32i = function() { var v = this.getInt32(this.idx); this.idx+=4; return v; }; DataView.prototype.flush = function() { this.idx=0; }; // buffer to store data samples var buf = new Uint8Array(100*ELEMENT_SIZE); // enough room for 100 samples var d = new DataView(buf.buffer); d.flush(); ... // A function to sample and store data into buffer function log() { // is the buffer full? If so, push it if (d.idx >= d.buffer.length-ELEMENT_SIZE) { // move all history values back by one element buf.set(new Uint8Array(buf.buffer,ELEMENT_SIZE)); d.idx -= ELEMENT_SIZE; } // sample the rest of the data and store it all in buffer d.setUint32i(Math.floor(Date.now()/1000)); d.setInt16i(Math.floor(NRF.getBattery()*1000)); d.setInt16i(temps[0]); d.setInt16i(temps[1]); } ... // This is a utility function that will output our data // in a form that can just be copy/pasted into a spreadsheet function output() { clearInterval(timer); var saveIdx = d.idx; d.flush(); while (d.idx < saveIdx) { var dt = d.getUint32i(); var bat = d.getInt16i(); var temp1 = d.getInt16i(); var temp2 = d.getInt16i(); var line = [dt,bat/1000,temp1/10,temp2/10].join(","); console.log(line+"\n"); i++; } d.idx = saveIdx; startSampling(); }
Note that the output() function might look a bit cumbersome, but that's mainly because I want to retain the current data set rather than output and flush the old stuff...
Thanks for everyone's help!
Tom -
@Gordon, I realized last night that I could probably extend Dataview, thanks for showing me how!! And, yes, I think Dataview is all I really need in this case.
Interesting approach, @asez73. Thanks for showing it. I don't think I would use that in my case, but it's good to have in the 'toolbox'. ;-)
Tom
-
-
Looking at Dataview, it seems as if it really doesn't matter if the array is a typed array or simply ArrayBuffer. The setting and getting is based on the type of the set or get and alignment is apparently not a concern. The key thing seems to be keeping the byte offset correctly tracking the data. But, I think you're right, Dataview makes the code cleaner rather than cryptic data conversion code.
Update: it's too bad the byte offset isn't post incremented by the size of the object that's set or get.
-
Thanks both. @Gordon, define 'not going to be especially fast'. In this particular case I'll have 10 second sampling interval and only need to manipulate two of the three elements if I store in a Uint32Array. The first value is used as-is, right from the array. The second two values would be 'deconstructed' from the next 32 bit value.
I'll take a look at Dataview. I had noticed it, but hadn't pursued it yet. Thanks for the tip. -
I was playing around with the simpleDataLogger and found that the Int16Array() used in the example is efficient (especially pushing new values onto the array), however, what is the recommendation if you have a tuple like: 32 bit timestamp and 2 16 bit temperature values, and want to maintain an array of these, to make a random example?
Being a 'C' programmer, I'd define a structure containing the 3 values and simply create an array of structures, keeping head and tail indexes to add to the array. Very efficient. I suspect those who have 'internal' knowledge and experience with Espruino would come up with a more efficient implementation than I would... :-)
-
Agreed it's risky without any confirmation from Nordic. I'll check in on that since the referenced article was based on nRF51 implementation. I would like to think that the newer device has a little more tolerance for 15uS latency. The fact that the non-nRF52 implementation of jsInterruptOff() DOES disable interrupts is interesting. In any case, I'll call this WIP and report as findings occur.
In practice it may not be an issue (at least for me). I would tend to isolate sensor readings to times when my node was not BT connected and then, start advertising once a reading was completed. That model doesn't work too well when connected to IDE, but, hopefully, that's not the operating case. -
I got a chance to look into this again and found, not surprisingly, that the OW search failures occurred when the read bit function was apparently interrupted. The image below is a screen cap of the logic analyzer showing the last operation of a failed search. In the photo you can see the read bit sequence took almost 101uS instead of the usual 68uS. While the time is within spec for a OW Time Slot, it indicates there was some interruption during the operation. My guess is that the processor didn't get to read the '0' bit in time and read a '1' bit instead. This would cause the search algorithm to fail since two '1' bits were read in a row (meaning no probes were responding).
So, with this theory, I modified the jshardware.c file in the targets/nrf5x/ folder to:static uint32_t prevPriMask=0; void jshInterruptOff() { [#if](https://forum.espruino.com/search/?q=%23if) defined(BLUETOOTH) && defined(NRF52) // TWS: replaced: disable non-softdevice IRQs. This only seems available on Cortex M3 (not the nRF51's M0) // TWS: With: turn off all IRQs after saving current priority mask prevPriMask = __get_PRIMASK(); __disable_irq(); //__set_BASEPRI(4<<5); // Disabling interrupts completely is not reasonable when using one of the SoftDevices. [#else](https://forum.espruino.com/search/?q=%23else) __disable_irq(); [#endif](https://forum.espruino.com/search/?q=%23endif) } void jshInterruptOn() { [#if](https://forum.espruino.com/search/?q=%23if) defined(BLUETOOTH) && defined(NRF52) //__set_BASEPRI(0); // TWS: Restore previous piority mask (probably 0) __set_PRIMASK(prevPriMask); [#else](https://forum.espruino.com/search/?q=%23else) __enable_irq(); [#endif](https://forum.espruino.com/search/?q=%23endif) }
Making this change the one wire search routine ALWAYS finds the two probes I have attached to the MDBT42Q. This seems safe for the one wire case since the interrupts are disabled for a short time in the read and write 1 cases (<= 13uS). The write 0 case, however, is longer (65uS), but a change to the OW code would improve this to 15uS if
} else { // long pulse jshInterruptOff(); jshPinSetValue(pin, 0); jshDelayMicroseconds(65); jshPinSetValue(pin, 1); jshInterruptOn(); jshDelayMicroseconds(5); }
Was changed to this:
} else { // long pulse jshInterruptOff(); jshPinSetValue(pin, 0); jshDelayMicroseconds(15); jshInterruptOn(); jshDelayMicroseconds(50); jshPinSetValue(pin, 1); jshDelayMicroseconds(5); }
This splits the 65uS delay into a critical 15uS delay and a not so critical 50uS adder.
I'm not going to generate a pull request for this until I play with it a while, although I'm not sure that jshInterruptOff() is used in that many places (Soft SPI, UART, Neopixel), it would still be worthwhile making sure there aren't any SoftDevice failures because of this...
Tom -
-
-
-
-
Interesting approach on the LIS2DH12. I like the fact that you can pass configuration settings in the options 'option'. It can eliminate a lot of superfluous setup functions.
Given this, if using SPI then cs could be a required option and the same connect function used - it's easy enough to distinguish whether the interface is SPI or I2C within the connect function. And, as you say, addr could be an option as you have it in LIS2DH12.
I'll mess around with it and see what I can come up with...
-
-
My board has an LIS3MDL magnetometer on it, but uses SPI, rather than the I2C interface used in the existing LIS3MDL device code.
Looking at the code, the IO interface is pretty well isolated from the LIS3MDL specific methods so I'm inclined to simply update that module so that, rather than requiring an I2C interface, the connect code accepts either an I2C or SPI instance and the read and write code simply tests for which interface is being used and calls the appropriate lower level IO function.
The alternative is to create a redundant device called something like LIS3MDL-S or something, but I think that's unnecessary since the first approach is not unwieldy.
Thoughts?
-
You're welcome!
I forgot to mention that your previous update to fattime.c, etc, was ok. It was my 'non-clean' build that kept it from updating (due the .h file that was also changed). The additional setting modification time during create (see #4) was not necessary UNLESS you did a fs.statSync immediately after opening a newly created file. Once the file was closed, the mod dates were updated properly.
-
As a newby to this forum, I was wondering what's preferred WRT various levels of development discussions.
As I look at the possible changes to various parts of Espruino, I see two distinct 'flavors' of changes. There are cases where someone might want to change, enhance, or clone existing modules, for example, and is looking for general advice on fairly high level implementations. The other case where, for example, looking at adding multiple SPI support to a platform, there are some pretty nitty-gritty changes required to a fundamental platform specific file like jshardware.c.
In these two cases, ISTM, that this forum is a good place to discuss the first case - general broad level changes, approaches, ideas. For the second case, it might be preferred that we open an issue on github and have the in-depth discussions there.
So, what's the recommendation?
-
-
Re NFC pins, I noticed that I failed to comment out:
'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPANÂ_WORKAROUND=1',
in the BOARD file. I'll rebuild and try pin D09 again.Well, that wasn't sufficient, however, adding:
'DEFINES+=-DCONFIG_NFCT_PINS_AS_GPIOS', # disable NFC pins to enable as GPIO
Did work. D09 and D10 are now freed up to be used as GPIO!
-
Hmmmm, wading deep into jshardware.c and I see that there is a bit of work to do to enable multiple SPI instances...
I think I'll save that for another day and just do individual device testing for the moment.Re NFC pins, I noticed that I failed to comment out:
'DEFINES+=-DHAL_NFC_ENGINEERING_BC_FTPAN_WORKAROUND=1',
in the BOARD file. I'll rebuild and try pin D09 again. -
Did you try an official build, or it was just what you built yourself? If you didn't make clean that'd have caused all kinds of issues.
But basically what I've done sets the creation time just fine, it just doesn't touch the modified time?
I was lazy and, since I had just built everything else, did a make and saw that fattime.c had been compiled. It was only after I changed ff.c and rebuilt, again not 'clean', that the behavior worked. Whether or not it would have worked without the change to ff.c and done a clean build, I'm afraid, I don't know. I'd have to remove the change to ff.c and rebuild it to know for sure whether that change was necessary.
Um, no, setting SPI to 3 did nothing useful other than remove the error when I tried to setup SPI2. No error, but also no joy as there was no SPI activity using SPI2 instance. If I simply changed the code to use SPI1, but the same pins, everything worked except when I tried to use D09 as a CSpin - that didn't work. In my Board file I had commented out the NFC section that defines the NFC pins, but apparently the HW still configures them. I had to move to pin D11 for the CSpin.
It makes sense that larger blocks would benefit from faster SPI, but I'm not there yet. 1MHz is ok for now. I can't use software SPI since theoretically I'll have concurrent SPI operations, but I have a feeling that, with this environment, I may be pushing the envelope a bit too much to achieve that.
I haven't heard of many people using it, but the current behaviour wasn't intentional - could you send a PR? :)
Sure, what's a 'PR'? :-)
-
-
Interesting results. Looking at the code, it seemed as every place the directory was modified, the DIR_WrtTime was updated EXCEPT in f_open. Here, only DIR_CrtTime was updated. So, figuring that creating the file was the same as 'modifying' it, I updated f_open (in ff.c) to update the DIR_WrtTime as well. The snippet at line 2494 in ff.c is:
dw = GET_FATTIME(); /* Created time */ ST_DWORD(dir+DIR_CrtTime, dw); ST_DWORD(dir+DIR_WrtTime, dw); //TWS: Added to init modification time too!
This worked, whenever I created a file, both modification time and creation time showed correct values in Windows File Explorer. If I updated a file that had a previous messed up file date, the modification date was updated to a proper date. I thought I had tested this case with the first build I did with your fattime.c update, but I guessed I missed this case.
So, it appears with this last change, the SD card file timestamps are good.
UPDATE: I think I may have done a partial build with your first changes and ff.c hadn't been rebuilt. That may explain why my first build didn't seem to fix the creation time problem.It appears, however, that fs.statSync() assumes that the file timestamp is GMT time, not local time and then adjusts it for localtime. (I'm GMT-0500). This is not tragic and might even be reasonable, although Windows and Linux assume local time.
Finally, as an FYI, I realized that the default clock rate was 100KHz, not 1MHz as I had thought. I changed this in my SPI1.setup({baudrate:1000000}) and the SD card write appeared faster, but I'll measure that shortly. 100KHz is probably a safe speed for old SD cards (250KHz should be the limit initially), but today's SD cards are much faster.
-
@Robin, Re Q1, I believe that Gordon is referring to the software 'port' of the code to the nRF52, not a specific hardware port. In the case of the MDBT42Q, the Espruino code only identifies one of each type of port even though the device supports up to 3 SPI, etc.
Q2: If NEO-6M is connected to any available SPI or I2C then it certainly can be used while BLE is active.
Q3: SPI or I2C ports can be arbitrarily assigned to any available GPIO pin. There are default pins, as defined in the Espruino Board package, but these can be overridden. Pin re-assignment occurs when you configure a SPI or I2C port as in: