2023/12/11 LabVIEW2020 with Python 3.6.8實作Protocol buffer序列化流程-環境準備篇

開發環境
LabVIEW 2020 32bit
Python 3.6.8 32bit
Python 套件 protobuf 3.7.0
protoc-3.6.1-win32.zip

前言
原於沒有辦法將開發環境全部更新到新版,在有新有舊下的狀況,又有軟體間的相依性問題,上面所列為我自己的開發需求下,相對可以用的狀況,目前還無得知會有什麼意外驚喜。
另外為了日後與作業系統的相容性需求,我的開發環境都會使用32位元的版本。

安裝流程
1. 首先到官網下載並安裝LabVIEW2020 社群版 32位元版
2. 下載並安裝Python 3.6.8
3. 安裝Python套件 protobuf 3.7.0
4. 下載protoc-3.6.1 for windows 32bit,並將其中的protoc.exe放在你的.proto檔案的相同資料中。

參考資料
Using python for protocol buffer
Protocol Buffers Documentation
python 在 windows 环境下如何安装protobuf?
protoc-3.6.1-win32.zip 下載路徑


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/

LabVIEW 抓取WebCAM影像

付費快速的方式
Vision Acquisition Software Download – NI
Vision Acquisition Software – NI

免費的方式
透過call windows DLL
avicap32.dll
user32.dll
下面網址有先進已經提供範例程式
2 小時 完成 網路攝像頭 程式 – 第 2 頁 – NI 社區
最後一個的貼文

參考來源
攝像頭程式設計WindowsAPI – avicap32.dll 參數詳解_avicap32 無法顯示攝像頭-CSDN博客
User32.dll詳細介紹_user32.dll函數-CSDN博客
2 小時 完成 網路攝像頭 程式 – 第 2 頁 – NI 社區
NI Vision Acquisition Software、NI Vision Builder和NI Vision Development Module之間的差異 – NI

ADB command 筆記 (Android Debug Bridge)

Query ADB version
adb –version

Print Android log
adb logcat

Stop Catch Android log
adb logcat -d

Wifi On
adb shell settings put global airplane_mode 0
adb shell settings put global wifi_on 1
adb shell settings put global wifi_scan_always_enabled 1

Wifi Off
adb shell settings put global airplane_mode_on 1
adb shell settings put global wifi_on 0
adb shell settings put global wifi_scan_always_enabled 0

Bluetooth On
adb shell cmd bluetooth_manager enable

Bluetooth Off
adb shell cmd bluetooth_manager disable

GPS On
adb shell settings put secure location_mode 3

GPS Off
adb shell settings put secure location_mode 0

LabVIEW 主從關係的subVI,如何將值傳到mainVI上

有下面幾個解決方案
1. 全域變數:subVI透過全域變數,將值寫到全域變數中,由mainVI自己決定讀subVI值。
2. 功能性全域變數:全域變數的進化版本,另外寫一個FGV來存取所需要的變數值。
3. 物件參考(reference):mainVI將要更新值的物件參考(reference)傳入到subVI,當subVI值有更新時,也將值透過property node更新。
4. Control Values by Index:以數值更新的方式來說,效率比property node好。
以上各有各的優缺點,需要依程式架構選擇適當方式。

參考資料來源
LabVIEW Cat ~喵喵的LabVIEW人參~在Actor中使用Set control value by index 來回覆資料元素
LabVIEW Cat ~喵喵的LabVIEW人參~Functional Global Variable的使用介紹
LabVIEW Programming Reference Manual-Property Node
Basic Functional Global Variable Example



LabVIEW .net Container 使用範例

上圖左側為vi內的graph的顯示元件,右側為LabVIEW .net Container顯示的圖片檔。
下圖為轉換的步驟,我把程式放在這裡。

資料來源
Convert a .NET PictureBox to an IMAQ Image – NI
Extract IMAQ Image from Camera Memory Using .NET – NI
LabVIEW 技術交流群 at LINE by Rex