انتقل إلى المحتوى الرئيسي

بداية العمل

سلسلة الأدوات

يُبنى صافي بـ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 cleanfullclean ثم بناء
./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.

أول تعديل لك

حلقة سريعة من البداية إلى النهاية لإثبات صحة الإعداد:

  1. عدّل main/main.c وأضف سطر سجل في app_main:

    ESP_LOGI("HELLO", "my first Safi build");
  2. ./build.sh && ./flash.sh && ./monitor.sh

  3. اعثر على سطرك في سجل الإقلاع، مباشرة بعد تقرير الكومة.

البناء تزايدي؛ بعد أول ترجمة كاملة (بضع دقائق)، تعاد الترجمة بعد التعديلات في ثوانٍ.

المنفذ التسلسلي

المنفذ بسرعة 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).