2026/03/29(Sun) [長年日記]
[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(); }
[ツッコミを入れる]


