الهيكل المركب يبدو هكذا
مكونات
القاعدة عبارة عن هيكل السيارة ذات المنصة الروبوتية ذات الطابقين 2WD Mini Kit
أبعاد المنصة: 135 مم × 135 مم × 80 مم
المحرك عبارة عن عجلتين قياسيتين مع علبة تروس ومحرك DC مع أقراص نقطية لمستشعرات السرعة:
- التصنيف الحالي: 250mA كحد أقصى. بجهد 3.6 فولت
- عزم الدوران 800 جم / سم (عند جهد 6 فولت)
- جهد التغذية: 6-8 فولت
- سرعة عدم التحميل: 170 دورة في الدقيقة (عند 3.6 فولت)
- نسبة التروس: 1:48
- المحاور تخرج من كلا الجانبين
- قطر المحور: 5 مم
- الأبعاد: 64x20x20 مم
- الوزن: 26 جرام
و وقع الاختيار على وحدة MX1508 كما المحرك driver.You
يمكن قراءة حول وحدة هنا
المعايير الفنية:
- جهد الإمداد: 2-10 فولت
- سائق العمل لكل قناة: 1.5 أ (تيار الذروة 2.5 أ ، لا يزيد عن 10 ثوانٍ)
- المدخلات المنطقية: 5V
- الأبعاد: 24.7 × 21 × 0.5 ملم
بالنسبة للحركة الأفقية والعمودية لكاميرا IP ، تم اختيار محركات سيرفو SG90 2kg الشهيرة ،
ويتم تقديم المواصفات التالية على موقع الشركة المصنعة:
- الوزن: 9 جرام
- الأبعاد: 23 × 12.2 × 29 ملم
- عزم الدوران: 1.8 كجم / سم (4.8 فولت)
- نوع العتاد: مجموعة تروس بوم
- سرعة التشغيل: 0.1 ثانية / 60 درجة (4.8 فولت)
- جهد التشغيل: 4.8 فولت
- نطاق درجة الحرارة: 0 ℃ _ 55 ℃
- عرض النطاق الميت: 1us
- مزود الطاقة: من خلال محول خارجي
- طول السلك المؤازر: 25 سم
- قابس مؤازر: JR (يناسب JR و Futaba)
تم اختيار FPV Bracket Kit لكاميرا الويب
وصف الحامل في المتجر عبر الإنترنت: "يتيح لك FPV توجيه كاميرا FPV في 3 طائرات. سيسمح لك الاتصال والتشغيل البسيط بتجميع النظام الأساسي وتوصيله بسرعة بوحدة التحكم أو وحدة التحكم في الطيران. تُستخدم مع أجهزة EMAX 9g ES08A Mini Servo أو SG90 (مع بعض التعديلات). "
"مع بعض التعديلات" - يجب أن يؤخذ في الاعتبار ، يجب تعديل المجموعة بملف بالمعنى الحرفي. ولكن بالنسبة لـ DIY مقابل 3 دولارات ، فهذا لا شيء إلى حد كبير. اشتكى البعض من أنه حتى التنقيح لم يساعد ، ولم تتناسب الماكينات مع الحجم ، في حالتي ، جميع القواعد. يتم استخدام شريحتين من SG90 لتحريك الكاميرا أفقيًا وعموديًا. تم أيضًا النظر في خيار التصميم والطباعة على طابعة ثلاثية الأبعاد ، لكنني توقفت حتى الآن عند هذا الحامل.
كاميرا IP تعتمد على ESP32 CAM
كما هو موضح: " يوفر النظام الفرعي I2S في ESP32 أيضًا ناقلًا عالي السرعة متصلًا مباشرة بذاكرة الوصول العشوائي للوصول المباشر إلى الذاكرة. بعبارة بسيطة ، يمكنك تكوين النظام الفرعي ESP32 I2S لإرسال أو استقبال بيانات متوازية تحت تحكم الأجهزة. "
أولئك. يمكنك تكوين واجهة I2S ESP32 لإرسال أو استقبال بيانات متوازية تحت التحكم في الأجهزة ، والذي يتم تنفيذه لتوصيل الكاميرا. تم تطوير هذه اللوحة بواسطة Seeed Studio ، السعر 9.90 دولارًا هنا ، ولكن في متاجر الراديو الخاصة بنا يبيعونها مقابل 8 دولارات ، على ما يبدو لا يمكن لشركة Seeed Studio وحدها إنتاجها.
معلومات تقنية:
- أصغر وحدة 802.11b / g / n Wi-Fi BT SoC
- وحدة المعالجة المركزية منخفضة الطاقة 32 بت ، يمكن أن تخدم أيضًا معالج التطبيق
- سرعة ساعة تصل إلى 160 ميجا هرتز ملخص طاقة الحوسبة حتى 600 DMIPS
- مدمج 520 كيلوبايت SRAM ، 4MPSRAM خارجي
- يدعم UART / SPI / I2C / PWM / ADC / DAC
- دعم الكاميرات OV2640 و OV7670 ، مصباح فلاش مدمج.
- دعم تحميل صورة WiFI
- دعم بطاقة TF
- يدعم أوضاع نوم متعددة.
- مضمنة Lwip و FreeRTOS
- يدعم وضع تشغيل STA / AP / STA + AP
- دعم تقنية التكوين الذكي / AirKiss
- دعم ترقيات البرامج الثابتة المحلية والبعيدة للمنفذ التسلسلي (FOTA)
مصدر الطاقة
لم يتم التحكم في المنصة من مصدر طاقة مستقل لفترة طويلة دون إعادة الشحن. لذلك ، تم اختيار وحدة تزويد طاقة 2A 18650 مع مخرج USB بفتحة واحدة كمصدر.
مواصفات:
- نوع البطارية: 18650 Li-Ion (بدون حماية)
- جهد الشاحن: 5V إلى 8V
- الفولتية الإخراج:
- 3V - مباشرة من البطارية من خلال جهاز الحماية
- 5V - من خلال محول دفعة.
- أقصى تيار الإخراج:
- الإخراج 3V - 1A
- الإخراج 5V - 2A
- الحد الأقصى لتيار الشحن: 1A
- نوع موصل الإدخال: micro-USB
- نوع موصل الإخراج: USB-A
- مستهلكون 5 فولت - من الحمل الزائد وقصيرة الدائرة
- الأبعاد:
- ثنائي الفينيل متعدد الكلور: 29.5 × 99.5 × 19 ملم
- الجهاز بالكامل: 30 × 116 × 20 ملم
تم اختيار ESP-WROOM-32 كوحدة تحكم رئيسية ،
وقد وصفت سابقًا خصائص ESP32 بمزيد من التفصيل. فيما يلي الخصائص الأساسية للوحدة:
- معالج ميكرو ثنائي النواة Xtensa LX6 32 بت حتى 240 ميجاهرتز
- ذاكرة فلاش: 4 ميجا بايت
- اتصال لاسلكي Wi-Fi 802.11b / g / n حتى 150 ميجابت / ثانية ، بلوتوث 4.2 BR / EDR / BLE
- دعم أوضاع STA / AP / STA + AP ، مكدس TCP / IP مدمج
- GPIO 32 (واجهات UART و SPI و I2C و I2S و PWM ووحدات تحكم SD والسعة اللمس و ADC و DAC والمزيد
- مزود الطاقة: عبر موصل microUSB (محول CP2102) أو النواتج
- خطوة الدبوس: 2.54 مم (يمكن إدخالها في اللوح)
- حجم اللوح: 5.2 × 2.8 سم
اثنين البصرية "بدون اسم" والترميز يستخدم كما استشعار سرعة عد النبضات دوران الأقراص النقطية المحرك على العجلات
.
- جهد الإمداد: 3.3 فولت - 5 فولت
- عرض أخدود الاستشعار: 6 مم ؛
- نوع الإخراج: التناظرية والرقمية
- المؤشر: حالة الإخراج
و استخدمت شعبية بالموجات فوق الصوتية ويأتي إطلاق الشركة HC-SR04 ل قياس المسافة.
الميزات:
- امدادات التيار الكهربائي: 5V
- الاستهلاك في الوضع الصامت: 2mA
- الاستهلاك أثناء التشغيل: 15 مللي أمبير
- مدى المسافة: 2-400 سم
- زاوية الرؤية الفعالة: 15
- زاوية عرض العمل: 30 درجة
تطبيقات البرمجيات
كانت الخطوة الأولى هي التعرف على وحدة ESP32 CAM ووميضها.
يتم تقديم وصف العمل مع الوحدة على Harba هنا وهنا وعلى مصادر أخرى.
تصف معظم المقالات عملية وميض بسيطة باستخدام Arduino IDE. في معظم الحالات ، يكون هذا كافيًا ، وفي البداية كان هذا الخيار مناسبًا لي أيضًا.
في متاجر الراديو ، تُباع وحدات ESP32-CAM بكاميرا OV2640 ، لذلك تحتاج إلى إجراء تغيير بسيط في الرسم التخطيطي:
// Select camera model
//#define CAMERA_MODEL_WROVER_KIT
//#define CAMERA_MODEL_ESP_EYE
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WIDE
#define CAMERA_MODEL_AI_THINKER
وأيضًا حدد SSID وكلمة المرور لنقطة وصول Wi-Fi
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
أحد شروط عمل كاميرا الويب في حالتي هو القدرة على إرسال دفق فيديو عبر خادم وكيل Keenetic. أنا أستخدم جهاز التوجيه المنزلي Keenetik Viva. توفر خدمة KeenDNS اسم المجال لمورد الويب المنزلي. لكن لدهشتي ، فشلت المحاولة الأولى. عند محاولة الوصول عن بعد عبر الإنترنت ، تلقيت الخطأ "حقول الرأس طويلة جدًا بحيث يتعذر على الخادم تفسيرها". مع هذه المشكلة التي واجهتها لأول مرة. حل هذه المشكلة هو تغيير تكوين CONFIG_HTTPD_MAX_REQ_HDR_LEN ، على سبيل المثال
#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 2048
في حالة Arduino IDE ESP32 ، تم بالفعل تجميع الوحدات النمطية وتقديمها كمكتبات ثابتة ، موجودة في Windows على طول المسار -٪ userprofile٪ \ AppData \ Local \ Arduino15 \packs \ esp32 \ Hardware \ esp32 \ 1.0.4 \ tools \ sdk \
Just لن يؤدي تغيير المعلمة في الرأس إلى إجراء أي شيء.
أي لتغيير التكوين ، نحتاج إلى إعادة تجميع مكتبات ESP-IDF.
كان الحل هو استنساخ مشروع github.com/espressif/esp-who . في الدليل الذي يحتوي على أمثلة ، نجد مشروع camera_web_server ، وقم بتغيير معلمة الحد الأقصى لطول الرأس ، ولا تنس أيضًا تحديد إعدادات اتصال Wi-Fi
حتى يتم تجميع المشروع ، كان علينا تثبيت مربع اختيار آخر - مصفوفة الدعم 'rtc_gpio_desc' لـ ESP32
بعد تجميع وتحميل المشروع بنجاح ، انتقل إلى عنوان IP المقابل في المتصفح وانتقل إلى الصفحة التي تحتوي على واجهة كاميرا الويب الخاصة بنا.
الواجهة مشابهة لأمثلة Arduino ، ولكن تمت إضافة بعض الوظائف.
لقد أجريت تغييرات صغيرة على ملف app_httpd.c الأصلي للتحكم في إشارة دبوس GPIO_NUM_2 من واجهة الويب. على الرغم من أن وصف الوحدة يشير إلى استخدام المسامير لاحتياجات بطاقة SD ، إلا أنني لا أستخدمها ، لذلك يمكنني استخدام هذه المسامير.
void app_httpd_main()
{
gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);
static esp_err_t cmd_handler(httpd_req_t *req)
{
.......
// 736
else if(!strcmp(variable, "gpio2")) {
if (val == 0)
gpio_set_level(GPIO_NUM_2, 0);
else
gpio_set_level(GPIO_NUM_2, 1);
}
بالنسبة لجهاز التحكم عن بعد ، قمت بالتعويض عن لوحة Node-Red غير معقدة تعمل على Raspberry pi.
تمكنا من تضمين صورة دفق الفيديو في عقدة القالب:
<iframe
src="http://192.168.1.61"
width="300" height="300">
</iframe>
نقطة واحدة مهمة هنا: من الضروري تضمين http ، في حالة https ستكون هناك مشاكل مع سياسة أمان المحتوى. إذا ظهرت مشاكل في هذه الحالة ، يمكنك محاولة إضافة رؤوس:
<script>
var meta = document.createElement('meta');
meta.httpEquiv = "Content-Security-Policy";
meta.content = "default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * 'unsafe-inline';";
document.getElementsByTagName('head')[0].appendChild(meta);
</script>
للتحكم في دبوس GPIO_NUM_2 الخاص بوحدة ESP32-CAM ، بعد إجراء تغييرات على البرنامج الثابت ، يجب تنفيذ طلب http GET التالي:
http://192.168.1.61/control?var=gpio2&val=1 // 0
على واجهة اللوحة ، هذا هو مفتاح التنشيط ، في سلسلة العامل يبدو مثل هذا
حيث وظيفة الطلب:
var newMsg = {}
var i = msg.payload ? 1 : 0;
newMsg.query = "control?var=gpio2&val=" + i
node.send(newMsg)
إعدادات عقدة طلب http:
يتم إرسال المعلمات والحالات الأخرى عبر MQTT
اتصال Wi-Fi و MQTT
سأقدم أمثلة على استخدام إطار عمل Arduino منذ أن جربته أيضًا. لكن في النهاية ، لدي تطبيق عملي على ESP-IDF.
# تضمين رأس <WiFi.h>
وظيفة اتصال Wi-Fi لإطار Arduino
void setup_wifi()
{
Serial.println("Starting connecting WiFi.");
delay(1000);
for (int8_t i = 0; i < 3; i++)
{
WiFi.begin(ssid, password);
uint32_t start = millis();
while (WiFi.status() != WL_CONNECTED && ((millis() - start) < 4000))
{
Serial.print(".");
delay(500);
}
if (WiFi.status() == WL_CONNECTED)
{
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
return;
}
else
{
Serial.println("Connecting Failed");
//WiFi.reconnect(); // this reconnects the AP so the user can stay on the page
}
}
}
تحتوي الوظيفة على حلقة لثلاث تكرارات ، منذ ذلك الحين غالبًا ما يفشل في الاتصال في المحاولة الأولى ، ثم ينتظر بلا نهاية لحالة WL_CONNECTED. ربما لا يزال بإمكانك حلها بطريقة أخرى ، لكنها تعمل على هذا النحو.
يتم الاتصال بـ MQTT لإطار عمل Arduino باستخدام مكتبة github.com/knolleary/pubsubclient.git .
لاستخدام المكتبة ، تحتاج إلى تضمين العنوان #include <PubSubClient.h>
وظيفة اتصال MQTT
bool setup_mqtt()
{
if (WiFi.status() == WL_CONNECTED)
{
if (!client.connected())
{
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
Serial.print("Connecting to MQTT server ");
Serial.print(mqtt_server);
Serial.println("...");
String clientId = "ESP32_car_client";
if (client.connect(clientId.c_str()))
{
Serial.println("Connected to MQTT server ");
//subscribing to topics
client.subscribe("esp32/car/#");
client.subscribe("esp32/camera/#");
return true;
}
else
{
Serial.println("Could not connect to MQTT server");
return false;
}
}
return false;
}
أولاً ، نتحقق من أننا متصلون بشبكة Wi-Fi ، ثم نتصل بعميل الوسيط .setServer (mqtt_server ، 1883) ؛
وتعيين وظيفة رد الاتصال client.setCallback (رد الاتصال) ؛
وظيفة رد الاتصال MQTT
void callback(char *topic, byte *payload, unsigned int length)
{
Serial.println("Message arrived ");
memset(payload_buf, 0, 10);
for (int i = 0; i < length; i++)
{
payload_buf[i] = (char)payload[i];
}
command_t mqtt_command = {
.topic = topic,
.message = payload_buf};
xQueueSend(messageQueue, (void *)&mqtt_command, 0);
}
في حالة الاتصال بنجاح ، اشترك في الموضوعات
client.subscribe("esp32/car/#");
client.subscribe("esp32/camera/#");
كانت هناك بعض حالات انقطاع اتصال MQTT ، لذلك تمت إضافة فحص إلى مهمة الاستقصاء الدوري.
مهمة الاقتراع الدوري
void pollingTask(void *parameter)
{
int32_t start = 0;
while (true) {
if (!client.connected()) {
long now = millis();
if (now - start > 5000) {
start = now;
// Attempt to reconnect
if (setup_mqtt()) {
start = 0;
}
}
}
else {
client.loop();
int val = digitalRead(WAKEUP_PIN);
if (val == LOW) {
Serial.println("Going to sleep now");
esp_deep_sleep_start();
}
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
تم وصف مثال على الاتصال بشبكات Wi-FI و MQTT باستخدام ESP-IDF في المقالة السابقة. في
حالة استخدام ESP-IDF ، لم تكن هناك أخطاء عند الاتصال بشبكة Wi-Fi و MQTT. فارق بسيط واحد عند معالجة البيانات من موضوع MQTT في دالة esp_err_t mqtt_event_handler (حدث esp_mqtt_event_handle_t): عندما يكون نوع الحدث هو MQTT_EVENT_DATA ، يجب أن تأخذ في الاعتبار الحدث-> topic_len و event-> data_len المعلمات ونأخذ اسم الموضوع وبياناته بالطول المناسب تمامًا. للقيام بذلك ، يمكننا إنشاء مصفوفات مخزن مؤقت أو تخصيص الذاكرة ديناميكيًا (ثم تحريرها) ، ونسخ البيانات ، على سبيل المثال
strncpy(topic, event->topic, event->topic_len);
strncpy(data, event->data, event→data_len);
يتم إرسال البيانات إلى موضوع باستخدام وظيفة esp_mqtt_client_publish
esp_mqtt_client_publish(client, topics[i], topic_buff[i], 0,0,0);
HC-SR04 معالجة بيانات مستشعر الموجات فوق الصوتية
HC-SR04 عبارة عن مستشعر رخيص وشائع لتصميم أجهزة الميكروكونترولر. وكما جرت العادة، وهناك الكثير من المواد على شبكة الإنترنت حول هذا الموضوع: هنا و هنا. يمكن أيضًا عرض الوصف هنا ، وورقة بيانات قصيرة هنا.
باختصار ، لبدء قياس المسافة ، يجب عليك تطبيق إشارة عالية لمدة 10 μs على Trig pin. هذا يبدأ المستشعر في إرسال 8 دورات من نبضة فوق صوتية 40 كيلو هرتز وانتظار النبض فوق الصوتي المنعكس. عندما يكتشف محول الطاقة إشارة فوق صوتية من جهاز الاستقبال ، فإنه يضبط خرج Echo عاليًا ويتأخر بمقدار فترة (عرض) تتناسب مع المسافة. لحساب المسافة ، تحتاج إلى حساب الصيغة:
distance = duration * 340 / = duration * 0.034 /,
340 م / ث - سرعة انتشار الصوت في الهواء.
في إطار عمل Arduino ، تتيح لك وظيفة pulseIn معرفة مدة النبض بـ μs.
بالنسبة لـ ESP-IDF ، يوجد مشروع مكتبة مكونات ESP-IDF ، والذي يحتوي أيضًا على مكون فوق صوتي لـ HC-SR04.
عينة من الرموز
esp_err_t ultrasonic_measure_cm(const ultrasonic_sensor_t *dev, uint32_t max_distance, uint32_t *distance)
{
CHECK_ARG(dev && distance);
PORT_ENTER_CRITICAL;
// Ping: Low for 2..4 us, then high 10 us
CHECK(gpio_set_level(dev->trigger_pin, 0));
ets_delay_us(TRIGGER_LOW_DELAY);
CHECK(gpio_set_level(dev->trigger_pin, 1));
ets_delay_us(TRIGGER_HIGH_DELAY);
CHECK(gpio_set_level(dev->trigger_pin, 0));
// Previous ping isn't ended
if (gpio_get_level(dev->echo_pin))
RETURN_CRITICAL(ESP_ERR_ULTRASONIC_PING);
// Wait for echo
int64_t start = esp_timer_get_time();
while (!gpio_get_level(dev->echo_pin))
{
if (timeout_expired(start, PING_TIMEOUT))
RETURN_CRITICAL(ESP_ERR_ULTRASONIC_PING_TIMEOUT);
}
// got echo, measuring
int64_t echo_start = esp_timer_get_time();
int64_t time = echo_start;
int64_t meas_timeout = echo_start + max_distance * ROUNDTRIP;
while (gpio_get_level(dev->echo_pin))
{
time = esp_timer_get_time();
if (timeout_expired(echo_start, meas_timeout))
RETURN_CRITICAL(ESP_ERR_ULTRASONIC_ECHO_TIMEOUT);
}
PORT_EXIT_CRITICAL;
*distance = (time - echo_start) / ROUNDTRIP;
return ESP_OK;
}
يوجد شرح للخوارزمية في التعليقات. يتم قياس مدة النبضة في حلقة while بينما يكون مستوى الإشارة مرتفعًا على دبوس Echo (بعد // الحصول على صدى وقياس) وبعد ذلك يتم قياس المسافة
*distance = (time - echo_start) / ROUNDTRIP
معامل الحصول على المسافة بالسنتيمتر ROUNDTRIP = 58.
في إطار Arduino يبدو الأمر أسهل
عينة من الرموز
#include "ultrasonic.h"
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
#define PORT_ENTER_CRITICAL portENTER_CRITICAL(&mux)
#define PORT_EXIT_CRITICAL portEXIT_CRITICAL(&mux)
Ultrasonic::Ultrasonic() {
pinMode(GPIO_NUM_33, OUTPUT);
pinMode(GPIO_NUM_26, INPUT);
}
uint32_t Ultrasonic::calculateDistance() {
PORT_ENTER_CRITICAL;
digitalWrite(GPIO_NUM_33, LOW);
delayMicroseconds(2);
digitalWrite(GPIO_NUM_33, HIGH);
delayMicroseconds(10);
digitalWrite(GPIO_NUM_33, LOW);
duration = pulseIn(GPIO_NUM_26, HIGH);
PORT_EXIT_CRITICAL;
distance = duration / 58;
return distance;
}
uint32_t Ultrasonic::getDistance() {
return distance;
}
كانت هناك محاولة لاستخدام مكتبة ESP-IDF بالموجات فوق الصوتية لمشروع ESP32 Arduino ، لكن هذه الحالة تعمل حتى حدوث خلل في المستشعر الأول. لماذا لم يكن من الممكن معرفة ذلك بالضبط. خلل المستشعر هو خطأ دوري في تقدير النبضات وإصدار قراءات خاطئة ، في الأرقام المحسوبة تبدو وكأنها مسافة تزيد عن 20000 سم ، وكتبوا في المنتديات أن هذا بسبب جهاز استشعار رديء الجودة (نسخة صينية).
قياس السرعة بأجهزة الاستشعار البصرية
تعتمد الوحدة البصرية لقراءة النبضات على مقارنة LM393 وجهاز استشعار الفتحة. مصمم للاستخدام مع الأقراص النقطية التي تتلاءم مع عمود علبة التروس أو المحرك الكهربائي.
كالعادة ، توجد بالفعل مقالات حول هذا الموضوع: digitrode.ru و mirrobo.ru و arduino-kit.ru .
دائرة الاستشعار:
في إطار Arduino نحسب السرعة على النحو التالي:
- تحديد متغير (هيكل) العداد ، على سبيل المثال
typedef struct {
int encoder_pin = ENCODER_PIN; // pulse output from the module
unsigned int rpm = 0; // rpm reading
volatile byte pulses = 0; // number of pulses
unsigned long timeold = 0;
unsigned int pulsesperturn = 20;
} pulse_t;
بعد ذلك ، في وظيفة الإعداد ، يجب علينا تسجيل دبوس الإدخال ومقاطعته
pinMode(pulse_struct.encoder_pin, INPUT);
attachInterrupt(pulse_struct.encoder_pin, counter, FALLING);
بعد ذلك ، يتم حساب عدد الدورات في الدقيقة
pulse_struct.rpm =
(60 * 1000 / pulse_struct.pulsesperturn )/
(1000)* pulse_struct.pulses;
عينة من الرموز
void pulseTask(void *parameters) {
sensor_data_t data;
data.sensor = OPTICAL_SENSOR;
portBASE_TYPE xStatus;
while (true) {
//Don't process interrupts during calculations
detachInterrupt(0);
pulse_struct.rpm =
(60 * 1000 / pulse_struct.pulsesperturn )/
(1000)* pulse_struct.pulses;
pulse_struct.pulses = 0;
data.value = pulse_struct.rpm;
//Restart the interrupt processing
attachInterrupt(0, counter, FALLING);
Serial.print("optical: ");
Serial.println(data.value);
//Sending data to sensors queue
xStatus = xQueueSend(sensorQueue, (void *)&data, 0);
if( xStatus != pdPASS ) {
printf("Could not send optical to the queue.\r\n");
}
taskYIELD();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
في 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;
void pulseTask(void *parameters) {
sensor_data_t data_1;
sensor_data_t data_2;
data_1.sensor = OPTICAL_SENSOR_1;
data_2.sensor = OPTICAL_SENSOR_2;
portBASE_TYPE xStatus;
speed_sensor_params_t params_1 = {
.delay = 100,
.pin = ENCODER_1_PIN,
.ctrl_pin = GPIO_NUM_0,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.count = 0,
};
ESP_ERROR_CHECK(init_speed_sensor(¶ms_1));
speed_sensor_params_t params_2 = {
.delay = 100,
.pin = ENCODER_2_PIN,
.ctrl_pin = GPIO_NUM_1,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_1,
.count = 0,
};
ESP_ERROR_CHECK(init_speed_sensor(¶ms_2));
while(true) {
data_1.value = calculateRpm(¶ms_1);
data_2.value = calculateRpm(¶ms_2);
sensor_array[OPTICAL_SENSOR_1] = data_1.value;
sensor_array[OPTICAL_SENSOR_2] = data_2.value;
printf("speed 1 = %d\n", data_1.value);
printf("speed 2 = %d\n", data_2.value);
xStatus = xQueueSend(sensorQueue, (void *)&data_1, 0);
xStatus = xQueueSend(sensorQueue, (void *)&data_2, 0);
if( xStatus != pdPASS ) {
printf("Could not send optical to the queue.\r\n");
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
تحكم PWM
يمكنك أن تقرأ عن التحكم في محركات الأقراص المؤازرة على Arduino على developer.alexanderklimov ، wiki.amperka.ru .
كما هو مذكور في المصدر أعلاه: "المؤازرة هي آلية بمحرك كهربائي يمكن أن تدور بزاوية معينة وتحافظ على وضعها الحالي." من الناحية العملية ، نحن نتعامل مع تعديل عرض النبضة ، حيث تعتمد زاوية دوران المشغل على عرض نبضة الإشارة.
بالنسبة لـ ESP32 على إطار عمل Arduino ، يمكنك استخدام مكتبة ESP32Servo
لهذا نقوم بتوصيل الرأس
#include <ESP32Servo.h>
قم بإنشاء كائن
Servo servo_horisontal;
نشير إلى دبوس الإخراج
servo_horisontal.attach(SERVO_CAM_HOR_PIN);
بعد ذلك يمكننا تدوين القيمة المطلوبة لمقدار الدوران
servo_horisontal.write(value);
يتم التحكم في PWM لأنواع أخرى من الأجهزة في إطار عمل Arduino باستخدام مكتبة esp32-hal-ledc.h.
لا تدعم وحدات التحكم الدقيقة ESP32 وظيفة Arduino analogWrite () القياسية لـ PWM. بدلا منهم، وظائف
قدم : ledcSetup (قناة، التكرار، resolution_bits) - القناة، تردد والقرار على
ledcAttachPin أشار (GPIO، قناة) - الميناء وقناة يشار إلى
ledcWrite (قناة، dutycycle) - القناة واجب دورة إشارة PWM يشار إلى
أمثلة يمكن رؤية
كيف كما يوحي الاسم ، تم تصميم الوظائف في الأصل للتحكم في وحدات LED ، ولكنها تُستخدم أيضًا لأغراض أخرى.
في إطار عمل ESP-IDF ، يتم التحكم في محرك الأقراص المؤازر بنفس طريقة التحكم في المبدل باستخدام وحدة MCPWM ، كما هو موضح في المقالة السابقة. يمكن مشاهدة مثال على التحكم في محرك مؤازر MCPWM هنا
عينة من الرموز
static uint32_t servo_per_degree_init(uint32_t degree_of_rotation)
{
uint32_t cal_pulsewidth = 0;
cal_pulsewidth = (SERVO_MIN_PULSEWIDTH + (((SERVO_MAX_PULSEWIDTH - SERVO_MIN_PULSEWIDTH) * (degree_of_rotation)) / (SERVO_MAX_DEGREE)));
return cal_pulsewidth;
}
void mcpwm_example_servo_control(void *arg)
{
uint32_t angle, count;
//1. mcpwm gpio initialization
mcpwm_example_gpio_initialize();
//2. initial mcpwm configuration
printf("Configuring Initial Parameters of mcpwm......\n");
mcpwm_config_t pwm_config;
pwm_config.frequency = 50; //frequency = 50Hz, i.e. for every servo motor time period should be 20ms
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
while (1) {
for (count = 0; count < SERVO_MAX_DEGREE; count++) {
printf("Angle of rotation: %d\n", count);
angle = servo_per_degree_init(count);
printf("pulse width: %dus\n", angle);
mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle);
vTaskDelay(10); //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation at 5V
}
}
}
أولئك. نحتاج إلى تهيئة الوحدة النمطية باستخدام وظيفة mcpwm_init (MCPWM_UNIT_0 ، MCPWM_TIMER_0 ، & pwm_config) ؛
ثم قم بتعيين قيمة الزاوية
mcpwm_set_duty_in_us (MCPWM_UNIT_0 ، MCPWM_TIMER_0 ، MCPWM_OPR_A ، زاوية) ؛
يمكن العثور على أمثلة لاستخدام وحدة MCPWM لأنواع محركات الأقراص المختلفة على جيثب .
تم أيضًا تقديم مثال على التحكم في المحرك المصقول في المقالة السابقة .
وتجدر الإشارة إلى أن مثل هذه المنصة هي نظام غير اقتصادي يتم التحكم فيه بشكل تفاضليالنظام. تتميز المحركات بتنوع في الأداء ، لذلك يتعين عليك تعيين إزاحة برمجية لأحدها لضمان سرعة موحدة. يمكنك التعرف على النظرية على robotosha.ru robotosha.ru/robotics/robot-motion.html . للتحكم الأمثل في المحركات الموجهة ، يتم استخدام خوارزمية PID مع ردود الفعل في شكل أجهزة استشعار بصرية. ويرد وصف خوارزمية هنا و هنا .
وصف معادلات الحركة ، وكذلك خوارزميات التحكم ، خارج نطاق هذا المقال. لم يتم تنفيذ الكينماتيكا التفاضلية بعد في الكود.
أوضاع النوم
وفقًا للوثائق ، بالإضافة إلى الوصف الوارد في المقالة ، يمكن لـ ESP32 التبديل بين أوضاع الطاقة المختلفة:
- الوضع النشط
- وضع السكون في المودم
- وضع النوم الخفيف
- وضع السكون العميق
- وضع السبات
يوضح الجدول الاختلافات في الاستهلاك الحالي في أوضاع مختلفة.
لقد قمت بتمكين وضع Deep Sleep في حالة عدم وجود إشارة عالية على دبوس GPIO_NUM_13
gpio_set_direction(WAKEUP_PIN, GPIO_MODE_INPUT);
esp_sleep_enable_ext0_wakeup(WAKEUP_PIN,1); //1 = High, 0 = Low
في حالة عدم وجود تأثير خارجي ، قمت بسحب إدخال 10 كيلو بمقاوم إلى 3.3 فولت ، على الرغم من أنه ممكن في البرنامج. وفي مهمة الاقتراع الدوري ، أتحقق من حالة إشارة الإدخال
if(!gpio_get_level(WAKEUP_PIN)) {
printf("Going to sleep now\n");
esp_deep_sleep_start();
}
هذا سوف يختتم الوصف. تم عرض مثال عملي لاستخدام وحدات ESP32 مع ملحقات مختلفة. كما تم التطرق إلى بعض قضايا تنفيذ البرامج والمقارنة بين نهج ESP-IDF و Arduino.