22 KiB
צילום תמונה - Wio Terminal
בחלק זה של השיעור, תוסיף מצלמה ל-Wio Terminal שלך ותצלם תמונות ממנה.
חומרה
ה-Wio Terminal זקוק למצלמה.
המצלמה שבה תשתמש היא ArduCam Mini 2MP Plus. זו מצלמה של 2 מגה-פיקסל המבוססת על חיישן תמונה OV2640. היא מתקשרת באמצעות ממשק SPI לצילום תמונות ומשתמשת ב-I2C כדי להגדיר את החיישן.
חיבור המצלמה
ל-ArduCam אין שקע Grove, במקום זאת היא מתחברת גם ל-SPI וגם ל-I2C דרך פיני GPIO ב-Wio Terminal.
משימה - חיבור המצלמה
חבר את המצלמה.
-
הפינים בבסיס ה-ArduCam צריכים להיות מחוברים לפיני GPIO ב-Wio Terminal. כדי להקל על מציאת הפינים הנכונים, הצמד את מדבקת פיני GPIO שמגיעה עם ה-Wio Terminal סביב הפינים:
-
באמצעות חוטי ג'אמפר, בצע את החיבורים הבאים:
פין ArduCAM פין Wio Terminal תיאור CS 24 (SPI_CS) בחירת שבב SPI MOSI 19 (SPI_MOSI) יציאת בקר SPI, כניסת התקן היקפי MISO 21 (SPI_MISO) כניסת בקר SPI, יציאת התקן היקפי SCK 23 (SPI_SCLK) שעון סדרתי SPI GND 6 (GND) אדמה - 0V VCC 4 (5V) ספק כוח 5V SDA 3 (I2C1_SDA) נתונים סדרתיים I2C SCL 5 (I2C1_SCL) שעון סדרתי I2C חיבורי GND ו-VCC מספקים ספק כוח של 5V ל-ArduCam. הוא פועל ב-5V, בניגוד לחיישני Grove שפועלים ב-3V. כוח זה מגיע ישירות מחיבור ה-USB-C שמספק כוח למכשיר.
💁 עבור חיבור SPI, תוויות הפינים ב-ArduCam ושמות הפינים ב-Wio Terminal המשמשים בקוד עדיין משתמשים במוסכמת השמות הישנה. ההוראות בשיעור זה ישתמשו במוסכמת השמות החדשה, למעט כאשר שמות הפינים משמשים בקוד.
-
כעת תוכל לחבר את ה-Wio Terminal למחשב שלך.
תכנות המכשיר להתחבר למצלמה
כעת ניתן לתכנת את ה-Wio Terminal להשתמש במצלמת ArduCAM המחוברת.
משימה - תכנות המכשיר להתחבר למצלמה
-
צור פרויקט חדש ל-Wio Terminal באמצעות PlatformIO. קרא לפרויקט זה
fruit-quality-detector
. הוסף קוד לפונקצייתsetup
כדי להגדיר את יציאת הסריאל. -
הוסף קוד להתחברות ל-WiFi, עם פרטי ה-WiFi שלך בקובץ שנקרא
config.h
. אל תשכח להוסיף את הספריות הנדרשות לקובץplatformio.ini
. -
ספריית ArduCam אינה זמינה כספריית Arduino שניתן להתקין מקובץ
platformio.ini
. במקום זאת, יש להתקין אותה ממקור בעמוד GitHub שלהם. ניתן להשיג זאת על ידי:- שיבוט הריפו מ-https://github.com/ArduCAM/Arduino.git
- מעבר לריפו ב-GitHub בכתובת github.com/ArduCAM/Arduino והורדת הקוד כקובץ zip מכפתור Code
-
אתה זקוק רק לתיקיית
ArduCAM
מהקוד הזה. העתק את כל התיקייה לתוך תיקייתlib
בפרויקט שלך.⚠️ יש להעתיק את כל התיקייה, כך שהקוד יהיה ב-
lib/ArduCam
. אל תעתיק רק את התוכן של תיקייתArduCam
לתוך תיקייתlib
, העתק את כל התיקייה. -
קוד ספריית ArduCam עובד עבור סוגים שונים של מצלמות. סוג המצלמה שברצונך להשתמש בו מוגדר באמצעות דגלי קומפילציה - זה שומר על הספרייה הבנויה קטנה ככל האפשר על ידי הסרת קוד עבור מצלמות שאינך משתמש בהן. כדי להגדיר את הספרייה עבור מצלמת OV2640, הוסף את הדברים הבאים לסוף קובץ
platformio.ini
:build_flags = -DARDUCAM_SHIELD_V2 -DOV2640_CAM
זה מגדיר שני דגלי קומפילציה:
ARDUCAM_SHIELD_V2
כדי להודיע לספרייה שהמצלמה נמצאת על לוח Arduino, המכונה מגן.OV2640_CAM
כדי להודיע לספרייה לכלול רק קוד עבור מצלמת OV2640.
-
הוסף קובץ כותרת לתיקיית
src
בשםcamera.h
. קובץ זה יכיל קוד לתקשורת עם המצלמה. הוסף את הקוד הבא לקובץ זה:#pragma once #include <ArduCAM.h> #include <Wire.h> class Camera { public: Camera(int format, int image_size) : _arducam(OV2640, PIN_SPI_SS) { _format = format; _image_size = image_size; } bool init() { // Reset the CPLD _arducam.write_reg(0x07, 0x80); delay(100); _arducam.write_reg(0x07, 0x00); delay(100); // Check if the ArduCAM SPI bus is OK _arducam.write_reg(ARDUCHIP_TEST1, 0x55); if (_arducam.read_reg(ARDUCHIP_TEST1) != 0x55) { return false; } // Change MCU mode _arducam.set_mode(MCU2LCD_MODE); uint8_t vid, pid; // Check if the camera module type is OV2640 _arducam.wrSensorReg8_8(0xff, 0x01); _arducam.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid); _arducam.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid); if ((vid != 0x26) && ((pid != 0x41) || (pid != 0x42))) { return false; } _arducam.set_format(_format); _arducam.InitCAM(); _arducam.OV2640_set_JPEG_size(_image_size); _arducam.OV2640_set_Light_Mode(Auto); _arducam.OV2640_set_Special_effects(Normal); delay(1000); return true; } void startCapture() { _arducam.flush_fifo(); _arducam.clear_fifo_flag(); _arducam.start_capture(); } bool captureReady() { return _arducam.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK); } bool readImageToBuffer(byte **buffer, uint32_t &buffer_length) { if (!captureReady()) return false; // Get the image file length uint32_t length = _arducam.read_fifo_length(); buffer_length = length; if (length >= MAX_FIFO_SIZE) { return false; } if (length == 0) { return false; } // create the buffer byte *buf = new byte[length]; uint8_t temp = 0, temp_last = 0; int i = 0; uint32_t buffer_pos = 0; bool is_header = false; _arducam.CS_LOW(); _arducam.set_fifo_burst(); while (length--) { temp_last = temp; temp = SPI.transfer(0x00); //Read JPEG data from FIFO if ((temp == 0xD9) && (temp_last == 0xFF)) //If find the end ,break while, { buf[buffer_pos] = temp; buffer_pos++; i++; _arducam.CS_HIGH(); } if (is_header == true) { //Write image data to buffer if not full if (i < 256) { buf[buffer_pos] = temp; buffer_pos++; i++; } else { _arducam.CS_HIGH(); i = 0; buf[buffer_pos] = temp; buffer_pos++; i++; _arducam.CS_LOW(); _arducam.set_fifo_burst(); } } else if ((temp == 0xD8) & (temp_last == 0xFF)) { is_header = true; buf[buffer_pos] = temp_last; buffer_pos++; i++; buf[buffer_pos] = temp; buffer_pos++; i++; } } _arducam.clear_fifo_flag(); _arducam.set_format(_format); _arducam.InitCAM(); _arducam.OV2640_set_JPEG_size(_image_size); // return the buffer *buffer = buf; } private: ArduCAM _arducam; int _format; int _image_size; };
זהו קוד ברמה נמוכה שמגדיר את המצלמה באמצעות ספריות ArduCam ומוציא את התמונות כאשר נדרש באמצעות אוטובוס SPI. קוד זה מאוד ספציפי ל-ArduCam, כך שאין צורך לדאוג כיצד הוא עובד בשלב זה.
-
ב-
main.cpp
, הוסף את הקוד הבא מתחת להצהרותinclude
האחרות כדי לכלול את הקובץ החדש וליצור מופע של מחלקת המצלמה:#include "camera.h" Camera camera = Camera(JPEG, OV2640_640x480);
זה יוצר
Camera
ששומר את התמונות כ-JPEG ברזולוציה של 640 על 480. למרות שתומכים ברזולוציות גבוהות יותר (עד 3280x2464), מסווג התמונות עובד על תמונות קטנות בהרבה (227x227), כך שאין צורך לצלם ולשלוח תמונות גדולות יותר. -
הוסף את הקוד הבא מתחת לכך כדי להגדיר פונקציה להגדרת המצלמה:
void setupCamera() { pinMode(PIN_SPI_SS, OUTPUT); digitalWrite(PIN_SPI_SS, HIGH); Wire.begin(); SPI.begin(); if (!camera.init()) { Serial.println("Error setting up the camera!"); } }
פונקציית
setupCamera
מתחילה בהגדרת פין בחירת השבב של SPI (PIN_SPI_SS
) כגבוה, מה שהופך את ה-Wio Terminal לבקר SPI. לאחר מכן היא מתחילה את אוטובוסי I2C ו-SPI. לבסוף היא מאתחלת את מחלקת המצלמה שמגדירה את הגדרות חיישן המצלמה ומוודאת שהכל מחובר כראוי. -
קרא לפונקציה זו בסוף פונקציית
setup
:setupCamera();
-
בנה והעלה את הקוד הזה, ובדוק את הפלט ממוניטור הסריאל. אם אתה רואה
Error setting up the camera!
, בדוק את החיווט כדי לוודא שכל הכבלים מחברים את הפינים הנכונים ב-ArduCam לפינים הנכונים ב-GPIO ב-Wio Terminal, וכל כבלי הג'אמפר מחוברים כראוי.
צילום תמונה
כעת ניתן לתכנת את ה-Wio Terminal לצלם תמונה כאשר לוחצים על כפתור.
משימה - צילום תמונה
-
מיקרו-בקרים מריצים את הקוד שלך ברציפות, כך שלא קל להפעיל משהו כמו צילום תמונה מבלי להגיב לחיישן. ל-Wio Terminal יש כפתורים, כך שניתן להגדיר את המצלמה להיות מופעלת על ידי אחד הכפתורים. הוסף את הקוד הבא לסוף פונקציית
setup
כדי להגדיר את כפתור C (אחד משלושת הכפתורים בחלק העליון, הקרוב ביותר למתג ההפעלה).pinMode(WIO_KEY_C, INPUT_PULLUP);
מצב
INPUT_PULLUP
למעשה הופך קלט. לדוגמה, בדרך כלל כפתור ישלח אות נמוך כאשר אינו נלחץ, ואות גבוה כאשר נלחץ. כאשר מוגדר ל-INPUT_PULLUP
, הם שולחים אות גבוה כאשר אינם נלחצים, ואות נמוך כאשר נלחצים. -
הוסף פונקציה ריקה להגיב ללחיצת הכפתור לפני פונקציית
loop
:void buttonPressed() { }
-
קרא לפונקציה זו בפונקציית
loop
כאשר הכפתור נלחץ:void loop() { if (digitalRead(WIO_KEY_C) == LOW) { buttonPressed(); delay(2000); } delay(200); }
מפתח זה בודק אם הכפתור נלחץ. אם הוא נלחץ, פונקציית
buttonPressed
נקראת, והלולאה מתעכבת למשך 2 שניות. זה כדי לאפשר זמן לשחרור הכפתור כך שלחיצה ארוכה לא תירשם פעמיים.💁 הכפתור ב-Wio Terminal מוגדר ל-
INPUT_PULLUP
, כך שהוא שולח אות גבוה כאשר אינו נלחץ, ואות נמוך כאשר נלחץ. -
הוסף את הקוד הבא לפונקציית
buttonPressed
:camera.startCapture(); while (!camera.captureReady()) delay(100); Serial.println("Image captured"); byte *buffer; uint32_t length; if (camera.readImageToBuffer(&buffer, length)) { Serial.print("Image read to buffer with length "); Serial.println(length); delete(buffer); }
קוד זה מתחיל את צילום המצלמה על ידי קריאה ל-
startCapture
. החומרה של המצלמה אינה עובדת על ידי החזרת הנתונים כאשר אתה מבקש אותם, במקום זאת אתה שולח הוראה להתחיל לצלם, והמצלמה תעבוד ברקע לצלם את התמונה, להמיר אותה ל-JPEG, ולאחסן אותה במאגר מקומי על המצלמה עצמה. קריאה ל-captureReady
לאחר מכן בודקת אם צילום התמונה הסתיים.לאחר שהצילום הסתיים, נתוני התמונה מועתקים מהמאגר על המצלמה למאגר מקומי (מערך של בתים) עם קריאה ל-
readImageToBuffer
. אורך המאגר נשלח לאחר מכן למוניטור הסריאל. -
בנה והעלה את הקוד הזה, ובדוק את הפלט במוניטור הסריאל. בכל פעם שתלחץ על כפתור C, תמונה תצולם ותראה את גודל התמונה נשלח למוניטור הסריאל.
Connecting to WiFi.. Connected! Image captured Image read to buffer with length 9224 Image captured Image read to buffer with length 11272
תמונות שונות יהיו בעלות גדלים שונים. הן נדחסות כ-JPEG וגודל קובץ JPEG עבור רזולוציה נתונה תלוי במה שיש בתמונה.
💁 תוכל למצוא את הקוד הזה בתיקיית code-camera/wio-terminal.
😀 הצלחת לצלם תמונות עם ה-Wio Terminal שלך.
אופציונלי - אימות תמונות המצלמה באמצעות כרטיס SD
הדרך הקלה ביותר לראות את התמונות שצולמו על ידי המצלמה היא לכתוב אותן לכרטיס SD ב-Wio Terminal ואז לצפות בהן במחשב שלך. בצע שלב זה אם יש לך כרטיס microSD פנוי ושקע microSD במחשב שלך או מתאם.
ה-Wio Terminal תומך רק בכרטיסי microSD בגודל של עד 16GB. אם יש לך כרטיס SD גדול יותר, הוא לא יעבוד.
משימה - אימות תמונות המצלמה באמצעות כרטיס SD
-
פרמט כרטיס microSD כ-FAT32 או exFAT באמצעות היישומים הרלוונטיים במחשב שלך (Disk Utility ב-macOS, File Explorer ב-Windows, או באמצעות כלי שורת פקודה ב-Linux).
-
הכנס את כרטיס ה-microSD לשקע ממש מתחת למתג ההפעלה. ודא שהוא נכנס עד הסוף עד שהוא נלחץ ונשאר במקום, ייתכן שתצטרך לדחוף אותו באמצעות ציפורן או כלי דק.
-
הוסף את הצהרות ה-include הבאות בראש קובץ
main.cpp
:#include "SD/Seeed_SD.h" #include <Seeed_FS.h>
-
הוסף את הפונקציה הבאה לפני פונקציית
setup
:void setupSDCard() { while (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) { Serial.println("SD Card Error"); } }
פונקציה זו מגדירה את כרטיס ה-SD באמצעות אוטובוס SPI.
-
קרא לפונקציה זו מפונקציית
setup
:setupSDCard();
-
הוסף את הקוד הבא מעל פונקציית
buttonPressed
:int fileNum = 1; void saveToSDCard(byte *buffer, uint32_t length) { char buff[16]; sprintf(buff, "%d.jpg", fileNum); fileNum++; File outFile = SD.open(buff, FILE_WRITE ); outFile.write(buffer, length); outFile.close(); Serial.print("Image written to file "); Serial.println(buff); }
זה מגדיר משתנה גלובלי למספר קבצים. זה משמש לשמות קבצי התמונות כך שניתן לצלם תמונות מרובות עם שמות קבצים עוקבים -
1.jpg
,2.jpg
וכן הלאה.לאחר מכן הוא מגדיר את הפונקציה
saveToSDCard
שלוקחת מאגר נתוני בתים ואת אורך המאגר. שם קובץ נוצר באמצעות מספר הקבצים, ומספר הקבצים מוגדל מוכן לקובץ הבא. הנתונים הבינאריים מהמאגר נכתבים לקובץ. -
קרא לפונקציה
saveToSDCard
מפונקצייתbuttonPressed
. הקריאה צריכה להיות לפני שהמאגר נמחק:Serial.print("Image read to buffer with length "); Serial.println(length); saveToSDCard(buffer, length); delete(buffer);
-
בנה והעלה את הקוד הזה, ובדוק את הפלט במוניטור הסריאל. בכל פעם שתלחץ על כפתור C, תמונה תצולם ותישמר בכרטיס ה-SD.
Connecting to WiFi.. Connected! Image captured Image read to buffer with length 16392 Image written to file 1.jpg Image captured Image read to buffer with length 14344 Image written to file 2.jpg
-
כבה את כרטיס ה-microSD והוצא אותו על ידי לחיצה קלה ושחרור, והוא יקפוץ החוצה. ייתכן שתצטרך להשתמש בכלי דק כדי לעשות זאת. חבר את כרטיס ה-microSD למחשב שלך כדי לצפות בתמונות.
💁 ייתכן שייקח כמה תמונות עד שהאיזון הלבן של המצלמה יתאים את עצמו. תבחינו בכך על סמך הצבע של התמונות שצולמו, הראשונות עשויות להיראות בצבע שגוי. תמיד תוכלו לעקוף זאת על ידי שינוי הקוד כך שיצלם כמה תמונות שמתעלמים מהן בפונקציית
setup
.
כתב ויתור:
מסמך זה תורגם באמצעות שירות תרגום מבוסס בינה מלאכותית Co-op Translator. למרות שאנו שואפים לדיוק, יש לקחת בחשבון שתרגומים אוטומטיים עשויים להכיל שגיאות או אי דיוקים. המסמך המקורי בשפתו המקורית צריך להיחשב כמקור הסמכותי. עבור מידע קריטי, מומלץ להשתמש בתרגום מקצועי על ידי אדם. איננו נושאים באחריות לאי הבנות או לפרשנויות שגויות הנובעות משימוש בתרגום זה.