بداية العمل
سلسلة الأدوات
يُبنى صافي بـESP-IDF v6.0.2، مثبتاً عبر EIM (مدير تثبيت ESP-IDF):
eim install -i v6.0.2 --idf-features=mcp --do-not-track true
ثم في كل صدفة تبني فيها:
source ~/.espressif/tools/activate_idf_v6.0.2.sh
أو شغل كل أمر عبر EIM بدلاً من تفعيل صدفة: eim run idf.py ...، eim run ./build.sh. يثبّت IDF بيئة Python الخاصة به ومترجمه وCMake (3.22.1 أو أحدث، وPython 3.10 أو أحدث)، فلا حاجة لتثبيت شيء آخر. لماذا نسخة مثبتة بدلاً من "الأحدث": تغير إصدارات IDF الثانوية افتراضيات Kconfig وسلوك المشغلات وتخطيط زمن الربط؛ البرنامج الثابت معتمد مقابل إصدار واحد بالضبط، وCI يبني الإصدار نفسه.
الاستنساخ والبناء
git clone https://github.com/ahmeddwalid/Safi.git
cd Safi
./build.sh
build.sh غلاف رقيق مقروء فوق idf.py يوجد لسبب واحد: طبقات الضبط (القسم التالي) يجب أن تُجمع بتناسق، وكتابتها يدوياً تدعو إلى الانحراف. تمر البناءات عبر الإعدادات المسبقة (presets) في CMake الخاصة بالمستودع (dev، st7789، production، test)، ويختار build.sh الإعداد المسبق حسب وضعه:
| الاستدعاء | الأثر |
|---|---|
./build.sh | بناء تطوير |
./build.sh clean | fullclean ثم بناء |
./build.sh --production | ملف الإنتاج: يتطلب sdkconfig.production ومفاتيح التوقيع والتشفير في البيئة، ويشغل فحوص ما قبل الإطلاق. انظر الأمان |
يحتاج الاستنساخ النظيف touch sdkconfig.local قبل أول idf.py --preset dev build: سلسلة افتراضيات الإعداد المسبق dev هي sdkconfig.defaults;sdkconfig.local، والملف المحلي المستثنى من git غير موجود في الاستنساخ الجديد.
اكتب على الفلاش وراقب المنفذ التسلسلي (منفذ USB-C في اللوحة جهاز USB تسلسلي أصلي، بلا محول):
./flash.sh # defaults to /dev/ttyACM0
./monitor.sh # idf.py monitor, Ctrl+] to exit
طبقات الضبط
ضبط ESP-IDF ملف sdkconfig واحد مولّد، يُبنى من افتراضيات متطابقة. يقسم صافي الطبقات حسب المقصد:
| الملف | متتبع في git | الغرض |
|---|---|---|
sdkconfig.defaults | نعم | خط الأساس للمشروع: جدول الأقسام، PSRAM، خيارات LVGL، موضع TLS، مقاسات مخازن WiFi المؤقتة |
sdkconfig.local | لا (مستثنى من git) | أسرار جهازك وتسهيلاته، أساساً CONFIG_SAFI_WIFI_SSID وCONFIG_SAFI_WIFI_PASSWORD |
sdkconfig.production | نعم | الملف المحصّن: الإقلاع الآمن، تشفير الفلاش، بوابات الإنتاج |
sdkconfig | لا (مولد) | ما استخدمه البناء فعلياً |
يضيف build.sh ملف sdkconfig.local لبناءات التطوير وsdkconfig.production لبناءات الإنتاج. هدف التصميم بسيط: لا يمكن أن تصل بيانات الاعتماد إلى git أبداً، لأن الملف الوحيد الذي يحملها لا يُتتبع أبداً، وبناء الإنتاج يرفض تضمينه.
لماذا لا نستخدم متغيرات البيئة لبيانات اعتماد WiFi؟ تتدفق قيم Kconfig إلى الملف الثنائي عبر آلية واحدة مدققة، وتظهر في menuconfig، وقابلة للمقارنة مع sdkconfig المولد؛ بينما عمليات البحث المخصصة في البيئة داخل CMake ليست أياً من ذلك.
تعيش الخيارات الخاصة بالمشروع تحت Safi Configuration في idf.py menuconfig؛ وكلها مفهرسة في مرجع Kconfig.
أول تعديل لك
حلقة سريعة من البداية إلى النهاية لإثبات صحة الإعداد:
-
عدّل
main/main.cوأضف سطر سجل فيapp_main:ESP_LOGI("HELLO", "my first Safi build"); -
./build.sh && ./flash.sh && ./monitor.sh -
اعثر على سطرك في سجل الإقلاع، مباشرة بعد تقرير الكومة.
البناء تزايدي؛ بعد أول ترجمة كاملة (بضع دقائق)، تعاد الترجمة بعد التعديلات في ثوانٍ.
المنفذ التسلسلي
المنفذ بسرعة 115200 باود هو أداة التطوير الأساسية. يسجل البرنامج الثابت بسخاء: تقدم الإقلاع مع معالم الكومة الداخلية، كل محاولة WiFi مع سبب فشلها ومسح للشبكات المرئية، تغيرات حالة التشغيل، ورمز API عند أول إقلاع. بدأت معظم جلسات تصحيح الأخطاء في قصص هذه الوثائق من الميدان بهذا السجل لا غير.
ملاحظتان عمليتان:
- فتح المنفذ يعيد تشغيل اللوحة (جهاز USB التسلسلي يوصل DTR/RTS بإعادة التشغيل، وهي الطريقة التي يعمل بها الفلاش أيضاً). لذا يرى الالتقاط المكتوب دائماً إقلاعاً جديداً.
- يفكك
idf.py monitorآثار الانهيار (backtraces) إلى أسماء دوال تلقائياً؛ أما طرفية خام فتعرض عناوين فقط. لفك العناوين يدوياً، انظر الملاحظة والمراقبة.
إخفاقات البناء الشائعة
أربعة أنماط فشل تفسر تقريباً كل بناء مكسور في تاريخ هذا المشروع:
| العَرَض | السبب | الإصلاح |
|---|---|---|
fatal error: cJSON.h: No such file or directory | مكوّن يستخدم مكتبة من دون الإعلان عنها | أضف الاسم الناقص (json هنا) إلى REQUIRES في CMakeLists.txt الخاص بذلك المكوّن |
undefined reference to esp_http_client_init | عُثر على الترويسة عبر تضمين غير مباشر، لكن المكتبة لم تُربط أبداً | الإصلاح نفسه: أعلن عن التبعية في REQUIRES |
Error: LittleFS image is too large | تجاوزت محتويات littlefs_data/ قسم storage | قلص المحتويات أو كبّر storage في partitions.csv (حالياً 2 ميغابايت) |
multiple definition of ... | متغير معرف في ترويسة مضمنة مرتين | اجعله static، أو أعلن extern في الترويسة وعرفه في ملف .c واحد |
النمط خلف الأولين: مكونات ESP-IDF ترى فقط ما تعلن عنه. تضمين يصادف أن يتحلل عبر ترويسات مكوّن آخر عامة سيظل يفشل في زمن الربط.
تشغيل الاختبارات
اختبارات الوحدة على الجهاز تطبيق منفصل في test/:
idf.py -C test -B build-test set-target esp32s3 build
idf.py -C test -B build-test -p /dev/ttyACM0 flash monitor
لا يصل اكتشاف الإعدادات المسبقة في CMake إلى المشروع الفرعي test/، لذا يُبنى تطبيق الاختبار مباشرة بـ-C test -B build-test (الصيغة نفسها التي يستخدمها CI). الإعدادات المسبقة للمشروع الجذر (dev، st7789، production) تنطبق على التطبيق الرئيسي فقط.
تعمل على الشريحة الحقيقية وتطبع ملخص Unity. ما يُختبر ولماذا تكمّله أدوات على المضيف: الاختبار.
بناء لوحة التحكم والوثائق
- لوحة التحكم عبر الويب HTML/JS خالص في
littlefs_data/www/؛ تُحزم في صورة LittleFS في زمن البناء وتُكتب على الفلاش كقسمstorage. تعديلها لا يتطلب سلسلة أدوات أبعد من بناء البرنامج الثابت نفسه. - يعيش موقع هذه الوثائق في
safi-docs/(npm install && npm run start).