pw_imu: Driver for icm42670p IMU
Change-Id: I05c3ccc5a49ff71501d322a47bd264fed8f77c3f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/kudzu/+/175692
Reviewed-by: Erik Gilling <konkers@google.com>
Commit-Queue: Asad Memon <asadmemon@google.com>
diff --git a/applications/app_common/BUILD.gn b/applications/app_common/BUILD.gn
index 161ee7c..64bfca0 100644
--- a/applications/app_common/BUILD.gn
+++ b/applications/app_common/BUILD.gn
@@ -30,6 +30,7 @@
"$dir_pw_display",
"$dir_pw_status",
"$dir_pw_thread:thread",
+ "//lib/kudzu_imu",
"//lib/pw_touchscreen",
]
public = [ "public/app_common/common.h" ]
diff --git a/applications/app_common/public/app_common/common.h b/applications/app_common/public/app_common/common.h
index dfe018c..6596189 100644
--- a/applications/app_common/public/app_common/common.h
+++ b/applications/app_common/public/app_common/common.h
@@ -13,6 +13,7 @@
// the License.
#pragma once
+#include "kudzu_imu/imu.h"
#include "pw_display/display.h"
#include "pw_status/status.h"
#include "pw_thread/thread.h"
@@ -33,6 +34,9 @@
// Return an initialized display.
static pw::display::Display& GetDisplay();
+ // Return an initialized display.
+ static kudzu::imu::PollingImu& GetImu();
+
// Return an initialized touchscreen.
static pw::touchscreen::Touchscreen& GetTouchscreen();
diff --git a/applications/app_common_impl/BUILD.gn b/applications/app_common_impl/BUILD.gn
index 3a9484b..2a54966 100644
--- a/applications/app_common_impl/BUILD.gn
+++ b/applications/app_common_impl/BUILD.gn
@@ -77,6 +77,7 @@
"//applications/app_common:app_common.facade",
"//lib/ft6236",
"//lib/icm42670p",
+ "//lib/kudzu_imu_icm42670p",
"//lib/max17948",
"//lib/tca9535",
]
@@ -121,6 +122,7 @@
"$dir_pw_thread:thread",
"$dir_pw_thread_stl:thread",
"//applications/app_common:app_common.facade",
+ "//lib/kudzu_imu_imgui",
"//lib/pw_touchscreen_imgui",
]
sources = [ "common_host_imgui.cc" ]
diff --git a/applications/app_common_impl/common_host_imgui.cc b/applications/app_common_impl/common_host_imgui.cc
index 26d5447..b9d3675 100644
--- a/applications/app_common_impl/common_host_imgui.cc
+++ b/applications/app_common_impl/common_host_imgui.cc
@@ -12,6 +12,7 @@
// License for the specific language governing permissions and limitations under
// the License.
#include "app_common/common.h"
+#include "kudzu_imu_imgui/imu.h"
#include "pw_color/color.h"
#include "pw_display_driver_imgui/display_driver.h"
#include "pw_display_imgui/display.h"
@@ -71,6 +72,11 @@
return s_touchscreen;
}
+kudzu::imu::PollingImu& Common::GetImu() {
+ static kudzu::imu::PollingImuImGui s_imu = kudzu::imu::PollingImuImGui();
+ return s_imu;
+}
+
const pw::thread::Options& Common::DisplayDrawThreadOptions() {
static pw::thread::stl::Options display_draw_thread_options;
return display_draw_thread_options;
diff --git a/applications/app_common_impl/common_pico.cc b/applications/app_common_impl/common_pico.cc
index 40192b9..0e1c43a 100644
--- a/applications/app_common_impl/common_pico.cc
+++ b/applications/app_common_impl/common_pico.cc
@@ -24,6 +24,7 @@
#include "hardware/pwm.h"
#include "hardware/vreg.h"
#include "icm42670p/device.h"
+#include "kudzu_imu_icm42670p/imu.h"
#include "max17948/device.h"
#include "pico/stdlib.h"
#include "pw_digital_io_rp2040/digital_io.h"
@@ -232,7 +233,7 @@
pw::i2c::PicoInitiator i2c1_bus(ki2c1Config);
pw::tca9535::Device io_expander(i2c1_bus);
-pw::icm42670p::Device imu(i2c0_bus);
+kudzu::icm42670p::Device imu(i2c0_bus);
pw::max17948::Device fuel_guage(i2c0_bus);
pw::ft6236::Device touch_screen_controller(i2c0_bus);
@@ -356,6 +357,11 @@
return s_touchscreen;
}
+kudzu::imu::PollingImu& Common::GetImu() {
+ static kudzu::imu::PollingImuICM42670P s_imu(&imu);
+ return s_imu;
+}
+
const pw::thread::Options& Common::DisplayDrawThreadOptions() {
static constexpr auto options =
pw::thread::freertos::Options()
diff --git a/applications/badge/BUILD.gn b/applications/badge/BUILD.gn
index 3eb7243..49e8030 100644
--- a/applications/badge/BUILD.gn
+++ b/applications/badge/BUILD.gn
@@ -50,6 +50,7 @@
"$pw_dir_third_party_32blit:32blit",
"//applications/app_common",
"//lib/framecounter",
+ "//lib/kudzu_imu",
"//lib/random",
]
remove_configs = [ "$dir_pw_build:strict_warnings" ]
diff --git a/lib/icm42670p/BUILD.gn b/lib/icm42670p/BUILD.gn
index 73e8fdb..e0b632a 100644
--- a/lib/icm42670p/BUILD.gn
+++ b/lib/icm42670p/BUILD.gn
@@ -23,6 +23,7 @@
public_configs = [ ":default_config" ]
public_deps = [
"$dir_pw_i2c:initiator",
+ "$dir_pw_result",
"$dir_pw_status",
]
public = [ "public/icm42670p/device.h" ]
@@ -30,6 +31,7 @@
"$dir_pw_digital_io",
"$dir_pw_i2c:register_device",
"$dir_pw_log",
+ "//lib/kudzu_imu:kudzu_imu",
]
sources = [ "device.cc" ]
remove_configs = [ "$dir_pw_build:strict_warnings" ]
diff --git a/lib/icm42670p/device.cc b/lib/icm42670p/device.cc
index 5c78506..ab1b4c3 100644
--- a/lib/icm42670p/device.cc
+++ b/lib/icm42670p/device.cc
@@ -25,12 +25,25 @@
#include "pw_i2c/address.h"
#include "pw_i2c/register_device.h"
#include "pw_log/log.h"
+#include "pw_result/result.h"
#include "pw_status/status.h"
using ::pw::Status;
using namespace std::chrono_literals;
-namespace pw::icm42670p {
+namespace kudzu::icm42670p {
+
+enum Icm42670pRegister : uint8_t {
+ kWhoAmI = 0x75,
+ kPwrMgmt0 = 0x1f,
+ kGyroConfig0 = 0x20,
+ kAccelConfig0 = 0x21,
+ kAccelDataX1 = 0x0b,
+ kGyroDataX1 = 0x11,
+};
+
+constexpr int16_t GYRO_UI_FS_SEL = 1000;
+constexpr float ACCEL_UI_FS_SEL = 8.0f;
namespace {
@@ -62,20 +75,128 @@
}
}
+pw::Result<kudzu::imu::ImuSample> ReadData(pw::i2c::RegisterDevice& device) {
+ // TODO: asadmemon - Check the status register before reading the values.
+
+ uint8_t data[12];
+ device.ReadRegisters8(Icm42670pRegister::kAccelDataX1,
+ pw::span(data, 12),
+ pw::chrono::SystemClock::for_at_least(10ms));
+
+ uint16_t accel_x =
+ pw::bytes::ReadInOrder<uint16_t>(pw::endian::big, &data[0]);
+ uint16_t accel_y =
+ pw::bytes::ReadInOrder<uint16_t>(pw::endian::big, &data[2]);
+ uint16_t accel_z =
+ pw::bytes::ReadInOrder<uint16_t>(pw::endian::big, &data[4]);
+
+ uint16_t gyro_x = pw::bytes::ReadInOrder<uint16_t>(pw::endian::big, &data[6]);
+ uint16_t gyro_y = pw::bytes::ReadInOrder<uint16_t>(pw::endian::big, &data[8]);
+ uint16_t gyro_z =
+ pw::bytes::ReadInOrder<uint16_t>(pw::endian::big, &data[10]);
+
+ float f_accel_x =
+ static_cast<float>((int16_t)accel_x) * ACCEL_UI_FS_SEL / INT16_MAX;
+ float f_accel_y =
+ static_cast<float>((int16_t)accel_y) * ACCEL_UI_FS_SEL / INT16_MAX;
+ float f_accel_z =
+ static_cast<float>((int16_t)accel_z) * ACCEL_UI_FS_SEL / INT16_MAX;
+
+ int16_t f_gyro_x =
+ static_cast<int16_t>((int16_t)gyro_x) * GYRO_UI_FS_SEL / INT16_MAX;
+ int16_t f_gyro_y =
+ static_cast<int16_t>((int16_t)gyro_y) * GYRO_UI_FS_SEL / INT16_MAX;
+ int16_t f_gyro_z =
+ static_cast<int16_t>((int16_t)gyro_z) * GYRO_UI_FS_SEL / INT16_MAX;
+
+ kudzu::imu::ImuSample sample = {
+ {f_accel_x, f_accel_y, f_accel_z},
+ {f_gyro_x, f_gyro_y, f_gyro_z},
+ };
+ return sample;
+}
+
} // namespace
Device::Device(pw::i2c::Initiator& initiator)
: initiator_(initiator),
device_(initiator,
kAddress,
- endian::little,
+ pw::endian::little,
pw::i2c::RegisterAddressSize::k1Byte) {}
-Status Device::Enable() {
+pw::Status Device::Enable() {
device_.WriteRegister8(
0x1f, 0x0f, pw::chrono::SystemClock::for_at_least(10ms));
- return OkStatus();
+ /*
+ +----------------+-----------------------------------+
+ | ACCEL_CONFIG0 | |
+ +----------------+-----------------------------------+
+ | Name | ACCEL_CONFIG0 |
+ | Address | 33 (21h) |
+ | Serial IF | R/W |
+ | Reset value | 0x06 |
+ +----------------+-----------------------------------+
+ | BIT | NAME | FUNCTION |
+ +----------------+-----------------------------------+
+ | 7 | - | Reserved |
+ | 6:5 | ACCEL_UI_FS_SEL | 00: ±16g |
+ | | | 01: ±8g |
+ | | | 10: ±4g |
+ | | | 11: ±2g |
+ | 4 | - | Reserved |
+ | 3:0 | ACCEL_ODR | 0101: 1.6k Hz (LN mode) |
+ | | | 0110: 800 Hz (LN mode) |
+ | | | 0111: 400 Hz (LP or LN) |
+ | | | 1000: 200 Hz (LP or LN) |
+ | | | 1001: 100 Hz (LP or LN) |
+ | | | 1010: 50 Hz (LP or LN) |
+ | | | 1011: 25 Hz (LP or LN) |
+ | | | 1100: 12.5 Hz (LP or LN) |
+ | | | 1101: 6.25 Hz (LP mode) |
+ | | | 1110: 3.125 Hz (LP mode) |
+ | | | 1111: 1.5625 Hz (LP mode) |
+ +----------------+-----------------------------------+
+ */
+ uint8_t new_accel_config = 0b00101000;
+ device_.WriteRegister8(Icm42670pRegister::kAccelConfig0,
+ new_accel_config,
+ pw::chrono::SystemClock::for_at_least(10ms));
+
+ /*
+ +--------------+------------------------+
+ | GYRO_CONFIG0 | |
+ +--------------+------------------------+
+ | Name | GYRO_CONFIG0 |
+ | Address | 32 (20h) |
+ | Serial IF | R/W |
+ | Reset value | 0x06 |
+ +--------------+------------------------+
+ | BIT | NAME | FUNCTION |
+ +--------------+------------------------+
+ | 7 | - | Reserved |
+ | 6:5 | GYRO_UI_FS_SEL | 00: ±2000 dps |
+ | | | 01: ±1000 dps |
+ | | | 10: ±500 dps |
+ | | | 11: ±250 dps |
+ | 4 | - | Reserved |
+ | 3:0 | GYRO_ODR | 0101: 1.6k Hz |
+ | | | 0110: 800 Hz |
+ | | | 0111: 400 Hz |
+ | | | 1000: 200 Hz |
+ | | | 1001: 100 Hz |
+ | | | 1010: 50 Hz |
+ | | | 1011: 25 Hz |
+ | | | 1100: 12.5 Hz |
+ +--------------+------------------------+
+ */
+ uint8_t new_gyro_config = 0b00101000;
+ device_.WriteRegister8(Icm42670pRegister::kGyroConfig0,
+ new_gyro_config,
+ pw::chrono::SystemClock::for_at_least(10ms));
+
+ return pw::OkStatus();
}
Status Device::Probe() {
@@ -94,10 +215,14 @@
device_.WriteRegister8(
0x1f, 0x0f, pw::chrono::SystemClock::for_at_least(10ms));
- ImuReadReg(device_, 0x75, "WHO_AM_I");
- ImuReadReg(device_, 0x1f, "PWR_MGMT0");
- ImuReadReg(device_, 0x0c, "X0");
- ImuReadReg(device_, 0x0b, "X1");
+ ImuReadReg(device_, Icm42670pRegister::kWhoAmI, "WHO_AM_I");
+ ImuReadReg(device_, Icm42670pRegister::kPwrMgmt0, "PWR_MGMT0");
+ ImuReadReg(device_, Icm42670pRegister::kGyroConfig0, "GYRO_CONFIG0");
+ ImuReadReg(device_, Icm42670pRegister::kAccelConfig0, "ACCEL_CONFIG0");
}
-} // namespace pw::icm42670p
+pw::Result<kudzu::imu::ImuSample> Device::ReadValues() {
+ return ReadData(device_);
+}
+
+} // namespace kudzu::icm42670p
diff --git a/lib/icm42670p/public/icm42670p/device.h b/lib/icm42670p/public/icm42670p/device.h
index 288e7a1..f88e790 100644
--- a/lib/icm42670p/public/icm42670p/device.h
+++ b/lib/icm42670p/public/icm42670p/device.h
@@ -16,25 +16,27 @@
#include <array>
#include <cstdint>
+#include "kudzu_imu/imu.h"
#include "pw_i2c/address.h"
#include "pw_i2c/initiator.h"
#include "pw_i2c/register_device.h"
#include "pw_status/status.h"
-namespace pw::icm42670p {
+namespace kudzu::icm42670p {
class Device {
public:
Device(pw::i2c::Initiator& initiator);
~Device() = default;
- Status Enable();
- Status Probe();
+ pw::Status Enable();
+ pw::Status Probe();
void LogControllerInfo();
+ pw::Result<kudzu::imu::ImuSample> ReadValues();
private:
pw::i2c::Initiator& initiator_;
pw::i2c::RegisterDevice device_;
};
-} // namespace pw::icm42670p
+} // namespace kudzu::icm42670p
diff --git a/lib/kudzu_imu/BUILD.gn b/lib/kudzu_imu/BUILD.gn
new file mode 100644
index 0000000..68ef3c0
--- /dev/null
+++ b/lib/kudzu_imu/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+config("public_includes") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("kudzu_imu") {
+ public_configs = [ ":public_includes" ]
+ public = [ "public/kudzu_imu/imu.h" ]
+ public_deps = [ "$dir_pw_status" ]
+}
diff --git a/lib/kudzu_imu/public/kudzu_imu/imu.h b/lib/kudzu_imu/public/kudzu_imu/imu.h
new file mode 100644
index 0000000..d5c425a
--- /dev/null
+++ b/lib/kudzu_imu/public/kudzu_imu/imu.h
@@ -0,0 +1,52 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <cstdint>
+
+#include "pw_result/result.h"
+#include "pw_status/status.h"
+
+namespace kudzu::imu {
+
+struct AccelerometerData {
+ float x, y, z; // g / axis
+};
+
+struct GyroscopeData {
+ int16_t x, y, z; // dps / axis
+};
+
+struct ImuSample {
+ AccelerometerData accelerometer;
+ GyroscopeData gyroscope;
+};
+
+// Abstract base class for Polling IMU
+class PollingImu {
+ public:
+ virtual ~PollingImu() = default;
+
+ // Initialize the IMU controller.
+ virtual pw::Status Init() = 0;
+
+ // Return true if the controller is ready.
+ virtual bool IsAvailable() = 0;
+
+ // Retrieve IMU data. The concrete implementation would typically communicate
+ // with the actual IMU hardware to get this data.
+ virtual pw::Result<ImuSample> ReadData() = 0;
+};
+
+} // namespace kudzu::imu
diff --git a/lib/kudzu_imu_icm42670p/BUILD.gn b/lib/kudzu_imu_icm42670p/BUILD.gn
new file mode 100644
index 0000000..fdc2bcb
--- /dev/null
+++ b/lib/kudzu_imu_icm42670p/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("kudzu_imu_icm42670p") {
+ public_configs = [ ":default_config" ]
+ public = [ "public/kudzu_imu_icm42670p/imu.h" ]
+ deps = [
+ "$dir_pw_log",
+ "$dir_pw_result",
+ "//lib/icm42670p",
+ "//lib/kudzu_imu",
+ ]
+ sources = [ "imu.cc" ]
+}
diff --git a/lib/kudzu_imu_icm42670p/imu.cc b/lib/kudzu_imu_icm42670p/imu.cc
new file mode 100644
index 0000000..4d3cd12
--- /dev/null
+++ b/lib/kudzu_imu_icm42670p/imu.cc
@@ -0,0 +1,51 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "kudzu_imu/imu.h"
+
+#include <math.h>
+
+#include <cinttypes>
+
+#define PW_LOG_MODULE_NAME "kudzu_imu_icm42670p"
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
+
+#include "icm42670p/device.h"
+#include "kudzu_imu_icm42670p/imu.h"
+#include "pw_log/log.h"
+
+namespace kudzu::imu {
+
+PollingImuICM42670P::PollingImuICM42670P(
+ kudzu::icm42670p::Device* imu_controller)
+ : imu_controller_(imu_controller) {}
+
+pw::Status PollingImuICM42670P::Init() {
+ imu_controller_->Enable();
+ imu_controller_->LogControllerInfo();
+ return pw::OkStatus();
+}
+
+bool PollingImuICM42670P::IsAvailable() { return true; }
+
+pw::Result<ImuSample> PollingImuICM42670P::ReadData() {
+ pw::Result<ImuSample> data = imu_controller_->ReadValues();
+
+ if (data.ok()) {
+ last_data = data.value();
+ }
+ return data;
+}
+
+} // namespace kudzu::imu
diff --git a/lib/kudzu_imu_icm42670p/public/kudzu_imu_icm42670p/imu.h b/lib/kudzu_imu_icm42670p/public/kudzu_imu_icm42670p/imu.h
new file mode 100644
index 0000000..a0a35ca
--- /dev/null
+++ b/lib/kudzu_imu_icm42670p/public/kudzu_imu_icm42670p/imu.h
@@ -0,0 +1,35 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "kudzu_imu/imu.h"
+#include "pw_status/status.h"
+
+namespace kudzu::imu {
+
+class PollingImuICM42670P : public PollingImu {
+ public:
+ PollingImuICM42670P(kudzu::icm42670p::Device* imu_controller);
+
+ pw::Status Init() override;
+ bool IsAvailable() override;
+ pw::Result<ImuSample> ReadData() override;
+
+ ImuSample last_data;
+
+ private:
+ kudzu::icm42670p::Device* imu_controller_;
+};
+
+} // namespace kudzu::imu
diff --git a/lib/kudzu_imu_imgui/BUILD.gn b/lib/kudzu_imu_imgui/BUILD.gn
new file mode 100644
index 0000000..c10a1a5
--- /dev/null
+++ b/lib/kudzu_imu_imgui/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2023 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+pw_source_set("kudzu_imu_imgui") {
+ public_configs = [ ":default_config" ]
+ public = [ "public/kudzu_imu_imgui/imu.h" ]
+ deps = [
+ "$dir_pw_log",
+ "$dir_pw_result",
+ "//lib/kudzu_imu:kudzu_imu",
+ ]
+ sources = [ "imu.cc" ]
+}
diff --git a/lib/kudzu_imu_imgui/imu.cc b/lib/kudzu_imu_imgui/imu.cc
new file mode 100644
index 0000000..f0aa56a
--- /dev/null
+++ b/lib/kudzu_imu_imgui/imu.cc
@@ -0,0 +1,43 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "kudzu_imu/imu.h"
+
+#include <math.h>
+
+#include <cinttypes>
+
+#define PW_LOG_MODULE_NAME "kudzu_imu_imgui"
+#define PW_LOG_LEVEL PW_LOG_LEVEL_DEBUG
+
+#include "kudzu_imu_imgui/imu.h"
+#include "pw_log/log.h"
+
+namespace kudzu::imu {
+
+PollingImuImGui::PollingImuImGui() {}
+
+pw::Status PollingImuImGui::Init() { return pw::Status::Unimplemented(); }
+
+bool PollingImuImGui::IsAvailable() { return true; }
+
+pw::Result<ImuSample> PollingImuImGui::ReadData() {
+ ImuSample data = {.accelerometer = {1.0f, 2.0f, 3.0f},
+ .gyroscope = {10, 20, 30}};
+
+ last_data = data;
+ return data;
+}
+
+} // namespace kudzu::imu
diff --git a/lib/kudzu_imu_imgui/public/kudzu_imu_imgui/imu.h b/lib/kudzu_imu_imgui/public/kudzu_imu_imgui/imu.h
new file mode 100644
index 0000000..33215ed
--- /dev/null
+++ b/lib/kudzu_imu_imgui/public/kudzu_imu_imgui/imu.h
@@ -0,0 +1,32 @@
+// Copyright 2023 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "kudzu_imu/imu.h"
+#include "pw_status/status.h"
+
+namespace kudzu::imu {
+
+class PollingImuImGui : public PollingImu {
+ public:
+ PollingImuImGui();
+
+ pw::Status Init() override;
+ bool IsAvailable() override;
+ pw::Result<ImuSample> ReadData() override;
+
+ ImuSample last_data;
+};
+
+} // namespace kudzu::imu