上傳檔案到「/」

This commit is contained in:
miketsai 2026-04-12 09:40:04 +00:00
commit 7e40e52a0e
5 changed files with 706 additions and 0 deletions

23
Dockerfile Normal file
View File

@ -0,0 +1,23 @@
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
build-essential cmake wget curl \
python3 python3-pip \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross \
pkg-config patchelf \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir flask>=2.3 opencv-python-headless>=4.8
ENV CC=arm-linux-gnueabihf-gcc
ENV CXX=arm-linux-gnueabihf-g++
WORKDIR /workspace/kl630_build
EXPOSE 8080 5000
CMD ["bash"]

226
README.md Normal file
View File

@ -0,0 +1,226 @@
# KL630 Host Stream Firmware
即時影像語義分割系統,運行於 Kneron KL630 (Cortex-A7, uClibc)。
透過 IMX662 DOL-HDR 雙曝光攝影機進行 STDC 語義分割,並將結果以 RTSP 串流或 HDMI 輸出。
**技術細節 → [`docs/technical_report.md`](docs/technical_report.md)**
---
## 目錄結構
```
kl630_build/
├── web_serve.py # Web 控制台(主要使用)
├── build_and_serve.py # CLI 版本(不需要瀏覽器)
├── compile.sh # Docker 內 ARM 交叉編譯腳本
├── Dockerfile # kl630-dev image 定義
├── requirements.txt # Python 套件清單
├── src/
│ ├── host_stream/ # 主程式(初始化、推論迴圈、結果處理)
│ ├── app_flow/ # VMF pipeline 控制
│ ├── pre_post/ # YOLOv5 前後處理
│ └── stdc/ # STDC 語義分割後處理
├── include/
│ ├── stdc/ # stdc_post_process.h分析結果結構
│ └── fake/ # SDK 缺少時的 stub headers
├── ini/
│ └── host_stream.ini # 執行期設定model、stream、ISP、FEC 參數)
├── lib/ # 裝置端 .so 函式庫VMF SDK
├── docs/
│ └── technical_report.md # 完整技術文件
└── build/ # 編譯輸出(由 compile.sh 產生)
```
---
## 快速開始
### 前置條件
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) 已安裝並執行中
- Python 3.8+
### 安裝
```bash
pip install -r requirements.txt
```
| 套件 | 用途 |
|------|------|
| `flask` | Web 控制台 + HTTP file server |
| `opencv-python` | RTSP 串流預覽(內建 FFmpeg無需另外安裝|
### 啟動
```bash
python web_serve.py
```
瀏覽器開啟 `http://localhost:8080/`
---
## 新機器首次設定 SOP
全新 KL630 裝置依序執行以下步驟:
### Step 1 — Compile
在 Web 控制台按 **Compile**
- 自動建立 Docker image若不存在
- 交叉編譯 ARM binary
- 複製 binary / INI / 腳本到 `build/`
> 若 binary 已是最新版本,可跳過此步驟直接從 Step 2 開始。
### Step 2 — 設定 Output Mode
**INI Settings** 面板選擇輸出模式(預設 RTSP
| 組合 | RTSP | HDMI | 啟動腳本 |
|------|------|------|---------|
| RTSP 串流 | ON | OFF | `demo_rtsp.sh` |
| HDMI 顯示 | OFF | ON | `demo_hdmi.sh` |
| RTSP + HDMI 同時 | ON | ON | `demo_rtsp_hdmi.sh`INI voc_enable=1|
| 純推論(無輸出) | OFF | OFF | 直接啟動 binary |
### Step 3 — Deploy to KL630
**Deploy to KL630**,自動透過 Telnet 完成:
1. 停止舊 firmware
2. 下載 binary、INI、NEF 模型
3. 下載 `demo_rtsp.sh``demo_hdmi.sh``demo_rtsp_hdmi.sh`
4. 設定 VOC 輸出模式
5. 啟動 firmware
### Step 4 — First-time ISP Setup
**First-time ISP Setup**
寫入 DOL-HDR 所需的 flash 參數(`dwStatisticsSrcType=2``bGTREnable=1`
**只需執行一次**,設定永久保留在 flash重新 Deploy 不會被覆蓋。
> 跳過此步驟影像會過暗或曝光不正確。
### Step 5 — 魚眼鏡頭校正FEC
**INI Settings** 面板設定 Fish-Eye Correction
| 參數 | 推薦設定 | 說明 |
|------|---------|------|
| FEC | ON | 開啟魚眼校正 |
| **Mode** | **4 — 180° Two Direction** | 天花板魚眼鏡頭推薦模式,同時展開水平與垂直方向 |
| Install Type | 0 — Ceiling | 天花板安裝 |
| EIS | 視需求 | 電子防手震 |
**Apply to Device + Restart** 套用並重啟 firmware。
---
## Web 控制台功能說明
### Network Config
設定 **Host IP**(這台 PC、**KL630 IP**、**HTTP Port**、**Docker Image**。
按 Save 後自動更新 `deploy.sh``HOST_URL`
### HTTP Server Files
列出 `build/` 目錄下所有可下載的檔案,以及裝置端手動部署指令:
```sh
wget http://<host_ip>:8080/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh
```
### RTSP Stream Preview
直接在網頁預覽 KL630 串流,不需要開 VLC。
**▶ Start Stream** 即可,連線失敗時 6 秒內回報錯誤。
---
## Actions
| 按鈕 | 說明 |
|------|------|
| **Compile** | Docker 交叉編譯 ARM binary複製到 `build/` |
| **Deploy to KL630** | Telnet 部署 binary / INI / NEF / 腳本,依 Output Mode 啟動 firmware |
| **First-time ISP Setup** | 新機器一次性 ISP flash 設定DOL-HDR 參數) |
| **Write Autostart** | 寫入開機自動啟動腳本 `/etc/init.d/S99firmware` |
---
## INI Settings
### Fish-Eye Correction (FEC)
| 參數 | 說明 |
|------|------|
| FEC ON/OFF | 開啟或關閉魚眼校正(`fec_mode = 0` 為關閉)|
| Mode 1 | Single Region |
| Mode 2 | 180° All Direction |
| Mode 3 | 180° One Direction |
| **Mode 4** | **180° Two Direction推薦** |
| Mode 5 | PT Mode |
| Install Type | Ceiling / Table / Wall`initial_fec_app_type`|
| EIS | 電子防手震(`eis_enable`|
| DrawBox | H.264 burn-in 偵測框(`DrawBoxEnable`|
### Output Mode
| 選項 | 說明 |
|------|------|
| RTSP | H.264 RTSP 串流輸出,由 `demo_rtsp.sh` 管理 |
| HDMI | VOC HDMI 顯示輸出,由 `demo_hdmi.sh` 管理 |
- **Save INIdisk only** — 更新本地 `ini/host_stream.ini`,下次 Deploy 時推送至裝置
- **Apply to Device + Restart** — 立即 Telnet 更新裝置 INI 並重啟 firmware
### Model Settings
切換推論模型NEF 檔、ModelId、JobId支援上傳新 NEF。
**Apply to Device + Restart** 會下載 NEF 到裝置並依目前 Output Mode 重啟。
---
## Terminal
Output Log 下方有命令輸入列,可直接在網頁對 KL630 下指令:
```sh
cat /tmp/fw.log # 查看 firmware 啟動 log
cat /tmp/rtsp_demo.log # 查看 RTSP demo log
ps | grep firmware # 確認 firmware 是否在執行
killall kp_firmware_host_stream
```
---
## CLI 版本(不需要瀏覽器)
```bash
python build_and_serve.py # 編譯 + 檢查 + 啟動 HTTP server
python build_and_serve.py --no-build # 跳過編譯,直接 serve
python build_and_serve.py --port 9090
```
---
## Firmware 執行結果console log
```
[STDC] frame=42 mov=1 diff=4.2 bunker=0.0% car=8.3% grass=0.0% greenery=12.1% person=0.0% pond=0.0% road=71.4% tree=8.2%
[STDC] ON ROAD
[STDC WARN] CAR 8.3%
```
---
## 詳細說明
IMX662 DOL-HDR 雙曝光設定、STDC 語義分割架構、新裝置部署 SOP、故障排查
**[`docs/technical_report.md`](docs/technical_report.md)**

302
build_and_serve.py Normal file
View File

@ -0,0 +1,302 @@
#!/usr/bin/env python3
"""
build_and_serve.py KL630 一鍵編譯 + 檢查 + HTTP server
使用方式
python build_and_serve.py # 編譯 + 檢查 + 啟動 server
python build_and_serve.py --no-build # 跳過編譯,只啟動 server
python build_and_serve.py --port 9090
裝置端
wget http://192.168.3.1:<port>/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh
"""
import sys
import os
import struct
import shutil
import argparse
import subprocess
from pathlib import Path
from http.server import HTTPServer, SimpleHTTPRequestHandler
from threading import Thread
# ── ANSI colors ─────────────────────────────────────────────────────────────
def _c(code, text): return f"\033[{code}m{text}\033[0m" if sys.stdout.isatty() else text
def ok(t): return _c("32;1", t)
def fail(t): return _c("31;1", t)
def info(t): return _c("36", t)
def warn(t): return _c("33", t)
def head(t): return _c("35;1", t)
# ── Paths ────────────────────────────────────────────────────────────────────
SCRIPT_DIR = Path(__file__).resolve().parent
BUILD_DIR = SCRIPT_DIR / "build"
BINARY_NAME = "kp_firmware_host_stream"
BINARY_PATH = BUILD_DIR / BINARY_NAME
INI_SRC = SCRIPT_DIR / "ini" / "host_stream.ini"
DEPLOY_SRC = SCRIPT_DIR / "tools" / "device" / "deploy.sh"
DEMO_RTSP_SRC = SCRIPT_DIR / "tools" / "device" / "demo_rtsp.sh"
# Docker image name — change if yours is different
DOCKER_IMAGE = "kl630-dev"
# ── Step 0: Ensure Docker image exists ───────────────────────────────────────
def step_ensure_image() -> bool:
"""Build the Docker image from Dockerfile if it doesn't exist yet."""
print(head("\n=== [0/3] Docker Image Check ==="))
if shutil.which("docker") is None:
print(fail(" ERROR: 'docker' not found in PATH"))
return False
# Check if image already exists
r = subprocess.run(
["docker", "image", "inspect", DOCKER_IMAGE],
capture_output=True
)
if r.returncode == 0:
print(ok(f" Image '{DOCKER_IMAGE}' already exists, skipping build."))
return True
# Image not found — build it from Dockerfile
dockerfile = SCRIPT_DIR / "Dockerfile"
if not dockerfile.exists():
print(fail(f" ERROR: Dockerfile not found at {dockerfile}"))
return False
print(warn(f" Image '{DOCKER_IMAGE}' not found. Building from Dockerfile..."))
print(info(f" docker build -t {DOCKER_IMAGE} {SCRIPT_DIR}"))
print()
result = subprocess.run(
["docker", "build", "-t", DOCKER_IMAGE, str(SCRIPT_DIR)]
)
if result.returncode != 0:
print(fail(f"\n IMAGE BUILD FAILED (exit {result.returncode})"))
return False
print(ok(f"\n Image '{DOCKER_IMAGE}' built successfully."))
return True
# ── Step 1: Docker build ──────────────────────────────────────────────────────
def step_build() -> bool:
print(head("\n=== [1/3] Docker Build ==="))
# Check docker available
if shutil.which("docker") is None:
print(fail(" ERROR: 'docker' not found in PATH"))
return False
# Mount the full project directory — SDK/lib/include/src all come from here.
mount = str(SCRIPT_DIR).replace("\\", "/")
cmd = [
"docker", "run", "--rm",
"-v", f"{mount}:/workspace/kl630_build",
DOCKER_IMAGE,
"bash", "/workspace/kl630_build/compile.sh"
]
print(info(f" Image : {DOCKER_IMAGE}"))
print(info(f" Mount : {mount} -> /workspace/kl630_build"))
print()
result = subprocess.run(cmd)
if result.returncode != 0:
print(fail(f"\n BUILD FAILED (exit {result.returncode})"))
return False
print(ok("\n Build succeeded."))
return True
# ── Step 2: Check binary ──────────────────────────────────────────────────────
def _check_elf_arm(path: Path):
"""Return (is_arm_elf, description)."""
try:
with open(path, "rb") as f:
hdr = f.read(20)
if len(hdr) < 20:
return False, "file too small"
if hdr[:4] != b"\x7fELF":
return False, "not an ELF file"
e_machine = struct.unpack_from("<H", hdr, 18)[0]
arch_map = {0x28: "ARM (armv7)", 0xb7: "AArch64", 0x3e: "x86_64"}
arch = arch_map.get(e_machine, f"unknown machine 0x{e_machine:04x}")
return e_machine == 0x28, arch
except Exception as e:
return False, str(e)
def step_check() -> bool:
print(head("\n=== [2/3] Check Binary ==="))
if not BINARY_PATH.exists():
print(fail(f" FAIL: {BINARY_PATH} not found"))
return False
size = BINARY_PATH.stat().st_size
is_arm, arch = _check_elf_arm(BINARY_PATH)
size_ok = size > 100_000
arch_ok = is_arm
print(f" File : {BINARY_PATH}")
print(f" Size : {size/1024:.1f} KB {'' if size_ok else ''}")
print(f" Arch : {arch} {'' if arch_ok else ''}")
# Try readelf if available (in Docker or WSL)
if shutil.which("readelf"):
r = subprocess.run(
["readelf", "-d", str(BINARY_PATH)],
capture_output=True, text=True
)
needed = [l.split("(NEEDED)")[1].strip() for l in r.stdout.splitlines() if "(NEEDED)" in l]
if needed:
print(f" NEEDED: {', '.join(needed)}")
if "libc.so.0" in needed:
print(ok(" uClibc patch: OK (libc.so.0 present)"))
elif "libc.so.6" in needed:
print(fail(" uClibc patch: MISSING (libc.so.6 still present)"))
if not (size_ok and arch_ok):
print(fail("\n CHECK FAILED"))
return False
print(ok("\n Binary looks good."))
return True
# ── Step 3: Copy Binary To Network Share ──────────────────────
def step_copy_to_network(dst_dir: str) -> bool:
print(head("\n=== [copy] Copy Binary To Network Share ==="))
if not BINARY_PATH.exists():
print(fail(f" FAIL: source binary not found: {BINARY_PATH}"))
return False
try:
dst_path = Path(dst_dir)
dst_path.mkdir(parents=True, exist_ok=True)
dst_file = dst_path / BINARY_NAME
shutil.copy2(BINARY_PATH, dst_file)
print(ok(f" Copied: {BINARY_PATH}"))
print(ok(f" -> {dst_file}"))
return True
except Exception as e:
print(fail(f" COPY FAILED: {e}"))
return False
# ── Step 4: Prepare serve directory & start HTTP server ──────────────────────
def step_serve(port: int):
print(head("\n=== [3/3] Prepare & Serve ==="))
BUILD_DIR.mkdir(exist_ok=True)
# Copy INI
if INI_SRC.exists():
dst = BUILD_DIR / "host_stream.ini"
shutil.copy2(INI_SRC, dst)
print(ok(f" Copied : {INI_SRC.name} ({dst.stat().st_size} bytes)"))
else:
print(warn(f" WARN: {INI_SRC} not found, skipping"))
# Copy deploy.sh
if DEPLOY_SRC.exists():
dst = BUILD_DIR / "deploy.sh"
shutil.copy2(DEPLOY_SRC, dst)
print(ok(f" Copied : deploy.sh"))
else:
print(warn(f" WARN: {DEPLOY_SRC} not found, skipping"))
# Copy demo_rtsp.sh
if DEMO_RTSP_SRC.exists():
dst = BUILD_DIR / "demo_rtsp.sh"
shutil.copy2(DEMO_RTSP_SRC, dst)
print(ok(f" Copied : demo_rtsp.sh"))
else:
print(warn(f" WARN: {DEMO_RTSP_SRC} not found, skipping"))
# List what will be served
print(f"\n Serving from: {BUILD_DIR}")
for f in sorted(BUILD_DIR.iterdir()):
size_str = f"{f.stat().st_size/1024:.1f} KB"
print(f" {f.name:<40} {size_str:>10}")
# Start server
os.chdir(BUILD_DIR)
class _Handler(SimpleHTTPRequestHandler):
def log_message(self, fmt, *args):
# Show only non-304 responses
code = args[1] if len(args) > 1 else ""
if code != "304":
print(info(f" [{self.address_string()}] {fmt % args}"))
httpd = HTTPServer(("", port), _Handler)
print(head(f"\n=== HTTP Server on port {port} ==="))
print(f" URL : http://0.0.0.0:{port}/")
print()
print(warn(" 裝置端執行:"))
print(f" wget http://192.168.3.1:{port}/deploy.sh -O /tmp/deploy.sh && sh /tmp/deploy.sh")
print()
print(warn(" 或分步驟:"))
print(f" wget http://192.168.3.1:{port}/{BINARY_NAME} -O /mnt/flash/vienna/{BINARY_NAME}")
print(f" wget http://192.168.3.1:{port}/host_stream.ini -O $BIN_DIR/ini/host_stream.ini")
print()
print(" Ctrl+C 停止\n")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print(info("\n Server stopped."))
# ── Main ─────────────────────────────────────────────────────────────────────
def main():
global DOCKER_IMAGE
parser = argparse.ArgumentParser(description="KL630 build + check + serve")
parser.add_argument("--no-build", action="store_true",
help="跳過 Docker build只 check + serve")
parser.add_argument("--no-check", action="store_true",
help="跳過 binary 檢查")
parser.add_argument("--copy-dst",default=r"\\192.168.0.122\public\nfs_share",
help="編譯成功後複製 binary 到指定網路資料夾")
parser.add_argument("--no-copy",action="store_true",
help="不要複製 binary 到網路資料夾")
parser.add_argument("--no-serve", action="store_true",
help="只 build + check不啟動 HTTP server")
parser.add_argument("--port", type=int, default=8080,
help="HTTP server port (default: 8080)")
parser.add_argument("--image", default=DOCKER_IMAGE,
help=f"Docker image name (default: {DOCKER_IMAGE})")
args = parser.parse_args()
DOCKER_IMAGE = args.image
print(head("KL630 Build & Serve"))
print(f" Root : {SCRIPT_DIR}")
print(f" Binary : {BINARY_PATH}")
print(f" Port : {args.port}")
print(f" Copy : {args.copy_dst}")
# ===== Step 1: Build =====
if not args.no_build:
if not step_ensure_image():
sys.exit(1)
if not step_build():
sys.exit(1)
# ===== Step 2: Check =====
if not args.no_check:
if not step_check():
if args.no_build:
print(warn(" (使用 --no-build 時 binary 可能是舊的)"))
else:
sys.exit(1)
# ===== Step 3: Copy重點=====
if not args.no_copy:
if not step_copy_to_network(args.copy_dst):
sys.exit(1)
# ===== Step 4: Serve =====
if not args.no_serve:
step_serve(args.port)
if __name__ == "__main__":
main()

153
compile.sh Normal file
View File

@ -0,0 +1,153 @@
#!/bin/bash
# compile.sh — KL630 cross-compile (ARM armv7-a)
# Run inside Docker: docker run --rm -v "<host_path>:/workspace/kl630_build" kl630-dev bash /workspace/kl630_build/compile.sh
set -e
WORKSPACE=/workspace/kl630_build
BUILD_DIR=$WORKSPACE/build
OUTPUT=$BUILD_DIR/kp_firmware_host_stream
CC=arm-linux-gnueabihf-gcc
LIB_DIR=$WORKSPACE/lib
echo "=== Checking compiler ==="
which $CC || { echo "ERROR: $CC not found"; exit 1; }
$CC --version | head -1
mkdir -p $BUILD_DIR
CFLAGS="-DVATICS_PLATFORM -DKL630 -D_GNU_SOURCE -U_FORTIFY_SOURCE"
CFLAGS="$CFLAGS -march=armv7-a -mfpu=neon -mfloat-abi=hard -Os"
CFLAGS="$CFLAGS -Wall -Wno-unused-variable -Wno-unused-function"
INCLUDES="-I$WORKSPACE/include/host_stream"
INCLUDES="$INCLUDES -I$WORKSPACE/include/app_flow"
INCLUDES="$INCLUDES -I$WORKSPACE/include/app_flow/pre_post_proc"
INCLUDES="$INCLUDES -I$WORKSPACE/include/common"
# Prefer real SDK headers when available; fallback to local fake headers.
SDK_INCLUDE_CANDIDATES=(
#"$WORKSPACE/SDK/sdk/vtcs_root_vienna/include"
"$WORKSPACE/include/vtcs_root_vienna/include"
"$WORKSPACE/third_party/kl630_sdk/include"
)
SDK_SYSROOT_INCLUDE="$WORKSPACE/third_party/kl630_sdk/sysroot/usr/include"
#SDK_MODULES_ROOT="$WORKSPACE/SDK/sdk/modules"
SDK_MODULES_ROOT="$WORKSPACE/include/modules"
USE_FAKE_HEADERS=1
for SDK_INCLUDE_ROOT in "${SDK_INCLUDE_CANDIDATES[@]}"; do
if [ -d "$SDK_INCLUDE_ROOT" ]; then
INCLUDES="$INCLUDES -I$SDK_INCLUDE_ROOT"
if [ -d "$SDK_INCLUDE_ROOT/vmf" ]; then
INCLUDES="$INCLUDES -I$SDK_INCLUDE_ROOT/vmf"
fi
USE_FAKE_HEADERS=0
echo "=== Using SDK include: $SDK_INCLUDE_ROOT ==="
break
fi
done
if [ -d "$SDK_MODULES_ROOT" ]; then
INCLUDES="$INCLUDES -I$SDK_MODULES_ROOT"
fi
if [ -d "$SDK_SYSROOT_INCLUDE" ]; then
INCLUDES="$INCLUDES -I$SDK_SYSROOT_INCLUDE"
fi
if [ -d "$WORKSPACE/include/fake" ]; then
# Keep fake headers as the last fallback for components not shipped in this SDK pack.
INCLUDES="$INCLUDES -I$WORKSPACE/include/fake"
fi
if [ "$USE_FAKE_HEADERS" -eq 1 ]; then
echo "=== Header mode: fallback fake headers ==="
else
CFLAGS="$CFLAGS -DUSE_REAL_SDK_HEADERS=1"
echo "=== Header mode: real SDK headers + fake fallback ==="
fi
ALL_SRCS=$(ls \
$WORKSPACE/src/host_stream/*.c \
$WORKSPACE/src/app_flow/*.c \
$WORKSPACE/src/pre_post_proc/*.c \
2>/dev/null)
echo ""
echo "=== Source files ==="
for s in $ALL_SRCS; do echo " $s"; done
echo ""
echo "=== Compiling ==="
OBJ_FILES=()
COMPILE_ERRORS=0
for src in $ALL_SRCS; do
base=$(basename $src .c)
obj=$BUILD_DIR/${base}.o
echo " CC $base.c"
if $CC $CFLAGS $INCLUDES -c "$src" -o "$obj" 2>&1; then
OBJ_FILES+=("$obj")
else
echo " *** COMPILE ERROR: $src ***"
COMPILE_ERRORS=$((COMPILE_ERRORS + 1))
fi
done
if [ $COMPILE_ERRORS -gt 0 ]; then
echo ""
echo "ERROR: $COMPILE_ERRORS file(s) failed to compile."
exit 1
fi
echo ""
echo "=== All objects compiled ==="
# 建立 app_yolo symlinkversioned .so
if [ ! -e "$LIB_DIR/libapp_yolo.so" ]; then
AYSO=$(ls $LIB_DIR/libapp_yolo.so* 2>/dev/null | head -1)
if [ -n "$AYSO" ]; then
ln -sf $(basename $AYSO) $LIB_DIR/libapp_yolo.so
echo " symlink: libapp_yolo.so -> $(basename $AYSO)"
fi
fi
# 建立 uClibc rpath-link symlinks
# 只建 libc.so.0/libc.so.1 供 rpath-link 解析 libvmf.so 的依賴
# 不建 libc.so 和 libpthread.so — 讓 -lc/-lpthread 繼續用 glibc startup
if [ -e "$LIB_DIR/libuClibc-1.0.34.so" ]; then
for name in libc.so.0 libc.so.1; do
[ ! -e "$LIB_DIR/$name" ] && ln -sf libuClibc-1.0.34.so $LIB_DIR/$name && echo " symlink: $name -> libuClibc-1.0.34.so"
done
else
echo "WARNING: libuClibc-1.0.34.so not found in $LIB_DIR"
echo " Transfer it from device: tftp -p -l /lib/libuClibc-1.0.34.so -r libuClibc-1.0.34.so <host_ip> 9069"
fi
echo ""
echo "=== Linking ==="
$CC -march=armv7-a -mfpu=neon -mfloat-abi=hard \
"${OBJ_FILES[@]}" \
-L$LIB_DIR \
-Wl,-rpath-link,$LIB_DIR \
-Wl,-rpath,\$ORIGIN/lib \
-Wl,--dynamic-linker,/lib/ld-uClibc.so.1 \
-Wl,--allow-shlib-undefined \
-Wl,--allow-multiple-definition \
-lvmf -lmembroker -lmsgbroker -lsyncringbuffer -liniparser \
-lvmf_nnm -lkutils -laio -lpthread -lm \
-lapp_yolo \
-o $OUTPUT \
&& echo "" \
&& echo "=== SUCCESS: $OUTPUT ===" \
|| { echo ""; echo "=== LINK FAILED ==="; exit 1; }
ls -lh $OUTPUT
echo ""
echo "=== Patching ELF for uClibc ==="
# Remove glibc dynamic linker (causes ld-uClibc to try loading itself as .so)
patchelf --remove-needed ld-linux-armhf.so.3 $OUTPUT
# Map glibc sonames to uClibc equivalents
patchelf --replace-needed libc.so.6 libc.so.0 $OUTPUT
patchelf --remove-needed libm.so.6 $OUTPUT
patchelf --remove-needed libpthread.so.0 $OUTPUT
echo "=== NEEDED after patch ==="
readelf -d $OUTPUT | grep NEEDED

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
flask>=2.3
opencv-python>=4.8