-
Hi everybody / Gordon,
thanks for Espruino/Puck/Pixl !!
Right now I am on a project using Puck.js to interface a (at this point unconnected/unused) steering wheel remote control to an Android tablet.
Generally, I read the wired remote control with the Puck and then send HID events to the Android tablet.
What I need is the possibility to send multimedia controls and normal keyboard events during the same connection. Right now it seems to me I have to decide up front (before connecting the puck), which role I want. Is there a way around it and connect in both roles? Preliminary testing did not work for me unfortunately, but I am generally clueless on this ...Thanks for any hint,
Joost
-
Hi,
sorry for opening a new topic again already, but I hope that our notes might help somebody else later on.
Right now I am connecting like this to my bike sensor:
NRF.requestDevice({ timeout:5000, filters: [{ services: "0x1816" /* namePrefix: 'Wahoo' */ }] }).then(function(device) { console.log(device); return device.gatt.connect(); }).then(function(gatt_object) { gatt = gatt_object; s = "Looking for service..."; g.clear(); g.drawString(s ,0 ,0); g.flip(); return gatt.getPrimaryService("1816"); }).then(function(service) { s = "Looking for char..."; g.clear(); g.drawString(s ,0 ,0); g.flip(); return service.getCharacteristic("0x2A5B"); }).then(function(characteristic) { characteristic.on('characteristicvaluechanged', OnNotify); return characteristic.startNotifications(); }).then(function() { connected = true; t = getTime(); setDisplayMode("livedata"); gatt.device.on('gattserverdisconnected', function(reason) {console.log("gattserverdisconnected, Grund: "+ reason+" Zeit: " + (getTime() -t) + " sec"); disconnect(reason);} ); }
Initially, I even had a timeout of 20 seconds in requestDevice, which led to pretty unreliable connection intitation.
With nrfConnect I see that the sensor starts advertising really fast with an interval of 330ms initially.
I wonder if it is possible to cut the timeout short (right now it seems that the code scans for the full timeout before handing back scan results) and return a matching device immediately?Or is it preferable to cache the sensor's address and do a direct connect to that later on? That would have the (very small) drawback of needing additional logic to do a full scan if the previous/cached device is not successfully connected, to enable connecting to new/other sensors.
Thanks,
Joost
-
Thank you both,
this works great it seems:
gatt.device.on('gattserverdisconnected', function(reason) {console.log("gattserverdisconnected, Grund: "+ reason+" Zeit: " + (getTime() -t) + " sec"); disconnect(reason);} );
with variable t being set earlier upon successful connect.
with reason 8 triggered when the BLE server (my sensor) goes out of reach (loss of reception) and reason (int) 19 gets triggered when the sensor shuts down due to inactivity.
Thanks again,
Joost
-
Hi everybody,
I'm wondering if there is a callback of some sort if/when the espruino device loses connection to a BLE sensor, when subscribed to it's notifications?
Otherwise I would reset a timer upon reception of every notification, and check say every 15 seconds if the timer was reset (meaning notifications were still received), and disconnect if not.
What do you think?
Thanks, Joost
-
-
Hi Gordon, thanks a lot!
The switch/break error seems fixed; now something creeped in giving me
____ _ | __|___ ___ ___ _ _|_|___ ___ | __|_ -| . | _| | | | | . | |____|___| _|_| |___|_|_|_|___| |_| espruino.com 1v99 (c) 2018 G.Williams >setDisplayMode: menu Uncaught Error: Cannot read property 'draw' of undefined at line 143 col 20 pixlmenu.draw(); ^ in function "setDisplayMode" called from line 115 col 24 setDisplayMode("menu"); ^ in function "onInit" called from line 1 col 8 onInit(); ^
(also not working if renaming pixlmenu back to "m", as I had in my code before).
This destroys the larger menu font you helped me with yesterday.This being the code right now:
var actual_menu, gatt; var last_wheel_event_time = 0, actual_wheel_event_time; var last_cumulative_wheel_revolutions = 0, actual_cumulative_wheel_revolutions = 0, track_cumulative_revolutions = 0; var delta_revolutions, delta_event_time, timer = -1; var wheel_circumference = 2340; var delta_time_in_ms, connected = false; Pixl._menu = Pixl.menu; //Bug Fix von Gordon, s. Forum Pixl.menu = function(m) { Pixl._menu(m); }; //Bug Fix function connect() { print("Starting scan..."); g.clear(); var s = "Connecting..."; g.drawString(s ,0 ,0); g.flip(); try { NRF.requestDevice({ timeout:20000, filters: [{ namePrefix: 'Wahoo' }] }).then(function(device) { console.log(device); return device.gatt.connect(); }).then(function(gatt_object) { gatt = gatt_object; s = "Looking for service..."; g.clear(); g.drawString(s ,0 ,0); g.flip(); return gatt.getPrimaryService("1816"); }).then(function(service) { s = "Looking for char..."; g.clear(); g.drawString(s ,0 ,0); g.flip(); return service.getCharacteristic("0x2A5B"); }).then(function(characteristic) { characteristic.on('characteristicvaluechanged', OnNotify); return characteristic.startNotifications(); }).then(function() { connected = true; setDisplayMode("livedata"); console.log("Done!"); // Then call gatt.disconnect(); if/when you want to disconnect }); } catch(error) { print("Sensor not found", error); g.clear(); g.drawString("No sensor found..." ,0 ,0); g.flip(); } } function disconnect() { ... } function OnNotify(event) { ... } var mainmenu = { "" : { "title" : "-- Main Menu --", "fontHeight" : 10 }, "Connect" : function () { setDisplayMode("connecting"); }, "Disconnect" : function () { disconnect(); }, "Live Data" : function() {setDisplayMode("livedata"); }, "New Track" : function () {}, "Toggle Backlight" : function() { LED1.toggle(); }, "-> Settings" : function () {Pixl.menu(SettingsMenu); }, //"Exit" : function() { Pixl.menu(); }, }; var SettingsMenu = { "" : { "title" : "-- Settings --", "fontHeight" : 10 }, "Set Wheel Circumference" : undefined, // do nothing "Set Total Distance Counter" : undefined, // do nothing "< Back" : function() { Pixl.menu(mainmenu); }, }; function showLiveData() { } function onInit () { require("Font8x12").add(Graphics); setDisplayMode("menu"); setWatch(function () { setDisplayMode("menu"); }, BTN2, {repeat:true, debounce:50}); } function setDisplayMode(displaymode) { print("setDisplayMode: ", displaymode); switch (displaymode) { case "switchToConnected": Pixl.menu(); break; case "connecting": if (timer!==-1) {clearInterval(timer); timer = -1;} Pixl.menu(); connect(); break; case "livedata": print("livedata"); Pixl.menu(); if (timer==-1) { print("Set timer"); timer = setInterval( function () {showLiveData(); }, 2000); print("timer = ", timer); } break; case "menu": if (timer!==-1) {clearInterval(timer); timer = -1;} pixlmenu=Pixl.menu(mainmenu); g.setFont8x12(); pixlmenu.draw(); } } onInit();
-
Thanks for your answer!
Here is the complete code:
var actual_menu, gatt; var last_wheel_event_time = 0, actual_wheel_event_time; var last_cumulative_wheel_revolutions = 0, actual_cumulative_wheel_revolutions = 0, track_cumulative_revolutions = 0; var delta_revolutions, delta_event_time, timer = -1; var wheel_circumference = 2340; var delta_time_in_ms, connected = false; function connect() { print("Starting scan..."); g.clear(); var s = "Connecting..."; g.drawString(s ,0 ,0); g.flip(); try { NRF.requestDevice({ timeout:20000, filters: [{ namePrefix: 'Wahoo' }] }).then(function(device) { console.log(device); return device.gatt.connect(); }).then(function(gatt_object) { gatt = gatt_object; s = "Looking for service..."; g.clear(); g.drawString(s ,0 ,0); g.flip(); return gatt.getPrimaryService("1816"); }).then(function(service) { s = "Looking for char..."; g.clear(); g.drawString(s ,0 ,0); g.flip(); return service.getCharacteristic("0x2A5B"); }).then(function(characteristic) { characteristic.on('characteristicvaluechanged', OnNotify); return characteristic.startNotifications(); }).then(function() { connected = true; setDisplayMode("livedata"); console.log("Done!"); // Then call gatt.disconnect(); if/when you want to disconnect }); } catch(error) { print("Sensor not found", error); g.clear(); g.drawString("No sensor found..." ,0 ,0); g.flip(); } } function disconnect() { if (gatt) { gatt.disconnect(); } connected = false; print("Connected: ", connected); setDisplayMode("menu"); } function OnNotify(event) { //console.log("-> "+(event.target.value.getUint8(0, true)>>> 0).toString(2)); //characteristics flags actual_cumulative_wheel_revolutions = event.target.value.getUint32(1, true); actual_wheel_event_time = event.target.value.getUint16(5, true); //console.log("Cumulative wheel Revolutions: " + actual_cumulative_wheel_revolutions); //console.log("Last Wheel Event time: "+ actual_wheel_event_time + " ms"); delta_revolutions = actual_cumulative_wheel_revolutions - last_cumulative_wheel_revolutions; //console.log("-> "+event.target.value.getUint16(7, true)); //crank //console.log("-> "+event.target.value.getUint16(9, true)); //crank if (actual_wheel_event_time >= last_wheel_event_time) { delta_event_time = actual_wheel_event_time - last_wheel_event_time; } else { delta_event_time = 65536 - last_wheel_event_time + actual_wheel_event_time; } delta_time_in_ms =(delta_event_time / 1.024); //console.log("delta_time_in_ms:" + delta_time_in_ms); last_wheel_event_time = actual_wheel_event_time; last_cumulative_wheel_revolutions = actual_cumulative_wheel_revolutions; track_cumulative_revolutions += delta_revolutions; } var mainmenu = { "" : { "title" : "-- Main Menu --", "fontHeight" : 10 }, "Connect" : function () { setDisplayMode("connecting"); }, "Disconnect" : function () { disconnect(); }, "Live Data" : function() {setDisplayMode("livedata"); }, "New Track" : function () {}, "Toggle Backlight" : function() { LED1.toggle(); }, "-> Settings" : function () {Pixl.menu(SettingsMenu); }, //"Exit" : function() { Pixl.menu(); }, }; var SettingsMenu = { "" : { "title" : "-- Settings --", "fontHeight" : 10 }, "Set Wheel Circumference" : undefined, // do nothing "Set Total Distance Counter" : undefined, // do nothing "< Back" : function() { Pixl.menu(mainmenu); }, }; function showLiveData() { //print("showlivedata"); g.clear(); var s = "Cum. Wheel Revolutions: "; g.drawString(s ,0 ,0); //95 - g.stringWidth(s) g.getWidth() g.drawString(actual_cumulative_wheel_revolutions, 0, 11); var speed = delta_revolutions * wheel_circumference / delta_time_in_ms * 60 * 60 / 1000; s = "Speed: " + String(speed.toFixed(2)); g.drawString(s, 0, 22); s = "Track distance: " + (track_cumulative_revolutions * wheel_circumference / 1000 / 1000).toFixed(2); g.drawString(s, 0, 33); g.flip(); } function onInit () { require("Font8x12").add(Graphics); setDisplayMode("menu"); setWatch(function () { setDisplayMode("menu"); }, BTN2, {repeat:true, debounce:50}); } function setDisplayMode(displaymode) { print("setDisplayMode: ", displaymode); switch (displaymode) { case "switchToConnected": Pixl.menu(); break; case "connecting": if (timer!==-1) {clearInterval(timer); timer = -1;} Pixl.menu(); setTimeout(function() {connect();},1); break; case "livedata": print("livedata"); Pixl.menu(); if (timer==-1) { print("Set timer"); timer = setInterval( function () {showLiveData(); }, 2000); print("timer = ", timer); } break; case "menu": if (timer!==-1) {clearInterval(timer); timer = -1;} m=Pixl.menu(mainmenu); g.setFont8x12(); m.draw(); } } onInit();
Though sadly your idea with
setTimeout(connect, 1);
did not work out; returning the same error. Thanks again!
-
Hi,
anyone knows why this code
function setDisplayMode(displaymode) { print("setDisplayMode: ", displaymode); switch (displaymode) { case "switchToConnected": Pixl.menu(); break; case "connecting": if (timer!==-1) {clearInterval(timer); timer = -1;} Pixl.menu(); connect(); break; case "livedata": print("livedata"); Pixl.menu(); if (timer==-1) { print("Set timer"); timer = setInterval( function () {showLiveData(); }, 1000); print("timer = ", timer); } break; case "menu": if (timer!==-1) {clearInterval(timer); timer = -1;} m=Pixl.menu(mainmenu); g.setFont8x12(); m.draw(); } }
gives me this runtime error?
>setDisplayMode: menu =undefined setDisplayMode: connecting Starting scan... Uncaught SyntaxError: BREAK statement outside of SWITCH, FOR or WHILE loop at line 119 col 12 break; ^ in function "setDisplayMode" called from line 1 col 28 setDisplayMode("connecting"); ^ in function "b" called from line 1 col 48 var b=d[c[a.selected]];"function"==typeof b&&b() ^ in function "select" called from line 1 col 10 b.select() ^ in function called from system
I looked it up a hundred times, this should be correct switch/case syntax, or is it not?
-
-
-
Hi everybody,
is there a way to enlarge the font size of the Pixl.menu() function/module? I found the option "fontHeight" : 10 (example), but this gives me an enlarged line height (free space between the letters in vertical dimension, like larger line spacing), but I'd like to get larger letters.
Executingg.setFontVector(40); Pixl.menu(mainmenu);
does not alter font size.
Thanks for any help,Joost
-
Thanks Gordon, I ordered one of those SW-18020P (e.g. https://www.sunrom.com/p/vibration-sensor ), hopeful it'll work!
-
Hi Benjamin,
the Wahoo Speed Sensor resets its "cumulative wheel revolutions" counter when switching off, so you will have to monitor the transmitted data continously with enabling the notifications for the service.
As it seems, we have similar goals with creating an unobtrusive device. Right now I did not advance, as I am awaiting the delivery of the MDBT42 Breakout board, which I ordered just vey recently. I think it will arrive next week (international shipping). The code above was tested with a Puck.js by the way.
My plan is to add a small LiPo battery (around 300-500mAh), a small LiPo charger and an ePaper display. My device will be switched on/active when cycling, I also want it to update the displayed data every couple of seconds. Perhaps it might be an option to add an IMU or vibration sensor to let it sleep when the bike is parked, and awaken when moving the bike - that'd be cool. I guess I will check with Gordonif he has any comments/hints for a very low power state with the bluetooth radio switched off, just monitoring a GPIO. -
As a quick follow up, this works!
var last_wheel_event_time = 0, actual_wheel_event_time; var last_cumulative_wheel_revolutions = 0, cumulative_wheel_revolutions; var delta_revolutions, delta_event_time; var wheel_circumference = 2340; var delta_time_in_ms; function OnNotify(event) { //console.log("-> "+(event.target.value.getUint8(0, true)>>> 0).toString(2)); //characteristics flags cumulative_wheel_revolutions = event.target.value.getUint32(1, true); actual_wheel_event_time = event.target.value.getUint16(5, true); console.log("Cumulative wheel Revolutions: "+ cumulative_wheel_revolutions); console.log("Last Wheel Event time: "+ actual_wheel_event_time + " ms"); delta_revolutions = cumulative_wheel_revolutions - last_cumulative_wheel_revolutions; //console.log("-> "+event.target.value.getUint16(7, true)); //crank //console.log("-> "+event.target.value.getUint16(9, true)); //crank if (actual_wheel_event_time >= last_wheel_event_time) { delta_event_time = actual_wheel_event_time - last_wheel_event_time; } else { delta_event_time = 65536 - last_wheel_event_time + actual_wheel_event_time; } delta_time_in_ms =(delta_event_time / 1.024); console.log(delta_event_time); console.log("delta_time_in_ms:" + delta_time_in_ms); last_wheel_event_time = actual_wheel_event_time; last_cumulative_wheel_revolutions = cumulative_wheel_revolutions; } var gatt; NRF.requestDevice({ timeout:20000, filters: [{ namePrefix: 'Wahoo' }] }).then(function(device) { console.log(device); return device.gatt.connect(); }).then(function(g) { gatt = g; return gatt.getPrimaryService("1816"); }).then(function(service) { //console.log("Service:" + service); return service.getCharacteristic("0x2A5B"); }).then(function(characteristic) { characteristic.on('characteristicvaluechanged', OnNotify); return characteristic.startNotifications(); }).then(function() { console.log("Done!"); // Then call gatt.disconnect(); if/when you want to disconnect });
Thanks a lot, J
-
-
Hi everybody and esp. Gordon (thanks for Espruino!!),
I'm looking into receiving sensor readings from the commercially sold CSC sensor Wahoo Speed (https://www.amazon.com/Wahoo-Cycling-Speed-Sensor-Bluetooth/dp/B01DIE7LUG/ref=sr_1_3?s=sporting-goods&ie=UTF8&qid=1532458958&sr=1-3&keywords=wahoo+speed) which came with my bike computer Wahoo ELEMNT Mini, of which I want to replace the cumbersome headunit.
The sensors sends data via BLE with the CSC service:
Using NRF connect on Android, the sensor readings are shown easily after enabling the notification.
Now to the crucial part (for me):
This code:var gatt; NRF.requestDevice({ filters: [{ namePrefix: 'Wahoo' }] }).then(function(device) { console.log(device); return device.gatt.connect(); }).then(function(g) { gatt = g; return gatt.getPrimaryService("1816"); }).then(function(service) { console.log("Service:" + service); return service.getCharacteristic("0x2A5B"); }).then(function(characteristic) { return console.log(characteristic); }).then(function() { gatt.disconnect(); console.log("Done!"); });
gives me this output:
____ _ | __|___ ___ ___ _ _|_|___ ___ | __|_ -| . | _| | | | | . | |____|___| _|_| |___|_|_|_|___| |_| espruino.com 1v99 (c) 2018 G.Williams > =undefined BluetoothDevice: { "id": "df:b3:22:19:bd:8e random", "rssi": -60, "data": new Uint8Array([2, 1, 6, 3, 3, 22, 24, 17, 9, 87, 97, 104, 111, 111, 32, 83, 80, 69, 69, 68, 32, 54, 51, 70, 49]).buffer, "name": "Wahoo SPEED 63F1", "services": [ "1816" ] } Service:[object Object] BluetoothRemoteGATTCharacteristic: { "uuid": "0x2a5b", "handle_value": 36, "handle_decl": 35, "properties": { "broadcast": false, "read": false, "writeWithoutResponse": false, "write": false, "notify": true, "indicate": false, "authenticatedSignedWrites": false } } Done!
Can anyone point me in the right direction how I can enable and receive the CSC measurement? Sorry I'm a dummy with programming/JavaScript...
The path to go I imagine would probably consist of writing to 0x2902 characteristic to enable notifications and have some kind of callback receive and interpret the data.Later on I plan on using the new MDBT42Q Breakout, connecting a Waveshare epaper display and 3d print a small case and have it running on a 18650 battery. Another option would be to use Pixl.js with a custom case and some kind of battery managment.
Thanks for any help!!
Best regards,Joost
Thanks for your suggestion! Could not try it for now due to time constraints, but hoping to do itin the coming days!
Cheers, Joost