Skip to content

mtelvers/gpio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OCaml Hardware Interface Library for Raspberry Pi

An OCaml library for interfacing with Raspberry Pi hardware using Linux APIs. Includes GPIO, SPI, I2C, and drivers for various display and sensor modules.

  • GPIO Character Device API - Uses /dev/gpiochipN (not deprecated sysfs)
  • SPI Communication - Hardware SPI support via /dev/spidev
  • I2C Communication - I2C bus support for sensors and peripherals
  • Hardware Drivers:
    • Waveshare 2.13" ePaper Display V4 (with partial update support)
    • MAX7219 LED matrix display (chainable)
    • LCD2004 20x4 character display with I2C backpack
    • DS3231 Real-Time Clock
    • AT24C32 I2C EEPROM
  • Graphics Libraries:
    • 1-bit framebuffer with rotation support (0°, 90°, 180°, 270°)
    • 5×7 ASCII bitmap font
    • Drawing primitives (lines, rectangles, circles)
  • Pure OCaml with ctypes - No C code, uses OCaml FFI
  • 32-bit and 64-bit compatible

Requirements

opam install ctypes ctypes-foreign dune

Building

opam exec -- dune build

Examples

ePaper Display Graphics

Demo of graphics capabilities on ePaper display:

sudo opam exec -- dune exec test/epaper_graphics.exe

Shows text, lines, rectangles, circles, and complex scenes.

MAX7219 LED Matrix Display

Drive multiple chained 8×8 LED matrices:

sudo opam exec -- dune exec test/test_max7219.exe

Real-Time Clock

DS3231 I2C RTC with temperature sensor:

sudo opam exec -- dune exec test/test_rtc.exe

I2C EEPROM

AT24C32 EEPROM read/write:

sudo opam exec -- dune exec test/test_eeprom.exe

Combined Clock Display

MAX7219 display showing time from DS3231 RTC:

sudo opam exec -- dune exec test/clock.exe

Basic GPIO

LED blinking, button reading, PWM:

sudo opam exec -- dune exec test/test_led.exe
sudo opam exec -- dune exec test/test_button.exe
sudo opam exec -- dune exec test/test_pwm.exe

I2C Bus Scanner

Scan for I2C devices on the bus:

sudo opam exec -- dune exec test/i2c_scan.exe

LCD2004 Display

20x4 character LCD with I2C backpack:

sudo opam exec -- dune exec test/test_lcd2004.exe
sudo opam exec -- dune exec test/seven_seg_clock.exe

Library Modules

GPIO (lib/gpio.ml)

Modern character device API for GPIO control:

let chip = Gpio.open_chip () in  (* Auto-detects GPIO chip *)
let led = Gpio.setup_output chip 17 in
Gpio.write led true;
Gpio.release_line led;
Gpio.close_chip chip

SPI (lib/spi.ml)

Hardware SPI communication:

let spi = Spi.open_device ~speed_hz:1000000 "/dev/spidev0.0" in
Spi.write_bytes spi [0x42; 0xFF];
Spi.close_device spi

I2C (lib/i2c.ml)

I2C bus communication:

let fd = I2c.open_device 1 in  (* Open /dev/i2c-1 *)
I2c.set_slave_address fd 0x68;
I2c.write_bytes fd [0x00; 0x45];
let data = I2c.read_bytes fd 7 in
I2c.close_device fd

ePaper Display (lib/epaper.ml, lib/epd2in13_v4.ml)

Waveshare 2.13" ePaper display with partial update support:

let display = Epaper.init () in
Epd2in13_v4.init display;

(* Full refresh *)
Epd2in13_v4.display display buffer;

(* Set base image for partial updates *)
Epd2in13_v4.display_partial_base display buffer;

(* Partial updates (only changed pixels) *)
Epd2in13_v4.display_partial display buffer;

Framebuffer Graphics (lib/framebuffer.ml)

1-bit graphics with rotation support (0°, 90°, 180°, 270°):

(* Create 250×122 framebuffer with 90° rotation *)
let fb = Framebuffer.create ~rotation:90 250 122 0xFF in

(* Drawing primitives *)
Framebuffer.draw_line fb 0 0 100 100 true;
Framebuffer.draw_circle fb 50 50 20 true;
Framebuffer.draw_filled_rect fb 10 10 30 40 true;

(* Get rotated buffer for display *)
let buffer = Framebuffer.to_list_rotated fb in

Font Rendering (lib/font.ml)

5×7 ASCII bitmap font:

let width = Font.draw_string fb 10 10 "Hello, World!" true in
let char_width = Font.draw_char fb x y 'A' true in

MAX7219 (lib/max7219.ml)

LED matrix display driver with chaining:

let display = Max7219.init "/dev/spidev0.0" 4 in  (* 4 daisy-chained displays *)
Max7219.set_intensity display 5;
Max7219.set_row display 0 3 0x7F;  (* device 0, row 3 *)
Max7219.clear_all display;
Max7219.close display;

DS3231 RTC (lib/ds3231.ml)

Real-time clock with temperature sensor:

let rtc = Ds3231.open_device 1 in  (* Open on I2C bus 1 *)

(* Read date/time *)
let dt = Ds3231.read_datetime rtc in
Printf.printf "%02d:%02d:%02d\n" dt.hours dt.minutes dt.seconds;

(* Write date/time *)
let dt = { year = 2025; month = 1; date = 15; day = 3;
           hours = 14; minutes = 30; seconds = 0 } in
Ds3231.write_datetime rtc dt;

(* Read temperature *)
let temp = Ds3231.read_temperature rtc in
Printf.printf "Temperature: %.2f°C\n" temp;

Ds3231.close_device rtc;

AT24C32 EEPROM (lib/at24c32.ml)

I2C EEPROM with page writing:

let eeprom = At24c32.open_device 1 0x57 in  (* Bus 1, address 0x57 *)

(* Single byte operations *)
At24c32.write_byte eeprom 0x0000 0x42;
let value = At24c32.read_byte eeprom 0x0000 in

(* Multi-byte operations *)
At24c32.write_bytes eeprom 0x0010 [0x01; 0x02; 0x03];
let data = At24c32.read_bytes eeprom 0x0010 3 in

(* String operations *)
At24c32.write_string eeprom 0x0100 "Hello, EEPROM!";
let str = At24c32.read_string eeprom 0x0100 64 in

At24c32.close_device eeprom;

Hardware Connections

ePaper Display (Waveshare 2.13" V4)

  • VCC → 3.3V
  • GND → Ground
  • DIN → SPI0 MOSI (GPIO 10)
  • CLK → SPI0 SCLK (GPIO 11)
  • CS → SPI0 CE0 (GPIO 8) - or any GPIO if using software CS
  • DC → GPIO 25
  • RST → GPIO 17
  • BUSY → GPIO 24
  • PWR → GPIO 18 (optional)

Note: When using hardware SPI CS, the CS pin is automatically managed by the SPI driver.

MAX7219 LED Matrix

  • VCC → 5V
  • GND → Ground
  • DIN → SPI0 MOSI (GPIO 10)
  • CLK → SPI0 SCLK (GPIO 11)
  • CS → SPI0 CE0 (GPIO 8)

DS3231 RTC

  • VCC → 3.3V or 5V
  • GND → Ground
  • SDA → I2C1 SDA (GPIO 2)
  • SCL → I2C1 SCL (GPIO 3)

AT24C32 EEPROM

  • VCC → 3.3V or 5V
  • GND → Ground
  • SDA → I2C1 SDA (GPIO 2)
  • SCL → I2C1 SCL (GPIO 3)
  • WP → Ground (write enable)
  • A0, A1, A2 → Ground (address pins)

Enabling SPI and I2C

# Enable interfaces using raspi-config
sudo raspi-config
# Interface Options → SPI → Enable
# Interface Options → I2C → Enable

# Reboot
sudo reboot

# Verify devices exist
ls /dev/spidev* /dev/i2c*

Permissions

GPIO, SPI, and I2C require appropriate permissions:

# Add user to gpio, spi, i2c groups
sudo usermod -a -G gpio,spi,i2c $USER

# Log out and back in for changes to take effect

# Or run with sudo
sudo opam exec -- dune exec test/test_max7219.exe

Technical Details

GPIO Character Device API

Uses modern /dev/gpiochipN interface with ioctl calls:

  • GPIO_V2_GET_LINE_IOCTL - Request GPIO lines
  • GPIO_V2_LINE_SET_VALUES_IOCTL - Set output values
  • GPIO_V2_LINE_GET_VALUES_IOCTL - Read input values

All ioctl constants use int32 for 32-bit compatibility.

ePaper Partial Updates

The display has two RAM buffers (0x24 and 0x26) for hardware pixel comparison:

  • Set base image → writes to both buffers
  • Partial update → controller compares and updates only changed pixels
  • Results in flicker-free updates (ideal for clocks, status displays)

Framebuffer Rotation

Supports 0°, 90°, 180°, 270° rotation:

  • Rotation applied when converting buffer to display format
  • Allows landscape/portrait modes without redrawing logic
  • Useful for physical mounting orientations

32-bit Compatibility

All ioctl constants are defined as int32 to work on both 32-bit and 64-bit systems. On 32-bit OCaml, the int type is only 31 bits, so large ioctl values like 0xC250B407 require int32.

Project Structure

gpio/
├── lib/
│   ├── ioctl.ml          - Low-level ioctl wrapper
│   ├── gpio.ml           - GPIO character device API
│   ├── spi.ml            - SPI communication
│   ├── i2c.ml            - I2C communication
│   ├── epaper.ml         - Generic ePaper driver
│   ├── epd2in13_v4.ml    - Waveshare 2.13" V4 specific
│   ├── max7219.ml        - MAX7219 LED matrix
│   ├── ds3231.ml         - DS3231 RTC
│   ├── at24c32.ml        - AT24C32 EEPROM
│   ├── framebuffer.ml    - Graphics primitives
│   ├── font.ml           - Bitmap font rendering
│   └── dune              - Library definitions
├── test/
│   ├── test_max7219.ml   - LED matrix demo
│   ├── clock.ml          - RTC + LED matrix clock
│   ├── test_rtc.ml       - RTC test
│   ├── test_eeprom.ml    - EEPROM test
│   ├── epaper_graphics.ml - Graphics demo
│   ├── test_epaper.ml    - ePaper test
│   ├── test_led.ml       - LED blink
│   ├── test_button.ml    - Button input
│   ├── test_pwm.ml       - Software PWM
│   ├── test_lcd2004.ml   - LCD2004 display test
│   ├── seven_seg_clock.ml - 7-segment clock on LCD
│   ├── i2c_scan.ml       - I2C bus scanner
│   └── dune              - Test executables
└── dune-project          - Project configuration

About

An OCaml library for Raspberry Pi hardware interfaces using ctypes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors