diff --git a/.canon.yaml b/.canon.yaml index 798511f..78246ba 100644 --- a/.canon.yaml +++ b/.canon.yaml @@ -1,5 +1,5 @@ # This file provides project-level configuration for the canon dev environment utility. https://github.com/viamrobotics/canon -MODULE: +tdk-invensense: default: true image_amd64: ghcr.io/viamrobotics/rdk-devenv:amd64-cache image_arm64: ghcr.io/viamrobotics/rdk-devenv:arm64-cache @@ -9,7 +9,7 @@ MODULE: group: testbot persistent: true -MODULE-antique: +tdk-invensense-antique: image_amd64: ghcr.io/viamrobotics/antique2:amd64-cache image_arm64: ghcr.io/viamrobotics/antique2:arm64-cache minimum_date: 2023-10-26T20:00:00.0Z diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 3a6d0dd..80704b6 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -21,14 +21,14 @@ jobs: script: | let prNumber = context.payload.pull_request && context.payload.pull_request.number; try { - await github.rest.issues.removeLabel({owner: "viam-modules", repo: "MODULE", issue_number: prNumber, name: "safe to test"}); + await github.rest.issues.removeLabel({owner: "viam-modules", repo: "tdk-invensense", issue_number: prNumber, name: "safe to test"}); } catch (err) { core.info(`Non-fatal error ${err}, while trying to remove 'safe to test' label.`); } let orgResp = await github.rest.orgs.checkMembershipForUser({org: "viam-modules", username: context.payload.sender.login}); if (orgResp.status === 204) { // order of labeling events must be preserved, so two seperate calls - await github.rest.issues.addLabels({owner: "viam-modules", repo: "MODULE", issue_number: prNumber, labels: ["safe to test"]}); + await github.rest.issues.addLabels({owner: "viam-modules", repo: "tdk-invensense", issue_number: prNumber, labels: ["safe to test"]}); return true; } return false; diff --git a/.github/workflows/pullrequest-trusted.yml b/.github/workflows/pullrequest-trusted.yml index cbde6c8..325dd76 100644 --- a/.github/workflows/pullrequest-trusted.yml +++ b/.github/workflows/pullrequest-trusted.yml @@ -12,10 +12,10 @@ on: jobs: test: if: (github.event.label.name == 'safe to test' || github.event.label.name == 'appimage') - uses: viam-modules/MODULE/.github/workflows/test.yml@main + uses: viam-modules/tdk-invensense/.github/workflows/test.yml@main secrets: MONGODB_TEST_OUTPUT_URI: ${{ secrets.MONGODB_TEST_OUTPUT_URI }} DOCKER_PUBLIC_READONLY_PAT: ${{ secrets.DOCKER_PUBLIC_READONLY_PAT }} license_finder: - uses: viam-modules/MODULE/.github/workflows/license_finder.yml@main + uses: viam-modules/tdk-invensense/.github/workflows/license_finder.yml@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7598d2..ba4bd9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,10 +46,10 @@ jobs: - name: Build and package run: | - canon --profile MODULE + canon --profile tdk-invensense TARGET_OS=${{ matrix.platform }} TARGET_ARCH=${{ matrix.arch }} make module - - name: Upload MODULE module to registry + - name: Upload tdk-invensense module to registry uses: viamrobotics/upload-module@main with: meta-path: meta.json diff --git a/MODEL/MODEL.go b/MODEL/MODEL.go deleted file mode 100644 index e69de29..0000000 diff --git a/Makefile b/Makefile index a348bb3..a333bbf 100644 --- a/Makefile +++ b/Makefile @@ -9,13 +9,13 @@ endif module: build rm -f $(BIN_OUTPUT_PATH)/module.tar.gz - tar czf $(BIN_OUTPUT_PATH)/module.tar.gz $(BIN_OUTPUT_PATH)/MODULE meta.json + tar czf $(BIN_OUTPUT_PATH)/module.tar.gz $(BIN_OUTPUT_PATH)/tdk-invensense meta.json build: build-go build-go: - rm -f $(BIN_OUTPUT_PATH)/MODULE - go build -tags no_cgo,osusergo,netgo -ldflags="-extldflags=-static $(COMMON_LDFLAGS)" -o $(BIN_OUTPUT_PATH)/MODULE main.go + rm -f $(BIN_OUTPUT_PATH)/tdk-invensense + go build -tags no_cgo,osusergo,netgo -ldflags="-extldflags=-static $(COMMON_LDFLAGS)" -o $(BIN_OUTPUT_PATH)/tdk-invensense main.go tool-install: GOBIN=`pwd`/$(TOOL_BIN) go install \ diff --git a/README.md b/README.md index ac25dd7..56ce650 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# [`MODULE` module](https://github.com/viam-modules/MODULE) +# [`tdk-invensense` module](https://github.com/viam-modules/tdk-invensense) -This [MODULE module](https://app.viam.com/module/viam/MODULE) implements a MODULE [MODEL COMPONENT](), used for using the [`rdk:component:COMPONENT` API](https://docs.viam.com/appendix/apis/components/COMPONENT/). +This [tdk-invensense module](https://app.viam.com/module/viam/tdk-invensense) implements a tdk-invensense [mpu6050 movement_sensor](), used for using the [`rdk:component:movement_sensor` API](https://docs.viam.com/appendix/apis/components/movement_sensor/). > [!NOTE] -> Before configuring your COMPONENT, you must [create a machine](https://docs.viam.com/cloud/machines/#add-a-new-machine). +> Before configuring your movement_sensor, you must [create a machine](https://docs.viam.com/cloud/machines/#add-a-new-machine). -## Configure your MODEL COMPONENT +## Configure your mpu6050 movement_sensor Navigate to the [**CONFIGURE** tab](https://docs.viam.com/configure/) of your [machine](https://docs.viam.com/fleet/machines/) in the [Viam app](https://app.viam.com/). -[Add COMPONENT / MODULE:MODEL to your machine](https://docs.viam.com/configure/#components). +[Add movement_sensor / tdk-invensense:mpu6050 to your machine](https://docs.viam.com/configure/#components). -On the new component panel, copy and paste the following attribute template into your COMPONENT's attributes field: +On the new component panel, copy and paste the following attribute template into your movement_sensor's attributes field: ```json { @@ -20,22 +20,22 @@ On the new component panel, copy and paste the following attribute template into ### Attributes -The following attributes are available for `viam:MODULE:MODEL` COMPONENTs: +The following attributes are available for `viam:tdk-invensense:mpu6050` movement_sensors: | Attribute | Type | Required? | Description | | --------- | ---- | --------- | ---------- | -| `i2c_bus` | string | **Required** | The index of the I2C bus on the board that the COMPONENT is wired to. | -| `i2c_address` | string | Optional | Default: `0x77`. The [I2C device address](https://learn.adafruit.com/i2c-addresses/overview) of the COMPONENT. | +| `i2c_bus` | string | **Required** | The index of the I2C bus on the board that the movement_sensor is wired to. | +| `i2c_address` | string | Optional | Default: `0x77`. The [I2C device address](https://learn.adafruit.com/i2c-addresses/overview) of the movement_sensor. | ## Example configuration -### `viam:MODULE:MODEL` +### `viam:tdk-invensense:mpu6050` ```json { - "name": "", - "model": "viam:MODULE:MODEL", - "type": "COMPONENT", + "name": "", + "model": "viam:tdk-invensense:mpu6050", + "type": "movement_sensor", "namespace": "rdk", "attributes": { }, @@ -44,6 +44,6 @@ The following attributes are available for `viam:MODULE:MODEL` COMPONENTs: ``` ### Next Steps -- To test your COMPONENT, expand the **TEST** section of its configuration pane or go to the [**CONTROL** tab](https://docs.viam.com/fleet/control/). -- To write code against your COMPONENT, use one of the [available SDKs](https://docs.viam.com/sdks/). -- To view examples using a COMPONENT component, explore [these tutorials](https://docs.viam.com/tutorials/). \ No newline at end of file +- To test your movement_sensor, expand the **TEST** section of its configuration pane or go to the [**CONTROL** tab](https://docs.viam.com/fleet/control/). +- To write code against your movement_sensor, use one of the [available SDKs](https://docs.viam.com/sdks/). +- To view examples using a movement_sensor component, explore [these tutorials](https://docs.viam.com/tutorials/). \ No newline at end of file diff --git a/go.mod b/go.mod index d903c1e..4acce2a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module MODULE +module tdk-invensense go 1.23.0 @@ -7,9 +7,13 @@ require ( github.com/axw/gocov v1.1.0 github.com/edaniels/golinters v0.0.5-0.20220906153528-641155550742 github.com/fullstorydev/grpcurl v1.8.6 + github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 github.com/golangci/golangci-lint v1.61.0 + github.com/kellydunn/golang-geo v0.7.0 + github.com/pkg/errors v0.9.1 github.com/rhysd/actionlint v1.6.24 go.viam.com/rdk v0.48.2 + go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2 go.viam.com/utils v0.1.110 gotest.tools/gotestsum v1.10.0 ) @@ -42,11 +46,15 @@ require ( github.com/alexkohler/nakedret/v2 v2.0.4 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect + github.com/apache/arrow/go/arrow v0.0.0-20201229220542-30ce2eb5d4dc // indirect github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect + github.com/aybabtme/uniplot v0.0.0-20151203143629-039c559e5e7e // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect github.com/bkielbasa/cyclop v1.2.1 // indirect + github.com/blackjack/webcam v0.6.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bluenviron/gortsplib/v4 v4.8.0 // indirect github.com/bombsimon/wsl/v4 v4.4.1 // indirect @@ -65,6 +73,8 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect + github.com/chewxy/hm v1.0.0 // indirect + github.com/chewxy/math32 v1.0.8 // indirect github.com/ckaznocha/intrange v0.2.0 // indirect github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect @@ -73,6 +83,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect + github.com/disintegration/imaging v1.6.2 // indirect github.com/dnephin/pflag v1.0.7 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/edaniels/golog v0.0.0-20230215213219-28954395e8d0 // indirect @@ -87,15 +98,22 @@ require ( github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/firefart/nonamedreturns v1.0.5 // indirect + github.com/fogleman/gg v1.3.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect + github.com/gen2brain/malgo v0.11.21 // indirect github.com/ghostiam/protogetter v0.3.6 // indirect + github.com/go-audio/audio v1.0.0 // indirect + github.com/go-audio/riff v1.0.0 // indirect + github.com/go-audio/transforms v0.0.0-20180121090939-51830ccc35a5 // indirect + github.com/go-audio/wav v1.1.0 // indirect github.com/go-critic/go-critic v0.11.4 // indirect github.com/go-fonts/liberation v0.3.0 // indirect github.com/go-gl/mathgl v1.0.0 // indirect github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-nlopt/nlopt v0.0.0-20230219125344-443d3362dcb5 // indirect github.com/go-pdf/fpdf v0.6.0 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect @@ -109,9 +127,9 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/flock v0.12.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -123,6 +141,7 @@ require ( github.com/golangci/revgrep v0.5.3 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect github.com/gonuts/binary v0.2.0 // indirect + github.com/google/flatbuffers v2.0.6+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect @@ -150,7 +169,6 @@ require ( github.com/jjti/go-spancheck v0.6.2 // indirect github.com/julz/importas v0.1.0 // indirect github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect - github.com/kellydunn/golang-geo v0.7.0 // indirect github.com/kisielk/errcheck v1.7.0 // indirect github.com/kkHAIKE/contextcheck v1.1.5 // indirect github.com/klauspost/compress v1.16.5 // indirect @@ -169,6 +187,7 @@ require ( github.com/lestrrat-go/jwx v1.2.29 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/lib/pq v1.10.9 // indirect + github.com/lmittmann/ppm v1.0.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufeee/execinquery v1.2.1 // indirect github.com/macabu/inamedparam v0.1.3 // indirect @@ -186,6 +205,8 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.7.0 // indirect github.com/moricho/tparallel v0.3.2 // indirect + github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 // indirect + github.com/muesli/kmeans v0.3.1 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect @@ -199,6 +220,7 @@ require ( github.com/pion/interceptor v0.1.29 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.12 // indirect + github.com/pion/mediadevices v0.6.4 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.14 // indirect github.com/pion/rtp v1.8.7 // indirect @@ -208,7 +230,7 @@ require ( github.com/pion/stun v0.6.1 // indirect github.com/pion/transport/v2 v2.2.10 // indirect github.com/pion/turn/v2 v2.1.6 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pion/webrtc/v3 v3.2.36 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polyfloyd/go-errorlint v1.6.0 // indirect @@ -266,6 +288,8 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xen0n/gosmopolitan v1.2.2 // indirect + github.com/xfmoulet/qoi v0.2.0 // indirect + github.com/xtgo/set v1.0.0 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect @@ -288,7 +312,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.viam.com/api v0.1.350 // indirect - go.viam.com/test v1.1.1-0.20220913152726-5da9916c08a2 // indirect + go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect @@ -302,6 +326,7 @@ require ( golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gonum.org/v1/gonum v0.12.0 // indirect gonum.org/v1/plot v0.12.0 // indirect google.golang.org/api v0.196.0 // indirect @@ -312,10 +337,16 @@ require ( google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gorgonia.org/tensor v0.9.24 // indirect + gorgonia.org/vecf32 v0.9.0 // indirect + gorgonia.org/vecf64 v0.9.0 // indirect honnef.co/go/tools v0.5.1 // indirect mvdan.cc/gofumpt v0.7.0 // indirect mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect nhooyr.io/websocket v1.8.7 // indirect + periph.io/x/conn/v3 v3.7.0 // indirect + periph.io/x/host/v3 v3.8.1-0.20230331112814-9f0d9f7d76db // indirect ) diff --git a/go.sum b/go.sum index 2d19bd2..56a6cd1 100644 --- a/go.sum +++ b/go.sum @@ -233,6 +233,7 @@ github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/chewxy/hm v1.0.0 h1:zy/TSv3LV2nD3dwUEQL2VhXeoXbb9QkpmdRAVUFiA6k= github.com/chewxy/hm v1.0.0/go.mod h1:qg9YI4q6Fkj/whwHR1D+bOGeF7SniIP40VweVepLjg0= +github.com/chewxy/math32 v1.0.0/go.mod h1:Miac6hA1ohdDUTagnvJy/q+aNnEk16qWUdb8ZVhvCN0= github.com/chewxy/math32 v1.0.8 h1:fU5E4Ec4Z+5RtRAi3TovSxUjQPkgRh+HbP7tKB2OFbM= github.com/chewxy/math32 v1.0.8/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -583,6 +584,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs= +github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v2.0.6+incompatible h1:XHFReMv7nFFusa+CEokzWbzaYocKXI6C7hdU5Kgh9Lw= github.com/google/flatbuffers v2.0.6+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -761,6 +763,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= +github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -813,6 +817,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -968,6 +973,7 @@ github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGq github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8= github.com/mozilla/tls-observatory v0.0.0-20201209171846-0547674fceff/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s= +github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 h1:p4A2Jx7Lm3NV98VRMKlyWd3nqf8obft8NfXlAUmqd3I= github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY= github.com/muesli/kmeans v0.3.1 h1:KshLQ8wAETfLWOJKMuDCVYHnafddSa1kwGh/IypGIzY= @@ -1312,6 +1318,7 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1391,6 +1398,7 @@ github.com/viamrobotics/evdev v0.1.3/go.mod h1:N6nuZmPz7HEIpM7esNWwLxbYzqWqLSZkf github.com/viamrobotics/webrtc/v3 v3.99.10 h1:ykE14wm+HkqMD5Ozq4rvhzzfvnXAu14ak/HzA1OCzfY= github.com/viamrobotics/webrtc/v3 v3.99.10/go.mod h1:ziH7/S52IyYAeDdwUUl5ZTbuyKe47fWorAz+0z5w6NA= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= +github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA= github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -1556,6 +1564,8 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ= @@ -1628,6 +1638,7 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1728,6 +1739,7 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1994,6 +2006,7 @@ google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU= google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4= @@ -2028,6 +2041,7 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v0.0.0-20200910201057-6591123024b3/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/main.go b/main.go index 9e7afc2..aa82a00 100644 --- a/main.go +++ b/main.go @@ -4,13 +4,16 @@ package main import ( "context" + "tdk-invensense/mpu6050" + + "go.viam.com/rdk/components/movementsensor" "go.viam.com/rdk/logging" "go.viam.com/rdk/module" "go.viam.com/utils" ) func main() { - utils.ContextualMain(mainWithArgs, module.NewLoggerFromArgs("MODULE")) + utils.ContextualMain(mainWithArgs, module.NewLoggerFromArgs("tdk-invensense")) } func mainWithArgs(ctx context.Context, args []string, logger logging.Logger) error { @@ -19,7 +22,7 @@ func mainWithArgs(ctx context.Context, args []string, logger logging.Logger) err return err } - if err = module.AddModelFromRegistry(ctx, COMPONENT.API, MODEL.Model); err != nil { + if err = module.AddModelFromRegistry(ctx, movementsensor.API, mpu6050.Model); err != nil { return err } diff --git a/meta.json b/meta.json index f8d7db0..910036e 100644 --- a/meta.json +++ b/meta.json @@ -1,13 +1,13 @@ { "$schema": "https://dl.viam.dev/module.schema.json", - "module_id": "viam:MODULE", + "module_id": "viam:tdk-invensense", "visibility": "public", - "url": "https://github.com/viam-modules/MODULE", - "description": "Go module for MODULE MODEL COMPONENT, compatible with Viam", + "url": "https://github.com/viam-modules/tdk-invensense", + "description": "Go module for tdk-invensense mpu6050 movement_sensor, compatible with Viam", "models": [ { - "api": "rdk:component:COMPONENT", - "model": "viam:MODULE:MODEL" + "api": "rdk:component:movement_sensor", + "model": "viam:tdk-invensense:mpu6050" } ], "build": { @@ -15,5 +15,5 @@ "path": "bin/module.tar.gz", "arch" : ["linux/arm64", "linux/amd64", "darwin/arm64"] }, - "entrypoint": "bin/MODULE" + "entrypoint": "bin/tdk-invensense" } \ No newline at end of file diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go new file mode 100644 index 0000000..4e4c1c0 --- /dev/null +++ b/mpu6050/mpu6050.go @@ -0,0 +1,350 @@ +//go:build linux + +// Package mpu6050 implements the movementsensor interface for an MPU-6050 6-axis accelerometer. A +// datasheet for this chip is at +// https://components101.com/sites/default/files/component_datasheet/MPU6050-DataSheet.pdf and a +// description of the I2C registers is at +// https://download.datasheets.com/pdfs/2015/3/19/8/3/59/59/invse_/manual/5rm-mpu-6000a-00v4.2.pdf +// +// We support reading the accelerometer, gyroscope, and thermometer data off of the chip. We do not +// yet support using the digital interrupt pin to notify on events (freefall, collision, etc.), +// nor do we yet support using the secondary I2C connection to add an external clock or +// magnetometer. +// +// The chip has two possible I2C addresses, which can be selected by wiring the AD0 pin to either +// hot or ground: +// - if AD0 is wired to ground, it uses the default I2C address of 0x68 +// - if AD0 is wired to hot, it uses the alternate I2C address of 0x69 +// +// If you use the alternate address, your config file for this component must set its +// "use_alternate_i2c_address" boolean to true. +package mpu6050 + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/golang/geo/r3" + geo "github.com/kellydunn/golang-geo" + "github.com/pkg/errors" + "go.viam.com/rdk/components/board/genericlinux/buses" + "go.viam.com/rdk/components/movementsensor" + "go.viam.com/rdk/logging" + "go.viam.com/rdk/resource" + "go.viam.com/rdk/spatialmath" + "go.viam.com/rdk/utils" + goutils "go.viam.com/utils" +) + +// Model for viam supported tdk-invensense mpu6050 movement sensor. +var Model = resource.NewModel("viam", "tdk-invensense", "mpu6050") + +const ( + defaultAddressRegister = 117 + expectedDefaultAddress = 0x68 + alternateAddress = 0x69 +) + +// Config is used to configure the attributes of the chip. +type Config struct { + I2cBus string `json:"i2c_bus"` + UseAlternateI2CAddress bool `json:"use_alt_i2c_address,omitempty"` +} + +// Validate ensures all parts of the config are valid, and then returns the list of things we +// depend on. +func (conf *Config) Validate(path string) ([]string, error) { + if conf.I2cBus == "" { + return nil, resource.NewConfigValidationFieldRequiredError(path, "i2c_bus") + } + + var deps []string + return deps, nil +} + +func init() { + resource.RegisterComponent(movementsensor.API, Model, resource.Registration[movementsensor.MovementSensor, *Config]{ + Constructor: newMpu6050, + }) +} + +type mpu6050 struct { + resource.Named + resource.AlwaysRebuild + bus buses.I2C + i2cAddress byte + mu sync.Mutex + + // The 3 things we can measure: lock the mutex before reading or writing these. + angularVelocity spatialmath.AngularVelocity + temperature float64 + linearAcceleration r3.Vector + // Stores the most recent error from the background goroutine + err movementsensor.LastError + + workers *goutils.StoppableWorkers + logger logging.Logger +} + +func addressReadError(err error, address byte, bus string) error { + msg := fmt.Sprintf("can't read from I2C address %d on bus %s", address, bus) + return errors.Wrap(err, msg) +} + +func unexpectedDeviceError(address, defaultAddress byte) error { + return errors.Errorf("unexpected non-MPU6050 device at address %d: response '%d'", + address, defaultAddress) +} + +// newMpu6050 constructs a new Mpu6050 object. +func newMpu6050( + ctx context.Context, + deps resource.Dependencies, + conf resource.Config, + logger logging.Logger, +) (movementsensor.MovementSensor, error) { + newConf, err := resource.NativeConfig[*Config](conf) + if err != nil { + return nil, err + } + + bus, err := buses.NewI2cBus(newConf.I2cBus) + if err != nil { + return nil, err + } + return makeMpu6050(ctx, deps, conf, logger, bus) +} + +// This function is separated from NewMpu6050 solely so you can inject a mock I2C bus in tests. +func makeMpu6050( + ctx context.Context, + _ resource.Dependencies, + conf resource.Config, + logger logging.Logger, + bus buses.I2C, +) (movementsensor.MovementSensor, error) { + newConf, err := resource.NativeConfig[*Config](conf) + if err != nil { + return nil, err + } + + var address byte + if newConf.UseAlternateI2CAddress { + address = alternateAddress + } else { + address = expectedDefaultAddress + } + logger.CDebugf(ctx, "Using address %d for MPU6050 sensor", address) + + sensor := &mpu6050{ + Named: conf.ResourceName().AsNamed(), + bus: bus, + i2cAddress: address, + logger: logger, + // On overloaded boards, the I2C bus can become flaky. Only report errors if at least 5 of + // the last 10 attempts to talk to the device have failed. + err: movementsensor.NewLastError(10, 5), + } + + // To check that we're able to talk to the chip, we should be able to read register 117 and get + // back the device's non-alternative address (0x68) + defaultAddress, err := sensor.readByte(ctx, defaultAddressRegister) + if err != nil { + return nil, addressReadError(err, address, newConf.I2cBus) + } + if defaultAddress != expectedDefaultAddress { + return nil, unexpectedDeviceError(address, defaultAddress) + } + + // The chip starts out in standby mode (the Sleep bit in the power management register defaults + // to 1). Set it to measurement mode (by turning off the Sleep bit) so we can get data from it. + // To do this, we set register 107 to 0. + err = sensor.writeByte(ctx, 107, 0) + if err != nil { + return nil, errors.Errorf("Unable to wake up MPU6050: '%s'", err.Error()) + } + + // Now, turn on the background goroutine that constantly reads from the chip and stores data in + // the object we created. + sensor.workers = goutils.NewBackgroundStoppableWorkers(func(cancelCtx context.Context) { + // Reading data a thousand times per second is probably fast enough. + timer := time.NewTicker(time.Millisecond) + defer timer.Stop() + + for { + select { + case <-timer.C: + rawData, err := sensor.readBlock(cancelCtx, 59, 14) + // Record `err` no matter what: even if it's nil, that's useful information. + sensor.err.Set(err) + if err != nil { + sensor.logger.CErrorf(ctx, "error reading MPU6050 sensor: '%s'", err) + continue + } + + linearAcceleration := toLinearAcceleration(rawData[0:6]) + // Taken straight from the MPU6050 register map. Yes, these are weird constants. + temperature := float64(utils.Int16FromBytesBE(rawData[6:8]))/340.0 + 36.53 + angularVelocity := toAngularVelocity(rawData[8:14]) + + // Lock the mutex before modifying the state within the object. By keeping the mutex + // unlocked for everything else, we maximize the time when another thread can read the + // values. + sensor.mu.Lock() + sensor.linearAcceleration = linearAcceleration + sensor.temperature = temperature + sensor.angularVelocity = angularVelocity + sensor.mu.Unlock() + case <-cancelCtx.Done(): + return + } + } + }) + + return sensor, nil +} + +func (mpu *mpu6050) readByte(ctx context.Context, register byte) (byte, error) { + result, err := mpu.readBlock(ctx, register, 1) + if err != nil { + return 0, err + } + return result[0], err +} + +func (mpu *mpu6050) readBlock(ctx context.Context, register byte, length uint8) ([]byte, error) { + handle, err := mpu.bus.OpenHandle(mpu.i2cAddress) + if err != nil { + return nil, err + } + defer func() { + err := handle.Close() + if err != nil { + mpu.logger.CError(ctx, err) + } + }() + + results, err := handle.ReadBlockData(ctx, register, length) + return results, err +} + +func (mpu *mpu6050) writeByte(ctx context.Context, register, value byte) error { + handle, err := mpu.bus.OpenHandle(mpu.i2cAddress) + if err != nil { + return err + } + defer func() { + err := handle.Close() + if err != nil { + mpu.logger.CError(ctx, err) + } + }() + + return handle.WriteByteData(ctx, register, value) +} + +// Given a value, scales it so that the range of int16s becomes the range of +/- maxValue. +func setScale(value int, maxValue float64) float64 { + return float64(value) * maxValue / (1 << 15) +} + +// A helper function to abstract out shared code: takes 6 bytes and gives back AngularVelocity, in +// radians per second. +func toAngularVelocity(data []byte) spatialmath.AngularVelocity { + gx := int(utils.Int16FromBytesBE(data[0:2])) + gy := int(utils.Int16FromBytesBE(data[2:4])) + gz := int(utils.Int16FromBytesBE(data[4:6])) + + maxRotation := 250.0 // Maximum degrees per second measurable in the default configuration + return spatialmath.AngularVelocity{ + X: setScale(gx, maxRotation), + Y: setScale(gy, maxRotation), + Z: setScale(gz, maxRotation), + } +} + +// A helper function that takes 6 bytes and gives back linear acceleration. +func toLinearAcceleration(data []byte) r3.Vector { + x := int(utils.Int16FromBytesBE(data[0:2])) + y := int(utils.Int16FromBytesBE(data[2:4])) + z := int(utils.Int16FromBytesBE(data[4:6])) + + // The scale is +/- 2G's, but our units should be m/sec/sec. + maxAcceleration := 2.0 * 9.81 /* m/sec/sec */ + return r3.Vector{ + X: setScale(x, maxAcceleration), + Y: setScale(y, maxAcceleration), + Z: setScale(z, maxAcceleration), + } +} + +func (mpu *mpu6050) AngularVelocity(ctx context.Context, extra map[string]interface{}) (spatialmath.AngularVelocity, error) { + mpu.mu.Lock() + defer mpu.mu.Unlock() + return mpu.angularVelocity, mpu.err.Get() +} + +func (mpu *mpu6050) LinearVelocity(ctx context.Context, extra map[string]interface{}) (r3.Vector, error) { + return r3.Vector{}, movementsensor.ErrMethodUnimplementedLinearVelocity +} + +func (mpu *mpu6050) LinearAcceleration(ctx context.Context, exta map[string]interface{}) (r3.Vector, error) { + mpu.mu.Lock() + defer mpu.mu.Unlock() + + lastError := mpu.err.Get() + if lastError != nil { + return r3.Vector{}, lastError + } + return mpu.linearAcceleration, nil +} + +func (mpu *mpu6050) Orientation(ctx context.Context, extra map[string]interface{}) (spatialmath.Orientation, error) { + return spatialmath.NewOrientationVector(), movementsensor.ErrMethodUnimplementedOrientation +} + +func (mpu *mpu6050) CompassHeading(ctx context.Context, extra map[string]interface{}) (float64, error) { + return 0, movementsensor.ErrMethodUnimplementedCompassHeading +} + +func (mpu *mpu6050) Position(ctx context.Context, extra map[string]interface{}) (*geo.Point, float64, error) { + return geo.NewPoint(0, 0), 0, movementsensor.ErrMethodUnimplementedPosition +} + +func (mpu *mpu6050) Accuracy(ctx context.Context, extra map[string]interface{}) (*movementsensor.Accuracy, error) { + return movementsensor.UnimplementedOptionalAccuracies(), nil +} + +func (mpu *mpu6050) Readings(ctx context.Context, extra map[string]interface{}) (map[string]interface{}, error) { + mpu.mu.Lock() + defer mpu.mu.Unlock() + + readings := make(map[string]interface{}) + readings["linear_acceleration"] = mpu.linearAcceleration + readings["temperature_celsius"] = mpu.temperature + readings["angular_velocity"] = mpu.angularVelocity + + return readings, mpu.err.Get() +} + +func (mpu *mpu6050) Properties(ctx context.Context, extra map[string]interface{}) (*movementsensor.Properties, error) { + return &movementsensor.Properties{ + AngularVelocitySupported: true, + LinearAccelerationSupported: true, + }, nil +} + +func (mpu *mpu6050) Close(ctx context.Context) error { + mpu.workers.Stop() + + mpu.mu.Lock() + defer mpu.mu.Unlock() + // Set the Sleep bit (bit 6) in the power control register (register 107). + err := mpu.writeByte(ctx, 107, 1<<6) + if err != nil { + mpu.logger.CError(ctx, err) + } + return err +} diff --git a/mpu6050/mpu6050_nonlinux.go b/mpu6050/mpu6050_nonlinux.go new file mode 100644 index 0000000..9087d5a --- /dev/null +++ b/mpu6050/mpu6050_nonlinux.go @@ -0,0 +1,2 @@ +// Package mpu6050 is only implemented for Linux systems. +package mpu6050 diff --git a/mpu6050/mpu6050_test.go b/mpu6050/mpu6050_test.go new file mode 100644 index 0000000..561d70a --- /dev/null +++ b/mpu6050/mpu6050_test.go @@ -0,0 +1,262 @@ +//go:build linux + +package mpu6050 + +import ( + "context" + "testing" + + "github.com/pkg/errors" + "go.viam.com/rdk/components/board/genericlinux/buses" + "go.viam.com/rdk/components/movementsensor" + "go.viam.com/rdk/logging" + "go.viam.com/rdk/resource" + "go.viam.com/rdk/testutils/inject" + "go.viam.com/test" + "go.viam.com/utils/testutils" +) + +func TestValidateConfig(t *testing.T) { + cfg := Config{} + deps, err := cfg.Validate("path") + expectedErr := resource.NewConfigValidationFieldRequiredError("path", "i2c_bus") + test.That(t, err, test.ShouldBeError, expectedErr) + test.That(t, deps, test.ShouldBeEmpty) +} + +func TestInitializationFailureOnChipCommunication(t *testing.T) { + logger := logging.NewTestLogger(t) + i2cName := "i2c" + + t.Run("fails on read error", func(t *testing.T) { + cfg := resource.Config{ + Name: "movementsensor", + Model: Model, + API: movementsensor.API, + ConvertedAttributes: &Config{ + I2cBus: i2cName, + }, + } + i2cHandle := &inject.I2CHandle{} + readErr := errors.New("read error") + i2cHandle.ReadBlockDataFunc = func(ctx context.Context, register byte, numBytes uint8) ([]byte, error) { + if register == defaultAddressRegister { + return nil, readErr + } + return []byte{}, nil + } + i2cHandle.CloseFunc = func() error { return nil } + i2c := &inject.I2C{} + i2c.OpenHandleFunc = func(addr byte) (buses.I2CHandle, error) { + return i2cHandle, nil + } + + deps := resource.Dependencies{} + sensor, err := makeMpu6050(context.Background(), deps, cfg, logger, i2c) + test.That(t, err, test.ShouldNotBeNil) + test.That(t, err, test.ShouldBeError, addressReadError(readErr, expectedDefaultAddress, i2cName)) + test.That(t, sensor, test.ShouldBeNil) + }) + + t.Run("fails on unexpected address", func(t *testing.T) { + cfg := resource.Config{ + Name: "movementsensor", + Model: Model, + API: movementsensor.API, + ConvertedAttributes: &Config{ + I2cBus: i2cName, + UseAlternateI2CAddress: true, + }, + } + i2cHandle := &inject.I2CHandle{} + i2cHandle.ReadBlockDataFunc = func(ctx context.Context, register byte, numBytes uint8) ([]byte, error) { + if register == defaultAddressRegister { + return []byte{0x64}, nil + } + return nil, errors.New("unexpected register") + } + i2cHandle.CloseFunc = func() error { return nil } + i2c := &inject.I2C{} + i2c.OpenHandleFunc = func(addr byte) (buses.I2CHandle, error) { + return i2cHandle, nil + } + + deps := resource.Dependencies{} + sensor, err := makeMpu6050(context.Background(), deps, cfg, logger, i2c) + test.That(t, err, test.ShouldNotBeNil) + test.That(t, err, test.ShouldBeError, unexpectedDeviceError(alternateAddress, 0x64)) + test.That(t, sensor, test.ShouldBeNil) + }) +} + +func TestSuccessfulInitializationAndClose(t *testing.T) { + logger := logging.NewTestLogger(t) + i2cName := "i2c" + + cfg := resource.Config{ + Name: "movementsensor", + Model: Model, + API: movementsensor.API, + ConvertedAttributes: &Config{ + I2cBus: i2cName, + UseAlternateI2CAddress: true, + }, + } + i2cHandle := &inject.I2CHandle{} + i2cHandle.ReadBlockDataFunc = func(ctx context.Context, register byte, numBytes uint8) ([]byte, error) { + return []byte{expectedDefaultAddress}, nil + } + // the only write operations that the sensor implementation performs is + // the command to put it into either measurement mode or sleep mode, + // and measurement mode results from a write of 0, so if is closeWasCalled is toggled + // we know Close() was successfully called + closeWasCalled := false + i2cHandle.WriteByteDataFunc = func(ctx context.Context, register, data byte) error { + if data == 1<<6 { + closeWasCalled = true + } + return nil + } + i2cHandle.CloseFunc = func() error { return nil } + i2c := &inject.I2C{} + i2c.OpenHandleFunc = func(addr byte) (buses.I2CHandle, error) { + return i2cHandle, nil + } + + deps := resource.Dependencies{} + sensor, err := makeMpu6050(context.Background(), deps, cfg, logger, i2c) + test.That(t, err, test.ShouldBeNil) + err = sensor.Close(context.Background()) + test.That(t, err, test.ShouldBeNil) + test.That(t, closeWasCalled, test.ShouldBeTrue) +} + +func setupDependencies(mockData []byte) (resource.Config, buses.I2C) { + i2cName := "i2c" + + cfg := resource.Config{ + Name: "movementsensor", + Model: Model, + API: movementsensor.API, + ConvertedAttributes: &Config{ + I2cBus: i2cName, + UseAlternateI2CAddress: true, + }, + } + + i2cHandle := &inject.I2CHandle{} + i2cHandle.ReadBlockDataFunc = func(ctx context.Context, register byte, numBytes uint8) ([]byte, error) { + if register == defaultAddressRegister { + return []byte{expectedDefaultAddress}, nil + } + return mockData, nil + } + i2cHandle.WriteByteDataFunc = func(ctx context.Context, b1, b2 byte) error { + return nil + } + i2cHandle.CloseFunc = func() error { return nil } + i2c := &inject.I2C{} + i2c.OpenHandleFunc = func(addr byte) (buses.I2CHandle, error) { + return i2cHandle, nil + } + return cfg, i2c +} + +//nolint:dupl +func TestLinearAcceleration(t *testing.T) { + // linear acceleration, temperature, and angular velocity are all read + // sequentially from the same series of 16-bytes, so we need to fill in + // the mock data at the appropriate portion of the sequence + linearAccelMockData := make([]byte, 16) + // x-accel + linearAccelMockData[0] = 64 + linearAccelMockData[1] = 0 + expectedAccelX := 9.81 + // y-accel + linearAccelMockData[2] = 32 + linearAccelMockData[3] = 0 + expectedAccelY := 4.905 + // z-accel + linearAccelMockData[4] = 16 + linearAccelMockData[5] = 0 + expectedAccelZ := 2.4525 + + logger := logging.NewTestLogger(t) + deps := resource.Dependencies{} + cfg, i2c := setupDependencies(linearAccelMockData) + sensor, err := makeMpu6050(context.Background(), deps, cfg, logger, i2c) + test.That(t, err, test.ShouldBeNil) + defer sensor.Close(context.Background()) + testutils.WaitForAssertion(t, func(tb testing.TB) { + linAcc, err := sensor.LinearAcceleration(context.Background(), nil) + test.That(tb, err, test.ShouldBeNil) + test.That(tb, linAcc, test.ShouldNotBeZeroValue) + }) + accel, err := sensor.LinearAcceleration(context.Background(), nil) + test.That(t, err, test.ShouldBeNil) + test.That(t, accel.X, test.ShouldEqual, expectedAccelX) + test.That(t, accel.Y, test.ShouldEqual, expectedAccelY) + test.That(t, accel.Z, test.ShouldEqual, expectedAccelZ) +} + +//nolint:dupl +func TestAngularVelocity(t *testing.T) { + // linear acceleration, temperature, and angular velocity are all read + // sequentially from the same series of 16-bytes, so we need to fill in + // the mock data at the appropriate portion of the sequence + angVelMockData := make([]byte, 16) + // x-vel + angVelMockData[8] = 64 + angVelMockData[9] = 0 + expectedAngVelX := 125.0 + // y-accel + angVelMockData[10] = 32 + angVelMockData[11] = 0 + expectedAngVelY := 62.5 + // z-accel + angVelMockData[12] = 16 + angVelMockData[13] = 0 + expectedAngVelZ := 31.25 + + logger := logging.NewTestLogger(t) + deps := resource.Dependencies{} + cfg, i2c := setupDependencies(angVelMockData) + sensor, err := makeMpu6050(context.Background(), deps, cfg, logger, i2c) + test.That(t, err, test.ShouldBeNil) + defer sensor.Close(context.Background()) + testutils.WaitForAssertion(t, func(tb testing.TB) { + angVel, err := sensor.AngularVelocity(context.Background(), nil) + test.That(tb, err, test.ShouldBeNil) + test.That(tb, angVel, test.ShouldNotBeZeroValue) + }) + angVel, err := sensor.AngularVelocity(context.Background(), nil) + test.That(t, err, test.ShouldBeNil) + test.That(t, angVel.X, test.ShouldEqual, expectedAngVelX) + test.That(t, angVel.Y, test.ShouldEqual, expectedAngVelY) + test.That(t, angVel.Z, test.ShouldEqual, expectedAngVelZ) +} + +func TestTemperature(t *testing.T) { + // linear acceleration, temperature, and angular velocity are all read + // sequentially from the same series of 16-bytes, so we need to fill in + // the mock data at the appropriate portion of the sequence + temperatureMockData := make([]byte, 16) + temperatureMockData[6] = 231 + temperatureMockData[7] = 202 + expectedTemp := 18.3 + + logger := logging.NewTestLogger(t) + deps := resource.Dependencies{} + cfg, i2c := setupDependencies(temperatureMockData) + sensor, err := makeMpu6050(context.Background(), deps, cfg, logger, i2c) + test.That(t, err, test.ShouldBeNil) + defer sensor.Close(context.Background()) + testutils.WaitForAssertion(t, func(tb testing.TB) { + readings, err := sensor.Readings(context.Background(), nil) + test.That(tb, err, test.ShouldBeNil) + test.That(tb, readings["temperature_celsius"], test.ShouldNotBeZeroValue) + }) + readings, err := sensor.Readings(context.Background(), nil) + test.That(t, err, test.ShouldBeNil) + test.That(t, readings["temperature_celsius"], test.ShouldAlmostEqual, expectedTemp, 0.001) +}