-
My guts tell me that Espruino is too slow to actually make it that fast...
There are ways to make it faster by hooking directly into the interrupt... but because JS
interpretation takes so much time you always miss hits... Did you check the error flag? I guess the internal event queue overflows within no time.
Do you want to measure how many pulses happen in a second?
-
addendum re protocols - google it. Used it before sockets were available. Roundtrip times were amazing despite the http overhead. Almost real time I could control the (trojan horse modified) firefox browser... or less intrusive, talk to single page app in another browser over the internet via a nio tomcat / getty server in the internet.
- Bayeux protocol / CometD project
- BOSH
- Bayeux protocol / CometD project
-
If your setup works for the uplink, could a parallel - independent - same-setup work for the downlink as well?
Of course, this means doubling the BLE devices per sensor location and make the pairs talk to each other as well as on the data collecting side a peer for broadcasting the commands?
It reminds me of the old days - not so long ago - when browser communication was kind of a one-way initiation. Long polling on separate connection and related protocols were the answer to overcome the technical limitations.
-
@user142416, enjoy the updated code (post #29... could not test it yet, though... if it does not work, used what is in post #25).
-
This code tried to become a module in http://espruino.com/modules a long time ago - at a time when I was not yet familiar enough to pull that thru. Even though the code is now pretty 'old', it is still in demand. Therefore, as a first step, I make a condensed version (w/ examples) that you can copy into your application to take advantage of SWButtons without having to get rid of all the noise by the comments. Second, I'll add files you can copy into your sandbox project modules folder to use the code as real module using
var SWBtn = require("SWButton");
.NOTE for previous users: I switched to
.enable()
from.disable()
.The example code does the following:
- uses BTN (or BTN1 when more than one button is on the board)
- short press toggles the red LED
- long press toggles the green LED
- short-long press sequence turns the red LED on
- short-short press sequence turns the red LED off
- long-long press sequence turns the green LED on
- long-short press sequence turns the green LED off
First, in-line module code w/ example:
// SWButtonInline.js // 2022.0410 - (c) allObjects; var SWBtn = // inline module emulation of // var SWBtn = require("SWButton"); (function(){ var exports = {}; // module begin: var SWBtn = function(f,b,e) { this.f = (f) ? f : function(){}; this.b = (b) ? b : BTN1; this.t = null; this.k = null; this.w = null; this.enable(e); }; SWBtn.prototype.C = // Config shared by all instances of SWBtn: { B: 20 // debounce [ms] , L: 0.250 // min Long press [s] , P: 220 // min Pause [ms] , D: 10 // delay of fnc function invocation [ms] }; SWBtn.prototype.enable = function(e) { if (e === undefined || e) { if (!this.w) { this.d = false; this.k = ""; var _this = this; this.w = setWatch( function(e){ _this.c(e); }, this.b , { repeat:true , edge:"both" // <--- required for built-in buttons! , debounce:_this.C.B } ); } } else { if (this.w) { this.d = true; this.w = clearWatch(this.w); if (this.t) this.t = clearTimeout(this.t); } } }; SWBtn.prototype.c = function(e){ // change of state - called by set watch if (e.state) { if (this.t) this.t = clearTimeout(this.t); } else { this.k = this.k + ((e.time - e.lastTime < this.C.L) ? "S" :"L"); var _this = this; this.t = setTimeout(function(){ _this.e(); }, this.C.P); } }; SWBtn.prototype.e = function() { this.t = null; var _k = this.k; if (_k.length > 0) { this.k = ""; var _this = this; setTimeout(function(){ _this.f(_k); },this.C.D); } }; exports = SWBtn; // module end return exports; })(); // :in-line module emulation end var functs = // function names match Short/Long key press pattern { S: function(){ LED1.toggle(); } , L: function(){ LED2.toggle(); } , SL: function(){ LED1.set(); } , SS: function(){ LED1.reset(); } , LL: function(){ LED2.set(); } , LS: function(){ LED2.reset(); } }; var mySWBtn = new SWBtn(function(k){ console.log("BTN1 detected " + k); // log detected key pattern and... if (functs[k]) { functs[k](); } // ...dispatch if defined },BTN1,false); // set it up disabled function onInit() { mySWBtn.enable(); } setTimeout(onInit,999); // for dev only; remove before upload for save()
- uses BTN (or BTN1 when more than one button is on the board)
-
Every electro mechanical switch bounce, that means that the on and off transition is not a single flank but a sequence of ons and offs until the switch settles to a steady on or off status.
The debouncing catches the first state change and then 'waits' a bit and checks again to consider it a state change and assesses what the final state is. In other words, the debounce option makes it easy for the program to overcome the flaws of switches. Some switches bounce more than others.
By default, Espruino board built-in (mounted) switches use the
debounce:10
option on thesetWatch(...)
even if you do not code it. For externally connected and watched switches (or signals in general), you have to define thedebounce:##
option yourself.For something you like you try to do, take a look at this conversation: Software Buttons - Many buttons from just one hardware button. The 'module' can figure sequences of long and short presse and pauses between these sequences following the concept of Morse Code. Long vs. Short press and the pauses between the presses of a sequence as well as the pause between sequences are configurable. The 'module' also detects sequences out of the order and ignores them as a whole.
-
Your spi should work the way you presented. With so little code you share it is though impossible to figure what is not going well. Setup/init of SPI1 and definition of CS would have to be known. Furthermore, the HW setup on breadboards can be challenging due to the quality of spring contacts and cables. The display controller needs a timely correct initialization sequence in order to operate.
-
I guess you figure when connected - and powered over USB - you can make the upload of code to set the time - an option in the ide-settings... Unfortunately, as far as I know, any power cycle loses the time.
You can power the pico from the batteries over the designated connector, then connect to usb, then upload the code into RAM with time setting - do not start your code / application yet, but have an onInit() function which includes the start of your code / application. While still connected, you can write the current time to Storage with function you call from console or with statements executed in the console. In your code, your read that time and compare it to the time stored - your hinted lower bound as a sanity check. If the stored time is ahead, you obviously got a power cycle and lost your time and you can signal that. You reconnect and you can set the time with some js statements in the console.
To resolve time for good, get yourself a simple RTC clock module w/ battery backup - either Dallas DS1302 or DB3231. (I assume you had planned to have nothing else connected to Esuprino except the batteries and temp sensor(s)).
-
@Abhigkar, sure you can do that. After you implemented the neopixel control software, how much space / variables do you have left? That kind of will define what you can do... I assume you have your ESP8266 on a local LAN as a station and not as an access point. In such configuration you can even pull in via - for example http://forum.espruino.com/conversations/267420/ , https://dojotoolkit.org/documentation/tutorials/1.10/hello_dojo/ - pull in nice frameworks, as I showed a while ago - http://forum.espruino.com/comments/12280075/ - and so did others, http://www.espruino.com/Interactive+Web+UI, etc.
-
And here is the 'short' version:
// puckD1extPushButton2.js pinMode(D1,"input_pullup"); var log = function() { console.log(arguments); } , lon = false // log on / off , wId , change = function(evt) { if (lon) log(evt); if (evt.state) { setTimeout(note,1,(Math.floor(evt.time - evt.lastTime))); } } , note = function (t) { // note (for show) console.log(t); } , start = function() { wId = setWatch(change,D1 ,{repeat:true, edge:"both",debounce:10}); } ; function onInit() { start(); } setTimeout(onInit,999); // comment line before upload for save()
If you turn the logging on (lon = true), then you see what is goin on:
The
evt
is the event object passed to the functionchange()
called by the watch. The watch is now setuprepeat:true
andedge:"both"
: In other words,change()
is called on press AND on release of the push button. The event object passed has 3 very convenient properties:- The
state
tells the state of the pin / what the has changed to - on press it is L/false and on release it is H/true, - The
lastTime
tells when the watch fired the last time. If it is the first time, then it isundefined
- The
time
tells the time of the current watch firing.
Using the event object simplifies the code a lot. Below show the log of the event object. Notice the first time - when the button was pressed for the first time - lastTime is undefined. On press event of the button we have to do nothing, but on release, we calculate the difference and pass it to the show function. In this example, the show function just logs the value in the console.
Notice, that with this solution, we could run into time binds: new press and release events may happen faster than display can happen. Some queueing and asynchronous display of the result can help to avoid it. Calling
note()
with a timeout makes it also asynchronous. The functionnote()
would then putt
into a FIFO / Queue and - when it is the first entry - start the display process. The display process would then take value by value out of the FIFO / Queue and show the value until the FIFO/Queue is empty. Consuming the queue follows the same asynchronous pattern as in previous example the show function.[ { "state": false, "lastTime": undefined, "time": 1644153426.08500003814 } ] [ { "state": true, "lastTime": 1644153426.08500003814, "time": 1644153428.08899998664 } ] [ { "state": false, "lastTime": 1644153428.08899998664, "time": 1644153429.83899998664 } ] [ { "state": true, "lastTime": 1644153429.83899998664, "time": 1644153431.21399998664 } ] [ { "state": false, "lastTime": 1644153431.21399998664, "time": 1644153433.33699989318 } ] ....
Now, @user140111 - Alberto - it's your turn to complete the code and add the display logic with the queue and show. What makes the things so easy to understand is that there is only one thread executing JS... (speaking in os terms...).
(PS: My dad's first name was Albert - the German version of yours...).
- The
-
Below is the code as described in post #2. The cycle starts over again after display has completed. Display is a series of blinking green and red blinking:
- green for every 10 seconds
- red for every second remaining
very brief red flash for less then a second
// puckD1extPushButton.js pinMode(D1,"input_pullup"); var log = function() { console.log(arguments); } , lon = false // log on / off , blT = 200 // blinkTime (on and off time) , flT = 20 // flashTime (for less than a second pressed) , t0, t1 , wIdD, wIdU , down = function() { if (wIdU) clearWatch(wIdU); wIdD = null; t0 = getTime(); wIdU = setWatch(up,D1 ,{repeat:false, edge:"rising",debounce:10}); } , up = function() { if (wIdD) clearWatch(wIdD); wIdU = null; t1 = getTime(); setTimeout(show,10,Math.floor(t1 - t0)); } , show = function(t) { if (lon) log(t); if (t>10) { LED2.set(); setTimeout(function(){ LED2.reset(); setTimeout(show,blT,t-10); },blT); } else if (t>0) { LED1.set(); setTimeout(function(){ LED1.reset(); if (--t>0) { setTimeout(show,blT,t); } else{ start(); } },blT); } else { LED1.set(); setTimeout(function(){ LED1.reset(); setTimeout(start,blT); },flT); } } , start = function() { wIdD = setWatch(down,D1 ,{repeat:false, edge:"falling",debounce:10}); } ; function onInit() { start(); } setTimeout(onInit,999); // comment line before upload for save()
With a pretty decent - not much bouncing switch - there should be a much simpler solution... except for the display, which then gets a bit more complicated: results would have to be written to a queue and display would run asynchronously / simultaneously. To distinguish between flash/blink sequences, blue led could be flashed between the sequences.
- green for every 10 seconds
-
For testing your wiring, upload this code. First, it sets pinMode of pin D1. After that - every 100[ms] - it writes to the LED1 the opposite what it reads from pin D1. When the switch is open, then D1 is (weak) pulled up (by internal resistor) and digital read returns 1 (or H/high or true), the opposite written to the LED1 turns it off. When switch is closed (pressed), D1 is pulled to Ground, D1 reads 0 (or L/low or false), and opposite written to LED1 turns it on. Variable iId - interval ID - is there so you can stop the program by entering
clearInterval(iId)
in the console.pinMode(D1,"input_pullup"); var iId = setInterval(function() { LED1.write(!digitalRead(D1)); }, 100);
-
Wire things up as shown in attached picture and use two no-repeat
setWatch(...)
on pinD1
initialized withpinMode(D1,"input_pullup")
. Setup first watch withedge:"falling"
to take the time witht0 = getTime()
and setup of second watch withedge:"rising"
with taking the time again (t1
) and re-setup first watch. Regarding the debounce time, you have to play a bit. In second watch you also calculate the difference of both times taken and show it somehow... for example by blinking as many seconds the button has been held. If it is a long time, you may use a green blinks for the tens of seconds and red blinks for the ones of seconds... ;-) -
in the page linked in your first post, the tech spec talks about DC impedance (20℃): 550Ω±50Ω. If you have a multimeter (or decently precise resistors for a voltage divider), you can measure your device's DC impedance... and that will tell how much current the device will draw.
The minimum pulse with (and maximum frequency) on given voltage is there to guarantee proper counting / mechanical movement. If you would open the device, you would see a simple relay setup which for every close (pulse) ratchets a gear for a tooth / given angle, similar to the rotary switches used in old, pulse driven dialing / connecting systems in phone offices (see https://en.wikipedia.org/wiki/Stepping_switch).
For the referenced device of 550Ω±50Ω on 5V, it is 10mA... and 6mA on 3.3V, which is really low... (explainable by the 200:1 reduction gear for incrementing LSD by 1). Based on your forum history, I assume your Espruino device is an Espruino Pico... and a Pico can drive and sink easily 10mA. Since it has 5V compatible GPIO pins, you may run it in sink mode / open drain / open collector mode to drive it with 5V to achieve higher pulse frequency. Be aware that for sink mode the logic is reverse: pin high is idle, pin low for pulse time is for pulse.
After you have it setup, turn the coil on ('infinit' / long pulse), and measure the voltage across the coil. Should you not have sufficient voltage, you can use more than one pin in parallel for driving / sinking. I did something similar in Powering / control power of sensors / peripherals by Espruino pins. I could not use the (usually preferred) sink mode because I needed a shared ground.
Btw, I have the same DSO and I like it a lot. I 3d-printed (and painted) a case with legs for it. The legs swing out to have the DSO in an angle comfortable for reading.... (For higher frequencies I have an old, still CRT and non-S oscilloscope).
PS: You can change the title of the conversation to reflect better what this is all about.
-
...ready for the art gallery (sorry @Gordon: had to sign it w/ espruino 22 )
-
-
It all depends on the layout of the pins of the board you want to place onto Espruino.
If the board has two lines of pins falling into the rows of holes of the prototyping area of the Espruino board, it is then just a wiring from the Espruino GPIO pins to those pins.
If the board has a non-fitting pin layout, you need a little adapter board in between for the wiring of the different pin layouts. At that point you may even consider forgetting the prototyping area and go for an adapter board going directly to the GPIO pins of the Espruino board.
What I did - years ago (see http://forum.espruino.com/conversations/127039/#comment5875744, http://forum.espruino.com/conversations/257994/#comment11937364) - was to put machined sockets into the Espruino board holes to have the most flexibility... You can populate just the prototyping area this way and do regular pins on the edge of the board... then you can plug and play using breadboards AND plug-on boards of your choice.
For the (signal) wiring I use enamelled copper with enamel that is heat sensitive and 'burns' away when soldering. I did that for the Pico - with the 1/20 inch pins - and with another Espruino original board - with the pins 'inside' (not on the edge) of the board (A2..A7, B0, B1, B10, B11, and others) and wired them to pins in line with the edge of the board on a separate prototyping board. The prototyping board is a plain thru holes contacted board that I cut to size (for Pico see: http://forum.espruino.com/conversations/327031/#comment14473842). The wiring is on the under side.
In addition to put pin sockets onto the board, I also put plain pins in... the requires a bit filing of the parts of the sockets and pins to fit into the same hole.
-
Unfortunately no meta data is kept about the custom font (except the fact that it is recognized as a custom font). Therefore, I wrapped in my http://www.espruino.com/ui (doc) / ...UI framework... (forum) the font handling. At the same time I used a palette approach to simplify / application-localize font referencing.
-
The chain is as strong as the weakest link. Espruino can save a state, but if you have a peripheral (link) in it that cannot save its state (or configuration) non-volatile, this great Espruino feature fails. This is typical for a lot of products that introduce a great (enhancement) feature to their detriment:
The user blames Espruino not working when in real it is all the stuff around it that is inferior to support the same level of features. Espruino is taken down by its own strength.
Therefore, any feature with such a potential should not be implemented - EXCEPT - where a significant number of use cases that benefit from the feature AND a clear NOTE / DISCLAIMER goes with the documentation about the feature's constraints and the overall context - exquisite to Espruino .
Even with all precautions taken, a feature may open pandora's box because users may not be aware of the ramifications of the constraints in their project. I experienced first hand what it means to withdraw a feature that jeopardizes the reputation of a product. Luckily it is not that serious with the 'save()' feature of Espruino. The repeated occurrence over time though shows that Espruino has reached a level of complexity where for some 'issues' there are no simple answers anymore.
Nevertheless, a loose (breadboard) wire connection can still be the cause, as simple as that.
-
@Gordon, thanks for the response. I guess a S4 was in place that could handle 1A. I was surprised the Diode gave way and the trace(s) survived... I figured what happened: I created a complete shortcut of 5V to GND with the jumper on the ULN2003 driver board... I did see nothing but noticed the funny smell of a cocked plastic case...
-
Here is the code:
// ldrCtrl.js // 2001-12-12 - 1st cut: cludged code... :\ // this: digital control loop... tracking speed constant, delta != 0 // next: -- reverse logic driver from sensing to tracking // -- analog control loop... tracking speed proportianl to delta // --------- general --------- var c = 0; // console // --------- light sensing code / LDR --------- var lsPs = [ A3 // 0 , A4 // 1 ] , lsDr = [ "ns", "-N", "-S" ] // direction , cw = 6 , iId , sampleCnt = 5 // times , sampleTme = 100 // [ms] , sampleBrk = 300 // [ms] , tstIntTme = sampleCnt*sampleTme + sampleBrk // [ms] , ctlIntTme = tstIntTme * 2 // [ms] , ctlSteps = 5 , delta = 10 // [mV] on 330[mV] w/ office lamp 18" away , lsState = 0 ; function test(vals,cnt,res) { var idx = lsPs.length; if (vals === undefined) { vals = []; while (--idx>=0) vals.push(0); idx = lsPs.length; cnt = sampleCnt; } while (--idx>=0) vals[idx]+=analogRead(lsPs[idx]); if (--cnt>0) { setTimeout(()=>test(vals,cnt),sampleTme); } else { idx = vals.length; while (--idx>=0) { vals[idx] = Math.round(vals[idx]/sampleCnt*3.3*1000); } // vals.push(Math.round((vals[0]+vals[1])/2)); vals.splice(0,0,Math.round((vals[0]+vals[1])/2)); idx = vals.length; if (c) { res = []; while (--idx>=0) res.push(fmt(vals[idx],cw)+lsDr[idx]); console.log(res.join("")); } if (Math.abs(vals[0]-vals[1]) < delta) { lsState = 0; LED1.set(); LED2.set(); setTimeout(()=>{ LED1.reset(); LED2.reset(); },20); } else { if (vals[0] < vals[1]) { lsState = -1; LED1.set(); LED2.set(); setTimeout(()=>{ LED2.reset(); },20); } else { lsState = +1; LED1.set(); LED2.set(); setTimeout(()=>{ LED1.reset(); },20); } } if (trOn) track(); } } function lsR() { lsH(); iId = setInterval(test,tstIntTme); } function lsH() { if (iId) iId = clearInterval(iId); } function lsIni() { // setBusyIndicator(LED1); idx = lsPs.length; while (--idx>=0) pinMode(lsPs[idx],"analog"); } // --------- stepper code --------- // stRn stepper status: is running // stPs stepper pins // stBW steps Backwards bit ON/OFF pattern // stFW steps Forward bit ON/OFF pattern // stSt step bit Stop / OFF pattern // stT step Time in milliseconds [ms] // stI step Interval (from setInterval() and for clearInterval() // sts steps 0001,0011,0010,... pin ON/OFF pattern in use // st step 0..7 current step // stDmy ...because of lint / (cond) ? exprT : exprF needs something to assign to var stRn = false; var stPs = [B4,B5,B6,B7]; var stFW = [0b1000,0b1100,0b0100,0b0110,0b0010,0b0011,0b0001,0b1001]; var stBW = [0b1001,0b0001,0b0011,0b0010,0b0110,0b0100,0b1100,0b1000]; var stSt = 0b0000; var stCk = ()=>0; var st = 0; var stT = 0; var stI = null; var sts = null; var st = 0; var stDmy; // setI setInterval(i,stsC) i in [ms] with (optionl) step Change (if not null), // and direction info (string) var setI = function(t,stsN,d) { if (c) console.log("t = ",t, d); if (stI) clearInterval(stI); if (stsN) sts = stsN; stRn = true; stI = setInterval(stp,t); }; // stp step var stp = function() { digitalWrite(stPs, sts[st = ++st % 8]); stCk(); }; // _sFW step ForWard var _sFW = function(t) { if (c) console.log("FW w/ " + t); if (stT > 0) { setI((stT = t),null," ~F"); } else { if (stT) { st = Math.abs(st - 7); } if (!stI) { st--; } setI((stT = t),stFW," FW"); } }; // _sBW step BackWards var _sBW = function(t) { if (c) console.log("BW w/ " + t); if (stT < 0) { setI(-(stT = t),null," ~B"); } else { if (stT) { st = Math.abs(st - 7); } if (!stI) { st--; } setI(-(stT = t),stBW," BW"); } }; // stH stepper halt function var stH = function() { if (c) console.log("stepper halt"); if (stI) { stI = clearInterval(stI); stI = null; } if (c) console.log(stI); stRn = false; digitalWrite(stPs, stSt); }; // stR stepper run function - t is stepping interval in [ms] var stR = function(t,ck) { if (typeof t === "undefined" ) { if (stT) { if (c) console.log((stT > 0) ? "F>B" : "B>F"); stR(-stT,ck); } else { if (c) console.log("What ?"); } } else { stCk = (ck) ? ck : ()=>0; stDmy = (t) ? (t>0) ? _sFW(t) : _sBW(t) : stH(); } }; // --------- track code --------- var trLsState = 0 // previous lsState , trOn = false // tracking on , trStTme = 4 // [ms] track step time ; // track function function track() { if (lsState != trLsState) { stH(); if (lsState != 0) stR((lsState<0) ? +trStTme : - trStTme , ()=> { if (Math.abs(Math.round((analogRead(lsPs[0])-analogRead(lsPs[1])) *3.3*1000))<delta) stH(); trLsState = 0; }); trLsState = lsState; } } // track run function function trR() { trOn = true; } // track halt function function trH() { stH(); trOn = false; } // --------- general ---------- // format (integer) value v(+|-) to ds(<=7|8) digits function fmt(v,ds) { var s = " "+v, l = s.length; return s.substr(l-ds); } function onInit() { lsIni(); // light sensing LDR analog pin init lsR(); // light sensing run trR(); // tracking run } setTimeout(onInit,999); // remove before upload for save()
-
Something I had in mind for a while, but never got to it: A light tracker (sun tracker).
Two LDRs - light dependent resistors - separated by an 'vertical, flat wall' mounted on a platform catch the light. When the light is in the plane of the wall - right above - both LDRs get about the same amount of light and pose about the same resistance. Each LDRs is series with a resistor between the power rails 0 - and 3.3V - and builds a voltage divider. The voltage at the divider points is sampled several times and then compared. If different by more as a given delta, a state information switches the values of -1, 0, +1. This state is used to trigger a tracker that rotates the platform - longitudinal to the wall - with a stepper until both LDRs catch about the same light again. To get the best results, I paired the LDRs (and series resistors) from a bunch to get two of each with most 'equal' specs.
The code started out with just working the LDRs and showing green or red LED of PICO with flashing of both on polled light sensing. I added - recycled - some old code of mine - Stepper Motor Spinning Slow from almost 7 years ago - to rotate the platform. I linked both the light sensing and stepper motor code together with a tracker function. Initially, I called the tracker on an interval as well, but that kept the platform oscillating. In a second version tracking, I called the tracking on change of light sensing state. But it was still overshooting and oscillating, except I made the rotation really, really slow, so that about a step or two per light sampling. Lastly - what is shown - is a combination of both, trigger rotation or stopping of it on intervalled light sampling AND check with stopping of rotation on every step by taking 'quickly' a simple light sample.
The attached clip shows the sensor platform 'following' the light desk lamp which I move back and forth. Notice a small correctional adjustment after a major adjustment - in seconds 11 and 16. - seen by a short 'after' flashing of the stepper LEDs. This is because the LDR is slow reacting (and also because only one sample read is taken for triggering the stop of rotation on every stepper step (Every 4[ms] a step is made. The rotation is so slow because of the high reduction gear as part of the motor.)
The code - see next post - is far from prime time... but it shows that with a few lines something can be done quickly. For real worl used, limits have to be introduced - with 'hard range end detection / switches' - and detection of false adjustment triggers. In the end, this tracker will serve a single or with a double a two axis solar tracker. Given real time, values in tables for each week of an earth-sun year for the geo-location and a few formulas, the trackers position is primed and uses presented contraption for fine adjustment.
Do you have a way to control the firing frequency? For example w/ PWM driven pin (of a second Espruino device to minimize interference)?
I would start at the rate of 100Hz and then crank it up until it becomes 'a wash' (measurements all over the place).
If the expected firing frequency cannot be safely handled, you have to switch approach: you run a register/counter on a pin and read and reset the value every second. The counting would be uninfluenced by what else is going on your device. Proper timing comes then important - who to reset timer and start period and do the read-out. For better control you could make it inline assembler or compiled C function and call it from JS. There are examples in the forum (tutorial?) how to setup counter register and read and reset it. It was for the STM chips... I'm not sure if it works exactly the same way for the NRF52832... some data sheet and coding examples from the web may help you.