Skip to content

Instantly share code, notes, and snippets.

@raspberrypisig
Last active November 8, 2025 21:38
Show Gist options
  • Select an option

  • Save raspberrypisig/e378e50add20431747f30dce6219d67c to your computer and use it in GitHub Desktop.

Select an option

Save raspberrypisig/e378e50add20431747f30dce6219d67c to your computer and use it in GitHub Desktop.
OUT = "output"
IN = "input"
HIGH = 1
LOW = 0
PULL_UP = "pull_up"
PULL_DOWN = "pull_down"
PULL_NONE = "pull_none"
# Define a Pin "object" using a dictionary
def create_pin(pin_number, mode, pull=PULL_NONE, initial_level=LOW):
return {
"number": pin_number,
"mode": mode,
"pull": pull,
"initial_level": initial_level,
}
# Define functions to manipulate pins
def pin_high(pin):
# Simulate setting the pin high
pass
def pin_low(pin):
# Simulate setting the pin low
pass
def pin_value(pin, level=None):
# Simulate reading or setting the pin value
pass
def pin_cleanup(pin):
# Simulate cleaning up the pin
pass
# Represent a pin as a string
def pin_repr(pin):
return "<Pin(GPIO{}, mode='{}')>".format(pin["number"], pin["mode"])
# Cleanup function
def cleanup(pin_number=None):
# Simulate cleanup logic
pass
# Expose constants as a dictionary
CONSTANTS = {
"OUT": OUT,
"IN": IN,
"HIGH": HIGH,
"LOW": LOW,
"PULL_UP": PULL_UP,
"PULL_DOWN": PULL_DOWN,
"PULL_NONE": PULL_NONE,
}
#
# gpio.star: A Starlark Module for GPIO Control on ESP32
#
# This module provides a high-level, Pythonic interface for General Purpose
# Input/Output (GPIO) pins. It acts as a user-friendly wrapper around
# native functions implemented in Rust and exposed via an FFI boundary.
#
# The Rust host is responsible for providing the following native functions
# in the Starlark environment before this module is loaded:
#
# - _native_setup_output(pin_number, initial_level) -> opaque_pin_handle
# Configures a pin as an output and returns a handle.
#
# - _native_setup_input(pin_number, pull) -> opaque_pin_handle
# Configures a pin as an input with a specific pull resistor and returns a handle.
#
# - _native_set_level(opaque_pin_handle, level)
# Sets the logical level of an output pin.
#
# - _native_get_level(opaque_pin_handle) -> int
# Reads the logical level of an input pin. Returns HIGH (1) or LOW (0).
#
# - _native_cleanup(pin_number)
# De-initializes a pin, returning it to a safe, high-impedance state.
#
# --- Module Constants ---
# Pin modes
OUT = "output"
IN = "input"
# Pin levels
HIGH = 1
LOW = 0
# Pull resistor configurations
PULL_UP = "pull_up"
PULL_DOWN = "pull_down"
PULL_NONE = "pull_none"
class Pin:
"""Represents a single physical GPIO pin."""
def __init__(self, pin_number, mode, pull=PULL_NONE, initial_level=LOW):
"""
Initializes and configures a GPIO pin.
This constructor calls the underlying native Rust functions to set up
the hardware pin according to the specified parameters.
Args:
pin_number (int): The GPIO number (e.g., 13 for GPIO13).
mode (str): The mode of the pin, either OUT or IN.
pull (str, optional): The pull resistor setting for input pins.
Can be PULL_UP, PULL_DOWN, or PULL_NONE.
Defaults to PULL_NONE.
initial_level (int, optional): The initial level for output pins.
Can be HIGH or LOW. Defaults to LOW.
"""
if not type(pin_number) == "int":
fail("pin_number must be an integer.")
self._number = pin_number
self._mode = mode
self._native_handle = None # Stores the opaque object from Rust
if mode == OUT:
if initial_level != HIGH and initial_level != LOW:
fail("initial_level for an output pin must be HIGH or LOW.")
# Call the native Rust function to configure the pin as an output.
# The returned handle is stored for future operations on this pin.
self._native_handle = _native_setup_output(pin_number, initial_level)
elif mode == IN:
# Call the native Rust function to configure the pin as an input.
self._native_handle = _native_setup_input(pin_number, pull)
else:
fail("Unsupported pin mode: '{}'. Use OUT or IN.".format(mode))
def high(self):
"""Sets the output pin level to HIGH. Fails if the pin is not an output."""
if self._mode != OUT:
fail("Cannot set level on an input pin.")
_native_set_level(self._native_handle, HIGH)
def low(self):
"""Sets the output pin level to LOW. Fails if the pin is not an output."""
if self._mode != OUT:
fail("Cannot set level on an input pin.")
_native_set_level(self._native_handle, LOW)
def value(self, level=None):
"""
Gets or sets the value of the pin.
- If `level` is provided, sets the pin's level (for output pins).
- If `level` is not provided, returns the pin's level (for input pins).
Args:
level (int, optional): The level to set (HIGH or LOW).
Returns:
int or None: The current level (HIGH or LOW) if getting, or None if setting.
"""
if level == None:
# Getting the value (for input pins)
if self._mode != IN:
fail("Can only read value from an input pin. Call high() or low() to set an output pin.")
return _native_get_level(self._native_handle)
else:
# Setting the value (for output pins)
if self._mode != OUT:
fail("Cannot set value on an input pin.")
if level != HIGH and level != LOW:
fail("Value must be HIGH or LOW.")
_native_set_level(self._native_handle, level)
def cleanup(self):
"""De-initializes the pin, returning it to a safe state."""
_native_cleanup(self._number)
self._native_handle = None # Invalidate the handle
def __repr__(self):
"""Provides a string representation of the Pin object."""
return "<Pin(GPIO{}, mode='{}')>".format(self._number, self._mode)
def cleanup(pin_number=None):
"""
De-initializes one or all pins.
If a pin number is provided, only that pin is cleaned up.
If no pin number is provided, the native cleanup function is expected
to handle all pins (if the underlying implementation supports this).
"""
if pin_number != None:
_native_cleanup(pin_number)
else:
# A pin_number of -1 could be a convention to tell the native
# layer to clean up all pins it has track of.
_native_cleanup(-1)
# --- Public API Export ---
# This dictionary defines what symbols are available when a user script
# executes `load("gpio.star", "gpio")`.
PIN = Pin
# Expose constants for easy access, e.g., `gpio.OUT`.
CONSTANTS = {
"OUT": OUT,
"IN": IN,
"HIGH": HIGH,
"LOW": LOW,
"PULL_UP": PULL_UP,
"PULL_DOWN": PULL_DOWN,
"PULL_NONE": PULL_NONE,
}
# gpio.star
OUT = "output"
IN = "input"
HIGH = 1
LOW = 0
PULL_UP = "pull_up"
PULL_DOWN = "pull_down"
PULL_NONE = "pull_none"
class Pin:
def __init__(self, pin_number, mode, pull=PULL_NONE, initial_level=LOW):
self._number = pin_number
self._mode = mode
pass
def high(self):
pass
def low(self):
pass
def value(self, level=None):
pass
def cleanup(self):
pass
def __repr__(self):
return "<Pin(GPIO{}, mode='{}')>".format(self._number, self._mode)
def cleanup(pin_number=None):
pass
PIN = Pin
CONSTANTS = {
"OUT": OUT,
"IN": IN,
"HIGH": HIGH,
"LOW": LOW,
"PULL_UP": PULL_UP,
"PULL_DOWN": PULL_DOWN,
"PULL_NONE": PULL_NONE,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment