Planet Linux of Taiwan

04月30日

Jollen's face
Jollen


縮小 展開

[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 2):P2P 通訊架構

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。 Decentralized 物聯網架構,需要 P2P 的通訊架構。 # 邁向 Decentralized 的關鍵 在 IoT 架構裡,實作 Peer-to-Peer (P2P) 網路有技術上的挑戰嗎?實作 Peer-to-Peer IoT Networking 的目標,是為了讓 IoT Devices 間能建立 P2P 架構的通訊方式,這就是技術上的挑戰了。讓 IoT Devices 能形成一個 P2P 網路,技術上似乎不太困難;不過,如果更深入技術細節來討論,就會發現許多學問。 第一、應用層的考量。IoT 裝置間必須以 Application Layer Protocols 來通訊,例如:HTTP。所以,我們需要能在 IoT 裝置上運行一個「Application Server」,也就是說,必須有一個「Programming...

04月29日

AppleBOY's Blog's face
AppleBOY's Blog


縮小 展開

用 Docker Multi-Stage 編譯出 Go 語言最小 Image

docker 之前應該沒寫過用 Docker 結合 Go 語言編譯出最小 Image 的文章,剛好趁這機會來介紹。其實網路上可以直接找到文章,像是這篇『Building Minimal Docker Containers for Go Applications』,那本文來介紹 Docker 新功能 multi-stage builds,此功能只有在 17.05.0-ce 才支援,看起來是 2017/05/03 號會 release 出來。我們拿 Go 語言的 Hello World 來介紹 Single build 及 Multiple build。

Single build

底下是 Go 語言 Hello World 範例:
package main

import "fmt"

func main() {
    fmt.Println("Hello World!")
}
接著用 alpine 的 Go 語言 Image 來編譯出執行檔。
FROM golang:alpine
WORKDIR /app
ADD . /app
RUN cd /app && go build -o app
ENTRYPOINT ./app
接著執行底下編譯指令:
$ docker build -t appleboy/go-app .
$ docker run --rm appleboy/go-app
最後檢查看看編譯出來的 Image 大小,使用 docker images | grep go-app,會發現 Image 大小為 258 MB

Multiple build

Multiple build 則是可以在 Dockerfile 使用多個不同的 Image 來源,請看看底下範例
# build stage
FROM golang:alpine AS build-env
ADD . /src
RUN cd /src && go build -o app

# final stage
FROM alpine
WORKDIR /app
COPY --from=build-env /src/app /app/
ENTRYPOINT ./app
從上面可以看到透過 AS--from 互相溝通,以前需要寫兩個 Dockerfile,現在只要一個就可以搞定。最後一樣執行編譯指令:
$ docker build -t appleboy/go-app .
$ docker run --rm appleboy/go-app
會發現最後大小為 6.35 MB,比上面是不是小了很多。

最小 Image?

6.35 MB 是最小的 Image 了嗎?才單單一個 Hello World 執行檔,用 Docker 包起來竟然要 6.35,其實不用這麼大,我們可以透過 Dokcer 所提供的最小 Image: scratch,將執行檔直接包進去即可,在編譯執行檔需加入特定參數才可以:
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app
再透過 Docker 包起來
FROM centurylink/ca-certs

ADD app /

ENTRYPOINT ["/app"]
編譯出來大小為: 1.81MB,相信這是最小的 Image 了。

結論

Multiple build 非常方便,這樣就可以將多個步驟全部合併在一個 Dockerfile 處理掉,像是底下例子
from debian as build-essential
arg APT_MIRROR
run apt-get update
run apt-get install -y make gcc
workdir /src

from build-essential as foo
copy src1 .
run make

from build-essential as bar
copy src2 .
run make

from alpine
copy --from=foo bin1 .
copy --from=bar bin2 .
cmd ...
用一個 Dockerfile 產生多個執行檔,最後再用 alpine 打包成 Image。

附上本篇程式碼範例

sakananote's face
sakananote


縮小 展開

20170429 openSUSE eBPF/BCC workshop 小記

openSUSE eBPF/BCC workshop 小記

Notes:
  • Kernel config 可以參考 /boot/ 下 config-開頭對應自己目前的核心, 看相關參數有沒有開
例如
# grep   BPF   /boot/config-4.4.46-11-default
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_NETFILTER_XT_MATCH_BPF=m
CONFIG_NET_CLS_BPF=m
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
CONFIG_HAVE_BPF_JIT=y
CONFIG_BPF_EVENTS=y
# CONFIG_TEST_BPF is not set

安裝相關套件
# zypper   in   python-bcc
# zypper  in  bcc-examples

相關 examples 存放地方
# pwd
/usr/share/bcc/examples

觀察 hello world
# cat   /usr/share/bcc/examples/hello_world.py  
#!/usr/bin/env python
# Copyright (c) PLUMgrid, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")

# run in project examples directory with:
# sudo ./hello_world.py"
# see trace_fields.py for a longer example

from bcc import BPF

BPF(text='void kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); }').trace_print()

安裝 ( 更新 ) 目前的 kernel-default
# zypper  in  kernel-default

重新開機
# reboot
執行相關測試

# cd   /usr/share/bcc/examples/

執行 hello_world.py
# python   /usr/share/bcc/examples/hello_world.py

接下來可以嘗試在另外一個視窗 ssh 機器

就可以觀察到相關輸出

template:/usr/share/bcc/examples # python  /usr/share/bcc/examples/hello_world.py
           sshd-2314  [001] d..2   479.045754: : Hello, World!
           sshd-2864  [000] d..2   479.072225: : Hello, World!
           sshd-2864  [000] d..2   479.172032: : Hello, World!
           sshd-2864  [000] d..2   485.447949: : Hello, World!

另外一個測試

# cat  /usr/share/bcc/examples/tracing/trace_fields.py
#!/usr/bin/env python
# Copyright (c) PLUMgrid, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")

# This is an example of tracing an event and printing custom fields.
# run in project examples directory with:
# sudo ./trace_fields.py"

from bcc import BPF

prog = """
int hello(void *ctx) {
 bpf_trace_printk("Hello, World!\\n");
 return 0;
}
"""
b = BPF(text=prog)
b.attach_kprobe(event="sys_clone", fn_name="hello")
print "PID MESSAGE"
b.trace_print(fmt="{1} {5}")

進行測試
# python    /usr/share/bcc/examples/tracing/trace_fields.py
PID MESSAGE
2954 Hello, World!
2473 Hello, World!
3042 Hello, World!


其他的 examples 檔案可以在 https://github.com/iovisor/bcc/tree/master/examples 找到

# cd   /usr/share/bcc/examples/tracing/

# wget   https://raw.githubusercontent.com/iovisor/bcc/master/examples/tracing/sync_timing.py

# cat   sync_timing.py
#!/usr/bin/python
#
# sync_timing.py    Trace time between syncs.
#                   For Linux, uses BCC, eBPF. Embedded C.
#
# Written as a basic example of tracing time between events.
#
# Copyright 2016 Netflix, Inc.
# Licensed under the Apache License, Version 2.0 (the "License")

from __future__ import print_function
from bcc import BPF

# load BPF program
b = BPF(text="""
#include <uapi/linux/ptrace.h>
#include <linux/blkdev.h>

BPF_HASH(last);

void do_trace(struct pt_regs *ctx) {
   u64 ts, *tsp, delta, key = 0;

   // attempt to read stored timestamp
   tsp = last.lookup(&key);
   if (tsp != 0) {
       delta = bpf_ktime_get_ns() - *tsp;
       if (delta < 1000000000) {
           // output if time is less than 1 second
           bpf_trace_printk("%d\\n", delta / 1000000);
       }
       last.delete(&key);
   }

   // update stored timestamp
   ts = bpf_ktime_get_ns();
   last.update(&key, &ts);
}
""")

b.attach_kprobe(event="sys_sync", fn_name="do_trace")
print("Tracing for quick sync's... Ctrl-C to end")

# format output
start = 0
while 1:
   (task, pid, cpu, flags, ts, ms) = b.trace_fields()
   if start == 0:
       start = ts
   ts = ts - start
   print("At time %.2f s: multiple syncs detected, last %s ms ago" % (ts, ms))

執行 sync_timing.py ( 另外在新視窗執行 #sync;sync;sync )
# python   /usr/share/bcc/examples/tracing/sync_timing.py
Tracing for quick sync's... Ctrl-C to end
At time 0.00 s: multiple syncs detected, last 177 ms ago
At time 0.16 s: multiple syncs detected, last 161 ms ago
At time 15.44 s: multiple syncs detected, last 1 ms ago
At time 15.44 s: multiple syncs detected, last 1 ms ago
At time 15.44 s: multiple syncs detected, last 1 ms ago


執行另外一個測試
# python   /usr/share/bcc/examples/tracing/disksnoop.py
TIME(s)            T  BYTES    LAT(ms)
4578.806008000     R  8           0.06
4580.853965000     R  8           0.06
4581.622036000     W  6144        0.12


執行另外一個測試
個人覺得這個測試很好用, 可以統計較常用的 block 大小 :)

# python    /usr/share/bcc/examples/tracing/bitehist.py
Tracing... Hit Ctrl-C to end.
^C
    kbytes              : count     distribution
        0 -> 1          : 8        |****************************************|
        2 -> 3          : 1        |*****                                   |
        4 -> 7          : 0        |                                        |
        8 -> 15         : 0        |                                        |
       16 -> 31         : 1        |*****                                   |


# cat   /usr/share/bcc/examples/tracing/bitehist.py

#!/usr/bin/python
#
# bitehist.py Block I/O size histogram.
# For Linux, uses BCC, eBPF. See .c file.
#
# Written as a basic example of using a histogram to show a distribution.
#
# The default interval is 5 seconds. A Ctrl-C will print the partially
# gathered histogram then exit.
#
# Copyright (c) 2015 Brendan Gregg.
# Licensed under the Apache License, Version 2.0 (the "License")
#
# 15-Aug-2015 Brendan Gregg Created this.

from bcc import BPF
from time import sleep

# load BPF program
b = BPF(src_file = "bitehist.c")

# header
print("Tracing... Hit Ctrl-C to end.")

# trace until Ctrl-C
try:
sleep(99999999)
except KeyboardInterrupt:
print

# output
b["dist"].print_log2_hist("kbytes")


感謝今天 AL 分享

~ enjoy it

AppleBOY's Blog's face
AppleBOY's Blog


縮小 展開

用 Go 語言打造 DevOps Bot

18190989_10210525473186864_1567687746_n 在 4/27 參加 iThome 舉辦的第一屆 ChatBot Day,我分享了如何用 Go 語言 實作 DevOps Bot,可以透過 Facebook MessengerLine Messenger API 來主動通知開發者。此議程希望可以幫助想玩 Bot 但是又不知道如何入門的開發者。如果不懂程式語言,也可以直些下載 Binary 來玩玩看。

DevOps Bot 需要哪些功能

  • 支援 Command Line Flag 參數功能
  • 支援 Bot API WebHook 功能
  • 支援 Https for WebHook Tunnel
  • 支援自動更新 https 憑證功能 (Let’s Encrypt)
  • 支援監控 WebHook 服務功能
  • 支援多種訊息格式 (圖片, 影片, 表情符號 … 等)
  • 支援跨平台編譯執行檔
  • 支援透過 Docker 發送訊息
  • 支援高並發 (處理大量發送訊息)
有興趣可以直接看投影片說明: 大家可以直接下載 drone-linedrone-facebook 執行檔來玩玩。

04月28日

Jollen's face
Jollen


縮小 展開

[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 1):Decentralized 創造附加新價值

本文章採用 Markdown 語法撰寫,若無法完整閱讀全文,請點擊這裡。 當你的 IoT 資料,將資料送至中央化的 IoT Platform 時,原本該屬於你的資料所有權、使用權與儲存地,將會默默超出自已的可控制範圍。 # 從 IoT Architecture 看物聯網區塊鏈 IEEE 在 2017 年 1 月發佈一篇 Newsletter 分析 IoT Blockchain 的技術挑戰 [1],文中提到,從 IoT Architecture 的角度,可以看到 IoT Blockchain 的幾個主要技術挑戰。簡單來說,一個「Decentralized」的 IoT Architecture 將會有機會克服當中的一些技術挑戰。 將 Blockchain 技術應用在 IoT 架構中時,需要「Decentralized」的...

04月26日

COSCUP's face
COSCUP


縮小 展開

Submit your Community Room application for COSCUP 17 before 8th May!

Community Rooms are assigned to self-organising groups to work, share, and discuss issues around an open-source-related topic. The application should be submitted before 8th May.

小默-IMG_3595.jpg

  • You can decide the content and format in your Community Room.
  • You can decide the duration of each sections and the whole schedule in your Community Room.

Please check out the Rules and submit your application here.

Key dates:

  • 8th May Deadline for Community Room application
  • 10th May Accepted Community Room announced on blog.coscup.org
  • 12th May Community Room coordinators provide an URL of Call for Proposal
  • 23rd June Community Room coordinators provide the complete schedule
  • 1st July Full schedule published on COSCUP.org (TBC)
  • 5th-6th Aug COSCUP 2017

Apply for a Community Room now!.

04月25日

瘋狂帽客's Blog's face
瘋狂帽客's Blog


縮小 展開

Cockpit

I saw this tools recently, and I am definitely to try it. :)

Cockpit


COSCUP's face
COSCUP


縮小 展開

COSCUP 2017 社群議程申請,5/8 日截止

COSCUP 2017 的社群議程即日起開始接受申請,並將於 5 月 8 日截止申請,請有興趣在今年與我們共襄盛舉的社群把握機會!申請請按此

小默-IMG_3595.jpg

過去 COSCUP 也曾數次採用社群議程方式,包括 2008 以前、2013 年都是與社群協調,將某個時段交由該社群規劃。今年的方式又再大膽了許多,有以下特點:

  • 自訂議程時長:各社群可以自己決定每段議程的時間,無論 15 分鐘、30 分鐘、45 分鐘,甚至直接開設 3 個小時的工作坊都可以。
  • 自訂休息時間:各社群可以決定是否有休息時間,或是連番上陣。
  • 自行招募議程:COSCUP 官網將協助欲投稿的人前往各社群自行投稿,而各社群也可以自己決定怎麼決定議程表。

今年社群議程招募將走閃電團路線,幾個重要日期茲列如下:

  • 05/08 「社群議程」截止申請
  • 05/10 於 COSCUP Blog 公布通過的社群議程
  • 05/12 通過的社群議程提供徵稿資訊連結
  • 06/23 社群提供所配發時段內的完整議程表
  • 預定 07/01 COSCUP 2017 總議程表發布。
  • 08/05 ~ 08/06 COSCUP 2017!

其他規則及申請書見此,快跟社群同夥討論一下,即刻申請吧

04月23日

AppleBOY's Blog's face
AppleBOY's Blog


縮小 展開

在 DigitalOcean 新竹社群簡介 Gitea 開源專案

gitea 很高興受到 DigitalOcean 新竹社群邀請來介紹輕量級的 Git 服務: Gitea,在不久之前筆者已經寫過一篇 Gitea 介紹,這次到交通大學宣傳這套免費的開源專案,目的就是希望台灣有更多開發者或企業可以了解用 Go 語言也可以打造一套輕量級 Git 服務,並且導入台灣的新創團隊。這次分享是透過 DigitalOcean 最小機器 (512MB 記憶體,每個月五美金) 來 Demo 如何在 Ubuntu 16.04 快速架設 Gitea 及使用 Caddy 來自動申請 Let’s Encrypt 憑證,最後搭配 Jenkins 串自動化部署及測試等…。 2017-04-23-18-18-45

投影片

底下是這次 Gitea 介紹投影片,使用底下服務或開源專案 A painless self-hosted Git service: Gitea from Bo-Yi Wu 課程中上我也給大家看 Gitea 在 Ubuntu 底下耗了多少記憶體:大約在 40 ~ 60 MB 所以開個最小機器也是綽綽有餘。 Screen Shot 2017-04-22 at 11.17.41 PM 最後來個工商服務,如果大家想學習 Go 語言,可以報名我在 iThome 的課程: 『Go 語言一天就上手』,可以由此連結進行報名。

後記

這次聚會遇到好多大神,像是 DK 大大保哥BlueTHackmd 作者 Max,難得大家來到新竹聚聚,感覺真的很棒,最後感謝 Peter (CDNJS Maintainer) 的主辦及工作團隊,活動真的很棒。

coldnew's blog's face
coldnew's blog


縮小 展開

將 Orange Pi Zero 改造成 AirPlay 音頻播放器

最近突然又想自己架設個簡單的 AirPlay 播放器,好透過 wifi 遠端播放我的 iPhone 或是 MacBook 上面的音樂。

剛好手邊的單板電腦都已經有各自的用途,於是便來找一片新的板子,最後鎖定了 Orange Pi Zero 這片開發板 (我選的是記憶體為 256 MB 的版本)。

會選這片的原因,主要是包含運費後他最便宜(大約 NT 362 元),剩下要自己準備的則是: USB 電源、USB 手機充電線、8GB MicroSD 卡。

這邊就來紀錄一下我怎樣進行設定的。

下載 Image

首先連線到 http://www.orangepi.org/downloadresources/ ,取得你要用的 Image。由於我只是要用於文字介面,因此選擇 Debian Server

下載好 Image 並解壓縮後,可以直接透過 dd 命令將其燒錄到 SD 卡 (假設 SD 卡設備為: /dev/sdc )

coldnew@gentoo ~ $ sudo dd if=debian_server_For_OrangePizero_v0_9_2.img of=/dev/sdc bs=1m

連接 UART

將 SD 卡插上後,我會先透過 USB 轉 TTL 的轉接線來登入終端介面

Xunlong Orange Pi Zero 上面可以看到 Orange Pi Zero 的腳位是這樣定義的:

因此我們接線的時候,要把 USB 轉 TTL 線上面的 TX 接到 Orange Pi Zero 的 RX, TTL 線上面的 RX 接到 Orange Pi Zero 的 TX:

如果接上去後,開機沒任何訊息的話,可以試著把 TX 和 RX 線對調,看看是不是接錯了。

預設帳號密碼

Orange Pi Zero 預設的帳號密碼是這樣的:

  • 帳號: root
  • 密碼: orangepi

不要輸入錯誤進可以進去啦

Debian GNU/Linux 8 OrangePizero ttyS0OrangePizero login: rootPassword:

擴充檔案系統大小

進入到系統的第一步,當然就是擴充 SD 卡的空間了

root@OrangePizero:~# fs_resizeDisk /dev/mmcblk0: 7.4 GiB, 7969177600 bytes, 15564800 sectors/dev/mmcblk0p1       40960  172031  131072   64M  b W95 FAT32/dev/mmcblk0p2      172032 2113536 1941505  948M 83 LinuxMax block: 15563776Part end: 2113536Part start: 172032WARNING: Do you want to resize "/dev/mmcblk0p2" (y/N)?  yPARTITION RESIZED.*********************************************Rootfs Extended. Please REBOOT to take effect*********************************************

好了後,我們就可以來進行網路設定,你也可以先接上網路線,然後更新套件。

設定 wifi

我自己對於 wifi 的設定,一直都是習慣使用 NetworkManager 來進行的。因此在這邊先啟動 NetworkManager:

root@OrangePizero:~# systemctl start NetworkManagerroot@OrangePizero:~# systemctl enable NetworkManager

另外,由於 Orange Pi Zero 有兩個 wifi 設備,一個是 p2p0,另外一個是 wlan1

root@OrangePizero:~# nmcli devDEVICE   TYPE      STATE      CONNECTIONeth0     ethernet  connected  eth0p2p0     wifi      unmanaged  --gre0     gre       unmanaged  --lo       loopback  unmanaged  --wlan1    wifi      unmanaged  --ip6tnl0  unknown   unmanaged  --sit0     unknown   unmanaged  --tunl0    unknown   unmanaged  --

由於 NetworkManager 預設會先去抓 p2p0 (按照字母排列),因此我會設定成讓 NetworkManager 忽略 p2p0 這個設備,避免設定 wifi 變得麻煩。

編輯 /etc/NetworkManager/NetworkManager.conf ,加入以下資訊:

[main]plugins=ifupdown,keyfile[ifupdown]managed=true[keyfile]unmanaged-devices=interface-name:p2p0

好了後,重新啟動 NetworkManager

root@OrangePizero:~# systemctl restart NetworkManager

這樣我們就可以列出我們可以連線的無線網路:

root@OrangePizero:~# nmcli dev wifi list*  SSID        MODE   CHAN  RATE       SIGNAL  BARS  SECURITY   Edgar       Infra  10    54 Mbit/s  100     ▂▄▆█  WPA1 WPA2   AUSTIN&FAY  Infra  6     54 Mbit/s  45      ▂▄__  WPA1 WPA2   75226490    Infra  1     54 Mbit/s  35      ▂▄__  WPA2   2.4aaaaaaa  Infra  4     54 Mbit/s  34      ▂▄__  WPA1 WPA2   RT-N16      Infra  6     54 Mbit/s  29      ▂___  WPA2   a89565282   Infra  1     54 Mbit/s  25      ▂___  WPA1 WPA2   dlink       Infra  11    54 Mbit/s  20      ▂___  WPA1 WPA2   mat         Infra  3     54 Mbit/s  22      ▂___  WPA1

假設我們要連接的 ESSID 為 Edgar,則接下來使用以下命令進行連接

root@OrangePizero:~# nmtui-connect Edgar

你會看到出現一個輸入密碼的視窗,輸入進去後應該就可以正常連上網路了

如果你想要一個指令就完成這個動作,你可以使用下面的命令 (替換 <essid> 和 <password>)

root@OrangePizero:~# nmcli dev wifi connect <essid> password <password>

這樣我們就完成無線網路的連接。

設定 avahi-daemon

為了讓 Apple 設備可以透過 Zeroconf 協定找到同一網域下的設備,我們必須啟用 Avahi

這邊啟用 avahi 並將其加入開機啟動項目去。

root@OrangePizero:~# systemctl start avahi-daemonroot@OrangePizero:~# systemctl enable avahi-daemon

編譯並安裝 shairport-sync

首先,我們要安裝一些軟體來幫助我們編譯並安裝 Shairport。

root@OrangePizero:~# apt-get install build-essential git autoconf libtool \    libdaemon-dev libasound2-dev libpopt-dev libconfig-dev \    avahi-daemon libavahi-client-dev \    libssl-dev

該裝的軟體都裝完以後,我們就可以下載最新的 shairport-sync 程式碼並安裝到系統內。

git clone https://github.com/mikebrady/shairport-sync.gitcd shairport-sync && autoreconf -i -f

接下來就是依照 shairport-sync 的 README 進行設置,我是設定了這些參數

./configure \    --with-alsa --with-stdout --with-pipe --with-avahi \    --with-ssl=openssl --with-metadata --with-systemd

接下來將整個程式安裝到系統中

root@OrangePizero:~/shairport-sync# make install

因為在前面的參數我是設定為給 systemd 的使用者使用,因此需要增加使用者與群組

root@OrangePizero:~# groupadd -r shairport-syncroot@OrangePizero:~# useradd -r -M -g shairport-sync -s /usr/bin/nologin -G audio shairport-sync

接著加入到 systemd 開機設定中,並啟用 shairport-sync 服務

root@OrangePizero:~# systemctl enable shairport-syncroot@OrangePizero:~# systemctl start shairport-sync

完成後,來設定輸出設備,這邊我採用的是 USB DAC。

設定 USB DAC

我自己播放音樂的時候,都是透過 USB DAC 來進行播放的 (這樣音質較好),因此要多進行一些步驟,讓 Orange Pi Zero 可以透過 USB 音效卡輸出。

載入需要的驅動

正常來講,我們只要載入了 snd_usb_audio 這個驅動,就可以讓 ALSA 識別 USB 音效卡。

不過……

root@OrangePizero:/lib/modules# modprobe snd_usb_audiomodprobe: ERROR: ../libkmod/libkmod.c:557 kmod_search_moddep() could not open moddep file '/lib/modules/3.4.39/modules.dep.bin'

好吧,有東西從缺,改用 insmod 的方式來載入。我們在 /etc/rc.local 加上這些東西

insmod /lib/modules/3.4.39_zero/snd-usbmidi-lib.koinsmod /lib/modules/3.4.39_zero/snd-hwdep.koinsmod /lib/modules/3.4.39_zero/snd-usb-audio.ko

這樣重開機後,你可以透過 lsmod 確認需要用到的驅動是否有真的被載入:

root@OrangePizero:~# lsmodModule                  Size  Used bysnd_usb_audio          79205  2snd_hwdep               5337  1 snd_usb_audiosnd_usbmidi_lib        17391  1 snd_usb_audioxradio_wlan           230792  0

如果上面列出的模組有缺的話,可以透過以下命令檢查是否 /etc/rc.local 有哪個地方出錯

root@OrangePizero:~# systemctl status rc-local● rc-local.service - /etc/rc.local Compatibility   Loaded: loaded (/lib/systemd/system/rc-local.service; static)   Active: active (exited) since Thu 1970-01-01 00:38:08 UTC; 47 years 3 months agoJan 01 00:38:07 OrangePizero systemd[1]: Starting /etc/rc.local Compatibility...Jan 01 00:38:08 OrangePizero systemd[1]: Started /etc/rc.local Compatibility.

確認該被載入的驅動有正常載入後,我們來指定 ALSA 預設得輸出設備。

指定 ALSA 輸出設備

那我們要怎樣讓 ALSA 知道我們指定的 USB DAC 的輸出位置呢?

首先先列出可以使用的音效卡資訊:

root@OrangePizero:~# cat /proc/asound/cards0 [audiocodec     ]: audiocodec - audiocodec                     audiocodec1 [sndhdmi        ]: sndhdmi - sndhdmi                     sndhdmi2 [Set            ]: USB-Audio - C-Media USB Headphone Set                     C-Media USB Headphone Set at usb-sunxi-ohci-1, full speed

找到編號是 2 後,我們就可以編輯 /etc/asound.conf 指定預設的 alsa 設備:

defaults.pcm.card 2defaults.ctl.card 2

這樣子播放的時候,ALSA 就會選用 USB-Audio - C-Media USB Headphone Set 這個設備進行播放。

在 macOS 下透過 AirPlay 播放

所有設定完成後,只要你的 macOS 和你的 AirPlay 設備在同一個網域,你就可以這樣找到他:

或是透過 iTunes 選擇設備:

試試看,播放聲音的時候,聲音是不是透過連接在 Orange Pi 上面的 USB DAC 送出來的 :)