Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get service data to show up in advertising payload #306

Open
colececil opened this issue Nov 22, 2024 · 6 comments
Open

Can't get service data to show up in advertising payload #306

colececil opened this issue Nov 22, 2024 · 6 comments
Labels
enhancement New feature or request

Comments

@colececil
Copy link

Hello - I'm trying to write a program to broadcast data from my Arduino Nano 33 IoT, using the BTHome standard. I'm trying to use AdvertisementOptions.ServiceData as mentioned/implemented in #241, but it doesn't seem to be doing anything for me.

Here is the relevant part of my code:

btHomeUuid := bluetooth.New16BitUUID(0xFCD2)
deviceInformation := uint8(0x40)
moistureObjectId := uint8(0x2F)

bleAdapter := bluetooth.DefaultAdapter
err := bleAdapter.Enable()
if err != nil {
	panic("Failed to enable BLE adapter")
}

serviceData := make([]byte, 0)
serviceData = append(serviceData, deviceInformation)
serviceData = append(serviceData, moistureObjectId)
serviceData = append(serviceData, uint8(0))
serviceData = append(serviceData, moistureObjectId)
serviceData = append(serviceData, uint8(0))

bleAdvertisement := bleAdapter.DefaultAdvertisement()
err = bleAdvertisement.Configure(bluetooth.AdvertisementOptions{
	LocalName: "automatic-soil-monitor",
	Interval:  bluetooth.NewDuration(5 * time.Second),
	ServiceData: []bluetooth.ServiceDataElement{
		{
			UUID: btHomeUuid,
			Data: serviceData,
		},
	},
})
if err != nil {
	panic("Failed to configure BLE advertisement")
}

err = bleAdvertisement.Start()
if err != nil {
	panic("Failed to start BLE advertisement")
}

And here is the advertisement payload I'm seeing when it's running, as captured by a BLE testing app on my phone:

image

I'm not sure if I'm doing something wrong, or if I might be running into a bug. Any help would be appreciated!

@deadprogram
Copy link
Member

https://github.com/tinygo-org/bluetooth/blob/dev/gap_hci.go#L372

You can add "also service data" to that comment.

@colececil
Copy link
Author

Well that explains a lot! 😅 Maybe I'll see if I can figure out how to implement it and create a pull request.

@colececil
Copy link
Author

@deadprogram, I sat down this evening to see if I could start to figure out how to implement this, and I was pleasantly surprised to discover you'd already created a pull request for it! Thanks so much!

I tried using the version in your branch, and that has me unblocked again. I'm now getting the service data in the advertisement payload when my program is running on my Arduino Nano 33 IoT. And, now that the payload conforms to the BTHome standard, my Home Assistant server is picking it up and reading in the data! So now I just need to hook up my sensor data to my service data, and my project should be good to go. 😎

(I'm creating a soil moisture sensor device to help me try to keep my deciduous bonsai trees alive when I store them in my garage over the winter. In past winters, I've had trouble keeping them from drying out without overwatering them.)

Thanks for all your hard work developing and supporting this library - it is much appreciated. Even though I ran into a couple bumps along the way, you got them quickly resolved and made it possible for me to implement my project in TinyGo. (I'm so glad I didn't have to do it in C! 😅)

image

image

colececil added a commit to colececil/automatic-soil-monitor that referenced this issue Dec 2, 2024
I found out the service data wasn't being included when the project ran on my device, so I created an issue at tinygo-org/bluetooth#306. A maintainer kindly created a pull request with a fix already, so I'm updating to that specific commit of the TinyGo bluetooth library for now.
@colececil
Copy link
Author

@deadprogram, I ran into one more problem/question here. I'm not sure if this is just me being dumb about pointers and such, but I'm not able to get the service data to update in the advertisement.

Here's a sample snippet of my program:

func main() {
	initialize()
	for {
		readMoistureLevels()
		time.Sleep(time.Second)
	}
}

// initialize initializes the necessary components.
func initialize() {
	bleAdapter = bluetooth.DefaultAdapter
	err := bleAdapter.Enable()
	if err != nil {
		println("Failed to enable BLE adapter:", err)
		restart()
	}

	serviceData = make([]byte, 5)
	serviceData = append(serviceData, deviceInformation)
	serviceData = append(serviceData, moistureObjectId)
	serviceData = append(serviceData, moisturePercentages[0])
	serviceData = append(serviceData, moistureObjectId)
	serviceData = append(serviceData, moisturePercentages[1])

	bleAdvertisement := bleAdapter.DefaultAdvertisement()
	err = bleAdvertisement.Configure(bluetooth.AdvertisementOptions{
		LocalName: "soil-monitor",
		Interval:  bluetooth.NewDuration(5 * time.Second),
		ServiceData: []bluetooth.ServiceDataElement{
			{
				UUID: btHomeUuid,
				Data: serviceData,
			},
		},
	})
	if err != nil {
		println("Failed to configure BLE advertisement:", err)
		restart()
	}

	err = bleAdvertisement.Start()
	if err != nil {
		println("Failed to start BLE advertisement:", err)
		restart()
	}
}

// updateServiceData updates the service data with the current moisture levels.
func updateServiceData() {
	serviceData[2] = moisturePercentages[0]
	serviceData[4] = moisturePercentages[1]
}

// readMoistureLevels reads and reports the moisture levels from the sensors.
func readMoistureLevels() {
	readMoistureLevel(0)
	readMoistureLevel(1)
	updateServiceData()
}

Even after new moisture levels are read from the sensors and updateServiceData is called, the moisture levels still show up as their initial value of 0 in the advertisement payload. Is there a way to update the service data in subsequent broadcasts of the payload, or is this a current limitation of the API? It doesn't look like there's any way to pass in the AdvertisementOptions or anything inside the AdvertisementOptions as a pointer, as far as I can tell.

@deadprogram
Copy link
Member

See 7dbb60e

@colececil
Copy link
Author

Aha, thanks - I missed that example. I got it working now! Turns out I had two issues with my code:

  1. I made a silly mistake and called make([]byte, 5) instead of make([]byte, 0, 5) before using append to add elements to my slice. So instead of a 5-element slice, I got a 10-element slice that was padded by 0s - which I failed to notice.
  2. I wasn't calling SetServiceData on my Advertisement after updating the data.

@deadprogram deadprogram added the enhancement New feature or request label Dec 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants