# 微電腦應用實習
Author 曾鏹毅
圖片來源: [raspberrypi.org](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/)

:::info
## 所需軟體
:::
1. [mobaxterm下載](https://mobaxterm.mobatek.net/download-home-edition.html)
2. [VNC](https://www.realvnc.com/en/connect/download/viewer/)
3. [Notepad++](https://notepad-plus-plus.org/downloads/)
4. [Visual Studio Code](https://code.visualstudio.com/)
## 燒錄raspberry pi os
{%youtube a9Hs0nqD7LQ%}
### *使用 raspberry pi imager*


#### 選擇樹梅派型號、OS版本以及micro SD卡(這裡以pi4、piOS(64bit Bookworm)與16G sd卡Demo



#### 按下 NEXT

#### 接著選擇 編輯設置後如下圖

#### 設定主機名稱、使用者名稱、密碼、WiFi的ssid名稱、WiFi連線密碼、WiFi國家
#### 接著切換到第2頁籤 SERVICES,並將SSH服務勾選(如下圖),並按下保存離開

#### 接著按下是(如下圖)


#### 開始下載並寫入micro sd卡


---
## visual studio code 下載與安裝
{%youtube c-jJO9A68wM%}
## [線上python程式練習(Colab;使用學校google帳戶登入)](https://colab.research.google.com/drive/1EQjJolXH6azjcpJOZaqJuY0xKirH56lr?usp=sharing)
## [3D建模軟體onshape](https://www.onshape.com/)
## 樹梅派4b正面配置
##### 圖片來源 [BeginnersGuide-4thEd-Eng_v2.pdf](https://magpi.raspberrypi.com/books/beginners-guide-4th-ed)

## GPIO接腳 [raspberrypy.org](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#gpio-and-the-40-pin-header)

## GPIO各接腳 pull up 與 pull down 狀態

## pull up 與 pull down(拉高、拉低電阻)
* ## [Multisim Live 模擬](https://www.multisim.com/content/FJ79pZWTZPdWfX6in6XmL4/pull_up_down-resistor/)
* 
* 啟動上拉電阻 python 語法
```
import RPi.GPIO as gpio
gpio.setmode(gpio.BCM)
button=7
gpio.setup(button,gpio.IN,pull_up_down=gpio.PUD_UP)
```
* 啟動下拉電阻 python 語法
```
import RPi.GPIO as gpio
gpio.setmode(gpio.BCM)
button=26
gpio.setup(button,gpio.IN,pull_up_down=gpio.PUD_DOWN)
```
## BCM模式與Board模式差異
* BOARD 選項是指定在電路版上接脚的號碼
* python寫法
```
import RPi.GPIO as gpio
gpio.setmode(gpio.BOARD) #設定為BOARD模式
```
* BCM 選項是指定GPIO後面的號碼
* python寫法
```
import RPi.GPIO as gpio
gpio.setmode(gpio.BCM) #設定為BCM模式
```
## Python 相關語法
* 異常處理 try…except…finally…
```
try:
....
....
except KeyboardInterrupt:
break
finally:
...
...
```
# 實驗
## 單顆LED on off
http://bit.ly/3tB2okQ
```
import RPi.GPIO as gpio
import time
gpio.setmode(gpio.BCM)
gpio.setup(17,gpio.OUT)
gpio.output(17,1)
time.sleep(1)
gpio.output(17,0)
gpio.cleanup()
```

## 單顆LED 間隔1秒閃爍
http://bit.ly/3UE7EAk
```
import RPi.GPIO as io
import time
io.setmode(io.BCM)
io.setup(17,io.OUT)
while True:
try:
io.output(17,1)
time.sleep(1)
io.output(17,0)
time.sleep(1)
except KeyboardInterrupt:
break
pass
io.cleanup()
```
## 作業 單方向流水燈(8顆LED)
提示:使用串列list簡化程式
http://bit.ly/3AgD9rP

---
## PWM測試
從暗到最亮,單向循環
http://bit.ly/3O5T16q
```
import RPi.GPIO as gpio
import time
led=17
gpio.setmode(gpio.BCM)
gpio.setup(led,gpio.OUT)
led=gpio.PWM(led,100) #設定pwm頻率100Hz
while True:
try:
for a in range(0,101,2):
led.start(a) # 啟動PWM DutyCycle,dutycycle從0~100,間隔2
time.sleep(0.2)
except KeyboardInterrupt:
break
pass
gpio.cleanup()
```
## 單按鈕測試(pull down)
http://bit.ly/3tyE0Ao
按下按鈕時,終端機顯示 Button Pressed!!

```
# pull down 測試
import RPi.GPIO as gpio
import time
button1=26
gpio.setmode(gpio.BCM)
gpio.setup(button1,gpio.IN,pull_up_down=gpio.PUD_DOWN)
while True:
try:
input_state=gpio.input(button1)
if (input_state==True):
print('Button Pressed!!')
time.sleep(0.3)
except KeyboardInterrupt:
break
pass
gpio.cleanup()
```
## 三顆按鈕測試(pull down)
##### 開關彈跳問題未解

{%youtube VKR4m-85HVU %}
```
# pull down 測試
import RPi.GPIO as gpio
import time
button1=13
button2=19
button3=26
gpio.setmode(gpio.BCM)
gpio.setup(button1,gpio.IN,pull_up_down=gpio.PUD_DOWN)
gpio.setup(button2,gpio.IN,pull_up_down=gpio.PUD_DOWN)
gpio.setup(button3,gpio.IN,pull_up_down=gpio.PUD_DOWN)
while True:
try:
in_state1=gpio.input(button1)
in_state2=gpio.input(button2)
in_state3=gpio.input(button3)
if in_state1==True:
print('Button1 Pressed!')
elif in_state2==True:
print('Button2 Pressed!')
elif in_state3==True:
print('Button3 Pressed!')
time.sleep(0.3)
except KeyboardInterrupt:
break
pass
gpio.cleanup()
```
## 簡易Webcam相機應用-使用fswebcam
安裝fswebcam
```
sudo apt-get install fswebcam
```
{%youtube xO0SJ8wduG0 %}
fswebcam用法可下指令查詢
```
fswebcam --help
```

最簡易的使用方式: fswebcam+ +檔名 即可透過webcam拍攝一張照片,並存放於當下目錄中
```
fswebcam test.jpg
```
{%youtube 5bMK6H2rCvE %}
---
## 練習:使用按鈕開關拍照--呼叫外部程式(fswebcam)

提示: python呼叫外部程式
```
import os
os.system('指令')
```
{%youtube 6u6ET7vAqKk %}
```
import RPi.GPIO as gpio
import time
import os
button=13
gpio.setmode(gpio.BCM)
gpio.setup(button,gpio.IN,pull_up_down=gpio.PUD_DOWN)
while True:
button_state=gpio.input(button)
try:
if button_state==True:
print('button pressed')
os.system('fswebcam a.jpg')
os.system('gpicview a.jpg')
time.sleep(0.3)
except KeyboardInterrupt:
break
pass
gpio.cleanup()
```
---
## 同上電路功能,增加line notify通知
[Line Notify網站](https://notify-bot.line.me/zh_TW/)
```
import RPi.GPIO as gpio
import requests, os #line notify用
import time
import os
bt1=13
gpio.setmode(gpio.BCM)
gpio.setup(bt1,gpio.IN,pull_up_down=gpio.PUD_DOWN) #啟動內建下拉電阻
def lineNotify(token, msg, picURI): #定義linenotify函式
url = "https://notify-api.line.me/api/notify"
headers = {"Authorization": "Bearer " + token}
payload = {'message': msg}
files = {'imageFile': open(picURI, 'rb')}
r = requests.post(url, headers = headers, params = payload, files = files)
return r.status_code
while True:
button_state=gpio.input(bt1)
try:
if button_state==True:
print('BT_pressed!')
os.system('fswebcam a.jpg')
token='填入自己建立的line notify token' #line notify token
msg = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) #lineNotify中傳送的文字訊息,這裡擷取系統時間來發送
picURI = 'a.jpg' #發送圖片的檔名
lineNotify(token, msg, picURI) #呼叫linenotify函式
except KeyboardInterrupt:
break
pass
gpio.cleanup()
\
```
# 建立python虛擬環境
因為python程式常有版本和不同套件的需求,因此須針對版本建立不同的python環境,以便於開發
## 建立虛擬環境(使用venv)
首先須有python3-venv套件
安裝
```
sudo apt install python3-venv
```
建立虛擬環境
```
python -m venv 環境名稱
```
啟動環境
```
source 環境名稱/bin/activate
```
退出環境
```
deactivate
```
# 安裝ubuntu 20.04 server
燒錄img



燒錄完成後,將micro sd放回raspberry pi並開機
第一次開機預設帳密皆為ubuntu(登入後會要求變更)
檢視網卡狀態
```
ip a
```

啟動有線網卡
```
sudo ip link set eth0 up
```
修改有線網卡ip( /etc/netplan 下的yaml檔)

修改此yaml檔內容

完成ip設定
```
sudo netplan apply
```
修改無線網卡部分
修改同上yaml檔案

變更後重啟,wifi即可使用
安裝Desktop(檔案很大,需要一些時間)
```
sudo apt-get install ubuntu-desktop
```

# 安裝Node-RED [參考資料](https://nodered.org/docs/getting-started/raspberrypi)
* 藉由官網提供的scrip來安裝
```
sudo apt install build-essential git curl
```
```
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
```
This script will:
* remove the existing version of Node-RED if present.
* if it detects Node.js is already installed, it will ensure it is at least v14. If less than v14 it will stop and let the user decide whether to stay with Node-RED version 1 - or upgrade Nodejs to a more recent LTS version. If nothing is found it will install the Node.js 16 LTS release using the NodeSource package.
* install the latest version of Node-RED using npm.
* optionally install a collection of useful Pi-specific nodes.
* setup Node-RED to run as a service and provide a set of commands to work with the service.


* 啟動Node-RED
```
node-red-start
```
* 樹梅派啟動時,自動啟動node-red
```
sudo systemctl enable nodered.service
```
---
# 安裝OpenCV
*
{%youtube sSX6zt8kxS4 %}
## 使用script安裝
OS:32bit
[GitHUB](https://github.com/Qengineering/Install-OpenCV-Raspberry-Pi-32-bits/)
OS:64bit
[GitHUB](https://github.com/Qengineering/Install-OpenCV-Raspberry-Pi-64-bits/)
## RaspOS Buster+opencv4.5.5+mediapipe 影像檔下載[img](https://hhvstncedu-my.sharepoint.com/:u:/g/personal/tseng_ms_hhvs_tn_edu_tw/ETSnLpP_T11HueyM9mjYNQMBgB9biqPyEeDg4RCPb-l23A?e=iQBido)
## 快速安裝 opencv與 FFmpeg
## [參考來源](https://pypi.org/project/mediapipe-rpi4/)
```
sudo apt install ffmpeg python3-opencv python3-pip
```
安裝完後,opencv為4.6版
另一種安裝方式
```
pip install opencv-contrib-python
```
## 安裝相依套件 (libdc1394-22 libopenexr23這兩個找不到)
```
sudo apt install libxcb-shm0 libcdio-paranoia-dev libsdl2-2.0-0 libxv1 libtheora0 libva-drm2 libva-x11-2 libvdpau1 libharfbuzz0b libbluray2 libatlas-base-dev libhdf5-103 libgtk-3-0 libdc1394-22 libopenexr23
```
## For rpi4
```
sudo pip3 install mediapipe-rpi4
```
上面指令若無法安裝;改用;[參考](https://developers.googleblog.com/2023/08/mediapipe-for-raspberry-pi-and-ios.html)
```
python -m pip install mediapipe
```
安裝後,opencv會更新至4.9版(2024.02.16.)
## **解決 EXTERNALLY-MANAGED問題**
[參考](https://www.yaolong.net/article/pip-externally-managed-environment/)
1.直接把 EXTERNALLY-MANAGED這個檔案刪除(變更檔名)
檔案位置 /usr/lib/python3.x/EXTERNALLY-MANAGED (x為python版本)
另兩種方式請參閱上述參考連結
2.使用python虛擬環境(參閱上面)
# 使用RC522及DHT22
* 簡易環境溫溼度紀錄推播及mifare感應運用
* 使用函式庫
* [RC522](https://github.com/ondryaso/pi-rc522)
* [Adafruin CircuitPython Libraries](https://learn.adafruit.com/dht-humidity-sensing-on-raspberry-pi-with-gdocs-logging/python-setup)
* 下圖GPIO12接紅色LED:無效卡片顯示燈號
* GPIO16接 Relay,控制磁力鎖
* 樹梅派GPIO與RC522連接如下表
|RFID-RC522 | Raspberry pi |
| -------- | -------- |
| 3.3V | 3.3V |
| RST | GPIO25 |
| GND | GND |
| IRQ | GPIO24 |
| MISO | GPIO9 |
| MOSI | GPIO10 |
| SCK | GPIO11 |
| SDA | GPIO8 |
* 
* 溫溼度紀錄與line Notify
* [google sheet紀錄](https://docs.google.com/spreadsheets/d/1RB332Ojkv6eNKpdKVBhbPMRCWochZPRbe5JXnOL9nBk/edit?usp=sharing)
```
import time
import board
import adafruit_dht
#======for ifttt=======
import urllib, json
import requests, time
from urllib import request
ifttturl="https://maker.ifttt.com/trigger/****" #填寫申請的ifttt URL
#===================
#=========for google sheet=============
import gspread
from oauth2client.service_account import ServiceAccountCredentials
auth_json_path='****.json' #填寫申請的憑證檔檔名
gss_scopes=['https://spreadsheets.google.com/feeds']
credentials=ServiceAccountCredentials.from_json_keyfile_name(auth_json_path,gss_scopes)
gss_client=gspread.authorize(credentials)
spreadsheet_key='**************' #填寫key值
sheet=gss_client.open_by_key(spreadsheet_key).sheet1
#listtile=["日期時間","溫度","濕度"]
#sheet.append_row(listtile)
#============================================
# Initial the dht device, with data pin connected to:
dhtDevice = adafruit_dht.DHT22(board.D18)
# you can pass DHT22 use_pulseio=False if you wouldn't like to use pulseio.
# This may be necessary on a Linux single board computer like the Raspberry Pi,
# but it will not work in CircuitPython.
# dhtDevice = adafruit_dht.DHT22(board.D18, use_pulseio=False)
list1=["溫度:","濕度:"]
while True:
try:
# Print the values to the serial port
temperature_c = dhtDevice.temperature
temperature_f = temperature_c * (9 / 5) + 32
humidity = dhtDevice.humidity
# print(
# "Temp: {:.1f} F / {:.1f} C Humidity: {}% ".format(temperature_f, temperature_c, humidity)
# )
localtime=time.ctime() #取得目前系統時間
data={'value1':localtime ,'value2':list1[0]+str(temperature_c) ,'value3':list1[1]+str(humidity)} # for ifttt use(呼叫line notify)
requests.post(ifttturl, json=data) # for ifttt use
listdata=[localtime,temperature_c,humidity]
sheet.append_row(listdata)
except RuntimeError as error:
# Errors happen fairly often, DHT's are hard to read, just keep going
print(error.args[0])
time.sleep(2.0)
continue
except Exception as error:
dhtDevice.exit()
raise error
time.sleep(1800.0) #每隔30分鐘發送一次line notify
```
* Mifare感應(RC522)
```
#!/usr/bin/env python
import signal
import time
import sys
import RPi.GPIO as GPIO
#======for ifttt=======
import urllib, json
import requests, time
from urllib import request
ifttturl="https://maker.ifttt.com/trigger/*****" #填寫申請的ifttt URL
#===================
from pirc522 import RFID
run = True
rdr = RFID()
util = rdr.util()
util.debug = True
Gled=36 #(Board mode pin)
Rled=32
def GBlink(speed):
GPIO.setup(Gled,GPIO.OUT)
GPIO.output(Gled,True)
time.sleep(speed)
GPIO.output(Gled,False)
time.sleep(speed)
def RBlink(speed):
GPIO.setup(Rled,GPIO.OUT)
GPIO.output(Rled,True)
time.sleep(speed)
GPIO.output(Rled,False)
time.sleep(speed)
def end_read(signal,frame):
global run
print("\nCtrl+C captured, ending read.")
run = False
rdr.cleanup()
sys.exit()
signal.signal(signal.SIGINT, end_read)
print("Starting")
while run:
rdr.wait_for_tag()
(error, data) = rdr.request()
if not error:
print("\nDetected: " + format(data, "02x"))
(error, uid) = rdr.anticoll()
if not error:
print("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3]))
if (uid[0]==** and uid[1]==**): #uid[0]與uid[1]請依據所使用的mifare card
# print("Welcom!")
GBlink(2)
localtime=time.ctime() #取得目前系統時間
data={'value1':localtime ,'value2':"Welcom! John" } #line notify通知資料
requests.post(ifttturl, json=data) # ifttt : line notify發送進入機房人員與時間通知
else:
print("Permission Deny!")
RBlink(0.1)
RBlink(0.1)
RBlink(0.1)
localtime=time.ctime() #取得目前系統時間
data={'value1':localtime ,'value2':"Warning! Jane Doe try to open door!!! " } #line notify通知資料
requests.post(ifttturl, json=data) # ifttt : line notify發送異常刷卡時間通知
time.sleep(0.5)
```
## 113-1 上課資料
### [上課用程式碼](https://github.com/chiangyih/113-1)
### [mediapipe程式碼](https://github.com/chiangyih/113-1-SoftwarePractice)
### [mediapipe操作](https://hackmd.io/@johnt/Bki9xeRl1x)
:::success
### 基本 IO Lab
#### 參閱範例01-07
:::
---
:::info
### 使用opencv拍照
1.先建立opencv用虛擬環境
```
python -m venv py-opencv
```
2.切換至虛擬環境
```
source py-opencv/bin/activate
```
3.進入虛擬環境後,類似下圖

4.安裝opencv套件
```
pip install opencv-contrib-python
```


5.程式碼請存放於py-opencv資料夾下

:::