Arduino 的OO寫作方式

範例程式
https://github.com/MarcoYang21/stepMotor/tree/master

//stepMotor.ino
// 此版本是用Arduino Uno R4 WiFi撰寫
// 用物件的寫法來控制步進馬達
// 這個範例只是轉動33度,不間斷往反運動。
#include "StepMotor.h"  // 引入自定義的步進馬達控制頭文件

int rotateTime = 100;    // 旋轉運動時間,單位毫秒
float targetAngle = 33;  // 目標旋轉角度

const int PUL = 11;  // 步進腳位
const int DIR = 12;  // 正反轉控制腳位
const int EN = 13;   // 致能腳位

StepMotor myMotor(DIR, EN, PUL);  // 創建名為 myMotor 的 StepMotor 物件並初始化引腳

void setup() {
  // 執行初始化
  pinMode(PUL, OUTPUT);     // 設定步進腳位為輸出模式
  pinMode(DIR, OUTPUT);     // 設定正反轉控制腳位為輸出模式
  pinMode(EN, OUTPUT);      // 設定致能腳位為輸出模式
  myMotor.enable();         // 最初啟用電機
  Serial.begin(115200);     // 開啟串列通信
  Serial.println("Ready");  // 顯示訊息,表示程式已就緒
}

void loop() {
  myMotor.run();  // 啟動步進馬達運動

  if (myMotor.isFinished()) {
    // 旋轉後到設定位置後,才執行的程式碼
    delay(200);                               // 延遲 200 毫秒
    myMotor.reverseRotation();                // 反轉旋轉方向
    myMotor.rotate(targetAngle, rotateTime);  // 以指定角度和時間旋轉
  }
  // 其它需要一直被執行的工作
}
\\stepMotor.h
#ifndef StepMotor_h
#define StepMotor_h

#include <Arduino.h>  // 引入 Arduino 標準庫

class StepMotor {
public:
  StepMotor(int dirPin, int enaPin, int stpPin);  // 類別建構函數,用於初始化步進馬達的引腳

  void setDirection(int dir);                              // 設定步進馬達的運動方向
  void enable();                                           // 啟用步進馬達
  void disable();                                          // 停用步進馬達
  void rotate(float degrees, unsigned long rotationTime);  // 控制步進馬達旋轉到指定角度和時間
  void reverseRotation();                                  // 反轉步進馬達的運動方向
  bool isFinished();                                       // 檢查步進馬達是否已完成運動
  void run();                                              // 開始步進馬達的運動

private:
  int directionPin;       // 步進馬達的方向控制引腳
  int enablePin;          // 步進馬達的啟用/停用控制引腳
  int stepPin;            // 步進馬達的步進引腳
  int stepsPerRev;        // 一個完整旋轉(360度)所需的步數
  int targetSteps;        // 目標步數
  int currentStep;        // 目前的步數
  int stepDelay;          // 步進間的時間延遲
  int lastStepTime;       // 最後一次輸出步進脈衝的時間
  int rotationDirection;  // 1 表示正向旋轉,-1 表示反向旋轉
  bool pulseDirection;    // 控制步進脈衝方向
  bool isRunning;         // 步進馬達是否正在運行
};

#endif
//stepMotor.cpp
#include "StepMotor.h"

#include <Arduino.h>
StepMotor::StepMotor(int dirPin, int enaPin, int stpPin) {
  directionPin = dirPin;  // 設定方向控制腳的引腳
  enablePin = enaPin;     // 設定啟用/停用控制腳的引腳
  stepPin = stpPin;       // 設定步進腳的引腳
  isRunning = false;      // 初始化為非運行狀態
  rotationDirection = 1;  // 初始為正向旋轉
  stepsPerRev = 1600;     // 一個完整旋轉(360度)所需的步數,看微步器的設定隨之改變
  pulseDirection = true;  // 起始脈衝方向為真(正向)

  pinMode(directionPin, OUTPUT);  // 設定方向控制腳為輸出模式
  pinMode(enablePin, OUTPUT);     // 設定啟用/停用控制腳為輸出模式
  pinMode(stepPin, OUTPUT);       // 設定步進腳為輸出模式
}

void StepMotor::setDirection(int dir) {
  digitalWrite(directionPin, dir);             // 設定方向控制腳的電位高低
  rotationDirection = (dir == HIGH) ? 1 : -1;  // 根據電位高低設定旋轉方向
}

void StepMotor::enable() {
  digitalWrite(enablePin, LOW);  // 啟用馬達
}

void StepMotor::disable() {
  digitalWrite(enablePin, HIGH);  // 停用馬達
}

void StepMotor::rotate(float degrees, unsigned long rotationTime) {
  targetSteps = int((degrees / 360.0) * stepsPerRev);                  // 計算所需的步數
  stepDelay = static_cast<double>(rotationTime) / targetSteps * 1000;  // 計算每步之間的時間延遲
  currentStep = 0;                                                     // 初始化當前步數
  isRunning = true;                                                    // 設定為運行狀態
  enable();                                                            // 啟用馬達
}

void StepMotor::reverseRotation() {
  rotationDirection = -rotationDirection;                             // 反轉旋轉方向
  digitalWrite(directionPin, (rotationDirection == 1) ? HIGH : LOW);  // 根據反轉方向設定方向控制腳
}

bool StepMotor::isFinished() {
  return !isRunning;  // 檢查是否完成運行
}

void StepMotor::run() {
  if (isRunning) {
    unsigned long currentTime = micros();  // 取得當前時間(微秒)
    if (currentStep > targetSteps) {
      isRunning = false;  // 如果已完成所有步數,則設定為非運行狀態
    } else {
      if (currentTime - lastStepTime >= stepDelay) {
        digitalWrite(enablePin, LOW);           // 啟用馬達
        digitalWrite(stepPin, pulseDirection);  // 輸出步進脈衝
        pulseDirection = !pulseDirection;       // 切換脈衝方向
        currentStep++;                          // 增加當前步數
        lastStepTime = currentTime;             // 更新最後一次輸出脈衝的時間
      }
    }
  }
}

Arduino R4 Wifi 定時中斷及設定14位元的ADC

Arduino Uno R3的 TimerOne不能用在R4上,所以上網找了一下,要換個方式做,寫起來有點小麻煩。

#include "FspTimer.h"  // 引入FspTimer庫,這個庫可能用於處理定時器

int sensorValue = 0;  // 創建一個整數變數sensorValue,用於儲存感應器讀取的數值
bool flag = 0;  // 創建一個布林變數flag,用於標記是否已讀取了感應器數值
FspTimer ADC_30mS_timer;  // 創建一個FspTimer類的實例ADC_30mS_timer,用於處理定時器
uint64_t start_time = 0;  // 創建一個64位整數變數start_time,用於儲存程式開始運行的時間

// 定義一個用於定時器回調的方法
void timer_callback(timer_callback_args_t __attribute((unused)) *p_args) {
  sensorValue = analogRead(A0);  // 讀取A0引腳的模擬數值,並存儲在sensorValue中
  flag = 1;  // 設置flag為1,表示已經讀取了感應器數值
}

// 初始化定時器
bool beginTimer(float rate) {
  uint8_t timer_type = GPT_TIMER;  // 指定定時器類型為GPT_TIMER
  int8_t tindex = FspTimer::get_available_timer(timer_type);  // 獲取可用的定時器索引
  if (tindex < 0) {
    tindex = FspTimer::get_available_timer(timer_type, true);  // 如果沒有可用的定時器,嘗試強制獲取
  }
  if (tindex < 0) {
    return false;  // 如果無法獲得可用的定時器,返回false
  }

  FspTimer::force_use_of_pwm_reserved_timer();  // 強制使用PWM保留的定時器

  if (!ADC_30mS_timer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, rate, 0.0f, timer_callback)) {
    return false;  // 如果無法初始化定時器,返回false
  }

  if (!ADC_30mS_timer.setup_overflow_irq()) {
    return false;  // 如果無法設置溢出中斷,返回false
  }

  if (!ADC_30mS_timer.open()) {
    return false;  // 如果無法打開定時器,返回false
  }

  if (!ADC_30mS_timer.start()) {
    return false;  // 如果無法啟動定時器,返回false
  }
  return true;  // 初始化成功,返回true
}

void setup() {
  analogReadResolution(14);  // 設置模擬數值讀取的位數為14位
  Serial.begin(115200);  // 初始化串口通信,波特率為115200
  beginTimer(33);  // 初始化定時器,設置間隔為33毫秒
  start_time = millis();  // 獲取當前時間,用於記錄程式開始運行的時間
  Serial.println("ready");  // 透過串口輸出"ready"消息
}

void loop() {
  if (flag) {  // 如果flag為1(表示已經讀取了感應器數值)
    Serial.println(sensorValue);  // 透過串口輸出感應器數值
    flag = 0;  // 重置flag為0,等待下一次讀取
  }
}

參考來源
https://www.pschatzmann.ch/home/2023/07/01/under-the-hood-arduino-uno-r4-timers/

在Visual Studio Code設定Arduino開發環境

安裝步驟
1.安裝Arduino IDE (版本要至少要1.6之後,但是1.8.7不能用)
2.安裝VS Code
3.安裝繁體中文的語言包
4.安裝MS出的Arduino套件
5.設定Arduino執行檔路徑

在VS code 先安裝繁體中文語言包
再安裝MS出的Arduino套件
進到設定畫面,準備設定Arduino路徑
設定Arduino路徑
環境版本

參考資料
使用微軟Visual Studio Code編寫Arduino程式(一)

使用ESP32 / ESP8266 發送Line 訊息

2024/03/03 更新 找到問題了,之前SSID與password是傳值,後來是改用傳址的方式傳入到WiFi函式庫中。所以改為傳址的方式後,就可以正常使用了。

2024/02/23更新
目前新版IDE+Library的WiFi的組合,我自已試起來也是無法進行WiFi連線的動作,之後有時間再來找問題。

開發環境如下
Arduino IDE 2.3.2
Line Notify函式庫 3.0.6
NodeMCU-32S 開發板

#include <WiFi.h>  //這裡是ESP32 WIFI函式庫
#include <WiFiClient.h>
#include <TridentTD_LineNotify.h>
#define LINE_TOKEN "你的token放在這"

// Set WiFi credentials
const char* ssid     =  "你的無線網路SSID";
const char* password =  "無線網路的密碼";

WiFiClient client; //網路連線物件

void setup(){
  Serial.begin(115200);  // 設定速率
// 進行無線網路連接
  WiFi.mode(WIFI_STA); 
  WiFi.begin(ssid, password);

//等待無線網路連接
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(100);
    Serial.print(".");
  }
  LINE.setToken(LINE_TOKEN); //設定Line Token權杖

  LINE.notify("這裡的文字會出現在Line上面"); //發送訊息到Line上面
}

void loop() {
}

參考資料
[Day23] Esp32 + LINE
Line Notify服務首頁
申請Line Token教學

在Arduino IDE中進行ESP8266 開發的環境設定

單純來看來就是三個步驟可以完成開發環境設定。

  1. 安裝Arduino IDE
  2. 新增開發板管理員網址
  3. 下載開發板套件

額外的開發板管理員網址
http://arduino.esp8266.com/stable/package_esp8266com_index.json

參考資料
Arduino 官網下載位置
Arduino 介紹 & 安裝
如何在Arduino IDE中,新增ESP8266系列開發板

在Arduino IDE中進行ESP32 開發的環境設定

新手開發,所以用基本開發環境就好,所以就直接使用Arduino IDE就好,單純好設定。

單純來看來就是三個步驟可以完成開發環境設定。

  1. 安裝Arduino IDE
  2. 新增開發板管理員網址
  3. 下載開發板套件

參考資料
Arduino 官網下載位置
Arduino 介紹 & 安裝
如何在Arduino IDE中,新增ESP32系列開發板

額外的開發板管理員網址

ssif.com/dl/package_esp32_index.json

計算Arduino 運行時間的方式

//使用時間檢查方式,來進行有週期性的工作。
int period = 50; //執行週期millisecond
unsigned long time_now = 0; //初始化時間
void setup()
{
   //初始化的工作
}
void loop()
{
    if((unsigned long)(millis() - time_now) > period) //檢查時間是否到期
    { 
        time_now = millis(); //重置時間計算
        //你要執行的工作
    }
}