File golang-github-baierjan-sata-hat-0.4.0+git0.obscpio of Package golang-github-baierjan-sata-hat
07070100000000000081A4000000000000000000000001654CDF0E0000001E000000000000000000000000000000000000003600000000golang-github-baierjan-sata-hat-0.4.0+git0/.gitignore*.swp
fan-control
oled
!src/*
07070100000001000081A4000000000000000000000001654CDF0E0000042A000000000000000000000000000000000000003300000000golang-github-baierjan-sata-hat-0.4.0+git0/LICENSEMIT License
Copyright (c) 2023 Jan Baier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
07070100000002000081A4000000000000000000000001654CDF0E000014A0000000000000000000000000000000000000003500000000golang-github-baierjan-sata-hat-0.4.0+git0/README.md# Quad SATA HAT software
This repository contains software for basic control over Quad SATA HAT by [Radxa](https://wiki.radxa.com/Dual_Quad_SATA_HAT). The initial implementation was created as a [HackWeek project](https://hackweek.opensuse.org/23/projects/tumbleweed-support-for-raspberry-pi-4-with-quad-sata-hat).
## Goal
The goal of the project is to start with a RPi 4 and SATA HAT and end up with a working NAS solution on openSUSE Tumbleweed. The provided software is only targeting Debian based distros and even in that case the support is not great.
## The Hacking
### Step 1: OS installation
As I already have some experience with Raspbian, it is trivial to grab a new SD card, extract a fresh new image onto the card, put it in the Pi and just boot the system. After a while I am able to log in to the system via SSH. All good.
Now, it is Tumbleweed time. I headed to our [RPi4 documentation](https://en.opensuse.org/HCL:Raspberry_Pi4), the recommended step is to take the JeOS image and extract it as in previous step. I like it, but I need to tweak it a little bit. Ultimately, I do not want to have SD card present as I want to boot the system from connected SSD disk. So, lets grab the JeOS Tumbleweed image and extract it on a fresh new SSD disk connected via USB-SATA connector.
I removed the SD card, put the SSD disk into the SATA slot and I am trying the boot process. Unfortunately, I am unable to see any new device in the DHCP log. Even more unfortunately, the RPi 4 has microHDMI output and I probably do not have any such cable around (and frankly, I am to lazy to do a thorough search). So it is time for my USB-TTL UART converter, the _config.txt_ should contain `enable_uart=1` and it might also help to include `console=ttyS0,115200n8` into default grub command line parameters. The pinout for RPi can be easily found, in my case I need to connect pins 6, 8 and 10 (GND, TX, RX). For the actual connection, `minicom` or `screen` can be used. I am not happy with the result (I do not see anything).
After some blind laboring, I am able to find out, that the Pi is not able to boot from the SATA HAT because it does not see it. After consulting the documentation (i.e. searching on the official forum), I found out that the HAT needs to be "activated" by setting pins 25 and 26 to HIGH. OK, additional search and I know how to solve the issue. At first, I need to boot from SD card (the previously installed Raspbian will do), and update EEPROM configuration.
```
# rpi-eeprom-config --edit
[all]
BOOT_UART=1
WAKE_ON_GPIO=1
ENABLE_SELF_UPDATE=1
BOOT_ORDER=0xf14
[config.txt]
gpio=25,26=op,dh
```
There are several important settings, `BOOT_UART=1` will enable serial console during boot. `BOOT_ORDER=0xf14` means try USB (`4`) first, then SD card (`1`), then repeat (`f`) if necessary. In my case the USB boot is the SATA HAT. Another useful mode could be `2` for network boot. The most important part are the last two lines, `gpio=25,26=op,dh` will set pins 25 and 26 to output mode, driving high. That will enable the HAT after power-on, so it will be able to boot from it.
After this, I can successfully boot into the JeOS first boot configuration. As I have already prepared everything for serial console connection, I can do the setup even without monitor and keyboard attached. I can finish the installation, setup network and ssh server.
### Step 2: Fan control
There are two controllable fans, one on the board for CPU and one on top for the hard drives. Both are PWM capable and are controlled through pins 12 and 13. The original code uses Python and `RPi.GPIO`, however that did not work well for me. I also tried another Python libraries for GPIO pins, but was not able to solve performance issues nicely. For the PWM to work correctly, the controlling frequency needs to be high enough to prevent buzzing noises in an audible spectrum from the fans and neither library was able to provide it. After some extended search, I stumbled upon Go-written library `go-rpio` which was able to do hardware PWM (instead of slower software emulated) and which was able to achieve the optimal 25kHZ frequency.
I created a simple script which allows to set each fan to any of 5 available levels (0%, 25%, 50%, 75%, 100%) and an auto mode, which will read the current CPU temperature and adapt the fan levels. The thresholds are customizable via environment variables. Provided service file for the `fan-control` utility can read those overrides and start the auto mode upon boot.
### Step 3: OLED display
After some fiddling and reverse engineering, I was able to find out, that I need to reset the display before use; the reset procedure is done by flipping pin 22. The display itself is a standard I2C device and instead of deprecated Adafruit Python library, I used a go-based one. Currently, the display can show current time, CPU temperature and disk use percentage. The path for the disk usage can be customized by environment variable `OLED_DU_PATH`. The output is rotated by 180° by default, which can be disabled by setting `OLED_ROTATE=false`.
For the I2C to work correctly the appropriate overlay needs to be activated. Inside _config.txt_ or even better inside _extraconfig.txt_ make sure to include line `dtparam=i2c1=on`.
07070100000003000041ED000000000000000000000002654CDF0E00000000000000000000000000000000000000000000003000000000golang-github-baierjan-sata-hat-0.4.0+git0/dist07070100000004000081A4000000000000000000000001654CDF0E000000B3000000000000000000000000000000000000004400000000golang-github-baierjan-sata-hat-0.4.0+git0/dist/fan-control.service[Unit]
Description=SATA HAT Fan Controller
[Service]
Type=exec
EnvironmentFile=-/etc/default/go-sata-hat
ExecStart=/usr/bin/fan-control auto 0
[Install]
WantedBy=default.target
07070100000005000081A4000000000000000000000001654CDF0E0000082F000000000000000000000000000000000000005500000000golang-github-baierjan-sata-hat-0.4.0+git0/dist/golang-github-baierjan-sata-hat.spec#
# spec file for package go-sata-hat
#
# Copyright (c) 2023 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
Name: golang-github-baierjan-sata-hat
Version: 0.2.0
Release: 0
Summary: Quad SATA HAT Controller
License: MIT
URL: https://github.com/baierjan/go-sata-hat
Source: %{name}-%{version}.tar.xz
Source1: vendor.tar.gz
BuildRequires: golang-packaging
BuildRequires: systemd-rpm-macros
%{go_nostrip}
%description
A simple control software for Quad SATA HAT with fan and LCD diplay for Raspberry Pi 4.
%prep
%autosetup -p1 -a1
%build
go build \
-mod=vendor \
-buildmode=pie \
./src/fan-control
go build \
-mod=vendor \
-buildmode=pie \
./src/oled
%install
install -D -m0755 %{_builddir}/%{name}-%{version}/fan-control %{buildroot}%{_bindir}/fan-control
install -D -m0755 %{_builddir}/%{name}-%{version}/oled %{buildroot}%{_bindir}/sys-oled
install -D -m0644 dist/fan-control.service %{buildroot}%{_unitdir}/fan-control.service
install -D -m0644 dist/sys-oled.service %{buildroot}%{_unitdir}/sys-oled.service
%pre
%service_add_pre fan-control.service sys-oled.service
%post
%service_add_post fan-control.service sys-oled.service
%preun
%service_del_preun fan-control.service sys-oled.service
%postun
%service_del_postun fan-control.service sys-oled.service
%files
%license LICENSE
%doc README.md
%{_bindir}/fan-control
%{_bindir}/sys-oled
%{_unitdir}/fan-control.service
%{_unitdir}/sys-oled.service
%changelog
07070100000006000081A4000000000000000000000001654CDF0E000000D2000000000000000000000000000000000000004100000000golang-github-baierjan-sata-hat-0.4.0+git0/dist/sys-oled.service[Unit]
Description=SATA HAT System status OLED
[Service]
Type=exec
EnvironmentFile=-/etc/default/go-sata-hat
ExecStartPre=+/sbin/modprobe i2c-dev
ExecStart=/usr/bin/sys-oled
[Install]
WantedBy=default.target
07070100000007000081A4000000000000000000000001654CDF0E000000E5000000000000000000000000000000000000003200000000golang-github-baierjan-sata-hat-0.4.0+git0/go.modmodule github.com/baierjan/go-sata-hat
go 1.21.3
require (
github.com/stianeikeland/go-rpio/v4 v4.6.0
golang.org/x/image v0.1.0
periph.io/x/devices/v3 v3.7.1
periph.io/x/host/v3 v3.8.0
)
require periph.io/x/conn/v3 v3.7.0
07070100000008000081A4000000000000000000000001654CDF0E00000D70000000000000000000000000000000000000003200000000golang-github-baierjan-sata-hat-0.4.0+git0/go.sumgithub.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/stianeikeland/go-rpio/v4 v4.6.0 h1:eAJgtw3jTtvn/CqwbC82ntcS+dtzUTgo5qlZKe677EY=
github.com/stianeikeland/go-rpio/v4 v4.6.0/go.mod h1:A3GvHxC1Om5zaId+HqB3HKqx4K/AqeckxB7qRjxMK7o=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
periph.io/x/conn/v3 v3.7.0 h1:f1EXLn4pkf7AEWwkol2gilCNZ0ElY+bxS4WE2PQXfrA=
periph.io/x/conn/v3 v3.7.0/go.mod h1:ypY7UVxgDbP9PJGwFSVelRRagxyXYfttVh7hJZUHEhg=
periph.io/x/devices/v3 v3.7.1 h1:BsExlfYJlZUZoawzpMF7ksgC9f1eBAdqvKRCGvb+VYw=
periph.io/x/devices/v3 v3.7.1/go.mod h1:ezQOe8WknDaMbKZXVwQUQkIauyLyJshwAHkIohHXA94=
periph.io/x/host/v3 v3.8.0 h1:T5ojZ2wvnZHGPS4h95N2ZpcCyHnsvH3YRZ1UUUiv5CQ=
periph.io/x/host/v3 v3.8.0/go.mod h1:rzOLH+2g9bhc6pWZrkCrmytD4igwQ2vxFw6Wn6ZOlLY=
07070100000009000041ED000000000000000000000005654CDF0E00000000000000000000000000000000000000000000002F00000000golang-github-baierjan-sata-hat-0.4.0+git0/src0707010000000A000041ED000000000000000000000002654CDF0E00000000000000000000000000000000000000000000003600000000golang-github-baierjan-sata-hat-0.4.0+git0/src/common0707010000000B000081A4000000000000000000000001654CDF0E000004B5000000000000000000000000000000000000004000000000golang-github-baierjan-sata-hat-0.4.0+git0/src/common/common.gopackage common
import (
"log"
"os"
"strconv"
"syscall"
"time"
"fmt"
)
var (
CPU_FAN_PIN = 12
DISK_FAN_PIN = 13
BUTTON_PIN = 17
OLED_RESET_PIN = 23
TEMP = GetEnv("TEMP_SENSOR", "/sys/class/thermal/thermal_zone0/temp")
DU_PATH = GetEnv("OLED_DU_PATH", "/")
)
func Clamp (x, min, max uint32) uint32 {
if x > max {
return max
}
if x < min {
return min
}
return x
}
func GetEnv(key string, default_value string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return default_value
}
func ReadTemp() float64 {
dat, err:= os.ReadFile(TEMP)
if err != nil {
log.Fatal(err)
}
var temperature, _ = strconv.ParseFloat(string(dat[:3]), 64)
return temperature / 10.0
}
func DiskUsage() float64 {
var stat syscall.Statfs_t
syscall.Statfs(DU_PATH, &stat)
return 100.0 * float64(stat.Blocks - stat.Bavail) / float64(stat.Blocks)
}
func GetLines() []string {
lines := []string {
time.Now().Format("Time: 15:04:05"),
fmt.Sprintf("Temp: %.1f°C", ReadTemp()),
fmt.Sprintf("Disk: %02.0f%%", DiskUsage()),
}
return lines
}
0707010000000C000041ED000000000000000000000002654CDF0E00000000000000000000000000000000000000000000003B00000000golang-github-baierjan-sata-hat-0.4.0+git0/src/fan-control0707010000000D000081A4000000000000000000000001654CDF0E00000957000000000000000000000000000000000000004300000000golang-github-baierjan-sata-hat-0.4.0+git0/src/fan-control/main.gopackage main
import (
"github.com/baierjan/go-sata-hat/src/common"
"fmt"
"log"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/stianeikeland/go-rpio/v4"
)
var (
fans = [2] rpio.Pin {rpio.Pin(common.CPU_FAN_PIN), rpio.Pin(common.DISK_FAN_PIN)}
current_level uint32
MIN, _ = strconv.ParseFloat(common.GetEnv("TEMP_MIN", "35.0"), 64)
MED, _ = strconv.ParseFloat(common.GetEnv("TEMP_MED", "50.0"), 64)
MAX, _ = strconv.ParseFloat(common.GetEnv("TEMP_MAX", "55.0"), 64)
)
func setFan(fan int, level uint32) {
log.Print(fmt.Sprintf("Setting fan %d to level %d\n", fan, level))
fans[fan].Mode(rpio.Pwm)
fans[fan].Freq(100000)
fans[fan].DutyCycle(level, 4)
}
func setLevel() {
var level uint32 = 2
var temperature = common.ReadTemp()
if temperature >= MAX {
level = 4
}
if temperature <= MAX {
level = 3
}
if temperature <= MED {
level = 2
}
if temperature <= MIN {
level = 1
}
if current_level != level {
log.Print(fmt.Sprintf("Current temperature is %.0f°C\n", temperature))
setFan(0, level)
setFan(1, level)
current_level = level
}
}
func main() {
if len(os.Args) < 3 {
fmt.Fprintf(os.Stderr, "Usage: %s <auto | cpu | disk | all> <level>\n", os.Args[0])
os.Exit(1)
}
if err := rpio.Open(); err != nil {
log.Fatal(err)
}
defer rpio.Close()
// Install signal handler
signal_chan := make(chan os.Signal, 1)
signal.Notify(signal_chan, os.Interrupt, os.Kill, syscall.SIGTERM)
go func() {
for {
s := <-signal_chan
switch s {
case os.Interrupt, os.Kill, syscall.SIGTERM:
log.Print("Exiting...")
rpio.Close()
os.Exit(0)
}
}
}()
var input, err = strconv.ParseUint(os.Args[2], 10, 64)
if err != nil {
log.Fatal(err)
}
var level = common.Clamp(uint32(input), 0, 4)
switch os.Args[1] {
case "cpu":
setFan(0, level)
case "disk":
setFan(1, level)
case "all":
setFan(0, level)
setFan(1, level)
case "auto":
log.Print("Starting automatic fan control...")
for {
setLevel()
time.Sleep(time.Second)
}
}
}
0707010000000E000041ED000000000000000000000002654CDF0E00000000000000000000000000000000000000000000003400000000golang-github-baierjan-sata-hat-0.4.0+git0/src/oled0707010000000F000081A4000000000000000000000001654CDF0E00000C50000000000000000000000000000000000000003C00000000golang-github-baierjan-sata-hat-0.4.0+git0/src/oled/main.gopackage main
import (
"github.com/baierjan/go-sata-hat/src/common"
"image"
"log"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/stianeikeland/go-rpio/v4"
"golang.org/x/image/font"
"golang.org/x/image/font/inconsolata"
"golang.org/x/image/math/fixed"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/devices/v3/ssd1306"
"periph.io/x/devices/v3/ssd1306/image1bit"
"periph.io/x/host/v3"
)
var (
ROTATE, _ = strconv.ParseBool(common.GetEnv("OLED_ROTATE", "true"))
)
func resetOled() {
pin := rpio.Pin(common.OLED_RESET_PIN)
pin.Mode(rpio.Output)
pin.Low()
time.Sleep(200 * time.Millisecond)
pin.High()
}
func setupButton() rpio.Pin {
pin := rpio.Pin(common.BUTTON_PIN)
pin.Input()
pin.PullUp()
pin.Detect(rpio.FallEdge)
return pin
}
func draw(dev *ssd1306.Dev) {
f := inconsolata.Bold8x16
pin := setupButton()
show := true
// Draw on it.
for {
if pin.EdgeDetected() {
if show {
dev.Halt()
show = false
log.Print("Display turned off")
} else {
show = true
log.Print("Display turned on")
}
}
if show {
lines := common.GetLines()
img := image1bit.NewVerticalLSB(dev.Bounds())
drawer := font.Drawer{
Dst: img,
Src: &image.Uniform{image1bit.On},
Face: f,
Dot: fixed.P(0, f.Ascent),
}
y := f.Ascent
for _, line := range lines {
drawer.DrawString(line)
y += f.Ascent + f.Descent
drawer.Dot = fixed.P(0, y)
}
if err := dev.Draw(dev.Bounds(), img, image.Point{}); err != nil {
log.Fatal(err)
}
}
time.Sleep(time.Second)
}
}
func main() {
// Load rpio
if err := rpio.Open(); err != nil {
log.Fatal(err)
}
defer rpio.Close()
// Load all the drivers:
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
// Open a handle to the first available I²C bus:
bus, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
defer bus.Close()
// Reset the display before connecting
resetOled()
// Open a handle to a ssd1306 connected on the I²C bus:
opts := ssd1306.DefaultOpts
if ROTATE {
opts.Rotated = true
}
dev, err := ssd1306.NewI2C(bus, &opts)
if err != nil {
log.Fatal(err)
}
// Install signal handler
signal_chan := make(chan os.Signal, 1)
signal.Notify(signal_chan, os.Interrupt, os.Kill, syscall.SIGTERM)
go func() {
for {
s := <-signal_chan
switch s {
case os.Interrupt, os.Kill, syscall.SIGTERM:
log.Print("Exiting...")
dev.Halt()
bus.Close()
rpio.Close()
os.Exit(0)
}
}
}()
log.Print("Starting OLED...")
draw(dev)
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!44 blocks