diff --git a/Makefile b/Makefile index da26c98..d010152 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,8 @@ smoketest: go build -o /tmp/test ./xiao-ble/ble-led-client tinygo build -o /tmp/test.hex --target xiao-ble --size short ./xiao-ble/ble-led-client tinygo build -o /tmp/test.hex --target xiao-ble --size short ./xiao-ble/ble-led-client-xiao + tinygo build -o /tmp/test.hex --target xiao-ble --size short ./xiao-ble-laptimer/laptimer-xiao + go build -o /tmp/test ./xiao-ble-laptimer/laptimer fmt-check: @unformatted=$$(gofmt -l `find . -name "*.go"`); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 diff --git a/README.md b/README.md index 35e9228..1188413 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,10 @@ This is a Demo using Bluetooth. [![](https://img.youtube.com/vi/HWBxuMbNUTI/0.jpg)](https://www.youtube.com/watch?v=HWBxuMbNUTI) +* [./xiao-ble-laptimer/](./xiao-ble-laptimer/) + +![](./xiao-ble-laptimer/xiao-ble-laptimer.jpg) + ## wioterminal initialize diff --git a/xiao-ble-laptimer/README.md b/xiao-ble-laptimer/README.md new file mode 100644 index 0000000..c442aec --- /dev/null +++ b/xiao-ble-laptimer/README.md @@ -0,0 +1,50 @@ +# xiao-ble-laptimer examples + +TinyGo example of XIAO BLE. +This is a Demo using Bluetooth. + +* [./xiao-ble-laptimer/laptimer/](./ble-laptimer/laptimer/) +* [./xiao-ble-laptimer/laptimer-xiao/](./laptimer-xiao/) + +![](./xiao-ble-laptimer.jpg) + + +## Usage + +First flash it to an nRF52840 microcontroller such as xiao-ble. + +``` +$ tinygo flash --target xiao-ble --size short ./xiao-ble-laptimer/laptimer-xiao + code data bss | flash ram + 11228 156 8012 | 11384 8168 +``` + +Then, start a program to measure the lap time on the computer. +If the BLE scan fails for 3 minutes, the next lap is considered to have progressed. + + +``` +$ go run ./xiao-ble-laptimer/laptimer +``` + +To change to a value other than 3 minutes, change the following settings. + + +```go + thresh := 3 * time.Minute +``` + +Below is the actual log file. + +``` +2022/10/22 10:46:25 2562047:47:16 0 found +2022/10/22 10:53:06 lost +2022/10/22 11:26:39 00:40:14 1 found +2022/10/22 11:30:02 lost +2022/10/22 11:38:02 00:11:23 2 found +2022/10/22 11:41:19 lost +2022/10/22 11:48:16 00:10:13 3 found +2022/10/22 11:51:31 lost +2022/10/22 11:58:43 00:10:26 4 found +2022/10/22 12:01:58 lost +``` diff --git a/xiao-ble-laptimer/laptimer-xiao/main.go b/xiao-ble-laptimer/laptimer-xiao/main.go new file mode 100644 index 0000000..abcfedd --- /dev/null +++ b/xiao-ble-laptimer/laptimer-xiao/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "machine" + "time" + + "tinygo.org/x/bluetooth" +) + +var adapter = bluetooth.DefaultAdapter +var ledColor = []byte{0x00, 0x00, 0x00, 0x00} + +var ( + serviceUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x01, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) + charUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x02, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) +) + +func main() { + println("starting") + must("enable BLE stack", adapter.Enable()) + adv := adapter.DefaultAdvertisement() + must("config adv", adv.Configure(bluetooth.AdvertisementOptions{ + LocalName: "No 10 - TinyGo LapTimer", + Interval: bluetooth.NewDuration(32 * time.Millisecond), + })) + must("start adv", adv.Start()) + + led := machine.LED + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + //var err error + for { + //fmt.Printf("start\r\n") + //err = adv.Start() + //if err != nil { + // fmt.Printf("err %s\r\n", err.Error()) + //} + + led.Low() + time.Sleep(time.Millisecond * 100) + led.High() + time.Sleep(time.Millisecond * 100) + //fmt.Printf("stop\r\n") + //err = adv.Stop() + //if err != nil { + // fmt.Printf("err %s\r\n", err.Error()) + //} + } +} + +func must(action string, err error) { + if err != nil { + panic("failed to " + action + ": " + err.Error()) + } +} diff --git a/xiao-ble-laptimer/laptimer/main.go b/xiao-ble-laptimer/laptimer/main.go new file mode 100644 index 0000000..2ebec28 --- /dev/null +++ b/xiao-ble-laptimer/laptimer/main.go @@ -0,0 +1,151 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "time" + + "tinygo.org/x/bluetooth" +) + +// bluetooth +var ( + adapter = bluetooth.DefaultAdapter + dc bluetooth.DeviceCharacteristic + serviceUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x01, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) + charUUID = bluetooth.NewUUID([16]byte{0xa0, 0xb4, 0x00, 0x02, 0x92, 0x6d, 0x4d, 0x61, 0x98, 0xdf, 0x8c, 0x5c, 0x62, 0xee, 0x53, 0xb3}) +) + +func getCurrentStatus(file string) (bool, time.Time, int, error) { + //time.Local = time.FixedZone("Asia/Tokyo", 9*60*60) + found := false + var prevTimeOfLap time.Time + laps := 0 + + r, err := os.Open(file) + if err != nil { + return found, prevTimeOfLap, laps, err + } + defer r.Close() + + scanner := bufio.NewScanner(r) + lastLine := "" + lastLineWithFound := "" + for scanner.Scan() { + lastLine = scanner.Text() + fmt.Printf("%s\n", lastLine) + if strings.HasSuffix(lastLine, " found") { + lastLineWithFound = lastLine + found = true + } else { + found = false + } + } + + prevTimeOfLap, err = time.Parse("2006/01/02 15:04:05", lastLineWithFound[:19]) + if err == nil { + prevTimeOfLap = prevTimeOfLap.Add(-1 * 9 * time.Hour) + } + + spl := strings.Split(lastLineWithFound, " ") + x, _ := strconv.ParseUint(spl[3], 10, 64) + laps = int(x) + 1 + //fmt.Printf("prev : %s\n", prevTimeOfLap.In(time.Local)) + //fmt.Printf("%s\n", lastLineWithFound) + + return found, prevTimeOfLap, laps, nil +} + +func run() error { + found, prevTimeOfLap, laps, err := getCurrentStatus("data.txt") + if err != nil { + //return err + } + + wfh, err := os.OpenFile("data.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + return err + } + defer wfh.Close() + + w := io.MultiWriter(os.Stdout, wfh) + log.SetOutput(w) + + must("enable BLE stack", adapter.Enable()) + + // Scan for NUS peripheral. + timeFromLastFound := time.Now() + //thresh := 1 * time.Second + thresh := 3 * time.Minute + if debug { + thresh = 1000 * time.Millisecond + } + // found -> lost の間は thresh 以上の時間が必要 + // 逆に lost -> found の間も thresh 以上の時間が必要 + for { + cont := true + //println("scanning") + for cont { + err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) { + //fmt.Printf("%#v\n", result.Address.String()) + //fmt.Printf("%#v %d\n", result.Address.String(), -1*time.Until(timer)) + if result.LocalName() != "No 10 - TinyGo LapTimer" { + if -1*time.Until(timeFromLastFound) > thresh { + if found { + log.Printf("lost") + //time.Sleep(thresh) + } + found = false + } + return + } + + // Stop the scan. + err := adapter.StopScan() + if err != nil { + // Unlikely, but we can't recover from this. + println("failed to stop the scan:", err.Error()) + } + }) + if err != nil { + println("could not start a scan:", err.Error()) + return err + } else { + if !found { + lapTime := int(time.Now().Sub(prevTimeOfLap).Seconds()) + log.Printf("%02d:%02d:%02d %d found", lapTime/3600, (lapTime/60)%60, lapTime%60, laps) + laps++ + prevTimeOfLap = time.Now() + } + found = true + timeFromLastFound = time.Now() + cont = false + } + } + } + return nil +} + +var debug bool + +func main() { + flag.BoolVar(&debug, "debug", debug, "debug") + flag.Parse() + + err := run() + if err != nil { + log.Fatal(err) + } +} + +func must(action string, err error) { + if err != nil { + panic("failed to " + action + ": " + err.Error()) + } +} diff --git a/xiao-ble-laptimer/xiao-ble-laptimer.jpg b/xiao-ble-laptimer/xiao-ble-laptimer.jpg new file mode 100644 index 0000000..f5ccd91 Binary files /dev/null and b/xiao-ble-laptimer/xiao-ble-laptimer.jpg differ