Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

67 add advanced interval shutter release #73

Merged
merged 11 commits into from
Mar 10, 2024
11 changes: 11 additions & 0 deletions include/furble_ui.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef FURBLE_UI_H
#define FURBLE_UI_H

#include <Furble.h>

struct FurbleCtx {
Furble::Device *device;
bool reconnected;
};

#endif
24 changes: 24 additions & 0 deletions include/interval.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef INTERVAL_H
#define INTERVAL_H

#include <Furble.h>

#include "furble_ui.h"
#include "spinner.h"

#define INTERVAL_DEFAULT_COUNT 10
#define INTERVAL_DEFAULT_COUNT_UNIT SPIN_UNIT_NIL
#define INTERVAL_DEFAULT_SHUTTER 30
#define INTERVAL_DEFAULT_SHUTTER_UNIT SPIN_UNIT_MS
#define INTERVAL_DEFAULT_DELAY 15
#define INTERVAL_DEFAULT_DELAY_UNIT SPIN_UNIT_SEC

struct __attribute__((packed)) interval_t {
SpinValue count;
SpinValue delay;
SpinValue shutter;
};

void remote_interval(FurbleCtx *ctx);

#endif
20 changes: 20 additions & 0 deletions include/settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef SETTINGS_H
#define SETTINGS_H

#include <esp_bt.h>

#include "interval.h"

extern interval_t interval;

void settings_menu_tx_power(void);
esp_power_level_t settings_load_esp_tx_power(void);
void settings_menu_gps(void);

void settings_load_interval(interval_t *interval);
void settings_save_interval(interval_t *interval);

void settings_add_interval_items(ezMenu *submenu);
void settings_menu_interval(void);

#endif
23 changes: 23 additions & 0 deletions include/spinner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef SPINNER_H
#define SPINNER_H

enum spin_unit_t {
SPIN_UNIT_NIL = 0, // no units
SPIN_UNIT_INF = 1, // ignore value, assume infinity
SPIN_UNIT_MS = 2, // milliseconds
SPIN_UNIT_SEC = 3, // seconds
SPIN_UNIT_MIN = 4 // minutes
};

struct __attribute__((packed)) SpinValue {
uint16_t value;
spin_unit_t unit;
};

void spinner_modify_value(const char *title, bool preset, SpinValue *sv);

String sv2str(SpinValue *sv);
unsigned long sv2ms(SpinValue *sv);
void ms2hms(unsigned long ms, unsigned int *h, unsigned int *m, unsigned int *s);

#endif
11 changes: 9 additions & 2 deletions lib/M5ez/src/M5ez.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ void ezButtons::show(String buttons) {
break;
case 2:
_drawButtons(buttonVector[0], "", buttonVector[1], "", "", "", "", "", "");
break;
case 3:
// Three elements, so shortpress only
_drawButtons(buttonVector[0], "", buttonVector[1], "", buttonVector[2], "", "", "", "");
Expand Down Expand Up @@ -2307,13 +2308,16 @@ String M5ez::msgBox(String header,
String buttons /* = "OK" */,
const bool blocking /* = true */,
const GFXfont *font /* = NULL */,
uint16_t color /* = NO_COLOR */) {
uint16_t color /* = NO_COLOR */,
const bool clear /* = true */) {
if (ez.header.title() != header) {
ez.screen.clear();
if (header != "")
ez.header.show(header);
} else {
ez.canvas.clear();
if (clear) {
ez.canvas.clear();
}
}
if (!font)
font = ez.theme->msg_font;
Expand All @@ -2330,6 +2334,9 @@ String M5ez::msgBox(String header,
for (int8_t n = 0; n < lines.size(); n++) {
int16_t y =
ez.canvas.top() + ez.canvas.height() / 2 - ((lines.size() - 1) * font_h / 2) + n * font_h;
if (!clear) {
M5.Lcd.fillRect(0, y - font_h / 2, TFT_W, font_h, ez.theme->background);
}
M5.Lcd.drawString(lines[n].line, TFT_W / 2, y);
}
if (buttons != "" && blocking) {
Expand Down
3 changes: 2 additions & 1 deletion lib/M5ez/src/M5ez.h
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,8 @@ class M5ez {
String buttons = "OK",
const bool blocking = true,
const GFXfont *font = NULL,
uint16_t color = NO_COLOR);
uint16_t color = NO_COLOR,
const bool clear = true);

// ez.textInput
static String textInput(String header = "", String defaultText = "");
Expand Down
57 changes: 7 additions & 50 deletions src/furble.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

#include <M5Unified.h>

#include "furble_ui.h"
#include "interval.h"
#include "settings.h"

const uint32_t SCAN_DURATION = 10;

static NimBLEScan *pScan = nullptr;
Expand All @@ -13,7 +17,7 @@ static std::vector<Furble::Device *> connect_list;

bool load_gps_enable();

static TinyGPSPlus gps;
TinyGPSPlus gps;
HardwareSerial GroveSerial(2);
static const uint32_t GPS_BAUD = 9600;
static const uint16_t GPS_SERVICE_MS = 250;
Expand All @@ -22,14 +26,9 @@ static const uint32_t GPS_MAX_AGE_MS = 60 * 1000;
static const uint8_t CURRENT_POSITION = LEFTMOST + 1;
static const uint8_t GPS_HEADER_POSITION = CURRENT_POSITION + 1;

static bool gps_enable = false;
bool gps_enable = false;
static bool gps_has_fix = false;

struct FurbleCtx {
Furble::Device *device;
bool reconnected;
};

/**
* BLE Advertisement callback.
*/
Expand Down Expand Up @@ -134,49 +133,6 @@ static void about(void) {
ez.msgBox(FURBLE_STR " - About", "Version: " + version, "Back", true);
}

static void trigger(Furble::Device *device, int counter) {
device->focusPress();
delay(250);
device->shutterPress();
delay(50);

ez.msgBox("Interval Release", String(counter), "Stop", false);
device->shutterRelease();
}

static void remote_interval(FurbleCtx *fctx) {
Furble::Device *device = fctx->device;
int i = 0;
int j = 1;

ez.msgBox("Interval Release", "", "Stop", false);
trigger(device, j);

while (device->isConnected()) {
i++;

if (fctx->reconnected) {
ez.msgBox("Interval Release", String(j), "Stop", false);
fctx->reconnected = false;
}

M5.update();

if (M5.BtnB.wasReleased()) {
break;
}

if (i > 50) {
i = 0;
j++;

trigger(device, j);
}

delay(50);
}
}

static void show_shutter_control(bool shutter_locked, unsigned long lock_start_ms) {
static unsigned long prev_update_ms = 0;

Expand Down Expand Up @@ -417,6 +373,7 @@ static void menu_settings(void) {
submenu.buttons("OK#down");
submenu.addItem("Backlight", ez.backlight.menu);
submenu.addItem("GPS", settings_menu_gps);
submenu.addItem("Intervalometer", settings_menu_interval);
submenu.addItem("Theme", ez.theme->menu);
submenu.addItem("Transmit Power", settings_menu_tx_power);
submenu.addItem("About", about);
Expand Down
160 changes: 160 additions & 0 deletions src/interval.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#include <Furble.h>
#include <M5ez.h>

#include "furble_ui.h"
#include "interval.h"
#include "settings.h"
#include "spinner.h"

enum interval_state_t {
INTERVAL_SHUTTER_OPEN = 0,
INTERVAL_SHUTTER_WAIT = 1,
INTERVAL_SHUTTER_CLOSE = 2,
INTERVAL_DELAY = 3,
INTERVAL_EXIT = 4
};

static void display_interval_msg(interval_state_t state,
unsigned int count,
SpinValue *sv_count,
unsigned long now,
unsigned long next) {
unsigned int rem_h = 0;
unsigned int rem_m = 0;
unsigned int rem_s = 0;

const char *statestr = NULL;
switch (state) {
case INTERVAL_SHUTTER_OPEN:
case INTERVAL_SHUTTER_WAIT:
statestr = "SHUTTER OPEN";
break;
case INTERVAL_SHUTTER_CLOSE:
case INTERVAL_DELAY:
statestr = "DELAY";
break;
}

unsigned long rem = next - now;
if (now > next) {
rem = 0;
}

ms2hms(rem, &rem_h, &rem_m, &rem_s);
static char prev_hms[32] = {0x0};
char hms[32] = {0x0};
int len = 0;

if (sv_count->unit == SPIN_UNIT_INF) {
len = snprintf(hms, 32, "%09u|%02u:%02u:%02u", count, rem_h, rem_m, rem_s);
} else {
len =
snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, sv_count->value, rem_h, rem_m, rem_s);
}
// Serial.println(hms);

if ((len > 0) && memcmp(prev_hms, hms, len)) {
memcpy(prev_hms, hms, len);
ez.msgBox((String)statestr, (String)hms, "Stop", false, NULL, NO_COLOR, false);
}
}

static void do_interval(FurbleCtx *fctx, interval_t *interval) {
Furble::Device *device = fctx->device;
const unsigned long config_delay = sv2ms(&interval->delay);
const unsigned long config_shutter = sv2ms(&interval->shutter);

unsigned int icount = 0;
unsigned long idelay = 0;
unsigned long ishutter = 0;

interval_state_t state = INTERVAL_SHUTTER_OPEN;

bool active = true;

unsigned long now = 0;
unsigned long next = 0;

ez.backlight.inactivity(NEVER);

do {
now = millis();
M5.update();

if (fctx->reconnected) {
fctx->reconnected = false;
}

switch (state) {
case INTERVAL_SHUTTER_OPEN:
if ((icount < interval->count.value) || (interval->count.unit == SPIN_UNIT_INF)) {
// Serial.println("Shutter Open");
device->shutterPress();
next = now + config_shutter;
state = INTERVAL_SHUTTER_WAIT;
} else {
state = INTERVAL_EXIT;
}
break;
case INTERVAL_SHUTTER_WAIT:
if (now > next) {
state = INTERVAL_SHUTTER_CLOSE;
}
break;
case INTERVAL_SHUTTER_CLOSE:
icount++;
// Serial.println("Shutter Release");
device->shutterRelease();
next = now + config_delay;
if ((icount < interval->count.value) || (interval->count.unit == SPIN_UNIT_INF)) {
state = INTERVAL_DELAY;
} else {
state = INTERVAL_EXIT;
}
break;
case INTERVAL_DELAY:
if (now > next) {
state = INTERVAL_SHUTTER_OPEN;
}
break;
case INTERVAL_EXIT:
active = false;
break;
}

if (M5.BtnB.wasClicked()) {
if (state == INTERVAL_SHUTTER_WAIT) {
// Serial.print("Shutter Release");
device->shutterRelease();
}
active = false;
}

ez.yield();
display_interval_msg(state, icount, &interval->count, now, next);
delay(10);
} while (active && device->isConnected());
ez.backlight.inactivity(USER_SET);
}

void remote_interval(FurbleCtx *fctx) {
ezMenu submenu(FURBLE_STR " - Interval");
submenu.buttons("OK#down");
submenu.addItem("Start");
settings_add_interval_items(&submenu);
submenu.addItem("Back");
submenu.downOnLast("first");

do {
int16_t i = submenu.runOnce();

if (submenu.pickName() == "Start") {
do_interval(fctx, &interval);
}

if (i == 0) {
return;
}

} while (submenu.pickName() != "Back");
}
Loading