M5StickC Intervalometer
Intervalometer for the Canon DSLR using an M5StickC I had laying around, as well as the Relay module for the device.
Here's the source code. M5StickC board and ESP32 libraries must be installed in the Arduino IDE
#include <M5StickC.h> int cur_val_front = 0; int last_val_front = 0; int cur_val_side = 0; int last_val_side = 0; int cursor_selection = 0; // 0 = minutes, 1 = seconds, 2 = shots, 3 = start, 4 = flash, 5 = reset bool is_running = false; int flash_on = false; int minute_count = 0; int second_count = 0; int shot_count = 0; // user selected shot amount, 0 = unlimited shots int relay_delay_interval = 1; // delay between relay closes in seconds int seconds_until_open = 0; // used to display countdown of seconds until relay opens int relay_counter = 0; // used to display count of relay cycles int shot_counter = 0; // used to display count of shots left uint32_t target_time = 0; // for next 1 second timeout //screen coord vals int min_lbl_x = 0; int min_lbl_y = 2; int min_cnt_x = 55; int min_cnt_y = 2; int sec_lbl_x = 80; int sec_lbl_y = 2; int sec_cnt_x = 125; int sec_cnt_y = 2; int shot_lbl_x = 0; int shot_lbl_y = 22; int shot_cnt_x = 55; int shot_cnt_y = 22; int flash_lbl_x = 80; int flash_lbl_y = 22; int flash_cnt_x = 125; int flash_cnt_y = 22; int start_lbl_x = 20; int start_lbl_y = 52; int start_rect[] = { 0, 48, 82, 24 }; int clr_lbl_x = 105; int clr_lbl_y = 52; int clr_rect[] = { 88, 48, 52, 24 }; int cnt_w = 20; // width of char erase rect int cnt_h = 15; // height of char erase rect void setup() { M5.begin(); M5.Lcd.setRotation(3); M5.Lcd.setTextFont(2); M5.Lcd.setTextSize(1); init_select_screen(); // set buttons for input pinMode(37, INPUT); // front 'M5' button pinMode(39, INPUT); // side button //disable the speak noise pinMode(32, OUTPUT); } void loop(void) { cur_val_front = digitalRead(37); cur_val_side = digitalRead(39); if (is_running == false){ // on selection menu if (cur_val_front != last_val_front) { if (cur_val_front == 0) { // button pressed if (cursor_selection == 0) { // minutes minute_count++; M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(min_cnt_x, min_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(min_cnt_x, min_cnt_y); M5.Lcd.print(String(minute_count)); } else if (cursor_selection == 1) { // seconds second_count++; M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(sec_cnt_x, sec_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(sec_cnt_x, sec_cnt_y); M5.Lcd.print(String(second_count)); } else if (cursor_selection == 2) { // shots shot_count++; M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(shot_cnt_x, shot_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(shot_cnt_x, shot_cnt_y); M5.Lcd.print(String(shot_count)); } else if (cursor_selection == 3) { // flash flash_on = (flash_on) ? 0 : 1; M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(flash_cnt_x, flash_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(flash_cnt_x, flash_cnt_y); M5.Lcd.print(String(flash_on)); } else if (cursor_selection == 4) { // start is_running = true; init_timer_screen(); // close relay digitalWrite(32, HIGH); target_time = millis(); } else { // reset minute_count = 0; second_count = 0; shot_count = 0; flash_on = 0; M5.Lcd.setTextColor(RED, BLACK); // update minutes M5.Lcd.fillRect(min_cnt_x, min_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(min_cnt_x, min_cnt_y); M5.Lcd.print(String(minute_count)); // update secs M5.Lcd.fillRect(sec_cnt_x, sec_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(sec_cnt_x, sec_cnt_y); M5.Lcd.print(String(second_count)); // update shots M5.Lcd.fillRect(shot_cnt_x, shot_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(shot_cnt_x, shot_cnt_y); M5.Lcd.print(String(shot_count)); // update flash M5.Lcd.fillRect(flash_cnt_x, flash_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(flash_cnt_x, flash_cnt_y); M5.Lcd.print(String(flash_on)); } } last_val_front = cur_val_front; } if (cur_val_side != last_val_side) { if (is_running == false) { // on selection menu if (cur_val_side == 0) { // button pressed cursor_selection++; if (cursor_selection > 5) { cursor_selection = 0; } if (cursor_selection == 0) { // minutes // display CLR as normal M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.fillRect(clr_rect[0], clr_rect[1], clr_rect[2], clr_rect[3], BLACK); M5.Lcd.setCursor(clr_lbl_x, clr_lbl_y); M5.Lcd.print("CLR"); M5.Lcd.drawRect(clr_rect[0], clr_rect[1], clr_rect[2], clr_rect[3], RED); // display minutes inv M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(min_cnt_x, min_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(min_cnt_x, min_cnt_y); M5.Lcd.print(String(minute_count)); } else if (cursor_selection == 1) { // seconds // display minutes as normal M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.fillRect(min_cnt_x, min_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(min_cnt_x, min_cnt_y); M5.Lcd.print(String(minute_count)); // display secs inv M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(sec_cnt_x, sec_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(sec_cnt_x, sec_cnt_y); M5.Lcd.print(String(second_count)); } else if (cursor_selection == 2) { // shots // display seconds as normal M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.fillRect(sec_cnt_x, sec_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(sec_cnt_x, sec_cnt_y); M5.Lcd.print(String(second_count)); // display shots inv M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(shot_cnt_x, shot_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(shot_cnt_x, shot_cnt_y); M5.Lcd.print(String(shot_count)); } else if (cursor_selection == 3) { // flash // display shots normal M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.fillRect(shot_cnt_x, shot_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(shot_cnt_x, shot_cnt_y); M5.Lcd.print(String(shot_count)); // display flash inv M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(flash_cnt_x, flash_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(flash_cnt_x, flash_cnt_y); M5.Lcd.print(String(flash_on)); } else if (cursor_selection == 4) { // start // display flash normal M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.fillRect(flash_cnt_x, flash_cnt_y, cnt_w, cnt_h, BLACK); M5.Lcd.setCursor(flash_cnt_x, flash_cnt_y); M5.Lcd.print(String(flash_on)); // display START inv M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(start_rect[0], start_rect[1], start_rect[2], start_rect[3], RED); M5.Lcd.setCursor(start_lbl_x, start_lbl_y); M5.Lcd.print("START"); } else { // reset // display START as normal M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.fillRect(start_rect[0], start_rect[1], start_rect[2], start_rect[3], BLACK); M5.Lcd.setCursor(start_lbl_x, start_lbl_y); M5.Lcd.print("START"); M5.Lcd.drawRect(start_rect[0], start_rect[1], start_rect[2], start_rect[3], RED); // display CLR inv M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.fillRect(clr_rect[0], clr_rect[1], clr_rect[2], clr_rect[3], RED); M5.Lcd.setCursor(clr_lbl_x, clr_lbl_y); M5.Lcd.print("CLR"); } } } last_val_side = cur_val_side; } } else { // timer display is running // stuff to do every second if (target_time < millis()) { each_second_interval(); target_time += 1000; } if (cur_val_front != last_val_front) { if (cur_val_front == 0) { // stop button is pressed // open relay digitalWrite(32, LOW); is_running = false; init_select_screen(); } last_val_front = cur_val_front; } } } void init_select_screen() { M5.Lcd.setTextFont(2); cursor_selection = 0; M5.Lcd.fillScreen(BLACK); M5.Lcd.setTextColor(RED, BLACK); M5.Lcd.setCursor(min_lbl_x, min_lbl_y); M5.Lcd.print("MINS: "); M5.Lcd.setCursor(sec_lbl_x, sec_lbl_y); M5.Lcd.print("SECS: "); M5.Lcd.setCursor(shot_lbl_x, shot_lbl_y); M5.Lcd.print("SHOTS: "); M5.Lcd.setCursor(flash_lbl_x, flash_lbl_y); M5.Lcd.print("FLASH: "); M5.Lcd.setCursor(start_lbl_x, start_lbl_y); M5.Lcd.print("START"); M5.Lcd.drawRect(start_rect[0], start_rect[1], start_rect[2], start_rect[3], RED); M5.Lcd.setCursor(clr_lbl_x, clr_lbl_y); M5.Lcd.print("CLR"); M5.Lcd.drawRect(clr_rect[0], clr_rect[1], clr_rect[2], clr_rect[3], RED); M5.Lcd.setCursor(sec_cnt_x, sec_cnt_y); M5.Lcd.print(String(second_count)); M5.Lcd.setCursor(shot_cnt_x, shot_cnt_y); M5.Lcd.print(String(shot_count)); M5.Lcd.setCursor(flash_cnt_x, flash_cnt_y); M5.Lcd.print(String(flash_on)); M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.setCursor(min_cnt_x, min_cnt_y); M5.Lcd.print(String(minute_count)); } void init_timer_screen() { M5.Lcd.setTextFont(4); // init all timer values seconds_until_open = (minute_count * 60) + second_count; // reset relay/shot display counters relay_counter = 0; shot_counter = shot_count; M5.Lcd.fillScreen(BLACK); M5.Lcd.setTextColor(RED, BLACK); // display timer, relay/shot counter M5.Lcd.setCursor(0, 2); M5.Lcd.print("Timer:"); M5.Lcd.setCursor(110, 2); M5.Lcd.print(String(seconds_until_open)); M5.Lcd.setCursor(0, 26); M5.Lcd.print("Shots:"); M5.Lcd.setCursor(110, 26); if (shot_count == 0) { // when zero, unlimited, so relay counter is shown instead M5.Lcd.print(String(relay_counter)); } else { M5.Lcd.print(String(shot_counter)); } // display stop button M5.Lcd.setTextColor(BLACK, RED); M5.Lcd.setCursor(40, 54); M5.Lcd.print("STOP"); } void flash_screen() { for (int i = 0; i < 4; i++) { M5.Lcd.fillScreen(RED); delay(100); M5.Lcd.fillScreen(BLACK); delay(100); } } void each_second_interval() { if (seconds_until_open == 0) { // open relay digitalWrite(32, LOW); // update relay counter relay_counter++; // update shot counter shot_counter--; } else if (seconds_until_open == (0 - relay_delay_interval)) { // close relay digitalWrite(32, HIGH); //reset interval seconds_until_open = (minute_count * 60) + second_count; } M5.Lcd.setTextColor(RED, BLACK); // update seconds until next open M5.Lcd.fillRect(110, 2, 40, 25, BLACK); M5.Lcd.setCursor(110, 2); M5.Lcd.print(String(seconds_until_open)); // update relay/shot counter M5.Lcd.fillRect(110, 26, 40, 25, BLACK); M5.Lcd.setCursor(110, 26); if (shot_count == 0) { // when zero, unlimited, so relay counter is shown instead M5.Lcd.print(String(relay_counter)); } else { M5.Lcd.print(String(shot_counter)); } // if shots are limited and number of shots reached, stop and return to select screen if (shot_count != 0 && shot_counter == 0) { is_running = false; delay(1000); if (flash_on) { flash_screen(); } init_select_screen(); } seconds_until_open--; }