Skip to content

Instantly share code, notes, and snippets.

@mtei
Last active February 1, 2026 10:09
Show Gist options
  • Select an option

  • Save mtei/7e6929e9b6c2f8382a6a870b0b81bb45 to your computer and use it in GitHub Desktop.

Select an option

Save mtei/7e6929e9b6c2f8382a6a870b0b81bb45 to your computer and use it in GitHub Desktop.
soft PWM for Arduino
#include <Arduino.h>
//==================================================
// softPWM クラス
//==================================================
class softPWM {
public:
using callback_t = void (*)(void);
void setup(unsigned int period_ms,
unsigned int duty_ms,
unsigned long run_ms,
callback_t on_cb,
callback_t off_cb);
void setup(unsigned int period_ms,
unsigned int duty_ms,
unsigned long run_ms);
bool start(void);
void stop(void);
bool update(unsigned long now_ms);
private:
unsigned int period_ms = 0;
unsigned int duty_ms = 0;
unsigned long run_ms = 0;
callback_t on_cb = nullptr;
callback_t off_cb = nullptr;
unsigned long last_cycle_ms = 0;
unsigned long start_ms = 0;
bool is_on = false;
bool active = false;
bool configured = false;
bool started = false;
};
//--------------------------------------------------
void softPWM::setup(unsigned int period,
unsigned int duty,
unsigned long run_time,
callback_t on,
callback_t off)
{
if (active) return;
if (period == 0) period = 1;
if (duty >= period) duty = period;
period_ms = period;
duty_ms = duty;
if (run_time == 0) {
run_ms = 0;
} else if (run_time <= duty_ms) {
run_ms = duty_ms;
} else {
const unsigned long remain = run_time - duty_ms;
const unsigned long k =
(remain + period_ms - 1UL) / period_ms;
run_ms = k * (unsigned long)period_ms + (unsigned long)duty_ms;
}
on_cb = on;
off_cb = off;
configured = true;
}
//--------------------------------------------------
void softPWM::setup(unsigned int period,
unsigned int duty,
unsigned long run_time)
{
setup(period, duty, run_time, on_cb, off_cb);
}
//--------------------------------------------------
bool softPWM::start(void)
{
if (!configured) return false;
if (active) {
stop();
}
active = true;
is_on = false;
started = false;
last_cycle_ms = 0;
start_ms = 0;
return true;
}
//--------------------------------------------------
void softPWM::stop(void)
{
if (!active) return;
active = false;
if (is_on && off_cb) {
off_cb();
}
is_on = false;
started = false;
}
//--------------------------------------------------
bool softPWM::update(unsigned long now_ms)
{
if (!active) return active;
if (!started) {
started = true;
last_cycle_ms = now_ms;
start_ms = now_ms;
if (on_cb) on_cb();
is_on = true;
return active;
}
if (run_ms > 0 && (now_ms - start_ms) >= run_ms) {
stop();
return active;
}
const unsigned long elapsed = now_ms - last_cycle_ms;
if (elapsed >= period_ms) {
last_cycle_ms = now_ms;
if (on_cb) on_cb();
is_on = true;
return active;
}
if (is_on && elapsed >= duty_ms) {
if (off_cb) off_cb();
is_on = false;
}
return active;
}
//==================================================
// テスト用コード(UNO ビルトインLED)
//==================================================
constexpr int LED_PIN = LED_BUILTIN;
void ledOn()
{
digitalWrite(LED_PIN, HIGH);
}
void ledOff()
{
digitalWrite(LED_PIN, LOW);
}
softPWM pwm;
void setup()
{
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// 周期 2000ms / duty 300ms / 稼働 8000ms
pwm.setup(2000, 300, 8000, ledOn, ledOff);
pwm.start();
delay(3000);
ledOn();
delay(3000);
ledOff();
delay(500);
}
void loop()
{
if (! pwm.update(millis()) ) {
ledOn();
delay(2000);
ledOff();
delay(1000);
pwm.setup(500, 250, 0, ledOn, ledOff);
pwm.start();
}
}
// # Local Variables:
// # mode: C++
// # End:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment