# Help Me Drive an E-Ink Display on a Raspberry Pi Using Julia!

**URL:** https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972
**Category:** General Usage
**Tags:** display, raspberry-pi
**Created:** [December 18, 2024, 10:57pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972 "2024-12-18T22:57:50Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [December 18, 2024, 10:57pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/1 "2024-12-18T22:57:50Z")

</div>

## Hi folks! 👋

I am on a quest to use Julia to drive an E-Ink Display that I am running from a Raspberry Pi 4 (Raspbian OS 64-bit). Here is an image of what I would like to be seeing happen:

![eink_python](https://global.discourse-cdn.com/julialang/original/3X/6/6/66b1c6e7e99e10ed2cca48a9f95c00ee3ba1d69e.gif)

This is using a Python library – but let’s use Julia instead! This little post is me summarizing what I am trying to do, what I have accomplished so far, and obstacles I have encountered.

Please help! I would love to put this up on my wall:

> **[Waveshare 7.5＂ screen and Rasberry Pi case by Krampster | Download free STL...](https://www.printables.com/model/324984-waveshare-75-screen-and-rasberry-pi-case/collections)**

## Goal: A Julia-Driven E-Ink Display 🎯

My mission is simple: I want to drive an E-Ink display using Julia running on a Raspberry Pi 4! Specifically, here is the hardware and software I am using:

- Raspberry Pi 4B (64GB SD Card)
- [Waveshare 800×480, 7.5inch E-Ink display HAT for Raspberry Pi, SPI interface](https://www.waveshare.com/7.5inch-e-Paper-HAT.htm)
- Raspbian OS (64-bit)
- Julia 1.10.7 (via juliaup)
- [PiGPIO.jl 0.2.1](https://github.com/JuliaBerry/PiGPIO.jl/commit/17efccc412359dadb5d38b7a94b6308b7953c1d5)

I have wanted to do this for quite some time and now am on holidays – so, I have some time now to tackle this fun engineering problem! Specifically, what I would like to accomplish in terms of features is the following:

- Communicate and update GPIO pin states
- Use SPI to interface with the E-Ink display
- Transmit images to E-ink display
- Periodic refresh of E-ink display
- Create a dashboard to display info I want
  - Daily agenda
  - TODOs

In terms of priority, the first 4 tasks are the most critical. I have the first task done thanks to [PiGPIO.jl](https://github.com/JuliaBerry/PiGPIO.jl)’s support for GPIO which is pretty rad!

## The Story So Far! 📚

So, Waveshare actually has a great foundation for interacting with the E-ink display via Python. The files that are most pertinent are:

- [epdconfig.py](https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epdconfig.py)
- [epd\_7in5\_V2\_test.py](https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/python/examples/epd_7in5_V2_test.py)

In fact, I have begun translating some of the code from epdconfig.py and epd\_7in5\_V2\_test.py into a new Julia package that I am calling [Jinkies.jl](https://github.com/TheCedarPrince/Jinkies.jl). Jinkies.jl is built upon PiGPIO.jl and is building properly on the Pi.

> Jinkies! Julia: E-Ink made Easy!

## Obstacles and Questions ⚠

Some of the most outstanding obstacles and questions I have run into are as follows:

**Obstacle 1:** I have no idea how to get SPI communication working from within Julia. PiGPIO.jl’s support for SPI is completely broken; here is an example:

```julia-repl
julia> using PiGPIO

julia> p = Pi()
[ Info: Successfully connected!
Pi("localhost", 8888, true, PiGPIO.SockLock(Sockets.TCPSocket(RawFD(17) open, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))), PiGPIO.CallbackThread(PiGPIO.SockLock(Sockets.TCPSocket(RawFD(17) open, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))), PiGPIO.SockLock(Sockets.TCPSocket(RawFD(18) paused, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))), true, true, 0, 0x00000000, Any[]))

julia> h = PiGPIO.spi_open(p, 1, 100000, Int32(0))
ERROR: MethodError: no method matching write(::IOBuffer, ::Int32)
You may have intended to import Base.write

Closest candidates are:
  write(::Pi, ::Any, ::Any)
   @ PiGPIO ~/.julia/packages/PiGPIO/U0QFH/src/pi.jl:458

Stacktrace:
 [1] spi_open(self::Pi, spi_channel::Int64, baud::Int64, spi_flags::Int32)
   @ PiGPIO ~/.julia/packages/PiGPIO/U0QFH/src/spiSerial.jl:92
 [2] top-level scope
   @ REPL[5]:1

```

I went through a very onerous debugging process for several hours yesterday testing out various versions of Julia (including 1.0, 1.3, 1.5, 1.6, and 1.10) and concluded the code itself is actually flawed. Once I kinda patched `spi_open` for example, I would run into further problems within PiGPIO.jl. Here’s the code that was involved in the failures:

> **PiGPIO Problems with SPI**
>
> Erroneous code from within [spiSerial.jl](https://github.com/JuliaBerry/PiGPIO.jl/blob/master/src/spiSerial.jl):
> 
> ```julia
> function spi_open(self::Pi, spi_channel, baud, spi_flags=0)
> # I p1 spi_channel
> # I p2 baud
> # I p3 4
> ## extension ##
> # I spi_flags
> extents=IOBuffer()
> write(extents, spi_flags::Cint)
> return _u2i(_pigpio_command_ext(
> self.sl, _PI_CMD_SPIO, spi_channel, baud, 4, extents))
> end
> 
> ```
> 
> ```julia
> function _u2i(x::UInt32)
> v = convert(Int32, x)
> if v < 0
> if exceptions
> error(error_text(v))
> end
> end
> return v
> end
> 
> ```
> 
> Erroneous code from within [pi.jl](https://github.com/JuliaBerry/PiGPIO.jl/blob/master/src/pi.jl):
> 
> ```julia
> """
> Runs an extended pigpio socket command.
> 
> * `sl`: command socket and lock.
> * `cmd`: the command to be executed.
> * `p1`: command parameter 1 (if applicable).
> * `p2`: command parameter 2 (if applicable).
> * `p3`: total size in bytes of following extents
> * `extents`: additional data blocks
> """
> function _pigpio_command_ext(sl, cmd, p1, p2, p3, extents, rl=true)
> ext = IOBuffer()
> Base.write(ext, Array(reinterpret(UInt8, [cmd, p1, p2, p3])))
> for x in extents
> write(ext, string(x))
> end
> lock(sl.l)
> write(sl.s, ext)
> msg = reinterpret(Cuint, sl.s)[4]
> if rl
> unlock(sl.l)
> end
> return res
> end
> 
> ```

So, this led me to looking at @Ronis_BR 's project BaremetalPi.jl which seems to handle SPI interfaces but I have no idea how to really use it and it doesn’t seem to work on my Pi:

```julia-repl
julia> using BaremetalPi

julia> init_spi("/dev/spidev0.0", max_speed_hz = 1000)

julia> tx_buf = [0x01, 0x80, 0x00]
3-element Vector{UInt8}:
 0x01
 0x80
 0x00

julia> rx_buf = zeros(UInt8, 3)
3-element Vector{UInt8}:
 0x00
 0x00
 0x00

julia> ret = spi_transfer!(1, tx_buf, rx_buf)
3

julia> rx_buf
3-element Vector{UInt8}:
 0x00
 0x00
 0x00

```

I also took a look at @notinaboat 's PiGPIOC.jl which provides a Clang.jl-based wrapper for pigpio. Again however, I was a bit lost as to how to make this work.

**Obstacle 2:** I am struggling with translate some of the Python code to Julia. Overall, I would like to be able to translate the following Python code:

```python
    # Translated to Julia
    def digital_write(self, pin, value):
        if pin == self.RST_PIN:
            if value:
                self.GPIO_RST_PIN.on()
            else:
                self.GPIO_RST_PIN.off()
        elif pin == self.DC_PIN:
            if value:
                self.GPIO_DC_PIN.on()
            else:
                self.GPIO_DC_PIN.off()
        elif pin == self.PWR_PIN:
            if value:
                self.GPIO_PWR_PIN.on()
            else:
                self.GPIO_PWR_PIN.off()

    # Translated to Julia
    def reset(self):
        epdconfig.digital_write(self.reset_pin, 1)
        epdconfig.delay_ms(20) 
        epdconfig.digital_write(self.reset_pin, 0)
        epdconfig.delay_ms(2)
        epdconfig.digital_write(self.reset_pin, 1)
        epdconfig.delay_ms(20)   

    # Unsure how to translate this
    def spi_writebyte(self, data):
        self.SPI.writebytes(data)

    # Unsure how to translate this
    def send_command(self, command):
        epdconfig.digital_write(self.dc_pin, 0)
        epdconfig.digital_write(self.cs_pin, 0)
        epdconfig.spi_writebyte([command])
        epdconfig.digital_write(self.cs_pin, 1)

    def send_data(self, data):
        epdconfig.digital_write(self.dc_pin, 1)
        epdconfig.digital_write(self.cs_pin, 0)
        epdconfig.spi_writebyte([data])
        epdconfig.digital_write(self.cs_pin, 1)

```

Into Julia. Here are methods I have tried so far:

```julia
function digital_write(p, pin, value)
    if pin == RST_PIN
        if value
            PiGPIO.write(p, RST_PIN, PiGPIO.ON)
        else
            PiGPIO.write(p, RST_PIN, PiGPIO.OFF)
        end
    elseif pin == DC_PIN
        if value
            PiGPIO.write(p, DC_PIN, PiGPIO.ON)
        else
            PiGPIO.write(p, DC_PIN, PiGPIO.OFF)
        end
    elseif pin == PWR_PIN
        if value
            PiGPIO.write(p, PWR_PIN, PiGPIO.ON)
        else
            PiGPIO.write(p, PWR_PIN, PiGPIO.OFF)
        end
    end
end

function reset(p::Pi)
    digital_write(p, reset_pin, true)
    delay_ms(20)
    digital_write(p, reset_pin, false)
    delay_ms(2)
    digital_write(p, reset_pin, true)
    delay_ms(20)
end

# Unsure how to translate this
function send_command(p, command):
    digital_write(p, DC_PIN, false)
    digital_write(p, CS_PIN, false)
    # Unsure how to translate this
    epdconfig.spi_writebyte([command])
    digital_write(p, CS_PIN, true)
end

```

The SPI interfacing is the most confusing to me at the moment. However, if I can get `spi_writebyte`, `send_command`, and `send_data` translated properly, then I can keep proceeding as I could do something like this:

```python
self.reset()    
self.send_command(0x06)
self.send_data(0x17)

```

to properly initialize my display.

## Final Thoughts 💭

So far, we are making progress, but I am stuck now! Would anyone be able to give some thoughts and guidance on how to best proceed? I was thinking if need be, maybe I should using PiGPIO.jl GPIO communication and BaremetalPi.jl or PiGPIOC.jl for SPI communication?

What do people think? I am going to CC a few folks whose opinions I would love to hear: @notinaboat @Ronis_BR @Sukera @Alexander-Barth @avik @yakir12 @asinghvi17

Cheers and happy holidays 🎄

~ tcp 🌳

---

<div class="post-metadata">

### Author: ![g-gundam](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/g-gundam/32/47593_2.png) [@g-gundam](https://discourse.julialang.org/u/g-gundam)
#### Post date: [December 18, 2024, 11:36pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/2 "2024-12-18T23:36:08Z")

</div>

It looks like a fun project. Good luck figuring out those technical puzzles. 🍀

---

<div class="post-metadata">

### Author: ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)
#### Post date: [December 19, 2024, 7:44am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/3 "2024-12-19T07:44:15Z")

</div>

Do you HAVE to use SPI? What will happen if you used another (perhaps slower) protocol?

---

<div class="post-metadata">

### Author: ![Alexander-Barth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander-barth/32/3692_2.png) [@Alexander-Barth](https://discourse.julialang.org/u/Alexander-Barth)
#### Post date: [December 19, 2024, 10:57am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/4 "2024-12-19T10:57:17Z")

</div>

Thank you for sharing the information about this nice project.

Unfortunately, there is a lot of untested code in PiGPIO.jl.  
Maybe does this commit already help you to go further?

> <https://github.com/JuliaBerry/PiGPIO.jl/commit/b48be396080e3e9fbbfb670235e35d281c25a52c>

Do not hesitate the fill an issue [here](https://github.com/JuliaBerry/PiGPIO.jl/issues/new).  
It is possible that you find other problems along the way.

If there are any ideas how we can improve the testing of such packages on GitHub Action, that would be helpful too.

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [December 19, 2024, 5:14pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/5 "2024-12-19T17:14:52Z")

</div>

Hey @Alexander-Barth!

> [@Alexander-Barth](#):
>
> Unfortunately, there is a lot of untested code in [PiGPIO.jl](https://juliahub.com/ui/Packages/General/PiGPIO).

Ah, hm. This is good to know!

> [@Alexander-Barth](#):
>
> Maybe does this commit already help you to go further?
> 
> [extent write · JuliaBerry/PiGPIO.jl@b48be39 · GitHub](https://github.com/JuliaBerry/PiGPIO.jl/commit/b48be396080e3e9fbbfb670235e35d281c25a52c)

It does! Now I get a different error – I can open up an issue and we can discuss more there.

**EDIT: Issue has been opened here discussing this current problem I run into:**

> <https://github.com/JuliaBerry/PiGPIO.jl/issues/18>
>
> Hi @Alexander-Barth and co,
> 
> Carrying on from a discussion from \[Discourse\](ht…tps://discourse.julialang.org/t/help-me-drive-an-e-ink-display-using-julia/123972), the merge b48be396080e3e9fbbfb670235e35d281c25a52c did help me get a bit further with opening an SPI interface, but I am still receiving an error:
> 
> \`\`\`julia-repl
> julia\> using PiGPIO
> 
> julia\> p = Pi()
> \[ Info: Successfully connected!
> Pi("localhost", 8888, true, PiGPIO.SockLock(Sockets.TCPSocket(RawFD(17) open, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (2, 547614619512, 547614189232))), PiGPIO.CallbackThread(PiGPIO.SockLock(Sockets.TCPSocket(RawFD(17) open, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (2, 547614619512, 547614189232))), PiGPIO.SockLock(Sockets.TCPSocket(RawFD(18) paused, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 547589698944, 0))), true, true, 0, 0x00000000, Any\[\]))
> 
> julia\> PiGPIO.spi\_^C
> 
> julia\> h = PiGPIO.spi\_open(p, 1, 100000, Int32(0))
> ERROR: MethodError: no method matching iterate(::IOBuffer)
> 
> Closest candidates are:
> iterate(::Pipe, ::Int64)
> @ Base stream.jl:1263
> iterate(::Pipe)
> @ Base stream.jl:1262
> iterate(::Base.MethodSpecializations)
> @ Base reflection.jl:1148
> ...
> 
> Stacktrace:
> \[1\] \_pigpio\_command\_ext(sl::PiGPIO.SockLock, cmd::Int64, p1::Int64, p2::Int64, p3::Int64, extents::IOBuffer, rl::Bool)
> @ PiGPIO ~/.julia/packages/PiGPIO/1FebP/src/pi.jl:128
> \[2\] \_pigpio\_command\_ext(sl::PiGPIO.SockLock, cmd::Int64, p1::Int64, p2::Int64, p3::Int64, extents::IOBuffer)
> @ PiGPIO ~/.julia/packages/PiGPIO/1FebP/src/pi.jl:126
> \[3\] spi\_open(self::Pi, spi\_channel::Int64, baud::Int64, spi\_flags::Int32)
> @ PiGPIO ~/.julia/packages/PiGPIO/1FebP/src/spiSerial.jl:93
> \[4\] top-level scope
> @ REPL\[5\]:1
> \`\`\`
> 
> After debugging, I can observe that the error occurs within this PiGPIO.jl function:
> 
> https://github.com/JuliaBerry/PiGPIO.jl/blob/e34746d9c0f0bcd2a7e09d3e48f192c0342ea9d4/src/pi.jl#L115-L138
> 
> In particular, I know it is this loop:
> 
> \`\`\`julia
> for x in extents 
> write(ext, string(x)) 
> end 
> \`\`\`
> 
> As it is trying to iterate over a \`IOBuffer\` -- but I am unsure what it is trying to actually do or access. I tested old Julia versions starting from 1.0 and I have concluded this is just buggy code. There was not a point in time post-1.0 Julia that I could find which allows iteration over an IOBuffer. 
> 
> Any thoughts here? Thanks!
> 
> ~ tcp :deciduous\_tree:

> [@Alexander-Barth](#):
>
> If there are any ideas how we can improve the testing of such packages on GitHub Action, that would be helpful too.

Honestly, if you would be willing to help me with my little hobby project, I’d be happy to also use my little Pi as a testing rig to test various Julia Raspberry Pi packages across versions. That’s the first step I see at the moment. Would that be helpful?

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [December 19, 2024, 5:24pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/6 "2024-12-19T17:24:09Z")

</div>

Just going to respond to some other comments/thoughts too in this message:

> [@yakir12](#):
>
> Do you HAVE to use SPI? What will happen if you used another (perhaps slower) protocol?

I don’t know – all the references I see for the E-Ink display use SPI instead of say serial. I just wouldn’t know what to do with serial whereas I have at least a bit of an idea from examples using SPI.

> **[Valentin Churavy (@vchuravy.dev)](https://bsky.app/profile/vchuravy.dev/post/3ldnnul2o5226)**
>
> I would start with fixing BaremetalPI that seems to be the best long-term idea, but you likely will have to delve into technical manuals or library code to adapt it to your PI.

Per @vchuravy on BlueSky, this could be a nice suggestion! Here’s the current failing example:

```julia-repl
julia> using BaremetalPi

julia> init_spi("/dev/spidev0.0", max_speed_hz = 1000)

julia> tx_buf = [0x01, 0x80, 0x00]
3-element Vector{UInt8}:
 0x01
 0x80
 0x00

julia> rx_buf = zeros(UInt8, 3)
3-element Vector{UInt8}:
 0x00
 0x00
 0x00

julia> ret = spi_transfer!(1, tx_buf, rx_buf)
3

julia> rx_buf
3-element Vector{UInt8}:
 0x00
 0x00
 0x00

```

It doesn’t appear to be receiving anything is the conundrum despite sending a transmission. @Ronis_BR , do you have any suggestions on this front?

> [@g-gundam](#):
>
> It looks like a fun project. Good luck figuring out those technical puzzles. 🍀

Thanks @g-gundam! We will see how far we can get! 😄

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [December 19, 2024, 5:49pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/7 "2024-12-19T17:49:58Z")

</div>

I don’t know OTOH which chip your Raspberry Pi uses, but if you want to go the route of directly toggling registers instead of going through the virtual device exposed on `/dev/spidev0.0`, I highly recommend using something like [MCUCommon.jl](https://github.com/Seelengrab/MCUCommon.jl) to define various operations on memory mapped registers. You’ll need the memory addresses the registers are mapped to for this to work - you can probably find them in the datasheet. I haven’t tried running this yet from a ARM-native julia running on a Pi, but I don’t inherently see why this shouldn’t work. Check the docstring of `@regdef` for the main usage. If you have trouble with it, let me know!

This does mean building up an abstraction for the SPI (which isn’t something I’ve done in Julia yet either for my experiments - sorry!), but it might be a worthwhile learning experience (though this will probably also take some time to get right). [This peripheral description](https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf) will probably be a good/helpful reference.

> [@TheCedarPrince](#):
>
> It doesn’t appear to be receiving anything is the conundrum despite sending a transmission. @Ronis_BR , do you have any suggestions on this front?

How are you checking that something is actually being sent? You can accomplish that by hooking the SPI output up to a breadboard, and connecting that then to your E-Ink display as well as a logic analyzer. There’s a bunch of knock-offs of the 8-pin Salae floating around on the internet, that are compatible with their [good software](https://www.saleae.com/pages/downloads). Just search for “logic analyzer 8 channel” on Amazon and you’ll find them for ~10$.

---

<div class="post-metadata">

### Author: ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)
#### Post date: [December 23, 2024, 12:59am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/8 "2024-12-23T00:59:04Z")

</div>

Hi @TheCedarPrince !

I have been using BaremetalPi.jl to communicate to an inertial sensor (gyro) using the SPI interface. However, I have only the Raspberry Pi Zero 2W.

You can see the docs here:

[https://ronisbr.github.io/BaremetalPi.jl/stable/man/spi/](https://ronisbr.github.io/BaremetalPi.jl/stable/man/spi/)

I remember struggling with SPI interface because of the different configurations you need to perform.

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [December 24, 2024, 8:34pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/9 "2024-12-24T20:34:20Z")

</div>

Hi folks, back with a little update! After some tinkering and help from @Alexander-Barth, PiGPIO.jl is now able to open SPI interfaces properly:

```julia-repl
julia> using PiGPIO

julia> p = Pi()
[ Info: Successfully connected!
Pi("localhost", 8888, true, PiGPIO.SockLock(Sockets.TCPSocket(RawFD(17) open, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))), PiGPIO.CallbackThread(PiGPIO.SockLock(Sockets.TCPSocket(RawFD(17) open, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))), PiGPIO.SockLock(Sockets.TCPSocket(RawFD(18) paused, 0 bytes waiting), ReentrantLock(nothing, 0x00000000, 0x00, Base.GenericCondition{Base.Threads.SpinLock}(Base.IntrusiveLinkedList{Task}(nothing, nothing), Base.Threads.SpinLock(0)), (0, 0, 0))), true, true, 0, 0x00000000, Any[]))

julia> h = PiGPIO.spi_open(p, 0, 100000, 0)
0

```

We are currently in the process of getting writing and reading to work but we have a nice path forward with comparing to the state of the art using the [Python `pigpio` package](https://abyz.me.uk/rpi/pigpio/python.html). It almost seems to work but reading bytes seems to have some flaws as discussed here:

> <https://github.com/JuliaBerry/PiGPIO.jl/issues/19>
>
> Hi @Alexander-Barth and co,
> 
> Just wanted to report that while #18 did work to …open a SPI interface, it now appears I am unable to write with SPI. Here's code in Python (using your tip about checking out the Python library):
> 
> \`\`\`python
> import pigpio
> 
> pi = pigpio.pi()
> 
> SPI\_CHANNEL = 0 # SPI channel (0 or 1)
> SPI\_SPEED = 1000000 # SPI speed in Hz (1 MHz)
> SPI\_FLAGS = 0 # SPI flags (0 for default)
> 
> handle = pi.spi\_open(SPI\_CHANNEL, SPI\_SPEED, SPI\_FLAGS)
> data\_to\_send = \[0x01, 0x02, 0x03, 0x04\] # Example data
> count, data\_received = pi.spi\_xfer(handle, data\_to\_send)
> \`\`\`
> 
> And the output
> 
> \`\`\`python
> \>\>\> handle
> 0 # Or 1 -- this is correct to the Julia version
> 
> \>\>\> count
> 4
> 
> \>\>\> data\_received
> bytearray(b'\\x00\\x00\\x00\\x00')
> \`\`\`
> 
> Here's the same version in Julia:
> 
> \`\`\`julia-repl
> julia\> using PiGPIO
> 
> julia\> p = Pi();
> \[ Info: Successfully connected!
> 
> julia\> h = PiGPIO.spi\_open(p, 1, 100000, 0)
> 0
> 
> julia\> data\_to\_send = \[0x01, 0x02, 0x03, 0x04\]
> 4-element Vector{UInt8}:
> 0x01
> 0x02
> 0x03
> 0x04
> 
> 
> julia\> count, data = PiGPIO.spi\_xfer(p, h, data\_to\_send)
> ERROR: UndefVarError: \`u2i\` not defined
> Stacktrace:
> \[1\] spi\_xfer(self::Pi, handle::Int32, data::Vector{UInt8})
> @ PiGPIO ~/.julia/packages/PiGPIO/UOeJH/src/spiSerial.jl:199
> \[2\] top-level scope
> @ REPL\[5\]:1
> \`\`\`
> 
> I then tried to patch it with the following fix: 
> 
> \`\`\`julia-repl
> julia\> function spi\_xfer(self::Pi, handle, data)
> # I p1 handle
> # I p2 0
> # I p3 len
> ## extension ##
> # s len data bytes
> 
> # Don't raise exception. Must release lock.
> bytes = signed(PiGPIO.\_pigpio\_command\_ext(
> self.sl, PiGPIO.\_PI\_CMD\_SPIX, handle, 0, length(data), data, false))
> if bytes \> 0
> data = PiGPIO.rxbuf(bytes)
> else
> data = ""
> end
> unlock(self.sl.l)
> return bytes, data
> end
> spi\_xfer (generic function with 1 method)
> 
> julia\> (count, data) = spi\_xfer(p, h, data\_to\_send)
> (0, "")
> \`\`\`
> 
> But that doesn't return what the Python version did. Any thoughts about how to fix this @Alexander-Barth or @aviks? I can examine the Python version a bit more, but was curious what your thought is here first. 
> 
> Cheers!
> 
> ~ tcp :deciduous\_tree:

---

<div class="post-metadata">

### Author: ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)
#### Post date: [December 24, 2024, 10:13pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/10 "2024-12-24T22:13:38Z")

</div>

Changing the title to include Raspberry Pi and SPI would make this post more searchable.

Going by memory, when I selected a package for pin toggling (not SPI), on the RPI one of the driving factors was to not have to use sudo for anything. I found BareMetalPI.jl fit the bill nicely (Thanks @Ronis_BR). I currently use the PI 4 and will start using the PI 5.

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [January 16, 2025, 5:21pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/11 "2025-01-16T17:21:21Z")

</div>

Latest update: after tinkering and help from @Alexander-Barth , we tentatively have reading and writing working over SPI!

> <https://github.com/JuliaBerry/PiGPIO.jl/pull/21>
>
> Per #19 and the suggestion from @Alexander-Barth, I was able to make this fix to… PiGPIO.jl! It works like it does in the python implementation now. I did have to do one more fix which was to explicitly pass in \`Base.read\`. Otherwise, this was what Alexander had suggested. :) 
> 
> Thanks!

Additionally, it seems like I have no blockers now with translating some of this Python code over to Julia code now using PiGPIO.jl. My first goal will be to translate the clear screen Python function for an e-ink display:

```python
def Clear(self):
    self.send_command(0x10)
    self.send_data2([0xFF] * int(self.width * self.height / 8))
    self.send_command(0x13)
    self.send_data2([0x00] * int(self.width * self.height / 8))

    self.send_command(0x12)
    epdconfig.delay_ms(100)
    self.ReadBusy()

```

I think there should be no more barriers now. I’ll report back what I am able to do sometime later. For now, back to some other research!

---

<div class="post-metadata">

### Author: ![stene](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stene/32/26044_2.png) [@stene](https://discourse.julialang.org/u/stene)
#### Post date: [May 13, 2025, 6:39pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/12 "2025-05-13T18:39:59Z")

</div>

I made a WaveShare e-paper driver as an example for the library [WiringPi](https://github.com/stensmo/WiringPi.jl/) . You can find the example here, [WaveShare e-paper in Julia](https://github.com/stensmo/WiringPi.jl/tree/main/examples) (you need to port the code for your exact display). See the code you need to port here: [https://github.com/waveshareteam/e-Paper/tree/master/RaspberryPi\_JetsonNano/c/lib/e-Paper](https://github.com/waveshareteam/e-Paper/tree/master/RaspberryPi_JetsonNano/c/lib/e-Paper)

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [June 30, 2025, 2:25am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/13 "2025-06-30T02:25:56Z")

</div>

**Update: It’s Alive! 🎉 🎉 🎉**

After a couple free hours over a few weekends, I was able to update and adapt the WiringPi.jl package you made here @stene to get things somewhat working for my configuration on a Raspberry Pi 4! Here’s an example demonstrating some of its capabilities so far:

Note that the video is sped up for compression! I’ll be updating [Jinkies.jl with my progress](https://github.com/TheCedarPrince/Jinkies.jl/tree/main) and hopefully tidying the code up a bit more to make better dashboards and printing messages to the display more easily.

Feel free to post questions and excited to keep hacking away!

Until the next update!

---

<div class="post-metadata">

### Author: ![stene](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stene/32/26044_2.png) [@stene](https://discourse.julialang.org/u/stene)
#### Post date: [July 3, 2025, 8:53am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/14 "2025-07-03T08:53:08Z")

</div>

Great that it is working. If you want to display images,(photos or similar) [GitHub - JuliaImages/DitherPunk.jl: Dithering algorithms in Julia.](https://github.com/JuliaImages/DitherPunk.jl) work well. I tested it with a 7 color screen.

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [July 3, 2025, 8:34pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/15 "2025-07-03T20:34:43Z")

</div>

Oh that looks excellent to use. Thanks Erik!

Also, one error that I haven’t quite figured out yet is how to draw an  
image with the full dimensions for my display (i.e. 800 x 480 pixels). I  
seem to have to draw images with an array of 48,000 representing 100 x  
480 pixels.

I couldn’t see in your code, but did you have to somehow “shrink” your  
images?

Cheers!

~ tcp

---

<div class="post-metadata">

### Author: ![stene](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stene/32/26044_2.png) [@stene](https://discourse.julialang.org/u/stene)
#### Post date: [July 3, 2025, 8:42pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/16 "2025-07-03T20:42:20Z")

</div>

There are two pixels per byte (maybe more for your display). So you need to pack them accordingly. See the examples where the SVG is drawn.

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [July 3, 2025, 11:26pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/17 "2025-07-03T23:26:01Z")

</div>

Interesting! Could you show me in Julia code how maybe this should look?  
I’ve actually examined that particular SVG generation script quite  
intensely but am not sure what part you are referring to.

Apologies for the confusion on my end. I am not following what you mean  
by “packing them accordingly”.

Thank you!

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [July 7, 2025, 12:36am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/18 "2025-07-07T00:36:30Z")

</div>

**Update: Images Working 🖼**

It took a weekend of tinkering off and on, but I finally have image displaying working using WiringPi.jl!

 ![image](https://global.discourse-cdn.com/julialang/original/3X/9/f/9f9037f4592c4c7b60aa5aef35624d4cf954e75e.jpeg)

I had to go back to the original library from WaveShare to figure out how to prepare images. As it would turn out, I had to reimplement some of the dithering abilities from the [PIL Python library](https://pillow.readthedocs.io/en/stable/) and do a bunch of byte-packing based on @stene 's suggestion. I got a little prototype working and now we shall do some cleaning and package preparation!

However, now that I have figured out how to drive an e-ink display with a Raspberry Pi using Julia, I suppose we can close this discussion for now!

I’ll make another discourse to share about further hardware experiments.

P.S. The display image here was made using Makie with the CairoMakie backend! 😃

---

<div class="post-metadata">

### Author: ![tecosaur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tecosaur/32/23206_2.png) [@tecosaur](https://discourse.julialang.org/u/tecosaur)
#### Post date: [July 7, 2025, 9:02am UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/19 "2025-07-07T09:02:04Z")

</div>

> [@TheCedarPrince](#):
>
> As it would turn out, I had to reimplement some of the dithering abilities from the [PIL Python library](https://pillow.readthedocs.io/en/stable/)

Just out of curiosity, why wasn’t DitherPunk.jl suitable?

---

<div class="post-metadata">

### Author: ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)
#### Post date: [July 7, 2025, 3:30pm UTC](https://discourse.julialang.org/t/help-me-drive-an-e-ink-display-on-a-raspberry-pi-using-julia/123972/20 "2025-07-07T15:30:18Z")

</div>

Good question!

To be more precise, what may have been better to say is that the dithering was fine, but the bytepacking that PIL does after dithering was what I had to account for. Here’s where I did bytepacking: [Jinkies.jl/src/Jinkies.jl at 598fb9c5650d864dc2c9f5990e4a5ae6c5fca0e8 · TheCedarPrince/Jinkies.jl · GitHub](https://github.com/TheCedarPrince/Jinkies.jl/blob/598fb9c5650d864dc2c9f5990e4a5ae6c5fca0e8/src/Jinkies.jl#L7)

Ideally, what I want to do next is either use DitherPunk.jl or some hand-rolled method to get 4-scale colors working as this particular display supports black (0x00), white (0xff), gray (0x80) and light gray (0xC0). Currently, I only binarize between black and white. Goal would be to make it support all 4 of those colors for better image rendering.

The hard part for me will be figuring out how to take DitherPunk output and then bytepack it for these four colors using the code I already have. As I was just binarizing originally between 2 colors, I am expecting this to get a bit more tricky…
