---
# System prepended metadata

title: Raspberry sample code

---

###### tags: 樹莓派,控制實驗
[TOC]
# Raspberry sample code
Ｗeek1.
1. LED開關、閃爍
2. 讀取按鈕、PWM控制(漸亮、閃爍)

實作1：LED閃爍 實作2：切換RGB

Week2.
1. 電阻控制馬達加減速(ADC)
2. 樹莓派與Arduino通訊控制馬達
3. 樹莓派 or Arduino計時

實作：控制馬達走矩形


## Week_1
### LED開關控制
```python=
import RPi.GPIO as gpio #引用RPi.GPIO函式庫
import time #引用time函式庫

LED_white = 14 #定義LED_white腳位

gpio.setwarnings(False) #關閉程式重複執行警告訊息
gpio.setmode(gpio.BCM)  #腳位定義為BCM
gpio.setup(LED_white, gpio.OUT) #定義為輸出腳位

def led_High_Low():
    gpio.output(LED_white, 1) #設定LED_white腳位為高電位
    time.sleep(1) #延遲1秒
    gpio.output(LED_white, 0) #設定LED_white腳位為低電位
    time.sleep(1)
    print("End.")

led_High_Low()
#GPIO.cleanup()
```

### 按鈕 + LED開關控制
```python=
import RPi.GPIO as gpio #引用RPi.GPIO函式庫
import time #引用time函式庫

LED_white = 14 #定義LED_white腳位
Buttun_0 = 15 #定義Buttun腳位

gpio.setwarnings(False) #關閉程式重複執行警告訊息
gpio.setmode(gpio.BCM)  #腳位定義為BCM
gpio.setup(LED_white, gpio.OUT) #定義為輸出腳位
gpio.setup(Buttun_0, gpio.IN)   #定義為輸入腳位

def Btn_LED(hl):
    gpio.output(LED_white, hl)
    
def Btn_test():
    while True:
        Btn_LED(1-gpio.input(Buttun_0))
        #按鈕按下讀取到的數值為0
        #按鈕放開讀取到的數值為1

Btn_test()
```

### PWM控制LED
```python=
import RPi.GPIO as gpio #引用RPi.GPIO函式庫
import time #引用time函式庫

LED_RGB = 14 #定義LED_white腳位

gpio.setwarnings(False) #關閉程式重複執行警告訊息
gpio.setmode(gpio.BCM)  #腳位定義為BCM
gpio.setup(LED_RGB, gpio.OUT) #定義為輸出腳位

#建立LED_RGB為PWM輸出通道，頻率設定為50
PWM = gpio.PWM(LED_RGB, 50)

#啟動PWM，占空比為0
PWM.start(0) 

def PWM_LED_Control():
    while True:
        dc = 0
        while(dc < 100):
            dc += 5
            PWM.ChangeDutyCycle(dc)#改變PWM占空比
            time.sleep(0.5)
            print("PWM: " + str(dc))
            
PWM_LED_Control()
```

### 控制RGB LED
```python=
import RPi.GPIO as gpio #引用RPi.GPIO函式庫
import time #引用time函式庫

RED_LED_PIN = 2 #定義RED_LED腳位
GREEN_LED_PIN = 3 #定義GREEN_LED腳位
BLUE_LED_PIN = 4 #定義BLUE_LED腳位
PWM_FREQ = 50 #定義PWM頻率

gpio.setwarnings(False) #關閉程式重複執行警告訊息
gpio.setmode(gpio.BCM)  #腳位定義為BCM
gpio.setup(LED_RGB, gpio.OUT) #定義為輸出腳位
gpio.setup(RED_LED_PIN, gpio.OUT)
gpio.setup(BLUE_LED_PIN, gpio.OUT)
gpio.setup(GREEN_LED_PIN, gpio.OUT)

#建立RED_LED_PIN為PWM輸出通道，頻率設定為PWM_FREQ
red_pwm = gpio.PWM(RED_LED_PIN, PWM_FREQ)
red_pwm.start(0)#啟動PWM，占空比為0

#建立BLUE_LED_PIN為PWM輸出通道，頻率設定為PWM_FREQ
blue_pwm = gpio.PWM(BLUE_LED_PIN, PWM_FREQ)
blue_pwm.start(0)#啟動PWM，占空比為0

#建立GREEN_LED_PIN為PWM輸出通道，頻率設定為PWM_FREQ
green_pwm = gpio.PWM(GREEN_LED_PIN, PWM_FREQ)
green_pwm.start(0)#啟動PWM，占空比為0

def setColor(r=0, g=0, b=0):
    red_pwm.ChangeDutyCycle(100-int(r/255*100))
    green_pwm.ChangeDutyCycle(100-int(g/255*100))
    blue_pwm.ChangeDutyCycle(100-int(b/255*100))
    
def RGB_LED_LOOP():
    while True:
        setColor(255, 0, 0) #R
        time.sleep(1)
        setColor(0, 255, 0) #G
        time.sleep(1)
        setColor(0, 0, 255) #B
        time.sleep(1)
        
RGB_LED_LOOP()
```

## Week 2

### 馬達歸零校正
```cpp=
int servoPin = 12; //設定servoPin由12腳位控制
/*此腳位只能控制單一馬達，要校正另一邊，請記得更改腳位*/
void setup(){
    pinMode(servoPin, OUTPUT);//設定12腳位為輸出
}
void loop(){
    digitalWrite(servoPin, HIGH);
    delayMicroseconds(1500);//維持1.5ms
    digitalWrite(servoPin, LOW);
    delay(20);
}
```

### Servo(SG90)
```cpp=
#include <Servo.h>         //載入函式庫
Servo myservo;  // 建立SERVO物件
void setup(){
    myservo.attach(8);  // 設定要將伺服馬達接到哪一個PIN腳
}
void loop(){
    myservo.write(0);  //旋轉到0度，就是一般所說的歸零
  delay(1000);
  myservo.write(90); //旋轉到90度
  delay(1000);
  myservo.write(180); //旋轉到180度
  delay(1000);
  myservo.write(90);
  delay(1000);
}
```

### Motor control in Arduino
```cpp==
#include <Servo.h>         //載入函式庫
#define Pin_Motor_Left 12  //定義左邊馬達腳位
#define Pin_Motor_Right 13 //定義右邊馬達腳位
Servo Motor_Left;          // 建立SERVO物件
Servo Motor_Right;         // 建立SERVO物件
char receive_msg;

void setup() {
  Serial.begin(115200);//設定通訊鮑率
  // 設定要將伺服馬達接到哪一個PIN腳
  Motor_Left.attach(Pin_Motor_Left);    
  Motor_Right.attach(Pin_Motor_Right);
}
void loop() {
    if(Serial.available()) //有訊息進來的時候才去做讀取的動作
    {
        receive_msg = Serial.Read();         //讀取字元
        switch(receive_msg)                  //判斷讀取字元
        {
            case 'X':                        
                ServoControl(1600, 1400);    //馬達後退     
                break;
            case 'A':
                ServoControl(1400, 1400);    //馬達左轉
                break;
            case 'W':
                ServoControl(1400, 1600);    //馬達前進
                break;
            case 'D':
                ServoControl(1600, 1600);    //馬達右轉
                break;
            case 'S':
                ServoControl(1500, 1500);    //馬達停止
                break;  
        }
    }
}
void ServoControl(int speed_L, int speed_R)
{
  Motor_Left.writeMicroseconds(speed_L);
  Motor_Right.writeMicroseconds(speed_R);
}
```

### Motor speed control in Arduino
```cpp=
#include <Servo.h>
#define Pin_Motor_Left 12
#define Pin_Motor_Right 13

Servo Motor_Left;
Servo Motor_Right;
char receive_char;

const uint8_t analogPin = A0;
//#define analogPin A0
int resistance_value;
int servo_speed_Forward;
int servo_speed_Reverse;

void setup() {
    Serial.begin(115200);
    Motor_Left.attach(Pin_Motor_Left);
    Motor_Right.attach(Pin_Motor_Right);
}
void loop() {
    resistance_value = analogRead(analogPin);
    //  Serial.println(resistance_value);
    servo_speed_Forward = map(resistance_value, 0, 255, 1500, 1700);
    servo_speed_Reverse = map(resistance_value, 0, 255, 1500, 1300);
    //  Serial.print(servo_speed_Forward);
    //  Serial.print(",");
    //  Serial.println(servo_speed_Reverse);
    if(Serial.available())
      {
        receive_char = Serial.read();
      }
      switch(receive_char)
      {
        case 'X':
          ServoControl(servo_speed_Forward, servo_speed_Reverse);
          break;
        case 'A':
          ServoControl(servo_speed_Reverse, servo_speed_Reverse);
          break;
        case 'W':
          ServoControl(servo_speed_Reverse, servo_speed_Forward);
          break;
        case 'D':
          ServoControl(servo_speed_Forward, servo_speed_Forward);
          break;
        case 'S':
          ServoControl(1500, 1500);
          break;  
      }
}
void ServoControl(int speed_L, int speed_R)
{
  Motor_Left.writeMicroseconds(speed_L);
  Motor_Right.writeMicroseconds(speed_R);
}
```

### 時間函數-計時
```cpp=
long last_time = 0;
long count = 0;
void setup() {
    Serial.begin(115200);
    last_time = millis();
}
void loop() {
    //判斷目前時間減去上次紀錄時間，是否大於1000ms
    if(millis() - last_time > 1000){ 
        Serial.println(count ++);
        last_time = millis();//更新並儲存時間
    }
}
```

## Week 3
### 超音波控制
```cpp=
#define RPi3 1
#define RPi4 0
#if RPi3
const int trig_1 = 10;
const int trig_2 = 11;
#endif
#if RPi4
const int trig_1 = 8;
const int echo_1 = 9;
const int trig_2 = 10;
const int echo_2 = 11;
#endif
void setup() {
  Serial.begin(115200);
#if RPi4
  pinMode(trig_1, OUTPUT);
  pinMode(echo_1, INPUT);
  pinMode(trig_2, OUTPUT);
  pinMode(echo_2, INPUT);
#endif
}
void loop(){
#if RPi3
  Serial.print(ReadDistance(trig_1));
  Serial.print(",");
  Serial.println(ReadDistance(trig_2));
#endif
#if RPi4
  Serial.print(ReadDistance(trig_1, echo_1));
  Serial.print(",");
  Serial.println(ReadDistance(trig_1, echo_1));
#endif
}
long ReadDistance(int trig_pin) //For Rpi3
{
  pinMode(trig_pin, OUTPUT);
  long distance = 0;
  digitalWrite(trig_pin, LOW);
  delayMicroseconds(2);
  digitalWrite(trig_pin, HIGH);
  delayMicroseconds(10);
  pinMode(trig_pin, INPUT);
  distance = pulseIn(trig_pin, HIGH)/2/29;
  return distance;
}
long ReadDistance(int trig_pin, int echo_pin) //For Rpi4
{
  long distance = 0;
  digitalWrite(trig_pin, LOW);
  delayMicroseconds(2);
  digitalWrite(trig_pin, HIGH);
  delayMicroseconds(10);
  distance = pulseIn(echo_pin, HIGH)/2/29;
  return distance;
}
```

```cpp=
#define Nothing 1
#define PI_Control 0
const int trig = 8;
const int echo = 9;
long Distance = 0;
unsigned int base_speed = 50;
uint8_t Kp = 10;		
unsigned int Sent_Motor = 0;
unsigned int V_Err = 0;
#include <Servo.h>         //載入函式庫
#define Pin_Motor_Left 12  //定義左邊馬達腳位
#define Pin_Motor_Right 13 //定義右邊馬達腳位
Servo Motor_Left;          // 建立SERVO物件
Servo Motor_Right;         // 建立SERVO物件

void setup() {
  Serial.begin(115200);
  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT);
  Motor_Left.attach(Pin_Motor_Left);    
  Motor_Right.attach(Pin_Motor_Right);
}
void loop(){
	Distance = ReadDistance(trig, echo);
#if Nothing
	if ( Distance > 12)
	{	//需前進
		ServoControl(1400, 1600);    //馬達前進
	}
	else if( Distance < 12)
	{	//需後退
		ServoControl(1600, 1400);    //馬達後退
	}
	else
	{	//需停止
		ServoControl(1500, 1500);    //馬達停止
	}
#endif
	//==========================================
#if PI_Control
	if(Distance > 30) 
		Distance = 30;
	V_Err = 12 - Distance;				
	if (V_Err < 0){
		V_Err = V_Err * (-1);
		Sent_Motor = V_Err * Kp + base_speed;

		if (Sent_Motor > 100) 
			Sent_Motor = 100;
		
		Forward_Left(Sent_Motor);
		Backward_Right(Sent_Motor);
	}					
	else if (V_Err > 0 ){
		Sent_Motor = V_Err * Kp + base_speed;
		if (Sent_Motor > 100)
			Sent_Motor = 100;
			
		Backward_Left(Sent_Motor);
		Forward_Right(Sent_Motor);
	}
  else{
      Backward_Left(0);
      Forward_Right(0);
  }
#endif
}
void Forward_Right(int speed_percent)
{
    Motor_Right.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1700));
}
void Forward_Left(int speed_percent)
{
    Motor_Left.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1700));
}

void Backward_Right(int speed_percent)
{
    Motor_Right.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1300));
}
void Backward_Left(int speed_percent)
{
    Motor_Left.writeMicroseconds(map(speed_percent, 0, 100, 1500, 1300));
}

void ServoControl(int speed_L, int speed_R)
{
  Motor_Left.writeMicroseconds(speed_L);
  Motor_Right.writeMicroseconds(speed_R);
}
long ReadDistance(int trig_pin, int echo_pin)
{
  long distance = 0;
  digitalWrite(trig_pin, LOW);
  delayMicroseconds(2);
  digitalWrite(trig_pin, HIGH);
  delayMicroseconds(10);
  distance = pulseIn(echo_pin, HIGH)/2/29;
  return distance;
}
```


## Week_4

### 視訊調整縮放
```python=
#!/usr/bin/env python
# -*- coding: utf8 -*-
import cv2
print("cv2 version: " + cv2.__version__)
import numpy as np

cap = cv2.VideoCapture(0) #開啟鏡頭裝置影像，利用變數cap作為視訊擷取後存放空間
ret, frame = cap.read() #擷取一張影像，存到陣列frame，是否成功存到布林變數ret
#影像初始大小為640x480
#cap.get(3) 確認影像寬度，cap.get(4) 確認影像高度
print(cap.get(3), cap.get(4))

cv2.imshow('image', frame)
cap.set(3, 320) #改變影像寬度
cap.set(4, 240) #改變影像高度
#改變大小可降低後續影像處理資料量
print(cap.get(3), cap.get(4))

while True:
    ret, hframe = cap.read() #擷取影像，存到陣列hframe
    cv2.imshow('half image', hframe) #利用視窗顯示hframe
    #waitKey 函數是用來等待與讀取使用者按下的按鍵，而其參數是等待時間（單位為毫秒），
    #若設定為 0 就表示持續等待至使用者按下按鍵為止，
    #此處設定為字母q，表示等待1秒確認是否按下按鍵，
    #這樣當我們按下任意按鍵之後，就會呼叫
    #cv2.destroyAllWindows 關閉所有 OpenCV 的視窗
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
    #在python opencv 中要補捉鍵盤事件的話可以使用 cv2.waitKey()，
    #它會在給定的時間內監聽鍵盤事件，給定的時間到了
    #cv2.waitKey() 回傳按下按鍵的數字，沒有按鍵按下的話會回傳-1
cap.release() #釋放攝影機
cv2.destroyAllWindows() #關閉所有OpenCV 視窗
```

### 樹梅派 相機 拍照
[ord函數](https://www.runoob.com/python/python-func-ord.html)
[OpenCV 擷取網路攝影機串流影像，處理並寫入影片檔案教學](https://blog.gtwang.org/programming/opencv-webcam-video-capture-and-file-write-tutorial/)
```python=
#!/usr/bin/env python
# -*- coding: utf8 -*-
import cv2
print("cv2 version: " + cv2.__version__)
import RPi.GPIO as GPIO
import time
import numpy as np

GPIO.setmode(GPIO.BCM)
GPIO.setup(17,GPIO.IN)

cap = cv2.VideoCapture(0)
ret, frame = cap.read()
print(cap.get(3), cap.get(4))
picture_num = 0
pre_btn_state = 0
while(True):
    ret, hframe = cap.read()
    cv2.imshow('image', hframe)
    btn_state = GPIO.input(17)
    if (not btn_state) and pre_btn_state:
        time.sleep(0.3)
        print('photograph')
        cv2.imwrite('Output.jpg', hframe) // 輸出圖片(存檔路徑為目前.py路徑底下)
        ''' //
        picture_num = picture_num + 1
        print('photograph')
        path = '/home/pi/Desktop/Control_exp_v_2/' //設定存檔絕對路徑
        cv2.imwrite(path + 'output_' + str(picture_num) + '.jpg', hframe) //
        '''
    pre_btn_state = btn_state
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()
```


[[Python+OpenCV] 圖片縮放 resize](https://shengyu7697.github.io/python-opencv-resize/)
### 影像圖片放大縮小
```python=
#!/usr/bin/env python
# -*- coding: utf8 -*-
import cv2
print("cv2 version: " + cv2.__version__)
import numpy as np

img = cv2.imread('123.jpg')
print(img.shape)
rows, cols, channel = img.shape
'''
圖像的大小可以通過其shape屬性來獲取
第一個元素表示圖像的高度，
第二個表示圖像的寬度，
第三個表示圖元的通道數。
'''

dst = cv2.resize(img, (2*cols, rows), interpolation = cv2.INTER_CUBIC)
dst1 = cv2.resize(img, (cols, 2*rows), interpolation = cv2.INTER_LINEAR)
dst2 = cv2.resize(img, (int(cols/2), int(rows/2)), interpolation = cv2.INTER_AREA)

cv2.imshow('Original', img)
cv2.imshow('INTER_CUBIC', dst)
cv2.imshow('INTER_LINEAR', dst1)
cv2.imshow('INTER_AREA', dst2)
cv2.waitKey(0)
cv2.destroyAllWindows()

```

[Pyplot tutorial](https://matplotlib.org/stable/tutorials/introductory/pyplot.html)

[python數位圖像處理](https://www.cnblogs.com/denny402/p/5122594.html)

[[Python+OpenCV] 影像二值化 Image Thresholding](https://shengyu7697.github.io/python-opencv-threshold/)
### 圖片二值化
```python=
#!/usr/bin/env python
# -*- coding: utf8 -*-
import cv2
print("cv2 version: " + cv2.__version__)
import numpy as np
import matplotlib
matplotlib.use('Tkagg', warn = False, force = True)
from matplotlib import pyplot as plt

img = cv2.imread('gradient.png')

ret, thresh1 = cv2.threshold(img, 127, 155, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img, 127, 5, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)

titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range:
    plt.subplot(2,3,i+1), plt.imshow(images[i])
    '''
    使用 subplot 可以在一幅圖中生成多个子圖，其參數為：
    plt.subplot(numrows, numcols, fignum)
    '''
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()
```