# 自學筆記 <!-- <font color="#f00">訂閱</font> 以上為改變字體顏色的語法--> <!-- Put the link to this slide here so people can follow --> >## 1. OpenCV學習 >## 2. NODE-RED學習 >## 3. MQTTx學習 >## 4. STM32學習 >## 5. STM32與NODE-RED結合 >## 6. STM32與MQTT結合 <!-- --- --> ## OpenCV學習 ++學習參考影片++ {%youtube xjrykYpaBBM %} - ### ==**程式練習位置在桌面'我的書審(大學)'**== --- ### 讀取照片 ```cpp= import cv2 img= cv2.imread('colorcolor.jpg') #讀取照片 img = cv2.resize(img,(0,0),fx=0.5,fy=0.5) #改變一開始的圖片大小使其為原本的一半fx=0.5是指為原來的0.5倍 img = cv2.resize(img,(300,300)) #resize可以指定(300X300)改變一開始的圖片大小 cv2.imshow('img',img) #顯示叫IMG的圖片 cv2.waitKey(0) #等待時間(毫秒) ``` --- ### 讀取影片 ```cpp= import cv2 cap = cv2.VideoCapture('thumb.mp4')#讀取影片 while True: ret, fram= cap.read() #read會回傳兩個數一是有沒有成功取得下一張圖片(1:成功0:失敗)=ret;二是下一張圖片=fram fram= cv2.resize(fram,(0,0),fx=0.4,fy=0.4) if ret: cv2.imshow('video',fram) else: break if cv2.waitKey(1) == ord('q'):#等待時間按下Q結束影片 break ``` --- ### 讀取鏡頭影像 ```cpp= import cv2 cap = cv2.VideoCapture(0) #讀取影片若括號打0則使用電腦預設鏡頭 while True: ret, fram= cap.read()#read會回傳兩個數一是有沒有成功取得下一張圖片(1:成功0:失敗)=ret;二是下一張圖片=fram fram= cv2.resize(fram,(0,0),fx=0.4,fy=0.4) if ret: cv2.imshow('video',fram) else: break if cv2.waitKey(1) == ord('q'):#等待時間按下Q結束影片 break ``` --- ### 認識圖片(舉例colorcolor.jpg) ![](https://i.imgur.com/pVOBDbX.jpg =40%x) ```cpp= img = cv2.imread('colorcolor.jpg') print(img.shape) #取得此img.shape陣列的大小 結果:(1015, 700, 3) 1015代表列有1015個[] 700代表每列裡包含700個[] 3代表700個[]裡的3個值 分別代表 B G R (0~255) ``` --- ### 構建一張自己的彩色圖片 ```cpp= 因為要使用陣列所以須引入import numpy as np img = np.empty((300,300,3),np.uint8) #創建一陣列指定長寬並且顏色種類 np.uint8為正整數大小範圍2的8次方256 for row in range(300): for col in range(300): img[row][col]= [random.randint(0,255),random.randint(0,255),random.randint(0,255)] cv2.imshow('img',img) cv2.waitKey(0) ``` --- ### 裁切圖片 ```cpp= img = cv2.imread('colorcolor.jpg') newimg = img[:150,200:400]#切成列0~150 行200~400 cv2.imshow('img',img) cv2.imshow('newimg',newimg) cv2.waitKey(0) ``` --- ### 影像處理常用函式 ```cpp= img= cv2.imread('colorcolor.jpg') img = cv2.resize(img,(0,0),fx=0.5,fy=0.5) img_1 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #將BGR圖片變成灰階 img_2 = cv2.GaussianBlur(img,(3,3),0) #高斯模糊 工用去除雜訊 圖檔,合(基數),標準差 img_3 = cv2.Canny(img,100,400) #圖片輪廓描邊 (圖片,最低門檻值,最高門檻值) 解釋:若兩齡近像素點顏色落差太大就會形成輪廓 Canny將點評分若低於150則視為邊緣若高於200視為邊緣 kernel=np.ones((10, 10), np.uint8) #新建一陣列來當作我們dilate的合(kermel) img_4 = cv2.dilate(img_3,kernel,iterations=1) #將突變輪廓變粗 (圖片,kernel合(二維陣列),變粗次數) kernel1=np.ones((3, 3), np.uint8) #新建一陣列來當作我們erode的合(kermel) img_5 = cv2.erode(img_4,kernel1,iterations=2) #將突變輪廓變系 (圖片,kernel合(二維陣列),變細次數) # cv2.imshow('img',img) # cv2.imshow('img_1',img_1) # cv2.imshow('img_2',img_2) cv2.imshow('img_3',img_3) cv2.imshow('img_4',img_4) cv2.imshow('img_5',img_5) cv2.waitKey(0) ``` --- ### 圖片上畫畫 ```cpp= img = np.zeros((600,600,3),np.uint8) #新建多維振烈zreo代表都為0(黑色) (列,行,色種) cv2.line(img,(0,0),(img.shape[1],img.shape[0]),(255,0,0),1) #畫直線 (檔案,線的點,線的點,顏色(BGR),粗度) #img.shap[1]可讀取圖片列的長度[0]則是寬 cv2.rectangle(img,(0,0),(300,300),(255,0,0),cv2.FILLED) #畫方形 (檔案,方形的點,方形的點,顏色(BGR),粗度) #img.FILLED 將圖形內填滿 cv2.circle(img,(300,400),30,(255,0,0),1) #畫圓形 (檔案,圓心,半徑,顏色(BGR),粗度) cv2.putText(img,'hello',(100,500),cv2.FONT_HERSHEY_SIMPLEX,2,(255,0,0),2) #寫字 (檔案,起始點,字體,文字大小,顏色(BGR),粗度)NOTE:沒辦法支援中文 cv2.imshow('img',img) cv2.waitKey(0) ``` --- ### 偵測顏色 ```cpp= img = cv2.imread('XiWinnie.jpg') img = cv2.resize(img,(0,0),fx=0.5,fy=0.5) def empty(v):#下面定義的empty所回傳的值 pass cv2.namedWindow('TrackBar')#建立一新視窗 cv2.resizeWindow('TrackBar', 640, 320)#視窗大小設定 cv2.createTrackbar('Hue Min', 'TrackBar', 0, 179, empty)#建立一控制條範圍是0~179 色調 cv2.createTrackbar('Hue Max', 'TrackBar', 179, 179, empty)#建立一控制條範圍是0~179 色調 cv2.createTrackbar('Sat Min', 'TrackBar', 0, 255, empty)#建立一控制條範圍是0~179 飽和度 cv2.createTrackbar('Sat Max', 'TrackBar', 255, 255, empty)#建立一控制條範圍是0~179 飽和度 cv2.createTrackbar('Val Min', 'TrackBar', 0, 255, empty)#建立一控制條範圍是0~179 亮度 cv2.createTrackbar('Val Max', 'TrackBar', 255, 255, empty)#建立一控制條範圍是0~179 亮度 hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)#將圖片轉換成HSV(色調,飽和度,亮度) while True: #將控制條的值印出來 h_min = cv2.getTrackbarPos('Hue Min', 'TrackBar') h_max = cv2.getTrackbarPos('Hue Max', 'TrackBar') s_min = cv2.getTrackbarPos('Sat Min', 'TrackBar') s_max = cv2.getTrackbarPos('Sat Max', 'TrackBar') v_min = cv2.getTrackbarPos('Val Min', 'TrackBar') v_max = cv2.getTrackbarPos('Val Max', 'TrackBar') print(h_min, h_max, s_min, s_max, v_min, v_max) #過濾顏色 lower = np.array([h_min,s_min,v_min]) upper = np.array([h_max,s_max,v_max]) mask = cv2.inRange(hsv,lower,upper)#過濾顏色(檔案,h、s、v的最小值,h、s、v的最大值# ) cv2.waitKey(1) result=cv2.bitwise_and(img,img,mask=mask)#將圖片1與圖片2的每個bit做and運算 (圖片1,圖片2,遮罩) 將白色地方蓋回原圖 cv2.imshow('img',img) cv2.imshow('hsv',hsv) cv2.imshow('mask',mask)#白色為保留 cv2.imshow('result',result)#白色為保留 ``` --- ### 輪廓檢測,形狀辨識 ![](https://i.imgur.com/tGCV6Ow.png) ```cpp= img = cv2.imread('shape.jpg') imgContour=img.copy()#複製一張 img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#將圖片轉為灰階因為判斷形狀並不需要顏色 canny = cv2.Canny(img,150,200)#圖片輪廓描邊 (圖片,最低門檻值,最高門檻值) 解釋:若兩齡近像素點顏色落差太大就會形成輪廓 Canny將點評分若低於150則視為邊緣若高於200視為邊緣 contours, hierarchy = cv2.findContours(canny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)#偵測輪廓(圖片,使用模式(內、外、內外,輪廓),壓縮輪廓點) EXTERNAL:外輪廓 for cnt in contours: cv2.drawContours(imgContour,cnt,-1,(255,0,0),4)#(畫的位置,要畫得輪廓,第幾個輪廓;都要(-1),顏色,粗度) area=cv2.contourArea(cnt)#輪廓面積 if area > 500: peri=cv2.arcLength(cnt,True)#輪廓邊長 vertices=cv2.approxPolyDP(cnt,peri * 0.02,True) corners=len(vertices)#顯示輪廓的角數 x,y,w,h = cv2.boundingRect(vertices)#將每個多邊形用方形圈起來 cv2.rectangle(imgContour,(x,y),(x+w,y+h),(0,255,0),4)#畫方形 (檔案,方形的點,方形的點,顏色(BGR),粗度) #img.FILLED 將圖形內填滿 #判斷形狀 if corners ==3: cv2.putText(imgContour,'triangle',(x,y-5),cv2.FONT_HERSHEY_SIMPLEX,1,(255,0,0),1)#寫字 (檔案,起始點,字體,文字大小,顏色(BGR),粗度)NOTE:沒辦法支援中文 elif corners==4: cv2.putText(imgContour, 'rectangle', (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0),1) # 寫字 (檔案,起始點,字體,文字大小,顏色(BGR),粗度)NOTE:沒辦法支援中文 elif corners==5: cv2.putText(imgContour, 'pentagon', (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0),1) # 寫字 (檔案,起始點,字體,文字大小,顏色(BGR),粗度)NOTE:沒辦法支援中文 elif corners>=6: cv2.putText(imgContour, 'circle', (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0),1) # 寫字 (檔案,起始點,字體,文字大小,顏色(BGR),粗度)NOTE:沒辦法支援中文 cv2.imshow('img',img) cv2.imshow('canny',canny) cv2.imshow('imgContour',imgContour) cv2.waitKey(0) ``` --- ### 人臉辨識 ![](https://i.imgur.com/Z4XXhRO.png =40%x) ```cpp= img = cv2.imread('qq.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faceCascade = cv2.CascadeClassifier('face_detect.xml')#載入訓練好的模型 (路徑) faceRect = faceCascade.detectMultiScale(gray, 1.1, 5)#辨識人臉(圖片,下一次偵測縮圖倍率,被框到幾次為正確) 流程:偵測 縮小圖片 偵測 .... for (x, y, w, h) in faceRect: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow('img', img) cv2.waitKey(0) ``` --- # NODE-RED學習 > 1.node-red工具 > 2.node-red上網抓資料 學習網址:https://tutorials.webduino.io/zh-tw/docs/socket/useful/node-red.html --- ### username : admin ### password : raspberry --- ### 節點介紹 | | <font color="#f00">inject</font> | <font color="#f00">debug</font> | | :--: | :--: | :--: | | <font color="#090">功能</font> | 輸入信號 | 輸出訊號 | | <font color="#090">類似於</font> | 訊號產生器 | 示波器 | >### ++:100:範例++ *設定輸入節點屬性:* ![](https://i.imgur.com/v1LnNRj.png =50%x) *結果:* ![](https://i.imgur.com/MJ4llTO.png =55%x) :star: node(節點)與node之間傳輸的資料我們稱為 =='payload'== --- ### node-red 工具 - dashboard **: 網頁按鈕、顯示、圖表、輸入都在裡面** *設計* ![](https://i.imgur.com/g8CGmtg.png =80%x) :bulb: dashboard每個節點都需有分組 ![](https://i.imgur.com/DFBfM2h.png =50%x) 部屬完後點右上角![](https://i.imgur.com/DEYpxP6.png =7%x)進入 ![](https://i.imgur.com/m5Hm2Cg.png =80%x) --- ### node-red 上網抓資料 參考網址:https://tutorials.webduino.io/zh-tw/docs/socket/useful/exchange-node-red.html > 1.找需要的網頁並開啟原始碼 2.設計NODE-RED 3.找到要的資料(第幾行?) 4.輸出:100: --- #### 1.找需要的資料(舉例:https://rate.bot.com.tw/xrt?Lang=zh-TW) ![](https://i.imgur.com/maSTBHn.png =80%x) *原始碼:* 閱讀網頁的原始碼我們可以得知現金的買入與賣出名稱為'rate-content-cash text-right print_hide' ![](https://i.imgur.com/KKErxUi.png =80%x) --- #### 2.設計NODE-RED ![](https://i.imgur.com/NRQS7vP.png =80%x) *<font color="#eb0">編輯'http in'節點</font>:* <font color="#090">URL</font>輸入要<font color="#f00">載入</font>資料的網址(舉例:https://rate.bot.com.tw/xrt?Lang=zh-TW) ![](https://i.imgur.com/1Xysu34.png =80%x) *<font color="#eb0">編輯'html'節點:</font>:* <font color="#090">選取項</font>要輸入要是<font color="#f00">篩選</font>的資料名稱(rate-content-cash text-right print_hide) ![](https://i.imgur.com/AtbL96a.png =80%x) --- #### 3.找到要的資料(第幾行?) *<font color="#eb0">deploy後並觸發後,到debug找尋需要的資料在哪一行:</font>* ![](https://i.imgur.com/LJ7NLon.png =80%x) *<font color="#eb0">編輯'function'節點:</font>* 將正確的那一個資訊<font color="#f00">抓出並打包</font>送出 ```cpp= var massage = msg.payload[15];//輸入需要傳送行數 var date = new Date(); var h = date.getHours(); var m = date.getMinutes(); var s = date.getSeconds(); if (h < 10) { h = '0' + h; } if (m < 10) { m = '0' + m; } if (s < 10) { s = '0' + s; } msg.payload = '(' + h + ':' + m + ':' + s + ')\n' + '日幣匯率:' + massage; return msg; ``` #### 4.輸出:100: ![](https://i.imgur.com/xWm7liT.png =50%x) --- - # MQTTX 學習 :bulb:MQTT概念:可以想像在 <font color="#f00">虛擬網路上的一個公布欄</font>供大家貼上自己的訊息資料,那張貼的訊息需自行命名 **user/大組別/小組別(EX:rayqop/room/light)** ,須注意命名盡量與他人不會重複以免訊息錯誤。 *新增頁面:* ![](https://i.imgur.com/3nlWuUJ.png =80%x) <!-- H<font color="#f00">e</font>llo, World 。用於字的顏色--> **服務器位置使用'test.mosquitto.org'** *連線頁面:* ![](https://i.imgur.com/2S0Su3J.png =80%x) **QOS是傳輸檔案的重要分級,數字越大越重** | | <font color="#f00">訂閱</font> | <font color="#f00">發布</font> | | :--: | :--: | :--: | | <font color="#090">意思</font> | 輸入資料 | 輸出資料 | | <font color="#090">類似於</font> | 到公佈欄抓資料回來| 張貼資料到公布欄 | --- # ESP32學習 > 1.IO例題 > 2.LCD模組 > 3.ESP32連上WIFI > 4.抓取網路資料至ESP32 設定額外開發管理員: https://github.com/espressif/arduino-esp32/releases/download/2.0.4/package_esp32_dev_index.json ![](https://i.imgur.com/v8VmdBh.png =80%x) 腳位認識: https://zerotech.club/esp32-gpio/ - ### ==**程式練習位置在桌面'我的書審(大學)'**== PIO 34,35,36,39:Input only(不能作為輸出腳位) GPIO 0,6,7,8,9,10,11:系統用,勿使用(淺灰色標示) GPIO 18:重開機 1. DHT11不可以用GPIO 5、26 2. GPIO 12, 2燒錄時不可接任何裝置,請空接,燒錄完成後,再接回,否則會上傳失敗。 3.WiFi啟動後,2,4,12,13,14,15,25,26,27僅能數位讀取,不可類比 ### IO例題: 簡單控制RGB LED模組。 ```cpp= const byte Red = 4; const byte Green = 16; const byte Blue = 17; //const 確保往後程式不會修改到 char data; //擷取資料 void setup() { pinMode(Red,OUTPUT); pinMode(Green,OUTPUT); pinMode(Blue,OUTPUT); Serial.begin(9600); } void loop() { if(Serial.available()){ data = Serial.read(); if(data == '1'){ digitalWrite(Blue,LOW); digitalWrite(Red,HIGH); delay(500); digitalWrite(Red,LOW); digitalWrite(Green,HIGH); delay(500); digitalWrite(Green,LOW); digitalWrite(Blue,HIGH); delay(500); } else { digitalWrite(Blue,LOW); digitalWrite(Green,LOW); digitalWrite(Red,LOW); } } } ``` *結果:* ![](https://i.imgur.com/WEC2G4v.jpg =60%x) --- ### LCD模組: 使用電腦發送訊號啟動LED,並結合LED狀態顯示當下Red、Green、Blue的狀態。 ```cpp= #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,16,2); //LCD接腳SDA SCL const byte Red = 4; const byte Green = 16; const byte Blue = 17; //const 確保往後程式不會修改到 char data; //擷取資料 void setup() { lcd.init(); // initialize the lcd // Print a message to the LCD. lcd.setCursor(0,0);//第一字元開始位置 lcd.print("waiting....."); lcd.backlight(); pinMode(Red,OUTPUT); pinMode(Green,OUTPUT); pinMode(Blue,OUTPUT); Serial.begin(9600); } void loop() { if(Serial.available()){ data = Serial.read(); if(data == '1'){ lcd.setCursor(0,0);//第一字元開始位置 lcd.clear(); Serial.println("紅"); digitalWrite(Blue,LOW); digitalWrite(Red,HIGH); lcd.setCursor(0,0);//第一字元開始位置 lcd.print("Red open"); delay(600); Serial.println("綠"); digitalWrite(Red,LOW); digitalWrite(Green,HIGH); lcd.setCursor(0,0);//第一字元開始位置 lcd.print("Green open"); delay(600); Serial.println("藍"); digitalWrite(Green,LOW); digitalWrite(Blue,HIGH); lcd.setCursor(0,0);//第一字元開始位置 lcd.print("Blue open"); delay(600); Serial.println("空"); digitalWrite(Blue,LOW); digitalWrite(Green,LOW); digitalWrite(Red,LOW); lcd.setCursor(0,0);//第一字元開始位置 lcd.print("waiting....."); } else { digitalWrite(Blue,LOW); digitalWrite(Green,LOW); digitalWrite(Red,LOW); lcd.setCursor(0,0);//第一字元開始位置 lcd.print("waiting....."); } } } ``` *結果:* ![](https://i.imgur.com/Ki2tnEk.jpg =60%x) --- ### ESP32連上WIFI: 學習網址:https://crazymaker.com.tw/esp32-connect-to-wifi-network/ | <font color="#f00">語法</font> | <font color="#f00">WIFI模式</font> | <font color="#f00">功能</font> | |:------------------------------:| :-----------------: | :---------------------------------------------------------------------------------: | | <font color="#090">WiFi.mode(WIFI_AP)</font> | Access Point (AP) | ESP32可以讓其他設備透過wifi接入(就像家裡的wifi基地台,可供手機連線)。 | | <font color="#090">WiFi.mode(WIFI_STA)</font> | Station(STA) | 無線終端模式,也就是讓ESP32可以連接上其他的熱點(就像手機一樣,可以連上家裡wifi)。 | | <font color="#090">WiFi.mode(WIFI_AP_STA)</font> | AP+STA | 將ESP32設置成兩個模式並存。 | <font color="#090">WiFi.mode(WIFI_OFF)</font>;|OFF|關閉wifi| 檔案資料夾:WIFI_ESP32。 ```cpp= #include<WiFi.h> const char ssid[]="Ray's Galaxy A52s 5G"; //修改為你家的WiFi網路名稱 const char pwd[]="xadr7782"; //修改為你家的WiFi密碼 void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); //設置WiFi模式 WiFi.begin(ssid,pwd); Serial.print("WiFi connecting"); //當WiFi連線時會回傳WL_CONNECTED,因此跳出迴圈時代表已成功連線 while(WiFi.status()!=WL_CONNECTED){ Serial.print("."); delay(500); } Serial.println(""); Serial.print("IP位址:"); Serial.println(WiFi.localIP()); //讀取IP位址 Serial.print("WiFi RSSI:"); Serial.println(WiFi.RSSI()); //讀取WiFi強度 } void loop() { } ``` *結果:* ![](https://i.imgur.com/jZRFJm1.png) --- # ESP32與NODE-RED結合 利用NODE-RED的Dashboard做出一個有按鈕的頁面,透過按紐我們可以啟動LED並在'LED'狀態欄裡顯示當下狀態。 *Dashboard頁面:* ![](https://i.imgur.com/IMdrbGb.png =80%x) *按下後 STM32與LCD:* ![](https://i.imgur.com/HQ3rHld.jpg =60%x) --- ### 抓取網路資料至ESP32: 學習參考網址:https://youyouyou.pixnet.net/blog/post/120275911-%E7%AC%AC%E5%8D%81%E7%AF%87-esp32-wifi%E7%B6%B2%E8%B7%AF%E9%80%A3%E7%B7%9A%28pm2.5%E9%A1%AF%E7%A4%BA%E5%99%A8%29 ---