نظام Linux لبوابة Matter: بناء RDK X5 SDK وBuildroot داخل WSL
بناء SDK الخاص بـ D-Robotics RDK X5 داخل WSL ودمج بوابة مكتوبة بـ Rust في صورة Buildroot: الترجمة المتقاطعة والطبقات وإيقاف الخدمات والتشغيل التلقائي ونواة بلا IPv6 افتراضيًا.
في المقال السابق تحدثت عن كتابة برنامج البوابة باستخدام Rust وrs-matter، وهو يعمل حاليًا على الحاسوب. ولكي يصبح بوابة قابلة للبيع، تنقصه طبقة أخرى: نظام Linux الخاص بالبوابة نفسها.
عندما تصلك لوحة التطوير، تأتي عادةً مع صورة Ubuntu جاهزة تعمل بمجرد توصيل الكهرباء. وأول ما يخطر لكثيرين: لماذا لا نثبّت برنامج البوابة مباشرةً على Ubuntu وننتهي؟
هذا مقبول في مرحلة التطوير، لكنه لا يصلح للإنتاج الكمي. في هذا المقال أوثّق كيف بنينا SDK الخاص بلوحة RDK X5 من D-Robotics داخل WSL، وكيف صنعنا صورة نظام البوابة باستخدام Buildroot، إضافةً إلى بعض المطبات التي لا تكتشفها إلا حين تشمّر عن ساعديك.
لماذا لا نستخدم Ubuntu مباشرةً
يوفّر SDK الخاص بـ X5 من D-Robotics أصلًا نوعين من أنظمة الملفات الجذرية: صورة Ubuntu 22.04 للتطوير، وBuildroot للإنتاج الكمي. والفرق بينهما واضح جدًا في سياق البوابة:
| صورة Ubuntu | نظام Buildroot المخصص | |
|---|---|---|
| الحجم | عدة غيغابايتات | بضع مئات من الميغابايتات، وقسم system لا يتجاوز 250MB |
| المحتوى | مجموعة حزم كاملة بمستوى سطح المكتب | فقط ما تختاره بنفسك |
| خدمات الإقلاع | كثيرة، ولا تعرف بوضوح أيّها يعمل | كل واحدة منها ظاهرة ويمكن حذفها |
| طريقة التحديث | apt، تحديث حزمة تلو الأخرى | استبدال صورة النظام كاملة، ومع أقسام A/B يمكن التراجع |
| التدقيق الأمني | عليك تبرير كومة من البرمجيات غير المستخدمة | سطح هجوم صغير يسهل شرحه |
البوابة جهاز يوضع في منزل المستخدم ولن يسجّل أحد الدخول إليه لسنوات. ما يحتاجه هو: تشغيل برنامج البوابة فور الإقلاع، ونظام للقراءة فقط لا يتلف بسبب الكتابة، وعودة تلقائية إلى الإصدار القديم إذا فشل التحديث. هذه كلها ممارسات معتادة في Buildroot، أما في Ubuntu فتتطلب جهدًا كبيرًا في التقليم.
لماذا بدأنا بـ RDK X5
في مقال اختيار المعالج الرئيسي صنّفنا RDK X5 ضمن فئة «صالح للاستخدام لكنه غير مناسب للبوابة»: فقدرة الذكاء الاصطناعي البالغة 10 TOPS لا حاجة للبوابة بها، والتكلفة مرتفعة نسبيًا.
لكن له ميزة عملية جدًا: الـ SDK متاح للتنزيل علنًا، ولكلٍّ من البناء وتقليم النظام والإقلاع الآمن وA/B OTA والتخزين الآمن عبر OP-TEE توثيق رسمي. كما أن SDK الخاص بـ Rockchip مبني هو الآخر على Buildroot، والطريقة متشابهة إلى حد كبير. لذلك قررنا في مرحلة تخصيص النظام أن نمرّ بالمسار كاملًا أولًا باستخدام SDK الخاص بـ X5، فهو الأوفر توثيقًا.
ولا بد من التنبيه مسبقًا: هذا المقال يغطي مرحلة بناء الصورة فقط، ولم نحرقها على اللوحة ونختبرها فعليًا بعد. أما الحرق والتشغيل الفعلي بعد وصول اللوحة فسيأتيان في مقالات لاحقة.
البيئة: توزيعة WSL مستقلة
يتطلب SDK الخاص بـ X5 LNX6.1.176_PL5.1_V2.0.0 نظام Ubuntu 22.04، ويبلغ حجمه بعد فك الضغط نحو 9.5GB. أستخدم يوميًا Ubuntu 24.04 ولم أرد خلط اعتماديات الـ SDK به، فثبّت توزيعة مستقلة ووضعت قرصها الافتراضي على القرص D:
wsl --install -d Ubuntu-22.04 --name x5build --location 'D:\wsl\x5build' --no-launch
عند تنفيذ أوامر wsl من Git Bash يجب إضافة MSYS_NO_PATHCONV=1، وإلا فإن Git Bash سيعيد كتابة المسارات التي تبدأ بـ / لتصبح مسارات Windows.
المكونات الرئيسية في الـ SDK:
| المكوّن | الإصدار |
|---|---|
| نواة Linux | 6.1.176 |
| U-Boot | 2022.10 |
| ATF | 2.8 |
| Buildroot | 2025.02 |
| نظام الملفات الجذري | glibc، SysV init |
| سلسلة الأدوات التقاطعية | ARM GNU Toolchain 11.3 (aarch64)، مضمّنة في الـ SDK |
جلب الشيفرة، وتثبيت الاعتماديات، وأول بناء كامل:
B=https://archive.d-robotics.cc/downloads/sdk/LNX6.1.176_PL5.1_V2.0.0/board_support_package
curl -O $B/platform_source_code.tar.gz.md5sum
curl -C - --retry 5 -O $B/platform_source_code.tar.gz # 约 4.1GB,断了能续传
md5sum -c platform_source_code.tar.gz.md5sum
tar -I pigz -xf platform_source_code.tar.gz && cd platform_source_code
sudo build/install_host_deps.sh
sudo tar -xf toolchain/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/
./bd.sh lunch 8 # board_x5_soc_debug_config.mk
./bd.sh # 第一次完整编译,约 12 分钟
lunch 8 هو إعداد debug، ويتضمن adb وأدوات التصحيح؛ أما للإنتاج الكمي فيجب التحويل إلى إعداد release عبر lunch 9.
انتبه أيضًا للذاكرة: الحد العام لـ WSL عندي 8GB، بينما يوصي الـ SDK بـ 16GB. حاليًا أبني البوابة وأعيد التحزيم فقط، فتكفي 8GB؛ أما إذا احتجت لاحقًا إلى بناء Buildroot بالكامل من المصدر ولم تكفِ الذاكرة، فعدّل memory= في C:\Users\<用户名>\.wslconfig ثم نفّذ wsl --shutdown. لاحظ أن هذا يوقف جميع توزيعات WSL.
دمج برنامج البوابة في النظام
كتبت المسار كله في سكربت واحد، فبعد تعديل الشيفرة يكفي تنفيذ سطر واحد من Windows:
wsl -d x5build -- bash /mnt/d/<项目目录>/x5/build.sh
يقوم السكربت بما يلي: مزامنة الشيفرة المصدرية، والترجمة التقاطعية لبرنامج البوابة، ووضعه في نظام الملفات الجذري، وإعادة تحزيم الصورة. وفي الطريق بعض التفاصيل التي تستحق الذكر.
مزامنة الشيفرة إلى نظام ملفات Linux قبل البناء
شيفرة المشروع موجودة على القرص D في Windows، ويقابلها /mnt/d داخل WSL. بناء Rust مباشرةً تحت /mnt/d أبطأ بكثير، لأن كل وصول إلى ملف يعبر نظام ملفات Windows. لذلك يستخدم السكربت أولًا rsync لمزامنة الشيفرة إلى مجلد داخل WSL نفسه (مع استثناء target و.git)، ثم يبني هناك.
استهداف glibc بدلًا من musl
أثناء اختيار المعالج الرئيسي كنا نخطط لاستخدام aarch64-unknown-linux-musl لبناء ملف ثنائي مربوط ربطًا ساكنًا يعمل على لوحات Rockchip وD-Robotics معًا.
لكن على X5 تحوّلنا إلى aarch64-unknown-linux-gnu، مع تحديد GCC 11.3 المضمّن في الـ SDK كرابط:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc \
cargo build --release --target aarch64-unknown-linux-gnu
والسبب أن نظام الملفات الجذري في X5 مبني أصلًا على glibc. وعند الربط بسلسلة أدوات الـ SDK، يكون إصدار glibc المرفق بها أقدم من الموجود في البرنامج الثابت، فيعمل البرنامج الناتج على اللوحة بلا شك، ولا داعي للتعامل مع مشكلات توافق musl.
مجلد التراكب: استعادة الأصل أولًا ثم التراكب في كل مرة
يوفر الـ SDK مجلد تراكب لنظام الملفات الجذري هو system/buildroot/prebuilt/boot-utils-runtime، وأي ملف يوضع فيه يدخل الصورة كما هو. أما ملفاتنا فنضعها في x5/overlay/ داخل المشروع.
في أول تشغيل للسكربت، يأخذ نسخة احتياطية من مجلد التراكب الأصلي للـ SDK كلقطة. وبعدها، في كل بناء، يستعيد اللقطة أولًا، ثم يضيف ملفاتنا فوقها.
ميزة هذا الأسلوب أنه قابل للعكس: احذف ملفًا من x5/overlay/ فيعود ما في الصورة إلى نسخة الـ SDK الأصلية، دون أن يبقى أثر من التعديل السابق.
إيقاف خدمات الإقلاع غير المستخدمة
إيقاف الخدمات يتم أيضًا عبر التراكب: نضع سكربتًا بالاسم نفسه لا يحتوي إلا على exit 0، فيحل محل سكربت الإقلاع الأصلي في الـ SDK. هذه هي الخدمات الموقوفة حاليًا:
| الخدمة | سبب الإيقاف |
|---|---|
S50telnet |
تسجيل دخول عن بُعد بنص مكشوف، لا يفي بمتطلبات لوائح الأمن السيبراني مثل EN 18031 الأوروبية وPSTI البريطانية |
S40bluetoothd |
البلوتوث المدمج في الوحدة غير مستخدم، وBluetooth Mesh يمر عبر وحدة Silicon Labs منفصلة |
S65ptp4l، S66phc2sys |
مزامنة الوقت الدقيقة عبر PTP، لا تحتاجها البوابة |
S90cam-service |
خدمة الكاميرا |
S99iiod |
خدمة حساسات IIO |
نُبقي sshd وadb في مرحلة التطوير، ونوقفهما عند الإنتاج الكمي.
التشغيل التلقائي عند الإقلاع: مطبٌّ سببه & واحدة
يجب أن يبدأ برنامج البوابة تلقائيًا عند الإقلاع، وأن يُعاد تشغيله تلقائيًا إذا انهار. يستخدم X5 نظام SysV init، فيكفي كتابة /etc/init.d/S95gateway.
أول ما يجب الانتباه إليه: لا يوجد bash على اللوحة. فمنذ الإصدار V2.0.0 لم يعد SDK الخاص بـ X5 يتضمن bash، ولا يمكن كتابة سكربتات النظام إلا بـ POSIX sh.
أما المطب الثاني فأخفى. الطريقة الأكثر بداهة هي وضع حلقة المراقبة في الخلفية داخل سكربت الإقلاع:
while true; do /usr/bin/gateway; sleep 2; done &
يبدو الأمر سليمًا، لكن عند الإقلاع، حين ينتهي rcS من تشغيل كل سكربتات الإقلاع ويُنهي نفسه، يرسل إشارة قطع الاتصال (hangup) إلى العمليات الخلفية التي أطلقها، فتُقتل هذه الحلقة معه. يظهر برنامج البوابة لحظةً عند الإقلاع ثم يختفي.
الطريقة الصحيحة هي استخدام start-stop-daemon -b لفصل العملية الخفية عن جلسة rcS:
LOOP="cd /userdata/gateway && while true; do /usr/bin/gateway </dev/null 2>&1 | logger -t gateway; sleep 2; done"
start-stop-daemon -S -b -m -p /var/run/gateway.pid -x /bin/sh -- -c "$LOOP"
إذا خرج البرنامج بشكل غير طبيعي يُعاد تشغيله تلقائيًا بعد ثانيتين، وتذهب مخرجاته إلى سجل النظام عبر logger. لا يوجد logread على X5، والسجلات موجودة في /userdata/log/kernel/message، ويمكن البحث فيها بـ grep gateway. أما بيانات البوابة فتُحفظ في /userdata/gateway/data/، وهذا القسم قابل للكتابة، بينما قسم النظام للقراءة فقط.
إعدادان افتراضيان في الـ SDK لا بد من تغييرهما
النواة لا تفعّل IPv6 افتراضيًا
هذا أخطر مطب في العملية كلها.
إعداد النواة الافتراضي في SDK الخاص بـ X5 يحتوي على # CONFIG_IPV6 is not set، وذلك في كلا الإعدادين hobot_x5_soc_defconfig وhobot_x5_soc_perf_defconfig.
في المقابل، Matter مبني فوق IPv6: فاكتشاف الأجهزة عبر mDNS، والاتصال بين Apple Home والبوابة، كلاهما يمر عبر IPv6. مع النواة الافتراضية للـ SDK يعمل برنامج البوابة، لكن Apple Home لن يعثر عليه أبدًا.
يفحص السكربت هذين الإعدادين، وإن وجد IPv6 غير مفعّل يغيّره إلى CONFIG_IPV6=y، ثم يعيد بناء النواة بـ ./bd.sh boot. وعند الانتقال مستقبلًا إلى إصدار أحدث من الـ SDK، يجب التحقق من هذا البند مجددًا.
جدول الأقسام: إزالة قسم app بحجم 700MB
يحتوي جدول الأقسام الافتراضي في الـ SDK على قسم app بحجم 700MB، لا يضم سوى البرامج التجريبية من D-Robotics، ولا حاجة للبوابة به. يحذفه السكربت بمقطع Python صغير من x5-soc-debug-gpt.json وx5-soc-release-gpt.json.
بمجرد تعديل جدول الأقسام، يلزم بناء كامل من جديد لتوليد GPT جديد. يتحقق السكربت مما إذا كان التعديل قد تم، فيتخطاه إن كان كذلك، ولا يعيد البناء الكامل في كل مرة.
بعد الإزالة يصبح قسم system بحجم 250MB، وuserdata بحجم 50MB فقط. تكفي 50MB حاليًا لتخزين جدول أجهزة البوابة وبيانات Matter، لكن لا بد من إعادة تخطيطها وفق الاحتياجات الفعلية قبل الإنتاج الكمي.
ما الذي خرجنا به
| الملف | الاستخدام |
|---|---|
out/product/emmc_disk.img |
صورة eMMC كاملة، يمكن حرقها عبر fastboot أو DFU أو TFTP |
out/product/emmc_disk.simg |
صورة متفرقة (sparse)، حجمها صغير لكنها لا تعمل مع DFU |
out/product/uart_usb/ |
برنامج إقلاع ثابت لحرق الشرائح الفارغة |
out/product/*.img |
صور منفصلة لكل قسم |
أداة الحرق xburn موجودة في software_tools/download_tools/ ضمن مجلد تنزيلات الـ SDK.
ما لم ننجزه بعد
بناء الصورة مجرد خطوة أولى. وما زالت أمامنا هذه المهام قبل الإنتاج الكمي:
- الحرق على اللوحة والاختبار الفعلي: التشغيل التلقائي، وIPv6، وإعداد Matter، والسجلات، كلها يجب تجربتها على الجهاز الحقيقي.
- أقسام A/B وOTA: يتضمن الـ SDK تحديثًا سلسًا بنظام A/B، ويستطيع
./bd.sh otapackageتوليد حزم تحديث كاملة وتفاضلية، ويوفر جانب الجهاز واجهة C عبرlibupdate.so، فيتولى برنامجنا التنزيل والاستدعاء. لكن الإعداد الافتراضي قسم واحد، ويجب تعديل جدول الأقسام لتفعيله. وبهذا لا نحتاج إلى دمج RAUC أو SWUpdate بشكل منفصل. - التحويل إلى إعداد release: إزالة adb وأدوات التصحيح، وإيقاف sshd.
- الإقلاع الآمن: استبدال مفاتيح التوقيع في كل مراحل التحقق بعد U-Boot بمفاتيحنا الخاصة وحرقها في eFuse. حرق eFuse لا رجعة فيه، لذا يجب التحقق أولًا على عدد قليل من النماذج الأولية.
في الختام
بالنظر إلى الوراء، لم يكن الوقت المستهلك في هذه المرحلة في أوامر البناء نفسها، فـ bd.sh في الـ SDK مغلّف بإتقان. ما استهلك الوقت فعلًا هو تلك القيم الافتراضية: نواة بلا IPv6، ولوحة بلا bash، وعمليات خلفية يقتلها rcS، وجدول أقسام محشوّ بقسم تجريبي حجمه 700MB. لا يُظهر أيٌّ منها رسالة خطأ، بل يجعل البوابة تتوقف بصمت عن العمل في مرحلة ما.
الخطوة التالية هي انتظار وصول اللوحة، وحرق الصورة عليها، وتوصيل وحدة البلوتوث من Silicon Labs في الوقت نفسه، لتتحكم البوابة لأول مرة على عتاد حقيقي في مصباح يعمل بـ Bluetooth Mesh.
إذا كنت تبني أنت أيضًا بوابات وأجهزة IoT باستخدام X5 من D-Robotics أو Rockchip أو أي SDK آخر مبني على Buildroot، فيسعدني التواصل معك: linktr.ee/weiyii