diff --git a/src/kaleidoscope/driver/sensor/ADNS5050.h b/src/kaleidoscope/driver/sensor/ADNS5050.h
new file mode 100644
index 00000000..aca99d01
--- /dev/null
+++ b/src/kaleidoscope/driver/sensor/ADNS5050.h
@@ -0,0 +1,224 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope - Firmware for computer input devices
+ * Copyright (C) 2022 Keyboard.io, Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#if defined(__AVR__)
+
+#include // for uint8_t
+#include "kaleidoscope/device/avr/pins_and_ports.h"
+
+namespace kaleidoscope {
+namespace driver {
+namespace sensor {
+
+struct ADNS5050BaseProps {
+ static constexpr uint8_t clock_pin = NO_PIN;
+ static constexpr uint8_t io_pin = NO_PIN;
+ static constexpr uint8_t chip_select_pin = NO_PIN;
+};
+
+template
+class ADNS5050 {
+ public:
+ ADNS5050() {}
+
+ typedef struct Report {
+ int8_t dx;
+ int8_t dy;
+ };
+
+ enum class CPI {
+ CPI125 = 0x11,
+ CPI250 = 0x12,
+ CPI375 = 0x13,
+ CPI500 = 0x14,
+ CPI625 = 0x15,
+ CPI750 = 0x16,
+ CPI875 = 0x17,
+ CPI1000 = 0x18,
+ CPI1125 = 0x19,
+ CPI1250 = 0x1a,
+ CPI1375 = 0x1b
+ };
+
+ void setup() {
+ static_assert(_Props::clock_pin != NO_PIN, "No clock pin specified!");
+ static_assert(_Props::io_pin != NO_PIN, "No IO pin specified!");
+ static_assert(_Props::chip_select_pin != NO_PIN, "No chip select pin specified!");
+
+ DDR_OUTPUT(_Props::clock_pin);
+ DDR_OUTPUT(_Props::io_pin);
+ DDR_OUTPUT(_Props::chip_select_pin);
+
+ // 0x5a is chip reset (datasheet page 26)
+ writeRegister(Register::CHIP_RESET, 0x5a);
+ delay(55);
+
+ // read an initial report from the sensor, and discard it.
+ readReport();
+ }
+
+ void sync() {
+ cs_select();
+ delayMicroseconds(1);
+ cs_deselect();
+ }
+
+ Report readReport() {
+ Report report;
+
+ cs_select();
+
+ serialWrite(Register::MOTION_BURST);
+
+ uint8_t x = serialRead();
+ uint8_t y = serialRead();
+
+ cs_deselect();
+
+ report.dx = convertTwosComp(x);
+ report.dy = convertTwosComp(y);
+
+ return report;
+ }
+
+ void setCPI(CPI cpi) {
+ uint8_t cpival = cpi / 125;
+ if (cpival < 0x01) {
+ cpival = 0x1;
+ }
+ if (cpival > 0x0d) {
+ cpival = 0x0d;
+ }
+
+ writeRegister(Register::MOUSE_CONTROL2, 0b10000 | cpival);
+ }
+ CPI getCPI() {
+ // not implemented yet
+ return CPI::CPI125;
+ }
+
+ bool checkSignature() {
+ uint8_t rid = readRegister(Register::REVISION_ID);
+ uint8_t pid = readRegister(Register::PRODUCT_ID);
+ uint8_t pid2 = readRegister(Register::PRODUCT_ID2);
+
+ return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
+ }
+
+ public:
+ enum class Register {
+ PRODUCT_ID = 0x00,
+ REVISION_ID = 0x01,
+ MOTION = 0x02,
+ DELTA_X = 0x03,
+ DELTA_Y = 0x04,
+ MOUSE_CONTROL2 = 0x19,
+ CHIP_RESET = 0x3a,
+ PRODUCT_ID2 = 0x3e,
+ MOTION_BURST = 0x63
+ };
+
+ int8_t convertTwosComp(uint8_t data) {
+ if ((data & 0x80) == 0x80)
+ return -128 + (data & 0x7F);
+ else
+ return data;
+ }
+
+ uint8_t serialRead() {
+ DDR_INPUT(_Props::io_pin);
+ delayMicroseconds(1);
+
+ uint8_t byte;
+
+ for (uint8_t i = 0; i < 8; i++) {
+ OUTPUT_LOW(_Props::clock_pin);
+ delayMicroseconds(1);
+
+ byte = (byte << 1) | READ_PIN(_Props::io_pin);
+
+ OUTPUT_HIGH(_Props::clock_pin);
+ delayMicroseconds(1);
+ }
+
+ return byte;
+ }
+
+ void serialWrite(uint8_t data) {
+ DDR_OUTPUT(_Props::io_pin);
+ delayMicroseconds(1);
+
+ for (int8_t i = 7; i >= 0; i--) {
+ OUTPUT_LOW(_Props::clock_pin);
+ delayMicroseconds(1);
+
+ if (data && (1 << i)) {
+ OUTPUT_HIGH(_Props::io_pin);
+ } else {
+ OUTPUT_LOW(_Props::io_pin);
+ }
+
+ delayMicroseconds(2);
+
+ OUTPUT_HIGH(_Props::clock_pin);
+ delayMicroseconds(1);
+ }
+
+ delayMicroseconds(4);
+ }
+ void serialWrite(Register reg) {
+ serialWrite((uint8_t) reg);
+ }
+
+ uint8_t readRegister(Register reg) {
+ cs_select();
+
+ serialWrite(reg);
+ uint8_t byte = serialRead();
+
+ delayMicroseconds(1);
+
+ cs_deselect();
+
+ return byte;
+ }
+ void writeRegister(Register reg, uint8_t data) {
+ cs_select();
+ serialWrite(0b10000000 | (uint8_t)reg);
+ serialWrite(data);
+ cs_deselect();
+ }
+
+ void cs_select() {
+ OUTPUT_LOW(_Props::chip_select_pin);
+ delayMicroseconds(1);
+ }
+ void cs_deselect() {
+ OUTPUT_HIGH(_Props::chip_select_pin);
+ delayMicroseconds(1);
+ }
+
+};
+
+} // namespace sensor
+} // namespace driver
+} // namespace kaleidoscope
+
+#endif // define(__AVR__)