Raspberry Pi access to GPIO, I2C, SPI?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
jcw
Posts: 37
Joined: Sat Dec 21, 2019 12:08 pm
Location: CET/CEST

Raspberry Pi access to GPIO, I2C, SPI?

Post by jcw » Fri Dec 27, 2019 1:50 pm

As I understand it, MPY's Unix build will run out of the box on a Raspberry Pi w/ Linux. What I haven't been able to find yet, is whether it will be possible to use the RPi's I/O, i.e. access to its GPIO pins, I2C buses, and SPI.

What I'm trying to evaluate, is whether MPY-on-RPI could be used as a convenient dev setup, before taking the code to one of the "real" embedded platforms supported by MPY. I.e. all the conveniences of editing, version control, speed, networking, no uploads, etc. Obviously not everything could be tested this way, but still.

I'm still learning and digging into the (great) docs and codebase. Will try it all out Real Soon Now, but tips and pointers appreciated.

-jcw

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: Raspberry Pi access to GPIO, I2C, SPI?

Post by kevinkk525 » Fri Dec 27, 2019 2:41 pm

RPI GPIO can't directly be accessed by micropython.
However I did also try to get that working and came across some code that uses the GPIO file-like access:

Code: Select all

# -*- coding: utf-8 -*-
__version__ = '0.1.0'

import os


class PinState(object):
    """An ultra simple pin-state object.
    Keeps track data related to each pin.
    Args:
        value: the file pointer to set/read value of pin.
        direction: the file pointer to set/read direction of the pin.
    """

    def __init__(self, value, direction):
        self.value = value
        self.direction = direction


path = os.path
pjoin = os.path.join

gpio_root = '/sys/class/gpio'
gpiopath = lambda pin: os.path.join(gpio_root, 'gpio{0}'.format(pin))

_open = dict()
FMODE = 'w+'

IN, OUT = "in", "out"
LOW, HIGH = 0, 1


def _write(f, v):
    print("writing: {}: {}".format(f, v))
    f.write(str(v))
    f.flush()


def _read(f):
    print("Reading: {}".format(f))
    f.seek(0)
    return f.read().strip()


def _verify(function):
    """decorator to ensure pin is properly set up"""

    def wrapped(pin, *args, **kwargs):
        pin = int(pin)
        if pin not in _open:
            ppath = gpiopath(pin)
            if not os.path.exists(ppath):
                print("Creating Pin {0}".format(pin))
                with open(pjoin(gpio_root, 'export'), 'w') as f:
                    _write(f, pin)
            value = open(pjoin(ppath, 'value'), FMODE)
            direction = open(pjoin(ppath, 'direction'), FMODE)
            _open[pin] = PinState(value=value, direction=direction)
        return function(pin, *args, **kwargs)

    return wrapped


def cleanup(pin=None, assert_exists=False):
    """Cleanup the pin by closing and unexporting it.
    Args:
        pin (int, optional): either the pin to clean up or None (default).
            If None, clean up all pins.
        assert_exists: if True, raise a ValueError if the pin was not
            setup. Otherwise, this function is a NOOP.
    """
    if pin is None:
        # Take a list of keys because we will be deleting from _open
        for pin in list(_open):
            cleanup(pin)
        return
    if not isinstance(pin, int):
        raise TypeError("pin must be an int, got: {}".format(pin))

    state = _open.get(pin)
    if state is None:
        if assert_exists:
            raise ValueError("pin {} was not setup".format(pin))
        return
    state.value.close()
    state.direction.close()
    if os.path.exists(gpiopath(pin)):
        print("Unexporting pin {0}".format(pin))
        with open(pjoin(gpio_root, 'unexport'), 'w') as f:
            _write(f, pin)

    del _open[pin]


@_verify
def setup(pin, mode, pullup=None, initial=False):
    '''Setup pin with mode IN or OUT.
    Args:
        pin (int):
        mode (str): use either gpio.OUT or gpio.IN
        pullup (None): rpio compatibility. If anything but None, raises
            value Error
        pullup (bool, optional): Initial pin value. Default is False
    '''
    if pullup is not None:
        raise ValueError("sysfs does not support pullups")

    if mode not in (IN, OUT):
        raise ValueError(mode)

    print("Setup {}: {}".format(pin, mode))
    f = _open[pin].direction
    _write(f, mode)
    if mode == OUT:
        if initial:
            set(pin, 1)
        else:
            set(pin, 0)


# @_verify
def mode(pin):
    '''get the pin mode
    Returns:
        str: "in" or "out"
    '''
    f = _open[pin].direction
    return _read(f)


# @_verify
def read(pin):
    '''read the pin value
    Returns:
        bool: 0 or 1
    '''
    f = _open[pin].value
    out = int(_read(f))
    print("Read {}: {}".format(pin, out))
    return out


# @_verify
def set(pin, value):
    '''set the pin value to 0 or 1'''
    print("Write {}: {}".format(pin, value))
    f = _open[pin].value
    _write(f, value)


input = read
output = set


def setwarnings(value):
    '''exists for rpio compatibility'''
    pass


def setmode(value):
    '''exists for rpio compatibility'''
    pass


BCM = None  # rpio compatibility
I did test this once but it only allows for very basic GPIO access because it is extremely slow. You won't get any protocol working except simply switching GPIOs on and off or reading a value.
Certainly no I2C etc possible.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
jcw
Posts: 37
Joined: Sat Dec 21, 2019 12:08 pm
Location: CET/CEST

Re: Raspberry Pi access to GPIO, I2C, SPI?

Post by jcw » Fri Dec 27, 2019 5:02 pm

Thx for that. Maybe a C-coded extension, similar to (or even based on) WiringPi could work.
I just found https://github.com/WiringPi/WiringPi-Python/ ... could be a starting point.

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: Raspberry Pi access to GPIO, I2C, SPI?

Post by kevinkk525 » Fri Dec 27, 2019 7:30 pm

I guess many people would be happy if you make it work :D
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

safetyfactorman
Posts: 28
Joined: Sat Jan 24, 2015 10:34 pm
Location: vancouver/kelowna/lethbridge

Re: Raspberry Pi access to GPIO, I2C, SPI?

Post by safetyfactorman » Thu Jan 02, 2020 3:35 am

openplc project may provide a leveraging point for many things. openplc has the goal to support iec61131-3 languages for automation programming. runs on raspberry pi, but also can run on similar embedded chips like stm32 and esp8266 (I believe).

1. openplc runs on multiple embedded platforms.
2. openplc can run on a dedicated core on raspberry pi.
3. leveraging off of openplc points towards codesys iec61131-3 compatibility. if it is possible to write a python wrapper to a codesys driver, or openplc driver, that might solve a lot of problems, or make gpio access easier.

I would like to suggest micropython as a IEC61131-3 language. I think that micropython will revolutionize the IEC61131 language standard.

Post Reply