-
Notifications
You must be signed in to change notification settings - Fork 142
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
deadprogram
wants to merge
2
commits into
dev
Choose a base branch
from
channelscan-example
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// This example program shows using Go routines and channels to coordinate | ||
// BLE scanning. | ||
// | ||
// The first Go routine starts scanning using the BLE adaptor. When it finds | ||
// a new device, it puts the information into a channel so it can be displayed. | ||
// | ||
// The second Go routine is a ticker that puts a "true" value into a channel every 3 seconds. | ||
// | ||
// The main function uses a select{} statement to wait until one of the two channels is unblocked | ||
// by receiving data. If a new device is found, the boolean variable named "found" will | ||
// be set to true, so that the timeout is reset for each 3 second period. | ||
package main | ||
|
||
import ( | ||
"time" | ||
|
||
"tinygo.org/x/bluetooth" | ||
) | ||
|
||
var ( | ||
adapter = bluetooth.DefaultAdapter | ||
devices = make(chan bluetooth.ScanResult, 1) | ||
ticker = make(chan bool, 1) | ||
found = true | ||
) | ||
|
||
func main() { | ||
// Enable BLE interface. | ||
if err := adapter.Enable(); err != nil { | ||
panic("failed to enable adaptor:" + err.Error()) | ||
} | ||
|
||
// Start scanning | ||
go performScan() | ||
|
||
// Start timeout ticker | ||
go startTicker() | ||
|
||
// Wait for devices to be scanned | ||
for { | ||
select { | ||
case device := <-devices: | ||
found = true | ||
println("found device:", device.Address.String(), device.RSSI, device.LocalName()) | ||
case <-ticker: | ||
if !found { | ||
println("no devices found in last 3 seconds...") | ||
} | ||
found = false | ||
} | ||
} | ||
} | ||
|
||
func performScan() { | ||
println("scanning...") | ||
|
||
err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) { | ||
devices <- device | ||
}) | ||
if err != nil { | ||
panic("failed to scan:" + err.Error()) | ||
} | ||
} | ||
|
||
func startTicker() { | ||
for { | ||
time.Sleep(3 * time.Second) | ||
ticker <- true | ||
} | ||
|
||
} | ||
Comment on lines
+65
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think nowadays you can also use |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 typeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Both
device.LocalName()
anddevice.Address.String()
do heap allocations which are not allowed inside interrupts. If heap allocations were allowed, that would indeed be the most sensible solution.There was a problem hiding this comment.
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:There was a problem hiding this comment.
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 theScan
implementation for NRF52, the provided callback is called directly (from the context ofScan
) 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 theScan
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.There was a problem hiding this comment.
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.