2021/9/30

udhcpc and dnsmasq -- release ip on exit

主要是要測試 udhcpc 的 -R (release ip) 功能。

所以準備兩台機器
  • server : run dnsmasq
  • client : run udhcpc
server 起來之後,可以在 /var/lib/dnsmasq/dnsmasq.leases 看到配發的 ip.


所以 client 用:
udhcpc -iwlan0 -R
可以看到取得 ip,然後用 ip addr 看 wlan0 有 ip address。
在 server 上 cat /var/lib/dnsmasq/dnsmasq.leases 可以看到 client 的 wlan0 mac 和配發的 ip

用 ps 看,udhcpc 在背景。
這時候在 client 上 kill udhcpc
再用 ip addr 看,wlan0 已經沒有 ip 了。
然後 server 的 /var/lib/dnsmasq/dnsmasq.leases 中,client 的那一行也被清掉。


如果不加上 -R
udhcpc -iwlan0
一樣,client 取得 ip,udhcpc 在背景。
server 的 dnsmasq.leases 有 client 的 mac 跟 ip

client kill udhcpc,之後確認 udhcpc 沒有在背景。
用 ip addr 看,wlan0 的 ip 還在。
server 的 dnsmasq.leases 中,client 的 ip 也還在。

如果不用 -R,然後要 udhcpc 取得 ip 後就退出,不要在背景。用 -q。
測試取得 ip 後, udhcpc 的確不會退到背景。


其他

udhcpc 在取得ip 後,會 執行 /usr/share/udhcpc/default.script
這個script 會依照 udhcpc call 他時給的參數,做:
  • 設定 interface 的 ip
  • 設定 route
  • 設定 resvolv.conf
其中 resolv.conf 是 /tmp/resolv.conf
猜是因為有些統是 readonly root,所以這樣,讓 /etc/resolv.conf link 到 /tmp/resolv.conf

2021/9/23

build and run LVGL example in linux

本來像依照這一篇 的說明,在 framebuffer 下 run demo。
但是...
這一篇 說中。在 X windows 下測試 framebuffer 程是會失敗,因為會被 x windows update 調。
... 所以可以用 Ctrl-Alt-F1/2/... 切到console 來測試。
不然,就要用 SDL ..

這篇 也是一樣。

用SDL 得話:這一篇 可以正常動作。


framebuffer port:
git clone https://github.com/lvgl/lv_port_linux_frame_buffer.git
cd lv_port_linux_frame_buffer/
git submodule update --init --recursive
make

SDL 比較麻煩,上面文章說要拉新版 SDL 自己 build。
git clone https://github.com/libsdl-org/SDL.git
cd SDK
git checkout release-2.0.10
configure , make and install
./configure --host=x86-linux --prefix=/usr --disable-x11 --enable-debug -disable-voodoo -disable-mmx --disable-see --enable-sdl --enable-jpeg --enable-zlib --enable-png --disable-gif --enable-freetype --disable-video4linux --disable-video4linux2 --with-gfxdrivers=none --with-inputdrivers=linuxinput
make
sudo make install
default install path 是 /usr/lib/libSDL2.so 會自動安裝 pkgconfig

lvgl 的 SDL port 要用
git clone --recursive https://github.com/littlevgl/pc_simulator_sdl_eclipse.git
就一樣,make 後就可以 run 了,直接可以在 X 上 run ...


2024/02/07

現在有更方便的方法了。

這個: 直接config 好 Makefile 了。所以直接 make 就可以。
看Makefile 就知道最後 run ./build/bin/demo

這個 port 也寫好了 vs code 的 workspace。
所以在 clone 下來的 folder 用 'code .' 開啟,之後會問你要不要開啟 workspace,回 OK
之後就可以用 F5 (starting debugging) 來 build 和 run 了。


要 try v8 的 lvgl,所以.. 一樣checkout lv_sim_vscode_sdl 後,
到 lvgl 去 checkout v8.3.0
到 lv_driver 去 checkout v8.3.0
然後修改 Makefile,LV_DRIVER 由 X11 改 SDL2 (因為 X11 要最新版本的 lv_driver 才有支援)
另外,v8.3.0 的 test/makefile/test.c 中,有 main(),也要改成 _main,因為 demo 已經有 main() 了。

之後一樣,在主folder 開啟 vscode,open workspace,F5 - debug and run,OK

Hello X Window

從網路上 copy 的 hello X window
/*
 *  This is a sample X11 program using Xt and the Athena widget set that
 *  simply puts up a main window with the text "hello" in it.
 */

#include >X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Label.h>

int main(int argc, char* argv[])
{
    XtAppContext app_context;
    Widget toplevel, hello;

    toplevel = XtVaAppInitialize(
        &app_context,
        "XHello",
        NULL, 0,
        &argc, argv,
        NULL,
        NULL);

    hello = XtVaCreateManagedWidget("hello", labelWidgetClass, toplevel, NULL);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app_context);
}
build 的 command:
gcc helloxwindow.c -lXt -lXaw
可以試試看後面的 -l 缺的化,各是會出現什麼Error..
Xaw:
helloxwindow.c:(.text+0x5d): undefined reference to `labelWidgetClass

Xt
undefined reference to symbol 'XtRealizeWidget'

都沒有的話:
helloxwindow.c:(.text+0x4e): undefined reference to `XtVaAppInitialize'
helloxwindow.c:(.text+0x5d): undefined reference to `labelWidgetClass'
helloxwindow.c:(.text+0x7a): undefined reference to `XtVaCreateManagedWidget'
helloxwindow.c:(.text+0x8a): undefined reference to `XtRealizeWidget'
helloxwindow.c:(.text+0x96): undefined reference to `XtAppMainLoop'

2021/9/20

Stereo matching

筆記一下 stereo matching。

matching 就是找A影像中某一區域是在B影像的哪裡?
所以是找最相似的。

但是影像這麼大一塊區域要怎麼找?
因為epipoloar line 的定義,所以只要沿著epicpolar line找就可以。

因為投影的關係,所有點的epipoloar line都會交會在epipoloar hole那一點。
為了好做?跟減少變形差異,要先把投影拉平。
這個就是 image rectification。
經過image rectification 後,epipolar line 會變成平行的水平線。

Epipolar hole, line and plan

這一篇說明比較清楚,用在兩個相機的系統上,用來描述這兩個相機的影像的交互關係。
有一個觀點就是兩個相機的焦點的連線。
就好像解幾何題目畫的輔助線,這條連接兩個相機焦點的線,叫他 base line。

這條base line 和各(/兩)個相機成像平面的交/切點,就叫epipolar hole(有兩個,一個相機一個)。
空間上的任一點,和 base line 就可以形成(/定義)一個 epipolar plan。
epipolar plan 和 相機成像平面的切線(intersection),叫epipolar line (一個相機一條)。

空間任一點,其實代表就是相機拍攝的物件在空間的位置。

2021/9/15

WM8974 driver : 增加一個control : mic bias

發現沒有寫,所以現在補一下..

Datasheet 上..
Datasheet P 19:

* Register R44: Input Control
* Bit 8
* Label: MBVSEL
* Default: 0
* Description: Microphone Bias Voltage Control
    0: 0.9 x AVDD
    1: 0.75 x AVDD
所以在 wm8974.c 中加一個 amixer control:
diff --git a/linux-4.14/sound/soc/codecs/wm8974.c b/linux-4.14/sound/soc/codecs/wm8974.c
index 61fbd7c55..7a1c492d2 100644
--- a/linux-4.14/sound/soc/codecs/wm8974.c
+++ b/linux-4.14/sound/soc/codecs/wm8974.c
@@ -66,6 +66,7 @@ static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
 static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
 static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
 static const char *wm8974_alc[] = {"ALC", "Limiter" };
+static const char *wm8974_mbvsel[]= {"0.9", "0.75"};
 
 static const struct soc_enum wm8974_enum[] = {
        SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
@@ -85,6 +86,7 @@ static const struct soc_enum wm8974_enum[] = {
 
        SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
        SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
+       SOC_ENUM_SINGLE(WM8974_INPUT, 8, 2, wm8974_mbvsel),
 };
 
 static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
@@ -131,6 +133,7 @@ SOC_ENUM("Equaliser EQ4 Bandwidth", wm8974_enum[9]),
 SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
 SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
 
+
 SOC_ENUM("Equaliser EQ5 Bandwidth", wm8974_enum[11]),
 SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
 SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
@@ -151,6 +154,8 @@ SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
 SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
 
 SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
+SOC_ENUM("MIC Bias Voltage", wm8974_enum[14]),
+
 SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
 SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
 
這樣就多一個
numid=38,iface=MIXER,name='MIC Bias Voltage'
cget, set
# amixer cget numid=38
numid=38,iface=MIXER,name='MIC Bias Voltage'
  ; type=ENUMERATED,access=rw------,values=1,items=2
  ; Item #0 '0.9'
  ; Item #1 '0.75'
  : values=1
設完後,cat register-map 來看,reg 2C bit 8 有改變。

2021/9/13

bookmark : hacking CAR canbus

ref: 作者用 can-dsub, dsub-usb 然後用 usb-can bus driver 在 linux 上開啟一個 socket 來 dump CAN bus data。
然後 log,配合 影像做 reverse engineering。

最後還附上hack 完的 source code. (在 github : toyothack)。

另外的,當然是 openpilot 最多,他們有一個opendbc 描述已經decode 的車輛canbus 格式。

use repo to manage multiple projects

ref: 要用 repo/manifest 來管理現有 一堆 projects。
只要 create 一個 git project,裡面就是 manifest 的 xml 檔。
其中要有一個 default.xml, repo 會用這個做 主要的 manifest.xml
repo init -u mymanifest
mymanifest 就是放 default.xml 的 project folder


舉例來說:

假設myrepo 專案有 proj1, proj2 兩個 git source
.
├── proj1
└── proj2
想要用 repo/manifest 來管理。
就create 一個 manifests,裡面有 default.xml,也要用 git 管理:
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
	<remote name="origin" fetch="./" />
	<default revision="master" remote="origin" />
	<project path='proj1' name='proj1' />
	<project path='prij2' name='proj2' />
</manifest>
然後 myrepo 下有三個 git projects:
.
├── manifests
│   └── default.xml
├── proj1
└── proj2

這樣,在 local 端,就可以用 repo init 來 clone 整個 project
mkdir testrepo && cd testrepo
repo init -u ~/myrepo/manifests
downloading Repo source from https://gerrit.googlesource.com/git-repo
remote: Counting objects: 2, done
remote: Finding sources: 100% (12/12)
remote: Total 12 (delta 0), reused 12 (delta 0)
Unpacking objects: 100% (12/12), done.
Downloading manifest from /home/charles-chang/myrepo/manifests/
remote: Counting objects: 15, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 15 (delta 4), reused 0 (delta 0)

Your identity is: charles.chang <charles.chang@loyaltech.com>
If you want to change this, please re-run 'repo init' with --config-name

repo has been initialized in /home/charles-chang/testrepo
之後 repo sync 就會拉下所有的 project (不包含 manifest,manifest 會在 .repo/manifests)
然後一樣在 repo init 時加上 --mirror 就可以產生 git server 端的目錄結構 (方便放在 git server)

現在看來,要先用 python script : follow repo/manifest.xml 做出 repo mirror 的結構.,重新安排一次 project 目錄結構。
之後才能讓人做 repo init, sync..
-- 新版的 repo 不從 manifest.xml parsing 了, manifest.xml 也不再是 link,而是一個 include name,include default.xml (或其他)。



repo 管理後,如果要create 一個版本, fix 住目前的 project revision,可以用 repo manifest, 如何導出並使用清單文件? 的方法。用 repo manifest,列出有家 option 和沒加 option 的結果:
一般:
charles-chang@rdsuper:~/testmirror$ repo manifest
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote fetch="./" name="origin"/>
  
  <default remote="origin" revision="master"/>
  
  <project name="pp/project1" path="project1"/>
  <project name="project2" path="priject2"/>
</manifest>
加上 supress-upstream-revision
charles-chang@rdsuper:~/testmirror$ repo manifest -r --suppress-upstream-revision
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote fetch="./" name="origin"/>
  
  <default remote="origin" revision="master"/>
  
  <project dest-branch="master" name="pp/project1" path="project1" revision="7c4bcfb45cd9aa2266c0acf3f850a9ea50ae5ef9"/>
  <project dest-branch="master" name="project2" path="priject2" revision="907de68553f9b6d638aceb967c7a9f55bc8fcb03"/>
</manifest>
舊可可以把這個列出的結果存成新的 myversion.xml,加到 manifests.git 中。

如果 project 在另一個 project 中...可以用 .gitignore..
例如:
.
├── priject2
│   └── pp2
└── project1
    ├── p3
    │   └── myfile
    └── pp1
p3 在 project1 裡。

manifest 是這樣:
<manifest>
	<remote name="origin" fetch="./" />
	<default revision="master" remote="origin" />
	<project path='project1' name='pp/project1' />
	<project path='priject2' name='project2' />
	<project path='project1/p3' name='p3' />
</manifest>
可以看到 project path='project1/p3' 這樣。
但是這樣在 repo sync 後,project1 下 git status 就會看到 p3 new.
所以要加 .gitignore
p3



manifest 中,project 有些會有:
 revision="36fb2aa4f7f25946e0ce3d4223095ce9601dd17f" upstream="cafe/QCLA.2.0-r00015.3"
這樣的話,project 要有 rev="36fb2aa4f7f25946e0ce3d4223095ce9601dd17f" 的 commit 存在,同時,還要有個branch nane叫 ""cafe/QCLA.2.0-r00015.3".
用 git ls-remote 可以列出 remote ref head. 可以檢查

2021/9/10

system service : report ip to another server

啟動後,等 remote server up,然後把 自己的 ip 送過去..
先把自己的 public key 送過去 (user charles-chang)。設定好 ssh 免 password.
準備 service file:
xeontitan:/etc/systemd/system# cat reportip.service 
[Unit]
Description=send my ip address file to rdsuper
Afteter=network-online.target

[Service]
User=charles-chang
WorkingDirectory=/home/charles-chang
ExecStart=/home/charles-chang/reportip.sh

[Install]
WantedBy=multi-user.target
charles-chang 自己的 shell script:
cat /home/charles-chang/reportip.sh 
#!/bin/bash
while true
do
	ping -c 1 rdsuper
	if [ $? -eq 0 ]
	then
		break
	fi
	sleep 10
done
ip addr > $HOSTNAME-ip
scp $HOSTNAME-ip rdsuper:~/
enable & start
systemctl daemon-reload
systemctl enable reportip.service
systemctl start reportip.service



改用 python 做,用 socket 直接 update /etc/hosts 的話: 叫 chatgpt 寫...
為了測試,先改 /etc/hosts_u 系統測試 OK 再改 /etc/hosts..

~$ cat updatehosts.py
import socket

def update_hosts_file(ip, hostname):
    # Read the current contents of /etc/hosts
    with open('/etc/hosts_u', 'r') as file:
        lines = file.readlines()

    # Check if the hostname already exists in the file
    exists = False
    for i, line in enumerate(lines):
        if hostname in line:
            # Update the existing entry
            lines[i] = f"{ip} {hostname}\n"
            exists = True
            break

    # If the hostname does not exist, add a new entry
    if not exists:
        lines.append(f"{ip} {hostname}\n")

    # Write the updated contents back to /etc/hosts
    with open('/etc/hosts_u', 'w') as file:
        file.writelines(lines)

def start_server(port):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('0.0.0.0', port))
    server_socket.listen(5)
    print(f"Listening on port {port}...")

    while True:
        client_socket, client_address = server_socket.accept()
        print(f"Connection from {client_address}")

        data = client_socket.recv(1024).decode('utf-8')
        if data:
            ip, hostname = data.split(',')
            update_hosts_file(ip, hostname)
            print(f"Updated /etc/hosts with IP: {ip}, Hostname: {hostname}")

        client_socket.close()

if __name__ == "__main__":
    start_server(61234)
然後 systemd service file:
$ cat updatehosts.service
[Unit]
Description=Sender Python Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/charles-chang/updatehosts.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

至於 sender 端,因為要等 dhcp ready...
要注意 nic interface name 是不是 eno1,不一定是。
要依照自己的系統修改。

$ cat sender_updatehosts.py
import socket
import time
import netifaces

SERVER_IP = '192.168.147.182' 
SERVER_PORT = 61234            

def get_ip_address(interface):
    try:
        ip_address = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
    except (ValueError, KeyError):
        ip_address = None
    hostname = socket.gethostname()
    return ip_address, hostname

def send_data(ip, hostname):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((SERVER_IP, SERVER_PORT))
        message = f"{ip},{hostname}"
        s.sendall(message.encode('utf-8'))
        response = s.recv(1024)
        print('Received', repr(response))

def main():
    interface = 'eno1'
    
    while True:
        ip, hostname = get_ip_address(interface)
        if ip:
            break
        print("Waiting for valid IP address...")
        time.sleep(5)

    send_data(ip, hostname)

if __name__ == "__main__":
    main()

2021/9/9

script to generate script : document here

ref:
cat << EOF > aaaa
this is line one
and two
EOF
可以用來產生 aaaa,內容就是 this is line one,
command 意思是 一直到 EOF,都 cat 出來。
後面的 > aaaa 就是把 cat 出來的東西都放到 aaaa 中。

這個command在 shell script中來create 一個 shell script。
也就是在 "this is line one" 到 EOF 的區域放要產生的 shell script 內容。

因為都是 shell script ,所以為了保險,不要讓 這個 script 處理內容出現問題,可以用
cat << "EOF"
代表 把 EOF 之間的內容都當作 字串處理。
這樣就不用擔心特殊字元的問題。

當然,特殊字元也可以用 跳脫符號 "\",但是只會對特殊字元跳脫。

2021/9/5

Mobilenet

我一直以為是數學上相等的(推導),一直到這一篇才比較清楚。
mobilenet 利用計算量少的方式,產生一樣維度的output featuremap。
至於等不等效,就用實際training 來證明。
原來是一次conv計算+一個active function 就可以得到 feature map。
變成兩次conv計算,兩次active functiom 才能得到(一樣大小.數量的)feature map。也就是說,網路的深度增加了。

depth 指的是 color,例如 RGB 三個就是 depth。而不是網路的深度(層數)
原來的 convolution 都是一個kernel 就要包含三個 matrix (R.G.B)分別對 input 的 R.G.B 做各自的 convolution 後,經過 active function加起來變成一個 matrix (feature map)。
depthwise就是三個 R.G.B 做完各自的 convolution 後,用各自的 active function 做出各自的 matrix (feature map),不要變成一個。
之後再用 1x3 的 kernel 把 R.G.B feature map 經果 activae function 轉成一個 matrix。
這樣舊跟以往的 convolution 一樣的。

做 R.G.B convolution 時,維持分開,不用一個acitvie function 加在一起,所以說是 depthwise separable convolution

2021/9/2

mender : modify product device to use local demo server

先啟動 demo server。
在 source code 中找到 demo.crt
修改 /etc/mender/mender/conf :
參考mender.io build and install client from sources 看設定為 demo 的 conf:

device 的部份:
  • 依照 mender.conf 的 Servers : ServerURL 作為 update server
  • ServerCertificate 有的話,用這個作為 跟 server 溝通的憑證
# diff /data/backup/mender.conf /etc/mender/mender.conf 
--- /data/backup/mender.conf
+++ /etc/mender/mender.conf
@@ -24,13 +24,13 @@
     "StateScriptRetryTimeoutSeconds": 0,
     "StateScriptRetryIntervalSeconds": 0,
     "ModuleTimeoutSeconds": 0,
-    "ServerCertificate": "",
+    "ServerCertificate": "/data/demo.crt",
     "ServerURL": "",
     "UpdateLogPath": "",
-    "TenantToken": "eyJI......",
+    "TenantToken": "",
     "Servers": [
         {
-            "ServerURL": "https://hosted.mender.io"
+            "ServerURL": "https://docker.mender.io"
         }
     ]
 }
ServerURL 好像一定要概,不然會出現 Error:
level=error msg="Failure occurred while executing authorization request: Method: Post, URL: https://host.mender.io/api/devices/v1/authentication/auth_requests" func="github.com/mendersoftware/me
level=error msg="Authorize failed: transient error: authorization request failed: transient error: authorization request failed: Unknown url.Error type: Host validation error" func="github.com/m
level=info msg="State transition: authorize [Sync] -> authorize-wait [Idle]" func=github.com/mendersoftware/mender/app.transitionState file="/home/pi/mender-src/app/mender.go:461"
一定要用 docker.mender.io 的原因可能是因為 demo server 的 hosts 有加:
127.0.0.1 s3.docker.mender.io
127.0.0.1 docker.mender.io
demo.crt copy 過來,mender.conf只要改成這樣, demo server 上就可以看到 request device

demo server 基本照這個
其中,docker 的機器要改 /etc/hosts,加上上面的 mender.io 指到 127.0.0.1