Compare commits
No commits in common. "548cec2d4f53283c3661909b2d32a0c2c5608ff6" and "9d7d9073a5420e5af7eec4f4eb32bfbfb91e52ea" have entirely different histories.
548cec2d4f
...
9d7d9073a5
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"host_ip": "192.168.0.114",
|
|
||||||
"kl630_ip": "192.168.0.201",
|
|
||||||
"port": 8080,
|
|
||||||
"docker_image": "kl630-dev"
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
BIN
build/bt_uart.o
BIN
build/bt_uart.o
Binary file not shown.
@ -1,46 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
|
|
||||||
FW=/mnt/flash/vienna/kp_firmware_host_stream
|
|
||||||
INI=$BIN_DIR/ini/host_stream.ini
|
|
||||||
|
|
||||||
cd $BIN_DIR
|
|
||||||
|
|
||||||
# Load hardware drivers if not already loaded (required for EDMC/NPU)
|
|
||||||
[ -e /dev/vpl_edmc ] || (cd /mnt/flash/vienna/drivers && sh driver.sh 2>/dev/null && sleep 1)
|
|
||||||
|
|
||||||
killall -9 kp_firmware_host_stream 2>/dev/null || true
|
|
||||||
killall -9 rtsps 2>/dev/null || true
|
|
||||||
sleep 1
|
|
||||||
rm -f /dev/shm/*
|
|
||||||
|
|
||||||
# Optional override: PIXFMT=NV21 MODEL_PATH=nef/other.nef MODEL_ID=xxxxx JOB_ID=yyy ./demo_hdmi.sh
|
|
||||||
PIXFMT=${PIXFMT:-NV12}
|
|
||||||
|
|
||||||
# Model settings — read from INI as default, env var overrides at runtime
|
|
||||||
_ini_val() { grep "^$1" "$INI" | sed 's/.*= *//' | tr -d '"' | awk '{print $1}'; }
|
|
||||||
MODEL_PATH=${MODEL_PATH:-$(_ini_val ModelPath)}
|
|
||||||
MODEL_ID=${MODEL_ID:-$(_ini_val ModelId)}
|
|
||||||
JOB_ID=${JOB_ID:-$(_ini_val JobId)}
|
|
||||||
|
|
||||||
# HDMI demo profile: inference on stream1 (724x362) + HDMI output from stream0 (1920x1080).
|
|
||||||
# InferenceStream=1: NPU receives 724x362 frames (model native res) — ~8x faster than InferenceStream=0.
|
|
||||||
# StreamCount=2: creates both stream0 (1920x1080, draw_box+HDMI) and stream1 (724x362, inference) SSMs.
|
|
||||||
sed -i 's/^NnmSource.*/NnmSource = 0/' $INI
|
|
||||||
sed -i 's/^GetImageBufMode.*/GetImageBufMode = 0/' $INI
|
|
||||||
sed -i 's/^InferenceStream.*/InferenceStream = 1/' $INI
|
|
||||||
sed -i 's/^StreamCount.*/StreamCount = 2/' $INI
|
|
||||||
sed -i 's/^voc_enable.*/voc_enable = 1/' $INI
|
|
||||||
sed -i "s/^PixFmt.*/PixFmt = ${PIXFMT}/" $INI
|
|
||||||
# DrawBoxEnable: respect INI value set by web UI (do not override)
|
|
||||||
|
|
||||||
echo "=== HDMI Demo INI ==="
|
|
||||||
grep -E "ModelPath|ModelId|JobId|NnmSource|GetImageBufMode|InferenceStream|StreamCount|voc_enable|PixFmt" $INI
|
|
||||||
echo "=== Model (runtime): MODEL_PATH=$MODEL_PATH MODEL_ID=$MODEL_ID JOB_ID=$JOB_ID ==="
|
|
||||||
|
|
||||||
echo "=== Start Firmware (HDMI demo) ==="
|
|
||||||
LD_LIBRARY_PATH=/mnt/flash/vienna/lib $FW -m "$MODEL_PATH" -i "$MODEL_ID" -j "$JOB_ID" &
|
|
||||||
FW_PID=$!
|
|
||||||
echo "Firmware PID: $FW_PID"
|
|
||||||
wait $FW_PID
|
|
||||||
Binary file not shown.
@ -1,56 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
|
|
||||||
FW=/mnt/flash/vienna/kp_firmware_host_stream
|
|
||||||
INI=$BIN_DIR/ini/host_stream.ini
|
|
||||||
|
|
||||||
cd $BIN_DIR
|
|
||||||
|
|
||||||
# Load hardware drivers if not already loaded (required for EDMC/NPU)
|
|
||||||
[ -e /dev/vpl_edmc ] || (cd /mnt/flash/vienna/drivers && sh driver.sh 2>/dev/null && sleep 1)
|
|
||||||
|
|
||||||
killall -9 kp_firmware_host_stream 2>/dev/null || true
|
|
||||||
killall -9 rtsps 2>/dev/null || true
|
|
||||||
sleep 1
|
|
||||||
rm -f /dev/shm/*
|
|
||||||
|
|
||||||
# Optional overrides:
|
|
||||||
# PIXFMT=NV21 INF_STREAM=0 MODEL_PATH=nef/other.nef MODEL_ID=xxxxx JOB_ID=yyy ./demo_rtsp.sh
|
|
||||||
PIXFMT=${PIXFMT:-NV12}
|
|
||||||
INF_STREAM=${INF_STREAM:-1}
|
|
||||||
|
|
||||||
# Model settings — read from INI as default, env var overrides at runtime
|
|
||||||
_ini_val() { grep "^$1" "$INI" | sed 's/.*= *//' | tr -d '"' | awk '{print $1}'; }
|
|
||||||
MODEL_PATH=${MODEL_PATH:-$(_ini_val ModelPath)}
|
|
||||||
MODEL_ID=${MODEL_ID:-$(_ini_val ModelId)}
|
|
||||||
JOB_ID=${JOB_ID:-$(_ini_val JobId)}
|
|
||||||
|
|
||||||
# RTSP demo profile: single-process source + one encoder stream + RTSP server.
|
|
||||||
sed -i 's/^NnmSource.*/NnmSource = 0/' $INI
|
|
||||||
sed -i 's/^GetImageBufMode.*/GetImageBufMode = 0/' $INI
|
|
||||||
sed -i "s/^InferenceStream.*/InferenceStream = ${INF_STREAM}/" $INI
|
|
||||||
sed -i 's/^StreamCount.*/StreamCount = 2/' $INI
|
|
||||||
sed -i "s/^PixFmt.*/PixFmt = ${PIXFMT}/" $INI
|
|
||||||
sed -i 's/^voc_enable.*/voc_enable = 0/' $INI
|
|
||||||
# DrawBoxEnable: respect INI value set by web UI (do not override)
|
|
||||||
|
|
||||||
echo "=== RTSP Demo INI ==="
|
|
||||||
grep -E "ModelPath|ModelId|JobId|NnmSource|GetImageBufMode|InferenceStream|StreamCount|voc_enable|PixFmt|DrawBoxEnable" $INI
|
|
||||||
echo "=== Model (runtime): MODEL_PATH=$MODEL_PATH MODEL_ID=$MODEL_ID JOB_ID=$JOB_ID ==="
|
|
||||||
|
|
||||||
echo "=== Start Firmware (RTSP demo) ==="
|
|
||||||
LD_LIBRARY_PATH=/mnt/flash/vienna/lib $FW -m "$MODEL_PATH" -i "$MODEL_ID" -j "$JOB_ID" &
|
|
||||||
FW_PID=$!
|
|
||||||
|
|
||||||
sleep 4
|
|
||||||
|
|
||||||
# Start RTSP server that reads venc_srb_* output.
|
|
||||||
LD_LIBRARY_PATH=/mnt/flash/vienna/lib ./rtsps -c stream_server_config.ini &
|
|
||||||
RTSP_PID=$!
|
|
||||||
|
|
||||||
echo "Firmware PID: $FW_PID"
|
|
||||||
echo "RTSP PID: $RTSP_PID"
|
|
||||||
echo "RTSP URL: rtsp://192.168.3.10/live1.sdp"
|
|
||||||
|
|
||||||
wait $FW_PID
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# by mars
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
|
|
||||||
FW=/mnt/flash/vienna/kp_firmware_host_stream
|
|
||||||
INI=$BIN_DIR/ini/host_stream.ini
|
|
||||||
|
|
||||||
cd $BIN_DIR
|
|
||||||
|
|
||||||
# Load hardware drivers if not already loaded (required for EDMC/NPU)
|
|
||||||
[ -e /dev/vpl_edmc ] || (cd /mnt/flash/vienna/drivers && sh driver.sh 2>/dev/null && sleep 1)
|
|
||||||
|
|
||||||
killall -9 kp_firmware_host_stream 2>/dev/null || true
|
|
||||||
killall -9 rtsps 2>/dev/null || true
|
|
||||||
sleep 1
|
|
||||||
rm -f /dev/shm/*
|
|
||||||
|
|
||||||
# Optional override: PIXFMT=NV21 MODEL_PATH=nef/other.nef MODEL_ID=xxxxx JOB_ID=yyy ./demo_rtsp_hdmi.sh
|
|
||||||
PIXFMT=${PIXFMT:-NV12}
|
|
||||||
|
|
||||||
# Model settings — read from INI as default, env var overrides at runtime
|
|
||||||
_ini_val() { grep "^$1" "$INI" | sed 's/.*= *//' | tr -d '"' | awk '{print $1}'; }
|
|
||||||
MODEL_PATH=${MODEL_PATH:-$(_ini_val ModelPath)}
|
|
||||||
MODEL_ID=${MODEL_ID:-$(_ini_val ModelId)}
|
|
||||||
JOB_ID=${JOB_ID:-$(_ini_val JobId)}
|
|
||||||
|
|
||||||
# Combined RTSP + HDMI profile.
|
|
||||||
# InferenceStream=1 uses stream1 (724x362) for inference.
|
|
||||||
# DrawOnResize=1 so segmentation overlay is drawn on resize streams (RTSP streams).
|
|
||||||
sed -i 's/^NnmSource.*/NnmSource = 0/' $INI
|
|
||||||
sed -i 's/^GetImageBufMode.*/GetImageBufMode = 0/' $INI
|
|
||||||
sed -i 's/^InferenceStream.*/InferenceStream = 1/' $INI
|
|
||||||
sed -i 's/^StreamCount.*/StreamCount = 2/' $INI
|
|
||||||
sed -i 's/^voc_enable.*/voc_enable = 1/' $INI
|
|
||||||
sed -i "s/^PixFmt.*/PixFmt = ${PIXFMT}/" $INI
|
|
||||||
# DrawBoxEnable: respect INI value set by web UI (do not override)
|
|
||||||
# DrawOnResize: not needed — firmware auto-disables it when InferenceStream != 0
|
|
||||||
|
|
||||||
echo "=== RTSP+HDMI Demo INI ==="
|
|
||||||
grep -E "ModelPath|ModelId|JobId|NnmSource|GetImageBufMode|InferenceStream|StreamCount|voc_enable|PixFmt|DrawBoxEnable|DrawOnResize" $INI
|
|
||||||
echo "=== Model (runtime): MODEL_PATH=$MODEL_PATH MODEL_ID=$MODEL_ID JOB_ID=$JOB_ID ==="
|
|
||||||
|
|
||||||
echo "=== Start Firmware (RTSP+HDMI demo) ==="
|
|
||||||
LD_LIBRARY_PATH=/mnt/flash/vienna/lib $FW -m "$MODEL_PATH" -i "$MODEL_ID" -j "$JOB_ID" &
|
|
||||||
FW_PID=$!
|
|
||||||
|
|
||||||
sleep 4
|
|
||||||
|
|
||||||
# Start RTSP server that reads venc_srb_* output.
|
|
||||||
LD_LIBRARY_PATH=/mnt/flash/vienna/lib ./rtsps -c stream_server_config.ini &
|
|
||||||
RTSP_PID=$!
|
|
||||||
|
|
||||||
echo "Firmware PID: $FW_PID"
|
|
||||||
echo "RTSP PID: $RTSP_PID"
|
|
||||||
echo "RTSP URL: rtsp://192.168.3.10/live1.sdp"
|
|
||||||
|
|
||||||
wait $FW_PID
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# deploy.sh — 部署新編譯的 firmware 到裝置並重啟 RTSP demo
|
|
||||||
#
|
|
||||||
# 使用方式(在裝置上執行):
|
|
||||||
# sh deploy.sh
|
|
||||||
#
|
|
||||||
# 前提:host 192.168.3.1:8080 在提供以下檔案:
|
|
||||||
# /kp_firmware_host_stream (compile.sh 的輸出)
|
|
||||||
# /host_stream.ini (kl630_build/ini/host_stream.ini)
|
|
||||||
#
|
|
||||||
# 一次性設定(只需在新機器上執行一次,之後重開機不需要再跑):
|
|
||||||
# sh deploy.sh --setup
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
HOST_URL="http://192.168.0.114:8080"
|
|
||||||
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
|
|
||||||
FW=/mnt/flash/vienna/kp_firmware_host_stream
|
|
||||||
INI=$BIN_DIR/ini/host_stream.ini
|
|
||||||
|
|
||||||
# ── 一次性 ISP 資源修正 ─────────────────────────────────────────────────────
|
|
||||||
# IMX662 DOL-HDR 需要 dwStatisticsSrcType=2 和 bGTREnable=1。
|
|
||||||
# 這些寫入 flash,重開機後自動保留,只需要執行一次。
|
|
||||||
one_time_setup() {
|
|
||||||
echo "=== 一次性 ISP resource 設定 ==="
|
|
||||||
sed -i 's/dwStatisticsSrcType = 0/dwStatisticsSrcType = 2/' \
|
|
||||||
$BIN_DIR/Resource/AWB/AutoWhiteBalance.ini
|
|
||||||
sed -i 's/bGTREnable = 0/bGTREnable = 1/' \
|
|
||||||
$BIN_DIR/Resource/ISP/0/pqtable_ispe_Config.cfg
|
|
||||||
sed -i 's/bGTREnable = 0/bGTREnable = 1/' \
|
|
||||||
$BIN_DIR/Resource/ISP/1/pqtable_ispe_Config.cfg
|
|
||||||
echo " dwStatisticsSrcType=$(grep dwStatisticsSrcType $BIN_DIR/Resource/AWB/AutoWhiteBalance.ini | head -1)"
|
|
||||||
echo " bGTREnable=$(grep bGTREnable $BIN_DIR/Resource/ISP/0/pqtable_ispe_Config.cfg)"
|
|
||||||
echo "=== 完成,之後重開機不需要再執行 ==="
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$1" = "--setup" ]; then
|
|
||||||
one_time_setup
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── 停止舊的 firmware ─────────────────────────────────────────────────────────
|
|
||||||
echo "=== 停止舊 firmware ==="
|
|
||||||
killall -9 kp_firmware_host_stream 2>/dev/null || true
|
|
||||||
killall -9 rtsps 2>/dev/null || true
|
|
||||||
sleep 1
|
|
||||||
rm -f /dev/shm/*
|
|
||||||
|
|
||||||
# ── 下載新 binary ─────────────────────────────────────────────────────────────
|
|
||||||
echo "=== 下載 firmware binary ==="
|
|
||||||
wget -q "$HOST_URL/kp_firmware_host_stream" -O $FW
|
|
||||||
chmod +x $FW
|
|
||||||
echo " $FW: $(ls -lh $FW | awk '{print $5, $6, $7, $8}')"
|
|
||||||
|
|
||||||
# ── 下載新 INI(保留裝置端的 fusion_cfg / ISP 設定)─────────────────────────
|
|
||||||
echo "=== 下載 INI ==="
|
|
||||||
wget -q "$HOST_URL/host_stream.ini" -O $INI
|
|
||||||
echo " fusion_cfg: $(grep '^fusion_cfg' $INI || echo '(not set!)')"
|
|
||||||
|
|
||||||
# ── 下載 demo_rtsp.sh(確保裝置上版本與 host 一致)────────────────────────────
|
|
||||||
echo "=== 下載 demo_rtsp.sh ==="
|
|
||||||
wget -q "$HOST_URL/demo_rtsp.sh" -O $BIN_DIR/ini/demo_rtsp.sh
|
|
||||||
chmod +x $BIN_DIR/ini/demo_rtsp.sh
|
|
||||||
|
|
||||||
# ── 啟動 RTSP demo ────────────────────────────────────────────────────────────
|
|
||||||
echo "=== 啟動 RTSP demo ==="
|
|
||||||
cd $BIN_DIR
|
|
||||||
sh ./ini/demo_rtsp.sh
|
|
||||||
Binary file not shown.
BIN
build/fec_api.o
BIN
build/fec_api.o
Binary file not shown.
Binary file not shown.
@ -1,99 +0,0 @@
|
|||||||
[sensor]
|
|
||||||
sensor_cfg = "./Resource/VIC/0/imx662_1920x1080_ch0.cfg"
|
|
||||||
fusion_cfg = "./Resource/VIC/1/imx662_1920x1080_ch1.cfg"
|
|
||||||
autoscene_config = "./Resource/AutoScene/autoscene_conf.cfg"
|
|
||||||
fec_calibrate_path = "./ini/fec_calibrate.ini"
|
|
||||||
fec_conf_path = "./ini/fec_conf.ini"
|
|
||||||
fec_mode = 4 # 0: Original, 1: 1 Region, 2: 180 all direction, 3: 180 one Direction, 4: 180 two direction, 5: PT Mode
|
|
||||||
initial_fec_app_type = 0 # 0: ceiling, 1: table, 2: wall
|
|
||||||
eis_enable = 1
|
|
||||||
|
|
||||||
[nnm]
|
|
||||||
ModelPath = "nef/STDC04012026_models_630.nef"
|
|
||||||
ModelId = 32769 # KNERON_YOLOV5S_COCO80_640_640_3 (YOLO v5s)
|
|
||||||
JobId = 200 # KDP2_INF_ID_APP_YOLO
|
|
||||||
InferenceStream = 1 # Inference stream index (use stream1: 640x640)
|
|
||||||
Threshold = 0.5 # for yolo only(JobId = 11)
|
|
||||||
Fps = 25 # Image input fps for NPU inference
|
|
||||||
GetImageBufMode = 0 # 0: block mode 1: non-block mode
|
|
||||||
RoiEnable = 0 # Enable ROI for nnm detect
|
|
||||||
RoiX = 0 # ROI start x
|
|
||||||
RoiY = 0 # ROI start y
|
|
||||||
DrawBoxEnable = 1 # draw object bounding box on stream0 (also enables STDC seg overlay)
|
|
||||||
OnlyPerson = 1 # only draw person bounding box when DrawBoxEnable
|
|
||||||
#(so far for yolo only, JobId = 11)
|
|
||||||
DrawOnResize = 0; # If InferenceStream is 0. This setting needs to be enabled. The box will be drawn on all resize streams.
|
|
||||||
verbose_log = 0 # 0: quiet (warnings only, ~30B/s), 1: per-frame class ratios every 20 frames (~1KB/s). Toggle from web UI.
|
|
||||||
|
|
||||||
NnmSource = 0 # 0: run host_stream independently, 1: run with streamer
|
|
||||||
ssm_name = "vsrc_ssm_ifp_0" # If NnmSource is 1. This setting needs to be set. The name is automaticlly create by streamer.
|
|
||||||
|
|
||||||
[voc]
|
|
||||||
voc_enable = 1 #enable Video Output Component
|
|
||||||
VocWidth = 1920 #video output width
|
|
||||||
VocHeight = 1080 #video output height
|
|
||||||
PixFmt = NV12 # HDMI input format for VOC: NV12 / NV21 / YM12
|
|
||||||
|
|
||||||
[streamer]
|
|
||||||
StreamCount = 2 #Stream amount
|
|
||||||
MemType = 0 #Encode buffer type, 0(SRB mode)/1(SCM mode)
|
|
||||||
|
|
||||||
#stream0 is the main stream which has the same resolution as the sensor.
|
|
||||||
[stream0]
|
|
||||||
Codec = 0 #VMF_VENC_CODEC_TYPE_H264
|
|
||||||
Width = 1920 #The image width of stream0
|
|
||||||
Height = 1080 #The image height of stream0
|
|
||||||
FPS = 25 #The frame rate of stream0
|
|
||||||
QP = 25 #The base value of the quantization parameter. Its range is from 0 to 51.
|
|
||||||
Bitrate = 2000000 #The average bitrate of the encoded stream. Its range is from 30,000 to 700,000,000 bps.
|
|
||||||
PIQ = 0 #PIQ setting. Reduce the difference of QP between intra and inter frame. Default: 0 (disable).
|
|
||||||
GOP = 50 #Group of pictures. It specifies the number of frames between two intra frames. The maximum GOP value is 600.
|
|
||||||
Virt_I_Interval = 0 #Virtual intra frame interval. 0:disable, range: 1 ~ (gop-1)
|
|
||||||
KeepFrameRatio = 0 # This option is valid only on resized stream.
|
|
||||||
EncodeBufferSize = 6291456 # 6M: 6*1024*1024 = 6291456
|
|
||||||
EncodeBufferAmount = 3 # (SRB only)
|
|
||||||
|
|
||||||
[stream1]
|
|
||||||
Codec = 0 #VMF_VENC_CODEC_TYPE_H264
|
|
||||||
Width = 724 #The image width of stream1 (STDC model training resolution)
|
|
||||||
Height = 362 #The image height of stream1 (STDC model training resolution)
|
|
||||||
FPS = 25 #The frame rate of stream0
|
|
||||||
QP = 25 #The base value of the quantization parameter. Its range is from 0 to 51.
|
|
||||||
Bitrate = 2000000 #The average bitrate of the encoded stream. Its range is from 30,000 to 700,000,000 bps.
|
|
||||||
PIQ = 0 #PIQ setting. Reduce the difference of QP between intra and inter frame. Default: 0 (disable).
|
|
||||||
GOP = 50 #Group of pictures. It specifies the number of frames between two intra frames. The maximum GOP value is 600.
|
|
||||||
Virt_I_Interval = 0 #Virtual intra frame interval. 0:disable, range: 1 ~ (gop-1)
|
|
||||||
KeepFrameRatio = 0 # This option is valid only on resized stream.
|
|
||||||
EncodeBufferSize = 2097152 # 2M: 2*1024*1024 = 2097152
|
|
||||||
EncodeBufferAmount = 3 # (SRB only)
|
|
||||||
|
|
||||||
[stream2]
|
|
||||||
Codec = 0 #VMF_VENC_CODEC_TYPE_H264
|
|
||||||
Width = 640 #The image width of stream0
|
|
||||||
Height = 480 #The image height of stream0
|
|
||||||
FPS = 25 #The frame rate of stream0
|
|
||||||
QP = 25 #The base value of the quantization parameter. Its range is from 0 to 51.
|
|
||||||
Bitrate = 2000000 #The average bitrate of the encoded stream. Its range is from 30,000 to 700,000,000 bps.
|
|
||||||
PIQ = 0 #PIQ setting. Reduce the difference of QP between intra and inter frame. Default: 0 (disable).
|
|
||||||
GOP = 50 #Group of pictures. It specifies the number of frames between two intra frames. The maximum GOP value is 600.
|
|
||||||
Virt_I_Interval = 0 #Virtual intra frame interval. 0:disable, range: 1 ~ (gop-1)
|
|
||||||
KeepFrameRatio = 0 # This option is valid only on resized stream.
|
|
||||||
EncodeBufferSize = 2097152 # 2M: 2*1024*1024 = 2097152
|
|
||||||
EncodeBufferAmount = 3 # (SRB only)
|
|
||||||
|
|
||||||
[capture]
|
|
||||||
# Legacy single-JPEG capture (kept for reference, not used by event_recorder)
|
|
||||||
enable = 0
|
|
||||||
http_url = http://192.168.0.114:8081/api/upload
|
|
||||||
cooldown_ms = 1000
|
|
||||||
|
|
||||||
[event]
|
|
||||||
# Violation event recorder — two-channel: JSON (BT/iPad) + tar.gz (Allxon OOB)
|
|
||||||
enable = 1
|
|
||||||
bt_uart_dev = /dev/ttyS1 # Channel A: UART device for DX-BT24 BLE module → iPad
|
|
||||||
bt_at_probe = 0 # 0: skip AT commands (normal); 1: one-time baud upgrade (factory/reset only)
|
|
||||||
upload_url = http://192.168.0.114:8081/api/golf.cgi # Channel B: tar.gz upload (OOB/cloud path)
|
|
||||||
# Production: http://192.168.0.99/api/golf.cgi
|
|
||||||
sd_path = /tmp/sdcard/events # SD card event archive path
|
|
||||||
sd_max_mb = 7168 # 7 GB — delete oldest when exceeded
|
|
||||||
upload_delay_ms = 0 # 0: upload immediately after tar.gz built (tar is built after event ends, no extra delay needed)
|
|
||||||
BIN
build/kCurl
BIN
build/kCurl
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,33 +0,0 @@
|
|||||||
#ifndef BT_UART_H
|
|
||||||
#define BT_UART_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* bt_uart — DX-BT24 Bluetooth module UART driver
|
|
||||||
*
|
|
||||||
* The DX-BT24 is a UART-transparent BLE module. Whatever is written to the
|
|
||||||
* serial port is forwarded verbatim over BLE to the connected iOS/Android app.
|
|
||||||
*
|
|
||||||
* Usage (normal operation — module already at 115200):
|
|
||||||
* bt_uart_init("/dev/ttyS1", 0) — do_at_probe=0: skip AT commands
|
|
||||||
* bt_uart_send_json(json_str) — every event, thread-safe
|
|
||||||
* bt_uart_close() — shutdown (optional)
|
|
||||||
*
|
|
||||||
* First-time / factory-reset setup (module at factory default 9600):
|
|
||||||
* bt_uart_init("/dev/ttyS1", 1) — do_at_probe=1: negotiate baud via AT
|
|
||||||
* After success, set bt_at_probe = 0 in INI — never probe again.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Open and configure the UART to the BT module.
|
|
||||||
* dev: device path, e.g. "/dev/ttyS1" (NULL or "" = disabled).
|
|
||||||
* do_at_probe: 0 = open directly at 115200 (normal operation, no AT sent).
|
|
||||||
* 1 = run AT baud negotiation first (one-time setup only).
|
|
||||||
* Returns 0 on success, -1 on failure / disabled. */
|
|
||||||
int bt_uart_init(const char *dev, int do_at_probe);
|
|
||||||
|
|
||||||
/* Write JSON bytes to the BT module (no CRLF added). Thread-safe. No-op if not init'd. */
|
|
||||||
void bt_uart_send_json(const char *json);
|
|
||||||
|
|
||||||
/* Close the UART fd and release resources. */
|
|
||||||
void bt_uart_close(void);
|
|
||||||
|
|
||||||
#endif /* BT_UART_H */
|
|
||||||
@ -7,7 +7,7 @@
|
|||||||
* event_recorder — golf cart violation event logger
|
* event_recorder — golf cart violation event logger
|
||||||
*
|
*
|
||||||
* Two channels:
|
* Two channels:
|
||||||
* A) Real-time JSON → UART → DX-BT24 BLE → iPad (bt_uart_send_json)
|
* A) Real-time JSON → POST <pc_url>/api/event (iPad / BLE path)
|
||||||
* B) tar.gz archive → POST <upload_url> (OOB / cloud path)
|
* B) tar.gz archive → POST <upload_url> (OOB / cloud path)
|
||||||
* Simulation: http://192.168.0.114:8081/api/upload
|
* Simulation: http://192.168.0.114:8081/api/upload
|
||||||
* Production: http://192.168.0.99/api/golf.cgi
|
* Production: http://192.168.0.99/api/golf.cgi
|
||||||
@ -21,9 +21,9 @@
|
|||||||
* event_recorder_provide_frame() — every frame, from app_header_send_inference
|
* event_recorder_provide_frame() — every frame, from app_header_send_inference
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Call once after INI is loaded (before VMF starts).
|
/* Call once after INI is loaded (before VMF starts). */
|
||||||
* bt_uart_init() must be called before this. */
|
void event_recorder_init(const char *pc_url,
|
||||||
void event_recorder_init(const char *upload_url,
|
const char *upload_url,
|
||||||
const char *sd_path,
|
const char *sd_path,
|
||||||
int sd_max_mb,
|
int sd_max_mb,
|
||||||
int upload_delay_ms,
|
int upload_delay_ms,
|
||||||
|
|||||||
@ -88,11 +88,10 @@ http_url = http://192.168.0.114:8081/api/upload
|
|||||||
cooldown_ms = 1000
|
cooldown_ms = 1000
|
||||||
|
|
||||||
[event]
|
[event]
|
||||||
# Violation event recorder — two-channel: JSON (BT/iPad) + tar.gz (Allxon OOB)
|
# Violation event recorder — two-channel: JSON (iPad) + tar.gz (Allxon OOB)
|
||||||
enable = 1
|
enable = 1
|
||||||
bt_uart_dev = /dev/ttyS1 # Channel A: UART device for DX-BT24 BLE module → iPad
|
pc_url = http://192.168.0.114:8081 # Channel A: PC mock server (iPad/BLE path)
|
||||||
bt_at_probe = 0 # 0: skip AT commands (normal); 1: one-time baud upgrade (factory/reset only)
|
upload_url = http://192.168.0.114:8081/api/upload # Channel B: tar.gz upload (OOB/cloud path)
|
||||||
upload_url = http://192.168.0.114:8081/api/golf.cgi # Channel B: tar.gz upload (OOB/cloud path)
|
|
||||||
# Production: http://192.168.0.99/api/golf.cgi
|
# Production: http://192.168.0.99/api/golf.cgi
|
||||||
sd_path = /tmp/sdcard/events # SD card event archive path
|
sd_path = /tmp/sdcard/events # SD card event archive path
|
||||||
sd_max_mb = 7168 # 7 GB — delete oldest when exceeded
|
sd_max_mb = 7168 # 7 GB — delete oldest when exceeded
|
||||||
|
|||||||
@ -1,279 +0,0 @@
|
|||||||
/*
|
|
||||||
* bt_uart.c — DX-BT24 Bluetooth module UART driver for KL630
|
|
||||||
*
|
|
||||||
* Normal operation (bt_at_probe = 0 in INI):
|
|
||||||
* Open directly at 115200 blocking (VMIN=1). No AT commands sent.
|
|
||||||
* The module's baud rate is persistent across power cycles once set.
|
|
||||||
*
|
|
||||||
* First-time / factory-reset setup (bt_at_probe = 1 in INI):
|
|
||||||
* Call bt_uart_probe_and_upgrade() which handles the 9600→115200 upgrade:
|
|
||||||
* 1. Try AT+BAUD at 115200 — if response, already configured.
|
|
||||||
* 2. If no response: try AT+BAUD at 9600 — if response, send AT+BAUD7
|
|
||||||
* then AT+RESET to lock in the new baud rate.
|
|
||||||
* After this one-time step, set bt_at_probe = 0 in INI so AT commands are
|
|
||||||
* never sent again (avoids forwarding AT strings to connected BLE clients).
|
|
||||||
*
|
|
||||||
* bt_uart_send_json() is fully non-blocking: it enqueues the message into an
|
|
||||||
* internal FIFO and returns immediately. A dedicated writer thread drains the
|
|
||||||
* queue and performs the actual UART write, so callers are never stalled by IO.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "bt_uart.h"
|
|
||||||
|
|
||||||
/* ── UART fd ─────────────────────────────────────────────────────────────── */
|
|
||||||
static int s_bt_fd = -1;
|
|
||||||
|
|
||||||
/* ── Message queue ───────────────────────────────────────────────────────── */
|
|
||||||
typedef struct BtMsg {
|
|
||||||
char *json;
|
|
||||||
struct BtMsg *next;
|
|
||||||
} BtMsg;
|
|
||||||
|
|
||||||
static BtMsg *s_q_head = NULL;
|
|
||||||
static BtMsg *s_q_tail = NULL;
|
|
||||||
static pthread_mutex_t s_q_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static pthread_cond_t s_q_cond = PTHREAD_COND_INITIALIZER;
|
|
||||||
static pthread_t s_writer_tid;
|
|
||||||
static volatile int s_running = 0;
|
|
||||||
|
|
||||||
/* ── Internal: write all bytes to fd ────────────────────────────────────── */
|
|
||||||
static void uart_write_all(const char *data, size_t len)
|
|
||||||
{
|
|
||||||
size_t off = 0;
|
|
||||||
while (off < len) {
|
|
||||||
ssize_t n = write(s_bt_fd, data + off, len - off);
|
|
||||||
if (n <= 0) { perror("[BT] write"); return; }
|
|
||||||
off += (size_t)n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Writer thread: drains queue, does actual IO ─────────────────────────── */
|
|
||||||
static void *bt_writer_thread(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
while (1) {
|
|
||||||
pthread_mutex_lock(&s_q_mtx);
|
|
||||||
while (!s_q_head && s_running)
|
|
||||||
pthread_cond_wait(&s_q_cond, &s_q_mtx);
|
|
||||||
|
|
||||||
if (!s_running && !s_q_head) {
|
|
||||||
pthread_mutex_unlock(&s_q_mtx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
BtMsg *msg = s_q_head;
|
|
||||||
if (msg) {
|
|
||||||
s_q_head = msg->next;
|
|
||||||
if (!s_q_head) s_q_tail = NULL;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&s_q_mtx);
|
|
||||||
|
|
||||||
if (msg) {
|
|
||||||
if (s_bt_fd >= 0)
|
|
||||||
uart_write_all(msg->json, strlen(msg->json));
|
|
||||||
free(msg->json);
|
|
||||||
free(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Internal: open + configure serial port ──────────────────────────────── */
|
|
||||||
static int open_uart(const char *dev, speed_t baud, int vmin, int vtime)
|
|
||||||
{
|
|
||||||
int fd = open(dev, O_RDWR | O_NOCTTY);
|
|
||||||
if (fd < 0) { perror("[BT] open uart"); return -1; }
|
|
||||||
|
|
||||||
struct termios opts;
|
|
||||||
if (tcgetattr(fd, &opts) != 0) {
|
|
||||||
perror("[BT] tcgetattr");
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cfmakeraw(&opts);
|
|
||||||
cfsetispeed(&opts, baud);
|
|
||||||
cfsetospeed(&opts, baud);
|
|
||||||
|
|
||||||
opts.c_cflag |= (CLOCAL | CREAD);
|
|
||||||
opts.c_cflag &= ~PARENB;
|
|
||||||
opts.c_cflag &= ~CSTOPB;
|
|
||||||
opts.c_cflag &= ~CSIZE;
|
|
||||||
opts.c_cflag |= CS8;
|
|
||||||
opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
|
||||||
opts.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | INLCR);
|
|
||||||
opts.c_oflag &= ~OPOST;
|
|
||||||
opts.c_cc[VMIN] = (cc_t)vmin;
|
|
||||||
opts.c_cc[VTIME] = (cc_t)vtime;
|
|
||||||
|
|
||||||
if (tcsetattr(fd, TCSANOW, &opts) != 0) {
|
|
||||||
perror("[BT] tcsetattr");
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── One-time AT baud setup (call only when bt_at_probe = 1 in INI) ─────── */
|
|
||||||
static void bt_uart_probe_and_upgrade(const char *dev)
|
|
||||||
{
|
|
||||||
char buf[128] = {0};
|
|
||||||
int n;
|
|
||||||
|
|
||||||
/* Try AT+BAUD at 115200 first */
|
|
||||||
int fd = open_uart(dev, B115200, 0, 10); /* VTIME=10 → 1s timeout */
|
|
||||||
if (fd < 0) return;
|
|
||||||
write(fd, "AT+BAUD\r\n", 9);
|
|
||||||
n = read(fd, buf, sizeof(buf) - 1);
|
|
||||||
buf[n > 0 ? n : 0] = '\0';
|
|
||||||
printf("[BT] AT probe @ 115200 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
if (n > 0) {
|
|
||||||
printf("[BT] module already at 115200 — no upgrade needed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No response at 115200 — try 9600 */
|
|
||||||
fd = open_uart(dev, B9600, 0, 10);
|
|
||||||
if (fd < 0) return;
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
write(fd, "AT+BAUD\r\n", 9);
|
|
||||||
n = read(fd, buf, sizeof(buf) - 1);
|
|
||||||
printf("[BT] AT probe @ 9600 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
|
||||||
|
|
||||||
if (n == 0) {
|
|
||||||
/* No response at either baud — cannot upgrade; proceed anyway */
|
|
||||||
printf("[BT] WARNING: no AT response at 9600 or 115200\n");
|
|
||||||
close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Module is at 9600 — send AT+BAUD7 to switch to 115200.
|
|
||||||
* The module switches baud immediately after responding; close this fd
|
|
||||||
* before sending any further commands. */
|
|
||||||
printf("[BT] upgrading module from 9600 to 115200\n");
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
write(fd, "AT+BAUD7\r\n", 10);
|
|
||||||
usleep(100000); /* 100 ms: wait for ack + baud switch */
|
|
||||||
n = read(fd, buf, sizeof(buf) - 1);
|
|
||||||
printf("[BT] AT+BAUD7 => %d byte(s): %s\n", n, n > 0 ? buf : "(none)");
|
|
||||||
close(fd);
|
|
||||||
usleep(300000); /* 300 ms: let module stabilise at 115200 */
|
|
||||||
|
|
||||||
/* Reopen at 115200 and reset so the new baud persists across power cycles */
|
|
||||||
fd = open_uart(dev, B115200, 0, 10);
|
|
||||||
if (fd < 0) return;
|
|
||||||
write(fd, "AT+RESET\r\n", 10);
|
|
||||||
usleep(100000);
|
|
||||||
read(fd, buf, sizeof(buf) - 1);
|
|
||||||
close(fd);
|
|
||||||
usleep(500000); /* 500 ms: wait for module to reboot */
|
|
||||||
printf("[BT] upgrade complete — set bt_at_probe = 0 in INI to skip AT on next boot\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Public API ───────────────────────────────────────────────────────────── */
|
|
||||||
|
|
||||||
int bt_uart_init(const char *dev, int do_at_probe)
|
|
||||||
{
|
|
||||||
if (!dev || !*dev) {
|
|
||||||
printf("[BT] bt_uart_init: no device configured — BT disabled\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (do_at_probe) {
|
|
||||||
/* One-time factory setup: negotiate baud rate via AT commands */
|
|
||||||
bt_uart_probe_and_upgrade(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open at 115200, blocking (VMIN=1) for normal operation */
|
|
||||||
int fd = open_uart(dev, B115200, 1, 0);
|
|
||||||
if (fd < 0) {
|
|
||||||
printf("[BT] bt_uart_init: open at 115200 failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flush any residual bytes */
|
|
||||||
tcflush(fd, TCIOFLUSH);
|
|
||||||
|
|
||||||
s_bt_fd = fd;
|
|
||||||
|
|
||||||
/* Start dedicated writer thread */
|
|
||||||
s_running = 1;
|
|
||||||
if (pthread_create(&s_writer_tid, NULL, bt_writer_thread, NULL) != 0) {
|
|
||||||
perror("[BT] pthread_create writer");
|
|
||||||
close(fd);
|
|
||||||
s_bt_fd = -1;
|
|
||||||
s_running = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
pthread_setname_np(s_writer_tid, "bt_writer");
|
|
||||||
|
|
||||||
printf("[BT] bt_uart_init OK: %s @ 115200 (at_probe=%d)\n", dev, do_at_probe);
|
|
||||||
|
|
||||||
/* Diagnostic ping — confirms UART→BLE channel is alive.
|
|
||||||
* No \r\n: BLE is packet-based; delimiter not needed and causes an extra
|
|
||||||
* empty notification in nRF Connect. */
|
|
||||||
bt_uart_send_json("{\"class\":\"boot\",\"level\":0}");
|
|
||||||
printf("[BT] boot ping queued\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_uart_send_json(const char *json)
|
|
||||||
{
|
|
||||||
if (s_bt_fd < 0 || !s_running || !json || !*json) return;
|
|
||||||
|
|
||||||
BtMsg *msg = (BtMsg *)malloc(sizeof(BtMsg));
|
|
||||||
if (!msg) return;
|
|
||||||
msg->json = strdup(json);
|
|
||||||
msg->next = NULL;
|
|
||||||
if (!msg->json) { free(msg); return; }
|
|
||||||
|
|
||||||
pthread_mutex_lock(&s_q_mtx);
|
|
||||||
if (s_q_tail) {
|
|
||||||
s_q_tail->next = msg;
|
|
||||||
s_q_tail = msg;
|
|
||||||
} else {
|
|
||||||
s_q_head = s_q_tail = msg;
|
|
||||||
}
|
|
||||||
pthread_cond_signal(&s_q_cond);
|
|
||||||
pthread_mutex_unlock(&s_q_mtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_uart_close(void)
|
|
||||||
{
|
|
||||||
if (!s_running) return;
|
|
||||||
|
|
||||||
/* Signal writer thread to exit after draining remaining messages */
|
|
||||||
pthread_mutex_lock(&s_q_mtx);
|
|
||||||
s_running = 0;
|
|
||||||
pthread_cond_signal(&s_q_cond);
|
|
||||||
pthread_mutex_unlock(&s_q_mtx);
|
|
||||||
|
|
||||||
pthread_join(s_writer_tid, NULL);
|
|
||||||
|
|
||||||
if (s_bt_fd >= 0) {
|
|
||||||
close(s_bt_fd);
|
|
||||||
s_bt_fd = -1;
|
|
||||||
printf("[BT] uart closed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drain any leftover queue entries */
|
|
||||||
BtMsg *m = s_q_head;
|
|
||||||
while (m) {
|
|
||||||
BtMsg *next = m->next;
|
|
||||||
free(m->json);
|
|
||||||
free(m);
|
|
||||||
m = next;
|
|
||||||
}
|
|
||||||
s_q_head = s_q_tail = NULL;
|
|
||||||
}
|
|
||||||
@ -4,8 +4,7 @@
|
|||||||
* Golf cart violation event recorder for KL630.
|
* Golf cart violation event recorder for KL630.
|
||||||
*
|
*
|
||||||
* Channel A (iPad / BLE path):
|
* Channel A (iPad / BLE path):
|
||||||
* Write JSON to UART → DX-BT24 BLE module → iPad on every level change.
|
* POST JSON to /api/event on every level change.
|
||||||
* bt_uart_init() must be called before event_recorder_init().
|
|
||||||
*
|
*
|
||||||
* Channel B (OOB / cloud path):
|
* Channel B (OOB / cloud path):
|
||||||
* After event ends, wait upload_delay_ms, then:
|
* After event ends, wait upload_delay_ms, then:
|
||||||
@ -37,23 +36,23 @@
|
|||||||
#include <vmf/video_source.h>
|
#include <vmf/video_source.h>
|
||||||
|
|
||||||
#include "event_recorder.h"
|
#include "event_recorder.h"
|
||||||
#include "bt_uart.h"
|
|
||||||
#include "stdc_post_process.h" /* THR_*_COLLISION constants */
|
#include "stdc_post_process.h" /* THR_*_COLLISION constants */
|
||||||
|
|
||||||
/* ── External (from kdp2_host_stream.c) ──────────────────────────────────── */
|
/* ── External (from kdp2_host_stream.c) ──────────────────────────────────── */
|
||||||
extern VMF_VSRC_HANDLE_T *g_ptVsrcHandle;
|
extern VMF_VSRC_HANDLE_T *g_ptVsrcHandle;
|
||||||
|
|
||||||
/* ── Config ──────────────────────────────────────────────────────────────── */
|
/* ── Config ──────────────────────────────────────────────────────────────── */
|
||||||
/* Channel A: JSON events → BT UART → iPad (via DX-BT24 BLE module)
|
/* Channel A (JSON events → iPad path) */
|
||||||
* Initialized by bt_uart_init() in kp_firmware.c. No local config needed. */
|
static char s_pc_host[64] = "192.168.0.114";
|
||||||
|
static int s_pc_port = 8081;
|
||||||
|
|
||||||
/* Channel B (tar.gz upload → OOB / cloud path)
|
/* Channel B (tar.gz upload → OOB / cloud path)
|
||||||
* Same endpoint as capture JPG upload (golf.cgi), just posting tar.gz.
|
* Same endpoint as capture JPG upload (golf.cgi), just posting tar.gz.
|
||||||
* Simulation: http://192.168.0.114:8081/api/upload
|
* Simulation: http://192.168.0.114:8081/api/upload
|
||||||
* Production: http://192.168.0.99/api/golf.cgi */
|
* Production: http://192.168.0.99/api/golf.cgi */
|
||||||
static char s_up_host[64] = "192.168.0.99";
|
static char s_up_host[64] = "192.168.0.114";
|
||||||
static int s_up_port = 80;
|
static int s_up_port = 8081;
|
||||||
static char s_up_path[128] = "/api/golf.cgi";
|
static char s_up_path[128] = "/api/upload";
|
||||||
|
|
||||||
static char s_sd_path[256] = "/tmp/sdcard/events";
|
static char s_sd_path[256] = "/tmp/sdcard/events";
|
||||||
static long long s_sd_max_bytes = (long long)7 * 1024 * 1024 * 1024; /* 7 GB — must be long long on 32-bit ARM */
|
static long long s_sd_max_bytes = (long long)7 * 1024 * 1024 * 1024; /* 7 GB — must be long long on 32-bit ARM */
|
||||||
@ -114,7 +113,7 @@ static int g_last_pond = 0;
|
|||||||
static int g_last_tree = 0;
|
static int g_last_tree = 0;
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
* URL / network helpers (Channel B only)
|
* URL / network helpers
|
||||||
* ═══════════════════════════════════════════════════════════════════════════ */
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
static void parse_url_into(const char *url,
|
static void parse_url_into(const char *url,
|
||||||
@ -145,6 +144,11 @@ static void parse_url_into(const char *url,
|
|||||||
if (host) snprintf(host, host_n, "%s", hostport);
|
if (host) snprintf(host, host_n, "%s", hostport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_url(const char *url)
|
||||||
|
{
|
||||||
|
parse_url_into(url, s_pc_host, sizeof(s_pc_host), &s_pc_port, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int open_socket_to(const char *host, int port)
|
static int open_socket_to(const char *host, int port)
|
||||||
{
|
{
|
||||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@ -163,6 +167,31 @@ static int open_socket_to(const char *host, int port)
|
|||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int open_socket(void) { return open_socket_to(s_pc_host, s_pc_port); }
|
||||||
|
|
||||||
|
/* ── Channel A: POST JSON to /api/event ──────────────────────────────────── */
|
||||||
|
static void http_post_json(const char *json)
|
||||||
|
{
|
||||||
|
int sock = open_socket();
|
||||||
|
if (sock < 0) {
|
||||||
|
printf("[EVT] JSON post: connect failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char hdr[256];
|
||||||
|
int hdr_len = snprintf(hdr, sizeof(hdr),
|
||||||
|
"POST /api/event HTTP/1.0\r\n"
|
||||||
|
"Host: %s\r\n"
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Content-Length: %zu\r\n"
|
||||||
|
"Connection: close\r\n\r\n",
|
||||||
|
s_pc_host, strlen(json));
|
||||||
|
send(sock, hdr, hdr_len, 0);
|
||||||
|
send(sock, json, strlen(json), 0);
|
||||||
|
char resp[128] = {0};
|
||||||
|
recv(sock, resp, sizeof(resp) - 1, 0);
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Channel B: POST tar.gz via kCurl (same method as golf.cgi JPEG upload) ── */
|
/* ── Channel B: POST tar.gz via kCurl (same method as golf.cgi JPEG upload) ── */
|
||||||
/* kCurl lives in the firmware bin directory — use absolute path so CWD doesn't matter */
|
/* kCurl lives in the firmware bin directory — use absolute path so CWD doesn't matter */
|
||||||
#define KCURL_PATH "/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/kCurl"
|
#define KCURL_PATH "/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin/kCurl"
|
||||||
@ -191,6 +220,44 @@ static void http_post_file(const char *filepath)
|
|||||||
printf("[EVT] upload: %s sent OK\n", basename);
|
printf("[EVT] upload: %s sent OK\n", basename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── GET /api/time → settimeofday ────────────────────────────────────────── */
|
||||||
|
static void http_sync_time(void)
|
||||||
|
{
|
||||||
|
int sock = open_socket();
|
||||||
|
if (sock < 0) {
|
||||||
|
printf("[EVT] time sync: connect failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char req[128];
|
||||||
|
int rlen = snprintf(req, sizeof(req),
|
||||||
|
"GET /api/time HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
||||||
|
s_pc_host);
|
||||||
|
send(sock, req, rlen, 0);
|
||||||
|
|
||||||
|
char resp[512] = {0};
|
||||||
|
recv(sock, resp, sizeof(resp) - 1, 0);
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
/* Find "unix": in response body */
|
||||||
|
char *p = strstr(resp, "\"unix\":");
|
||||||
|
if (!p) {
|
||||||
|
printf("[EVT] time sync: parse failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p += 7;
|
||||||
|
while (*p == ' ') p++;
|
||||||
|
long unix_ts = atol(p);
|
||||||
|
if (unix_ts <= 0) {
|
||||||
|
printf("[EVT] time sync: bad timestamp %ld\n", unix_ts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct timeval tv = { (time_t)unix_ts, 0 };
|
||||||
|
if (settimeofday(&tv, NULL) == 0)
|
||||||
|
printf("[EVT] time synced: unix=%ld\n", unix_ts);
|
||||||
|
else
|
||||||
|
printf("[EVT] time sync: settimeofday failed (errno=%d)\n", errno);
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
* Helpers
|
* Helpers
|
||||||
* ═══════════════════════════════════════════════════════════════════════════ */
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
||||||
@ -198,8 +265,7 @@ static void http_post_file(const char *filepath)
|
|||||||
/* UTC+8 offset for Taiwan time (TZ env may not be set on embedded device) */
|
/* UTC+8 offset for Taiwan time (TZ env may not be set on embedded device) */
|
||||||
#define TZ_OFFSET_SEC (8 * 3600)
|
#define TZ_OFFSET_SEC (8 * 3600)
|
||||||
|
|
||||||
/* UTC time with Z suffix — reserved for future use */
|
/* UTC time with Z suffix — used in channel A JSON (spec requires UTC ISO 8601) */
|
||||||
__attribute__((unused))
|
|
||||||
static void now_iso_utc(char *buf, size_t n)
|
static void now_iso_utc(char *buf, size_t n)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@ -241,7 +307,7 @@ static void write_event_json(const char *work_dir, const char *event_id,
|
|||||||
char path[320];
|
char path[320];
|
||||||
snprintf(path, sizeof(path), "%s/event.json", work_dir);
|
snprintf(path, sizeof(path), "%s/event.json", work_dir);
|
||||||
FILE *f = fopen(path, "w");
|
FILE *f = fopen(path, "w");
|
||||||
if (!f) { printf("[EVT] write_event_json: fopen failed: %s\n", path); return; }
|
if (!f) return;
|
||||||
|
|
||||||
char ts[32];
|
char ts[32];
|
||||||
now_iso(ts, sizeof(ts));
|
now_iso(ts, sizeof(ts));
|
||||||
@ -351,21 +417,39 @@ static void rm_work_dir(const char *dir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
* JSON event fire — bt_uart_send_json() is non-blocking (internal queue +
|
* JSON event post (detached thread — doesn't block inference pipeline)
|
||||||
* dedicated writer thread), so we call it directly without a wrapper thread.
|
|
||||||
* ═══════════════════════════════════════════════════════════════════════════ */
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char json[512];
|
||||||
|
} JsonPostArg;
|
||||||
|
|
||||||
|
static void *json_post_thread(void *arg)
|
||||||
|
{
|
||||||
|
JsonPostArg *a = (JsonPostArg *)arg;
|
||||||
|
http_post_json(a->json);
|
||||||
|
free(a);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void fire_json_async(const char *event_id, const char *type, int level)
|
static void fire_json_async(const char *event_id, const char *type, int level)
|
||||||
{
|
{
|
||||||
(void)event_id; /* not included in BT payload — kept for caller compatibility */
|
char ts[32];
|
||||||
|
now_iso_utc(ts, sizeof(ts)); /* spec: ISO 8601 UTC with Z suffix */
|
||||||
|
|
||||||
if (!type) type = "unknown";
|
JsonPostArg *a = (JsonPostArg *)malloc(sizeof(JsonPostArg));
|
||||||
|
if (!a) return;
|
||||||
|
snprintf(a->json, sizeof(a->json),
|
||||||
|
"{\"response_type\":\"violation\","
|
||||||
|
"\"content\":{\"id\":\"%s\",\"date\":\"%s\",\"type\":\"%s\",\"level\":%d}}",
|
||||||
|
event_id, ts, type, level);
|
||||||
|
|
||||||
char json[64];
|
pthread_t t;
|
||||||
/* Compact format fits in a single BLE packet:
|
pthread_attr_t attr;
|
||||||
* e.g. {"class":"lane","level":1} = 26 bytes */
|
pthread_attr_init(&attr);
|
||||||
snprintf(json, sizeof(json), "{\"class\":\"%s\",\"level\":%d}", type, level);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
bt_uart_send_json(json); /* enqueues and returns immediately */
|
if (pthread_create(&t, &attr, json_post_thread, a) != 0) free(a);
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════════════════════
|
||||||
@ -455,10 +539,7 @@ static void launch_upload(const char *work_dir, const char *event_id,
|
|||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
if (pthread_create(&t, &attr, upload_thread, a) != 0) {
|
if (pthread_create(&t, &attr, upload_thread, a) != 0) free(a);
|
||||||
printf("[EVT] launch_upload: pthread_create failed — upload dropped id=%s\n", a->event_id);
|
|
||||||
free(a);
|
|
||||||
}
|
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,9 +599,6 @@ static void request_snap(const char *filename, const char *work_dir,
|
|||||||
snprintf(g_snap_req.work_dir, sizeof(g_snap_req.work_dir), "%s", work_dir);
|
snprintf(g_snap_req.work_dir, sizeof(g_snap_req.work_dir), "%s", work_dir);
|
||||||
snprintf(g_snap_req.event_id, sizeof(g_snap_req.event_id), "%s", event_id);
|
snprintf(g_snap_req.event_id, sizeof(g_snap_req.event_id), "%s", event_id);
|
||||||
snprintf(g_snap_req.event_type, sizeof(g_snap_req.event_type), "%s", event_type);
|
snprintf(g_snap_req.event_type, sizeof(g_snap_req.event_type), "%s", event_type);
|
||||||
} else {
|
|
||||||
printf("[EVT] snap dropped: already pending %s (new: %s/%s)\n",
|
|
||||||
g_snap_req.filename, event_id, filename);
|
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&g_snap_mtx);
|
pthread_mutex_unlock(&g_snap_mtx);
|
||||||
}
|
}
|
||||||
@ -545,7 +623,8 @@ static void grass_enter_level(int level)
|
|||||||
* Public API
|
* Public API
|
||||||
* ═══════════════════════════════════════════════════════════════════════════ */
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
void event_recorder_init(const char *upload_url,
|
void event_recorder_init(const char *pc_url,
|
||||||
|
const char *upload_url,
|
||||||
const char *sd_path,
|
const char *sd_path,
|
||||||
int sd_max_mb,
|
int sd_max_mb,
|
||||||
int upload_delay_ms,
|
int upload_delay_ms,
|
||||||
@ -555,16 +634,22 @@ void event_recorder_init(const char *upload_url,
|
|||||||
s_upload_delay_ms = (upload_delay_ms >= 0) ? upload_delay_ms : 60000;
|
s_upload_delay_ms = (upload_delay_ms >= 0) ? upload_delay_ms : 60000;
|
||||||
s_sd_max_bytes = (long long)sd_max_mb * 1024 * 1024;
|
s_sd_max_bytes = (long long)sd_max_mb * 1024 * 1024;
|
||||||
|
|
||||||
|
if (pc_url && *pc_url) parse_url(pc_url);
|
||||||
if (upload_url && *upload_url) parse_url_into(upload_url,
|
if (upload_url && *upload_url) parse_url_into(upload_url,
|
||||||
s_up_host, sizeof(s_up_host),
|
s_up_host, sizeof(s_up_host),
|
||||||
&s_up_port,
|
&s_up_port,
|
||||||
s_up_path, sizeof(s_up_path));
|
s_up_path, sizeof(s_up_path));
|
||||||
if (sd_path && *sd_path) snprintf(s_sd_path, sizeof(s_sd_path), "%s", sd_path);
|
if (sd_path && *sd_path) snprintf(s_sd_path, sizeof(s_sd_path), "%s", sd_path);
|
||||||
|
|
||||||
printf("[EVT] init: enable=%d ch_a=bt_uart ch_b=%s:%d%s sd=%s max=%dMB delay=%dms\n",
|
printf("[EVT] init: enable=%d ch_a=%s:%d ch_b=%s:%d%s sd=%s max=%dMB delay=%dms\n",
|
||||||
s_enabled,
|
s_enabled, s_pc_host, s_pc_port,
|
||||||
s_up_host, s_up_port, s_up_path,
|
s_up_host, s_up_port, s_up_path,
|
||||||
s_sd_path, sd_max_mb, s_upload_delay_ms);
|
s_sd_path, sd_max_mb, s_upload_delay_ms);
|
||||||
|
|
||||||
|
if (!s_enabled) return;
|
||||||
|
|
||||||
|
/* Sync time from PC server */
|
||||||
|
http_sync_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Recv callback: drives state machine ─────────────────────────────────── */
|
/* ── Recv callback: drives state machine ─────────────────────────────────── */
|
||||||
|
|||||||
@ -1655,13 +1655,6 @@ void *kdp2_host_stream_image_thread(void *arg)
|
|||||||
release_video_source(g_ptVsrcHandle);
|
release_video_source(g_ptVsrcHandle);
|
||||||
goto EXIT_MIPI_IMAGE_THREAD;
|
goto EXIT_MIPI_IMAGE_THREAD;
|
||||||
}
|
}
|
||||||
/* Set g_dwDrawBoxType BEFORE g_dwInitBind=1.
|
|
||||||
* VOC thread unblocks on g_dwInitBind and immediately checks g_dwDrawBoxType
|
|
||||||
* to decide which SSM pin to read from. If draw box is enabled on stream 0,
|
|
||||||
* it must see g_dwDrawBoxType=1 so it reads from VENC_VSRC_B_PIN (overlay
|
|
||||||
* output) rather than the raw ISP SSM — fixes intermittent HDMI no-overlay. */
|
|
||||||
if (pHostStreamInit->bDrawBoxEnable && pHostStreamInit->dwEncodeStreamCount > 0)
|
|
||||||
g_dwDrawBoxType = 1;
|
|
||||||
g_dwInitBind = 1;
|
g_dwInitBind = 1;
|
||||||
|
|
||||||
if (((dwInferenceWidth == g_tLayout.dwVideoWidth) && (dwInferenceHeight == g_tLayout.dwVideoHeight)) || (pHostStreamInit->bRoiEnable)) {
|
if (((dwInferenceWidth == g_tLayout.dwVideoWidth) && (dwInferenceHeight == g_tLayout.dwVideoHeight)) || (pHostStreamInit->bRoiEnable)) {
|
||||||
|
|||||||
@ -41,7 +41,6 @@
|
|||||||
#include "kdp2_host_stream.h"
|
#include "kdp2_host_stream.h"
|
||||||
#include "fec_api.h"
|
#include "fec_api.h"
|
||||||
#include "event_recorder.h"
|
#include "event_recorder.h"
|
||||||
#include "bt_uart.h"
|
|
||||||
|
|
||||||
//fifo queue buffer setting
|
//fifo queue buffer setting
|
||||||
#define IMAGE_BUFFER_COUNT 3
|
#define IMAGE_BUFFER_COUNT 3
|
||||||
@ -225,14 +224,12 @@ int loadConfig(HOST_STREAM_INIT_OPT_T* pHostStreamInit)
|
|||||||
/* --- [event] section: violation event recording + upload --- */
|
/* --- [event] section: violation event recording + upload --- */
|
||||||
{
|
{
|
||||||
int ev_enable = iniparser_getint(ini, "event:enable", 0);
|
int ev_enable = iniparser_getint(ini, "event:enable", 0);
|
||||||
const char *bt_dev = iniparser_getstring(ini, "event:bt_uart_dev", "/dev/ttyS1");
|
const char *ev_url = iniparser_getstring(ini, "event:pc_url", "http://192.168.0.114:8081");
|
||||||
int bt_at_probe = iniparser_getint(ini, "event:bt_at_probe", 0);
|
|
||||||
const char *ev_up = iniparser_getstring(ini, "event:upload_url", "http://192.168.0.114:8081/api/upload");
|
const char *ev_up = iniparser_getstring(ini, "event:upload_url", "http://192.168.0.114:8081/api/upload");
|
||||||
const char *ev_sd = iniparser_getstring(ini, "event:sd_path", "/tmp/sdcard/events");
|
const char *ev_sd = iniparser_getstring(ini, "event:sd_path", "/tmp/sdcard/events");
|
||||||
int ev_max_mb = iniparser_getint(ini, "event:sd_max_mb", 7168);
|
int ev_max_mb = iniparser_getint(ini, "event:sd_max_mb", 7168);
|
||||||
int ev_delay = iniparser_getint(ini, "event:upload_delay_ms", 60000);
|
int ev_delay = iniparser_getint(ini, "event:upload_delay_ms", 60000);
|
||||||
bt_uart_init(bt_dev, bt_at_probe);
|
event_recorder_init(ev_url, ev_up, ev_sd, ev_max_mb, ev_delay, ev_enable);
|
||||||
event_recorder_init(ev_up, ev_sd, ev_max_mb, ev_delay, ev_enable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iniparser_freedict(ini);
|
iniparser_freedict(ini);
|
||||||
@ -444,6 +441,13 @@ int main (int argc, char* argv[])
|
|||||||
VMF_NNM_Fifoq_Manager_Allocate_Buffer(IMAGE_BUFFER_COUNT, ImageBufferSize, RESULT_BUFFER_COUNT, RESULT_BUFFER_SIZE);
|
VMF_NNM_Fifoq_Manager_Allocate_Buffer(IMAGE_BUFFER_COUNT, ImageBufferSize, RESULT_BUFFER_COUNT, RESULT_BUFFER_SIZE);
|
||||||
printf("[DBG] After AllocateBuffer, ImageBufferSize=%u\n", ImageBufferSize);
|
printf("[DBG] After AllocateBuffer, ImageBufferSize=%u\n", ImageBufferSize);
|
||||||
|
|
||||||
|
printf("###################################################\n");
|
||||||
|
printf("###################################################\n");
|
||||||
|
printf("%s: func:%s,line:%d \n", __FILE__, __func__,__LINE__);
|
||||||
|
printf("###################################################\n");
|
||||||
|
printf("###################################################\n");
|
||||||
|
|
||||||
|
|
||||||
pthread_create(&task_stream_image_handle, NULL, kdp2_host_stream_image_thread, &HostStreamInit);
|
pthread_create(&task_stream_image_handle, NULL, kdp2_host_stream_image_thread, &HostStreamInit);
|
||||||
pthread_create(&task_update_result_handle, NULL, kdp2_host_update_result_thread, &HostStreamInit);
|
pthread_create(&task_update_result_handle, NULL, kdp2_host_update_result_thread, &HostStreamInit);
|
||||||
if(!HostStreamInit.dwNnmSource && HostStreamInit.dwEncodeStreamCount > 0)
|
if(!HostStreamInit.dwNnmSource && HostStreamInit.dwEncodeStreamCount > 0)
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# /usr/local/sbin/www/api/time — Allxon Bolt time CGI
|
|
||||||
# Returns current Unix timestamp in JSON.
|
|
||||||
# Same response format as the KL630 mock server GET /api/time.
|
|
||||||
#
|
|
||||||
# Response: {"unix": <epoch_seconds>}
|
|
||||||
printf "Content-Type: application/json\r\n"
|
|
||||||
printf "\r\n"
|
|
||||||
printf '{"unix": %s}' "$(date +%s)"
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# 安裝 time CGI 到 Allxon Bolt HTTP server
|
|
||||||
# 在 Bolt 上執行:sh install.sh
|
|
||||||
# 安裝後可用:GET http://192.168.0.100/api/time → {"unix": <epoch>}
|
|
||||||
cp ./content/time /usr/local/sbin/www/api/time
|
|
||||||
chmod +x /usr/local/sbin/www/api/time
|
|
||||||
echo "time CGI installed: http://192.168.0.100/api/time"
|
|
||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
HOST_URL="http://192.168.0.114:8080"
|
HOST_URL="http://192.168.0.102:8080"
|
||||||
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
|
BIN_DIR=/mnt/flash/plus/kp_firmware/kp_firmware_0/kp_firmware/bin
|
||||||
FW=/mnt/flash/vienna/kp_firmware_host_stream
|
FW=/mnt/flash/vienna/kp_firmware_host_stream
|
||||||
INI=$BIN_DIR/ini/host_stream.ini
|
INI=$BIN_DIR/ini/host_stream.ini
|
||||||
|
|||||||
@ -184,7 +184,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.send_error(400, str(e))
|
self.send_error(400, str(e))
|
||||||
|
|
||||||
# ── Channel B: tar.gz archive upload ─────────────────────────
|
# ── Channel B: tar.gz archive upload ─────────────────────────
|
||||||
elif path in ('/api/upload', '/api/golf.cgi'):
|
elif path == '/api/upload':
|
||||||
# 1) kCurl sends filename as query param: /api/upload?filename=xxx
|
# 1) kCurl sends filename as query param: /api/upload?filename=xxx
|
||||||
qs = parse_qs(urlparse(self.path).query)
|
qs = parse_qs(urlparse(self.path).query)
|
||||||
filename = (qs.get('filename') or [None])[0]
|
filename = (qs.get('filename') or [None])[0]
|
||||||
@ -205,8 +205,8 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
fp = os.path.join(UPLOAD_DIR, filename)
|
fp = os.path.join(UPLOAD_DIR, filename)
|
||||||
with open(fp, 'wb') as f:
|
with open(fp, 'wb') as f:
|
||||||
f.write(body)
|
f.write(body)
|
||||||
print(f" [CHANNEL-B] {path} saved {filename} ({len(body):,} bytes)")
|
print(f" [CHANNEL-B] saved {filename} ({len(body):,} bytes)")
|
||||||
self._json({'ok': True, 'path': path, 'filename': filename, 'bytes': len(body)})
|
self._json({'ok': True, 'filename': filename, 'bytes': len(body)})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.send_error(404)
|
self.send_error(404)
|
||||||
@ -250,7 +250,6 @@ if __name__ == '__main__':
|
|||||||
print(f" Time API : GET http://localhost:{PORT}/api/time")
|
print(f" Time API : GET http://localhost:{PORT}/api/time")
|
||||||
print(f" Event API : POST http://localhost:{PORT}/api/event")
|
print(f" Event API : POST http://localhost:{PORT}/api/event")
|
||||||
print(f" Upload API : POST http://localhost:{PORT}/api/upload")
|
print(f" Upload API : POST http://localhost:{PORT}/api/upload")
|
||||||
print(f" Golf API : POST http://localhost:{PORT}/api/golf.cgi")
|
|
||||||
print(f" Uploads : {UPLOAD_DIR}")
|
print(f" Uploads : {UPLOAD_DIR}")
|
||||||
print("=" * 55)
|
print("=" * 55)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -147,7 +147,7 @@ def build_targz(event_id: str, event_type: str, max_level: int,
|
|||||||
|
|
||||||
# ── Scenarios ─────────────────────────────────────────────────────────
|
# ── Scenarios ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def scenario_grass(server, event_id, upload_path):
|
def scenario_grass(server, event_id):
|
||||||
"""Full grass violation: L1 → L2 → L3 → L0, then upload tar.gz."""
|
"""Full grass violation: L1 → L2 → L3 → L0, then upload tar.gz."""
|
||||||
print("\n[Grass Scenario]")
|
print("\n[Grass Scenario]")
|
||||||
print("Step 0: get server time (simulates NTP from OOB Enabler)")
|
print("Step 0: get server time (simulates NTP from OOB Enabler)")
|
||||||
@ -191,10 +191,10 @@ def scenario_grass(server, event_id, upload_path):
|
|||||||
ts_fn = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
ts_fn = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
||||||
filename = f'event_{event_id}_{ts_fn}.tar.gz'
|
filename = f'event_{event_id}_{ts_fn}.tar.gz'
|
||||||
print(f"\n→ [CHANNEL B] uploading {filename} ({len(tgz):,} bytes)")
|
print(f"\n→ [CHANNEL B] uploading {filename} ({len(tgz):,} bytes)")
|
||||||
post_targz(server, upload_path, filename, tgz)
|
post_targz(server, '/api/upload', filename, tgz)
|
||||||
|
|
||||||
|
|
||||||
def scenario_hazard(server, hazard_type, event_id, upload_path):
|
def scenario_hazard(server, hazard_type, event_id):
|
||||||
"""Single-shot hazard event — no level, no tar.gz wait."""
|
"""Single-shot hazard event — no level, no tar.gz wait."""
|
||||||
labels = {'bunker': '沙坑', 'pond': '水池', 'tree': '樹木'}
|
labels = {'bunker': '沙坑', 'pond': '水池', 'tree': '樹木'}
|
||||||
label = labels.get(hazard_type, hazard_type)
|
label = labels.get(hazard_type, hazard_type)
|
||||||
@ -215,10 +215,10 @@ def scenario_hazard(server, hazard_type, event_id, upload_path):
|
|||||||
ts_fn = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
ts_fn = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
||||||
filename = f'event_{event_id}_{ts_fn}.tar.gz'
|
filename = f'event_{event_id}_{ts_fn}.tar.gz'
|
||||||
print(f"→ [CHANNEL B] uploading {filename} ({len(tgz):,} bytes)")
|
print(f"→ [CHANNEL B] uploading {filename} ({len(tgz):,} bytes)")
|
||||||
post_targz(server, upload_path, filename, tgz)
|
post_targz(server, '/api/upload', filename, tgz)
|
||||||
|
|
||||||
|
|
||||||
def scenario_person(server, event_id, upload_path):
|
def scenario_person(server, event_id):
|
||||||
"""Person detection — single-shot."""
|
"""Person detection — single-shot."""
|
||||||
print("\n[Person Scenario]")
|
print("\n[Person Scenario]")
|
||||||
get_server_time(server)
|
get_server_time(server)
|
||||||
@ -236,15 +236,13 @@ def scenario_person(server, event_id, upload_path):
|
|||||||
ts_fn = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
ts_fn = datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S')
|
||||||
filename = f'event_{event_id}_{ts_fn}.tar.gz'
|
filename = f'event_{event_id}_{ts_fn}.tar.gz'
|
||||||
print(f"→ [CHANNEL B] uploading {filename} ({len(tgz):,} bytes)")
|
print(f"→ [CHANNEL B] uploading {filename} ({len(tgz):,} bytes)")
|
||||||
post_targz(server, upload_path, filename, tgz)
|
post_targz(server, '/api/upload', filename, tgz)
|
||||||
|
|
||||||
|
|
||||||
# ── Main ───────────────────────────────────────────────────────────────
|
# ── Main ───────────────────────────────────────────────────────────────
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('--server', default=DEFAULT_SERVER)
|
parser.add_argument('--server', default=DEFAULT_SERVER)
|
||||||
parser.add_argument('--upload-path', default='/api/upload',
|
|
||||||
choices=['/api/upload', '/api/golf.cgi'])
|
|
||||||
parser.add_argument('--hazard', choices=['bunker', 'pond', 'tree'])
|
parser.add_argument('--hazard', choices=['bunker', 'pond', 'tree'])
|
||||||
parser.add_argument('--person', action='store_true')
|
parser.add_argument('--person', action='store_true')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -252,8 +250,8 @@ if __name__ == '__main__':
|
|||||||
event_id = str(int(time.time()))
|
event_id = str(int(time.time()))
|
||||||
|
|
||||||
if args.hazard:
|
if args.hazard:
|
||||||
scenario_hazard(args.server, args.hazard, event_id, args.upload_path)
|
scenario_hazard(args.server, args.hazard, event_id)
|
||||||
elif args.person:
|
elif args.person:
|
||||||
scenario_person(args.server, event_id, args.upload_path)
|
scenario_person(args.server, event_id)
|
||||||
else:
|
else:
|
||||||
scenario_grass(args.server, event_id, args.upload_path)
|
scenario_grass(args.server, event_id)
|
||||||
|
|||||||
113
web_serve.py
113
web_serve.py
@ -506,101 +506,36 @@ def api_deploy_run():
|
|||||||
return Response(generate(), mimetype="text/event-stream",
|
return Response(generate(), mimetype="text/event-stream",
|
||||||
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
|
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
|
||||||
|
|
||||||
# ── API: BT UART one-time baud setup via Telnet (SSE) ────────────────────────
|
# ── API: first-time ISP setup via Telnet (SSE) ────────────────────────────────
|
||||||
@app.route("/api/bt_setup/run")
|
@app.route("/api/setup/run")
|
||||||
def api_bt_setup_run():
|
def api_setup_run():
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
kl_ip = request.args.get("ip", cfg["kl630_ip"])
|
kl_ip = request.args.get("ip", cfg["kl630_ip"])
|
||||||
bd = BIN_DIR_DEVICE
|
bd = BIN_DIR_DEVICE
|
||||||
fw = FW_PATH_DEVICE
|
awb = f"{bd}/Resource/AWB/AutoWhiteBalance.ini"
|
||||||
restart_cmd = (f"cd {bd} && rm -f /dev/shm/* && "
|
isp0 = f"{bd}/Resource/ISP/0/pqtable_ispe_Config.cfg"
|
||||||
f"nohup sh ./ini/demo_rtsp.sh > /tmp/fw.log 2>&1 &")
|
isp1 = f"{bd}/Resource/ISP/1/pqtable_ispe_Config.cfg"
|
||||||
|
|
||||||
|
commands = [
|
||||||
|
f"sed -i 's/dwStatisticsSrcType = 0/dwStatisticsSrcType = 2/' {awb}",
|
||||||
|
f"sed -i 's/bGTREnable = 0/bGTREnable = 1/' {isp0}",
|
||||||
|
f"sed -i 's/bGTREnable = 0/bGTREnable = 1/' {isp1}",
|
||||||
|
f"grep dwStatisticsSrcType {awb}",
|
||||||
|
f"grep bGTREnable {isp0}",
|
||||||
|
]
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
yield sse("⚠ 請先關閉手機 nRF Connect 並確保 DX-BT24 未連線,再繼續!", "warn")
|
|
||||||
yield sse(f"Connecting to {kl_ip}:23 via Telnet...")
|
yield sse(f"Connecting to {kl_ip}:23 via Telnet...")
|
||||||
try:
|
try:
|
||||||
tn = _telnet_connect(kl_ip)
|
tn = _telnet_connect(kl_ip)
|
||||||
_drain_shell(tn)
|
_drain_shell(tn)
|
||||||
yield sse("Connected.", "ok")
|
yield sse("Connected.", "ok")
|
||||||
|
for cmd in commands:
|
||||||
# Step 1: kill firmware so UART is free
|
yield sse(f"$ {cmd}", "prompt")
|
||||||
yield sse("Step 1: stopping firmware...")
|
for ln in _telnet_run(tn, cmd):
|
||||||
_telnet_run(tn, "killall -9 kp_firmware_host_stream 2>/dev/null; "
|
|
||||||
"killall -9 rtsps 2>/dev/null; sleep 1; echo stopped", timeout=10)
|
|
||||||
|
|
||||||
# Step 2: probe current baud by reading UART response
|
|
||||||
yield sse("Step 2: probing module baud rate...")
|
|
||||||
probe_115200 = (
|
|
||||||
"stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; "
|
|
||||||
"cat /dev/ttyS1 > /tmp/_bt_resp.bin & CPID=$!; "
|
|
||||||
"printf 'AT+BAUD\\r\\n' > /dev/ttyS1; sleep 1; "
|
|
||||||
"kill $CPID 2>/dev/null; "
|
|
||||||
"BYTES=$(wc -c < /tmp/_bt_resp.bin 2>/dev/null || echo 0); "
|
|
||||||
"echo \"115200_bytes=$BYTES\"; "
|
|
||||||
"[ \"$BYTES\" -gt 0 ] && cat /tmp/_bt_resp.bin || true"
|
|
||||||
)
|
|
||||||
resp_115200 = _telnet_run(tn, probe_115200, timeout=8)
|
|
||||||
for ln in resp_115200:
|
|
||||||
yield sse(ln, "ok")
|
|
||||||
|
|
||||||
got_115200 = any("115200_bytes=" in ln and not ln.endswith("=0") for ln in resp_115200)
|
|
||||||
|
|
||||||
if got_115200:
|
|
||||||
yield sse("Module responded at 115200 — already configured!", "ok")
|
|
||||||
else:
|
|
||||||
yield sse("No response at 115200 — trying 9600...", "warn")
|
|
||||||
probe_9600 = (
|
|
||||||
"stty -F /dev/ttyS1 9600 raw cs8 -parenb -cstopb -echo; "
|
|
||||||
"cat /dev/ttyS1 > /tmp/_bt_resp.bin & CPID=$!; "
|
|
||||||
"printf 'AT+BAUD\\r\\n' > /dev/ttyS1; sleep 1; "
|
|
||||||
"kill $CPID 2>/dev/null; "
|
|
||||||
"BYTES=$(wc -c < /tmp/_bt_resp.bin 2>/dev/null || echo 0); "
|
|
||||||
"echo \"9600_bytes=$BYTES\"; "
|
|
||||||
"[ \"$BYTES\" -gt 0 ] && cat /tmp/_bt_resp.bin || true"
|
|
||||||
)
|
|
||||||
resp_9600 = _telnet_run(tn, probe_9600, timeout=8)
|
|
||||||
for ln in resp_9600:
|
|
||||||
yield sse(ln, "ok")
|
yield sse(ln, "ok")
|
||||||
|
|
||||||
got_9600 = any("9600_bytes=" in ln and not ln.endswith("=0") for ln in resp_9600)
|
|
||||||
|
|
||||||
if got_9600:
|
|
||||||
yield sse("Module at 9600 — sending AT+BAUD7 to upgrade...", "ok")
|
|
||||||
upgrade = (
|
|
||||||
"stty -F /dev/ttyS1 9600 raw cs8 -parenb -cstopb -echo; "
|
|
||||||
"printf 'AT+BAUD7\\r\\n' > /dev/ttyS1; sleep 0.3; "
|
|
||||||
"stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; "
|
|
||||||
"printf 'AT+RESET\\r\\n' > /dev/ttyS1; sleep 1.5; "
|
|
||||||
"echo 'upgrade_sent'"
|
|
||||||
)
|
|
||||||
for ln in _telnet_run(tn, upgrade, timeout=8):
|
|
||||||
yield sse(ln, "ok")
|
|
||||||
yield sse("AT+BAUD7 + AT+RESET sent. Module rebooting...", "ok")
|
|
||||||
else:
|
|
||||||
yield sse("No response at 9600 either.", "warn")
|
|
||||||
yield sse("Can't probe module — it may be in BLE transparent mode or disconnected.", "warn")
|
|
||||||
yield sse("Make sure phone is DISCONNECTED from DX-BT24, then run this again.", "error")
|
|
||||||
|
|
||||||
# Step 3: send test string at 115200 to verify
|
|
||||||
yield sse("Step 3: sending test ping at 115200...")
|
|
||||||
test_cmd = (
|
|
||||||
"stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; "
|
|
||||||
"printf '{\"class\":\"test\",\"level\":0}' > /dev/ttyS1; "
|
|
||||||
"echo 'ping_sent'"
|
|
||||||
)
|
|
||||||
for ln in _telnet_run(tn, test_cmd, timeout=5):
|
|
||||||
yield sse(ln, "ok")
|
|
||||||
|
|
||||||
# Step 4: restart firmware normally (bt_at_probe stays 0)
|
|
||||||
yield sse("Step 4: restarting firmware...")
|
|
||||||
_telnet_run(tn, "killall -9 kp_firmware_host_stream 2>/dev/null; "
|
|
||||||
"killall -9 rtsps 2>/dev/null; sleep 1; rm -f /dev/shm/*", timeout=10)
|
|
||||||
_telnet_run_bg(tn, restart_cmd)
|
|
||||||
yield sse("Firmware restarted.", "ok")
|
|
||||||
|
|
||||||
tn.close()
|
tn.close()
|
||||||
yield sse('完成!現在連上手機 → 訂閱 Notify → 如果看到 {"class":"test","level":0} 或 {"class":"boot","level":0} 表示成功', "ok")
|
yield sse("One-time ISP setup complete. Settings written to flash.", "ok")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield sse(f"Telnet error: {e}", "error")
|
yield sse(f"Telnet error: {e}", "error")
|
||||||
yield sse_done()
|
yield sse_done()
|
||||||
@ -1613,9 +1548,9 @@ input:checked+.slider:before{transform:translateX(16px);background:#fff}
|
|||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="5 12 12 5 19 12"/><line x1="12" y1="5" x2="12" y2="19"/></svg>
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="5 12 12 5 19 12"/><line x1="12" y1="5" x2="12" y2="19"/></svg>
|
||||||
Deploy to KL630
|
Deploy to KL630
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-warn" id="btn-bt-setup" onclick="runAction('bt_setup')">
|
<button class="btn btn-warn" id="btn-setup" onclick="runAction('setup')">
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/></svg>
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 0-14.14 0M4.93 19.07a10 10 0 0 0 14.14 0"/></svg>
|
||||||
BT 初始化
|
First-time ISP Setup
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-ghost" onclick="clearLog()">Clear Log</button>
|
<button class="btn btn-ghost" onclick="clearLog()">Clear Log</button>
|
||||||
<button class="btn btn-ghost" id="btn-autostart" onclick="runAction('autostart')">
|
<button class="btn btn-ghost" id="btn-autostart" onclick="runAction('autostart')">
|
||||||
@ -1772,14 +1707,14 @@ function runAction(action) {
|
|||||||
'&port=' + encodeURIComponent(cfg.port) +
|
'&port=' + encodeURIComponent(cfg.port) +
|
||||||
'&out_rtsp=' + (document.getElementById('out-rtsp').checked ? 1 : 0) +
|
'&out_rtsp=' + (document.getElementById('out-rtsp').checked ? 1 : 0) +
|
||||||
'&out_hdmi=' + (document.getElementById('out-hdmi').checked ? 1 : 0),
|
'&out_hdmi=' + (document.getElementById('out-hdmi').checked ? 1 : 0),
|
||||||
bt_setup: '/api/bt_setup/run?ip=' + encodeURIComponent(cfg.kl630_ip),
|
setup: '/api/setup/run?ip=' + encodeURIComponent(cfg.kl630_ip),
|
||||||
autostart: '/api/autostart/write?ip=' + encodeURIComponent(cfg.kl630_ip) +
|
autostart: '/api/autostart/write?ip=' + encodeURIComponent(cfg.kl630_ip) +
|
||||||
'&out_rtsp=' + (document.getElementById('out-rtsp').checked ? 1 : 0) +
|
'&out_rtsp=' + (document.getElementById('out-rtsp').checked ? 1 : 0) +
|
||||||
'&out_hdmi=' + (document.getElementById('out-hdmi').checked ? 1 : 0),
|
'&out_hdmi=' + (document.getElementById('out-hdmi').checked ? 1 : 0),
|
||||||
autostart_read: '/api/autostart/read?ip=' + encodeURIComponent(cfg.kl630_ip),
|
autostart_read: '/api/autostart/read?ip=' + encodeURIComponent(cfg.kl630_ip),
|
||||||
mount_sd: '/api/mount_sd?ip=' + encodeURIComponent(cfg.kl630_ip),
|
mount_sd: '/api/mount_sd?ip=' + encodeURIComponent(cfg.kl630_ip),
|
||||||
};
|
};
|
||||||
const labels = { compile:'Compile', deploy:'Deploy to KL630', bt_setup:'BT 初始化', autostart:'Write Autostart', autostart_read:'Read Autostart', mount_sd:'Mount SD' };
|
const labels = { compile:'Compile', deploy:'Deploy to KL630', setup:'First-time ISP Setup', autostart:'Write Autostart', autostart_read:'Read Autostart', mount_sd:'Mount SD' };
|
||||||
|
|
||||||
appendLog('\\n── ' + labels[action] + ' ' + '─'.repeat(40), 'prompt');
|
appendLog('\\n── ' + labels[action] + ' ' + '─'.repeat(40), 'prompt');
|
||||||
setLogStatus('Running...');
|
setLogStatus('Running...');
|
||||||
@ -1875,7 +1810,7 @@ function appendLog(text, kind) {
|
|||||||
}
|
}
|
||||||
function clearLog() { document.getElementById('log').textContent = ''; setLogStatus(''); }
|
function clearLog() { document.getElementById('log').textContent = ''; setLogStatus(''); }
|
||||||
function setLogStatus(s) { document.getElementById('log-status').textContent = s; }
|
function setLogStatus(s) { document.getElementById('log-status').textContent = s; }
|
||||||
function setBtns(disabled){ ['btn-compile','btn-deploy','btn-bt-setup','btn-autostart','btn-autostart-read','btn-mount-sd','btn-model-apply'].forEach(id => document.getElementById(id).disabled = disabled); }
|
function setBtns(disabled){ ['btn-compile','btn-deploy','btn-setup','btn-autostart','btn-autostart-read','btn-mount-sd','btn-model-apply'].forEach(id => document.getElementById(id).disabled = disabled); }
|
||||||
|
|
||||||
// ── Terminal ──────────────────────────────────────────────────────────────────
|
// ── Terminal ──────────────────────────────────────────────────────────────────
|
||||||
// by mars
|
// by mars
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user