4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenWrt Banana Pi BPI-R4 Wi-Fi7 MLO

4
Last updated at Posted at 2025-06-29

:flag_jp: Japanese article
OpenWrt_icon.png

はじめに

記事について

Banana Pi BPI-R4のOpenWrt初期設定・運用ガイドです
インストールから各種設定、チューニングなどをまとめています

おススメ管理インターフェース

検証環境

  • ボード: Revision 1.1
  • OpenWrt: 24.10.2
  • クライアントPC: Windows 11 25H2
  • ビルド環境: Ubuntu Desktop 24.04.2 LTS

OpenWrt搭載Wi-Fi7デバイス比較

Wi-Fi7デバイス
  • SinoVoip Banana Pi BPI-R4
    用途: オープンソースルーター開発向け
    CPU: MediaTek MT7988A (Filogic 880)
    メモリ: 4GB or 8GB DDR4
    ストレージ: MicroSD(TF) card or 8GB eMMC
    WAN: 1G ×1
    LAN: 1G ×3
    SFP: 10G ×2
    USB: USB 3.2 Type-A
    OS: OpenWrt

  • Linksys Velop WRT Pro 7
    用途: ホーム&ビジネス向け
    CPU: Qualcomm IPQ9554
    メモリ: 1GB DDR4
    ストレージ: 8GB eMMC
    WAN: 2.5G ×1
    LAN: 1G ×4
    OS: QSDK 19.07/カーネル 5.4.213(公式OpenWrtインストール不可)
    MLO: サポート

  • GL.iNet Flint 3 GL-BE9300
    https://www.gl-inet.com/campaign/gl-be9300-jp/
    用途: 家庭や小規模オフィス向け
    CPU: Qualcomm IPQ9554?
    メモリ: 1GB DDR4
    ストレージ: 8GB eMMC
    WAN: 2.5G ×1
    WAN/LAN: 2.5G ×1
    LAN: 2.5G ×4
    USB: USB 3.0 Type-A
    OS: QSDK 23.05/カーネル 5.4.213 (公式OpenWrtインストール不可?)
    MLO: サポート

wiki-qsdk


OpenWrt

OpenWrt Project へようこそ

320.logo.png

仕様
The UCI system
LuCI web interface

スクリーンショット 2025-07-15 091642.jpg


ファームウェア

ダウンロード

公式ファームウェア

デバイス用のOpenWrtカスタムファームウェアをダウンロード
※ビルダーは非公式

SDカード利用の場合:

  • イメージのダウンロード >> SDCARD.IMG.GZ をダウンロード
BPI-SINOVOIP MTK-SDK
OpenMPTCProuter

OpenMPTCProuter


SDカードサイズ拡張

SDカード初期化
  • コマンドプロンプト
    ※管理者として実行
diskpart
list disk
select disk 
clean
create partition primary
select partition 1
format fs=fat32 quick
assign
exit

SDカード拡張
  • 用意するもの:
    SDカード ×2枚 ※サイズが異なっても可
    SDカードリーダー
  • 作業工程
    両方のSDカードに同じイメージを書き込み
    サブのSDカードでデバイス起動
    メインのSDカードをリーダーに差してUSB接続
    fdiskでパーティション7を容量拡張
    SDカードを入れ替え (完了)

Type UUID: CAE9BE83-B15F-49CC-863F-081B744A2D93
Partition UUID: 5452574F-2211-4433-5566-778899AABB07

  • 手順
PKGS="f2fs-tools fdisk gdisk kmod-usb-storage kmod-usb-storage-uas parted"
command -v opkg && opkg update && opkg install $PKGS
command -v apk  && apk update  && apk add $PKGS
partprobe /dev/sda
fdisk /dev/sda
d
7
n
7
未入力
未入力
t
7
CAE9BE83-B15F-49CC-863F-081B744A2D93
x
u
7
5452574F-2211-4433-5566-778899AABB07
n
7
production
r
w
  • 断電しメインのSDカードをデバイスに入替挿入 ※USBカードリーダーを抜く

ビルドツール

Linuxライブディストリビューション (設定永続化仕様)

USB M.2 SSD エンクロージャー

ElecGear NV2242A
51yxNrsSvhL.AC_SL1200.jpg


Live Linux

Ubuntu


RAMディスク作成
  • クライアント確認
free -h
df -h
  • RAMディスク自動マウント設定
    /etc/fstab 自動追記(要8GB以上: 動的サイズ)
    ※推奨メモリ: 16GB以上
#!/bin/bash
sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d%H%M%S)
free -m | awk '
/^Mem:/ {
    total_mb = $2
    if (total_mb <= 8000) exit 0
    size_gb = int(total_mb / 2 / 1024)
    if (size_gb > 32) size_gb = 32
    if (size_gb < 1) size_gb = 1
    print "tmpfs /mnt/ramdisk tmpfs defaults,size=" size_gb "G 0 0"
}
' > /tmp/ramdisk_fstab
sudo sed -i '/^tmpfs \/mnt\/ramdisk tmpfs /d' /etc/fstab
if [ -s /tmp/ramdisk_fstab ]; then
    sudo tee -a /etc/fstab < /tmp/ramdisk_fstab
fi
rm -f /tmp/ramdisk_fstab
sudo mkdir -p /mnt/ramdisk
sudo umount /mnt/ramdisk 2>/dev/null
sudo mount -a
df -h /mnt/ramdisk
grep '/mnt/ramdisk' /etc/fstab
echo "RAMディスク設定完了" 

  • Windowsの時刻がUTCになる問題対応
sudo timedatectl set-local-rtc 1
timedatectl
# RTC in local TZ: yes

起動可能なUSBドライブ作成
  • Rufus
    rufus-**p.exe
    ※Persistent partition size: 8GB以上推奨 (永続化設定)

チューニング

ネットワークパフォーマンス

ツール
  • メモリ・バッファ設定
    net.core.rmem_max/wmem_max: ソケット受信/送信バッファの最大サイズ
    tcp_rmem/tcp_wmem: TCP受信/送信バッファサイズ(最小/デフォルト/最大)
    初期値: rmem_max/wmem_max = 212KB (212,992 bytes)
    推奨値: 12MB以上(4GBメモリなら16MB推奨)
    設定値: 16MB (16,777,216 bytes)

  • TCP最適化
    tcp_fastopen: TCP Fast Open(接続高速化)
    tcp_keepalive_probes: 接続維持のプローブ回数
    初期値: tcp_wmem最大値 = 4MB (4,194,304), tcp_rmem最大値 = 6MB (6,291,456)
    推奨値: 16MB以上
    設定値: 16MB (16,777,216)

  • TCP設定
    tcp_congestion_control: 輻輳制御アルゴリズム
    初期値: cubic, fastopen=1, keepalive_probes=9
    推奨値: westwood, fastopen=3, keepalive_probes=3
    設定値: cubic, fastopen=3, keepalive_probes=3(westwoodは利用不可)

  • コネクション追跡
    nf_conntrack_max: 同時接続数の上限
    netdev_max_backlog: ネットワークデバイスのキューサイズ
    somaxconn: リスニングキューの最大サイズ
    初期値: nf_conntrack_max=65,536, netdev_max_backlog=1,000, somaxconn=4,096
    推奨値: 131,072接続以上
    設定値: nf_conntrack_max=262,144, netdev_max_backlog=5,000, somaxconn=16,384

  • 設定

#!/bin/sh

TMP=/tmp/aios
mkdir -p "$TMP"

cat > "$TMP/dynamic-network-optimizer.sh" << 'SCRIPT_END'
#!/bin/sh
set -e

CONFIG_FILE="/etc/sysctl.d/99-network-optimization-auto.conf"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
NC='\033[0m'

log_info() { printf "${BLUE}[INFO]${NC} %s\n" "$1"; }
log_success() { printf "${GREEN}[OK]${NC} %s\n" "$1"; }
log_warning() { printf "${YELLOW}[WARN]${NC} %s\n" "$1"; }
log_error() { printf "${RED}[ERROR]${NC} %s\n" "$1"; }
log_highlight() { printf "${CYAN}[HIGHLIGHT]${NC} %s\n" "$1"; }

detect_memory() {
    local mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    printf '%s\n' "$((mem_kb / 1024))"
}

detect_cpu_cores() {
    grep -c ^processor /proc/cpuinfo
}

get_current_connections() {
    if [ -f /proc/sys/net/netfilter/nf_conntrack_count ]; then
        cat /proc/sys/net/netfilter/nf_conntrack_count
    else
        printf '%s\n' "0"
    fi
}

get_best_congestion_control() {
    local available
    available=$(cat /proc/sys/net/ipv4/tcp_available_congestion_control)
    for algo in bbr cubic reno; do
        if printf '%s\n' "$available" | grep -q "$algo"; then
            printf '%s\n' "$algo"
            return
        fi
    done
    printf '%s\n' "$(printf '%s\n' "$available" | awk '{print $1}')"
}

# Get current value dynamically - this is the key fix
get_current_value() {
    local setting="$1"
    case "$setting" in
        "rmem_max")
            cat /proc/sys/net/core/rmem_max 2>/dev/null || printf "%s" "unknown"
            ;;
        "wmem_max")
            cat /proc/sys/net/core/wmem_max 2>/dev/null || printf "%s" "unknown"
            ;;
        "tcp_rmem")
            cat /proc/sys/net/ipv4/tcp_rmem 2>/dev/null | tr -s '\t' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//' || printf "%s" "unknown"
            ;;
        "tcp_wmem")
            cat /proc/sys/net/ipv4/tcp_wmem 2>/dev/null | tr -s '\t' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//' || printf "%s" "unknown"
            ;;
        "tcp_congestion_control")
            cat /proc/sys/net/ipv4/tcp_congestion_control 2>/dev/null || printf "%s" "unknown"
            ;;
        "nf_conntrack_max")
            cat /proc/sys/net/netfilter/nf_conntrack_max 2>/dev/null || printf "%s" "unknown"
            ;;
        "netdev_max_backlog")
            cat /proc/sys/net/core/netdev_max_backlog 2>/dev/null || printf "%s" "unknown"
            ;;
        "somaxconn")
            cat /proc/sys/net/core/somaxconn 2>/dev/null || printf "%s" "unknown"
            ;;
        "active_connections")
            get_current_connections
            ;;
        *)
            printf "%s" "unknown"
            ;;
    esac
}

calculate_buffer_sizes() {
    local mem_mb=$1
    local cores=$2
    
    if [ "$mem_mb" -ge 3072 ]; then
        rmem_max=16777216; wmem_max=16777216
        tcp_rmem="4096 262144 16777216"; tcp_wmem="4096 262144 16777216"
        conntrack_max=262144; netdev_backlog=5000; somaxconn=16384
    elif [ "$mem_mb" -ge 1536 ]; then
        rmem_max=8388608; wmem_max=8388608
        tcp_rmem="4096 131072 8388608"; tcp_wmem="4096 131072 8388608"
        conntrack_max=131072; netdev_backlog=2500; somaxconn=8192
    elif [ "$mem_mb" -ge 512 ]; then
        rmem_max=4194304; wmem_max=4194304
        tcp_rmem="4096 65536 4194304"; tcp_wmem="4096 65536 4194304"
        conntrack_max=65536; netdev_backlog=1000; somaxconn=4096
    else
        rmem_max=1048576; wmem_max=1048576
        tcp_rmem="4096 32768 1048576"; tcp_wmem="4096 32768 1048576"
        conntrack_max=32768; netdev_backlog=500; somaxconn=2048
    fi
    
    if [ "$cores" -gt 4 ]; then
        netdev_backlog=$((netdev_backlog * 2))
        somaxconn=$((somaxconn * 2))
    elif [ "$cores" -gt 2 ]; then
        netdev_backlog=$((netdev_backlog + netdev_backlog / 2))
        somaxconn=$((somaxconn + somaxconn / 2))
    fi
}

format_bytes() {
    local bytes=$1
    if [ "$bytes" = "unknown" ]; then
        printf '%s' "unknown"
        return
    fi
    if [ "$bytes" -ge 1048576 ]; then
        printf '%s' "$((bytes / 1048576))MB"
    elif [ "$bytes" -ge 1024 ]; then
        printf '%s' "$((bytes / 1024))KB"
    else
        printf '%s' "${bytes}B"
    fi
}

values_equal() {
    local current="$1"
    local new="$2"
    
    if [ "$current" = "unknown" ] || [ "$new" = "unknown" ]; then
        return 1
    fi
    
    local norm_current=$(printf '%s\n' "$current" | tr -s ' \t' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
    local norm_new=$(printf '%s\n' "$new" | tr -s ' \t' ' ' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
    
    [ "$norm_current" = "$norm_new" ]
}

show_comparison() {
    printf '\n'
    printf "BEFORE vs AFTER COMPARISON\n"
    printf '\n'

    local max_setting=7
    local max_value=6
    local status_width=11

    local single_settings="rmem_max wmem_max tcp_congestion_control nf_conntrack_max netdev_max_backlog somaxconn"

    for setting in $single_settings; do
        [ ${#setting} -gt $max_setting ] && max_setting=${#setting}
    done

    local tcp_items="tcp_rmem (min) tcp_rmem (default) tcp_rmem (max) tcp_wmem (min) tcp_wmem (default) tcp_wmem (max)"
    for item in $tcp_items; do
        [ ${#item} -gt $max_setting ] && max_setting=${#item}
    done

    for setting in $single_settings; do
        local current_value=$(get_current_value "$setting")
        local new_value
        case "$setting" in
            "rmem_max") new_value="$rmem_max" ;;
            "wmem_max") new_value="$wmem_max" ;;
            "tcp_congestion_control") new_value="$best_congestion" ;;
            "nf_conntrack_max") new_value="$conntrack_max" ;;
            "netdev_max_backlog") new_value="$netdev_backlog" ;;
            "somaxconn") new_value="$somaxconn" ;;
        esac
        local display_current display_new
        case "$setting" in
            "rmem_max"|"wmem_max")
                display_current="$(format_bytes "$current_value")"
                display_new="$(format_bytes "$new_value")"
                ;;
            *)
                display_current="$current_value"
                display_new="$new_value"
                ;;
        esac
        [ ${#display_current} -gt $max_value ] && max_value=${#display_current}
        [ ${#display_new}     -gt $max_value ] && max_value=${#display_new}
    done

    for proto in rmem tcp_wmem; do
        local cur=$(get_current_value "${proto/tcp_/tcp_}")
        local new=$(eval echo "\$tcp_${proto/tcp_}")
        if [ "$cur" != "unknown" ]; then
            for val in $(echo "$cur") $(echo "$new"); do
                [ ${#val} -gt $max_value ] && max_value=${#val}
            done
        fi
    done

    max_setting=$((max_setting + 1))
    max_value=$((max_value + 1))

    local sep_setting sep_value sep_status
    sep_setting=$(printf '%*s' "$max_setting" '' | tr ' ' '-')
    sep_value=$(printf '%*s' "$max_value" '' | tr ' ' '-')
    sep_status=$(printf '%*s' "$status_width" '' | tr ' ' '-')

    printf "%-${max_setting}s| %-${max_value}s| %-${max_value}s| %-${status_width}s\n" "Setting" "Before" "After" "Status"

    printf "%s+-%s+-%s+-%s\n" "$sep_setting" "$sep_value" "$sep_value" "$sep_status"

    print_row() {
        local setting="$1" current_val="$2" new_val="$3" status="$4"
        printf "%-${max_setting}s| %-${max_value}s| %-${max_value}s| %s\n" "$setting" "$current_val" "$new_val" "$status"
    }

    for setting in $single_settings; do
        local current_value=$(get_current_value "$setting")
        local new_value status
        case "$setting" in
            "rmem_max") new_value="$rmem_max" ;;
            "wmem_max") new_value="$wmem_max" ;;
            "tcp_congestion_control") new_value="$best_congestion" ;;
            "nf_conntrack_max") new_value="$conntrack_max" ;;
            "netdev_max_backlog") new_value="$netdev_backlog" ;;
            "somaxconn") new_value="$somaxconn" ;;
        esac
        local display_current display_new
        case "$setting" in
            "rmem_max"|"wmem_max")
                display_current="$(format_bytes "$current_value")"
                display_new="$(format_bytes "$new_value")"
                ;;
            *)
                display_current="$current_value"
                display_new="$new_value"
                ;;
        esac
        if values_equal "$current_value" "$new_value"; then
            status="Same"
        else
            status="Changed"
            if [ "$current_value" != "unknown" ] && [ "$new_value" != "unknown" ]; then
                case "$setting" in
                    "rmem_max"|"wmem_max"|"netdev_max_backlog"|"somaxconn"|"nf_conntrack_max")
                        if [ "$new_value" -gt "$current_value" ] 2>/dev/null; then
                            status="↑ Increased"
                        elif [ "$new_value" -lt "$current_value" ] 2>/dev/null; then
                            status="↓ Optimized"
                        fi
                        ;;
                esac
            fi
        fi
        print_row "$setting" "$display_current" "$display_new" "$status"
    done

    print_tcp_rows() {
        local name="$1" cur="$2" new="$3"
        if [ "$cur" = "unknown" ]; then
            print_row "${name} (min)"     "unknown" "$(echo "$new" | awk '{print $1}')" "Unknown"
            print_row "${name} (default)" "unknown" "$(echo "$new" | awk '{print $2}')" "Unknown"
            print_row "${name} (max)"     "unknown" "$(echo "$new" | awk '{print $3}')" "Unknown"
            return
        fi
        local c1 c2 c3 n1 n2 n3 s1 s2 s3
        c1=$(echo "$cur" | awk '{print $1}');   n1=$(echo "$new" | awk '{print $1}')
        c2=$(echo "$cur" | awk '{print $2}');   n2=$(echo "$new" | awk '{print $2}')
        c3=$(echo "$cur" | awk '{print $3}');   n3=$(echo "$new" | awk '{print $3}')
        s1="Same"; s2="Same"; s3="Same"
        [ "$c1" != "$n1" ] && s1="Changed"
        [ "$c2" != "$n2" ] && s2="Changed"
        [ "$c3" != "$n3" ] && s3="Changed"
        [ "$n1" -gt "$c1" ] 2>/dev/null && s1="↑ Increased"
        [ "$n1" -lt "$c1" ] 2>/dev/null && s1="↓ Decreased"
        [ "$n2" -gt "$c2" ] 2>/dev/null && s2="↑ Increased"
        [ "$n2" -lt "$c2" ] 2>/dev/null && s2="↓ Decreased"
        [ "$n3" -gt "$c3" ] 2>/dev/null && s3="↑ Increased"
        [ "$n3" -lt "$c3" ] 2>/dev/null && s3="↓ Decreased"
        print_row "${name} (min)"     "$c1" "$n1" "$s1"
        print_row "${name} (default)" "$c2" "$n2" "$s2"
        print_row "${name} (max)"     "$c3" "$n3" "$s3"
    }

    print_tcp_rows "tcp_rmem" "$(get_current_value tcp_rmem)" "$tcp_rmem"
    print_tcp_rows "tcp_wmem" "$(get_current_value tcp_wmem)" "$tcp_wmem"

    printf "%s+-%s+-%s+-%s\n" "$sep_setting" "$sep_value" "$sep_value" "$sep_status"
    print_row "active_connections" "$(get_current_connections)" "$(get_current_connections)" "Same"

    printf '\n'
    printf "Legend:\n"
    printf "  Same        - No change needed\n"
    printf "  ↑ Increased - Value will be increased for better performance\n"
    printf "  ↓ Optimized - Value will be reduced for memory optimization\n"
    printf "  Changed     - Value will be modified\n"
    printf '\n'
}

verify_applied_settings() {
    printf '\n'
    log_info "=== Post-Application Verification ==="
    
    local verification_failed=0
    
    verify_setting() {
        local setting_name="$1"
        local expected_value="$2"
        local actual_value="$3"
        local display_name="$4"
        
        if values_equal "$actual_value" "$expected_value"; then
            log_success "$display_name: $(format_bytes "$actual_value" 2>/dev/null || printf "%s" "$actual_value")"
        else
            log_warning "$display_name: $actual_value (expected: $expected_value)"
            verification_failed=1
        fi
    }
    
    # Verify each setting using the same dynamic fetch method
    verify_setting "rmem_max" "$rmem_max" "$(get_current_value "rmem_max")" "rmem_max"
    verify_setting "wmem_max" "$wmem_max" "$(get_current_value "wmem_max")" "wmem_max"
    verify_setting "netdev_max_backlog" "$netdev_backlog" "$(get_current_value "netdev_max_backlog")" "netdev_max_backlog"
    verify_setting "somaxconn" "$somaxconn" "$(get_current_value "somaxconn")" "somaxconn"
    verify_setting "tcp_rmem" "$tcp_rmem" "$(get_current_value "tcp_rmem")" "tcp_rmem"
    verify_setting "tcp_wmem" "$tcp_wmem" "$(get_current_value "tcp_wmem")" "tcp_wmem"
    
    # Overall result
    if [ "$verification_failed" -eq 0 ]; then
        log_success "All settings verified successfully!"
    else
        log_warning "Some settings may not have been applied correctly"
        printf '\n'
        log_info "This may be due to:"
        printf "  - Kernel module not loaded (e.g., nf_conntrack)\n"
        printf "  - Insufficient permissions\n"
        printf "  - Kernel version compatibility\n"
        printf "  - Hardware limitations\n"
        printf '\n'
        log_info "Running 'dmesg | tail' might provide more information"
    fi
}

show_performance_impact() {
    printf "Performance Impact Summary\n"
    printf '\n'

    mem_mb=$(detect_memory)
    cores=$(detect_cpu_cores)

    printf "System Profile: %dMB RAM, %d CPU cores\n" "$mem_mb" "$cores"
    printf '\n'

    changes_found=0
    improvements=""

    # Check for actual improvements by comparing current vs new values
    current_rmem=$(get_current_value rmem_max)
    current_wmem=$(get_current_value wmem_max)
    current_backlog=$(get_current_value netdev_max_backlog)
    current_somaxconn=$(get_current_value somaxconn)

    if ! values_equal "$current_rmem" "$rmem_max" && [ "$current_rmem" != unknown ] && [ "$current_rmem" -gt 0 ] 2>/dev/null; then
        rmem_impr=$(( (rmem_max - current_rmem) * 100 / current_rmem ))
        if [ "$rmem_impr" -gt 0 ]; then
            improvements="${improvements}Receive buffer: +${rmem_impr}% increase -> Better download performance\n"
            changes_found=1
        elif [ "$rmem_impr" -lt 0 ]; then
            improvements="${improvements}Receive buffer: ${rmem_impr}% decrease -> Memory optimized\n"
            changes_found=1
        fi
    fi

    if ! values_equal "$current_wmem" "$wmem_max" && [ "$current_wmem" != unknown ] && [ "$current_wmem" -gt 0 ] 2>/dev/null; then
        wmem_impr=$(( (wmem_max - current_wmem) * 100 / current_wmem ))
        if [ "$wmem_impr" -gt 0 ]; then
            improvements="${improvements}Send buffer: +${wmem_impr}% increase -> Better upload performance\n"
            changes_found=1
        elif [ "$wmem_impr" -lt 0 ]; then
            improvements="${improvements}Send buffer: ${wmem_impr}% decrease -> Memory optimized\n"
            changes_found=1
        fi
    fi

    if ! values_equal "$current_backlog" "$netdev_backlog" && [ "$current_backlog" != unknown ] && [ "$current_backlog" -gt 0 ] 2>/dev/null; then
        backlog_impr=$(( (netdev_backlog - current_backlog) * 100 / current_backlog ))
        if [ "$backlog_impr" -gt 0 ]; then
            improvements="${improvements}Network backlog: +${backlog_impr}% increase -> Better packet processing\n"
            changes_found=1
        fi
    fi

    if ! values_equal "$current_somaxconn" "$somaxconn" && [ "$current_somaxconn" != unknown ] && [ "$current_somaxconn" -gt 0 ] 2>/dev/null; then
        connq_impr=$(( (somaxconn - current_somaxconn) * 100 / current_somaxconn ))
        if [ "$connq_impr" -gt 0 ]; then
            improvements="${improvements}Connection queue: +${connq_impr}% increase -> More concurrent connections\n"
            changes_found=1
        fi
    fi

    if [ "$changes_found" -eq 1 ]; then
        printf "Performance Improvements:\n"
        printf "%b" "$improvements"
    else
        printf "System is already optimally configured for your hardware\n"
        printf "No significant changes needed\n"
    fi

    printf '\n'
    printf "Expected Benefits:\n"
    printf "  - Reduced packet drops under high load\n"
    printf "  - Better throughput for large file transfers\n"
    printf "  - Improved responsiveness for multiple connections\n"
    printf "  - Optimized memory usage for your hardware\n"
    printf '\n'
}

remove_optimizer() {
    printf "Network Optimizer Removal Tool\n"
    printf "==============================\n"
    
    if [ ! -f "$CONFIG_FILE" ]; then
        log_info "No optimization config found"
        return 0
    fi

    printf "Do you want to remove the existing optimization config? (y/N): "
    read -r confirm_remove
    if [ "$confirm_remove" != "y" ] && [ "$confirm_remove" != "Y" ]; then
        log_info "Removal cancelled by user"
        return 0
    fi

    local backup_file=""
    for backup in "${CONFIG_FILE}.backup."*; do
        [ -f "$backup" ] && { backup_file="$backup"; break; }
    done

    if [ -n "$backup_file" ]; then
        log_info "Restoring from backup: $backup_file"
        cp "$backup_file" "$CONFIG_FILE"
        sysctl -p "$CONFIG_FILE" >/dev/null 2>&1
        log_success "Original settings restored"
    else
        log_info "No backup found – removing config file"
        rm "$CONFIG_FILE"
        log_success "Config file removed"
        log_highlight "Reboot required to return to system defaults"
    fi
}

optimizer_main() {
    printf "Dynamic Network Performance Optimizer\n"
    printf "=====================================\n"
  
    if [ -f "$CONFIG_FILE" ]; then
        log_warning "Existing config detected: $CONFIG_FILE"
        remove_optimizer
        exit 0
    fi
    
    printf '\n'
    log_info "=== System Analysis ==="
    local mem_mb=$(detect_memory)
    local cores=$(detect_cpu_cores)
    local best_congestion=$(get_best_congestion_control)
    
    log_info "Detected RAM: ${mem_mb}MB"
    log_info "Detected CPU cores: $cores"
    log_info "Best congestion control: $best_congestion"
    
    calculate_buffer_sizes "$mem_mb" "$cores"
    
    show_comparison
    
    show_performance_impact
    
    printf "Do you want to apply these changes? (y/N): "
    read -r confirm
    
    if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
        log_info "Operation cancelled by user"
        exit 0
    fi
    
    if [ -f "$CONFIG_FILE" ]; then
        local backup_file="${CONFIG_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
        cp "$CONFIG_FILE" "$backup_file"
        log_info "Backed up existing config to: $backup_file"
    fi
    
    log_info "Creating configuration: $CONFIG_FILE"
    cat > "$CONFIG_FILE" << CONFIG_EOF
net.core.rmem_max = $rmem_max
net.core.wmem_max = $wmem_max
net.ipv4.tcp_rmem = $tcp_rmem
net.ipv4.tcp_wmem = $tcp_wmem
net.ipv4.tcp_congestion_control = $best_congestion
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_keepalive_probes = 3
net.netfilter.nf_conntrack_max = $conntrack_max
net.core.netdev_max_backlog = $netdev_backlog
net.core.somaxconn = $somaxconn
CONFIG_EOF

    printf '\n'
    log_info "Applying configuration"
    if sysctl -p "$CONFIG_FILE" >/dev/null 2>&1; then
        log_success "Configuration applied successfully"
    else
        log_warning "Some settings may have failed (check kernel support)"
    fi
    
    verify_applied_settings
    
    printf '\n'
    log_success "Network optimization completed!"
    log_highlight "Reboot recommended for full effect and persistent changes"
    
    printf '\n'
    log_info "Current memory usage:"
    free -h
}

if [ "$0" = "${0#*/}" ] || [ "${0##*/}" = "$(basename "$0")" ]; then
    optimizer_main "$@"
fi
SCRIPT_END
chmod +x "$TMP/dynamic-network-optimizer.sh"
sh "$TMP/dynamic-network-optimizer.sh"


Wi-Fi

ドライバー

BPI-R4-MT76-OPENWRT-V21.02


wget -O /tmp/mt7996_eeprom_233_2i5i6i.bin 'https://github.com/openwrt/mt76/raw/refs/heads/master/firmware/mt7996/mt7996_eeprom_233_2i5i6i.bin'
FW_DIR="/lib/firmware/mediatek/mt7996"
if [ -f "$FW_DIR/mt7996_eeprom_233_2i5i6i.bin" ]; then
  mv "$FW_DIR/mt7996_eeprom_233_2i5i6i.bin" "$FW_DIR/mt7996_eeprom_233_2i5i6i.bin.bak"
fi
mv /tmp/mt7996_eeprom_233_2i5i6i.bin "$FW_DIR/"

wget -O /tmp/mt7996_eeprom_233.bin 'https://github.com/openwrt/mt76/raw/refs/heads/master/firmware/mt7996/mt7996_eeprom_233.bin'
FW_DIR="/lib/firmware/mediatek/mt7996"
if [ -f "$FW_DIR/mt7996_eeprom_233.bin" ]; then
  mv "$FW_DIR/mt7996_eeprom_233.bin" "$FW_DIR/mt7996_eeprom_233.bin.bak"
fi
mv /tmp/mt7996_eeprom_233.bin "$FW_DIR/"

バックグラウンドレーダー
uci set wireless.default_radio1.background_radar=1
# uci set wireless.default_radio2.background_radar=1
uci commit wireless
wifi reload

ワイヤレスオフロード

WED ( Wireless Ethernet D ispatch )

25.12.0-rc5ではRAM制限不要
RAMを2GBに制限した後(上記の注記を参照、一時的な回避策)、WOファームウェアの読み込みを有効にします。

WOファームウェア有効化

map-eは非推奨(アップロードがドロップする)

echo 'options mt7996e wed_enable=Y sr_scene_detect=Y' >> /etc/modules.conf

確認

cat /sys/module/mt7996e/parameters/sr_scene_detect 
cat /sys/module/mt7996e/parameters/wed_enable 

削除

sed -i '/^options mt7996e /d' /etc/modules.conf
# reboot

SN比改善
#!/bin/sh

grep -q 'pcie_aspm/parameters/policy' /etc/rc.local || \
sed -i '/^exit 0/i \
# Set CPU governor to "ondemand" to reduce power consumption and heat generation\
for CPUFREQ in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do\
    echo ondemand > "$CPUFREQ"\
done\
\
# Adjust parameters for the "ondemand" governor to further optimize power consumption\
for ONDEMAND_DIR in /sys/devices/system/cpu/cpu*/cpufreq/ondemand; do\
    echo 70 > "$ONDEMAND_DIR/up_threshold"\
    echo 60000 > "$ONDEMAND_DIR/sampling_rate"\
    echo 1 > "$ONDEMAND_DIR/io_is_busy"\
done\
\
# Enable ASPM with "powersupersave" policy to improve WiFi signal-to-noise ratio\
echo powersupersave > /sys/module/pcie_aspm/parameters/policy\
' /etc/rc.local

# reboot

設定確認

cat /sys/module/pcie_aspm/parameters/policy
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

メインラインイメージのtxpowerが低い

https://docs.banana-pi.org/en/BPI-R4/BananaPi_BPI-R4_FAQ#_openwrt_mainline_image_txpower_low


Wi-Fi 7

MLO (Multi Link Operation)

MLO設定

動作確認:25.12.0-rc4
image.png
wireless-mlo-openwrt

  • /etc/config/wireless
#!/bin/sh
# MLO Configuration for BPI-R4
# Based on: github.com/danpawlik/openwrt-builder/blob/master/settings-configs/BPI-R4/wireless-mlo-openwrt

SSID='ばなな'
WPA_KEY='password'
COUNTRY='JP'

# バックアップ作成
cp /etc/config/wireless /etc/config/wireless.mlo.bak

# UCI batch設定
uci -q batch << EOF
# 既存の個別インターフェース削除
delete wireless.default_radio0
delete wireless.default_radio1
delete wireless.default_radio2

# radio0: 2.4GHz
set wireless.radio0.disabled='0'
set wireless.radio0.band='2g'
set wireless.radio0.channel='1'
set wireless.radio0.htmode='EHT20'
set wireless.radio0.country='${COUNTRY}'
set wireless.radio0.rnr='1'

# radio1: 5GHz
set wireless.radio1.disabled='0'
set wireless.radio1.band='5g'
set wireless.radio1.channel='36'
set wireless.radio1.htmode='EHT80'
set wireless.radio1.country='${COUNTRY}'
set wireless.radio1.rnr='1'
set wireless.radio1.background_radar='1'

# radio2: 6GHz
set wireless.radio2.disabled='0'
set wireless.radio2.band='6g'
set wireless.radio2.channel='auto'
set wireless.radio2.htmode='EHT160'
set wireless.radio2.country='${COUNTRY}'
set wireless.radio2.rnr='1'

# MLO統合インターフェース
set wireless.wifinet0='wifi-iface'
add_list wireless.wifinet0.device='radio0'
add_list wireless.wifinet0.device='radio1'
add_list wireless.wifinet0.device='radio2'
set wireless.wifinet0.mlo='1'
set wireless.wifinet0.network='lan'
set wireless.wifinet0.mode='ap'
set wireless.wifinet0.ssid='${SSID}'
set wireless.wifinet0.encryption='sae'
set wireless.wifinet0.key='${WPA_KEY}'
set wireless.wifinet0.ieee80211w='2'

commit wireless
EOF

wifi reload
echo "MLO設定完了"
  • 2.4GHz除外設定
#!/bin/sh
# MLO Configuration for BPI-R4 (5GHz + 6GHz)
# Tested on: OpenWrt 25.12.0-rc5, r32673-482ba7230a
# Note: 2.4GHz is excluded from MLO (disabled)

SSID='ばなな'
WPA_KEY='password'
COUNTRY='JP'

# バックアップ作成
cp /etc/config/wireless /etc/config/wireless.mlo.bak

# UCI batch設定
uci -q batch << EOF
# 既存の個別インターフェース削除
delete wireless.default_radio0
delete wireless.default_radio1
delete wireless.default_radio2

# radio0: 2.4GHz (MLO非対応のため無効化)
set wireless.radio0.disabled='1'
set wireless.radio0.country='${COUNTRY}'

# radio1: 5GHz (MLO link ID 1)
set wireless.radio1.disabled='0'
set wireless.radio1.band='5g'
set wireless.radio1.channel='36'
set wireless.radio1.htmode='EHT80'
set wireless.radio1.country='${COUNTRY}'
set wireless.radio1.rnr='1'
set wireless.radio1.background_radar='1'

# radio2: 6GHz (MLO link ID 2)
set wireless.radio2.disabled='0'
set wireless.radio2.band='6g'
set wireless.radio2.channel='5'
set wireless.radio2.htmode='EHT320'
set wireless.radio2.country='${COUNTRY}'
set wireless.radio2.rnr='1'

# MLO統合インターフェース (5GHz + 6GHz)
set wireless.mlo='wifi-iface'
add_list wireless.mlo.device='radio1'
add_list wireless.mlo.device='radio2'
set wireless.mlo.mlo='1'
set wireless.mlo.network='lan'
set wireless.mlo.mode='ap'
set wireless.mlo.ssid='${SSID}'
set wireless.mlo.encryption='sae'
set wireless.mlo.key='${WPA_KEY}'
set wireless.mlo.ieee80211w='2'

commit wireless
EOF

wifi up
echo "MLO設定完了"
# 確認
echo "--- 設定確認 ---"
uci show wireless
echo "--- インターフェース確認 ---"
iw dev
echo "--- station dump確認 ---"
iw dev ap-mld0 station dump
  • 復元
cp /etc/config/wireless.mlo.bak /etc/config/wireless
uci commit wireless
wifi reload

echo "復元完了"

Wi-Fi 6

高速ローミング構成 (usteer + 802.11r/k/v)

wifi.png

usteerとバンドステアリングの設置

#!/bin/sh
I18N="ja"
SSID='ばなな'
WIFI_KEY='password'
COUNTRY='JP'
MOBILITY_DOMAIN='1234'
BANDS="2g 5g 6g"
HTMODES="HE20 HE80 HE160"
TXPOWERS="10 15"
CHANNELS="1 auto auto"
NASIDS="ap1-2g ap1-5g ap1-6g"
SNR="30 20 15"
PKGS="luci-i18n-usteer-${I18N}"
command -v opkg && { opkg list-installed | grep -q "$PKGS" || { opkg update && opkg install $PKGS; }; }
command -v apk && { apk list -I 2>/dev/null | grep -q "$PKGS" || { apk update && apk add $PKGS; }; }
cp /etc/config/wireless /etc/config/wireless.usteer.bak
rm /etc/config/wireless
wifi config
NUM_IFACES=$(grep -c "^config wifi-device" /etc/config/wireless)
i=0
while [ $i -lt $NUM_IFACES ]; do
    iface="default_radio${i}"
    radio="radio${i}"
    band=$(echo $BANDS | awk -v n=$((i+1)) '{print $n}')
    htmode=$(echo $HTMODES | awk -v n=$((i+1)) '{print $n}')
    txpower=$(echo $TXPOWERS | awk -v n=$((i+1)) '{print $n}')
    nasid=$(echo $NASIDS | awk -v n=$((i+1)) '{print $n}')
    min_snr=$(echo $SNR | awk -v n=$((i+1)) '{print $n}')
    channel=$(echo $CHANNELS | awk -v n=$((i+1)) '{print $n}')

    uci -q batch << EOF
set wireless.${radio}.band=${band}
set wireless.${radio}.channel=${channel}
set wireless.${radio}.htmode=${htmode}
set wireless.${radio}.country=${COUNTRY}
set wireless.${radio}.disabled=0
set wireless.${iface}.device=${radio}
set wireless.${iface}.network=lan
set wireless.${iface}.mode=ap
set wireless.${iface}.ssid=${SSID}
set wireless.${iface}.encryption=sae
set wireless.${iface}.key=${WIFI_KEY}
set wireless.${iface}.isolate=1
set wireless.${iface}.ocv=1
set wireless.${iface}.ieee80211r=1
set wireless.${iface}.mobility_domain=${MOBILITY_DOMAIN}
set wireless.${iface}.ft_over_ds=1
set wireless.${iface}.nasid=${nasid}
set wireless.${iface}.usteer_min_snr=${min_snr}
set wireless.${iface}.ieee80211k=1
set wireless.${iface}.ieee80211v=1
set wireless.${iface}.disabled=0
EOF
    [ -n "$txpower" ] && uci set wireless.$radio.txpower="$txpower"
    [ "$band" = "5g" ] && uci set wireless.$radio.background_radar='1' && uci set wireless.$iface.ft_psk_generate_local='1'

    i=$((i+1))
done
uci -q batch << EOF
set usteer.@usteer[0].roam_scan_snr=-65
set usteer.@usteer[0].signal_diff_threshold=8
commit
EOF
/etc/init.d/usteer enable
/etc/init.d/usteer start
wifi reload
  • 復元
#!/bin/sh
/etc/init.d/usteer stop
/etc/init.d/usteer disable

cp /etc/config/wireless.usteer.bak /etc/config/wireless

rm -f /etc/config/usteer

opkg remove luci-app-usteer usteer

uci commit wireless
wifi reload

PCIe2

PCIeモード無効化(USBモード化)
#!/bin/sh

PKG="u-boot-envtools"

if command -v opkg >/dev/null 2>&1; then
    opkg update
    opkg install $PKG
elif command -v apk >/dev/null 2>&1; then
    apk update
    apk add $PKG
else
    echo "パッケージマネージャが見つかりません。"
    exit 1
fi

cat <<'EOF' > /etc/init.d/bootconf
#!/bin/sh /etc/rc.common
START=10

boot() {
    fw_setenv bootconf_extra 'mt7988a-bananapi-bpi-r4-disable-pcie2'
}
EOF

chmod +x /etc/init.d/bootconf
/etc/init.d/bootconf enable

echo "rebootしてください。次回起動時に反映されます。"

パーツ

ACアダプター

UCB Type-C PD 20V
  • 純正ACアダプター: 12V/5.2A or 19V 3.2A

  • USB-C回路図
    CH224Kは20V/3.25A(65W)プロファイルを固定で要求する
    9d71e1f22c5df37ed962d51aa307a6b95d715893.jpeg

  • PD 20V: 65Wアダプター
    PC-VP-BP143: 20V/3.25A,15V/3A,9V/3A,5V/3A
    EC-AC8565BK: 20V/3.25A,15V/3A,12V/3A,9V/3A,5V/3A


ファン

BPI-R4用ファン確認

汎用Thermal Sysfsドライバの使い方

  • ハードウェアモニタ(hwmon)デバイス一覧
ls -l /sys/class/hwmon/
  • ファンPWM値の現在値確認
cat /sys/class/hwmon/hwmon1/pwm1
  • ファンPWM制御の状態確認
cat /sys/class/hwmon/hwmon1/pwm1_enable
  • サーマルゾーン(温度トリップポイント)の種類確認
cat /sys/class/thermal/thermal_zone0/trip_point_0_type # クリティカル: 100%
cat /sys/class/thermal/thermal_zone0/trip_point_1_type # ホット
cat /sys/class/thermal/thermal_zone0/trip_point_2_type # アクティブ: 50%
cat /sys/class/thermal/thermal_zone0/trip_point_3_type # アクティブ: 30%
cat /sys/class/thermal/thermal_zone0/trip_point_4_type # アクティブ: 0%
  • サーマルゾーン(温度トリップポイント)の温度設定確認
cat /sys/class/thermal/thermal_zone0/trip_point_0_temp # 臨界: 125℃
cat /sys/class/thermal/thermal_zone0/trip_point_1_temp # 高: 120℃
cat /sys/class/thermal/thermal_zone0/trip_point_2_temp # 高: 115℃
cat /sys/class/thermal/thermal_zone0/trip_point_3_temp # 中: 85℃
cat /sys/class/thermal/thermal_zone0/trip_point_4_temp # 低: 40℃

BPI-R4用ファンスクリプト
  • pwmfan
# /etc/init.d/pwmfan
cat <<'EOF' > /etc/init.d/pwmfan
#!/bin/sh /etc/rc.common

START=99
PIDFILE=/var/run/pwmfan.pid

start() {
    if [ -f $PIDFILE ]; then
        PID=$(cat $PIDFILE 2>/dev/null)
        if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
            echo "Already running"
            return 1
        else
            rm -f $PIDFILE
        fi
    fi
    /usr/bin/pwmfan-loop &
    echo $! > $PIDFILE
}

stop() {
    if [ -f $PIDFILE ]; then
        PID=$(cat $PIDFILE 2>/dev/null)
        if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
            kill $PID
        fi
        rm -f $PIDFILE
    fi
}

restart() {
    stop
    sleep 1
    start
}
EOF

chmod 755 /etc/init.d/pwmfan

# /usr/bin/pwmfan-loop
cat <<'EOF' > /usr/bin/pwmfan-loop
#!/bin/sh

CONFIG=pwmfan
SECTION="pwmfan"

get_temp() {
    local temp
    temp=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null)
    [ -z "$temp" ] && temp=0
    echo $((temp / 1000))
}

set_pwm() {
    local pwm="$1"
    echo 1 > /sys/class/hwmon/hwmon1/pwm1_enable
    echo $pwm > /sys/class/hwmon/hwmon1/pwm1
}

read_config() {
    INTERVAL=$(uci get ${CONFIG}.@${SECTION}[0].interval 2>/dev/null)
    [ -z "$INTERVAL" ] && INTERVAL=30
    TRIP_TEMP=$(uci get ${CONFIG}.@${SECTION}[0].trip_temp 2>/dev/null)
    TRIP_PWM=$(uci get ${CONFIG}.@${SECTION}[0].trip_pwm 2>/dev/null)
    MINPWM=$(uci get ${CONFIG}.@${SECTION}[0].minpwm 2>/dev/null)
    [ -z "$MINPWM" ] && MINPWM=100
    MAXPWM=$(uci get ${CONFIG}.@${SECTION}[0].maxpwm 2>/dev/null)
    [ -z "$MAXPWM" ] && MAXPWM=255

    if [ -z "$TRIP_TEMP" ] || [ -z "$TRIP_PWM" ]; then
        exit 1
    fi
    case "$INTERVAL" in
        ''|*[!0-9]*) INTERVAL=30 ;;
    esac
}

main_loop() {
    read_config
    local temp pwm i t p last_pwm=0
    set -- $TRIP_TEMP
    local trip_count=$#
    while true; do
        temp=$(get_temp)
        pwm=$MINPWM
        set -- $TRIP_TEMP
        local j=1
        for t in $@; do
            set -- $TRIP_PWM
            p=$(eval "echo \$$j")
            [ -z "$p" ] && p=$MINPWM
            [ "$temp" -ge "$t" ] && pwm=$p
            j=$((j+1))
        done
        [ "$pwm" -lt "$MINPWM" ] && pwm=$MINPWM
        [ "$pwm" -gt "$MAXPWM" ] && pwm=$MAXPWM
        if [ "$pwm" != "$last_pwm" ]; then
            set_pwm "$pwm"
            last_pwm=$pwm
        fi
        sleep "$INTERVAL"
    done
}

main_loop
EOF

chmod 755 /usr/bin/pwmfan-loop

touch /etc/config/pwmfan
uci -q delete pwmfan.@pwmfan[0]
uci -q commit pwmfan
uci -q add pwmfan pwmfan
uci -q set pwmfan.@pwmfan[-1].interval='20'
uci -q set pwmfan.@pwmfan[-1].minpwm='80'
uci -q set pwmfan.@pwmfan[-1].maxpwm='255'
uci -q set pwmfan.@pwmfan[-1].trip_temp='60 70'
uci -q set pwmfan.@pwmfan[-1].trip_pwm='128 255'
uci -q commit pwmfan
/etc/init.d/pwmfan enable
/etc/init.d/pwmfan restart

# /usr/bin/fan-status
cat <<'EOF' > /usr/bin/fan-status
#!/bin/sh

TEMP_RAW=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null)
TEMP_C=$(awk "BEGIN {printf \"%.1f\", $TEMP_RAW/1000}")
PWM=$(cat /sys/class/hwmon/hwmon1/pwm1 2>/dev/null)
PWM_PERCENT=$((PWM * 100 / 255))
echo "========================="
echo " Fan & Temperature Status"
echo "-------------------------"
echo " Temperature : ${TEMP_C} °C"
echo " Fan PWM     : ${PWM} (${PWM_PERCENT} %)"
echo "========================="
EOF

chmod 755 /usr/bin/fan-status
fan-status
  • 監視
while true; do
    fan-status
    sleep 20
done
  • リムーブ
/etc/init.d/pwmfan stop
/etc/init.d/pwmfan disable

rm -f /etc/init.d/pwmfan
rm -f /usr/bin/pwmfan-loop
rm -f /usr/bin/fan-status

uci -q delete pwmfan.@pwmfan[0]
uci -q commit pwmfan

[ ! -s /etc/config/pwmfan ] && rm -f /etc/config/pwmfan

モデム

SIMカードトレイ
  • SIMサイズ: nanoSIM
  • SIMトレイ: iPhone5/5S/SE用

※SIMを入れずトレイを挿入すると壊れます!
iphone5_simcardtray.jpg


Fibocom FM350-GL (14c3:4d75)

FM350 AT Commands User Manual

  • 動作環境
    Revision: 1.1
    OpenWrt: 24.10.2
    ModemManager: 1.22.0
    kmod-mtk-t7xx: 6.6.93-r1
    14C3:4D75: 81600.0000.00.29.18.16_DO
    モード: PCIe
    SIMスロット: SIM1
    キャリア: docomo

  • MHF4コネクタ (アンテナ端子)
    FM350.png
    M: プライマリ/送信 (全利用)
    M1: 4×4 MIMO
    M2: 4×4 MIMO
    D/G: ダイバーシティ/受信 (全利用)

  • 既知の問題
    モデムデバイスは、ルーター本体を完全に断電してから通電させないと、認識しない
    ※M.2スロットの3.3 V電源ラインはカーネルから制御できず、PCIeリセットピンも露出していない
    PCIeモードでは、ATコマンドは送信出来ない
    SIM1の青色LEDは、通信が確立しないと点灯しない
    SIMトレイがスムースに挿入出来ない場合、SIMが認識しない事がある (ケースネジを緩め要調整)

  • モデム設定

#!/bin/sh

WWAN="wwan"
APN="spmode.ne.jp"
ALLOWEDAUTH="chap"
ALLOWEDMODE="5G" # SPモードの場合必須
METRIC="100"
IPTYPE="ipv4v6"
LOGLEVEL="ERR"
IP6ASSIGN="64"
MTU="1500"
SIGNALRATE="120"
INIT_EPSBEARER="default" # SPモードの場合必須
AUTO="0"

cp /etc/config/network /etc/config/network.wwan.bak
cp /etc/config/firewall /etc/config/firewall.wwan.bak

PKGS="kmod-mtk-t7xx modemmanager-rpcd luci-proto-modemmanager pciutils mbim-utils ubus"
command -v opkg && opkg update && opkg install $PKGS
command -v apk && apk update && apk add $PKGS

WWAN_IDX=$(uci show network | grep "name='wwan0'" | sed -n "s/network.@device

\[\([0-9]\+\)\]

.*/\1/p")
if [ -n "$WWAN_IDX" ]; then
  uci set network.@device[$WWAN_IDX].ipv6='1'
  [ -n "$MTU" ] && uci set network.@device[$WWAN_IDX].mtu="${MTU}"
  [ -n "$MTU" ] && uci set network.@device[$WWAN_IDX].mtu6="${MTU}"
else
  uci add network device
  uci set network.@device[-1].name='wwan0'
  uci set network.@device[-1].ipv6='1'
  [ -n "$MTU" ] && uci set network.@device[-1].mtu="${MTU}"
  [ -n "$MTU" ] && uci set network.@device[-1].mtu6="${MTU}"
fi

uci set network.${WWAN}=interface
uci set network.${WWAN}.proto='modemmanager'
uci set network.${WWAN}.apn="${APN}"
uci set network.${WWAN}.allowedauth="${ALLOWEDAUTH}"
uci set network.${WWAN}.allowedmode="${ALLOWEDMODE}"
uci set network.${WWAN}.iptype="${IPTYPE}"
uci set network.${WWAN}.loglevel="${LOGLEVEL}"
uci set network.${WWAN}.metric="${METRIC}"
uci set network.${WWAN}.force_link='1'
uci set network.${WWAN}.ip6assign="${IP6ASSIGN}"
[ -n "$MTU" ] && uci set network.${WWAN}.mtu="${MTU}"
[ -n "$SIGNALRATE" ] && uci set network.${WWAN}.signalrate="${SIGNALRATE}"
uci set network.${WWAN}.auto="${AUTO}"
uci set network.${WWAN}.init_epsbearer="${INIT_EPSBEARER}"

[ -z "$(uci get firewall.@zone[1].network 2>/dev/null | grep -w 'wwan')" ] && uci add_list firewall.@zone[1].network='wwan'

uci commit network
uci commit firewall

/etc/init.d/modemmanager stop
sleep 2
/etc/init.d/modemmanager start
sleep 3

ubus call network reload
sleep 2
ifup wwan

  • 復元
cp /etc/config/network.wwan.bak /etc/config/network
cp /etc/config/firewall.wwan.bak /etc/config/firewall
uci commit network
uci commit firewall
# reboot
  • デバイス確認
lspci
ls -la /dev/ | grep -E "(cdc|wwan)"
  • ネットワークインターフェース確認
ip link show
  • ベンダーID、プロダクトID確認
ls /sys/class/net/wwan0/device/
cat /sys/class/net/wwan0/device/../../../modalias
cat /sys/class/net/wwan0/device/../../../uevent
  • モデム確認
ubus call modemmanager info
ubus call modemmanager dump
mmcli -L
mmcli -m 0
  • 使用可能なすべてのオプション
mbimcli --help-all
  • バージョン確認
mbimcli --version
  • FCCロック確認
mbimcli -d /dev/wwan0mbim0 --intel-query-fcc-lock
mbimcli -d /dev/wwan0mbim0 --set-radio-state=on
  • FCCロック解除(必要な場合)
mbimcli -d /dev/wwan0mbim0 --intel-set-fcc-unlock

/etc/ModemManager/fcc.unlock/
dispatcher-fcc-unlock

MODEM="14c3" # 例
mkdir -p /etc/ModemManager/fcc.unlock/
chmod 644 /etc/ModemManager/fcc.unlock/$MODEM

cat <<'EOF' > /etc/ModemManager/fcc.unlock/$MODEM
# <ここにdispatcher-fcc-unlockの該当ソースを貼る>
EOF

/etc/init.d/modemmanager restart
  • モデム有効化(enable)
mmcli -m 0 -e
  • ラジオ有効化
mbimcli -d /dev/wwan0mbim0 --set-radio-state=on
  • ラジオ状態確認
mbimcli -d /dev/wwan0mbim0 --query-radio-state
  • デバイス機能確認
mbimcli -d /dev/wwan0mbim0 --query-device-caps
  • SIM状態確認
mbimcli -d /dev/wwan0mbim0 --query-subscriber-ready-status
  • ネットワーク登録状態確認
mbimcli -d /dev/wwan0mbim0 --query-registration-state
  • 信号強度確認
mmcli -m 0 --signal-get
mbimcli -d /dev/wwan0mbim0 --query-signal-state
  • APN設定確認
mbimcli -d /dev/wwan0mbim0 --query-provisioned-contexts
  • 接続IP情報
mbimcli -d /dev/wwan0mbim0  -p --query-ip-configuration=0
  • モデムの詳細状態確認
mmcli -m 0 --output-keyvalue
  • モデムの詳細情報
root@bpi-r4:~# mmcli -m 0
  -----------------------------------
  General   |                   path: /org/freedesktop/ModemManager1/Modem/0
            |              device id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  -----------------------------------
  Hardware  |           manufacturer: generic
            |                  model: MBIM [14C3:4D75]
            |      firmware revision: 81600.0000.00.29.18.16_DO
            |                         C43
            |           h/w revision: V1.0.6
            |              supported: gsm-umts, lte, 5gnr
            |                current: gsm-umts, lte, 5gnr
            |           equipment id: XXXXXXXXXXXXXXXX
  -----------------------------------
  System    |                 device: /sys/devices/platform/soc/11280000.pcie/pci0003:00/0003:00:00.0/0003:01:00.0
            |                physdev: /sys/devices/platform/soc/11280000.pcie/pci0003:00/0003:00:00.0/0003:01:00.0
            |                drivers: mtk_t7xx
            |                 plugin: generic
            |           primary port: wwan0mbim0
            |                  ports: wwan0 (net), wwan0at0 (at), wwan0mbim0 (mbim)
  -----------------------------------
  Numbers   |                    own: XXXXXXXXXXXX
  -----------------------------------
  Status    |                   lock: sim-pin2
            |         unlock retries: sim-pin2 (3)
            |                  state: connected
            |            power state: on
            |            access tech: lte, 5gnr
            |         signal quality: 48% (cached)
  -----------------------------------
  Modes     |              supported: allowed: 3g; preferred: none
            |                         allowed: 4g; preferred: none
            |                         allowed: 3g, 4g; preferred: none
            |                         allowed: 5g; preferred: none
            |                         allowed: 3g, 5g; preferred: none
            |                         allowed: 4g, 5g; preferred: none
            |                         allowed: 3g, 4g, 5g; preferred: none
            |                current: allowed: 5g; preferred: none
  -----------------------------------
  IP        |              supported: ipv4, ipv6, ipv4v6
  -----------------------------------
  3GPP      |                   imei: XXXXXXXXXXXXXXXX
            |          enabled locks: fixed-dialing
            |            operator id: 44010
            |          operator name: NTT DOCOMO
            |           registration: home
            |   packet service state: attached
  -----------------------------------
  3GPP EPS  |   ue mode of operation: csps-2
            |    initial bearer path: /org/freedesktop/ModemManager1/Bearer/4
            |     initial bearer apn: spmode.ne.jp
            | initial bearer ip type: ipv4v6
  -----------------------------------
  3GPP 5GNR |              mico mode: disabled
  -----------------------------------
  SIM       |       primary sim path: /org/freedesktop/ModemManager1/SIM/0
            |         sim slot paths: slot 1: /org/freedesktop/ModemManager1/SIM/0 (active)
            |                         slot 2: /org/freedesktop/ModemManager1/SIM/1
  -----------------------------------
  Bearer    |                  paths: /org/freedesktop/ModemManager1/Bearer/5
root@bpi-r4:~# 

スクリーンショット 2025-07-03 002228.jpg
Telit LN940 よりは速いが、5Gの速度にしては遅すぎる

docomo.png
iPhoneよりは速かった(笑)

モデムリセット(検証専用)
  • 動作不安定(セーフモードに入るリスク有)
cat > /etc/modem_power.sh << 'EOF'
#!/bin/sh

pci_reset() {
    local pci_path="/sys/bus/pci/devices/0003:01:00.0"
    
    if [ -f "$pci_path/reset" ]; then
        echo 1 > "$pci_path/reset"
        return 0
    else
        return 1
    fi
}

driver_reset() {   
    echo "0003:01:00.0" > /sys/bus/pci/drivers/mtk_t7xx/unbind 2>/dev/null
    sleep 2
    
    echo "0003:01:00.0" > /sys/bus/pci/drivers/mtk_t7xx/bind 2>/dev/null
    sleep 3
}

modem_reset() {
    /etc/init.d/modemmanager stop
    sleep 2
    
    if ! pci_reset; then
        driver_reset
    fi
    
    sleep 3
    
    /etc/init.d/modemmanager start
    sleep 5
}

check_status() {
    lspci | grep -i mediatek | grep 4d75
    
    ls -la /dev/wwan0* 2>/dev/null || echo "なし"
    
    mmcli -L 2>/dev/null || echo "モデムなし"
    
    if mmcli -L 2>/dev/null | grep -q "Modem"; then
        mmcli -m 0 2>/dev/null | grep -E "(状態|State|オペレータ|Operator|信号|Signal)" || true
    fi
    
    dmesg | grep -i -E "(t7xx|mtk|modem)" | tail -5
}

case "$1" in
    reset)
        modem_reset
        ;;
    status)
        check_status
        ;;
    *)
        echo "使用法: $0 {reset|status}"
        ;;
esac
EOF
chmod +x /etc/modem_power.sh

# モデムリセット
sh /etc/modem_power.sh reset


SFP

SFP 10G
PKGS="ethtool-full"
command -v opkg && opkg update && opkg install $PKGS
command -v apk  && apk update  && apk add $PKGS

RTC

RTC有効化
PKGS="i2c-tools"
command -v opkg && opkg update && opkg install $PKGS
command -v apk  && apk update  && apk add $PKGS

fw_setenv bootconf_extra mt7988a-bananapi-bpi-r4-rtc

Mini PCI Express

BPI-R4-NIC-BE14
AW7916-NPD

AW7916-NPD.png

#!/bin/sh
command -v opkg && opkg update
command -v apk  && apk update
# 不要
REMOVE="kmod-mt7996-firmware kmod-mt7996-233-firmware mt7988-wo-firmware"
command -v opkg && opkg list $REMOVE
command -v apk  && apk search $REMOVE
# 必要
REQUIRED="kmod-mt7915e kmod-mt7916-firmware"
command -v opkg && opkg list $REQUIRED
command -v apk  && apk search $REQUIRED

フラッシュ&インストールシステム

aios.png

デバイスアクセス

デバイスアクセス(UCI)

パワーシェルでアクセス

PowerShellの開始

  • パワーシェル起動:キー入力:Win+x > a > はい

  • コンソールログイン (パワーシェル)
    ※192.168.1.1以外の場合、以下の形式で入力後にワンライナーを入力下さい

$ip="192.168.*.*"
  • コンソールログイン (192.168.1.1用)
    万能型ワンライナー
if(!$ip){$ip="192.168.1.1"}; ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o GlobalKnownHostsFile=NUL -o HostKeyAlgorithms=+ssh-rsa -tt root@$ip

LuCi

ブラウザーでアクセス

初期設定

パスワード

初期値から変更
passwd

Changing password for root
New password:

passwd:入力モード
任意のパスワード

Retype password:

passwd:入力モード
再入力

passwd: password for root changed by root

exit

ホストネーム

ホストネーム(openwrt)を変更
#!/bin/sh
HOSTNAME='openwrt' # デバイス名
uci set system.@system[0].hostname=${HOSTNAME}
uci commit system
/etc/init.d/system reload

タイムゾーン

タイムゾーンを変更
#!/bin/sh
TIMEZONE='JST-9'
ZONENAME='Asia/Tokyo'
uci set system.@system[0].timezone=${TIMEZONE}
uci set system.@system[0].zonename=${ZONENAME}
uci commit system
/etc/init.d/sysntpd restart

NTP

NTPサーバーを変更
#!/bin/sh
POOL='jp'
uci delete system.ntp.server
uci add_list system.ntp.server=0.${POOL}.pool.ntp.org
uci add_list system.ntp.server=1.${POOL}.ppl.ntp.org
uci add_list system.ntp.server=2.${POOL}.pool.ntp.org 
uci add_list system.ntp.server=3.${POOL}.pool.ntp.org
uci set system.ntp.enable_server='1'
uci set system.ntp.use_dhcp='0'
uci set system.ntp.interface='lan'
uci commit system
/etc/init.d/sysntpd restart

SSH

WANからのアクセスを遮断
uci set dropbear.@dropbear[0].Interface='lan'
uci commit dropbear

LED

ネットワークアクセスで点灯点滅
#! /bin/sh
uci add system led
uci set system.@led[-1].name='wan'
uci set system.@led[-1].sysfs='green:status'
uci set system.@led[-1].trigger='netdev'
uci set system.@led[-1].dev='br-wan'
uci add_list system.@led[-1].mode='link'
uci add_list system.@led[-1].mode='tx'
uci add_list system.@led[-1].mode='rx'
uci add system led
uci set system.@led[-1].name='br-lan'
uci set system.@led[-1].sysfs='blue:wps'
uci set system.@led[-1].trigger='netdev'
uci set system.@led[-1].dev='br-lan'
uci add_list system.@led[-1].mode='link'
uci add_list system.@led[-1].mode='tx'
uci add_list system.@led[-1].mode='rx'
uci commit system
/etc/init.d/led reload

cronで点灯消灯を時間制御
cat >> /etc/crontabs/root << 'EOF'
# 0時にbr-wan・br-lanのLEDを消灯
0 0 * * * uci set system.@led[0].trigger='none' && uci set system.@led[1].trigger='none' && uci commit system && /etc/init.d/led restart
# 8時にbr-wan・br-lanのLEDを点灯(netdev連動に戻す)
0 8 * * * uci set system.@led[0].trigger='netdev' && uci set system.@led[1].trigger='netdev' && uci commit system && /etc/init.d/led restart
EOF
/etc/init.d/cron restart

パッケージ

母国語サポート

LuCiの言語パッケージをインストール
  • 対応言語検索
#! /bin/sh
PATTERN="luci-i18n-base*"
command -v opkg && opkg update && opkg list $PATTERN
command -v apk  && apk update  && apk search $PATTERN
  • 設定
#! /bin/sh
I18N="ja"
PKGS="luci-i18n-base-${I18N} luci-i18n-firewall-${I18N}"
command -v opkg && opkg update && opkg install $PKGS
command -v apk && apk update && apk add $PKGS

UCI(TTYD)

ブラウザーでUCIにアクセス
#! /bin/sh
I18N="ja"
PKGS="luci-i18n-ttyd-${I18N}"
command -v opkg && opkg update && opkg install $PKGS
command -v apk && apk update && apk add $PKGS
uci set ttyd.@ttyd[0].ipv6='1'
uci set ttyd.@ttyd[0].command='/bin/login -f root' #自動ログイン
uci commit ttyd
/etc/init.d/rpcd restart

ファイラー (filebrowser)

ブラウザでファイルにアクセス
mkdir -p /tmp && wget --no-check-certificate -O /tmp/filebrowser.sh "https://site-u.pages.dev/www/custom-scripts/filebrowser.sh" && chmod +x /tmp/filebrowser.sh && sh /tmp/filebrowser.sh -i

※/etc/config/filebrowserのパスワードは12文字以上必須


ファイラー (openssh-sftp-server)

WinSCPでファイルにアクセス
#! /bin/sh
PKGS="openssh-sftp-server"
command -v opkg && opkg update && opkg install $PKGS
command -v apk  && apk update  && apk add $PKGS

クライアント設定(Windows)

  • 手動インストール
  • 自動インストール
    • キー入力:Win+x > a > はい
    • 最新版ソフトウェアのインストール
powershell:クライアントPC
$psVersion = $PSVersionTable.PSVersion.Major
$LINKS = Invoke-WebRequest "https://winscp.net/eng/download.php"
$LINKS_VERSION = $LINKS.Links | Where-Object {$_.href -like "*WinSCP-*-Setup.exe*"} | Select-Object -ExpandProperty href
$VERSION = ($LINKS_VERSION -split '/')[-2] -replace "WinSCP-([0-9]+\.[0-9]+\.[0-9]+).*", '$1'
Write-Host "Version to install: $VERSION"
$downloadUrl = "https://jaist.dl.sourceforge.net/project/winscp/WinSCP/$VERSION/WinSCP-$VERSION-Setup.exe?viasf=1"
Write-Host "Downloading from: $downloadUrl"
$ONAMAE = (whoami).Split('\')[1]
$destinationPath = "C:\Users\$ONAMAE\Downloads\WinSCP-$VERSION-Setup.exe"
Invoke-WebRequest -Uri $downloadUrl -OutFile $destinationPath
Write-Host "Installing WinSCP..."
Start-Process -FilePath $destinationPath -ArgumentList "/VERYSILENT /NORESTART" -Wait
Invoke-Expression "C:\Users\$ONAMAE\AppData\Local\Programs\WinSCP\WinSCP.exe"
  • 警告 > 強制的に貼り付け

  • WinSCP設定

    • セッション
      • ホスト名:192.168.1.1
      • ユーザー名:root
      • パスワード:設定したパスワード
      • ログインをクリック

CPU負荷分散

インストール
#! /bin/sh
PKGS="irqbalance"
command -v opkg && opkg update && opkg install $PKGS
command -v apk  && apk update  && apk add $PKGS
uci set irqbalance.irqbalance=irqbalance
uci set irqbalance.irqbalance.enabled='1'
uci commit irqbalance
/etc/init.d/irqbalance start

ネットワーク統計インターフェイス

インストール
#! /bin/sh
I18N="ja"
PKGS="luci-i18n-statistics-${I18N}"
command -v opkg && opkg update && opkg install $PKGS
command -v apk && apk update && apk add $PKGS
/etc/init.d/collectd enable
/etc/init.d/rpcd restart
  • プラグイン検索
#! /bin/sh
PATTERN="collectd-mod\*"
command -v opkg && opkg update && opkg list $PATTERN
command -v apk  && apk update  && apk search $PATTERN

追加テーマ

インストール
#! /bin/sh
PKGS="luci-theme-openwrt luci-theme-material"
command -v opkg && opkg update && opkg install $PKGS
command -v apk  && apk update  && apk add $PKGS

AdGuard Home

インストール

オフィシャル版
OpenWrt版

初期設定

  • インストール及びリムーブ
mkdir -p /tmp && wget --no-check-certificate -O /tmp/adguardhome.sh "https://site-u.pages.dev/www/custom-scripts/adguardhome.sh" && chmod +x /tmp/adguardhome.sh && sh /tmp/adguardhome.sh

Webmin

インストール
  • webminインストール
#!/bin/sh
set -e

## 1. 変数定義
INSTALL_DIR=/usr/libexec/webmin        # Webmin 本体設置先
CONFIG_DIR=/etc/webmin                # 設定ディレクトリ
LOG_DIR=/lib/webmin                   # ログ出力先
PID_DIR=/var/run/webmin               # pid 保存先
USER=admin                            # 管理ユーザー名
PASS=$(tr -dc A-Za-z0-9 </dev/urandom | head -c12)   # ランダムパスワード
PORT=10000                            # リッスンポート

## 2. 依存パッケージのインストール(SSL 有効化に perl-net-ssleay)
opkg update
BASE_PKGS="
  tar gzip unzip shared-mime-info openssl-util
  coreutils coreutils-install coreutils-stty coreutils-df
  make gawk fdisk patch diffutils binutils-objdump
  perl perlbase perlbase-bignum perlbase-commons perlbase-time
  perl-test-warn perl-test-harness
"
PERLBASE_PKGS="$(opkg list | awk '/^perlbase-/{print $1}')"
opkg install $BASE_PKGS $PERLBASE_PKGS --force-overwrite
echo "依存パッケージのインストールが完了しました。"

## 3. グループ「bin」の確認/追加
grep -q '^bin:' /etc/group || echo 'bin:x:2:' >> /etc/group

## 4. ディレクトリ作成
mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${LOG_DIR} ${PID_DIR}

## 5. Webmin ダウンロード&展開
wget --no-check-certificate -O /tmp/webmin.tar.gz https://www.webmin.com/download/webmin-current.tar.gz
tar xzf /tmp/webmin.tar.gz -C ${INSTALL_DIR} --strip-components=1

## 6. setup.sh の無人化パッチ
#  atboot=1 → 0(xinetd 非依存化)
#  makeboot=1 行削除
sed -i -e 's/^atboot=1/atboot=0/' -e '/^makeboot=1/d' ${INSTALL_DIR}/setup.sh

## 7. setup.sh を非対話で実行
perl_bin=$(which perl || echo /usr/bin/perl)
os_name="Generic Linux"
os_ver=$(uname -r)
/usr/bin/env bash <<EOF | ${INSTALL_DIR}/setup.sh ${INSTALL_DIR}

${LOG_DIR}
${perl_bin}
${os_name}
${os_ver}
${PORT}
${USER}
${PASS}
${PASS}
n
EOF

## 8. SSL 無効化(SSLeay ライブラリなしでも動くように)
sed -i -e 's/^ssl=1/ssl=0/' ${CONFIG_DIR}/miniserv.conf

## 9. procd/rc.common ベースの init スクリプト作成
cat << 'EOI' > /etc/init.d/webmin
#!/bin/sh /etc/rc.common
START=90
STOP=10
USE_PROCD=1

PIDFILE=/var/run/webmin/miniserv.pid
COMMAND=/etc/webmin/.start-init

start_service() {
    procd_open_instance
    procd_set_param command ${COMMAND}
    procd_set_param pidfile ${PIDFILE}
    procd_set_param respawn
    procd_close_instance
}

stop_service() {
    /etc/webmin/.stop-init
}

reload_service() {
    /etc/webmin/.reload-init
}
EOI

chmod +x /etc/init.d/webmin

## 10. サービス有効化&起動
/etc/init.d/webmin enable
/etc/init.d/webmin start

## 完了メッセージ
echo "-------------------------------------------------"
echo "Webmin Installed!"
echo "URL:   http://$(ip addr show dev br-lan | grep -Po 'inet \K[\d.]+'):${PORT}/"
echo "User:  ${USER}"
echo "Pass:  ${PASS}"
echo "Log:   ${LOG_DIR}/miniserv.log"
echo "-------------------------------------------------"


  • リムーブ
service webmin stop
rm -rf /etc/init.d/webmin
rm -rf /etc/init.d/webmin.old
sed -i "/bin:x:2:/d" /etc/group
sed -i "/webmin/d" /etc/services
/etc/webmin/uninstall.sh

テスト

iperf3

インストール
#!/bin/sh

TMP=/tmp/aios
mkdir -p "$TMP"
cat > "$TMP"/iperf3_setup.sh << 'SCRIPT_END'
#!/bin/sh

# Usage: ./iperf3_setup.sh [start|stop|restart|enable|disable|status|interactive]

SERVICE_FILE="/etc/init.d/iperf3"
PIDFILE="/var/run/iperf3.pid"
LAN="br-lan"

RED='\033[1;31m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
BLUE='\033[1;34m'
CYAN='\033[1;36m'
NC='\033[0m'

print_msg() { printf "${1}${2}${NC}\n"; }

get_iface_addrs() {
    local flag=0
    if ip -4 -o addr show dev "$LAN" scope global 2>/dev/null | grep -q 'inet '; then
        NET_ADDR=$(ip -4 -o addr show dev "$LAN" scope global | awk 'NR==1{sub(/\/.*/,"",$4); print $4}')
        flag=$((flag | 1))
    else
        printf "\033[1;33mWarning: No IPv4 address on %s\033[0m\n" "$LAN"
    fi
    
    if ip -6 -o addr show dev "$LAN" scope global 2>/dev/null | grep -q 'inet6 '; then
        NET_ADDR6_LIST=$(ip -6 -o addr show dev "$LAN" scope global | grep -v temporary | awk 'match($4,/^(2|fd|fc)/){sub(/\/.*/,"",$4); print $4;}')
        flag=$((flag | 2))
    else
        printf "\033[1;33mWarning: No IPv6 address on %s\033[0m\n" "$LAN"
    fi
    
    case $flag in
        3) FAMILY_TYPE=any ;;
        1) FAMILY_TYPE=ipv4 ;;
        2) FAMILY_TYPE=ipv6 ;;
        *) FAMILY_TYPE="" ;;
    esac
}

get_lan_ip() {
    get_iface_addrs
    if [ -n "$NET_ADDR" ]; then
        printf "${NET_ADDR}"
    else
        printf "192.168.1.1"
    fi
}

check_package() {
    command -v iperf3 >/dev/null 2>&1 || {
        command -v apk >/dev/null 2>&1 && apk info iperf3 >/dev/null 2>&1
    } || {
        command -v opkg >/dev/null 2>&1 && opkg list-installed | grep -q "^iperf3 "
    }
}

check_service() { [ -f "$SERVICE_FILE" ]; }

install_package(){
  PKGS="iperf3"
  print_msg "$BLUE" "Installing $PKGS package"
  command -v opkg >/dev/null 2>&1 && opkg update >/dev/null 2>&1 && opkg install $PKGS && return 0
  command -v apk  >/dev/null 2>&1 && apk update >/dev/null 2>&1 && apk add $PKGS && return 0
  print_msg "$RED" "No supported package manager found (opkg/apk)"
  return 1
}

create_service() {
    local ip
    ip=$(get_lan_ip)
    print_msg "$BLUE" "Creating iperf3 service for $ip:5201"
    
    cat > "$SERVICE_FILE" << 'SERVICEEOF'
#!/bin/sh /etc/rc.common
START=95
STOP=01
USE_PROCD=1
PROG=/usr/bin/iperf3
pidfile=/var/run/iperf3.pid

start_service() {
    local ip
    ip=$(ip -4 -o addr show dev br-lan scope global 2>/dev/null | awk 'NR==1{sub(/\/.*/,"",$4); print $4}')
    [ -z "$ip" ] && ip="192.168.1.1"
    
    printf "Starting iperf3 server on %s:5201\n" "$ip"
    procd_open_instance
    procd_set_param command "$PROG" --server --daemon --pidfile "$pidfile" --bind "$ip"
    procd_set_param pidfile "$pidfile"
    procd_set_param respawn
    procd_close_instance
}

stop_service() {
    printf "Stopping iperf3 server\n"
    [ -f "$pidfile" ] && {
        pid=$(cat "$pidfile" 2>/dev/null)
        [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && {
            kill "$pid" 2>/dev/null && printf "iperf3 server stopped\n" || {
                printf "Failed to stop iperf3 server (PID: %s)\n" "$pid"; return 1
            }
        }
        rm -f "$pidfile"
    }
}

status() {
    local ip pid
    ip=$(ip -4 -o addr show dev br-lan scope global 2>/dev/null | awk 'NR==1{sub(/\/.*/,"",$4); print $4}')
    [ -z "$ip" ] && ip="192.168.1.1"
    
    [ -f "$pidfile" ] && {
        pid=$(cat "$pidfile" 2>/dev/null)
        [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && {
            printf "iperf3 server is running (PID: %s) on %s:5201\n" "$pid" "$ip"; return 0
        }
        printf "iperf3 server is not running (stale pidfile)\n"; rm -f "$pidfile"; return 1
    } || { printf "iperf3 server is not running\n"; return 1; }
}
SERVICEEOF
    
    chmod +x "$SERVICE_FILE"
    print_msg "$GREEN" "Service configured for $ip:5201"
}

install_iperf3() {
  print_msg "$BLUE" "Installing iperf3"
  command -v iperf3 >/dev/null 2>&1 || install_package iperf3 || { print_msg "$RED" "Package installation failed"; return 1; }
  [ -f "$SERVICE_FILE" ]   || create_service  || { print_msg "$RED" "Service creation failed";  return 1; }
  print_msg "$GREEN" "Installation completed"
  printf "${CYAN}service iperf3 [enable|start|stop|status]${NC}\n"
  printf "${CYAN}Test: iperf3 -c $(get_lan_ip) -t 10${NC}\n"
}

remove_iperf3() {
    local auto="$1"
    print_msg "$BLUE" "Removing iperf3"
    
    local pkg=false svc=false
    check_package && pkg=true
    check_service && svc=true
    
    [ "$pkg" = "false" ] && [ "$svc" = "false" ] && { print_msg "$RED" "iperf3 not found"; return 1; }
    
    [ "$auto" != "auto" ] && {
        printf "Remove iperf3? (y/N): "
        read -r confirm
        case "$confirm" in 
            [yY]*) ;; 
            *) print_msg "$YELLOW" "Cancelled"; return 0;; 
        esac
    }
    
    [ "$svc" = "true" ] && {
        print_msg "$BLUE" "Removing service"
        service iperf3 stop 2>/dev/null || true
        service iperf3 disable 2>/dev/null || true
        rm -f "$SERVICE_FILE" "$PIDFILE"
        print_msg "$GREEN" "Service removed"
    }
    
    [ "$pkg" = "true" ] && {
        print_msg "$BLUE" "Removing package"
        if command -v apk >/dev/null 2>&1; then
            apk del iperf3 && print_msg "$GREEN" "Package removed" || { print_msg "$RED" "Remove failed"; return 1; }
        elif command -v opkg >/dev/null 2>&1; then
            opkg remove iperf3 && print_msg "$GREEN" "Package removed" || { print_msg "$RED" "Remove failed"; return 1; }
        else
            print_msg "$YELLOW" "Cannot remove package - no package manager"
        fi
    }
    
    print_msg "$GREEN" "Removal completed"
}

show_usage() {
    printf "Usage: %s [command]\n\n" "$0"
    printf "Commands:\n"
    printf "  start       - Start iperf3 service\n"
    printf "  stop        - Stop iperf3 service\n"
    printf "  restart     - Restart iperf3 service\n"
    printf "  enable      - Enable service at boot\n"
    printf "  disable     - Disable service at boot\n"
    printf "  status      - Show service status\n"
    printf "  interactive - Interactive mode (default)\n"
    printf "  help        - Show this help\n\n"
    printf "Note: Install/Uninstall is determined automatically\n"
}

interactive_mode() {
    while true; do
        printf "\n${BLUE}iperf3 Management${NC}\n"

        check_package && pkg=true || pkg=false
        check_service && svc=true || svc=false
        if [ "$svc" = "true" ]; then
            service iperf3 status >/dev/null 2>&1 && running=true || running=false
        else
            running=false
        fi

        if [ "$pkg" = "false" ] || [ "$svc" = "false" ]; then
            printf "[1] Install iperf3\n"
            printf "[2] Exit\n"
            printf "Please select (1-2): "
            read -r choice

            case "$choice" in
                1)
                    install_iperf3
                    ;;
                2)
                    print_msg "$GREEN" "Goodbye"
                    break
                    ;;
                *)
                    printf "${RED}Invalid selection: %s${NC}\n" "$choice"
                    ;;
            esac

        else
            printf "[1] Start service     [5] Disable service\n"
            printf "[2] Stop service      [6] Show status\n"
            printf "[3] Restart service   [7] Remove iperf3\n"
            printf "[4] Enable service    [8] Exit\n"
            printf "Please select (1-8): "
            read -r choice

            case "$choice" in
                1) service iperf3 start;;
                2) service iperf3 stop;;
                3) service iperf3 restart;;
                4)
                    service iperf3 enable
                    print_msg "$GREEN" "Enabled at boot"
                    ;;
                5)
                    service iperf3 disable
                    print_msg "$GREEN" "Disabled at boot"
                    ;;
                6)
                    service iperf3 status
                    ;;
                7)
                    remove_iperf3
                    ;;
                8)
                    print_msg "$GREEN" "Goodbye"
                    break
                    ;;
                *)
                    printf "${RED}Invalid selection: %s${NC}\n" "$choice"
                    ;;
            esac
        fi

        if [ "$choice" != "2" ] && [ "$choice" != "8" ]; then
            printf "\n${CYAN}Press Enter to continue${NC}"
            read -r dummy 2>/dev/null || true
        fi
    done
}

iperf3_main() {
    case "${1:-interactive}" in
        start|stop|restart)
            check_service || {
                print_msg "$RED" "Service not configured. Run script to install."
                exit 1
            }
            service iperf3 "$1"
            ;;

        enable|disable)
            check_service || {
                print_msg "$RED" "Service not configured. Run script to install."
                exit 1
            }
            service iperf3 "$1"
            print_msg "$GREEN" "Service ${1}d at boot"
            ;;

        status)
            check_service || {
                print_msg "$RED" "Service not configured"
                exit 1
            }
            service iperf3 status
            printf "\n${CYAN}Server: $(get_lan_ip):5201${NC}\n"
            printf "${CYAN}Test: iperf3 -c $(get_lan_ip) -t 10${NC}\n"
            ;;

        interactive)
            interactive_mode
            ;;

        auto-toggle)
            if check_package || check_service; then
                remove_iperf3
            else
                install_iperf3
            fi
            ;;

        help|-h|--help)
            show_usage
            ;;

        *)
            print_msg "$RED" "Unknown command: $1"
            show_usage
            exit 1
            ;;
    esac
}

[ "${0##*/}" = "iperf3_setup.sh" ] && iperf3_main "$@"
SCRIPT_END

chmod +x "$TMP"/iperf3_setup.sh
printf "\n${GREEN}=== iperf3 Setup Script Created ===${NC}\n"
printf "${CYAN}Location: $TMP/iperf3_setup.sh${NC}\n"
printf "${YELLOW}Usage: $TMP/iperf3_setup.sh [start|stop|status|help]${NC}\n"
printf "${YELLOW}Interactive: $TMP/iperf3_setup.sh${NC}\n"

printf "\n${BLUE}Running script in interactive mode${NC}\n"
sh "$TMP"/iperf3_setup.sh
  • サーバー起動 (2回目以降)
/etc/init.d/iperf3 start
  • クライアントで実行
$iperf3 = Get-Command iperf3.exe -ErrorAction SilentlyContinue
if (-not $iperf3) {
    $iperf3 = Get-ChildItem -Path $PWD -Filter iperf3.exe -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
    if ($iperf3) { $iperf3 = $iperf3.FullName }
    else {
        Write-Host "iperf3.exe が見つかりません。" -ForegroundColor Red
        exit 1
    }
} else {
    $iperf3 = $iperf3.Source
}

$server = (Get-DnsClientServerAddress -AddressFamily IPv4 | Select-Object -ExpandProperty ServerAddresses)[0]

& "$iperf3" -c $server -t 10
  • 追加テスト
& "$iperf3" -c $server -t 60 -P 16 -w 2M
  • リムーブ
sh /tmp/aios/iperf3_setup.sh auto-toggle

トラブル

既知の問題

2025年7月現在
  • Wi-FiはBEよりAXの方が安定的かつ速い
    ※AXは最速レベル (ただしノイズフロアは-80 dBmでちょっと非常用的)

2026年2月現在:EEPROM
  • 25.12.0-RC5
  • キャリブレーション済みEEPROMデータ
    MT7990_EEPROM_iFEM233.binは実装されているが
    キャリブレーションデータはスカスカ
ls /lib/firmware/mediatek/mt7996/

初期化

ファクトリーリセット(初期化)

リセット
# 要注意
firstboot && reboot now

This will erase all settings and remove any installed packages. Are you sure? [N/y]

初期化:入力モード
y

デバイスリセットボタン
デバイスのリセットボタンを10秒押し続ける


あとがき

  • BPI-R3より、色々進化している

  • ハードウェアアクセレーターは有効

  • Wi-Fi7はまだまだ成熟していないが、逆にどんどん良くなるのが面白いかな

  • 購入について、AliExpressが一番安いが、アフターサービスを考えると、Amazonやエレファインの方が安心かもしれない

  • モデム動かすのに、丸1日掛かった。。。

  • Wi-Fi BEはまだ不安定だが、AXの完成度は高い
    ※BPI-R3より早いね (アップロードはWANチューニングの問題)
    ocn.png

  • BPI-BE1900 なんだか値が張りそうな気がするが

  • AdGuardHomeとWebminのメンテついでに掲載した

  • AW7916-NPDを換装してみた
    BPI-R4-NIC-BE14より気持ちSNRが良い程度
    ボード側の方に問題がありそうだ
    このBPI-R4は文字通り机上の検証用だね

  • 2026年1月31日
    mloが動いた!
    が、しかし、、、(6GHz + 2.4GHz)
    image.png
    もとい、、、 (6GHz + 5GHz)
    image.png
    DFSで2Ghz掴んじゃうと、なんかおかしい?

4
3
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?