[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-74285":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":25,"hasPages":23,"topics":26,"createdAt":10,"pushedAt":10,"updatedAt":42,"readmeContent":43,"aiSummary":44,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":45,"discoverSource":46},74285,"apple-silicon-accelerometer","olvvier\u002Fapple-silicon-accelerometer","olvvier","reading the undocumented mems accelerometer + gyroscope on apple silicon macbooks via iokit hid","https:\u002F\u002Fmedium.com\u002F@oli.bourbonnais\u002Fyour-macbook-has-an-accelerometer-and-you-can-read-it-in-real-time-in-python-28d9395fb180",null,"Python",1163,62,4,7,0,2,8,20,6,18.4,"MIT License",false,"main",true,[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41],"accelerometer","apple","applespu","gyroscope","hid","iokit","m2","m3","m4","macbook","macos","mems","research","sensor","spu","2026-06-12 02:03:24","# apple-silicon-accelerometer\n\n## Built with this\n\n| Project | Description |\n|---------|-------------|\n| **[Haptyk](https:\u002F\u002Fhaptyk.com)** | Free macOS app that turns your typing force into real mechanical keyboard sounds using the accelerometer |\n| [taigrr\u002Fspank](https:\u002F\u002Fgithub.com\u002Ftaigrr\u002Fspank) | Slap your MacBook, it yells back. Uses Apple Silicon accelerometer via IOKit HID |\n| [pirate\u002Fmac-hardware-toys](https:\u002F\u002Fgithub.com\u002Fpirate\u002Fmac-hardware-toys) | Programmatically control Mac keyboard & display brightness, accelerometer data, fan speed, and more |\n| [Knock](https:\u002F\u002Fwww.tryknock.app\u002F) | Turns taps on your MacBook into instant actions using the built-in accelerometer in Apple Silicon MacBooks |\n| [SlapMac](https:\u002F\u002Fslapmac.com\u002F) | Slap your MacBook. It talks back. |\n\nmore information: [read the article on Medium](https:\u002F\u002Fmedium.com\u002F@oli.bourbonnais\u002Fyour-macbook-has-an-accelerometer-and-you-can-read-it-in-real-time-in-python-28d9395fb180)\n\nit turns out modern macbook pros have an undocumented mems accelerometer + gyroscope managed by the sensor processing unit (spu).\nthis project reads both via iokit hid, along with lid angle and ambient light sensors from the same interface\n\nBuilt with this\nHaptyk - Free macOS app that turns your typing force into real mechanical keyboard sounds using the accelerometer.\ntaigrr\u002Fspank - Slap your MacBook, it yells back. Uses Apple Silicon accelerometer via IOKit HID.\npirate\u002Fmac-hardware-toys - Programmatically control Mac keyboard & display brightness, accelerometer data, fan speed, microphone, speaker, and more.\n\n![demo](https:\u002F\u002Fraw.githubusercontent.com\u002Folvvier\u002Fapple-silicon-accelerometer\u002Fmain\u002Fassets\u002Fdemo.gif)\n\n## try it\n\n    git clone https:\u002F\u002Fgithub.com\u002Folvvier\u002Fapple-silicon-accelerometer\n    cd apple-silicon-accelerometer\n    python3 -m venv .venv && source .venv\u002Fbin\u002Factivate\n    pip install -e .[demo]\n    sudo .venv\u002Fbin\u002Fpython3 motion_live.py\n\n## what is this\n\napple silicon chips (M2\u002FM3\u002FM4\u002FM5) have a hard to find mems IMU (accelerometer + gyroscope) managed by the sensor processing unit (SPU).\nit's not exposed through any public api or framework.\nthis project reads raw 3-axis acceleration and angular velocity data at ~800hz via iokit hid callbacks.\n\nonly tested on macbook pro m3 pro so far - might work on other apple silicon macs but no guarantees\n\n## how it works\n\nthe sensor lives under AppleSPUHIDDevice in the iokit registry, on vendor usage page 0xFF00.\nusage 3 is the accelerometer, usage 9 is the gyroscope (same physical IMU, believed to be Bosch BMI286 based on teardowns).\nthe driver is AppleSPUHIDDriver which is part of the sensor processing unit.\nwe open it with IOHIDDeviceCreate and register an asynchronous callback via IOHIDDeviceRegisterInputReportCallback.\ndata comes as 22-byte hid reports with x\u002Fy\u002Fz as int32 little-endian at byte offsets 6, 10, 14.\ndivide by 65536 to get the value in g (accel) or deg\u002Fs (gyro).\ncallback rate is ~100hz (decimated from ~800hz native)\n\norientation is computed by fusing accel + gyro with a Mahony AHRS quaternion filter and displayed as roll\u002Fpitch\u002Fyaw gauges\n\nyou can verify the device exists on your machine with:\n\n    ioreg -l -w0 | grep -A5 AppleSPUHIDDevice\n\n## install (beta API)\n\n    pip install macimu\n\nif you get `externally-managed-environment` (homebrew python), use a venv:\n\n    python3 -m venv .venv && source .venv\u002Fbin\u002Factivate && pip install macimu\n\n```python\nfrom macimu import IMU\n\nif __name__ == '__main__':\n    with IMU() as imu:\n        accel = imu.latest_accel()       # Sample(x, y, z) in g\n        gyro = imu.latest_gyro()         # Sample(x, y, z) in deg\u002Fs\n\n        for s in imu.read_accel():       # all new samples since last call\n            print(s.x, s.y, s.z)\n```\n\nrequires root (sudo) because iokit hid device access needs elevated privileges.\nnote: accelerometer reads ~1g at rest (gravity). use `macimu.filters.remove_gravity()` to isolate dynamic acceleration.\n\n### check if sensor exists (no root needed)\n\n```python\nfrom macimu import IMU\nprint(IMU.available())   # True on macbook pro m2+\n```\n\n### real-time orientation (roll \u002F pitch \u002F yaw)\n\nfuses accel + gyro with a mahony quaternion filter, no math needed on your side\n\n```python\nfrom macimu import IMU\n\nif __name__ == '__main__':\n    with IMU(orientation=True) as imu:\n        o = imu.orientation()\n        print(f\"{o.roll:.1f}° {o.pitch:.1f}° {o.yaw:.1f}°\")\n        print(o.qw, o.qx, o.qy, o.qz)  # raw quaternion\n```\n\n### timestamped samples (hardware timestamps from iokit)\n\neach sample includes a precise timestamp from the hid report (mach_absolute_time),\nnot a python-side clock. every report gets its own unique timestamp.\n\n```python\nfrom macimu import IMU\n\nif __name__ == '__main__':\n    with IMU() as imu:\n        for s in imu.read_accel_timed():\n            print(f\"t={s.t:.6f}  x={s.x:.3f}  y={s.y:.3f}  z={s.z:.3f}\")\n```\n\n### streaming with callback\n\n```python\nimport time\nfrom macimu import IMU\n\ndef on_sample(s):\n    print(s.x, s.y, s.z)\n\nif __name__ == '__main__':\n    with IMU() as imu:\n        stop = imu.on_accel(on_sample)  # background thread\n        time.sleep(10)\n        stop()                          # unregister\n```\n\n### sample rate control\n\n```python\nIMU(sample_rate=200)  # ~200 hz (preferred way)\nIMU(sample_rate=50)   # ~50 hz\nIMU(decimation=1)     # ~800 hz (full native rate)\nIMU(decimation=8)     # ~100 hz (default)\n```\n\n### signal processing (zero-dependency biquad butterworth filters)\n\n```python\nfrom macimu import IMU\nfrom macimu.filters import magnitude, remove_gravity, high_pass, low_pass, peak_detect\n\nif __name__ == '__main__':\n    with IMU() as imu:\n        samples = imu.read_accel()\n        m = magnitude(samples[0].x, samples[0].y, samples[0].z)\n        dynamic = remove_gravity(samples)               # kalman filter gravity removal\n        smooth = low_pass(samples, 5.0, 100.0)          # 2nd-order butterworth\n        taps = high_pass(samples, 10.0, 100.0, order=4) # 4th-order, -24 dB\u002Foct\n        mags = [magnitude(s.x, s.y, s.z) for s in samples]\n        hits = peak_detect(mags, threshold=1.2)          # detect impacts\n```\n\n### mock mode (no root needed, for development \u002F testing)\n\n```python\nfrom macimu import IMU\n\nimu = IMU.mock(duration=10.0, rate=100)  # synthetic sinusoidal data\nfor s in imu.stream_accel():\n    print(s)\n```\n\n### record and replay\n\n```python\nfrom macimu import IMU\n\nif __name__ == '__main__':\n    # record\n    with IMU() as imu:\n        imu.record_to(\"session.csv\")\n        time.sleep(10)\n\n    # replay (no root needed)\n    imu = IMU.from_recording(\"session.csv\")\n    for s in imu.stream_accel_timed():\n        print(s)\n```\n\n### api reference\n\n**constructor**\n\n    IMU(accel=True, gyro=True, als=False, lid=False, orientation=False, decimation=8, sample_rate=None)\n\n**class methods** (no root needed)\n\n| method | returns | description |\n|--------|---------|-------------|\n| `IMU.available()` | `bool` | check if sensor exists |\n| `IMU.device_info()` | `dict` | sensors list, serial, product name |\n| `IMU.mock(duration, rate, noise)` | `IMU` | synthetic data for testing |\n| `IMU.from_recording(path)` | `IMU` | replay from csv |\n\n**reading data**\n\n| method | returns | description |\n|--------|---------|-------------|\n| `imu.read_accel()` | `list[Sample]` | new samples since last call (x, y, z in g) |\n| `imu.read_gyro()` | `list[Sample]` | new samples since last call (x, y, z in deg\u002Fs) |\n| `imu.read_accel_timed()` | `list[TimedSample]` | with hardware timestamp (t, x, y, z) |\n| `imu.read_gyro_timed()` | `list[TimedSample]` | same for gyro |\n| `imu.latest_accel()` | `Sample \\| None` | most recent sample |\n| `imu.latest_gyro()` | `Sample \\| None` | most recent sample |\n| `imu.read_all()` | `dict` | latest from all enabled sensors |\n\n**orientation & sensors**\n\n| method | returns | description |\n|--------|---------|-------------|\n| `imu.orientation()` | `Orientation \\| None` | roll, pitch, yaw (deg) + quaternion |\n| `imu.read_lid()` | `float \\| None` | lid angle in degrees |\n| `imu.read_als()` | `ALSReading \\| None` | lux + 4 spectral channels |\n\n**streaming**\n\n| method | returns | description |\n|--------|---------|-------------|\n| `imu.stream_accel()` | generator | blocking, yields `Sample` |\n| `imu.stream_gyro()` | generator | blocking, yields `Sample` |\n| `imu.stream_accel_timed()` | generator | blocking, yields `TimedSample` |\n| `imu.stream_gyro_timed()` | generator | blocking, yields `TimedSample` |\n| `imu.on_accel(callback)` | `stop_fn` | background thread, call `stop()` to end |\n| `imu.on_gyro(callback)` | `stop_fn` | background thread, call `stop()` to end |\n\n**lifecycle**\n\n| method \u002F property | description |\n|-------------------|-------------|\n| `imu.start()` \u002F `imu.stop()` | manual lifecycle (or use `with IMU() as imu:`) |\n| `imu.is_running` | `True` if worker is active |\n| `imu.effective_sample_rate` | measured hz (or `None` if not enough data) |\n| `imu.record_to(path)` | start writing samples to csv |\n\n**filters** (`from macimu.filters import ...`) -- biquad butterworth, zero external deps\n\n| function | description |\n|----------|-------------|\n| `magnitude(x, y, z)` | euclidean magnitude |\n| `remove_gravity(samples, Q, R)` | kalman filter gravity subtraction |\n| `GravityKalman(Q, R)` | real-time gravity estimator (stateful) |\n| `low_pass(samples, cutoff_hz, rate, order=2)` | butterworth low-pass (-12 dB\u002Foct per order of 2) |\n| `high_pass(samples, cutoff_hz, rate, order=2)` | butterworth high-pass |\n| `bandpass(samples, low, high, rate, order=2)` | cascaded hp + lp |\n| `filtfilt_low_pass(samples, cutoff_hz, rate)` | zero-phase lp (no lag, offline only) |\n| `filtfilt_high_pass(samples, cutoff_hz, rate)` | zero-phase hp (no lag, offline only) |\n| `median_filter(samples, window=5)` | spike \u002F outlier removal |\n| `peak_detect(values, threshold, min_spacing)` | find peaks in 1d signal |\n| `rolling_rms(samples, window)` | rolling root-mean-square of magnitude |\n\n**exceptions**: `macimu.SensorNotFound` if no SPU device, `PermissionError` if not root\n\n## demo dashboard\n\n    git clone https:\u002F\u002Fgithub.com\u002Folvvier\u002Fapple-silicon-accelerometer\n    cd apple-silicon-accelerometer\n    python3 -m venv .venv && source .venv\u002Fbin\u002Factivate\n    pip install -e .[demo]\n    sudo .venv\u002Fbin\u002Fpython3 motion_live.py\n\nthe demo includes vibration detection, orientation gauges, experimental heartbeat (bcg), lid angle, ambient light, and optional keyboard flash\n\n### keyboard flash mode (bundled KBPulse)\n\n`motion_live.py` can flash the keyboard backlight from vibration intensity in near realtime.\nthe repo now vendors KBPulse, including a prebuilt apple silicon binary at `KBPulse\u002Fbin\u002FKBPulse`.\n\nrun as usual:\n\n    sudo python3 motion_live.py\n\noptional overrides:\n\n    sudo python3 motion_live.py --no-kbpulse\n    sudo python3 motion_live.py --kbpulse-bin \u002Fpath\u002Fto\u002FKBPulse\n\n### with uv\n\nIf you have `uv`\u002F`uvx` installed, you can also just\n\n    sudo uvx git+https:\u002F\u002Fgithub.com\u002Folvvier\u002Fapple-silicon-accelerometer.git\n\n## code structure\n\n- macimu\u002F - python package (`pip install macimu`): high-level IMU class + low-level iokit bindings, shared memory ring buffers\n- motion_live.py - demo app: vibration detection, heartbeat bcg, terminal ui\n- KBPulse\u002F - vendored keyboard backlight driver code + binary (`KBPulse\u002Fbin\u002FKBPulse`)\n\n## heartbeat demo\n\nplace your wrists on the laptop near the trackpad and wait 10-20 seconds for the signal to stabilize.\nthis uses ballistocardiography - the mechanical vibrations from your heartbeat transmitted through your arms into the chassis.\nexperimental, not reliable, just a fun use-case to show what the sensor can pick up.\nthe bcg bandpass is 0.8-3hz and bpm is estimated via autocorrelation on the filtered signal\n\n## notes\n\n- experimental \u002F undocumented AppleSPU hid path\n- requires sudo\n- may break on future macos updates\n- use at your own risk\n- not for medical use\n\n## tested on\n\n- macbook pro m3 pro, macos 15.6.1\n- python 3.14\n\n\n## known incompatible\n\n- intel macs (no spu)\n- m1 macbook pro (2020)\n- mac studio m4 max \n\n\n## license\n\nMIT\n\n---\n\nnot affiliated with Apple or any employer\n","该项目通过IOKit HID接口读取Apple Silicon MacBook中未公开的MEMS加速度计和陀螺仪数据。核心功能包括实时获取3轴加速度及角速度信息，频率约为800Hz，并支持读取屏幕开合角度和环境光传感器的数据。技术上利用Python编程语言实现，通过异步回调机制处理来自AppleSPUHIDDriver的数据流。适用于需要利用MacBook内置运动传感器进行研究、开发创新应用（如基于敲击或移动的交互体验）等场景。","2026-06-11 03:49:49","high_star"]