From b421b2c9fbbe750a24258fb79ab0e26b226b4f17 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:57:32 +1030 Subject: [PATCH 01/10] Add spinner UI for number input. Also add random test code to demo what it looks like. --- src/furble.ino | 20 +++++++++ src/spinner.h | 16 +++++++ src/spinner.ino | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 src/spinner.h create mode 100644 src/spinner.ino diff --git a/src/furble.ino b/src/furble.ino index 0e56e88..c2961c5 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -5,6 +5,8 @@ #include +#include "spinner.h" + const uint32_t SCAN_DURATION = 10; static NimBLEScan *pScan = nullptr; @@ -411,9 +413,27 @@ void setup() { pScan->setWindow(6553); } +static SpinValue sv1 = { 123, SPIN_UNIT_MS }; +bool do_delay(ezMenu *menu) { + spinner_modify_value("Delay", &sv1); + menu->setCaption("delay", "Delay\t" + spinvalue2str(&sv1)); + + return true; +} + +static SpinValue sv2 = { 456, SPIN_UNIT_NIL }; +bool do_count(ezMenu *menu) { + spinner_modify_value("Count", &sv2); + menu->setCaption("count", "Count\t" + spinvalue2str(&sv2)); + + return true; +} + void loop() { ezMenu mainmenu(FURBLE_STR); mainmenu.buttons("OK#down"); + mainmenu.addItem("delay | Delay\t" + spinvalue2str(&sv1), NULL, do_delay); + mainmenu.addItem("count | Count\t" + spinvalue2str(&sv2), NULL, do_count); mainmenu.addItem("Connect", do_saved); mainmenu.addItem("Scan", do_scan); mainmenu.addItem("Delete Saved", menu_delete); diff --git a/src/spinner.h b/src/spinner.h new file mode 100644 index 0000000..b545ad7 --- /dev/null +++ b/src/spinner.h @@ -0,0 +1,16 @@ +#ifndef SPINNER_H +#define SPINNER_H + +enum spin_unit_t { + SPIN_UNIT_NIL = 0, + SPIN_UNIT_MS = 1, + SPIN_UNIT_SEC = 2, + SPIN_UNIT_MIN = 3 +}; + +struct SpinValue { + uint16_t value; + spin_unit_t unit; +}; + +#endif diff --git a/src/spinner.ino b/src/spinner.ino new file mode 100644 index 0000000..05c0c54 --- /dev/null +++ b/src/spinner.ino @@ -0,0 +1,117 @@ +#include "spinner.h" + +static const char *unit2str[4] = { + " ", // SPIN_UNIT_NIL + "msec", // SPIN_UNIT_MS + "secs", // SPIN_UNIT_SEC + "mins" }; // SPIN_UNIT_MIN + +#define FMT_NONE_LEN (4) +static const char *fmt_none[FMT_NONE_LEN] = { + " %1u %1u %1u ", + "[%1u] %1u %1u ", + " %1u [%1u] %1u ", + " %1u %1u [%1u]" }; + +#define FMT_UNIT_LEN (5) +static const char *fmt_unit[FMT_UNIT_LEN] = { + " %1u %1u %1u %4s ", + "[%1u] %1u %1u %4s ", + " %1u [%1u] %1u %4s ", + " %1u %1u [%1u] %4s ", + " %1u %1u %1u [%4s]" }; + +#define SPIN_ROW_LEN (32) + +static void display_spinner(const char *title, unsigned int index, unsigned int h, unsigned int t, unsigned int u, spin_unit_t unit) { + char spin_row[SPIN_ROW_LEN] = { 0x0 }; + + if (unit == SPIN_UNIT_NIL) { + snprintf(spin_row, SPIN_ROW_LEN, fmt_none[index], h, t, u); + } else { + snprintf(spin_row, SPIN_ROW_LEN, fmt_unit[index], h, t, u, unit2str[unit]); + } + + const char *buttons = "Adjust#Next"; + if (index == 0) { + buttons = "OK#Next"; + } + + ez.msgBox(title, (String)spin_row, buttons, false); +} + +void spinner_modify_value(const char *title, SpinValue *sv) { + const unsigned int imax = sv->unit == SPIN_UNIT_NIL ? FMT_NONE_LEN : FMT_UNIT_LEN; + unsigned int i = 0; + bool ok = false; + + // decontruct the value + unsigned int h = sv->value / 100; + unsigned int t = (sv->value % 100) / 10; + unsigned int u = sv->value % 10; + + display_spinner(title, i, h, t, u, sv->unit); + M5.update(); + + do { + if (M5.BtnA.wasClicked()) { + switch (i) { + case 0: + ok = true; + break; + case 1: + h++; + if (h > 9) { + h = 0; + } + break; + case 2: + t++; + if (t > 9) { + t = 0; + } + break; + case 3: + u++; + if (u > 9) { + u = 0; + } + break; + case 4: + switch (sv->unit) { + case SPIN_UNIT_MS: + sv->unit = SPIN_UNIT_SEC; + break; + case SPIN_UNIT_SEC: + sv->unit = SPIN_UNIT_MIN; + break; + case SPIN_UNIT_MIN: + sv->unit = SPIN_UNIT_MS; + break; + default: + sv->unit = SPIN_UNIT_NIL; + } + break; + } + display_spinner(title, i, h, t, u, sv->unit); + } + + if (M5.BtnB.wasClicked()) { + i++; + if (i >= imax) { + i = 0; + } + display_spinner(title, i, h, t, u, sv->unit); + } + + ez.yield(); + delay(50); + } while (!ok); + + // reconstruct the value + sv->value = (h * 100) + (t * 10) + u; +} + +String spinvalue2str(SpinValue *sv) { + return String(sv->value) + unit2str[sv->unit]; +} From e2640e54a2664f93ea5c30b5bf9e3aef39035cf0 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Fri, 1 Mar 2024 20:24:48 +1030 Subject: [PATCH 02/10] Add more intervalometer pieces. Whilst here, fix a major bug in our M5ez button display code. This was causing a crash at certain times. --- lib/M5ez/src/M5ez.cpp | 1 + src/furble.ino | 78 +++++++++++++++++++++++-------------------- src/spinner.ino | 4 +-- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/lib/M5ez/src/M5ez.cpp b/lib/M5ez/src/M5ez.cpp index 1b904d4..72311e9 100644 --- a/lib/M5ez/src/M5ez.cpp +++ b/lib/M5ez/src/M5ez.cpp @@ -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], "", "", "", ""); diff --git a/src/furble.ino b/src/furble.ino index c2961c5..109d3de 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -131,7 +131,32 @@ static void about(void) { ez.msgBox(FURBLE_STR " - About", "Version: " + version, "Back", true); } -static void trigger(Furble::Device *device, int counter) { +static SpinValue interval_count = { 10, SPIN_UNIT_NIL }; +static SpinValue interval_delay = { 10, SPIN_UNIT_SEC }; +static SpinValue interval_shutter = { 50, SPIN_UNIT_MS }; + +bool configure_count(ezMenu *menu) { + spinner_modify_value("Count", &interval_count); + menu->setCaption("interval_count", "Count\t" + spinvalue2str(&interval_count)); + + return true; +} + +bool configure_delay(ezMenu *menu) { + spinner_modify_value("Delay", &interval_delay); + menu->setCaption("interval_delay", "Delay\t" + spinvalue2str(&interval_delay)); + + return true; +} + +bool configure_shutter(ezMenu *menu) { + spinner_modify_value("Shutter", &interval_shutter); + menu->setCaption("interval_shutter", "Shutter\t" + spinvalue2str(&interval_shutter)); + + return true; +} + +static void interval_start(Furble::Device *device, int counter) { device->focusPress(); delay(250); device->shutterPress(); @@ -142,30 +167,27 @@ static void trigger(Furble::Device *device, int counter) { } static void remote_interval(Furble::Device *device) { - int i = 0; - int j = 1; - - ez.msgBox("Interval Release", "", "Stop", false); - trigger(device, j); - - while (true) { - i++; + ezMenu submenu(FURBLE_STR " - Interval"); + submenu.buttons("OK#down"); + submenu.addItem("Start"); + submenu.addItem("interval_count | Count\t" + spinvalue2str(&interval_count), NULL, configure_count); + submenu.addItem("interval_delay | Delay\t" + spinvalue2str(&interval_delay), NULL, configure_delay); + submenu.addItem("interval_shutter | Shutter\t" + spinvalue2str(&interval_shutter), NULL, configure_shutter); + submenu.addItem("Back"); + submenu.downOnLast("first"); - M5.update(); + do { + int16_t i = submenu.runOnce(); - if (M5.BtnB.wasReleased()) { - break; + if (submenu.pickName() == "Start") { + interval_start(device, 2); } - if (i > 50) { - i = 0; - j++; - - trigger(device, j); + if (i == 0) { + return; } - delay(50); - } + } while (submenu.pickName() != "Back"); } static void show_shutter_control(bool shutter_locked, unsigned long lock_start_ms) { @@ -413,27 +435,9 @@ void setup() { pScan->setWindow(6553); } -static SpinValue sv1 = { 123, SPIN_UNIT_MS }; -bool do_delay(ezMenu *menu) { - spinner_modify_value("Delay", &sv1); - menu->setCaption("delay", "Delay\t" + spinvalue2str(&sv1)); - - return true; -} - -static SpinValue sv2 = { 456, SPIN_UNIT_NIL }; -bool do_count(ezMenu *menu) { - spinner_modify_value("Count", &sv2); - menu->setCaption("count", "Count\t" + spinvalue2str(&sv2)); - - return true; -} - void loop() { ezMenu mainmenu(FURBLE_STR); mainmenu.buttons("OK#down"); - mainmenu.addItem("delay | Delay\t" + spinvalue2str(&sv1), NULL, do_delay); - mainmenu.addItem("count | Count\t" + spinvalue2str(&sv2), NULL, do_count); mainmenu.addItem("Connect", do_saved); mainmenu.addItem("Scan", do_scan); mainmenu.addItem("Delete Saved", menu_delete); diff --git a/src/spinner.ino b/src/spinner.ino index 05c0c54..1ce7b0c 100644 --- a/src/spinner.ino +++ b/src/spinner.ino @@ -37,7 +37,7 @@ static void display_spinner(const char *title, unsigned int index, unsigned int buttons = "OK#Next"; } - ez.msgBox(title, (String)spin_row, buttons, false); + ez.msgBox(title, (String)spin_row), buttons, false); } void spinner_modify_value(const char *title, SpinValue *sv) { @@ -45,7 +45,7 @@ void spinner_modify_value(const char *title, SpinValue *sv) { unsigned int i = 0; bool ok = false; - // decontruct the value + // deconstruct the value unsigned int h = sv->value / 100; unsigned int t = (sv->value % 100) / 10; unsigned int u = sv->value % 10; From 4b05da51eb2e1d45b93a34322d0c65f820a5a70e Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:22:28 +1030 Subject: [PATCH 03/10] Experiment with different spinner inputs. --- src/furble.ino | 27 ++++++-- src/spinner.ino | 172 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 179 insertions(+), 20 deletions(-) diff --git a/src/furble.ino b/src/furble.ino index 109d3de..f80c577 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -136,33 +136,43 @@ static SpinValue interval_delay = { 10, SPIN_UNIT_SEC }; static SpinValue interval_shutter = { 50, SPIN_UNIT_MS }; bool configure_count(ezMenu *menu) { - spinner_modify_value("Count", &interval_count); + spinner_modify_value("Count", false, &interval_count); menu->setCaption("interval_count", "Count\t" + spinvalue2str(&interval_count)); return true; } bool configure_delay(ezMenu *menu) { - spinner_modify_value("Delay", &interval_delay); + spinner_modify_value("Delay", true, &interval_delay); menu->setCaption("interval_delay", "Delay\t" + spinvalue2str(&interval_delay)); return true; } bool configure_shutter(ezMenu *menu) { - spinner_modify_value("Shutter", &interval_shutter); + spinner_modify_value("Shutter", true, &interval_shutter); menu->setCaption("interval_shutter", "Shutter\t" + spinvalue2str(&interval_shutter)); return true; } -static void interval_start(Furble::Device *device, int counter) { +static void do_interval(Furble::Device *device, SpinValue *count, SpinValue *idelay, SpinValue *shutter) { + const unsigned int initial_count = count->value; + const unsigned long initial_delay = spinvalue2ms(idelay); + const unsigned long initial_shutter = spinvalue2ms(shutter); + + bool shooting = true; + + do { + ez.yield(); + delay(10); + } while (shooting); device->focusPress(); delay(250); device->shutterPress(); delay(50); - ez.msgBox("Interval Release", String(counter), "Stop", false); + ez.msgBox("Interval Release", String(initial_count), "Stop", false); device->shutterRelease(); } @@ -180,7 +190,7 @@ static void remote_interval(Furble::Device *device) { int16_t i = submenu.runOnce(); if (submenu.pickName() == "Start") { - interval_start(device, 2); + do_interval(device, &interval_count, &interval_delay, &interval_shutter); } if (i == 0) { @@ -435,9 +445,14 @@ void setup() { pScan->setWindow(6553); } +static void test_interval(void) { + remote_interval(nullptr); +} + void loop() { ezMenu mainmenu(FURBLE_STR); mainmenu.buttons("OK#down"); + mainmenu.addItem("Interval", test_interval); mainmenu.addItem("Connect", do_saved); mainmenu.addItem("Scan", do_scan); mainmenu.addItem("Delete Saved", menu_delete); diff --git a/src/spinner.ino b/src/spinner.ino index 1ce7b0c..1c46232 100644 --- a/src/spinner.ino +++ b/src/spinner.ino @@ -6,6 +6,21 @@ static const char *unit2str[4] = { "secs", // SPIN_UNIT_SEC "mins" }; // SPIN_UNIT_MIN +#define PRESET_NUM 10 + +static uint16_t spin_preset[PRESET_NUM] = { + 1, + 2, + 4, + 8, + 15, + 30, + 60, + 125, + 250, + 500 }; + + #define FMT_NONE_LEN (4) static const char *fmt_none[FMT_NONE_LEN] = { " %1u %1u %1u ", @@ -21,15 +36,46 @@ static const char *fmt_unit[FMT_UNIT_LEN] = { " %1u %1u [%1u] %4s ", " %1u %1u %1u [%4s]" }; +#define FMT_PRESET_NONE_LEN (2) +static const char *fmt_preset_none[FMT_PRESET_NONE_LEN] = { + " %1u %1u %1u ", + "[%1u %1u %1u]" }; + +#define FMT_PRESET_UNIT_LEN (3) +static const char *fmt_preset_unit[FMT_PRESET_UNIT_LEN] = { + " %1u %1u %1u %4s ", + "[%1u %1u %1u] %4s ", + " %1u %1u %1u [%4s]" }; + #define SPIN_ROW_LEN (32) -static void display_spinner(const char *title, unsigned int index, unsigned int h, unsigned int t, unsigned int u, spin_unit_t unit) { +static void value2htu(uint16_t value, unsigned int *h, unsigned int *t, unsigned int *u) { + // deconstruct the value + *h = value / 100; + *t = (value % 100) / 10; + *u = value % 10; +} + +static uint16_t htu2value(unsigned int h, unsigned int t, unsigned int u) { + // reconstruct the value + return (h * 100) + (t * 10) + u; +} + +static void display_spinner(const char *title, bool preset, unsigned int index, unsigned int h, unsigned int t, unsigned int u, spin_unit_t unit) { char spin_row[SPIN_ROW_LEN] = { 0x0 }; - if (unit == SPIN_UNIT_NIL) { - snprintf(spin_row, SPIN_ROW_LEN, fmt_none[index], h, t, u); + if (preset) { + if (unit == SPIN_UNIT_NIL) { + snprintf(spin_row, SPIN_ROW_LEN, fmt_preset_none[index], h, t, u); + } else { + snprintf(spin_row, SPIN_ROW_LEN, fmt_preset_unit[index], h, t, u, unit2str[unit]); + } } else { - snprintf(spin_row, SPIN_ROW_LEN, fmt_unit[index], h, t, u, unit2str[unit]); + if (unit == SPIN_UNIT_NIL) { + snprintf(spin_row, SPIN_ROW_LEN, fmt_none[index], h, t, u); + } else { + snprintf(spin_row, SPIN_ROW_LEN, fmt_unit[index], h, t, u, unit2str[unit]); + } } const char *buttons = "Adjust#Next"; @@ -37,20 +83,92 @@ static void display_spinner(const char *title, unsigned int index, unsigned int buttons = "OK#Next"; } - ez.msgBox(title, (String)spin_row), buttons, false); + ez.msgBox(title, (String)spin_row, buttons, false); } -void spinner_modify_value(const char *title, SpinValue *sv) { +static void spinner_preset(const char *title, SpinValue *sv) { + const unsigned int imax = sv->unit == SPIN_UNIT_NIL ? FMT_PRESET_NONE_LEN : FMT_PRESET_UNIT_LEN; + unsigned int i = 0; + unsigned int n = 0; + bool ok = false; + + uint16_t value = sv->value; + unsigned int h; + unsigned int t; + unsigned int u; + + // find closest preset equal or lower + for (n = (PRESET_NUM-1); n > 0; n--) { + if (value >= spin_preset[n]) { + break; + } + } + + value2htu(value, &h, &t, &u); + display_spinner(title, true, i, h, t, u, sv->unit); + M5.update(); + + do { + if (M5.BtnA.wasClicked()) { + switch (i) { + case 0: + ok = true; + break; + case 1: +Serial.println(n); + n++; + if (n >= PRESET_NUM) { + n = 0; + } + value = spin_preset[n]; + break; + case 2: + switch (sv->unit) { + case SPIN_UNIT_MS: + sv->unit = SPIN_UNIT_SEC; + break; + case SPIN_UNIT_SEC: + sv->unit = SPIN_UNIT_MIN; + break; + case SPIN_UNIT_MIN: + sv->unit = SPIN_UNIT_MS; + break; + default: + sv->unit = SPIN_UNIT_NIL; + } + break; + } + value2htu(value, &h, &t, &u); + display_spinner(title, true, i, h, t, u, sv->unit); + } + + if (M5.BtnB.wasClicked()) { + i++; + if (i >= imax) { + i = 0; + } + display_spinner(title, true, i, h, t, u, sv->unit); + } + + ez.yield(); + delay(50); + } while (!ok); + + // reconstruct the value + sv->value = htu2value(h, t, u); +} + +static void spinner_custom(const char *title, SpinValue *sv) { const unsigned int imax = sv->unit == SPIN_UNIT_NIL ? FMT_NONE_LEN : FMT_UNIT_LEN; unsigned int i = 0; bool ok = false; - // deconstruct the value - unsigned int h = sv->value / 100; - unsigned int t = (sv->value % 100) / 10; - unsigned int u = sv->value % 10; + unsigned int h = 0; + unsigned int t = 0; + unsigned int u = 0; - display_spinner(title, i, h, t, u, sv->unit); + value2htu(sv->value, &h, &t, &u); + display_spinner(title, false, i, h, t, u, sv->unit); M5.update(); do { @@ -93,7 +211,7 @@ void spinner_modify_value(const char *title, SpinValue *sv) { } break; } - display_spinner(title, i, h, t, u, sv->unit); + display_spinner(title, false, i, h, t, u, sv->unit); } if (M5.BtnB.wasClicked()) { @@ -101,7 +219,7 @@ void spinner_modify_value(const char *title, SpinValue *sv) { if (i >= imax) { i = 0; } - display_spinner(title, i, h, t, u, sv->unit); + display_spinner(title, false, i, h, t, u, sv->unit); } ez.yield(); @@ -109,9 +227,35 @@ void spinner_modify_value(const char *title, SpinValue *sv) { } while (!ok); // reconstruct the value - sv->value = (h * 100) + (t * 10) + u; + sv->value = htu2value(h, t, u); +} + +void spinner_modify_value(const char *title, bool preset, SpinValue *sv) { + if (preset) { + spinner_preset(title, sv); + } else { + spinner_custom(title, sv); + } } String spinvalue2str(SpinValue *sv) { return String(sv->value) + unit2str[sv->unit]; } + +unsigned long spinvalue2ms(SpinValue *sv) { + switch (sv->unit) { + case SPIN_UNIT_MIN: + return (sv->value * 60 * 1000); + case SPIN_UNIT_SEC: + return (sv->value * 1000); + case SPIN_UNIT_MS: + return (sv->value); + } + return 0; +} + +void ms2hms(unsigned long ms, unsigned int *h, unsigned int *m, unsigned int *s) { + *h = (ms / 1000) / (60 * 60); + *m = ((ms / 1000) / 60) % 60; + *s = (ms / 1000) % 60; +} From 7eff9e4317d91a60c73473e5dd7ed92e820cd289 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:33:05 +1030 Subject: [PATCH 04/10] Refactor ino to cpp because arduino is weird. Refactor the interval code to its own files. --- src/furble.ino | 76 +------------- src/interval.cpp | 74 ++++++++++++++ src/interval.h | 8 ++ src/settings.cpp | 164 +++++++++++++++++++++++++++++++ src/{settings.ino => settings.h} | 13 ++- src/{spinner.ino => spinner.cpp} | 8 +- src/spinner.h | 6 ++ 7 files changed, 272 insertions(+), 77 deletions(-) create mode 100644 src/interval.cpp create mode 100644 src/interval.h create mode 100644 src/settings.cpp rename src/{settings.ino => settings.h} (94%) rename src/{spinner.ino => spinner.cpp} (97%) diff --git a/src/furble.ino b/src/furble.ino index f80c577..026c70e 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -5,7 +5,8 @@ #include -#include "spinner.h" +#include "interval.h" +#include "settings.h" const uint32_t SCAN_DURATION = 10; @@ -15,7 +16,7 @@ static std::vector 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; @@ -24,7 +25,7 @@ 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; /** @@ -131,75 +132,6 @@ static void about(void) { ez.msgBox(FURBLE_STR " - About", "Version: " + version, "Back", true); } -static SpinValue interval_count = { 10, SPIN_UNIT_NIL }; -static SpinValue interval_delay = { 10, SPIN_UNIT_SEC }; -static SpinValue interval_shutter = { 50, SPIN_UNIT_MS }; - -bool configure_count(ezMenu *menu) { - spinner_modify_value("Count", false, &interval_count); - menu->setCaption("interval_count", "Count\t" + spinvalue2str(&interval_count)); - - return true; -} - -bool configure_delay(ezMenu *menu) { - spinner_modify_value("Delay", true, &interval_delay); - menu->setCaption("interval_delay", "Delay\t" + spinvalue2str(&interval_delay)); - - return true; -} - -bool configure_shutter(ezMenu *menu) { - spinner_modify_value("Shutter", true, &interval_shutter); - menu->setCaption("interval_shutter", "Shutter\t" + spinvalue2str(&interval_shutter)); - - return true; -} - -static void do_interval(Furble::Device *device, SpinValue *count, SpinValue *idelay, SpinValue *shutter) { - const unsigned int initial_count = count->value; - const unsigned long initial_delay = spinvalue2ms(idelay); - const unsigned long initial_shutter = spinvalue2ms(shutter); - - bool shooting = true; - - do { - ez.yield(); - delay(10); - } while (shooting); - device->focusPress(); - delay(250); - device->shutterPress(); - delay(50); - - ez.msgBox("Interval Release", String(initial_count), "Stop", false); - device->shutterRelease(); -} - -static void remote_interval(Furble::Device *device) { - ezMenu submenu(FURBLE_STR " - Interval"); - submenu.buttons("OK#down"); - submenu.addItem("Start"); - submenu.addItem("interval_count | Count\t" + spinvalue2str(&interval_count), NULL, configure_count); - submenu.addItem("interval_delay | Delay\t" + spinvalue2str(&interval_delay), NULL, configure_delay); - submenu.addItem("interval_shutter | Shutter\t" + spinvalue2str(&interval_shutter), NULL, configure_shutter); - submenu.addItem("Back"); - submenu.downOnLast("first"); - - do { - int16_t i = submenu.runOnce(); - - if (submenu.pickName() == "Start") { - do_interval(device, &interval_count, &interval_delay, &interval_shutter); - } - - if (i == 0) { - return; - } - - } while (submenu.pickName() != "Back"); -} - static void show_shutter_control(bool shutter_locked, unsigned long lock_start_ms) { static unsigned long prev_update_ms = 0; diff --git a/src/interval.cpp b/src/interval.cpp new file mode 100644 index 0000000..0859d87 --- /dev/null +++ b/src/interval.cpp @@ -0,0 +1,74 @@ +#include +#include + +#include "interval.h" +#include "spinner.h" + +static SpinValue interval_count = { 10, SPIN_UNIT_NIL }; +static SpinValue interval_delay = { 10, SPIN_UNIT_SEC }; +static SpinValue interval_shutter = { 50, SPIN_UNIT_MS }; + +static bool configure_count(ezMenu *menu) { + spinner_modify_value("Count", false, &interval_count); + menu->setCaption("interval_count", "Count\t" + sv2str(&interval_count)); + + return true; +} + +static bool configure_delay(ezMenu *menu) { + spinner_modify_value("Delay", true, &interval_delay); + menu->setCaption("interval_delay", "Delay\t" + sv2str(&interval_delay)); + + return true; +} + +static bool configure_shutter(ezMenu *menu) { + spinner_modify_value("Shutter", true, &interval_shutter); + menu->setCaption("interval_shutter", "Shutter\t" + sv2str(&interval_shutter)); + + return true; +} + +static void do_interval(Furble::Device *device, SpinValue *count, SpinValue *idelay, SpinValue *shutter) { + const unsigned int initial_count = count->value; + const unsigned long initial_delay = sv2ms(idelay); + const unsigned long initial_shutter = sv2ms(shutter); + + bool shooting = true; + + do { + ez.yield(); + delay(10); + } while (shooting); + device->focusPress(); + delay(250); + device->shutterPress(); + delay(50); + + ez.msgBox("Interval Release", String(initial_count), "Stop", false); + device->shutterRelease(); +} + +void remote_interval(Furble::Device *device) { + ezMenu submenu(FURBLE_STR " - Interval"); + submenu.buttons("OK#down"); + submenu.addItem("Start"); + submenu.addItem("interval_count | Count\t" + sv2str(&interval_count), NULL, configure_count); + submenu.addItem("interval_delay | Delay\t" + sv2str(&interval_delay), NULL, configure_delay); + submenu.addItem("interval_shutter | Shutter\t" + sv2str(&interval_shutter), NULL, configure_shutter); + submenu.addItem("Back"); + submenu.downOnLast("first"); + + do { + int16_t i = submenu.runOnce(); + + if (submenu.pickName() == "Start") { + do_interval(device, &interval_count, &interval_delay, &interval_shutter); + } + + if (i == 0) { + return; + } + + } while (submenu.pickName() != "Back"); +} diff --git a/src/interval.h b/src/interval.h new file mode 100644 index 0000000..58e814a --- /dev/null +++ b/src/interval.h @@ -0,0 +1,8 @@ +#ifndef INTERVAL_H +#define INTERVAL_H + +#include + +void remote_interval(Furble::Device *device); + +#endif diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 0000000..5d6903e --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,164 @@ +#include +#include + +#include +#include +#include +#include + +#include "settings.h" + +extern TinyGPSPlus gps; +extern bool gps_enable; + +const char *PREFS_TX_POWER = "txpower"; +const char *PREFS_GPS = "gps"; + +/** + * Save BLE transmit power to preferences. + */ +static void save_tx_power(uint8_t tx_power) { + Preferences prefs; + + prefs.begin(FURBLE_STR, false); + prefs.putUChar(PREFS_TX_POWER, tx_power); + prefs.end(); +} + +/** + * Load BLE transmit power from preferences. + */ +static uint8_t load_tx_power() { + Preferences prefs; + + prefs.begin(FURBLE_STR, true); + uint8_t power = prefs.getUChar(PREFS_TX_POWER, 1); + prefs.end(); + + return power; +} + +/** + * Load and map prefs power to ESP power. + */ +esp_power_level_t settings_load_esp_tx_power() { + uint8_t power = load_tx_power(); + switch (power) { + case 0: + return ESP_PWR_LVL_P3; + case 1: + return ESP_PWR_LVL_P6; + case 2: + return ESP_PWR_LVL_P9; + default: + return ESP_PWR_LVL_P3; + } + return ESP_PWR_LVL_P3; +} + +/** + * Transmit power menu. + * + * Options are 1, 2 and 3. + */ +void settings_menu_tx_power(void) { + uint8_t power = load_tx_power(); + ezProgressBar power_bar(FURBLE_STR, "Set transmit power", "Adjust#Back"); + power_bar.value(power / 0.03f); + while (true) { + String b = ez.buttons.poll(); + if (b == "Adjust") { + power++; + if (power > 3) { + power = 1; + } + power_bar.value(power / 0.03f); + } + if (b == "Back") { + break; + } + } + + save_tx_power(power); +} + +/** + * Display GPS data. + */ +static void show_gps_info(void) { + Serial.println("GPS Data"); + char buffer[256] = {0x0}; + bool first = true; + + do { + bool updated = gps.location.isUpdated() || gps.date.isUpdated() || gps.time.isUpdated(); + + snprintf( + buffer, 256, "%s (%d) | %.2f, %.2f | %.2f metres | %4u-%02u-%02u %02u:%02u:%02u", + gps.location.isValid() && gps.date.isValid() && gps.time.isValid() ? "Valid" : "Invalid", + gps.location.age(), gps.location.lat(), gps.location.lng(), gps.altitude.meters(), + gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), + gps.time.second()); + + if (first || updated) { + first = false; + ez.header.draw("gps"); + ez.msgBox("GPS Data", buffer, "Back", false); + } + + M5.update(); + + if (M5.BtnB.wasPressed()) { + break; + } + + ez.yield(); + delay(100); + } while (true); +} + +/** + * Read GPS enable setting. + */ +bool load_gps_enable() { + Preferences prefs; + + prefs.begin(FURBLE_STR, true); + bool enable = prefs.getBool(PREFS_GPS, false); + prefs.end(); + + return enable; +} + +/** + * Save GPS enable setting. + */ +static void save_gps_enable(bool enable) { + Preferences prefs; + + prefs.begin(FURBLE_STR, false); + prefs.putBool(PREFS_GPS, enable); + prefs.end(); +} + +bool gps_onoff(ezMenu *menu) { + gps_enable = !gps_enable; + menu->setCaption("onoff", "GPS\t" + (String)(gps_enable ? "ON" : "OFF")); + save_gps_enable(gps_enable); + + return true; +} + +/** + * GPS settings menu. + */ +void settings_menu_gps(void) { + ezMenu submenu(FURBLE_STR " - GPS settings"); + + submenu.buttons("OK#down"); + submenu.addItem("onoff | GPS\t" + (String)(gps_enable ? "ON" : "OFF"), NULL, gps_onoff); + submenu.addItem("GPS Data", show_gps_info); + submenu.downOnLast("first"); + submenu.addItem("Back"); + submenu.run(); +} diff --git a/src/settings.ino b/src/settings.h similarity index 94% rename from src/settings.ino rename to src/settings.h index 8f02e4b..2b24607 100644 --- a/src/settings.ino +++ b/src/settings.h @@ -1,3 +1,12 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +void settings_menu_tx_power(void); +esp_power_level_t settings_load_esp_tx_power(void); +void settings_menu_gps(void); + +#endif +#if 0 const char *PREFS_TX_POWER = "txpower"; const char *PREFS_GPS = "gps"; @@ -28,7 +37,6 @@ static uint8_t load_tx_power() { /** * Load and map prefs power to ESP power. */ -esp_power_level_t settings_load_esp_tx_power() { uint8_t power = load_tx_power(); switch (power) { case 0: @@ -48,7 +56,6 @@ esp_power_level_t settings_load_esp_tx_power() { * * Options are 1, 2 and 3. */ -void settings_menu_tx_power(void) { uint8_t power = load_tx_power(); ezProgressBar power_bar(FURBLE_STR, "Set transmit power", "Adjust#Back"); power_bar.value(power / 0.03f); @@ -139,7 +146,6 @@ bool gps_onoff(ezMenu *menu) { /** * GPS settings menu. */ -void settings_menu_gps(void) { ezMenu submenu(FURBLE_STR " - GPS settings"); submenu.buttons("OK#down"); @@ -149,3 +155,4 @@ void settings_menu_gps(void) { submenu.addItem("Back"); submenu.run(); } +#endif diff --git a/src/spinner.ino b/src/spinner.cpp similarity index 97% rename from src/spinner.ino rename to src/spinner.cpp index 1c46232..c101e21 100644 --- a/src/spinner.ino +++ b/src/spinner.cpp @@ -1,3 +1,7 @@ +#include +#include +#include + #include "spinner.h" static const char *unit2str[4] = { @@ -238,11 +242,11 @@ void spinner_modify_value(const char *title, bool preset, SpinValue *sv) { } } -String spinvalue2str(SpinValue *sv) { +String sv2str(SpinValue *sv) { return String(sv->value) + unit2str[sv->unit]; } -unsigned long spinvalue2ms(SpinValue *sv) { +unsigned long sv2ms(SpinValue *sv) { switch (sv->unit) { case SPIN_UNIT_MIN: return (sv->value * 60 * 1000); diff --git a/src/spinner.h b/src/spinner.h index b545ad7..da42faf 100644 --- a/src/spinner.h +++ b/src/spinner.h @@ -13,4 +13,10 @@ struct SpinValue { 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 From 438821e18529ef02ec8a0b57d3bad828d3a1e403 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:41:56 +1030 Subject: [PATCH 05/10] Move header files to include directory. --- {src => include}/interval.h | 0 {src => include}/settings.h | 0 {src => include}/spinner.h | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {src => include}/interval.h (100%) rename {src => include}/settings.h (100%) rename {src => include}/spinner.h (100%) diff --git a/src/interval.h b/include/interval.h similarity index 100% rename from src/interval.h rename to include/interval.h diff --git a/src/settings.h b/include/settings.h similarity index 100% rename from src/settings.h rename to include/settings.h diff --git a/src/spinner.h b/include/spinner.h similarity index 100% rename from src/spinner.h rename to include/spinner.h From 5f6883834f38cf0b135e303059939a487c9d6014 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:40:25 +1030 Subject: [PATCH 06/10] Implement interval logic loop. --- include/interval.h | 14 ++++ include/settings.h | 155 ++------------------------------------------ include/spinner.h | 2 +- src/interval.cpp | 156 +++++++++++++++++++++++++++++++++++++-------- src/settings.cpp | 30 ++++++++- src/spinner.cpp | 1 - 6 files changed, 180 insertions(+), 178 deletions(-) diff --git a/include/interval.h b/include/interval.h index 58e814a..c60df96 100644 --- a/include/interval.h +++ b/include/interval.h @@ -2,6 +2,20 @@ #define INTERVAL_H #include +#include "spinner.h" + +#define INTERVAL_DEFAULT_COUNT 10 +#define INTERVAL_DEFAULT_COUNT_UNIT SPIN_UNIT_NIL +#define INTERVAL_DEFAULT_SHUTTER 50 +#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(Furble::Device *device); diff --git a/include/settings.h b/include/settings.h index 2b24607..9d77d90 100644 --- a/include/settings.h +++ b/include/settings.h @@ -1,158 +1,15 @@ #ifndef SETTINGS_H #define SETTINGS_H +#include + +#include "interval.h" + void settings_menu_tx_power(void); esp_power_level_t settings_load_esp_tx_power(void); void settings_menu_gps(void); -#endif -#if 0 -const char *PREFS_TX_POWER = "txpower"; -const char *PREFS_GPS = "gps"; - -/** - * Save BLE transmit power to preferences. - */ -static void save_tx_power(uint8_t tx_power) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putUChar(PREFS_TX_POWER, tx_power); - prefs.end(); -} - -/** - * Load BLE transmit power from preferences. - */ -static uint8_t load_tx_power() { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - uint8_t power = prefs.getUChar(PREFS_TX_POWER, 1); - prefs.end(); - - return power; -} - -/** - * Load and map prefs power to ESP power. - */ - uint8_t power = load_tx_power(); - switch (power) { - case 0: - return ESP_PWR_LVL_P3; - case 1: - return ESP_PWR_LVL_P6; - case 2: - return ESP_PWR_LVL_P9; - default: - return ESP_PWR_LVL_P3; - } - return ESP_PWR_LVL_P3; -} - -/** - * Transmit power menu. - * - * Options are 1, 2 and 3. - */ - uint8_t power = load_tx_power(); - ezProgressBar power_bar(FURBLE_STR, "Set transmit power", "Adjust#Back"); - power_bar.value(power / 0.03f); - while (true) { - String b = ez.buttons.poll(); - if (b == "Adjust") { - power++; - if (power > 3) { - power = 1; - } - power_bar.value(power / 0.03f); - } - if (b == "Back") { - break; - } - } - - save_tx_power(power); -} - -/** - * Display GPS data. - */ -static void show_gps_info(void) { - Serial.println("GPS Data"); - char buffer[256] = {0x0}; - bool first = true; - - do { - bool updated = gps.location.isUpdated() || gps.date.isUpdated() || gps.time.isUpdated(); - - snprintf( - buffer, 256, "%s (%d) | %.2f, %.2f | %.2f metres | %4u-%02u-%02u %02u:%02u:%02u", - gps.location.isValid() && gps.date.isValid() && gps.time.isValid() ? "Valid" : "Invalid", - gps.location.age(), gps.location.lat(), gps.location.lng(), gps.altitude.meters(), - gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), - gps.time.second()); - - if (first || updated) { - first = false; - ez.header.draw("gps"); - ez.msgBox("GPS Data", buffer, "Back", false); - } - - M5.update(); - - if (M5.BtnB.wasPressed()) { - break; - } - - ez.yield(); - delay(100); - } while (true); -} - -/** - * Read GPS enable setting. - */ -bool load_gps_enable() { - Preferences prefs; - - prefs.begin(FURBLE_STR, true); - bool enable = prefs.getBool(PREFS_GPS, false); - prefs.end(); - - return enable; -} - -/** - * Save GPS enable setting. - */ -static void save_gps_enable(bool enable) { - Preferences prefs; - - prefs.begin(FURBLE_STR, false); - prefs.putBool(PREFS_GPS, enable); - prefs.end(); -} - -bool gps_onoff(ezMenu *menu) { - gps_enable = !gps_enable; - menu->setCaption("onoff", "GPS\t" + (String)(gps_enable ? "ON" : "OFF")); - save_gps_enable(gps_enable); - - return true; -} - -/** - * GPS settings menu. - */ - ezMenu submenu(FURBLE_STR " - GPS settings"); +void settings_load_interval(interval_t *interval); +void settings_save_interval(interval_t *interval); - submenu.buttons("OK#down"); - submenu.addItem("onoff | GPS\t" + (String)(gps_enable ? "ON" : "OFF"), NULL, gps_onoff); - submenu.addItem("GPS Data", show_gps_info); - submenu.downOnLast("first"); - submenu.addItem("Back"); - submenu.run(); -} #endif diff --git a/include/spinner.h b/include/spinner.h index da42faf..9ef7df4 100644 --- a/include/spinner.h +++ b/include/spinner.h @@ -8,7 +8,7 @@ enum spin_unit_t { SPIN_UNIT_MIN = 3 }; -struct SpinValue { +struct __attribute__((packed)) SpinValue { uint16_t value; spin_unit_t unit; }; diff --git a/src/interval.cpp b/src/interval.cpp index 0859d87..99083d0 100644 --- a/src/interval.cpp +++ b/src/interval.cpp @@ -2,60 +2,164 @@ #include #include "interval.h" +#include "settings.h" #include "spinner.h" -static SpinValue interval_count = { 10, SPIN_UNIT_NIL }; -static SpinValue interval_delay = { 10, SPIN_UNIT_SEC }; -static SpinValue interval_shutter = { 50, SPIN_UNIT_MS }; +static interval_t interval; static bool configure_count(ezMenu *menu) { - spinner_modify_value("Count", false, &interval_count); - menu->setCaption("interval_count", "Count\t" + sv2str(&interval_count)); + spinner_modify_value("Count", false, &interval.count); + menu->setCaption("interval_count", "Count\t" + sv2str(&interval.count)); + settings_save_interval(&interval); return true; } static bool configure_delay(ezMenu *menu) { - spinner_modify_value("Delay", true, &interval_delay); - menu->setCaption("interval_delay", "Delay\t" + sv2str(&interval_delay)); + spinner_modify_value("Delay", true, &interval.delay); + menu->setCaption("interval_delay", "Delay\t" + sv2str(&interval.delay)); + settings_save_interval(&interval); return true; } static bool configure_shutter(ezMenu *menu) { - spinner_modify_value("Shutter", true, &interval_shutter); - menu->setCaption("interval_shutter", "Shutter\t" + sv2str(&interval_shutter)); + spinner_modify_value("Shutter", true, &interval.shutter); + menu->setCaption("interval_shutter", "Shutter\t" + sv2str(&interval.shutter)); + settings_save_interval(&interval); return true; } -static void do_interval(Furble::Device *device, SpinValue *count, SpinValue *idelay, SpinValue *shutter) { - const unsigned int initial_count = count->value; - const unsigned long initial_delay = sv2ms(idelay); - const unsigned long initial_shutter = sv2ms(shutter); +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, unsigned int config_count, unsigned long now, unsigned long next) { + static unsigned long prev_update_ms = 0; + + if ((now - prev_update_ms) < 500) { + // Don't update if less than 500ms + return; + } + + 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); + char hms[32] = { 0x0 }; + snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, config_count, rem_h, rem_m, rem_s); + Serial.println(hms); + + prev_update_ms = now; + ez.msgBox((String)statestr, (String)hms, "Stop", false); +} + +static void do_interval(Furble::Device *device, interval_t *interval) { + const unsigned int config_count = interval->count.value; + 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; - bool shooting = true; + ez.backlight.inactivity(NEVER); do { + now = millis(); + M5.update(); + + switch (state) { + case INTERVAL_SHUTTER_OPEN: + if (icount < config_count) { + //device->shutterPress(); + Serial.println("Shutter Open"); + 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++; + //device->shutterRelease(); + Serial.println("Shutter Release"); + next = now + config_delay; + if (icount < config_count) { + 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) { + //device->shutterRelease(); + Serial.print("Shutter Release"); + } + active = false; + } + ez.yield(); + display_interval_msg(state, icount, config_count, now, next); delay(10); - } while (shooting); - device->focusPress(); - delay(250); - device->shutterPress(); - delay(50); - - ez.msgBox("Interval Release", String(initial_count), "Stop", false); - device->shutterRelease(); + } while (active); + ez.backlight.inactivity(USER_SET); } void remote_interval(Furble::Device *device) { + settings_load_interval(&interval); + ezMenu submenu(FURBLE_STR " - Interval"); submenu.buttons("OK#down"); submenu.addItem("Start"); - submenu.addItem("interval_count | Count\t" + sv2str(&interval_count), NULL, configure_count); - submenu.addItem("interval_delay | Delay\t" + sv2str(&interval_delay), NULL, configure_delay); - submenu.addItem("interval_shutter | Shutter\t" + sv2str(&interval_shutter), NULL, configure_shutter); + submenu.addItem("interval_count | Count\t" + sv2str(&interval.count), NULL, configure_count); + submenu.addItem("interval_delay | Delay\t" + sv2str(&interval.delay), NULL, configure_delay); + submenu.addItem("interval_shutter | Shutter\t" + sv2str(&interval.shutter), NULL, configure_shutter); submenu.addItem("Back"); submenu.downOnLast("first"); @@ -63,7 +167,7 @@ void remote_interval(Furble::Device *device) { int16_t i = submenu.runOnce(); if (submenu.pickName() == "Start") { - do_interval(device, &interval_count, &interval_delay, &interval_shutter); + do_interval(device, &interval); } if (i == 0) { diff --git a/src/settings.cpp b/src/settings.cpp index 5d6903e..a8f99ac 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2,10 +2,11 @@ #include #include -#include #include #include +#include +#include "interval.h" #include "settings.h" extern TinyGPSPlus gps; @@ -13,6 +14,7 @@ extern bool gps_enable; const char *PREFS_TX_POWER = "txpower"; const char *PREFS_GPS = "gps"; +const char *PREFS_INTERVAL= "interval"; /** * Save BLE transmit power to preferences. @@ -162,3 +164,29 @@ void settings_menu_gps(void) { submenu.addItem("Back"); submenu.run(); } + +void settings_load_interval(interval_t *interval) { + Preferences prefs; + + prefs.begin(FURBLE_STR, true); + size_t len = prefs.getBytes(PREFS_INTERVAL, interval, sizeof(interval_t)); + if (len != sizeof(interval_t)) { + // default values + interval->count.value = INTERVAL_DEFAULT_COUNT; + interval->count.unit = INTERVAL_DEFAULT_COUNT_UNIT; + interval->shutter.value = INTERVAL_DEFAULT_SHUTTER; + interval->shutter.unit = INTERVAL_DEFAULT_SHUTTER_UNIT; + interval->delay.value = INTERVAL_DEFAULT_DELAY; + interval->delay.unit = INTERVAL_DEFAULT_DELAY_UNIT; + } + + prefs.end(); +} + +void settings_save_interval(interval_t *interval) { + Preferences prefs; + + prefs.begin(FURBLE_STR, false); + prefs.putBytes(PREFS_INTERVAL, interval, sizeof(interval_t)); + prefs.end(); +} diff --git a/src/spinner.cpp b/src/spinner.cpp index c101e21..77faada 100644 --- a/src/spinner.cpp +++ b/src/spinner.cpp @@ -119,7 +119,6 @@ static void spinner_preset(const char *title, SpinValue *sv) { ok = true; break; case 1: -Serial.println(n); n++; if (n >= PRESET_NUM) { n = 0; From 626955973eff38d42a1bf0de9b6e9a94e520fced Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:42:15 +1030 Subject: [PATCH 07/10] Add infinite count support. --- include/interval.h | 2 +- include/spinner.h | 9 ++-- src/interval.cpp | 124 +++++++++++++++++++++++++++++++++------------ src/settings.cpp | 2 +- src/spinner.cpp | 61 +++++++++------------- 5 files changed, 122 insertions(+), 76 deletions(-) diff --git a/include/interval.h b/include/interval.h index c60df96..4e69710 100644 --- a/include/interval.h +++ b/include/interval.h @@ -6,7 +6,7 @@ #define INTERVAL_DEFAULT_COUNT 10 #define INTERVAL_DEFAULT_COUNT_UNIT SPIN_UNIT_NIL -#define INTERVAL_DEFAULT_SHUTTER 50 +#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 diff --git a/include/spinner.h b/include/spinner.h index 9ef7df4..de589a6 100644 --- a/include/spinner.h +++ b/include/spinner.h @@ -2,10 +2,11 @@ #define SPINNER_H enum spin_unit_t { - SPIN_UNIT_NIL = 0, - SPIN_UNIT_MS = 1, - SPIN_UNIT_SEC = 2, - SPIN_UNIT_MIN = 3 + 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 { diff --git a/src/interval.cpp b/src/interval.cpp index 99083d0..1a5c325 100644 --- a/src/interval.cpp +++ b/src/interval.cpp @@ -8,15 +8,51 @@ static interval_t interval; static bool configure_count(ezMenu *menu) { - spinner_modify_value("Count", false, &interval.count); - menu->setCaption("interval_count", "Count\t" + sv2str(&interval.count)); + ezMenu submenu("Count"); + submenu.buttons("OK#down"); + submenu.addItem("Custom"); + submenu.addItem("Infinite"); + submenu.downOnLast("first"); + + submenu.runOnce(); + if (submenu.pickName() == "Custom") { + interval.count.unit = SPIN_UNIT_NIL; + spinner_modify_value("Count", false, &interval.count); + } + + if (submenu.pickName() == "Infinite") { + interval.count.unit = SPIN_UNIT_INF; + } + + String countstr = sv2str(&interval.count); + if (interval.count.unit == SPIN_UNIT_INF) { + countstr = "INF"; + } + + menu->setCaption("interval_count", "Count\t" + countstr); settings_save_interval(&interval); return true; } static bool configure_delay(ezMenu *menu) { - spinner_modify_value("Delay", true, &interval.delay); + ezMenu submenu("Delay"); + submenu.buttons("OK#down"); + submenu.addItem("Custom"); + submenu.addItem("Preset"); + submenu.downOnLast("first"); + + bool preset; + + submenu.runOnce(); + if (submenu.pickName() == "Custom") { + preset = false; + } + if (submenu.pickName() == "Preset") { + preset = true; + } + + spinner_modify_value("Delay", preset, &interval.delay); menu->setCaption("interval_delay", "Delay\t" + sv2str(&interval.delay)); settings_save_interval(&interval); @@ -24,7 +60,23 @@ static bool configure_delay(ezMenu *menu) { } static bool configure_shutter(ezMenu *menu) { - spinner_modify_value("Shutter", true, &interval.shutter); + ezMenu submenu("Shutter"); + submenu.buttons("OK#down"); + submenu.addItem("Custom"); + submenu.addItem("Preset"); + submenu.downOnLast("first"); + + bool preset; + + submenu.runOnce(); + if (submenu.pickName() == "Custom") { + preset = false; + } + if (submenu.pickName() == "Preset") { + preset = true; + } + + spinner_modify_value("Shutter", preset, &interval.shutter); menu->setCaption("interval_shutter", "Shutter\t" + sv2str(&interval.shutter)); settings_save_interval(&interval); @@ -39,7 +91,11 @@ enum interval_state_t { INTERVAL_EXIT = 4 }; -static void display_interval_msg(interval_state_t state, unsigned int count, unsigned int config_count, unsigned long now, unsigned long next) { +static void display_interval_msg(interval_state_t state, + unsigned int count, + SpinValue *sv_count, + unsigned long now, + unsigned long next) { static unsigned long prev_update_ms = 0; if ((now - prev_update_ms) < 500) { @@ -65,12 +121,16 @@ static void display_interval_msg(interval_state_t state, unsigned int count, uns unsigned long rem = next - now; if (now > next) { - rem = 0; + rem = 0; } ms2hms(rem, &rem_h, &rem_m, &rem_s); - char hms[32] = { 0x0 }; - snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, config_count, rem_h, rem_m, rem_s); + char hms[32] = {0x0}; + if (sv_count->unit == SPIN_UNIT_INF) { + snprintf(hms, 32, "%09u|%02u:%02u:%02u", count, rem_h, rem_m, rem_s); + } else { + snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, sv_count->value, rem_h, rem_m, rem_s); + } Serial.println(hms); prev_update_ms = now; @@ -78,7 +138,6 @@ static void display_interval_msg(interval_state_t state, unsigned int count, uns } static void do_interval(Furble::Device *device, interval_t *interval) { - const unsigned int config_count = interval->count.value; const unsigned long config_delay = sv2ms(&interval->delay); const unsigned long config_shutter = sv2ms(&interval->shutter); @@ -101,51 +160,51 @@ static void do_interval(Furble::Device *device, interval_t *interval) { switch (state) { case INTERVAL_SHUTTER_OPEN: - if (icount < config_count) { - //device->shutterPress(); - Serial.println("Shutter Open"); - next = now + config_shutter; - state = INTERVAL_SHUTTER_WAIT; + if ((icount < interval->count.value) || (interval->count.unit == SPIN_UNIT_INF)) { + // device->shutterPress(); + Serial.println("Shutter Open"); + next = now + config_shutter; + state = INTERVAL_SHUTTER_WAIT; } else { state = INTERVAL_EXIT; - } + } break; case INTERVAL_SHUTTER_WAIT: - if (now > next) { + if (now > next) { state = INTERVAL_SHUTTER_CLOSE; - } - break; + } + break; case INTERVAL_SHUTTER_CLOSE: - icount++; - //device->shutterRelease(); + icount++; + // device->shutterRelease(); Serial.println("Shutter Release"); - next = now + config_delay; - if (icount < config_count) { - state = INTERVAL_DELAY; - } else { + 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) { + if (now > next) { state = INTERVAL_SHUTTER_OPEN; - } + } break; case INTERVAL_EXIT: - active = false; + active = false; break; } if (M5.BtnB.wasClicked()) { if (state == INTERVAL_SHUTTER_WAIT) { - //device->shutterRelease(); - Serial.print("Shutter Release"); + // device->shutterRelease(); + Serial.print("Shutter Release"); } active = false; } ez.yield(); - display_interval_msg(state, icount, config_count, now, next); + display_interval_msg(state, icount, &interval->count, now, next); delay(10); } while (active); ez.backlight.inactivity(USER_SET); @@ -159,7 +218,8 @@ void remote_interval(Furble::Device *device) { submenu.addItem("Start"); submenu.addItem("interval_count | Count\t" + sv2str(&interval.count), NULL, configure_count); submenu.addItem("interval_delay | Delay\t" + sv2str(&interval.delay), NULL, configure_delay); - submenu.addItem("interval_shutter | Shutter\t" + sv2str(&interval.shutter), NULL, configure_shutter); + submenu.addItem("interval_shutter | Shutter\t" + sv2str(&interval.shutter), NULL, + configure_shutter); submenu.addItem("Back"); submenu.downOnLast("first"); diff --git a/src/settings.cpp b/src/settings.cpp index a8f99ac..d9305ea 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -14,7 +14,7 @@ extern bool gps_enable; const char *PREFS_TX_POWER = "txpower"; const char *PREFS_GPS = "gps"; -const char *PREFS_INTERVAL= "interval"; +const char *PREFS_INTERVAL = "interval"; /** * Save BLE transmit power to preferences. diff --git a/src/spinner.cpp b/src/spinner.cpp index 77faada..1ef39ff 100644 --- a/src/spinner.cpp +++ b/src/spinner.cpp @@ -1,55 +1,34 @@ #include -#include #include +#include #include "spinner.h" -static const char *unit2str[4] = { - " ", // SPIN_UNIT_NIL - "msec", // SPIN_UNIT_MS - "secs", // SPIN_UNIT_SEC - "mins" }; // SPIN_UNIT_MIN +static const char *unit2str[5] = {" ", // SPIN_UNIT_NIL + " ", // SPIN_UNIT_INF + "msec", // SPIN_UNIT_MS + "secs", // SPIN_UNIT_SEC + "mins"}; // SPIN_UNIT_MIN #define PRESET_NUM 10 -static uint16_t spin_preset[PRESET_NUM] = { - 1, - 2, - 4, - 8, - 15, - 30, - 60, - 125, - 250, - 500 }; - +static uint16_t spin_preset[PRESET_NUM] = {1, 2, 4, 8, 15, 30, 60, 125, 250, 500}; #define FMT_NONE_LEN (4) -static const char *fmt_none[FMT_NONE_LEN] = { - " %1u %1u %1u ", - "[%1u] %1u %1u ", - " %1u [%1u] %1u ", - " %1u %1u [%1u]" }; +static const char *fmt_none[FMT_NONE_LEN] = {" %1u %1u %1u ", "[%1u] %1u %1u ", + " %1u [%1u] %1u ", " %1u %1u [%1u]"}; #define FMT_UNIT_LEN (5) -static const char *fmt_unit[FMT_UNIT_LEN] = { - " %1u %1u %1u %4s ", - "[%1u] %1u %1u %4s ", - " %1u [%1u] %1u %4s ", - " %1u %1u [%1u] %4s ", - " %1u %1u %1u [%4s]" }; +static const char *fmt_unit[FMT_UNIT_LEN] = {" %1u %1u %1u %4s ", "[%1u] %1u %1u %4s ", + " %1u [%1u] %1u %4s ", " %1u %1u [%1u] %4s ", + " %1u %1u %1u [%4s]"}; #define FMT_PRESET_NONE_LEN (2) -static const char *fmt_preset_none[FMT_PRESET_NONE_LEN] = { - " %1u %1u %1u ", - "[%1u %1u %1u]" }; +static const char *fmt_preset_none[FMT_PRESET_NONE_LEN] = {" %1u %1u %1u ", "[%1u %1u %1u]"}; #define FMT_PRESET_UNIT_LEN (3) static const char *fmt_preset_unit[FMT_PRESET_UNIT_LEN] = { - " %1u %1u %1u %4s ", - "[%1u %1u %1u] %4s ", - " %1u %1u %1u [%4s]" }; + " %1u %1u %1u %4s ", "[%1u %1u %1u] %4s ", " %1u %1u %1u [%4s]"}; #define SPIN_ROW_LEN (32) @@ -65,8 +44,14 @@ static uint16_t htu2value(unsigned int h, unsigned int t, unsigned int u) { return (h * 100) + (t * 10) + u; } -static void display_spinner(const char *title, bool preset, unsigned int index, unsigned int h, unsigned int t, unsigned int u, spin_unit_t unit) { - char spin_row[SPIN_ROW_LEN] = { 0x0 }; +static void display_spinner(const char *title, + bool preset, + unsigned int index, + unsigned int h, + unsigned int t, + unsigned int u, + spin_unit_t unit) { + char spin_row[SPIN_ROW_LEN] = {0x0}; if (preset) { if (unit == SPIN_UNIT_NIL) { @@ -102,7 +87,7 @@ static void spinner_preset(const char *title, SpinValue *sv) { unsigned int u; // find closest preset equal or lower - for (n = (PRESET_NUM-1); n > 0; n--) { + for (n = (PRESET_NUM - 1); n > 0; n--) { if (value >= spin_preset[n]) { break; } From 1831e73271d290e85268bcae470e7cd2a0f843c6 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:00:06 +1030 Subject: [PATCH 08/10] Make it drive the camera. --- src/furble.ino | 5 ----- src/interval.cpp | 14 +++++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/furble.ino b/src/furble.ino index 026c70e..cf461e4 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -377,14 +377,9 @@ void setup() { pScan->setWindow(6553); } -static void test_interval(void) { - remote_interval(nullptr); -} - void loop() { ezMenu mainmenu(FURBLE_STR); mainmenu.buttons("OK#down"); - mainmenu.addItem("Interval", test_interval); mainmenu.addItem("Connect", do_saved); mainmenu.addItem("Scan", do_scan); mainmenu.addItem("Delete Saved", menu_delete); diff --git a/src/interval.cpp b/src/interval.cpp index 1a5c325..ee64a4c 100644 --- a/src/interval.cpp +++ b/src/interval.cpp @@ -131,7 +131,7 @@ static void display_interval_msg(interval_state_t state, } else { snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, sv_count->value, rem_h, rem_m, rem_s); } - Serial.println(hms); + // Serial.println(hms); prev_update_ms = now; ez.msgBox((String)statestr, (String)hms, "Stop", false); @@ -161,8 +161,8 @@ static void do_interval(Furble::Device *device, interval_t *interval) { switch (state) { case INTERVAL_SHUTTER_OPEN: if ((icount < interval->count.value) || (interval->count.unit == SPIN_UNIT_INF)) { - // device->shutterPress(); - Serial.println("Shutter Open"); + // Serial.println("Shutter Open"); + device->shutterPress(); next = now + config_shutter; state = INTERVAL_SHUTTER_WAIT; } else { @@ -176,8 +176,8 @@ static void do_interval(Furble::Device *device, interval_t *interval) { break; case INTERVAL_SHUTTER_CLOSE: icount++; - // device->shutterRelease(); - Serial.println("Shutter Release"); + // Serial.println("Shutter Release"); + device->shutterRelease(); next = now + config_delay; if ((icount < interval->count.value) || (interval->count.unit == SPIN_UNIT_INF)) { state = INTERVAL_DELAY; @@ -197,8 +197,8 @@ static void do_interval(Furble::Device *device, interval_t *interval) { if (M5.BtnB.wasClicked()) { if (state == INTERVAL_SHUTTER_WAIT) { - // device->shutterRelease(); - Serial.print("Shutter Release"); + // Serial.print("Shutter Release"); + device->shutterRelease(); } active = false; } From 2a6aa524a27219f46a5491746787358ffdb0c28d Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Fri, 8 Mar 2024 13:43:45 +1030 Subject: [PATCH 09/10] Avoid full canvas redraw within msgBox. Only redraw the lines if they change and msgBox has been asked to avoid a full clear/redraw. --- lib/M5ez/src/M5ez.cpp | 10 ++++++++-- lib/M5ez/src/M5ez.h | 3 ++- src/interval.cpp | 21 ++++++++++----------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/M5ez/src/M5ez.cpp b/lib/M5ez/src/M5ez.cpp index 9d90c7e..e01f0e0 100644 --- a/lib/M5ez/src/M5ez.cpp +++ b/lib/M5ez/src/M5ez.cpp @@ -2308,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; @@ -2331,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) { diff --git a/lib/M5ez/src/M5ez.h b/lib/M5ez/src/M5ez.h index f52ed94..b8da919 100644 --- a/lib/M5ez/src/M5ez.h +++ b/lib/M5ez/src/M5ez.h @@ -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 = ""); diff --git a/src/interval.cpp b/src/interval.cpp index 2988f8e..596ade0 100644 --- a/src/interval.cpp +++ b/src/interval.cpp @@ -97,13 +97,6 @@ static void display_interval_msg(interval_state_t state, SpinValue *sv_count, unsigned long now, unsigned long next) { - static unsigned long prev_update_ms = 0; - - if ((now - prev_update_ms) < 500) { - // Don't update if less than 500ms - return; - } - unsigned int rem_h = 0; unsigned int rem_m = 0; unsigned int rem_s = 0; @@ -126,16 +119,22 @@ static void display_interval_msg(interval_state_t state, } 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) { - snprintf(hms, 32, "%09u|%02u:%02u:%02u", count, rem_h, rem_m, rem_s); + len = snprintf(hms, 32, "%09u|%02u:%02u:%02u", count, rem_h, rem_m, rem_s); } else { - snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, sv_count->value, rem_h, rem_m, rem_s); + len = + snprintf(hms, 32, "%03u/%03u|%02u:%02u:%02u", count, sv_count->value, rem_h, rem_m, rem_s); } // Serial.println(hms); - prev_update_ms = now; - ez.msgBox((String)statestr, (String)hms, "Stop", false); + 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) { From d2c0efbdde6ea1fce1bb36e553d24d14d5d861e7 Mon Sep 17 00:00:00 2001 From: Guo-Rong <5484552+gkoh@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:06:14 +1030 Subject: [PATCH 10/10] Refactor intervalometer settings. Intervalometer settings can now be configured in both 'Settings' and the 'Interval' menu. --- include/settings.h | 5 +++ src/furble.ino | 1 + src/interval.cpp | 85 +-------------------------------------- src/settings.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 84 deletions(-) diff --git a/include/settings.h b/include/settings.h index 9d77d90..4447160 100644 --- a/include/settings.h +++ b/include/settings.h @@ -5,6 +5,8 @@ #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); @@ -12,4 +14,7 @@ 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 diff --git a/src/furble.ino b/src/furble.ino index 2901e28..d53270b 100644 --- a/src/furble.ino +++ b/src/furble.ino @@ -373,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); diff --git a/src/interval.cpp b/src/interval.cpp index 596ade0..4bc3740 100644 --- a/src/interval.cpp +++ b/src/interval.cpp @@ -6,84 +6,6 @@ #include "settings.h" #include "spinner.h" -static interval_t interval; - -static bool configure_count(ezMenu *menu) { - ezMenu submenu("Count"); - submenu.buttons("OK#down"); - submenu.addItem("Custom"); - submenu.addItem("Infinite"); - submenu.downOnLast("first"); - - submenu.runOnce(); - if (submenu.pickName() == "Custom") { - interval.count.unit = SPIN_UNIT_NIL; - spinner_modify_value("Count", false, &interval.count); - } - - if (submenu.pickName() == "Infinite") { - interval.count.unit = SPIN_UNIT_INF; - } - - String countstr = sv2str(&interval.count); - if (interval.count.unit == SPIN_UNIT_INF) { - countstr = "INF"; - } - - menu->setCaption("interval_count", "Count\t" + countstr); - settings_save_interval(&interval); - - return true; -} - -static bool configure_delay(ezMenu *menu) { - ezMenu submenu("Delay"); - submenu.buttons("OK#down"); - submenu.addItem("Custom"); - submenu.addItem("Preset"); - submenu.downOnLast("first"); - - bool preset; - - submenu.runOnce(); - if (submenu.pickName() == "Custom") { - preset = false; - } - if (submenu.pickName() == "Preset") { - preset = true; - } - - spinner_modify_value("Delay", preset, &interval.delay); - menu->setCaption("interval_delay", "Delay\t" + sv2str(&interval.delay)); - settings_save_interval(&interval); - - return true; -} - -static bool configure_shutter(ezMenu *menu) { - ezMenu submenu("Shutter"); - submenu.buttons("OK#down"); - submenu.addItem("Custom"); - submenu.addItem("Preset"); - submenu.downOnLast("first"); - - bool preset; - - submenu.runOnce(); - if (submenu.pickName() == "Custom") { - preset = false; - } - if (submenu.pickName() == "Preset") { - preset = true; - } - - spinner_modify_value("Shutter", preset, &interval.shutter); - menu->setCaption("interval_shutter", "Shutter\t" + sv2str(&interval.shutter)); - settings_save_interval(&interval); - - return true; -} - enum interval_state_t { INTERVAL_SHUTTER_OPEN = 0, INTERVAL_SHUTTER_WAIT = 1, @@ -216,15 +138,10 @@ static void do_interval(FurbleCtx *fctx, interval_t *interval) { } void remote_interval(FurbleCtx *fctx) { - settings_load_interval(&interval); - ezMenu submenu(FURBLE_STR " - Interval"); submenu.buttons("OK#down"); submenu.addItem("Start"); - submenu.addItem("interval_count | Count\t" + sv2str(&interval.count), NULL, configure_count); - submenu.addItem("interval_delay | Delay\t" + sv2str(&interval.delay), NULL, configure_delay); - submenu.addItem("interval_shutter | Shutter\t" + sv2str(&interval.shutter), NULL, - configure_shutter); + settings_add_interval_items(&submenu); submenu.addItem("Back"); submenu.downOnLast("first"); diff --git a/src/settings.cpp b/src/settings.cpp index d9305ea..263f6f2 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -16,6 +16,11 @@ const char *PREFS_TX_POWER = "txpower"; const char *PREFS_GPS = "gps"; const char *PREFS_INTERVAL = "interval"; +/** + * Global intervalometer configuration. + */ +interval_t interval; + /** * Save BLE transmit power to preferences. */ @@ -190,3 +195,97 @@ void settings_save_interval(interval_t *interval) { prefs.putBytes(PREFS_INTERVAL, interval, sizeof(interval_t)); prefs.end(); } + +static bool configure_count(ezMenu *menu) { + ezMenu submenu("Count"); + submenu.buttons("OK#down"); + submenu.addItem("Custom"); + submenu.addItem("Infinite"); + submenu.downOnLast("first"); + + submenu.runOnce(); + if (submenu.pickName() == "Custom") { + interval.count.unit = SPIN_UNIT_NIL; + spinner_modify_value("Count", false, &interval.count); + } + + if (submenu.pickName() == "Infinite") { + interval.count.unit = SPIN_UNIT_INF; + } + + String countstr = sv2str(&interval.count); + if (interval.count.unit == SPIN_UNIT_INF) { + countstr = "INF"; + } + + menu->setCaption("interval_count", "Count\t" + countstr); + settings_save_interval(&interval); + + return true; +} + +static bool configure_delay(ezMenu *menu) { + ezMenu submenu("Delay"); + submenu.buttons("OK#down"); + submenu.addItem("Custom"); + submenu.addItem("Preset"); + submenu.downOnLast("first"); + + bool preset; + + submenu.runOnce(); + if (submenu.pickName() == "Custom") { + preset = false; + } + if (submenu.pickName() == "Preset") { + preset = true; + } + + spinner_modify_value("Delay", preset, &interval.delay); + menu->setCaption("interval_delay", "Delay\t" + sv2str(&interval.delay)); + settings_save_interval(&interval); + + return true; +} + +static bool configure_shutter(ezMenu *menu) { + ezMenu submenu("Shutter"); + submenu.buttons("OK#down"); + submenu.addItem("Custom"); + submenu.addItem("Preset"); + submenu.downOnLast("first"); + + bool preset; + + submenu.runOnce(); + if (submenu.pickName() == "Custom") { + preset = false; + } + if (submenu.pickName() == "Preset") { + preset = true; + } + + spinner_modify_value("Shutter", preset, &interval.shutter); + menu->setCaption("interval_shutter", "Shutter\t" + sv2str(&interval.shutter)); + settings_save_interval(&interval); + + return true; +} + +void settings_add_interval_items(ezMenu *submenu) { + settings_load_interval(&interval); + + submenu->addItem("interval_count | Count\t" + sv2str(&interval.count), NULL, configure_count); + submenu->addItem("interval_delay | Delay\t" + sv2str(&interval.delay), NULL, configure_delay); + submenu->addItem("interval_shutter | Shutter\t" + sv2str(&interval.shutter), NULL, + configure_shutter); +} + +void settings_menu_interval(void) { + ezMenu submenu(FURBLE_STR " - Intervalometer settings"); + submenu.buttons("OK#down"); + settings_add_interval_items(&submenu); + submenu.addItem("Back"); + submenu.downOnLast("first"); + submenu.run(); +}