Reading BLE service in Appinventor

Hello
I am using some programs that send data from a board, data through BLE, using their services, in the style of these:
https://os.mbed.com/teams/mbed-os-examples/code/mbed-os-example-ble-HeartRate/

I am trying to read that data using this AppInventor program
https://appinventor-mit-edu.ezproxy.canberra.edu.au/explore/blogs/karen/2016/08/heart.html
I manage to connect to the board using the mobile Bluetooth.
In App inventor I have updated service_UUID and characteristic_UUID.

However I can't read the data. I know the key is here, but I don't know how to fix it :frowning:

Thanks and cheers!


Please export your project and post it here.
That would help verify your BLE extension version and how you do your connect.
The C++ transmission code would also help.

Hi again and thank you very much.
HeartRateMonitor.aia (141.9 KB)

/* mbed Microcontroller Library
  • Copyright © 2006-2015 ARM Limited
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

#include <events/mbed_events.h>
#include <mbed.h>
#include “ble/BLE.h”
#include “ble/Gap.h”
#include “ble/services/HeartRateService.h”

DigitalOut led1(LED1, 1);

const static char DEVICE_NAME[] = “HRM”;
static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};

static uint8_t hrmCounter = 100; // init HRM to 100bps
static HeartRateService *hrServicePtr;

static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
BLE::Instance().gap().startAdvertising(); // restart advertising
}

void updateSensorValue() {
// Do blocking calls or whatever is necessary for sensor polling.
// In our case, we simply update the HRM measurement.
hrmCounter++;

//  100 <= HRM bps <=175
if (hrmCounter == 175) {
    hrmCounter = 100;
}

hrServicePtr->updateHeartRate(hrmCounter);

}

void periodicCallback(void)
{
led1 = !led1; /* Do blinky on LED1 while we’re waiting for BLE events */

if (BLE::Instance().getGapState().connected) {
    eventQueue.call(updateSensorValue);
}

}

void onBleInitError(BLE &ble, ble_error_t error)
{
(void)ble;
(void)error;
/* Initialization error handling should go here */
}

void printMacAddress()
{
/* Print out device MAC address to the console*/
Gap::AddressType_t addr_type;
Gap::Address_t address;
BLE::Instance().gap().getAddress(&addr_type, address);
printf(“DEVICE MAC ADDRESS: “);
for (int i = 5; i >= 1; i–){
printf(”%02x:”, address[i]);
}
printf("%02x\r\n", address[0]);
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
BLE& ble = params->ble;
ble_error_t error = params->error;

if (error != BLE_ERROR_NONE) {
    onBleInitError(ble, error);
    return;
}

if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
    return;
}

ble.gap().onDisconnection(disconnectionCallback);

/* Setup primary service. */
hrServicePtr = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);

/* Setup advertising. */
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble.gap().setAdvertisingInterval(1000); /* 1000ms */
ble.gap().startAdvertising();

printMacAddress();

}

void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
BLE &ble = BLE::Instance();
eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
}

int main()
{
eventQueue.call_every(500, periodicCallback);

BLE &ble = BLE::Instance();
ble.onEventsToProcess(scheduleBleEventsProcessing);
ble.init(bleInitComplete);

eventQueue.dispatch_forever();

return 0;

}

I suspect you are using an old BLE extension, for an old tutorial.

ble extension version 2

1 Like

Thank you very much.
What extension do you recommend using?

I will try with this one:
https://mit-cml.github.io/extensions/

Your github source looks current.

However, extension upgrades are tricky.
Also read this entire thread for how to clean out old versions without losing blocks ...

Hello again
I have loaded the updated library. But I don’t know how to solve the data capture part.
Any help or ideas will be welcome.
Thanks for everything.HeartRateMonitor_02.aia (262.5 KB)

Hello
I have tried Readstring and Readbyte. I have also tried changing the UUIDs. And I still don’t receive data.
I only find examples made from Arduinos or the like, with their own GATT, but none with predefined GATT Services.
I do not know what else to do.

This thread mentions BLE heart rate measurement success.

Have you seen it yet?

Thank you.
I will study it slowly, but I see it difficult for my ability in this :frowning:

Hello again
I have tried to apply what they say in the post.
I connect well to the device, but my app does not read the data.

I am running out of things to suggest.

I notice you are registering in a Clock Timer.

You only need to register once per device channel.
Repeatedly registering has caused problems for other users.
Once you register, you don’t read bytes.
The bytes (whatever you registered for) arrive when they want, and trigger the event block.

Another thing to try after cleaning that up …
Instead of registering for bytes, register for shorts or for strings, and add events for those.

I have a couple of observations in your screen shots …

What is the 6 byte hex code in the stock BLE app you have connected to your BLE device?
Is it an address, and if so, why is it not used in your AI2 UUIDs?
Or is it a reading value, changing as you watch the app?

Also, I notice the Heart Rate Measurement in the app shown is verbose, a full sentence.
Did that app add the extra words, or are they being transmitted by the device on the BLE channel, thus requiring registering for strings instead of bytes?

Hello @ABG

I just put on the relevant part of the program. I have removed the clock and tried to capture by strings and integers, with no results. So it is now complete:

The data is sent from the device through a GATT Service Standard:

void updateSensorValue () {
// Do blocking calls or whatever is necessary for sensor polling.
// In our case, we simply update the HRM measurement.
hrmCounter ++;

// 100 <= HRM bps <= 175
if (hrmCounter == 175) {
hrmCounter = 100;
}

hrServicePtr-> updateHeartRate (hrmCounter);
}

I have consulted the HeartRateService.h library and I think it is an integer. I think the rest of the information in the string is added by nRF Connect.

Thanks a lot!!!

Looking at your new blocks, I see you Register For Bytes, but you did not include a BytesReceived event to catch them when (and if) they arrive.
That can’t be right.

I can’t find the controls that let me assign this thread to some one else with more BLE Heart Rate Monitor experience.

I suggest you add a post to this thread asking them (@Martin_Wood) to visit this thread Reading BLE service in Appinventor
and help you.

I’m in over my head.

One last thought for you …

Increasing the font size to 50 on the output label opens you up to seeing nothing because the text is too big to display. Leave that out for now.

It doesn’t hurt to have extra event blocks for the different when BLe.somethingReceived types.
If you go down the list of them (Strings, Reals, Shorts, etc.) and add events to catch and display them, maybe you will get lucky.

Hi,
There are several codes for BLE_heartrate, you have loaded that code in the device, because it can have this other …

Hello
I have read all your contributions, and thanks to your help I am improving:

I have tested this program with every data type. In some cases I already receive data, then the UUIDs are correct.
But I don’t get what I should. The program sends values between 100 and 174 each 500 ms:

void updateSensorValue () {
// Do blocking calls or whatever is necessary for sensor polling.
// In our case, we simply update the HRM measurement.
hrmCounter ++;

 // 100 <= HRM bps <= 175
 if (hrmCounter == 175) {
     hrmCounter = 100;
 }

 hrServicePtr-> updateHeartRate (hrmCounter);

}

And this is exactly what I receive for each type of data:

Shorts:
(26112)
(26368)
(26624)
(26880)

Strings:
(f)
(g)
(h)
(i)

Floats:
()
()
()
()

Integers:
()
()
()
()

Bytes:
Nothing

I don’t know how to fine tune this.
Thanks again.

Only
RegisterForString
and
StringReceived

hrmCounter ++;

f
g
h
i

I've adapted it to this, and the reading remains the same.