لا يُقصد من هذه المقالة أن تكون دليلاً شاملاً ، بل هي مجموعة من مصادر المواد والتوصيات. في المقال ، أريد أن أتطرق إلى المشكلات التي كان عليّ مواجهتها عند اختيار أدوات برمجية لتطوير المشروع ، بالإضافة إلى بعض حالات التطبيق العملي لوحدات ESP32. في المقالة التالية ، أريد أن أعرض مثالاً توضيحيًا لاستخدام ESP32 كوحدة تحكم لمنصة متنقلة صغيرة ذات عجلتين. لذلك ، سننظر هنا في تفاصيل مثل:
- اختيار بيئة التطوير ؛
- تهيئة بيئة العمل ، وتجميع وتحميل مشروع ESP-IDF ؛
- معالجة إشارات الإدخال / الإخراج GPIO ؛
- تعديل عرض النبض باستخدام وحدة MCPWM ؛
- عداد أجهزة PCNT ؛
- اتصال WI-Fi و MQTT.
نظرة عامة على وحدة ESP32-WROOM-32E
وفقًا لورقة البيانات ، تحتوي الوحدة النمطية على:
MCU
- ESP32-D0WD-V3 مضمن ، معالج Xtensa ثنائي النواة 32 بت LX6 ، حتى 240 ميجاهرتز
- 448 كيلو بايت ROM للوظائف الأساسية والتمهيد
- 520 كيلوبايت SRAM للبيانات والتعليمات
- 16 كيلو بايت SRAM في RTC
واي فاي
- 802.11b / ز / ن
- معدل البت: 802.11n حتى 150 ميجابت في الثانية
- تجميع A-MPDU و A-MSDU
- دعم فاصل الحرس 0.4 µs
- مدى التردد المركزي لقناة التشغيل: 2412 ~ 2484 ميجا هرتز
تقنية البلوتوث
- مواصفات Bluetooth V4.2 BR / EDR و Bluetooth LE
- جهاز إرسال من الفئة 1 والفئة 2 والفئة 3
- AFH
- CVSD و SBC
المعدات
- Interfaces: SD card, UART, SPI, SDIO, I 2 C, LED PWM, Motor PWM, I 2 S, IR, pulse counter, GPIO, capacitive touch sensor, ADC, DAC
- 40 MHz crystal oscillator
- 4 MB SPI flash
- Operating voltage/Power supply: 3.0 ~ 3.6 V
- Operating temperature range: –40 ~ 85 °C
- Dimensions: See Table 1
Certification
- Bluetooth certification: BQB
- RF certification: FCC/CE-RED/SRRC
- Green certification: REACH/RoHS
مخطط الكتلة الوظيفية
يمكن العثور على مزيد من التفاصيل حول ميزات وحدة التحكم الدقيقة على ويكيبيديا .
تعتمد الوحدة على الدائرة المصغرة ESP32-D0WD-V3 *. تم تصميم الشريحة المدمجة مع مراعاة قابلية التوسع والقدرة على التكيف. تحتوي وحدة المعالجة المركزية على نواتين يمكن التحكم فيهما بشكل فردي ، ويمكن ضبط سرعة ساعة وحدة المعالجة المركزية من 80 ميجاهرتز إلى 240 ميجاهرتز. تحتوي الشريحة أيضًا على معالج مساعد منخفض الطاقة يمكن استخدامه بدلاً من وحدة المعالجة المركزية لتوفير الطاقة عند تنفيذ المهام التي لا تتطلب الكثير من طاقة الحوسبة ، مثل مراقبة حالة المسامير. يدمج ESP32 مجموعة غنية من الأجهزة الطرفية التي تتراوح من مستشعرات اللمس بالسعة ، مستشعرات القاعة ، واجهة بطاقة SD ، إيثرنت ، SPI عالي السرعة ، UART ، I²S و I²C.
يتم تقديم الوثائق الفنية على المورد الرسمي .
يمكن العثور بسهولة على معلومات حول pinout لوحدة ESP-WROOM-32 في المساحات المفتوحة للشبكة ، كما هو الحال هنا
اختيار بيئة التطوير
اردوينو IDE
ظهرت المتحكمات الدقيقة لعائلة AVR ، ومن ثم منصة Arduino ، قبل وقت طويل من ESP32. تتمثل إحدى الميزات الرئيسية لـ Arduino في وجود حاجز منخفض نسبيًا للدخول ، مما يسمح لأي شخص تقريبًا بإنشاء شيء بسرعة وسهولة. قدمت المنصة مساهمة مهمة في مجتمع الأجهزة مفتوحة المصدر وسمحت لعدد كبير من هواة الراديو بالانضمام. يمكن تنزيل Arduino IDE مجانًا من خارج الموقع . على الرغم من القيود الواضحة مقارنة ببيئة التطوير المهني ، فإن Arduino IDE يغطي 90٪ مما هو مطلوب لمشاريع الهوايات. يوجد أيضًا عدد كافٍ من المقالات على الشبكة حول موضوع تثبيت Arduino IDE وتكوينه لبرمجة وحدات ESP32 ، على سبيل المثال: Arduino core لـ ESP32 ، habr.comو voltiq.ru و randomnerdtutorials.com .
عند برمجة ESP32 في بيئة Arduino ، يجب أن تأخذ في الاعتبار pinout كما هو موضح في صفحة arduino-esp32 .
Pinout وحدة ESP32
الميزة الرئيسية لنهج التصميم هذا هي الدخول السريع وسهولة إنشاء المشاريع ، باستخدام نفس مبادئ Arduino. وأيضًا استخدام العديد من المكتبات مثل Arduino. ميزة أخرى لطيفة هي القدرة على الجمع بين مكتبات Arduino ومبادئ التصميم مع إطار عمل ESP-IDF الأصلي.
منهاج
كما هو مذكور في المورد الرسمي : "منصة IDE عبر الأنظمة الأساسية ومصحح الأخطاء الموحد · محلل الكود الثابت واختبار الوحدة عن بُعد. نظام إنشاء متعدد المنصات ومتعدد المعمار · مستكشف ملفات البرامج الثابتة وفحص الذاكرة »بعبارة أخرى ، يعد النظام الأساسي PlatformIO نظامًا بيئيًا لتطوير الأجهزة المضمنة التي تدعم منصات متعددة بما في ذلك Arduino و ESP32. IDE هو Visual Studio Code أو Atom. التثبيت والتكوين بسيط للغاية - بعد تثبيت محرر التعليمات البرمجية ، حدد PlatformIO من قائمة المكونات الإضافية والتثبيت. مرة أخرى، هناك الكثير من المواد حول هذا الموضوع على شبكة الإنترنت، بدءا من مصدر رسمي هنا و هنا ، والاستمرار مع المواد مع توضيحات مفصلة هنا و هنا....
مقارنةً بـ Arduino IDE ، يتمتع PlatformIO بجميع صفات بيئة التطوير الحديثة: تنظيم المشروع ، ودعم المكونات الإضافية ، وإكمال الكود ، وغير ذلك الكثير.
ميزة التطوير على PlatformIO هي هيكل مشروع موحد لجميع الأنظمة الأساسية
project_dir
├── lib
│ └── README
├── platformio.ini
└── src
└── main.cpp
يحتوي كل مشروع PlatformIO على ملف تكوين يسمى platformio.ini في جذر المشروع. تحتوي platformio.ini على أقسام (يُشار إلى كل منها بـ [العنوان]) وأزواج المفتاح / القيمة داخل الأقسام. سطور تبدأ بحرف نقطي "؛" يتم تجاهلها ويمكن استخدامها للتعليقات. يمكن تحديد معلمات القيمة المتعددة بطريقتين:
- فصل القيمة بـ "،" (فاصلة + مسافة) ؛
- تنسيق متعدد الأسطر ، حيث يبدأ كل سطر جديد بمسافتين على الأقل.
ميزة التطوير التالية لـ ESP32 هي القدرة على اختيار إطار عمل: Arduino أو ESP-IDF. باختيار Arduino كإطار عمل ، نحصل على فوائد التطوير الموصوفة مسبقًا.
يتضمن PlatformIO أدوات ملائمة لإنشاء المشاريع وتنزيلها وتصحيحها
إطار عمل Espressif IoT Development
بالنسبة لـ ESP32 ، طورت Espressif إطار عمل يسمى إطار عمل تطوير إنترنت الأشياء المعروف باسم "ESP-IDF". يمكن العثور عليها على جيثب . يحتوي المشروع على وثائق جيدة جدًا ويتم تزويده بأمثلة يمكنك أخذها كأساس. تم توثيق إعداد البيئة وإعدادها جيدًا في قسم البدء . هناك عدة خيارات لتثبيت الإطار والعمل معه.
استنساخ مشروع من المستودع وتثبيت الأدوات المساعدة يدويًا.
استنساخ مشروع من جيثب
mkdir -p ~/esp
cd ~/esp
git clone --recursive https://github.com/espressif/esp-idf.git
بالنسبة لنظام التشغيل Windows ، من الممكن تثبيت أدوات التطوير المساعدة باستخدام المثبت أو استخدام البرامج النصية لسطر الأوامر:
cd %userprofile%\esp\esp-idf
install.bat
من أجل PowerShell
cd ~/esp/esp-idf
./install.ps1
لنظامي Linux و macOS
cd ~/esp/esp-idf
./install.sh
الخطوة التالية هي إعداد متغيرات البيئة . إذا تم تثبيت أدوات التطوير على Windows باستخدام المثبت ، فسيتم إضافة اختصار لوحدة تحكم الأوامر إلى القائمة وإلى سطح المكتب ، وبعد ذلك يمكنك فتح غلاف الأوامر والعمل مع المشاريع. بدلاً من ذلك ، لتشغيل غلاف أوامر Windows:
%userprofile%\esp\esp-idf\export.bat
أو Windows PowerShell:
.$HOME/esp/esp-idf/export.ps1
Linux و macOS:
. $HOME/esp/esp-idf/export.sh
يجب الانتباه إلى المسافة بين النقطة والمسار إلى البرنامج النصي.
علاوة على ذلك في الدليل يوصى بإضافة اسم مستعار إلى البرنامج النصي لإعداد متغيرات البيئة في ملف تعريف المستخدم إذا كنت تعمل على Linux أو macOS. للقيام بذلك ، انسخ الأمر التالي والصقه في ملف تعريف shell الخاص بك (.profile ، .bashrc ، .zprofile ، إلخ):
alias get_idf='. $HOME/esp/esp-idf/export.sh'
من خلال استدعاء الأمر get_idf في وحدة التحكم ، يتم تصدير متغيرات البيئة المطلوبة. في حالتي ، كان من الضروري أيضًا تسجيل اسم مستعار لبدء بيئة Python الافتراضية
alias esp_va=’source $HOME/.espressif/python_env/idf4.2_py2.7_env/bin/activate’
وأضفه إلى الاسم المستعار التالي
alias get_idf='esp_ve && . $HOME/esp/esp-idf/export.sh'
لإنشاء مشروع جديد من البداية ، يمكنك استنساخ المصادر من github.com أو نسخها من الدليل مع أمثلة esp-idf / أمثلة / get-started / hello_world /.
يقع المعلومات حول بنية المشروع، تجميع والتحميل والمرافق التكوين، وما إلى ذلك هنا .
المشروع عبارة عن دليل بالهيكل التالي:
- myProject/
- CMakeLists.txt
- sdkconfig
- components/ - component1/ - CMakeLists.txt
- Kconfig
- src1.c
- component2/ - CMakeLists.txt
- Kconfig
- src1.c
- include/ - component2.h
- main/ - CMakeLists.txt
- src1.c
- src2.c
- build/
يتم تضمين تكوين المشروع في ملف sdkconfig في الدليل الجذر. لتغيير الإعدادات ، تحتاج إلى استدعاء الأمر idf.py menuconfig (أو ربما idf.py.exe menuconfig على Windows).
عادة ما يتم إنشاء تطبيقين في مشروع واحد - "تطبيق المشروع" (الملف التنفيذي الرئيسي ، أي برنامجك الثابت المخصص) و "تطبيق bootloader" (برنامج محمل الإقلاع الخاص بالمشروع).
"المكونات" هي أجزاء معيارية من التعليمات البرمجية المستقلة التي يتم تجميعها في مكتبات ثابتة (ملفات .a) وربطها بالتطبيق. يتم توفير بعض هذه بواسطة ESP-IDF نفسه ، ويمكن الحصول على البعض الآخر من مصادر أخرى.
توفر الأداة المساعدة لسطر الأوامر idf.py واجهة لإدارة إنشاءات المشروع بسهولة. موقعه على Windows هو٪ userprofile٪ \. Espressif \ tools \ idf-exe \ 1.0.1 \ idf.py.exe. تتحكم في الأدوات التالية:
- CMake - تكوين المشروع للبناء
- منشئ مشروع وحدة التحكم: Ninja أو GNU Make)
- esptool.py - للوحدات الوامضة.
يحتوي كل مشروع على ملف CMakeLists.txt من المستوى الأعلى يحتوي على إعدادات الإنشاء للمشروع بأكمله. يتضمن الحد الأدنى لتكوين الملف الأسطر المطلوبة التالية:
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(myProject)
يمكن اعتبار مشروع ESP-IDF على أنه مجموعة من المكونات التي يكون فيها الدليل الرئيسي هو المكون الرئيسي الذي يقوم بتشغيل الكود. لذلك ، يحتوي هذا الدليل أيضًا على ملف CMakeLists.txt. غالبًا ما يكون هيكلها مشابهًا:
idf_component_register(SRCS "main.c" INCLUDE_DIRS ".")
حيث يشار إلى أن الملف المصدر main.c يجب تسجيله للمكون ، وملفات الرأس موجودة في الدليل الحالي. إذا لزم الأمر ، يمكنك إعادة تسمية الدليل الرئيسي عن طريق تعيين EXTRA_COMPONENT_DIRS في مشروع CMakeLists.txt. يمكن العثور على مزيد من التفاصيل هنا .
بالإضافة إلى ذلك ، يحتوي الدليل على ملف main.c (يمكن أن يكون الاسم أيًا) مع نقطة إدخال - وظيفة app_main (void) باطلة.
سيتم إنشاء المكونات المخصصة في دليل المكونات. يتم وصف العملية بمزيد من التفاصيل في قسم متطلبات المكونات.
يتم توصيل وحدة ESP32 بجهاز الكمبيوتر في معظم الحالات باستخدام كبل USB مثل لوحات Arduino بسبب أداة تحميل التشغيل الموجودة. يتم وصف العملية بمزيد من التفاصيل هنا... الشيء الوحيد المطلوب هو وجود برنامج تشغيل محول USB إلى UART في النظام ، والذي يمكن تنزيله من المصدر المتوفر. بعد تثبيت برنامج التشغيل ، تحتاج إلى تحديد رقم منفذ COM في النظام لتحميل البرامج الثابتة المترجمة في الوحدة النمطية.
تكوين المشروع.
الإعدادات الافتراضية جيدة في معظم الحالات. ولكن للاتصال بواجهة قائمة وحدة التحكم ، يلزمك الانتقال إلى دليل المشروع وكتابة سطر الأوامر:
idf.py menuconfig
قائمة بإعدادات التكوين
بعد استدعاء هذا الأمر ، سيتم إنشاء ملف sdkconfig إذا لم يكن قد تم سابقًا أو إذا تم إعادة تكوينه. في البرامج التعليمية السابقة ، ستجد أوامر menuconfig التي عفا عليها الزمن.
من الممكن إضافة إعدادات مخصصة إلى ملف sdkconfig يدويًا ، على سبيل المثال:
#
# WiFi Settings
#
CONFIG_ESP_HOST_NAME=" "
CONFIG_ESP_WIFI_SSID=" "
CONFIG_ESP_WIFI_PASSWORD=""
لكن الطريقة المفضلة هي استخدام ملف تكوين إضافي Kconfig.projbuild ، والذي يجب أن يكون موجودًا في الدليل الذي يحتوي على المكون. يمكن أن يكون محتوى الملف كما يلي:
# put here your custom config value
menu "Example Configuration"
config ESP_WIFI_SSID
string "Keenetic"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
endmenu
بعد استدعاء الأمر idf.py menuconfig ، تتم إضافة قسم إضافي تلقائيًا في ملف sdkconfig. من الممكن أيضًا استدعاء الأمر idf.py menuconfig في مشروع PlatformIO ، ومع ذلك ، يجب أن تأخذ في الاعتبار حقيقة أن بنية مشروع PlatformIO تختلف عن ESP-IDF الكلاسيكي ، مما قد يتسبب في إعادة إنشاء ملف sdkconfig وتعديل الإعدادات المخصصة. الخيارات المذكورة أعلاه ممكنة هنا - تحرير الملف يدويًا ، أو إعادة تسمية دليل src بشكل مؤقت ، أو إعداد ملف CMakeLists.txt.
تجميع وتحميل المشروع.
لبناء مشروع ، تحتاج إلى كتابة الأمر
idf.py build
سيقوم هذا الأمر بتجميع التطبيق وجميع مكونات ESP-IDF ثم إنشاء المُحمل وجدول الأقسام وثنائيات التطبيق.
$ idf.py build
Running cmake in directory /path/to/hello_world/build
Executing "cmake -G Ninja --warn-uninitialized /path/to/hello_world"...
Warn about uninitialized values.
-- Found Git: /usr/bin/git (found version "2.17.0")
-- Building empty aws_iot component due to configuration
-- Component names: ...
-- Component paths: ...
... (more lines of build system output)
[527/527] Generating hello-world.bin
esptool.py v2.3.1
Project build complete. To flash, run this command:
../../../components/esptool_py/esptool/esptool.py -p (PORT) -b 921600 write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x10000 build/hello-world.bin build 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin
or run 'idf.py -p PORT flash'
يجب أن يؤخذ في الاعتبار أن عملية التجميع الأولية حتى لمشروع بسيط تستغرق وقتًا ، لذلك ، على عكس إطار عمل Arduino ، يتم تجميع العديد من وحدات ESP-IDF الإضافية. يؤدي التعديل الإضافي للمصادر إلى تجميع نفس الملفات فقط. الاستثناء هو تغيير التكوين.
لتنزيل الثنائيات المترجمة (bootloader.bin ، و partition-table.bin و hello-world.bin) إلى لوحة ESP32 ، قم بتشغيل الأمر:
idf.py -p PORT [-b BAUD] flash
حيث نستبدل PORT بما نحتاجه (COM1 ، / dev / ttyUSB1) ، ويمكننا بشكل اختياري تغيير سرعة التنزيل من خلال تحديد القيم المطلوبة لـ BAUD
لتتبع البرنامج المحمّل ، يمكنك استخدام أي أداة مساعدة لمراقبة منفذ com ، مثل HTerm و CoolTerm ، أو استخدم أداة مراقبة IDF Monitor ، لبدء تشغيلها ، أدخل الأمر:
idf.py -p PORT monitor
البرنامج المساعد ESP-IDF Eclipse
توجد وثائق تثبيت المكون الإضافي وتكوينه هنا
إعدادات مسبقة للاستخدام:
- جافا 11 وما فوق ؛ (على الرغم من أنه يعمل على جافا 8 ، ربما بسبب هذا الثغرة) ؛
- Python 3.5 وما فوق ؛
- كسوف 2020-06 CDT ؛
- شخص سخيف؛
- ESP-IDF 4.0 وما فوق ؛
تم دمج المكون الإضافي جيدًا في بيئة التطوير ، ويقوم بأتمتة حصة الأسد من الوظائف. لكن ، لسوء الحظ ، ليس بدون ذبابة في المرهم. في إصدارات Eclipse الأحدث من 2019-09 ، لا يزال هناك خطأ في فهرسة ملفات المصدر في مشاريع ESP-IDF على Windows
، بالإضافة إلى ذلك ، هناك مواطن الخلل الأخرى عندما لا يتم إنشاء المشروع ببساطة لأسباب غير معروفة. يساعد إغلاق المشروع وإعادة تشغيل Eclipse فقط.
امتداد كود الاستوديو المرئي ESP-IDF
والأخير ، في رأيي ، الخيار الأكثر إثارة للاهتمام هو المكون الإضافي الرسمي لبرنامج Visual Studio Code.
مثل PlatformIO ، يمكن تثبيته بسهولة من قسم الامتدادات. يتم تقديم تثبيت وتكوين إطار عمل ESP-IDF في هذا الامتداد كقائمة داخلية ، والتي تم وصفها أيضًا في الوصف. يتم تنزيل جميع المكونات وتثبيتها تلقائيًا في عملية الانتقال عبر مراحل القائمة. يمكن الاستشهاد بجميع لقطات الشاشة للعملية ، لكنها بديهية وتتطلب القليل من الشرح أو لا تتطلب أي تفسير. لصالح PlatformIO ، يمكن ملاحظة مجموعة أدوات أكثر ملاءمة لبناء مشروع وتنزيله ومراقبته. في المقابل ، يتم التحكم في المكون الإضافي ESP-IDF باستخدام قائمة أوامر يمكن استدعاؤها باستخدام مفتاح F1 ، أو مجموعة من المفاتيح الموضحة في الدليل.
الإعداد الأولي للمكوِّن الإضافي
تتمثل ميزة استخدام المكون الإضافي في احترام بنية المشروع الكلاسيكية ، ولا داعي للتلاعب بالإعدادات بطريقة أو بأخرى (في PlatformIO ، تظهر هذه الحاجة). هناك فارق بسيط واحد ، إذا أردنا فتح مشروع تم إنشاؤه مسبقًا في Visual studio code باستخدام المكون الإضافي ESP-IDF ، فسنحتاج فقط إلى نسخ دليل .vscode إلى جذر المشروع ، والذي يمكن الحصول عليه عن طريق إنشاء مشروع نموذجي مرة واحدة على الأقل باستخدام ESP- المكون الإضافي لـ IDF.
قائمة الأوامر
FreeRTOS
وفقًا لويكيبيديا ، يعد FreeRTOS نظام تشغيل متعدد المهام في الوقت الفعلي (RTOS) للأنظمة المدمجة. يوفر FreeRTOS تعدد المهام من خلال مشاركة وقت وحدة المعالجة المركزية من خلال جميع مؤشرات الترابط ، أو المهام في مصطلحات نظام التشغيل. في رأيي، FreeRTOS الأكثر اكتمالا واضح اليدوي في روسيا هو هنا . في اللغة الأصلية ، يمكن دراسة الكتيبات من المصدر الرسمي . سأقدم فقط صورة عن حالة المهام.
تم نقل FreeRTOS إلى مجموعة متنوعة من الأنظمة الأساسية للأجهزة بما في ذلك معالجات Xtensa المستخدمة في ESP32. يمكن العثور على مزيد من التفاصيل في الوثائق.
GPIOs
GPIO أو الإدخال / الإخراج العام هو القدرة على التحكم بشكل منفصل في دبوس بإشارة "1" أو "0".
كما يوحي الاسم ، تحتوي هذه المسامير على وضعين للتشغيل - الإدخال أو الإخراج. في الحالة الأولى نقرأ القيمة ، وفي الحالة الثانية نكتبها. عامل مهم آخر عند التعامل مع GPIOs هو مستوى الجهد. ESP32 عبارة عن جهاز 3.3 فولت ، لذلك يجب أن تكون حذرًا عند العمل مع الأجهزة الأخرى ذات الجهد الكهربائي 5 فولت أو أعلى. من المهم أيضًا أن تفهم أن الحد الأقصى للتيار الذي يمكن تطبيقه على دبوس GPIO هو 12 مللي أمبير. لاستخدام وظائف GPIO التي يوفرها ESP-IDF ، نحتاج إلى توصيل رأس السائق / gpio.h. يمكنك بعد ذلك استدعاء gpio_pad_select_gpio () لتحديد وظيفة هذا الدبوس. هناك 34 GPIOs مختلفة متاحة على ESP32. تم تعيينهم على النحو التالي:
- GPIO_NUM_0 - GPIO_NUM_19
- GPIO_NUM_21 - GPIO_NUM_23
- GPIO_NUM_25 - GPIO_NUM_27
- GPIO_NUM_32 - GPIO_NUM_39
لم يتم تضمين الترقيم التالي في عدد المسامير 20 و 24 و 28 و 29 و 30 و 31.
يمكن العثور على جدول pinout هنا .
يرجى ملاحظة أن الدبابيس GPIO_NUM_34 - GPIO_NUM_39 - تستخدم وضع الإدخال فقط. لا يمكن استخدامها لإخراج إشارة. بالإضافة إلى ذلك ، يتم استخدام الدبابيس 6 و 7 و 8 و 9 و 10 و 11 للتفاعل مع بطاقة فلاش خارجية عبر SPI ، ولا يوصى باستخدامها لأغراض أخرى ، ولكن إذا كنت تريد ذلك حقًا ، يمكنك ذلك. نوع البيانات gpio_num_t هو تعداد مع القيم المقابلة لأرقام التعريف الشخصي. يوصى باستخدام هذه القيم بدلاً من الأرقام. يتم ضبط اتجاه الدبوس باستخدام وظيفة gpio_set_direction (). على سبيل المثال ، لتعيين دبوس كإخراج:
gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT);
لتعيين دبوس كمدخل:
gpio_set_direction(GPIO_NUM_17, GPIO_MODE_INPUT);
إذا قمنا بتكوين GPIO كإخراج ، فيمكننا تعيين قيمته على 1 أو 0 عن طريق استدعاء gpio_set_level ().
المثال التالي يقوم بتبديل GPIOs مرة واحدة في الثانية:
gpio_pad_select_gpio(GPIO_NUM_17);
gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT);
while(1) {
printf("Off\n");
gpio_set_level(GPIO_NUM_17, 0);
vTaskDelay(1000 / portTICK_RATE_MS);
printf("On\n");
gpio_set_level(GPIO_NUM_17, 1);
vTaskDelay(1000 / portTICK_RATE_MS);
}
كبديل لتعيين جميع سمات الدبابيس الفردية ، يمكننا تعيين خصائص جهة اتصال واحدة أو أكثر عن طريق استدعاء وظيفة gpio_config (). يأخذ بنية gpio_config_t كمدخلات ويضبط الاتجاه ، ويسحب لأعلى ، ويسحب لأسفل ويقطع الإعدادات لجميع الدبابيس الممثلة في قناع البت.
على سبيل المثال:
gpio_config_t gpioConfig;
gpioConfig.pin_bit_mask = (1 << 16) | (1 << 17);
gpioConfig.mode = GPIO_MODE_OUTPUT;
gpioConfig.pull_up_en = GPIO_PULLUP_DISABLE;
gpioConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
gpioConfig.intr_type = GPIO_INTR_DISABLE;
gpio_config(&gpioConfig);
إعدادات السحب لأعلى والسحب لأسفل
عادة ما تقرأ أن دبوس إدخال GPIO مرتفع أو منخفض. هذا يعني أنه متصل بمصدر طاقة أو بالأرض. ومع ذلك ، إذا لم يكن الدبوس متصلاً بأي شيء ، فهو في حالة "عائم". غالبًا ما يكون من الضروري ضبط المستوى الأولي للدبوس غير المتصل على مرتفع أو منخفض. في هذه الحالة ، يتم إجراء جهاز (اتصال باستخدام مقاومات) أو سحب برمجي للإخراج ، على التوالي ، إلى + V - سحب لأعلى أو إلى 0 - سحب لأسفل. في ESP32 SDK ، يمكننا تعريف GPIO على أنها سحب لأعلى أو لأسفل باستخدام وظيفة gpio_set_pull_mode (). تأخذ هذه الوظيفة كمدخل رقم الدبوس الذي نريد تعيينه ووضع السحب المرتبط بهذا الدبوس.
على سبيل المثال:
gpio_set_pull_mode (21, GPIO_PULLUP_ONLY);
معالجة مقاطعة GPIO
لاكتشاف تغيير في إشارة الإدخال على دبوس ، يمكننا بشكل دوري فحص حالته ، ولكن هذا ليس الحل الأفضل لعدد من الأسباب. أولاً ، يتعين علينا إجراء الفحص ، وإضاعة وقت وحدة المعالجة المركزية. ثانيًا ، في لحظة الاقتراع ، قد لا تكون حالة الدبوس ذات صلة بسبب التأخير ويمكنك تخطي إشارات الإدخال. الحل لهذه المشاكل هو الانقطاع. المقاطعة مثل جرس الباب. بدون رنين ، سيتعين علينا التحقق بشكل دوري لمعرفة ما إذا كان أي شخص على الباب. في الكود المصدري ، يمكننا تحديد وظيفة استدعاء المقاطعة التي سيتم استدعاؤها عندما يغير الدبوس قيمة إشارته. يمكننا أيضًا تحديد سبب استدعاء المعالج من خلال تعيين المعلمات التالية:
- تعطيل - لا تطلق مقاطعة عندما تتغير الإشارة ؛
- PosEdge - استدعاء معالج المقاطعة عند التغيير من منخفض إلى مرتفع ؛
- NegEdge - استدعاء معالج المقاطعة عند التغيير من عالي إلى منخفض ؛
- AnyEdge - استدعاء معالج المقاطعة إما عند التغيير من منخفض إلى مرتفع ، أو عند التغيير من مرتفع إلى منخفض ؛
يمكن وضع علامة على معالج المقاطعة للتحميل إلى ذاكرة الوصول العشوائي في وقت الترجمة. بشكل افتراضي ، يكون الرمز الذي تم إنشاؤه في ذاكرة فلاش. إذا قمت بتمييزه على أنه IRAM_ATTR مسبقًا ، فسيكون جاهزًا للتنفيذ الفوري من ذاكرة الوصول العشوائي.
void IRAM_ATTR my_gpio_isr_handle(void *arg) {...}
أولئك الذين عملوا مع ميكروكنترولر يعرفون أن معالجة إشارات الإدخال من الأزرار مصحوبة بارتداد التلامس. والتي يمكن تفسيرها على أنها سلسلة من الانتقالات ، وبالتالي سلسلة من أحداث معالج المقاطعة. للقيام بذلك ، يجب أن نضيف معالجة ارتداد جهات الاتصال إلى الكود. للقيام بذلك ، نحتاج إلى قراءة الحدث الأصلي ، والانتظار حتى تهدأ الاهتزازات ، ثم إعادة أخذ عينة من حالة الإدخال.
يوضح المثال التالي معالجة المقاطعة لإشارات الإدخال. أوصي بشدة بأن تتعرف على إدارة قائمة الانتظار في FreeRTOS لفهم الكود بشكل أكبر ، إذا لم تكن على دراية به بالفعل. يوضح المثال مهمتين:
- test1_task ، والذي يتم إلغاء قفله عند حدوث حدث مقاطعة عند تنشيط الإشارة على الرقم 25 ويتم عرض رسالة "تم تسجيل نقرة" على وحدة التحكم مرة واحدة ؛
- يتم استقصاء test2_task بشكل دوري ، وعندما يتم تنشيط الإشارة على الطرف 26 ، يتم إخراج الرسالة "GPIO 26 مرتفع!" إلى وحدة التحكم كل 100 مللي ثانية.
يحتوي المثال أيضًا على جهاز ضبط مؤقت للبرامج xTimer ، وهو اختياري لهذه الحالة ، بدلاً من ذلك كمثال على التأخير غير المتزامن.
يتم إجراء مكافحة الارتداد باستخدام وظيفة timeval_durationBeforeNow ، والتي تتحقق مما إذا كانت الصحافة تستمر لأكثر من 100 مللي ثانية. هناك أنماط برامج أخرى لمكافحة الارتداد ، لكن المعنى هو نفسه تقريبًا. يتضمن ESP-IDF أيضًا مثالاً على كيفية عمل GPIO.
معالجة إشارة الإدخال
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/queue.h"
#include "c_timeutils.h"
#include "freertos/timers.h"
static char tag[] = "test_intr";
static QueueHandle_t q1;
TimerHandle_t xTimer;
#define TEST_GPIO (25)
static void handler(void *args) {
gpio_num_t gpio;
gpio = TEST_GPIO;
xQueueSendToBackFromISR(q1, &gpio, NULL);
}
void test1_task(void *ignore) {
struct timeval lastPress;
ESP_LOGD(tag, ">> test1_task");
gpio_num_t gpio;
q1 = xQueueCreate(10, sizeof(gpio_num_t));
gpio_config_t gpioConfig;
gpioConfig.pin_bit_mask = GPIO_SEL_25;
gpioConfig.mode = GPIO_MODE_INPUT;
gpioConfig.pull_up_en = GPIO_PULLUP_DISABLE;
gpioConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
gpioConfig.intr_type = GPIO_INTR_POSEDGE;
gpio_config(&gpioConfig);
gpio_install_isr_service(0);
gpio_isr_handler_add(TEST_GPIO, handler, NULL);
while(1) {
//ESP_LOGD(tag, "Waiting on queue");
BaseType_t rc = xQueueReceive(q1, &gpio, portMAX_DELAY);
//ESP_LOGD(tag, "Woke from queue wait: %d", rc);
struct timeval now;
gettimeofday(&now, NULL);
if (timeval_durationBeforeNow(&lastPress) > 100) {
if(gpio_get_level(GPIO_NUM_25)) {
ESP_LOGD(tag, "Registered a click");
if( xTimerStart( xTimer, 0 ) != pdPASS ) {
// The timer could not be set into the Active state.
}
}
}
lastPress = now;
}
vTaskDelete(NULL);
}
void test2_task(void *ignore) {
gpio_set_direction(GPIO_NUM_26, GPIO_MODE_INPUT);
gpio_set_pull_mode(GPIO_NUM_26, GPIO_PULLDOWN_ONLY);
while(true) {
if(gpio_get_level(GPIO_NUM_26)) {
ESP_LOGD(tag, "GPIO 26 is high!");
if( xTimerStart( xTimer, 0 ) != pdPASS ) {
// The timer could not be set into the Active state.
}
}
vTaskDelay(100/portTICK_PERIOD_MS);
}
}
void vTimerCallback( TimerHandle_t pxTimer ) {
ESP_LOGD(tag, "The timer has expired!");
}
void app_main(void)
{
xTaskCreate(test1_task, "test_task1", 5000, NULL, 8, NULL);
xTaskCreate(test2_task, "test_task2", 5000, NULL, 8, NULL);
xTimer = xTimerCreate("Timer", // Just a text name, not used by the kernel.
2000/portTICK_PERIOD_MS, // The timer period in ticks.
pdFALSE, // The timers will auto-reload themselves when they expire.
( void * ) 1, // Assign each timer a unique id equal to its array index.
vTimerCallback // Each timer calls the same callback when it expires.
);
}
PCNT (عداد النبض)
تم تصميم وحدة PCNT (عداد النبض) لحساب عدد الحواف الصاعدة و / أو الهابطة لإشارة الإدخال. تحتوي كل كتلة من الوحدة النمطية على سجل موقع 16 بت وقناتين يمكن تهيئتهما لزيادة أو تقليل قيمة العداد. تحتوي كل قناة على إشارة إدخال تلتقط التغيير في الإشارة ، بالإضافة إلى إدخال تحكم يمكن استخدامه لتمكين أو تعطيل العد. تحتوي المدخلات على مرشحات إضافية يمكن استخدامها للقضاء على ارتفاعات الإشارة غير المرغوب فيها.
يحتوي عداد PCNT على ثماني وحدات مستقلة ، مرقمة من 0 إلى 7. في API ، تم تحديدها باستخدام pcnt_unit_t. تحتوي كل وحدة نمطية على قناتين مستقلتين ، مرقمتين 0 و 1 ، يشار إليها بواسطة pcnt_channel_t.
يتم توفير التكوين بشكل منفصل لكل قناة جهاز باستخدام pcnt_config_t ويغطي:
- رقم الوحدة ورقم القناة التي ينتمي إليها هذا التكوين ؛
- إدخال النبض وأرقام إدخال البوابة GPIO ؛
- زوجان من المعلمات ، pcnt_ctrl_mode_t و pcnt_count_mode_t ، لتحديد كيفية استجابة العداد بناءً على حالة إشارة التحكم وكيفية حساب الحافة الصاعدة / الهابطة.
- قيمتان حديتان (min / max) تُستخدمان لتعيين نقاط المراقبة وتشغيل المقاطعات عندما يصل عداد النبض إلى حد معين.
يتم بعد ذلك تكوين قناة معينة عن طريق استدعاء وظيفة pcnt_unit_config () باستخدام بنية التكوين pcnt_config_t أعلاه كمعامل إدخال.
لتعطيل نبض أو إدخال تحكم في التكوين ، تحتاج إلى تحديد PCNT_PIN_NOT_USED بدلاً من رقم GPIO.
بعد التهيئة باستخدام pcnt_unit_config () ، يبدأ العداد في العمل على الفور. يمكن التحقق من قيمة العداد المتراكمة عن طريق استدعاء pcnt_get_counter_value ().
تسمح لك الوظائف التالية بالتحكم في عملية العداد: pcnt_counter_pause () و pcnt_counter_resume () و pcnt_counter_clear ()
ومن الممكن أيضًا تغيير أوضاع العداد المحددة مسبقًا بشكل ديناميكي باستخدام pcnt_unit_config () عن طريق استدعاء pcnt_set_mode ().
إذا رغبت في ذلك ، يمكن تغيير دبوس إدخال النبض ودبوس إدخال التحكم بسرعة باستخدام pcnt_set_pin ().
تحتوي وحدة PCNT على مرشحات على كل من مدخلات النبض والتحكم ، مما يضيف القدرة على تجاهل التموجات القصيرة في الإشارات. يتم توفير طول النبضات التي تم تجاهلها في دورات ساعة APB_CLK عن طريق استدعاء pcnt_set_filter_value (). يمكن التحقق من إعدادات التصفية الحالية باستخدام pcnt_get_filter_value (). تعمل دورة APB_CLK عند 80 ميجا هرتز.
يتم تشغيل / إيقاف المرشح مؤقتًا عن طريق استدعاء pcnt_filter_enable () / pcnt_filter_disable ().
الأحداث التالية ، المحددة في pcnt_evt_type_t ، يمكن أن تؤدي إلى مقاطعة. يقع الحدث عندما يصل عداد النبض إلى قيم معينة:
- : counter_l_lim counter_h_lim, pcnt_config_t;
- 0 1, pcnt_set_event_value ().
- = 0
للتسجيل أو تمكين أو تعطيل المقاطعة للأحداث المذكورة أعلاه ، يجب عليك استدعاء pcnt_isr_register () و pcnt_intr_enable () و pcnt_intr_disable (). لتمكين الأحداث أو تعطيلها عند الوصول إلى الحدود ، ستحتاج أيضًا إلى استدعاء pcnt_event_enable () و pcnt_event_disable ().
للتحقق من العتبات المعينة حاليًا ، استخدم وظيفة pcnt_get_event_value ().
يتم تقديم مثال من ESP-IDF هنا .
لقد استخدمت عداد PCNT لحساب سرعة العجلة. للقيام بذلك ، من الضروري حساب عدد النبضات لكل دورة ، ثم إعادة تعيين العداد.
عينة من الرموز
typedef struct {
uint16_t delay; //delay im ms
int pin;
int ctrl_pin;
pcnt_channel_t channel;
pcnt_unit_t unit;
int16_t count;
} speed_sensor_params_t;
esp_err_t init_speed_sensor(speed_sensor_params_t* params) {
/* Prepare configuration for the PCNT unit */
pcnt_config_t pcnt_config;
// Set PCNT input signal and control GPIOs
pcnt_config.pulse_gpio_num = params->pin;
pcnt_config.ctrl_gpio_num = params->ctrl_pin;
pcnt_config.channel = params->channel;
pcnt_config.unit = params->unit;
// What to do on the positive / negative edge of pulse input?
pcnt_config.pos_mode = PCNT_COUNT_INC; // Count up on the positive edge
pcnt_config.neg_mode = PCNT_COUNT_DIS; // Keep the counter value on the negative edge
pcnt_config.lctrl_mode = PCNT_MODE_REVERSE; // Reverse counting direction if low
pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // Keep the primary counter mode if high
pcnt_config.counter_h_lim = INT16_MAX;
pcnt_config.counter_l_lim = - INT16_MAX;
/* Initialize PCNT unit */
esp_err_t err = pcnt_unit_config(&pcnt_config);
/* Configure and enable the input filter */
pcnt_set_filter_value(params->unit, 100);
pcnt_filter_enable(params->unit);
/* Initialize PCNT's counter */
pcnt_counter_pause(params->unit);
pcnt_counter_clear(params->unit);
/* Everything is set up, now go to counting */
pcnt_counter_resume(params->unit);
return err;
}
int32_t calculateRpm(speed_sensor_params_t* params) {
pcnt_get_counter_value(params->unit, &(params->count));
int32_t rpm = 60*(1000/params->delay)*params->count/PULSE_PER_TURN;
pcnt_counter_clear(params->unit);
return rpm;
}
تعديل عرض النبضة (PWM) باستخدام وحدة MCPWM
يتم تقديم معلومات حول الوحدة هنا.
هناك العديد من المقالات على الشبكة حول موضوع PWM ، خاصة إذا كنت تبحث عن Arduino.
تقدم ويكيبيديا تعريفًا قصيرًا وموجزًا - تعديل عرض النبضة (PWM) - عملية التحكم في الطاقة عن طريق نبض الجهاز وإيقافه. مبدأ التحكم في PWM هو تغيير عرض النبض بسعة وتردد ثابت للإشارة.
تردد Arduino PWM هو 488.28 هرتز ، والدقة 8 بت (0 ... 255) ، ومن الممكن استخدام ستة دبابيس للأجهزة 3 ، 5 ، 6 ، 9 ، 10 ، 11. ومع ذلك ، باستخدام إعدادات التسجيل في متحكم AVR ، يمكنك تحقيق قيم أخرى تردد PWM.
يحتوي متحكم ESP32 في ترسانته على وحدة MCPWM منفصلة ، أو بالأحرى وحدتان ، كل منهما تحتوي على ثلاثة أزواج من دبابيس PWM.
علاوة على ذلك ، في الوثائق ، يتم تمييز مخرجات كتلة منفصلة على أنها PWMxA / PWMxB.
ويرد أدناه مخطط كتلة أكثر تفصيلاً لكتلة MCPWM. يمكن مزامنة كل زوج A / B مع أي من ثلاثة أجهزة ضبط الوقت: Timer 0 و 1 و 2. يمكن استخدام نفس المؤقت لمزامنة أكثر من زوج واحد من مخرجات PWM. يمكن لكل وحدة أيضًا جمع بيانات الإدخال مثل إشارات المزامنة ، واكتشاف الإنذارات مثل التيار الزائد أو الجهد الزائد للمحرك ، وتلقي ردود الفعل من إشارات الالتقاط مثل موضع الدوار.
يعتمد نطاق التكوين على نوع المحرك ، ولا سيما عدد المخرجات والمدخلات المطلوبة وما سيكون تسلسل الإشارات للتحكم في المحرك.
في حالتنا ، نصف تكوينًا بسيطًا لقيادة محرك DC ناعم يستخدم فقط عددًا قليلاً من موارد MCPWM المتاحة. يتم عرض مثال على الدائرة أدناه. يشتمل على جسر H لتبديل استقطاب الجهد المزود للمحرك (M) وتوفير تيار كافٍ لقيادته.
يتضمن التكوين الخطوات التالية:
- اختيار كتلة MPWn التي سيتم استخدامها لقيادة المحرك. هناك نوعان من الوحدات المتاحة على لوحة ESP32 من تلك المدرجة في mcpwm_unit_t.
- يقوم بتهيئة اثنين من GPIOs كمخرجات على الوحدة المحددة عن طريق استدعاء mcpwm_gpio_init (). عادةً ما تُستخدم إشارتا الخرج لدفع المحرك إلى اليمين أو اليسار. يتم سرد جميع معلمات الإشارة المتاحة في mcpwm_io_signals_t. لتعيين أكثر من دبوس في وقت واحد ، استخدم الدالة mcpwm_set_pin () مع mcpwm_pin_config_t.
- اختيار الموقت. هناك ثلاثة موقتات متوفرة على الجهاز. يتم سرد الموقتات في mcpwm_timer_t.
- ضبط تردد المؤقت والتمهيد في بنية mcpwm_config_t.
- استدعاء mcpwm_init () بالمعلمات أعلاه.
طرق التحكم في PWM هي كما يلي:
- mcpwm_set_signal_high () mcpwm_set_signal_low (). . A B .
- — , mcpwm_start () mcpwm_stop (). .
- , mcpwm_set_duty () . mcpwm_set_duty_in_us (), . mcpwm_get_duty (). , mcpwm_set_duty_type (). A B mcpwm_generator_t. . mcpwm_init (), , mcpwm_duty_type_t.
يوجد هنا مثال على رمز لمحرك ناعم .
في مشروعي ، استخدمت عمليًا الرمز من المثال ، وقمت بتصحيحه قليلاً وإضافة عنصر تحكم ثانٍ في المحرك. للتحكم المستقل في قنوات PWM ، يجب تكوين كل منها بمؤقت منفصل ، على سبيل المثال MCPWM_TIMER_0 و CPWM_TIMER_1:
عينة من الرموز
void mcpwm_example_gpio_initialize(void)
{
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
//mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_0, GPIO_SYNC0_IN);
mcpwm_config_t pwm_config;
pwm_config.frequency = 1000; //frequency = 500Hz,
pwm_config.cmpr_a = 0; //duty cycle of PWMxA = 0
pwm_config.cmpr_b = 0; //duty cycle of PWMxb = 0
pwm_config.counter_mode = MCPWM_UP_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM0A & PWM0B with above settings
// deadtime (see clock source changes in mcpwm.c file)
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_BYPASS_FED, 80, 80); // 1us deadtime
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_FED, 80, 80);
}
الاتصال بشبكة WI-Fi والعمل مع MQTT
موضوع بروتوكول Wi-FI واسع للغاية. ستكون هناك حاجة إلى سلسلة من المقالات المنفصلة لوصف البروتوكول. في الدليل الرسمي ، راجع قسم برنامج تشغيل Wi-Fi . وصف لبرمجيات API هنا . يمكن الاطلاع على أمثلة التعليمات البرمجية هنا
توفر مكتبات Wi-Fi الدعم لتكوين وظائف شبكة Wi-Fi ESP32 ومراقبتها. التكوينات التالية متوفرة:
- ( STA Wi-Fi). ESP32 .
- AP ( Soft-AP ). ESP32.
- AP-STA (ESP32 , ).
- (WPA, WPA2, WEP . .)
- ( ).
- Wi-Fi IEEE802.11.
MQTT
يمكنك التعرف على الموضوع هنا أو هنا . دليل ESP-IDF مع أمثلة هنا .
لإعداد MQTT في الكود ، تحتاج أولاً إلى الاتصال بشبكة Wi-Fi. ثم قم بإنشاء اتصال بالوسيط. تتم معالجة الرسالة في رد نداء ، المعلمة الخاصة بها هي حدث esp_mqtt_event_handle_t. إذا كان نوع الحدث هو MQTT_EVENT_DATA ، فيمكن عندئذٍ تحليل الموضوع والبيانات. يمكنك تخصيص سلوك مختلف كنتيجة للاتصال الناجح وقطع الاتصال واشتراكات الموضوع.
مثال على اتصال Wi-Fi:
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
wifi_config_t sta_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
.bssid_set = false
}
};
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &sta_config) );
ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]", CONFIG_ESP_WIFI_SSID, "******");
ESP_ERROR_CHECK( esp_wifi_start() );
ESP_LOGI(TAG, "Waiting for wifi");
xEventGroupWaitBits(wifi_event_group, BIT0, false, true, portMAX_DELAY);
//MQTT init
mqtt_event_group = xEventGroupCreate();
mqtt_app_start(mqtt_event_group);
الاتصال بوسيط MQTT
void mqtt_app_start(EventGroupHandle_t event_group)
{
mqtt_event_group = event_group;
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtt://mqtt.eclipse.org:1883", //mqtt://mqtt.eclipse.org:1883
.event_handle = mqtt_event_handler,
.keepalive = 10,
.lwt_topic = "esp32/status/activ",
.lwt_msg = "0",
.lwt_retain = 1,
};
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_start(client);
معالج MQTT
esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
int msg_id;
command_t command;
// your_context_t *context = event.context;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
xEventGroupSetBits(mqtt_event_group, BIT1);
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, "esp32/car/#", 0);
msg_id = esp_mqtt_client_subscribe(client, "esp32/camera/#", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "esp32/status/activ", "1", 0, 0, 1);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
memset(topic, 0, strlen(topic));
memset(data, 0, strlen(data));
strncpy(topic, event->topic, event->topic_len);
strncpy(data, event->data, event->data_len);
command_t command = {
.topic = topic,
.message = data,
};
parseCommand(&command);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
break;
}
return ESP_OK;
}
بهذا أنهي قصتي حول استخدام وحدة ESP32. نظرت المقالة في أمثلة على ESP-IDF ، كإطار عمل يحقق أقصى استفادة من موارد الوحدة. يمكن العثور على البرمجة باستخدام منصات أخرى مثل javaScript و MicroPython و Lua في الموارد ذات الصلة. في المقالة التالية ، كما ذكرنا سابقًا ، سأقدم مثالًا عمليًا لاستخدام متحكم دقيق ، وأقارن أيضًا نهج البرنامج في Arduino و ESP-IDF.