2023年5月25日 星期四

odrive perform Sunnysky X2212 980KV motor with Encoder TLE5012B

我的Odrive Board
版號: Odrive v3.6-56
ODrive control utility v0.6.5.post2
Odrive 硬件內部 FW V0.5.6
odrivetool 架構在Anaconda的多重Python環境中
要讓ODrive啟動要在odrivetool中設定以下四個configuration
主板 configuration
dev0.erase_configuration()
dev0.config.brake_resistance=2.0
dev0.config.dc_bus_undervoltage_trip_level=8.0
dev0.config.dc_bus_overvoltage_trip_level=56.0
dev0.config.dc_max_positive_current=20.0
dev0.config.dc_max_negative_current=-3.0
dev0.save_configuration()
電機 X2212 motor configuration
dev0.axis0.motor.config.pole_pairs=7
dev0.axis0.motor.config.calibration_current = 5
dev0.axis0.motor.config.resistance_calib_max_voltage= 2
dev0.axis0.motor.config.motor_type =MOTOR_TYPE_HIGH_CURRENT
dev0.axis0.motor.config.current_lim=15
dev0.axis0.motor.config.requested_current_range=20
dev0.save_configuration()
Magnetic Encoder TLE5012B
dev0.axis0.encoder.config.mode=ENCODER_MODE_INCREMENTAL
dev0.axis0.encoder.config.cpr=16384
dev0.axis0.encoder.config.bandwidth=3000
dev0.axis0.config.calibration_lockin.current=5
dev0.axis0.config.calibration_lockin.ramp_time=0.4
dev0.axis0.config.calibration_lockin.ramp_distance=3.1415927410125732
dev0.axis0.config.calibration_lockin.accel=20
dev0.axis0.config.calibration_lockin.vel=40
//---controller setting
dev0.axis0.controller.config.control_mode = CONTROL_MODE_POSITION_CONTROL
dev0.axis0.trap_traj.config.vel_limit=30
dev0.axis0.controller.config.pos_gain = 30
dev0.axis0.controller.config.vel_gain = 0.02
dev0.axis0.controller.config.vel_integrator_gain = 0.2
dev0.axis0.controller.config.input_mode=INPUT_MODE_TRAP_TRAJ
dev0.axis0.trap_traj.config.accel_limit=5
dev0.axis0.trap_traj.config.decel_limit=5
dev0.save_configuration()
dev0.reboot()
//-- Test
dev0.axis0.requested_state = AXIS_STATE_MOTOR_CALIBRATION
dev0.axis0.motor.config.pre_calibrated = True
dev0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
dev0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
dev0.axis0.config.startup_encoder_offset_calibration= True
dev0.axis0.config.startup_closed_loop_control = True
dev0.save_configuration()
dev0.axis0.controller.input_pos=10
//-------------
pip install matplotlib

start_liveplotter(lambda: [dev0.axis0.encoder.pos_estimate])

在live_plotter調適的畫面

利用CAN BUS進行ODrive位置控制影片如下

2023年5月14日 星期日

ESP32 利用 uart communication with mega2560 並用 wifi mesh 控制

開發構想: 用ESP32透過UART與Arduino Mega2560溝通並透過ESP32的WiFi mesh的功能做指令的控制. 如下圖: 手機端的painless mesh的APP連接ESP32, ESP32利用UART2與Mega2560的UART1做指令的傳輸

下圖左為ESP32的com port訊息圖右為Mega2560的com port訊息
實作結果:手機傳送DIO14_ON的指令, 透過廣播到ESP32再傳輸到Mega2560去控制Relay4作動.


2023年5月9日 星期二

ESP32 + MCP2515 use CanHacker on CAN Bus system

在之前文章CAN BUS 通訊研究中, 利用了MEGA2560+MCP2515去做CAN BUS上接收和傳送資料. 最近看到一篇文章 可以用ESP32去做CAN BUS上的sniffer來分析.

CANHacker 參考 https://github.com/autowp/arduino-canhacker install library

其中是想利用他的windows端的軟體CANHackerV2.00.01.exe來做CAN bus analyzer

download link


功能傳輸圖:

上圖右邊是ESP32做控制, 左邊是MEGA2560
其中裡面燒得code是參照https://github.com/autowp/can-usb
線路圖

在燒錄can-usb時記得加入下圖紅線字以及disable loopback不然windows 端的軟體會找不到interface


焊接版本

//-----------------------------------------------------------------------------------
修改程式
Modify the original sample code to use ESP32 dual-core multiplex architecture FreeRTOS
//--------------------------------------------------------------------------------------
#include <can.h>
#include <mcp2515.h>

#include <CanHacker.h>
#include <CanHackerLineReader.h>
#include <lib.h>

#include <SPI.h>
#include <SoftwareSerial.h>
//----------------------------------------------------------------------
//------ESP32 CAN BUS setting ---------------------------------------
const int SPI_CS_PIN = 5;
const int INT_PIN = 21;
//---------------------------------------------------------------------
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif

const int SS_RX_PIN = 3;
const int SS_TX_PIN = 4;
//----------------------------------------------------------------
TaskHandle_t hled;
TaskHandle_t huart;
TaskHandle_t hcan;

CanHackerLineReader *lineReader = NULL;
CanHacker *canHacker = NULL;

SoftwareSerial softwareSerial(SS_RX_PIN, SS_TX_PIN);

void setup() {
  Serial.begin(115200);
  while (!Serial);
  SPI.begin();
  softwareSerial.begin(115200);

  Stream *interfaceStream = &Serial;
  Stream *debugStream = &softwareSerial;


  canHacker = new CanHacker(interfaceStream, debugStream, SPI_CS_PIN);

  canHacker->setClock(MCP_8MHZ);    // For 8MHz crystal oscillator
  //canHacker->enableLoopback(); // uncomment this for loopback
  lineReader = new CanHackerLineReader(canHacker);

  pinMode(INT_PIN, INPUT);
  //--------------- create task----------------------------------
  xTaskCreatePinnedToCore(
    vLEDTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------
  xTaskCreatePinnedToCore(
    vCANTask, "CANTask"
    ,
    1024 // Stack size
    ,
    NULL, 1 // Priority
    ,
    &hcan
    ,
    1);

  //Serial.println("Systom On!");

}
void loop() {
  /**
    CanHacker::ERROR error;

    if (digitalRead(INT_PIN) == LOW) {
      error = canHacker->processInterrupt();
      handleError(error);
    }

    error = lineReader->process();
    handleError(error);**/
}

static void vCANTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.println(F("CANTask at core:"));
  Serial.println(xPortGetCoreID());
  for (;;)
  {
    CanHacker::ERROR error;

    if (digitalRead(INT_PIN) == LOW) {
      error = canHacker->processInterrupt();
      handleError(error);
    }

    error = lineReader->process();
    handleError(error);
  }
  vTaskDelay(1);
}

static void vLEDTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.println(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
    vTaskDelay(200);
    digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
    vTaskDelay(200);
  }
}

void handleError(const CanHacker::ERROR error) {

  switch (error) {
    case CanHacker::ERROR_OK:
    case CanHacker::ERROR_UNKNOWN_COMMAND:
    case CanHacker::ERROR_NOT_CONNECTED:
    case CanHacker::ERROR_MCP2515_ERRIF:
    case CanHacker::ERROR_INVALID_COMMAND:
      return;

    default:
      break;
  }

  softwareSerial.print("Failure (code ");
  softwareSerial.print((int)error);
  softwareSerial.println(")");

  digitalWrite(SPI_CS_PIN, HIGH);
  pinMode(LED_BUILTIN, OUTPUT);

  while (1) {
    int c = (int)error;
    for (int i = 0; i < c; i++) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(500);
      digitalWrite(LED_BUILTIN, LOW);
      delay(500);
    }

    delay(2000);
  } ;
}

驗證影片
//------------------------------------------------------------
若有問題歡迎留言討論!!!
//------------------------------------------------------------
YouTube 影片




2023年5月7日 星期日

ESP32 Bluetooth SPP Profile Application

Purpose:

“ESP32 uses BT SPP Profile to connect with mobile phones or PCs and transmit strings through BT functions to perform specific functional actions for functional strings.”

Fundamental:
SPP (Serial Port Profile) is a Classic Bluetooth profile, SPP defines the requirements for Bluetooth devices necessary for setting up emulated serial cable connections using RFCOMM between two peer devices. The requirements are expressed in terms of services provided to applications, and by defining the features and procedures that are required for interoperability between Bluetooth devices.

不論是PC端或是手機端, 只要跟 ESP32的藍芽連接, 就會有一個SPP建構的無線的serial Port.
我們就可以利用serial port的軟體或是APP來跟 ESP32通訊傳輸指令去控制. 
最簡單就是直接控制ESP32板子的LED燈

#include <BluetoothSerial.h>

BluetoothSerial BT; //宣告藍芽物件,名稱為BT

void setup() {

  Serial.begin(115200);

  BT.begin("BT_Test"); // BTName為藍芽廣播名稱

}

void loop() {

  BT.println("Hello World!");  //傳輸到藍芽裝置

  //檢查藍芽內是否有資料

  while (BT.available()) {

    //讀取藍芽資料

    String BTdata=BT.readString();

    //顯示在序列視窗

    Serial.println(BTdata);

    BTprocessCommand(BTdata); // do something with the command

  }

  delay(500);

}

void BTprocessCommand(String data)

{

    if (data == "LED_OFF")

  {

    Serial.println(F("LED_OFF"));

    vTaskSuspend(hled);

  }

  if (data == "LED_ON")

  {

    Serial.println(F("LED_ON"));

    vTaskResume(hled);

  }

}

以上程序為ESP32端的code

手機端最簡單方式可以用現成的APP

Arduino Bluetooth Control



ESP32與手機APP相互溝通接收到的指令
從手機APP傳送控制字元到 ESP32接收作相對應的處理(控制IO做家電控制)
手機APP Demo:
NB端的 Demo:

Youtube





2023年5月5日 星期五

C# use windows API for Auto testing mehtod

 最近自動測試案子遇到廠商沒有sample code可用, 只得用最原始的暗黑技術來做:

1. 執行廠商可用的執行程式

2. 利用C#呼叫API模擬類似"按鍵精靈"的作法去點擊廠商的程式

以上做法可以解決沒有sample code可以包進自己的測試程式裡.

下面介紹所利用到的windows API

先將廠商程式開啟

hyper = new Process();

hyper.StartInfo.FileName = "c:\\ISP Programmer_V1.5.10\\ISPProgrammer.exe";

hyper.Start();

if (hyper != null){

    hyper.WaitForExit(2000);

    HyperWindow = FindWindow(null, "ISP Programmer_V1.5.10");

    if (HyperWindow != IntPtr.Zero) {

       EnumChildWindows(HyperWindow, new CallBack(delegate (IntPtr hwnd, int lParam) {

             listWnd.Add(hwnd);

             GetWindowText(hwnd, title, title.Capacity);

             GetClassName(hwnd, className, className.Capacity);

              return true;

              }), 0);

       SendMessage(listWnd1[8], BM_CLICK, 0, 0);     //發送點擊按鈕的消息

       }

}

在利用FindWinsow找到開啟的程式

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

在用EnumChildWindows去找程式上component的handle依據各個handle用Sendmessage去做觸發或讀取的功能

[DllImport("user32.dll", EntryPoint = "SendMessage")]

 public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

public const int BM_CLICK = 0xF5;