{"id":51193103,"url":"https://github.com/westphae/go-iio","last_synced_at":"2026-06-27T17:31:17.958Z","repository":{"id":358133821,"uuid":"1236639345","full_name":"westphae/go-iio","owner":"westphae","description":"Go library for reading Linux Industrial I/O (IIO) sensors — device-tree-overlay sensors like BMP280 and ICM20948 — via sysfs. Pure Go, no cgo.","archived":false,"fork":false,"pushed_at":"2026-05-15T21:02:04.000Z","size":56,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-15T23:52:50.143Z","etag":null,"topics":["bmp280","device-tree","golang","iio","linux","raspberry-pi","sensors","sysfs"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/westphae.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-12T12:40:11.000Z","updated_at":"2026-05-15T21:02:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/westphae/go-iio","commit_stats":null,"previous_names":["westphae/go-iio"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/westphae/go-iio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westphae%2Fgo-iio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westphae%2Fgo-iio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westphae%2Fgo-iio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westphae%2Fgo-iio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/westphae","download_url":"https://codeload.github.com/westphae/go-iio/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/westphae%2Fgo-iio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34862627,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bmp280","device-tree","golang","iio","linux","raspberry-pi","sensors","sysfs"],"created_at":"2026-06-27T17:31:14.084Z","updated_at":"2026-06-27T17:31:17.945Z","avatar_url":"https://github.com/westphae.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-iio\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/westphae/go-iio.svg)](https://pkg.go.dev/github.com/westphae/go-iio)\n[![CI](https://github.com/westphae/go-iio/actions/workflows/ci.yml/badge.svg)](https://github.com/westphae/go-iio/actions/workflows/ci.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/westphae/go-iio)](https://goreportcard.com/report/github.com/westphae/go-iio)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nA small, idiomatic Go library for reading Linux Industrial I/O (IIO) sensors\n— the ones that show up under `/sys/bus/iio/devices/` after a device tree\noverlay loads. Polled and buffered captures, dynamic channel discovery, no\ncgo.\n\n```go\nimport \"github.com/westphae/go-iio\"\n\ndev, _ := iio.Open(\"bmp280\")\ndefer dev.Close()\n\nt, _ := dev.ReadFloat(\"temp\")     // °C\np, _ := dev.ReadFloat(\"pressure\") // kPa\n```\n\nBuffered capture at a fixed rate (BMP280 has no hardware data-ready, so the\nlibrary drives it from a kernel hrtimer trigger):\n\n```go\ntrig, _ := iio.EnsureHRTimer(\"mytrig\", 10) // 10 Hz; needs CAP_SYS_ADMIN\ndefer trig.Remove()\n\nbuf, _ := dev.Buffer(iio.BufferOptions{\n    Channels: []string{\"pressure\", \"temp\", \"timestamp\"},\n    Length:   16,\n    Trigger:  trig.Name(),\n})\ndefer buf.Close()\n\nrecs := make([]iio.Record, 16)\nfor {\n    n, _ := buf.Read(ctx, recs)\n    for i := 0; i \u003c n; i++ { /* recs[i].Time, recs[i].Values[\"temp\"], ... */ }\n}\n```\n\nSee `examples/` for runnable programs, and `CLAUDE.md` for the design notes.\n\n## Sensor wrappers\n\nThe generic `iio` API works for any kernel-supported sensor, but the\n`bmp280`, `icm20948`, and `mmc5983ma` subpackages bundle the channel names,\nscale writes, trigger setup, and decode logic specific to those chips so\nthe calling code is just `Open / Read / Stream`. All three wrappers expose\n`Device()` for the underlying `*iio.Device` when you need an attribute the\nwrapper doesn't cover.\n\n### BMP280\n\nBosch BMP280 pressure/temperature sensor. Wraps the mainline `bmp280`\nkernel driver. Samples are `°C` and `kPa`.\n\n```go\nimport \"github.com/westphae/go-iio/bmp280\"\n\ndev, err := bmp280.Open(\n    bmp280.WithOversampling(2, 16), // temp x2, pressure x16\n)\nif err != nil { /* ... */ }\ndefer dev.Close()\n\n// Polled — one sample per call, two sysfs reads each:\ns, _ := dev.Read()\nfmt.Println(s.TempC, s.PressKPa)\n```\n\nBuffered capture (the BMP280 has no hardware data-ready, so the wrapper\ncreates an hrtimer trigger via configfs — needs `CAP_SYS_ADMIN`, and\n`iio-trig-hrtimer` must be loaded):\n\n```go\nctx, cancel := context.WithCancel(context.Background())\ndefer cancel()\n\nch, err := dev.Stream(ctx, bmp280.StreamOptions{\n    FrequencyHz:  10,                // required, \u003e 0\n    TriggerName:  \"\",                // optional, defaults to \"goiio-bmp280\"\n    BufferLength: 16,                // kernel ring depth in samples\n})\nif err != nil { /* ... */ }\nfor s := range ch {\n    fmt.Printf(\"%s  %.2f °C  %.3f kPa\\n\", s.Time.Format(time.RFC3339Nano), s.TempC, s.PressKPa)\n}\n```\n\nOptions:\n\n- `WithPath(p)` — open a specific sysfs path instead of looking up by name\n  (useful if multiple BMP280s are present).\n- `WithOversampling(temp, pressure)` — writes `in_temp_oversampling_ratio`\n  and `in_pressure_oversampling_ratio`. Valid: `1, 2, 4, 8, 16`.\n\n### ICM-20948\n\nInvenSense / TDK ICM-20948 9-axis IMU (3-axis accel + 3-axis gyro + AK09916\nmagnetometer + die temperature). Targets the out-of-tree\n`github.com/westphae/icm20948-mod` driver or the mainline `inv_icm20948`\ndriver — both expose the same standard IIO channel naming. Samples are SI:\nm/s² (accel), rad/s (gyro), µT (mag — converted from the kernel's Gauss),\n°C (temp).\n\n```go\nimport \"github.com/westphae/go-iio/icm20948\"\n\ndev, err := icm20948.Open(\n    icm20948.WithAccelScale(4),        // ±4 G\n    icm20948.WithGyroScale(500),       // ±500 dps\n    icm20948.WithAccelDLPFHz(50),      // snap to nearest available cutoff\n    icm20948.WithGyroDLPFHz(50),\n)\nif err != nil { /* ... */ }\ndefer dev.Close()\n\n// Polled — one Sample per call, ten sysfs reads:\ns, _ := dev.Read()\nfmt.Printf(\"a=(%.2f,%.2f,%.2f) g=(%.3f,%.3f,%.3f) m=(%.1f,%.1f,%.1f) T=%.1f\\n\",\n    s.AccelX, s.AccelY, s.AccelZ,\n    s.GyroX, s.GyroY, s.GyroZ,\n    s.MagX, s.MagY, s.MagZ,\n    s.TempC,\n)\n```\n\nBuffered capture (same hrtimer mechanics as BMP280):\n\n```go\nctx, cancel := context.WithCancel(context.Background())\ndefer cancel()\n\nch, err := dev.Stream(ctx, icm20948.StreamOptions{\n    FrequencyHz:  100, // accel/gyro pace; AK09916 mag is fixed at 100 Hz in the driver\n    BufferLength: 32,\n})\nif err != nil { /* ... */ }\nfor s := range ch {\n    // s.Time, s.AccelX/Y/Z, s.GyroX/Y/Z, s.MagX/Y/Z, s.TempC\n}\n```\n\nOptions:\n\n- `WithPath(p)` — open a specific sysfs path instead of looking up by name.\n- `WithAccelScale(g)` — `2 / 4 / 8 / 16`. Writes `in_accel_scale` with the\n  exact SI string the driver expects.\n- `WithGyroScale(dps)` — `250 / 500 / 1000 / 2000`. Writes `in_anglvel_scale`.\n- `WithAccelDLPFHz(hz)` / `WithGyroDLPFHz(hz)` — on-chip digital low-pass\n  cutoff. The driver only accepts values from\n  `in_\u003cch\u003e_filter_low_pass_3db_frequency_available`; the wrapper snaps to\n  the nearest available value.\n\nMagnetometer overflow: the AK09916 saturates at ±4912 µT. The wrapper\nexposes the sticky `in_magn_overrange` flag so callers can detect a clipped\naxis without scanning every sample:\n\n```go\nif over, _ := dev.Overrange(); over {\n    // at least one sample since the last clear hit the chip's range\n    _ = dev.ClearOverrange()\n}\n```\n\nNote on rates: the magnetometer's internal conversion runs at a fixed\n100 Hz in the kernel driver, so at trigger rates above 100 Hz consecutive\nsamples will repeat the same mag values until the next conversion lands.\n\n### MMC5983MA\n\nMEMSIC MMC5983MA 3-axis AMR magnetometer (±8 G, 18-bit, on-chip temperature\nsensor). Targets the out-of-tree `github.com/westphae/mmc5983ma-mod`\nkernel driver. Samples are µT (mag — converted from the kernel's Gauss)\nand °C (temp).\n\n```go\nimport \"github.com/westphae/go-iio/mmc5983ma\"\n\ndev, err := mmc5983ma.Open(\n    mmc5983ma.WithSamplingFrequencyHz(100),  // continuous-mode ODR\n    mmc5983ma.WithBandwidthHz(200),          // measurement bandwidth\n)\nif err != nil { /* ... */ }\ndefer dev.Close()\n\n// Polled — one Sample per call, four sysfs reads (mag x/y/z + temp):\ns, _ := dev.Read()\nfmt.Printf(\"mag: %.1f %.1f %.1f µT  %.2f °C\\n\",\n    s.MagX, s.MagY, s.MagZ, s.TempC)\n```\n\nBuffered capture (same hrtimer mechanics as BMP280/ICM-20948):\n\n```go\nctx, cancel := context.WithCancel(context.Background())\ndefer cancel()\n\nch, err := dev.Stream(ctx, mmc5983ma.StreamOptions{\n    FrequencyHz:  100,\n    BufferLength: 32,\n})\nif err != nil { /* ... */ }\nfor s := range ch {\n    // s.Time, s.MagX/Y/Z — TempC is not in the buffered scan; poll Read for it.\n}\n```\n\nOptions:\n\n- `WithPath(p)` — open a specific sysfs path instead of looking up by name.\n- `WithSamplingFrequencyHz(hz)` — `1 / 10 / 20 / 50 / 100 / 200 / 1000`.\n  The driver auto-bumps `BandwidthHz` if required by the chip's\n  ODR-vs-BW constraints (200 Hz → BW≥200, 1000 Hz → BW=800).\n- `WithBandwidthHz(hz)` — `100 / 200 / 400 / 800`. Higher BW = shorter\n  measurement window = noisier readings but faster max ODR.\n- `WithPeriodicSet(samples)` — supplementary periodic-SET cadence on top\n  of Auto SR (which is always on). `0` disables. Useful only in extreme\n  thermal environments; leave at the default otherwise.\n\nAMR-specific helpers:\n\n```go\n// Recover after a strong-field event:\n_ = dev.SetPulse()        // 500 ns SET pulse through the chip's coil\n\n// Null the bridge offset (best in a known-stable field — Helmholtz coil\n// or magnetic shield). Updates the driver's software calibbias so\n// subsequent Read/Stream values come back with offset cancelled.\n_ = dev.AutoNullCalibBias()\nx, y, z, _ := dev.CalibBias()  // inspect the new offsets\n\n// Confirm the chip is responsive (St_enp / St_enm coils):\n_ = dev.RunSelftest(true)            // positive coil\ndx, dy, dz, _ := dev.SelftestDelta() // delta vs baseline in raw LSB\n_ = dev.ClearSelftest()\n```\n\nNote on temperature: the chip can only do mag OR temp at a time, so the\nbuffered scan layout includes the three mag axes plus timestamp but\n*not* temperature. `Sample.TempC` is zero in `Stream` samples; use\n`Read()` (which briefly pauses the mag stream for a one-shot temp\nmeasurement) when you need temperature.\n\n## Status\n\nv0: pure-Go sysfs backend, BMP280 polled and buffered captures, ICM-20948\nand MMC5983MA convenience wrappers. A `backend/libiio` (cgo) slot is\nreserved for remote sensors over `iiod` but is not yet implemented.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwestphae%2Fgo-iio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwestphae%2Fgo-iio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwestphae%2Fgo-iio/lists"}