Sending data via BLE to webpage

Hi,

I’m trying to send commands and receive some stats over BLE to a web page.

The sending is working fine, but cant work out how to get stats back, for a char or a string.

I’m using this example for control https://github.com/NickDST/BLEwithXenonAndChrome

In the HTML it does touch on reading values.

//This is unused, but you can use this to read values from the Particle
async readSomething() {
  const service = await this.device.gatt.getPrimaryService("4677062c-ad02-4034-9abf-9858177xxxxx");
  const characteristic = await service.getCharacteristic("dc13b36a-3499-46b0-ac11-5ac0173xxxxx");
  await characteristic.readValue(); 
}

here is the device code:

// Variables for keeping state
typedef struct {
  uint8_t light;
  uint8_t light2;
} led_level_t;

// Static level tracking
static led_level_t m_led_level;

int led1 = A3;
int led2 = D7;
int num = 1;

bool lightStatus = false;

const char* serviceUuid = "4677062c-ad02-4034-9abf-98581772427c"; //service
const char* light         = "dc13b36a-3499-46b0-ac11-5ac0173c4cc5"; //red char
const char* light2       =  "26f260de-0b93-4352-baa3-56b8be1e606a"; //green char 

//Static function for handling Bluetooth Low Energy callbacks
static void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
  // Sets the global level
  if( context == light ) {
    m_led_level.light = data[0];
}

  if( context == light2 ) {
    m_led_level.light2 = data[0];
}

}

void setup() {

// RGB.control(true);
// RGB.control(m_led_level.green);

pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);

  // Set the RGB BLE service
BleUuid rgbService(serviceUuid);

BleCharacteristic redCharacteristic("lightValue", BleCharacteristicProperty::WRITE_WO_RSP, light, serviceUuid, onDataReceived, (void*)light);
BleCharacteristic blueCharacteristic("lightValue", BleCharacteristicProperty::WRITE_WO_RSP, light2, serviceUuid, onDataReceived, (void*)light2);


 // Add the characteristics
 BLE.addCharacteristic(redCharacteristic);
 BLE.addCharacteristic(blueCharacteristic);

 // Advertising data
 BleAdvertisingData advData;

 // Add the RGB LED service
 advData.appendServiceUUID(rgbService);

 // Start advertising!
 BLE.advertise(&advData);

}

void loop() {


if(m_led_level.light){
            digitalWrite(led1, HIGH);
            Particle.variable("light1", "On");
} else {
           digitalWrite(led1, LOW);
           Particle.variable("light1", "Off");
}


if(m_led_level.light2){
            digitalWrite(led2, HIGH);
            Particle.variable("light2", "On");
} else {
           digitalWrite(led2, LOW);
           Particle.variable("light2", "Off");
}


delay(500);
}

I’ve experimented with
voltageInputCharacteristic.setValue(&ltc_v_input); but just don’t really get it.

Ive searched but can find any simple examples to send data to a webpage other then https://rickkas7.github.io/ble-powersource/ which seems to be two totally different connection methods…

Anyone have any pointers?

If you want to push data, you should use a ::NOTIFY characteristic as shown in the tutorial.
Your characteristics are pure write characteristics (i.e. can be written to) which don't cater for reading, indicating or notifying back.

I'm not sure what you mean with that or in what way this is "totally different".
Can you elaborate?

@ScruffR its been a while…

I meant
https://github.com/NickDST/BLEwithXenonAndChrome (control)
And
https://rickkas7.github.io/ble-powersource/ 2 (read)
Examples are different.

I guess im trying to create a code and html that can write and read values.
I want to read if somthing is on / off and then turn it on or off depending.

I had acheived this by mesh, but since it is no longer supported im looking at ble to fill my gap of additional device control other then catm1 should it be in a location with no reception etc. Hope that makes better sense…

I’m also wanting to accomplish the same 2way communicatiin with Chrome BLE.

So I hope to see you get this working so you can share with us.

Have you seen this example also?

Yep, looked over that. And your posts and the internet but still not having much luck. Tried most things but still coming up falure… lol.

Yea BLE has a steep learning curve.

@ScruffR Should this be something we should be able to get working if we just work together on it?

I would love to get a simple demo page working showing how we can edit variables on the Argon from the Web page vs the web page only receiving data.

Is it just about getting the Chararistics and Notify settings right?

I have been doing some BLE testing this week and performance and stability has greatly improved since the last time I played with it.

I want to be able to flip a switch on the web page and have a bool variable on the Argon switch state. Seems simple but not to us noobs.

We need some professional help to get this working.

1 Like

Definetly. I've had such a project working a while back as PoC but have "lost" it since.

Unfortunately, I'm away for the long weekend so I can't help before next week :blush:

One starting point for bidirectional communication would be the UART tutorial.

1 Like

Cool, I have plenty to do until then so lets link back up when your back about your POC you already have built.

Im making head way… but now stuck on selecting the external ble antenna. Thought that would be the most strait forward part. :frowning:

Error: …/wiring/inc/spark_wiring_arduino_constants.h:44:21: expected unqualified-id before numeric constant

What does your code look like? This example from the docs is compiling for me with 1.5.2:

BLE.selectAntenna(BleAntennaType::EXTERNAL);

Edit: Oh, I see what’s happening. Something in your code or a library you are using is enabling Arduino compatibility mode (spark_wiring_arduino_constants.h). That unfortunately makes EXTERNAL a defined constant on line 44.

#define EXTERNAL    0

That’s going to break BleAntennaType::EXTERNAL because it gets interpreted as BleAntennaType::0 which is wrong.

After your #include statements you can probably do a

#undef EXTERNAL

and it will probably compile.

Yeah, I just did the same test on a simple code and it worked. Its must be something conflicting in my code. :frowning:

BINGO! that worked. Thanks.

1 Like

Nice!

Be sure to share when you get things working, I’m heading down the same road soon.

Just testing to see if it was achievable.

So far I can read and write to the Boron. It may not be 100% correct or neat but works.

I’ll tidy it up and have it read and write the Led D7 so you can see how it works if you like?

Yes please do share.

I’ll see if I can pitch in also.

@ScruffR tell us more about your BLE proof of concept code you put together.

I thought this already was the solution so I didn't go for a deep search for that :blush:

Sorry, I was talking about sharing the 2-Way BLE communication Proof of Concept you said you had but I totally missed the part where you said you had Lost that code.

Ok, here is what I have. Its not perfect but it should work. Its a scaled down code to show it should work. I cant test it as I don’t have a Device with me at the moment. I’ve been using a Boron and it works on that platform.

// Variables for keeping state
typedef struct {
  uint8_t d1;

} led_level_t;

// Static level tracking
static led_level_t m_led_level;

int led1 = D7;
int num = 1;

bool lightStatus = false;

const char* serviceUuid = "4753062c-ad02-4094-9abf-98581772463c"; //service
const char* d7_write      = "9cf8f670-e1fd-11ea-87d0-0242ac130003"; 
const char* d7_read       =  "0218317c-de04-11ea-87d0-0242ac130003";

//Static function for handling Bluetooth Low Energy callbacks
static void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
  // Sets the global level
  if( context == d7_write ) {
    m_led_level.d1 = data[0];
  }
}

BleCharacteristic d7_readCharacteristic("d7_read", BleCharacteristicProperty::READ, d7_read, serviceUuid);

void setup() {

Particle.keepAlive(55);
// RGB.control(true);
// RGB.control(m_led_level.green);

pinMode(led1, OUTPUT);

 // Set the BLE service
BleUuid controlService(serviceUuid);

BleCharacteristic d7_writeCharacteristic("d7_write", BleCharacteristicProperty::WRITE_WO_RSP, d7_write, serviceUuid, onDataReceived, (void*)d7_write);

 // Add the characteristics
 BLE.addCharacteristic(d7_writeCharacteristic);

 BLE.addCharacteristic(d7_readCharacteristic);

 // Advertising data
 BleAdvertisingData advData;

 // Add the RGB LED service
 advData.appendServiceUUID(controlService);

 // Start advertising!
 BLE.advertise(&advData);
}

void loop() {

if(m_led_level.d1){
            digitalWrite(led1, HIGH);
            Particle.variable("light1", "On");
} else {
           digitalWrite(led1, LOW);
           Particle.variable("light1", "Off");
}   
    d7_readCharacteristic.setValue((digitalRead(D7)));
delay(500);
}

Here is the HTML code that should work with it.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

</head>
<body>
    
<div style="margin: 30px" style = "border: red 5px solid" >
   
<br>
    
<font size="4" style="color:blue"> Connection Status:   </font>   
<button class="btn-default" style="background-color: red" id='connect'> Click me to connect </button> 
    
<br>
<hr>

<font size="4">D7    :  </font> 
<button class="btn btn-success"  id='readd7'> READ </button> 
<br>

<hr>

 <font size="5">1. </font>
  <button class="btn btn-success" onclick="if(confirm('Do you really want to turn D7 on?')){}else{return false;};" id='d7on'>ON </button> 
  <button class="btn btn-danger" onclick="if(confirm('Do you really want to turn D7 OFF?')){}else{return false;};" id='d7off'>OFF </button> 
  <hr>

<hr>
</div>
</body>
<script>

//The class for the BLE connection. Obscure name generated from the Boilerplate.

class PlaybulbCandle {

constructor() {
  this.device = null;
  this.onDisconnected = this.onDisconnected.bind(this);
}

//Looking for the particular Uuid from the Particle
async request() {
  let options = {
    "filters": [{
      services: ['4753062c-ad02-4094-9abf-98581772463c']
    }],
    "optionalServices": [ ]
 
  };
  this.device = await navigator.bluetooth.requestDevice(options);
  if (!this.device) {
    throw "No device selected";
  }
  this.device.addEventListener('gattserverdisconnected', this.onDisconnected);
}

    
//Connecting to the Particle
async connect() {
  if (!this.device) {
    return Promise.reject('Device is not connected.');
  }
  await this.device.gatt.connect();
   var text = ('Connected');   
    
myFunction1();
     document.getElementById('connect').innerHTML = text; 
}
    
async disconnect() {
  if (!bluetoothDevice) {
    return;
  }
  log('Disconnecting from Bluetooth Device...');
  if (bluetoothDevice.gatt.connected) {
    bluetoothDevice.gatt.disconnect();
  } else {
    log('> Bluetooth Device is already disconnected');
  }
}
    
    

//This is unused, but you can use this to read values from the Particle
async readSomethingreadd7() {
const service = await this.device.gatt.getPrimaryService("4753062c-ad02-4094-9abf-98581772463c");   

myFunction2();
    
var characteristic = await service.getCharacteristic("0218317c-de04-11ea-87d0-0242ac130003");
characteristic.addEventListener('characteristicvaluechanged', handlereadd7Notifications); 
await characteristic.readValue();
}

async writeSomethingLED1(data) {
  const service = await this.device.gatt.getPrimaryService("4753062c-ad02-4094-9abf-98581772463c");
  const characteristic = await service.getCharacteristic("9cf8f670-e1fd-11ea-87d0-0242ac130003");
  await characteristic.writeValue(data);
}
    
//tells you whether or not something has been disconnected
disconnect() {
  if (!this.device) {
    return Promise.reject('Device is not connected.');
  }
  return this.device.gatt.disconnect();
      var text = ("Disconnected");
    myFunction();
    document.getElementById('connect').innerHTML = text;
}

onDisconnected() {
  console.log('Device is disconnected.');
     var text = ("Disconnected");
    myFunction();
    document.getElementById('connect').innerHTML = text;
}
}
//************************* class finished

//instanitating a new BLE connection object
var playbulbCandle = new PlaybulbCandle();

//standard Javascript getElementbyID click events
document.getElementById('connect').addEventListener('click', async event => {
try {
  await playbulbCandle.request();
  await playbulbCandle.connect();
  /* Do something with playbulbCandle... */
} catch(error) {
  console.log(error);
}
});

//standard Javascript getElementbyID click events
document.getElementById('connect').addEventListener('click', async event => {
try {
  await playbulbCandle.disconnect();
  /* Do something with playbulbCandle... */
} catch(error) {
  console.log(error);
}
});

document.getElementById('d7on').addEventListener('click', async event => {
try {
  var turnLight = Uint8Array.of(1);
  await playbulbCandle.writeSomethingLED1(turnLight);
  console.log("Turning D7 On");
  /* Do something with playbulbCandle... */
} catch(error) {
  console.log(error);
}
});

document.getElementById('d7off').addEventListener('click', async event => {
try {
  var turnLight = Uint8Array.of(0);
  await playbulbCandle.writeSomethingLED1(turnLight);
  console.log("Turning D7 Off");

  /* Do something with playbulbCandle... */
} catch(error) {
  console.log(error);
}
});

document.getElementById('readd7').addEventListener('click', async event => {
try {
  var read = Uint8Array.of(0);
  await playbulbCandle.readSomethingreadd7(read);
  console.log("Trying to read D7 state");

  /* Do something with playbulbCandle... */
} catch(error) {
  console.log(error);
}
});

function handlereadd7Notifications(event) {
// 
	var value = event.target.value.getUint8(0);
	 console.log('readd7=' + value);
	// OFF = 0,
	// ON = 1,
	var text;
	switch(value) {
	case 1:
		text = 'ON';
		break;
	case 0:
	default:
		text = 'OFF';
		break;
	}	
	document.getElementById('readd7').innerHTML = text;
}
    
function myFunction() {
  document.getElementById("connect").style.backgroundColor = "red";
}
    
function myFunction1() {
  document.getElementById("connect").style.backgroundColor = "lawngreen";
}
    
    function myFunction2() {
    document.getElementById("readd7").style.color = "red";
}
     
</script>
</html>
2 Likes

It works! :grinning:

Now we just need to figure out how to 2-way the other data types.

1 Like

You are always merely transferring a bunch of bytes - on the receiving side you need to catch that byte stream and “parse” it into the respective data type (which requires both sides to know the type or have a way of “negotiating” that beforehand).