microbit-web-bluetooth
microbit-web-bluetooth copied to clipboard
IOpin service documentation
Hey Rob,
Thanks for your great library! It really simplifies talking to micro:bit over web-bluetooth.
I cannot find any documentation about accessing IOpin service.
After diggin into console I managed to work out PWM control to operate a servo motor.
let pwm = {
pin: pin, // 0,1,2
value: val, // 50-250
period: 10000 // 10000 microsecconds = 10 millisecconds
}
services.ioPinService.setPwmControl(pwm);
....
Still I am confused how to take analog readings from IO pins. Setting AD and IO configuration with bitmasks is a riddle to me. Could you please provide any code sample how to set: setIoConfiguration and getAdConfiguration to configure P0 , P1, and P2 for analog readings??
Thank you in advance.
Maciej
I haven't had the chance to set this up, so haven't documented it. I was working in the dark a little, so any help from the community here would be awesome.
I'm happy to write up some basic docs if someone has this working.
@benmoran Anything you could share?
Hi! @LoFiRobot I have personally only controlled servos and not used the analog readings. But I think:
-
you will definitely need to be using the latest version with the patch that was recently merged with the iopin service fixes
-
you should get back an array of 24 zeros and ones from
getAdConfiguration
corresponding to the analog /digital configuration bits for each of the 24 pins -
you should provide a similar array to
setAdConfiguration
, so I think you want an array of length 24 that starts with at least three 1s to enable analog reading if I have that the right way around.
Before the little endian patch, these functions didn't work - you'd get a list of 24 garbage ints instead.
(You don't need to do any bit twiddling from javascript, unlike the raw C++ api which expects a single 24bit unsigned int - @thegecko has thoughtfully done that for us)
Hello @thegecko and @benmoran
I think I am using latest version of the library - I cloned the repo few days ago.
When it comes to pin readings.
- getIoCconfiguration method returns an array of 24 int [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] But when I try to pass similar array to setIoConfiguration with first element changed to 1 (to set pin 0 as input) as this:
let io_conf = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; services.ioPinService.setIoConfiguration(io_conf);
nothing changes - using getIoConfiguration afterwards still returns array of zeroes.
- I am also using this guide to better understand how MB services work - https://ukbaz.github.io/howto/ubit_ble_profile.html When I pass bits manually to the microbit as in the guide I am able to set the pins for inputs and get readings.
So i tried it that way: I connect MB to NRFConnect app and passed 0x01000000 to Pin IO Configuration characteristic (set P0 as input). Then disconnected from the NRF app and connected to your library. I used getIoConfiguration method and received this values in return:
[65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0]
However when I reset the microbit, reconnect to your library and try to pass the same array to setIoConfiguration - no effect, getIoConfiguration returns zeroes.
When I am setting P0 to analog input IO and AD in NRF app ( passing 0x01000000 to both characteristics) then reconnecting to your library and using services.ioPinService.readPinData() I recieve proper readings from the pin - {pin: 0, value: 83}
So part I am missing is how to use setIoConfiguration and setAdConfiguration methods properly.
Update
I still have not managed to figure out how to use setIoConfiguration and seAdConfiguration methods.
But I managed to set pins configuration by writing to characteristics directly:
const Ad_char = "e95d5899-251d-470a-a062-fa1922dfa9a8";
const Io_char = "e95db9fe-251d-470a-a062-fa1922dfa9a8";
let cmd = new Uint32Array([0x01]);
services.ioPinService.helper.setCharacteristicValue(Io_char, cmd);
services.ioPinService.helper.setCharacteristicValue(Ad_char, cmd);
Important part is the cmd variable - each pin has its address P0 - 0x01 P1 - 0x02 P2 - 0x04
To set P0 to analog read - pass 0x01 to both IO and AD characteristics. To set P0 to digital read - pass 0x01 to IO and 0x00 to AD characteristic.
To set P0 and P1 to analog read - sum the bytes - pass 0x03 to IO and AD To set P0, P1 and P2 to analog read - sum the bytes - pass 0x07 to IO and AD
To read the values simply call:
services.ioPinService.readPinData();
Performance suggestion:
When I read the data with event handler
services.ioPinService.addEventListener("pindatachanged", eventHandler);
at times it gets updated so fast that it tends to overload web-bluetooth communication speed
I test on Macbook pro (2012), Android (OnePlus5) and Chromebook (Acer R11)
Even on the Macbook the app tends too freeze as the web-bluetooth queue gets overloaded.
Android and ChromeOS performs about two times slower so it will get even worse there.
It would be nice to have an eqivalent of
accelerometerService.setAccelerometerPeriod(50)
for reading pin data. Setting te period to about 50-100ms would keep the performance stable on slower devices and still keep the refresh rate smooth enough.
It would be nice to have an eqivalent of accelerometerService.setAccelerometerPeriod(50) for reading pin data.
Nice, feel free to create a separate issue in case I forget ;)
Do you have any example on reading analog from pin 1?
if(services.ioPinService){ console.log('ioPinServices'); const Ad_char = "e95d5899-251d-470a-a062-fa1922dfa9a8"; const Io_char = "e95db9fe-251d-470a-a062-fa1922dfa9a8"; let cmd = new Uint32Array([0x02]); services.ioPinService.helper.setCharacteristicValue(Io_char, cmd); services.ioPinService.helper.setCharacteristicValue(Ad_char, cmd); services.ioPinService.readPinData(); }
I'm getting crash on browser and also on microbit....I thing it has to do with giving the period of 50ms before read.
Any javascript examples code would be nice.
Hey dakandaka,
I am using timer to ping the service for data (the interval is 100ms, no crashes):
componentDidMount() { this.intervalId = setInterval(this.timer.bind(this), 100); } componentWillUnmount(){ clearInterval(this.intervalId); }
async timer() {
let pin_data = []
let _p0 = 0;
let _p1 = 0;
let _p2 = 0;
if (window.services != null && this.state.connected === true) {
pin_data = await window.services.ioPinService.readPinData();
}
for (let i = 0; i < pin_data.length; i++) {
if (pin_data[i].pin === 0 ) {
_p0 = Math.round(pin_data[i].value/2.55);
window.p0 = _p0;
}
if (pin_data[i].pin === 1 ) {
_p1 = Math.round(pin_data[i].value/2.55);
window.p1 = _p1;
}
if (pin_data[i].pin === 2 ) {
_p2 = Math.round(pin_data[i].value/2.55);
window.p2 = _p2;
}
}
thank you for quick response :)
I see componentDidMount() you are doing it in react? and state...I think it is react. Can you post complete react class/function or code?
Yes it is React. Below you have full component. It is a messy solution because I am keeping the microbit object in scope of main window object and accessing it in react from there (not a proper solution ;-) Anyway feel free to ask anything, but I was doing this project over a year ago so forgot a bit how and why the things were made.
The app live is here: https://lofi-apps.web.app/ Microbit firmware: https://www.lofirobot.com/files/lofi_firmware.hex
BLE component .........
import React, {Component} from 'react'; import InputsMonitor from './inputs_monitor.js' import {FullscreenIcon, RobotIcon} from './icons.js' //import {microbit} from 'microbit-web-bluetooth';
let check_connection_interval = null;
let x = 0; let y = 0; let temp = 0; let compass = 0; let p0 = 0; let p1 = 0; let p2 = 0;
let MBdevice = null; let MBservices = null;
class BLE_connect extends Component {
constructor(props) { super(props); this.state = { disconnection_feedback: 0, connected: false, RX: null, sensors: {A:0,B:0,x:0,y:0,temp:0,comp:0,p0:0,p1:0,p2:0,connected:false} }; window.send = this.send_ble.bind(this);
}
componentDidMount() { this.intervalId = setInterval(this.timer.bind(this), 100); } componentWillUnmount(){ clearInterval(this.intervalId); }
async timer() {
let pin_data = []
let _p0 = 0;
let _p1 = 0;
let _p2 = 0;
if (window.services != null && this.state.connected === true) {
pin_data = await window.services.ioPinService.readPinData();
}
for (let i = 0; i < pin_data.length; i++) {
if (pin_data[i].pin === 0 ) {
_p0 = Math.round(pin_data[i].value/2.55);
window.p0 = _p0;
}
if (pin_data[i].pin === 1 ) {
_p1 = Math.round(pin_data[i].value/2.55);
window.p1 = _p1;
}
if (pin_data[i].pin === 2 ) {
_p2 = Math.round(pin_data[i].value/2.55);
window.p2 = _p2;
}
}
//console.log(_p0);
this.setState(prevState => ({
sensors: {
...prevState.sensors,
p0: _p0,
p1: _p1,
p2: _p2,
}
}));
}
async connect_ble() {
const onDisconnected = event => { this.setState({connected:false}); window.connected = false; this.setState(prevState => ({ sensors: { ...prevState.sensors, connected: false, } }));
}
const eventHandler = event => {
this.props.sensors(this.state.sensors);
if (event.type === "buttonastatechanged") {
const a_button = event.detail*100;
window.buttonA = a_button;
this.setState(prevState => ({
sensors: {
...prevState.sensors,
A: a_button,
}
}));
}
if (event.type === "buttonbstatechanged") {
const b_button = event.detail*100;
window.buttonB = b_button;
this.setState(prevState => ({
sensors: {
...prevState.sensors,
B: b_button,
}
}));
}
if (event.type === "temperaturechanged") {
window.temp = event.detail;
this.setState(prevState => ({
sensors: {
...prevState.sensors,
temp: event.detail,
}
}));
}
if (event.type === "accelerometerdatachanged") {
const x = Math.max(Math.min(Math.round((event.detail.x+1)*50),100),0);
const y = Math.max(Math.min(Math.round((event.detail.y+1)*50),100),0);
window.x = x;
window.y = y;
this.setState(prevState => ({
sensors: {
...prevState.sensors,
x: x,
y: y,
}
}));
}
if (event.type === "magnetometerbearingchanged") {
//console.log(event.detail);
window.compass = event.detail/3.6;
this.setState(prevState => ({
sensors: {
...prevState.sensors,
comp: Math.round(event.detail/3.6),
}
}));
}
if (event.type === "pindatachanged") {
const pin_data = event.detail;
let _p0 = 0;
let _p1 = 0;
let _p2 = 0;
for (let i = 0; i < pin_data.length; i++) {
if (pin_data[i].pin === 0 )
_p0 = Math.round(pin_data[i].value/2.55);
if (pin_data[i].pin === 1 )
_p1 = Math.round(pin_data[i].value/2.55);
if (pin_data[i].pin === 2 )
_p2 = Math.round(pin_data[i].value/2.55);
}
//console.log(_p0);
this.setState(prevState => ({
sensors: {
...prevState.sensors,
p0: _p0,
p1: _p1,
p2: _p2,
}
}));
}
}
const device = await window.microbit.requestMicrobit(window.navigator.bluetooth); if (device) { let bluetoothDevice = device; MBdevice = device; window.device = device; bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected);
this.setState({connected:true});
window.connected = true;
this.setState(prevState => ({
sensors: {
...prevState.sensors,
connected: true,
}
}));
const services = await window.microbit.getServices(device);
MBservices = services;
window.services = services;
if (services.deviceInformationService) {
console.log(await services.deviceInformationService.readDeviceInformation());
}
if (services.uartService) {
services.uartService.addEventListener("receiveText", eventHandler);
await services.uartService.send(new Uint8Array([104, 101, 108, 108, 111, 58])); // hello:
}
if (services.ledService) {
await services.ledService.writeMatrixState([
[1, 0, 0, 1, 1],
[1, 1, 0, 1, 1],
[0, 0, 0, 0, 0],
[1, 1, 0, 1, 0],
[1, 0, 0, 1, 0]
]);
//console.log(await services.ledService.readMatrixState());
await services.ledService.setScrollingDelay(70);
//console.log(await services.ledService.getScrollingDelay());
//await services.ledService.writeText("LOFI");
}
if (services.buttonService) {
services.buttonService.addEventListener("buttonastatechanged", eventHandler);
services.buttonService.addEventListener("buttonbstatechanged", eventHandler);
}
if (services.temperatureService) {
await services.temperatureService.setTemperaturePeriod(2000);
console.log(await services.temperatureService.getTemperaturePeriod());
services.temperatureService.addEventListener("temperaturechanged", eventHandler);
}
if (services.accelerometerService) {
await services.accelerometerService.setAccelerometerPeriod(50);
console.log(await services.accelerometerService.getAccelerometerPeriod());
services.accelerometerService.addEventListener("accelerometerdatachanged", eventHandler);
}
if (services.magnetometerService) {
await services.magnetometerService.setMagnetometerPeriod(640);
console.log(await services.magnetometerService.getMagnetometerPeriod());
//services.magnetometerService.addEventListener("magnetometerdatachanged", eventHandler);
services.magnetometerService.addEventListener("magnetometerbearingchanged", eventHandler);
}
if (services.ioPinService) {
const Ad_char = "e95d5899-251d-470a-a062-fa1922dfa9a8";
const Io_char = "e95db9fe-251d-470a-a062-fa1922dfa9a8";
let cmd = new Uint32Array([0x07]);
//await services.ioPinService.helper.setCharacteristicValue(Io_char, cmd);
//await services.ioPinService.helper.setCharacteristicValue(Ad_char, cmd);
//services.ioPinService.addEventListener("pindatachanged", eventHandler);
}
}
}
send_ble(key,value) {
let _key = key;
if (key === 208) { _key = 0; } if (key === 209) { _key = 1; } if (key === 210) { _key = 2; }
let pwm = { pin: _key, value: (value*2)+50, period: 10000 } MBservices.ioPinService.setPwmControl(pwm);
if (key === 211) { console.log(value); MBservices.ledService.writeText(value.toString()); }
}
render () {
return (
<div id="right_toolbox">
<button id="connect_button" className=
{ this.state.connected === true ? ("outline") : ("") }
onClick={() => this.connect_ble()}> <RobotIcon fill={this.props.color}/>
<InputsMonitor abutton={this.state.sensors.A} bbutton={this.state.sensors.B} x={this.state.sensors.x} y={this.state.sensors.y} temperature={this.state.sensors.temp} compass={this.state.sensors.comp} p0={this.state.sensors.p0} p1={this.state.sensors.p1} p2={this.state.sensors.p2} color={this.props.color} />
{// }
)
} }
export default BLE_connect
hey,
I have the issue that ioPinService comes back as undifined. How do i fix this issue? bare in mind this is my first time using a mircobit so it is probably something stupid! I'm using the ''ble-open-microbit-universal.hex'' hex file.
Thanks in advance.