[ANN] Introducing BaremetalPi.jl - A package to access Raspberry Pi peripherals without external libraries

Indeed! If your cycle is 25.5ms, meaning that the frequency is almost 40 Hz, then it should be fine!

Yeah, thereā€™s a tradeoff between granularity of control, and speed of cycle. If you have 8 bit level control, then you need 255 of these 100us time periods. But you need not arrange them as on on on on on ā€¦ off off off offā€¦ If you do that on an LED, youā€™ll have 40Hz flicker. Instead, imagine you want to have say 35% of full power. you start with an array of 89 one values and 166 zero valuesā€¦ you shuffle them in random orderā€¦ and then at each cycle you assert the current value from the arrayā€¦ This produces more like a 10kHz signal thatā€™s modulated to 35% ā€œbrightnessā€ no 40Hz flicker. same for motor controllers you will get a smoother torque.

2 Likes

I do not know if it is the new kernel (5.4), compiled with a new version of gcc, or something at the user space, but the latency I achieved with ArchLinux is way better than Raspbian:

This is data acquired during almost 3 days. With those numbers, I do not even need PREEMPT_RT patch.

2 Likes

Hi @Ronis_BR,

I have been experimenting with Julia on a Pi Zero W too.

I have found that it is quite simple to directly use the C interface of libpigpio thanks to https://github.com/JuliaInterop/Clang.jl.

I probably wonā€™t be able to achieve the raw performance of your mmap implementation, but I do get access to some nice features in libpigpio like DMA driven PWM and serial bit bang (e.g. gpioSerialReadOpen()).

I use the following Clang.jl command to generate wrappers for pigpio.h:

    using Clang
    Clang.init(
        headers        = ["pigpio.h"],
        output_file    =  "pigpio.jl",
        common_file    =  "pigpio_common.jl",
        clang_includes = [CLANG_INCLUDE],
        header_wrapped = (root, current)->root == current,
        cursor_wrapped = (cursorname, cursor) -> !contains(string(cursor), "Thread")
    ) |> run

After that I can call the C API of libpigpio from Julia like this:

include("pigpio_common.jl")
include("pigpio.jl")

const RESET_PIN = 18
const SERIAL_PIN = 14
const SERIAL_BAUD = 10000

buffer = Channel{UInt32}(10000)

# Configure pigpio...
gpioCfgClock(10, 1, 1); # us, PCM, ignored
res = gpioInitialise();
@assert(res != PI_INIT_FAILED)


# Hold AVR in RESET...
gpioSetMode(RESET_PIN, PI_OUTPUT)
gpioWrite(RESET_PIN, 0)

# Start reading from serial port...
err = gpioSerialReadOpen(SERIAL_PIN, SERIAL_BAUD, 32);
@assert(err == 0);

# Release AVR RESET...
gpioDelay(100000)#us
gpioWrite(RESET_PIN, 1)
gpioSetMode(RESET_PIN, PI_INPUT)


while true

    v = [UInt32(0)]

    n = gpioSerialRead(SERIAL_PIN, v, sizeof(UInt32));
    @assert(n == 0 || n == sizeof(UInt32))

    if n == sizeof(UInt32)
        put!(buffer, v[1])
    else
        sleep(0.005)
    end
end

Maybe a Clang.jl interface to libpigpio could be incorporated into your package to implement some of the things that you are currently missing (like PWM).

4 Likes

Hi @notinaboat!

Thanks for the tip. However, the idea of BaremetalPi.jl is to create a 100% Julia library that can be used to manage the interfaces of the Raspberry Pi. I mean, something that does not require external dependencies such as libpigpio. I am really lacking the time to implement what is missing, but it will be done eventually :slight_smile:

3 Likes

Related: this blog has good info on using the SMI and DMA to do high speed IO on a Pi Zero W.

3 Likes

This is awesome! Thanks for the tip. It will save me some time :slight_smile:

1 Like

Hello,
Iā€™m trying to install BaremetalPi.jl on a Raspberry 3A + (64 bit) for use in my metal testing lab.
Unfortunately the compilation doesnā€™t seem to work.
I use Julia 1.7
Do you have any idea why?

Thanks for your help
Adriano Bassignana
abassign@gmail.com

(@v1.7) pkg> add https://github.com/ronisbr/BaremetalPi.jl
     Cloning git-repo `https://github.com/ronisbr/BaremetalPi.jl`
    Updating git-repo `https://github.com/ronisbr/BaremetalPi.jl`
    Updating registry at `~/.julia/registries/General.toml`
   Resolving package versions...
    Updating `~/.julia/environments/v1.7/Project.toml`
  [3a90a9a9] + BaremetalPi v0.1.1 `https://github.com/ronisbr/BaremetalPi.jl#master`
    Updating `~/.julia/environments/v1.7/Manifest.toml`
  [3a90a9a9] + BaremetalPi v0.1.1 `https://github.com/ronisbr/BaremetalPi.jl#master`
Precompiling project...
  āœ— BaremetalPi
  0 dependencies successfully precompiled in 106 seconds (10 already precompiled)
  1 dependency errored. To see a full report either run `import Pkg; Pkg.precompile()` or load the package

(@v1.7) pkg> ^C

julia> import Pkg; Pkg.precompile()
Precompiling project...
  āœ— BaremetalPi
  0 dependencies successfully precompiled in 40 seconds (10 already precompiled)

ERROR: The following 1 direct dependency failed to precompile:

BaremetalPi [3a90a9a9-52f9-452b-94bd-193d585bd84f]

Failed to precompile BaremetalPi [3a90a9a9-52f9-452b-94bd-193d585bd84f] to /home/dietpi/.julia/compiled/v1.7/BaremetalPi/jl_xgbKmk.
ERROR: LoadError: TypeError: in ccall method definition, expected Type, got a value of type TypeVar
Stacktrace:
  [1] top-level scope
    @ ~/.julia/packages/BaremetalPi/r8tXm/src/ioctl.jl:12
  [2] include(mod::Module, _path::String)
    @ Base ./Base.jl:418
  [3] include(x::String)
    @ BaremetalPi ~/.julia/packages/BaremetalPi/r8tXm/src/BaremetalPi.jl:1
  [4] top-level scope
    @ ~/.julia/packages/BaremetalPi/r8tXm/src/BaremetalPi.jl:22
  [5] include
    @ ./Base.jl:418 [inlined]
  [6] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1318
  [7] top-level scope
    @ none:1
  [8] eval
    @ ./boot.jl:373 [inlined]
  [9] eval(x::Expr)
    @ Base.MainInclude ./client.jl:453
 [10] top-level scope
    @ none:1
in expression starting at /home/dietpi/.julia/packages/BaremetalPi/r8tXm/src/ioctl.jl:12
in expression starting at /home/dietpi/.julia/packages/BaremetalPi/r8tXm/src/BaremetalPi.jl:1
Stacktrace:
 [1] pkgerror(msg::String)
   @ Pkg.Types /opt/julia-1.7.0/share/julia/stdlib/v1.7/Pkg/src/Types.jl:68
 [2] precompile(ctx::Pkg.Types.Context; internal_call::Bool, strict::Bool, warn_loaded::Bool, kwargs::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ Pkg.API /opt/julia-1.7.0/share/julia/stdlib/v1.7/Pkg/src/API.jl:1362
 [3] precompile
   @ /opt/julia-1.7.0/share/julia/stdlib/v1.7/Pkg/src/API.jl:1013 [inlined]
 [4] #precompile#220
   @ /opt/julia-1.7.0/share/julia/stdlib/v1.7/Pkg/src/API.jl:1011 [inlined]
 [5] precompile()
   @ Pkg.API /opt/julia-1.7.0/share/julia/stdlib/v1.7/Pkg/src/API.jl:1011
 [6] top-level scope
   @ REPL[4]:1

1 Like

Hi @Adriano_Bassignana

I think this version does not support 1.7.0. In fact, I have never been able to run v1.6 or newer in my Raspberry Pi (zero). I will try to build Julia 1.7.0 again and, if it works, then I update the package to support it.

Does anyone manage to build v1.7.0 for the Pi Zero?

1 Like

Hi, wow what an orbital speed in the response!
I currently operate only with 1.7 (at the limit I can switch to 1.6 which has just become LTS), but honestly I prefer to operate on 1.7 which has several advantages.
Personally I believe that the problem is related to an error, at least it seems like this to tell the error:

ERROR: LoadError: TypeError: in ccall method definition, expected Type, got a value of type TypeVar                                                                                               
Stacktrace:                                                                                                                                                                                       
  [1] top-level scope                                                                                                                                                                             
    @ ~/.julia/packages/BaremetalPi/r8tXm/src/ioctl.jl:12                                                                                                                                         
  [2] include(mod::Module, _path::String)

But I can be wrong as I have only been working in Julia for a few months and this is the first Raspberry application I am doing.
For the raspberry zero I would like to try, only due to Brexit and international speculation, finding Raspberry is becoming difficult here in Italy.
But if I understand correctly, do you want to put a raspberry zero in a cube sat?
I would be curious to understand, unfortunately I am a mechanical engineer and have never been able to work in the aerospace sector, but these applications always give me a great curiosity. If you have a job that explains it I would be grateful!
You can find me on GitHub at this address:
https://github.com/abassign

The Photo Scenery application is an application that I developed to download photorealistic maps from some internet sites to be able to use them with Flightgear which is an opensource flight simulator.
The G91R1B is an Italian simulated aircraft that I developed for Flightgear and the FDM is made with JSBSim a simulation language for realtime system configured with XML scripts.
Now the zeroes are back on Amazon
If I can get the compilation to work on 3A + Iā€™m sure it works on zero as well, at this point I can place an order for my laboratory to have zeroes available and then Iā€™ll let you know if it goes ā€¦ The zero is very interesting as newer alternative to 3A +.

Greetings
Adriano Bassignana
abassign@gmail.com

2 Likes

Hi @Adriano_Bassignana !

:slight_smile:

The construction I was using seems to be ā€œwrongā€ and it now returns an error in v1.7. I asked about it in this thread:

Letā€™s see what is the best way to fix it.

Thatā€™s the idea :slight_smile: Everything got delayed due to COVID, but I am still pursing this objective. I want to make a CubeSat in which the on-board computer is a RaspberryPi running Julia. The goal is to make a proof-of-concept that we can use Julia to perform critical control tasks. If we succeed, then we will have access to a much better development platform, IMHO, than coding everything in C++.

I currently work at the Brazilian National Institute for Space Research as a Space Systems Engineer. Feel free to ask me any question :slight_smile: I do not currently have any official project about this application yet. I need to perform a demonstration using a mock-up to show that the idea is feasible.

Nice! Letā€™s try to make it work then :smiley: If someone provides me some guideline at that Github issue, then maybe I can fix it without requiring to actually run v1.7.0 on the Pi Zero.

I donā€™t think you have many options besides doing the ccalls (and probably defining the function) with concrete types. You can make things slightly simpler with metaprogramming, something like (NOTE: Iā€™m totally making up the types here):

for T in (Cint, Cfloat, Cdouble)
    for itype, ctype in ((T, T), (Base.RefValue{T}, Ref{T}), (AbstractVector{T}, Ref{T}))
        @eval function _ioctl(fd::Integer, request::Integer, arg::$itype)
            ret = ccall(:ioctl, Cint, (Cint, Culong, $ctype), fd, request, arg)
            ret < 0 && throw(SystemError("Error in IOCTL call", Libc.errno()))
            return ret
        end
    end
end
1 Like

Understood! I will do this. Thanks @giordano !!

For those playing with the data acquisition on the Raspberry PI, MCC makes some interesting HATS. This is probably going in the opposite direction of BareMetalPI. They have a C/C++ library and a Python library. I have translated the mcc172 calls to Julia and put it in https://github.com/Spectrum-Tec/MccDaqHats.jl . This is not registered, I have never done that. With your encouragement and help I can try. The other mcc HATS need to be interfaced, and more examples can be added, I have only worked on two examples. It seems to have good potential though. I have put the HAT on an 8 G PI 4 with 32 bit Raspbian.

I have make pure Julia equivalents to the C functions. Then I found the best way to port the examples was from the Python code.

1 Like

I actually use the 1.7.0 RC3, and it works for me, but I havenā€™t tested anything specific here. You can find any release you like here: https://julialang-s3.julialang.org/bin/versions.json

Unfortunately I could not find v1.7.3 for armv7 :frowning:

No worries, here is the link: https://julialang-s3.julialang.org/bin/linux/armv7l/1.7/julia-1.7.0-rc3-linux-armv7l.tar.gz

Keep in mind, v1.7.3 doesnā€™t exist yet, this is 1.7.0 RC3, so your miles may vary.

Thanks! I will test.

I have a PI 4 with 8 GB memory and tested the BaremetalPi.jl on Julia 1.6.1 and the test ran successfully. I turned a pin on and off and tested the results with a voltmeter and that works. Thanks for the package!

1 Like

Hi @Jake

Nice to know :slight_smile: I will check if I can fix the problems with 1.7.0. Unfortunately, I could not run Julia v1.7.0 in the Pi Zero even with the pre-built package for some unknown reason. It just crashes saying Segmentation Fault or something like it.