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

examples: add channelscan example that shows use of goroutines and channels #23

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from

Conversation

deadprogram
Copy link
Member

This PR adds the "channelscan" example that shows use of goroutines and channels with Go Bluetooth.

@aykevl
Copy link
Member

aykevl commented Sep 22, 2020

I haven't looked at the code yet, but can you rebase this PR? There is a conflict.

@deadprogram
Copy link
Member Author

Rebased.

examples/channelscan/main.go Outdated Show resolved Hide resolved
@deadprogram deadprogram force-pushed the channelscan-example branch from f3862f6 to afe3dff Compare June 8, 2023 11:19
@deadprogram
Copy link
Member Author

Corrections made and ready for review again.

Copy link
Member

@aykevl aykevl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but see the comments below.

Comment on lines +65 to +71
func startTicker() {
for {
time.Sleep(3 * time.Second)
ticker <- true
}

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think nowadays you can also use time.NewTicker for this.

println("scanning...")

err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
devices <- device
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is actually still a short race condition here.
Basically, the scan result will get overwritten once the next scan result comes in. So if the main goroutine isn't fast enough to process this scan result, it might display some duplicate scan results (and miss one of them).

I'm not sure how (or if) this should be fixed. One possibly expensive way is to stop scanning on each incoming packet, and start it again after the result is displayed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One solution would be to create local struct with fields we need from bluetooth.ScanResult and make the channel of that type

Suggested change
devices <- device
devices <- scanData{
name: device.LocalName(),
address: device.Address.String(),
rssi: device.RSSI,
}

Copy link
Member

@aykevl aykevl Sep 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Both device.LocalName() and device.Address.String() do heap allocations which are not allowed inside interrupts. If heap allocations were allowed, that would indeed be the most sensible solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, looking again at the code, the issue is bigger: devices <- device is a blocking send. This will usually work if the main goroutine is fast enough, but there is no guarantee that it will be. It would be better to have a non-blocking send like this:

select {
case devices <- device:
default:
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aykevl I actually ran my suggestion on NRF52 board and didn't get panic even if -print-allocs shows heap allocation. Looking at the Scan implementation for NRF52, the provided callback is called directly (from the context of Scan) and not from interrupt handler. Also there is a comment that scan is stopped when first result comes and a call to restart the scan every time the result comes. If this is correct (I would have to check SD documentation) it would mean that on NRF52 device there would be no race (with my suggestion) and also that blocking the Scan callback would not be an issue since scan is stopped during it's execution and restarted after (which explains why I get so many duplicate results in the scan). I didn't look at implementations for other targets but I hope the scan callback is also decoupled there because it is much more convenient that way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HattoriHanzo031 I checked the code and you are correct. The code in the PR seems fine.

Copy link
Member

@aykevl aykevl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. @deadprogram can you rebase the PR?

println("scanning...")

err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
devices <- device
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HattoriHanzo031 I checked the code and you are correct. The code in the PR seems fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants