# 生活科技筆記 ## 1. OnShape 3D 建模 OnShape 是一個免費線上 3D 建模網站。 ![Screenshot 2025-03-19 082401](https://hackmd.io/_uploads/BJVm19w31x.png) 梅開板手(一邊是**十二邊形**,一邊是**六邊形**) --- ### 作業:機器人小車建模 其實不難,但我遇到兩個問題。 * 因為我視力比較不好需要放大,而放大之後有些功能就會一時找不到,要用搜尋的方法找到它。 ==解決方法:搜尋也不是一件麻煩的事,沒關係,以後不要找不到就以為沒有就好。== * **不知道怎麼改圓的半徑**。在老師的影片裡,看起來像點完圓心及圓上一點後,點一下旁邊的半徑數字再輸入半徑。但是我發現如果這麼做,會變成再畫一個圓。 ==解決方法:後面的影片有說滑鼠游標指到數字上後直接輸入就好。== 成果圖: ![Screenshot 2025-03-19 083859](https://hackmd.io/_uploads/HJc4zcDhyg.png) --- **心得:原來 3D 建模比我想像中容易。** ## 2. TinkerCAD 基礎電路設計 TinkerCAD 也是 3D 繪圖網站,它還有電路模擬器。 成果圖片: ![Screenshot 2025-03-05 093507](https://hackmd.io/_uploads/HJ3hc7rj1g.png) 發亮的 LED ![螢幕擷取畫面 2025-03-09 165129](https://hackmd.io/_uploads/B1Bp-kjjyg.png) 滑動開關 1 ![螢幕擷取畫面 2025-03-09 165138](https://hackmd.io/_uploads/rJP1GJsiJg.png) 滑動開關 2 --- ### 作業:滑動開關,左邊串聯兩個紅色 LED ,右邊並聯兩個綠色 LED 我一開始的電路是這樣,經過計算: 電池為 9V ,一個 LED 要 2V 和 0.02A 。 * 左邊串聯部分,電阻設定為 5V / 0.02A = 250 歐姆 * 右邊並聯部分,電阻設定為 7V / 0.04A = 175 歐姆。 ![螢幕擷取畫面 2025-03-09 171950](https://hackmd.io/_uploads/rJkE71joJg.png) 結果根本無法開始模擬,**初始化之後就直接結束了。** 我一直 Debug , *把連接其中一個綠色 LED 的線路拿掉* 或是 *把連接紅色 LED 的線路拿掉* 都成功,但是**全部一起**就是不行。 我嘗試到網路上找資料發現找不到。 最後問 ChatGPT ,他一開始以為我是在用並聯電池,接下來以為中間那個是 IC 。 他建議把紅色 LED 的電阻提高,於是我隨便選了個大於 250 的數字,是 300 ,接著按下 “ ***開始模擬*** ”,成功了。但是完全不知道原因。 疑似是 TinkerCAD 的 Bug ,老師也這麼認為。 --- ### 第一次段考前的生活科技課,我忘記是自修,沒帶書,做的實驗 當 235 <= 紅色 LED 電阻 <= 255 時,模擬無法開始。 改成**兩個電池並聯**或是**以 Arduino 供電**,兩個電阻重算後,也成功。 --- **心得:原來模擬器和實際電路無法完全相同,有時候模擬器會出現實際上根本不會遇到的 bug 。這時候不需要太執著理論數值,就像科學實驗會有誤差一樣。** ## 3. Arduino 在 TinkerCAD 的模擬器中也可以寫 Arduino 程式。 成果: ![Screenshot 2025-03-12 092549](https://hackmd.io/_uploads/H1dafv0s1g.png) 閃爍的 LED --- ### 平交道號誌 ![螢幕擷取畫面 2025-04-29 203505](https://hackmd.io/_uploads/ryuhPS0Jlx.png) [Arduino 平交道號誌影片連結](https://youtu.be/flvlnSG0mFk) 程式碼: ``` cpp= void setup() { // LEDs are connected to pin12 and pin13. pinMode(13, OUTPUT); pinMode(12, OUTPUT); } const int t = 500; // delay time (ms) void loop() { digitalWrite(13, HIGH); digitalWrite(12, LOW); delay(t); digitalWrite(13, LOW); digitalWrite(12, HIGH); delay(t); } ``` --- ### 跑馬燈 ![螢幕擷取畫面 2025-04-29 203709](https://hackmd.io/_uploads/HyNMuBAklx.png) [Arduino 跑馬燈影片連結](https://youtu.be/mlkKAL95aEs) 程式碼: ```cpp= void setup() { for (int i = 9 ; i < 14 ; i ++) pinMode(i, OUTPUT); } const int t = 200; void loop() { for (int i = 9 ; i < 14 ; i ++) { digitalWrite(i, HIGH); for (int j = 9 ; j < 14 ; j ++) { if (j != i ) digitalWrite(j, LOW); } delay(t); } for (int i = 12 ; i > 9 ; i --) { digitalWrite(i, HIGH); for (int j = 9 ; j < 14 ; j ++) { if (j != i ) digitalWrite(j, LOW); } delay(t); } } ``` --- ### 呼吸燈 ![螢幕擷取畫面 2025-04-29 203834](https://hackmd.io/_uploads/ByuL_H0yle.png) [Arduino 呼吸燈影片連結](https://youtu.be/9VS17z4Mfwk) 程式碼: ```cpp= void setup() { pinMode(9, OUTPUT); } const int t = 10; void loop() { for (int i = 0 ; i < 255 ; i ++) { analogWrite(9, i); delay(t); } for (int i = 255 ; i >= 0 ; i --) { analogWrite(9, i); delay(t); } } ``` --- ### 觸摸感測器 這是電容式觸碰感測器。TinkerCAD 沒有所以在模擬器中以按鈕代替。 ![螢幕擷取畫面 2025-07-01 161335](https://hackmd.io/_uploads/BJwpuz-Ble.png) [Arduino 觸摸感測器影片連結](https://youtu.be/RprlG2-t8RY) 程式碼: ```cpp= void setup() { pinMode(3, OUTPUT); pinMode(13, INPUT); } void loop() { bool touched = digitalRead(13); if (touched) digitalWrite(3, HIGH); else digitalWrite(3, LOW); } ``` --- ### 可變電阻調整 LED 亮度 ![螢幕擷取畫面 2025-04-05 162824](https://hackmd.io/_uploads/ryLAKv06Jg.png) 我一開始的程式是這樣的 ```cpp= void setup() { pinMode(A0, INPUT); pinMode(10, OUTPUT); Serial.begin(9600); } void loop() { int pot = analogRead(A0); analogWrite(10, pot*255/1023); Serial.print(pot); Serial.print(" "); Serial.print(pot*255/1023); Serial.print("\n"); } ``` 讀入從 0 到 1023 ,為避免整數除法使明暗變化不夠平順,先乘 255 再除 1023 ,得到從 0 到 255 的整數。 執行發現 **LED 忽亮忽暗,不是預期中越往右轉越亮**。 我覺得很奇怪,懷疑又是 TinkerCAD 的 Bug。 打開 Serial Monitor 之後,發現傳入 `analogWrite()` 的數值出現 ***負數*** 。 我一開始就算過 1023 * 255 不會超過 2^31^ ,覺得不會溢位卻出現負數。 問了 ChatGPT 才發現原來 ==Arduino 的 `int` 跟平常電腦上 C / C++ 的 `int` 範圍不一樣==。 在發現 Arduino UNO 系列有最新版 R4 後,發現事情不是一般電腦和 MCU 的區別這麼單純。 * 電腦 C++ 及新版 Arduino Uno R4 的 `int` 為 32 bit ,最大可達 2^31^ - 1 。 * Arduino Uno R3 的 `int` 是 16 bit ,最大只能到 2^15^ - 1 = 32767 。 在實體或 TinkerCAD 模擬的 Arduino Uno R3 執行程式時,若 pot * 255 > 32767 ,就會溢位變成負數導致執行結果無法預期。 實際上,把 0 ~ 1023 轉換成 0 ~ 255 有幾種方法。 1. 直接放進 `analogWrite()` 它會自動對 256 取餘。但不是我預期的功能。 2. 使用 `constrain(pot, 0, 255)` ,小於 0 會轉換成 0,大於 255 會轉換成 255。也不是我預期的樣子。 3. 發現 1023 約為 255 的 4 倍,於是使用 `pot / 4` 轉換。 4. 通用的方法,使用 `map(pot, 0, 1023, 0, 255)` ,可將任意範圍透過線性映射轉換成另一個範圍。 還有一個小問題,連寫數個 `Serial.print()` 在 `loop()` 裡面看起來就有些冗長,於是我把這些 `print` 包成一個函式。 最終程式: ```cpp= void setup() { pinMode(A0, INPUT); pinMode(10, OUTPUT); Serial.begin(9600); } void log(int a, int b) { Serial.print(a); Serial.print(" "); Serial.println(b); } void loop() { int pot = analogRead(A0); analogWrite(10, pot/4); log(pot, pot/4); } ``` [成果影片連結](https://youtu.be/1W2N-pGaMEY) **心得:原來在不同的平台,同樣的型別名也會有不一樣的範圍,Arduino Uno R3 的處理器是 8 bit , R4 的處理器則是 32 bit 。** **這次經驗也讓我更了解用輸出 Debug 的實用性。** --- ### 光敏電阻 ![螢幕擷取畫面 2025-07-01 160616](https://hackmd.io/_uploads/HknbDGWrel.png) [Arduino 光敏電阻影片連結](https://youtu.be/7qWIPNYvUXc) 程式碼 ```cpp= void setup() { pinMode(A0, INPUT); pinMode(3, OUTPUT); Serial.begin(9600); } void loop() { digitalWrite(3, analogRead(A0) > 800); } ``` ### 自主學習區 #### a. 關於腳位 D0 和 D1 D0 和 D1 是 Arduino 的序列埠通訊腳位,MCU 透過這兩個腳位與 USB 轉序列埠晶片連接。 故若在這兩個腳位接上其他裝置,可能干擾程式上傳等任何與 USB 通訊相關的程序。 >[!Note] 以下內容為上傳學習歷程後補 Uno R4 的 USB 介面為原生支援,D0 和 D1 可用作一般腳位和 UART 介面。 --- #### b. 隨機數 ```cpp= void setup() { Serial.begin(9600); randomSeed(analogRead(0)); } void loop() { n = random(1, 11); Serial.println(n); delay(200); } ``` `analogRead()` 裡可以直接寫 0,也代表 A0 。 A0 為未接腳時數值會因雜訊而亂跳,適合作為亂數種子。 1 和 6 分別為最小值和最大值,包含最小值但不包含最大值。 如果沒有寫最小值,亂數範圍就是 `0 ~ 最大值 - 1` 。 --- #### c. `pulseIn()` 有些感測器不是回傳固定電壓,是回傳一個脈衝訊號,此時就要用這個函式。語法如下。 ```cpp= unsigned long in = pulseIn(pin, HIGH, timeout); ``` `pin` 為要讀取的腳位,可以是任何腳位。 第二個參數可以是 `HIGH` 或 `LOW` ,為要讀取的脈衝。 第三個參數可有可無,是最長等待時間,單位為微秒。 --- #### d. `millis()` 和 `delay()` 的差異 `delay()` 會讓程式整個卡在進入 `delay()` 前一刻的狀態,不會讀取感測器的數值也不會改變任何腳位的輸出。 這會導致遺漏感測器訊息,只能一次做一件事。 `millis()` 則是回傳從啟動到現在經過的毫秒數,型別為 `unsigned long` 。 使用 `if (millis() - prevMillis >= t)` 可以達到**等待 t 毫秒** 的效果,不會讓程式卡住,可以邊做其他事情。 --- #### e. Uno R4 Arduino Uno R4 於 2023 年上市,有 DAC 等 R3 沒有的功能,WiFi 版有 WiFi 和藍芽。 PWM 是用占空比模擬連續電壓,DAC 是真的輸出連續電壓,Uno R4 的 DAC 腳位為 A0 。 Uno R4 WiFi 有 WiFi 模組,可連線到 Arduino IoT Cloud 或是任何線上 API 。也可以作為 Webserver 讓其他裝置連線遠端控制。 Uno R4 WiFi 還有一個 12 * 8 的 LED 矩陣,可以顯示文字和圖案。 ## 4. 輪型機器人(RP2040) 不是用 Arduino 有以下原因: 1. Arduino 反應沒有 RP2040 快。Arduino Uno R3 的 CPU 是 16 MHz,R4 是 48 MHz。RP2040 是 133 MHz。 2. Arduino 控制馬達要接擴充板比較麻煩。 老師已經寫好了大部分的程式。 重啟指令如下。 ```cpp= void resetFunc() { // restart watchdog_reboot(0, 0, 0); // 第一個 0 是重啟後程式開始的位置。 // 第二個 0 是重啟後堆疊的初始位置。 // 第三個 0 是從執行到重啟的毫秒數。 } ``` ### a. 直流馬達 每個馬達都各自透過 H 橋電路接到 A 、 B 兩個腳位。 ![H_bridge](https://hackmd.io/_uploads/HkVOiGbHxg.svg) 圖片來源:維基百科。由 Cyril BUTTAY - 自己的作品, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=854051 在馬達運轉時,將S1和S3閉合或將S2和S4閉合,即短接馬達兩端,則馬達因反電流被快速剎停,若將H橋的全部開關斷開,則馬達自由停轉 ```cpp= void drive(int powerL, int powerR) { // left and right motor control // powerL: Left motor direction and power. // powerR: Right motor direction and power. powerL = constrain(powerL, -255, 255); // -255<= powerL <= 255 powerR = constrain(powerR, -255, 255); // -255<= powerR <= 255 if (powerL > 0) { analogWrite(pinM1A, powerL); analogWrite(pinM1B, 0); } else { analogWrite(pinM1A, 0); analogWrite(pinM1B, -powerL); } if (powerR > 0) { analogWrite(pinM2A, powerR); analogWrite(pinM2B, 0); } else { analogWrite(pinM2A, 0); analogWrite(pinM2B, -powerR); } } void coast() { // stop the car and coast analogWrite(pinM1A, 255); analogWrite(pinM1B, 255); analogWrite(pinM2A, 255); analogWrite(pinM2B, 255); delay(1000); } void brake() { // stop the car and brake analogWrite(pinM1A, 0); analogWrite(pinM1B, 0); analogWrite(pinM2A, 0); analogWrite(pinM2B, 0); delay(500); } ``` ### b. 超音波感測器 原理:超音波反射。 太近、太遠、角度太斜、物體吸音都會導致量不到。 ```cpp= float checkDistance() { // detection distance // Use ultrasonic to detect the distance of obstacles in centimeters. digitalWrite(pinTrig, LOW); delayMicroseconds(2); // 2 微秒,等於 0.002毫秒 digitalWrite(pinTrig, HIGH); // 發射超音波 delayMicroseconds(10); digitalWrite(pinTrig, LOW); // Measure the response from the HC-SR04P Echo Pin int duration = pulseIn(pinEcho, HIGH); // Determine distance from duration // Use 343 meters per second as speed of sound float distance = duration * 0.034 / 2; delay(20); // Pause to prevent frequent reading 避免被之前的超音波干擾 return distance; } ``` ### c. 紅外線感測器 原理:反射紅外線 ```cpp= int checkColor() { // detection black and white // Use digital signals from infrared reflective sensor to detect black and white // Returns 0 for black and 1 for white. int IR_D = digitalRead(pinIR_D); // 0:white 1:black 因為反射光越多電阻越小,數值越小 delay(1); // Pause to prevent noise caused by frequent reads. return !IR_D; // Invert, 0:black 1:white } ``` #### 循線 並非真正的循跡,僅是根據看到的顏色判斷左轉或右轉。 若直接寫一邊是 180 一邊是 0,會不斷搖晃。 經過測試發現一邊是 150 另一邊 110 很穩。 ```cpp= void track() { if (checkColor()) drive(110, 150); else drive(150, 110); } ``` #### 找第 n 條黑線 我最開始的程式是這樣: ```cpp= void findLine(int n) { int curr = 0; while (curr < n) { drive(150, 150); if (checkColor() == 0) curr ++; } brake(); } ``` 發現機器人在第一條黑線就停下,原因是程式執行非常快,看到第一條黑線的不到一毫秒內 curr 就可以累加到 n 導致停下。 解決方法是在偵測到黑線後繼續前進直到偵測到白色,再將 curr 加一。 ```cpp= void findLine(int n) { int curr = 0; while (curr < n) { drive(150, 150); if (checkColor() == 0) { while (!checkColor()); curr ++; } } brake(); } ``` ### d. 伺服馬達 只能轉 0° ~ 180° ,用於精準控制角度。 ![爪子](https://hackmd.io/_uploads/SJD-pf-Sgl.jpg) ```cpp= void inPosition() { // Servo motor initial positioning handL.write(180); handR.write(0); delay(1000); } void closeHands() { for (int i = 0; i <= 170; i++) { handL.write(180 - i); handR.write(i); delay(7); } } void openHands() { for (int i = 0; i <= 170; i++) { handL.write(i + 10); handR.write(170 - i); delay(7); } } void leftHook() { handL.write(0); delay(1000); handL.write(180); delay(1000); } void rightHook() { handR.write(180); delay(1000); handR.write(0); delay(1000); } ``` 如果 `.write(angle)` 後面沒有 `delay()` 就直接 write 下一個角度,會看到馬達不斷在約 30 度的範圍內反覆轉向。 ### 實作測驗 ![螢幕擷取畫面 2025-07-01 163545](https://hackmd.io/_uploads/H18lAM-Hxx.png) 任務:循跡到A,閃開A後繼續循跡到B,並使用爪子打倒B,循跡到C,並使用爪子抱住C,循跡回終點並停車並放開C。 試了超多次不是 A 那裡不穩定就是看不到 B。 發現 A 那邊不穩定是因為大量使用 `delay` 控制走的距離,後來用找黑線的方法就穩定了。 看不到 B 是因為角度太斜,需要把 B 放外面一點。 [成果影片](https://youtu.be/2aku2yZSsgM) ## 5. 心得總結 我非常喜歡這學期的生活科技課,尤其是 Arduino 的部分。 其實我很早就知道 Arduino 了只是沒有寫程式,從上個學期我就非常期待學到 Arduino 。 下學期的自主學習計畫就是 Arduino ,使用 Uno R4 WiFi 製作環境監測系統,整合溫溼度感測器和 PM2.5 粉塵感測器。