Skip to content

Instantly share code, notes, and snippets.

@GOROman
Created May 21, 2025 02:33
Show Gist options
  • Save GOROman/94563caf239a08310a5ecb5c643a7128 to your computer and use it in GitHub Desktop.
Save GOROman/94563caf239a08310a5ecb5c643a7128 to your computer and use it in GitHub Desktop.
M5Core2_PWM.cpp
/*
Wiring Diagram:
M5Stack Core2
-------------
GPIO 26 ---+ 1kΩ Resistor --- Gate/Base of MOSFET/Transistor
|
GND (Common Ground)
Motor Supply
-------------
+5V --- Anode of Fly-back Diode --- Emitter/Collector of MOSFET/Transistor
| --- Cathode of Fly-back Diode --- Motor Terminal 1
GND (Common Ground) --- Motor Terminal 2
Note: Ensure the M5Stack Core2's GND and the Motor Supply GND are connected.
The fly-back diode should be across the motor terminals, protecting against inductive spikes.
*/
#include <M5Core2.h>
// --- Hardware Constants ---
constexpr uint8_t PWM_PIN = 26; // GPIO pin for PWM output
constexpr uint8_t PWM_CHANNEL = 0; // LEDC channel for PWM
constexpr uint32_t PWM_FREQUENCY = 1000; // PWM frequency in Hz (1 kHz)
constexpr uint8_t PWM_RESOLUTION_BITS = 8; // PWM resolution in bits (0-255)
constexpr uint32_t PWM_MAX_DUTY_CYCLE = (1 << PWM_RESOLUTION_BITS) - 1; // Max duty cycle value (255)
// --- UI Elements ---
M5::Slider *speed_slider = nullptr;
M5::Label *speed_label = nullptr;
M5::Button *start_button = nullptr;
M5::Button *stop_button = nullptr;
M5::Label *fps_label = nullptr;
// --- Global Variables ---
uint8_t current_duty_cycle = 0; // Current PWM duty cycle (0-255)
bool motor_running = false; // Flag to indicate if the motor is supposed to be running
uint32_t last_update_time_ms = 0; // Time of last FPS update
uint32_t frame_count = 0; // Frame counter for FPS calculation
/**
* @brief Updates the FPS (Frames Per Second) display on the screen.
*/
void update_fps() {
frame_count++;
uint32_t current_time_ms = millis();
if (current_time_ms - last_update_time_ms >= 1000) { // Update every 1 second
float fps = (float)frame_count * 1000.0 / (current_time_ms - last_update_time_ms);
char fps_text[20];
snprintf(fps_text, sizeof(fps_text), "FPS: %.1f", fps);
fps_label->setText(fps_text);
frame_count = 0;
last_update_time_ms = current_time_ms;
}
}
/**
* @brief Initializes the M5Stack Core2 and the UI elements.
*/
void setup_m5_ui() {
M5.begin();
M5.Lcd.setRotation(1); // Set screen orientation
M5.Lcd.setTextFont(4); // Set default font size (adjust as needed)
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
// Create UI elements
speed_slider = new M5::Slider(50, 80, 220, 30);
speed_slider->setRange(0, 100);
speed_slider->setValue(0);
speed_slider->setLabel("Speed ( 0–100 % )");
speed_label = new M5::Label(180, 120, 80, 20, "0 %");
start_button = new M5::Button(50, 160, 100, 40);
start_button->setLabel("START");
stop_button = new M5::Button(170, 160, 100, 40);
stop_button->setLabel("STOP");
fps_label = new M5::Label(10, 220, 100, 20, "FPS: 0.0");
// Add elements to the display stack
M5.add(speed_slider);
M5.add(speed_label);
M5.add(start_button);
M5.add(stop_button);
M5.add(fps_label);
// Set initial state
current_duty_cycle = 0;
motor_running = false;
speed_label->setText("0 %");
}
/**
* @brief Configures the PWM peripheral for the motor control.
*/
void setup_pwm() {
// Configure PWM channel
ledcSetup(PWM_CHANNEL, PWM_FREQUENCY, PWM_RESOLUTION_BITS);
// Attach PWM channel to the specified GPIO pin
ledcAttachPin(PWM_PIN, PWM_CHANNEL);
// Set initial duty cycle to 0 (motor off)
ledcWrite(PWM_CHANNEL, 0);
}
/**
* @brief Updates the PWM duty cycle based on the slider value.
* @param percentage The desired speed percentage (0-100).
*/
void update_motor_duty_cycle(int percentage) {
// Convert percentage (0-100) to duty cycle (0-255)
current_duty_cycle = (uint8_t)(((float)percentage / 100.0) * PWM_MAX_DUTY_CYCLE);
// Apply the new duty cycle if the motor is running
if (motor_running) {
ledcWrite(PWM_CHANNEL, current_duty_cycle);
}
// Update the display label
char label_text[10];
snprintf(label_text, sizeof(label_text), "%d %%", percentage);
speed_label->setText(label_text);
}
/**
* @brief Starts the motor with the currently set duty cycle.
*/
void start_motor() {
motor_running = true;
ledcWrite(PWM_CHANNEL, current_duty_cycle);
}
/**
* @brief Stops the motor by setting the duty cycle to 0.
*/
void stop_motor() {
motor_running = false;
ledcWrite(PWM_CHANNEL, 0);
}
void setup() {
setup_m5_ui();
setup_pwm();
// Attach event handlers for UI elements
speed_slider->onChanged([](const Event_t &e) {
update_motor_duty_cycle(speed_slider->getValue());
});
start_button->onPressed([](const Event_t &e) {
start_motor();
});
stop_button->onPressed([](const Event_t &e) {
stop_motor();
});
last_update_time_ms = millis();
frame_count = 0;
}
void loop() {
M5.update(); // Update M5Stack screen and button states
update_fps(); // Update the FPS display
// No other complex logic needed in the loop for this example,
// UI updates are handled by M5.update() and event handlers.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment