トップ «前の日記(2026/03/22(Sun)) 最新 RSS

titletitle


2026/03/29(Sun) [長年日記]

uk-icon [mini] ステッパーモーターをMaxxEcuのPWMで駆動する

これまで、ミニのアイドリングをArduinoで水温をチェックしてステッパーモーターを制御する方法でやっていたのだけど、なんか安定しない。なのでAIに協力してもらい、MaxxECUのIdle PWM solenoid出力を使って制御する方法に変更した。PWMは、50Hzの初期状態のまま使用。それほど更新頻度は高くないが、アイドリング制御には問題ないというAIの判断。

以下は、PWMの信号をブロッキングで取得する方法を使っている。非ブロッキングの方法でもやってみたのだが、ノイズの影響なのか安定しないのでそちらはやめてしまった。

これに加え、MaxxxECU側でIdle controlをとりあえずOpen loopで設定した。まだ温度域による微調整が必要そうだが、いい感じの手応え。

#include 
#include 
#include 
#include 
#include 
#include "MCP_ADC.h"
 
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 
float Vref = 3.82;
int THERM_B = 3395;
int THERM_R0 = 9290;
float THERM_T0 = 273.15 + 27.0;
 
const int DIR = 8;
const int STEP = 9;
 
MCP3004 mcp1;
const int inputPin = 7;
 
AccelStepper stepper(AccelStepper::DRIVER, STEP, DIR);
 
// 状態管理
int stepperPos = 0;
int pwm_target = 0;
 
// 温度取得
float getTemperature() {
  uint16_t val = mcp1.read(0);
  float volts0 = val * Vref / 1023;
  float rr1 = 9320 / volts0 - 2000;
  float t = 1.0 / (log(rr1 / (float)THERM_R0) / (float)THERM_B + 1.0 / (float)THERM_T0);
  return t - 273.15;
}
 
void move_stepper_pwm() {
    unsigned long pw = pulseIn(inputPin, LOW, 40000); // μs
    //Serial.println(pw);
 
    static float filt_pw = 10000;  // 初期値(適当でOK)
 
    if (pw > 1000 && pw < 30000) {
      float alpha = 0.2;
      filt_pw = filt_pw * (1 - alpha) + pw * alpha;
    }
    // PWM → ステップ位置に変換
    // 例:1000~2000us → 0~50ステップ
    pwm_target = map((int)filt_pw, 0, 20000, 5, 85);
    pwm_target = constrain(pwm_target, 5, 85);
 
    // ステッパー移動
    if (stepper.distanceToGo() == 0 && pwm_target != stepperPos) {
      pwm_target = pwm_target - 4; // 補正
      stepper.moveTo(pwm_target);
      stepperPos = pwm_target;
    }
}
 
void setup() {
  Wire.begin();
  Wire.setClock(100000);
  Serial.begin(9600);
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for(;;);
  }
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  display.println("INIT OK");
  display.display();
  delay(10);
 
  SPI.begin();
  mcp1.begin(10);
 
  // AccelStepper 設定
  stepper.setMaxSpeed(2000);
  stepper.setAcceleration(3000);
 
  // 初期位置調整
  stepper.moveTo(-85);
  while (stepper.distanceToGo() != 0) {
    stepper.run();
  }
  stepperPos = -85;
 
  pinMode(inputPin, INPUT_PULLUP);
  move_stepper_pwm();
 
  disp_header();
}
 
unsigned long lastUpdate = 0;
 
void loop() {
  // --- AccelStepper の run() は常に高速で回す ---
  stepper.run();
 
  unsigned long now = millis();
  if (now - lastUpdate >= 600) {
    lastUpdate = now;
 
    move_stepper_pwm();
 
    float temp = getTemperature();
    update_temp_and_level(temp, stepperPos);
  }
}
 
void disp_header() {
  display.clearDisplay();
   
  display.setTextSize(1);
  display.setCursor(0,0);
  display.println("Water Temp");
 
  display.setTextSize(1);
  display.setCursor(67,10);
  display.cp437(true);
  display.write(167);
  display.print("C");
 
  display.setCursor(77,0);
  display.println("Accel Lv");
 
  display.display();
}
 
void update_temp_and_level(float temp, int lv){
  int i_temp = (int)temp;
  float f_dot = temp - i_temp;
  int i_dot = (int)(f_dot * 10);
 
  display.setTextSize(3);
  char buf[10], buf2[10];
  sprintf(buf, "%03d", i_temp);
  sprintf(buf2, ".%d", i_dot);
 
  display.fillRect(0, 10, 72, 24, SSD1306_BLACK);
  display.setCursor(0,10);
  display.setTextSize(3);
  display.print(buf);
 
  // 値表示部分だけ消す
  display.fillRect(48, 16, 40, 16, SSD1306_BLACK);
  display.setCursor(48,16);
  display.setTextSize(2);
  display.print(buf2);
 
  // 値表示部分だけ消す
  display.fillRect(90, 10, 38, 22, SSD1306_BLACK);
 
  // 再描画
  display.setCursor(90, 10);
  display.setTextSize(3);
  display.println(lv);
 
  display.display();  
}