Compare commits
7 Commits
reduce-mem
...
8bb89a5f4b
| Author | SHA1 | Date | |
|---|---|---|---|
| 8bb89a5f4b | |||
| 499bc7a643 | |||
| 3f670fa9f7 | |||
| b5029bde88 | |||
| 4bcd618073 | |||
| 6ada2aba30 | |||
| c5965aa1f7 |
29
README.md
29
README.md
@ -1,6 +1,18 @@
|
|||||||
# Sitka Instruments Gravity Firmware Abstraction
|
# Sitka Instruments Gravity Firmware Abstraction
|
||||||
|
|
||||||
This library helps make writing firmware easier by abstracting away the initialization and peripheral interactions. Now your firmware code can just focus on the logic and behavior of the app, and keep the low level code neatly tucked away in this library.
|
This library helps make writing firmware for the [Sitka Instruments Gravity](https://sitkainstruments.com/gravity/) eurorack module easier by abstracting away the initialization and peripheral interactions. Now your firmware code can just focus on the logic and behavior of the app, and keep the low level code neatly tucked away in this library.
|
||||||
|
|
||||||
|
The latest releases of all Sitka Instruments Gravity firmware builds can be found on the [Updater](https://sitkainstruments.com/gravity/updater/) page. You can use this page to flash the latest build directly to the Arduino Nano on the back of your module.
|
||||||
|
|
||||||
|
## Project Code Layout
|
||||||
|
|
||||||
|
* [`src/`](src/) - **libGravity**: This is the hardware abstraction library used to simplify the creation of new Gravity module firmware by providing common reusable wrappers around the module peripherials like [DigitalOutput](src/digital_output.h#L18) providing methods like [`Update(uint8_t state)`](src/digital_output.h#L45) which allow you to set that output channel voltage high or low, and common module behavior like [Clock](src/clock.h#L30) which provides handlers like [`AttachExtHandler(callback)`](src/clock.h#L69) which takes a callback function to handle external clock tick behavior when receiving clock trigger.
|
||||||
|
|
||||||
|
* [`firmware/Gravity`](firmware/Gravity/) - **Alt Gravity**: This is the implementation of the default 6-channel trigger/gate clock modulation firmware. This is a full rewrite of the original firmware designed to use `libGravity` with a focus on open source friendlines.
|
||||||
|
|
||||||
|
* `firmware/GridSeq` - **GridSeq**: Comming Soon.
|
||||||
|
|
||||||
|
* [`examples/skeleton`](examples/skeleton/skeleton.ino) - **Skeleton**: This is the bare bones scaffloding for a `libGravity` firmware app.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -17,13 +29,14 @@ Common directory locations:
|
|||||||
* [uClock](https://github.com/midilab/uClock) [MIT] - (Included with this repo) Handle clock tempo, external clock input, and internal clock timer handler.
|
* [uClock](https://github.com/midilab/uClock) [MIT] - (Included with this repo) Handle clock tempo, external clock input, and internal clock timer handler.
|
||||||
* [RotateEncoder](https://github.com/mathertel/RotaryEncoder) [BSD] - Library for reading and interpreting encoder rotation.
|
* [RotateEncoder](https://github.com/mathertel/RotaryEncoder) [BSD] - Library for reading and interpreting encoder rotation.
|
||||||
* [U8g2](https://github.com/olikraus/u8g2/) [MIT] - Graphics helper library.
|
* [U8g2](https://github.com/olikraus/u8g2/) [MIT] - Graphics helper library.
|
||||||
|
* [NeoHWSerial](https://github.com/SlashDevin/NeoHWSerial) [GPL] - Hardware serial library with attachInterrupt.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Here's a trivial example showing some of the ways to interact with the library. This script rotates the active clock channel according to the set tempo. The encoder can change the temo or rotation direction. The play/pause button will toggle the clock activity on or off. The shift button will freeze the clock from advancing the channel rotation.
|
Here's a trivial example showing some of the ways to interact with the library. This script rotates the active clock channel according to the set tempo. The encoder can change the temo or rotation direction. The play/pause button will toggle the clock activity on or off. The shift button will freeze the clock from advancing the channel rotation.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include "gravity.h"
|
#include "libGravity.h"
|
||||||
|
|
||||||
byte idx = 0;
|
byte idx = 0;
|
||||||
bool reversed = false;
|
bool reversed = false;
|
||||||
@ -75,11 +88,11 @@ void HandlePlayPressed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleRotate(Direction dir, int val) {
|
void HandleRotate(int val) {
|
||||||
if (selected_param == 0) {
|
if (selected_param == 0) {
|
||||||
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
||||||
} else if (selected_param == 1) {
|
} else if (selected_param == 1) {
|
||||||
reversed = (dir == DIRECTION_DECREMENT);
|
reversed = (val < 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +124,14 @@ void UpdateDisplay() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Building New Firmware Using libGravity**
|
||||||
|
|
||||||
|
When starting a new firmware sketch you can use the [skeleton](examples/skeleton/skeleton.ino) app as a place to start.
|
||||||
|
|
||||||
|
**Building New Firmware from scratch**
|
||||||
|
|
||||||
|
If you do not want to use the libGravity hardware abstraction library and want to roll your own vanilla firmware, take a look at the [peripherials.h](src/peripherials.h) file for the pinout definitions used by the module.
|
||||||
|
|
||||||
### Build for release
|
### Build for release
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
* TODO: Store the calibration value in EEPROM.
|
* TODO: Store the calibration value in EEPROM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gravity.h"
|
#include "libGravity.h"
|
||||||
|
|
||||||
#define TEXT_FONT u8g2_font_profont11_tf
|
#define TEXT_FONT u8g2_font_profont11_tf
|
||||||
#define INDICATOR_FONT u8g2_font_open_iconic_arrow_1x_t
|
#define INDICATOR_FONT u8g2_font_open_iconic_arrow_1x_t
|
||||||
@ -43,7 +43,7 @@ void NextCalibrationPoint() {
|
|||||||
selected_param = (selected_param + 1) % 6;
|
selected_param = (selected_param + 1) % 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibrateCV(Direction dir, int val) {
|
void CalibrateCV(int val) {
|
||||||
AnalogInput* cv = (selected_param > 2) ? &gravity.cv2 : &gravity.cv1;
|
AnalogInput* cv = (selected_param > 2) ? &gravity.cv2 : &gravity.cv1;
|
||||||
switch (selected_param % 3) {
|
switch (selected_param % 3) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gravity.h"
|
#include "libGravity.h"
|
||||||
|
|
||||||
#define TEXT_FONT u8g2_font_profont11_tf
|
#define TEXT_FONT u8g2_font_profont11_tf
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ void NextCalibrationPoint() {
|
|||||||
selected_param = (selected_param + 1) % 2;
|
selected_param = (selected_param + 1) % 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalibrateCV(Direction dir, int val) {
|
void CalibrateCV(int val) {
|
||||||
// AnalogInput* cv = (selected_param > 2) ? &gravity.cv2 : &gravity.cv1;
|
// AnalogInput* cv = (selected_param > 2) ? &gravity.cv2 : &gravity.cv1;
|
||||||
AnalogInput* cv = &gravity.cv1;
|
AnalogInput* cv = &gravity.cv1;
|
||||||
switch (selected_param % 2) {
|
switch (selected_param % 2) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#include "gravity.h"
|
#include "libGravity.h"
|
||||||
|
|
||||||
byte idx = 0;
|
byte idx = 0;
|
||||||
bool reversed = false;
|
bool reversed = false;
|
||||||
@ -33,28 +33,28 @@ void IntClock(uint32_t tick) {
|
|||||||
if (tick % 12 == 0 && ! freeze) {
|
if (tick % 12 == 0 && ! freeze) {
|
||||||
gravity.outputs[idx].Low();
|
gravity.outputs[idx].Low();
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
idx = (idx == 0) ? OUTPUT_COUNT - 1 : idx - 1;
|
idx = (idx == 0) ? Gravity::OUTPUT_COUNT - 1 : idx - 1;
|
||||||
} else {
|
} else {
|
||||||
idx = (idx + 1) % OUTPUT_COUNT;
|
idx = (idx + 1) % Gravity::OUTPUT_COUNT;
|
||||||
}
|
}
|
||||||
gravity.outputs[idx].High();
|
gravity.outputs[idx].High();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandlePlayPressed() {
|
void HandlePlayPressed() {
|
||||||
gravity.clock.Pause();
|
gravity.clock.Stop();
|
||||||
if (gravity.clock.IsPaused()) {
|
if (gravity.clock.IsPaused()) {
|
||||||
for (int i = 0; i < OUTPUT_COUNT; i++) {
|
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
|
||||||
gravity.outputs[i].Low();
|
gravity.outputs[i].Low();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleRotate(Direction dir, int val) {
|
void HandleRotate(int val) {
|
||||||
if (selected_param == 0) {
|
if (selected_param == 0) {
|
||||||
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
gravity.clock.SetTempo(gravity.clock.Tempo() + val);
|
||||||
} else if (selected_param == 1) {
|
} else if (selected_param == 1) {
|
||||||
reversed = (dir == DIRECTION_DECREMENT);
|
reversed = (val < 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ void UpdateDisplay() {
|
|||||||
gravity.display.print("Direction: ");
|
gravity.display.print("Direction: ");
|
||||||
gravity.display.print((reversed) ? "Backward" : "Forward");
|
gravity.display.print((reversed) ? "Backward" : "Forward");
|
||||||
|
|
||||||
gravity.display.drawChar(0, selected_param * 10, 0x10, 1, 0, 1);
|
gravity.display.drawStr(0, selected_param * 10, "x");
|
||||||
|
|
||||||
gravity.display.display();
|
gravity.display.display();
|
||||||
}
|
}
|
||||||
118
examples/skeleton/skeleton.ino
Normal file
118
examples/skeleton/skeleton.ino
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* @file skeleton.ino
|
||||||
|
* @author YOUR_NAME (<url>)
|
||||||
|
* @brief Skeleton app for Sitka Instruments Gravity.
|
||||||
|
* @version vX.Y.Z - MONTH YEAR YOUR_NAME
|
||||||
|
* @date YYYY-MM-DD
|
||||||
|
*
|
||||||
|
* @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com
|
||||||
|
*
|
||||||
|
* Skeleton app for basic structure of a new firmware for Sitka Instruments
|
||||||
|
* Gravity using the libGravity library.
|
||||||
|
*
|
||||||
|
* ENCODER:
|
||||||
|
* Press: change between selecting a parameter and editing the parameter.
|
||||||
|
* Hold & Rotate: change current selected output channel.
|
||||||
|
*
|
||||||
|
* BTN1:
|
||||||
|
* Play/pause - start or stop the internal clock.
|
||||||
|
*
|
||||||
|
* BTN2:
|
||||||
|
* Shift - hold and rotate encoder to change current selected output channel.
|
||||||
|
*
|
||||||
|
* EXT:
|
||||||
|
* External clock input. When Gravity is set to INTERNAL or MIDI clock
|
||||||
|
* source, this input is used to reset clocks.
|
||||||
|
*
|
||||||
|
* CV1:
|
||||||
|
* External analog input used to provide modulation to any channel parameter.
|
||||||
|
*
|
||||||
|
* CV2:
|
||||||
|
* External analog input used to provide modulation to any channel parameter.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libGravity.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Global state for settings and app behavior.
|
||||||
|
struct AppState {
|
||||||
|
int tempo = Clock::DEFAULT_TEMPO;
|
||||||
|
Clock::Source selected_source = Clock::SOURCE_INTERNAL;
|
||||||
|
// Add app specific state variables here.
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState app;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Arduino setup and loop.
|
||||||
|
//
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Start Gravity.
|
||||||
|
gravity.Init();
|
||||||
|
|
||||||
|
// Clock handlers.
|
||||||
|
gravity.clock.AttachIntHandler(HandleIntClockTick);
|
||||||
|
gravity.clock.AttachExtHandler(HandleExtClockTick);
|
||||||
|
|
||||||
|
// Encoder rotate and press handlers.
|
||||||
|
gravity.encoder.AttachPressHandler(HandleEncoderPressed);
|
||||||
|
gravity.encoder.AttachRotateHandler(HandleRotate);
|
||||||
|
gravity.encoder.AttachPressRotateHandler(HandlePressedRotate);
|
||||||
|
|
||||||
|
// Button press handlers.
|
||||||
|
gravity.play_button.AttachPressHandler(HandlePlayPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Process change in state of inputs and outputs.
|
||||||
|
gravity.Process();
|
||||||
|
|
||||||
|
// Non-ISR loop behavior.
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Firmware handlers for clocks.
|
||||||
|
//
|
||||||
|
|
||||||
|
void HandleIntClockTick(uint32_t tick) {
|
||||||
|
bool refresh = false;
|
||||||
|
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
|
||||||
|
// Process each output tick handlers.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleExtClockTick() {
|
||||||
|
switch (app.selected_source) {
|
||||||
|
case Clock::SOURCE_INTERNAL:
|
||||||
|
case Clock::SOURCE_EXTERNAL_MIDI:
|
||||||
|
// Use EXT as Reset when not used for clock source.
|
||||||
|
gravity.clock.Reset();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Register EXT cv clock tick.
|
||||||
|
gravity.clock.Tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// UI handlers for encoder and buttons.
|
||||||
|
//
|
||||||
|
|
||||||
|
void HandlePlayPressed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleEncoderPressed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleRotate(int val) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePressedRotate(int val) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Application logic goes here.
|
||||||
|
//
|
||||||
@ -66,10 +66,6 @@ void setup() {
|
|||||||
// Start Gravity.
|
// Start Gravity.
|
||||||
gravity.Init();
|
gravity.Init();
|
||||||
|
|
||||||
// Show bootsplash when initializing firmware.
|
|
||||||
Bootsplash();
|
|
||||||
delay(2000);
|
|
||||||
|
|
||||||
// Initialize the state manager. This will load settings from EEPROM
|
// Initialize the state manager. This will load settings from EEPROM
|
||||||
stateManager.initialize(app);
|
stateManager.initialize(app);
|
||||||
InitGravity(app);
|
InitGravity(app);
|
||||||
@ -213,31 +209,34 @@ void HandleEncoderPressed() {
|
|||||||
// Check if leaving editing mode should apply a selection.
|
// Check if leaving editing mode should apply a selection.
|
||||||
if (app.editing_param) {
|
if (app.editing_param) {
|
||||||
if (app.selected_channel == 0) { // main page
|
if (app.selected_channel == 0) { // main page
|
||||||
// TODO: rewrite as switch
|
switch (app.selected_param) {
|
||||||
if (app.selected_param == PARAM_MAIN_ENCODER_DIR) {
|
case PARAM_MAIN_ENCODER_DIR:
|
||||||
app.encoder_reversed = app.selected_sub_param == 1;
|
app.encoder_reversed = app.selected_sub_param == 1;
|
||||||
gravity.encoder.SetReverseDirection(app.encoder_reversed);
|
gravity.encoder.SetReverseDirection(app.encoder_reversed);
|
||||||
}
|
break;
|
||||||
if (app.selected_param == PARAM_MAIN_SAVE_DATA) {
|
case PARAM_MAIN_ROTATE_DISP:
|
||||||
|
app.rotate_display = app.selected_sub_param == 1;
|
||||||
|
gravity.display.setFlipMode(app.rotate_display ? 1 : 0);
|
||||||
|
break;
|
||||||
|
case PARAM_MAIN_SAVE_DATA:
|
||||||
if (app.selected_sub_param < StateManager::MAX_SAVE_SLOTS) {
|
if (app.selected_sub_param < StateManager::MAX_SAVE_SLOTS) {
|
||||||
app.selected_save_slot = app.selected_sub_param;
|
app.selected_save_slot = app.selected_sub_param;
|
||||||
stateManager.saveData(app);
|
stateManager.saveData(app);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if (app.selected_param == PARAM_MAIN_LOAD_DATA) {
|
case PARAM_MAIN_LOAD_DATA:
|
||||||
if (app.selected_sub_param < StateManager::MAX_SAVE_SLOTS) {
|
if (app.selected_sub_param < StateManager::MAX_SAVE_SLOTS) {
|
||||||
app.selected_save_slot = app.selected_sub_param;
|
app.selected_save_slot = app.selected_sub_param;
|
||||||
stateManager.loadData(app, app.selected_save_slot);
|
stateManager.loadData(app, app.selected_save_slot);
|
||||||
InitGravity(app);
|
InitGravity(app);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if (app.selected_param == PARAM_MAIN_FACTORY_RESET) {
|
case PARAM_MAIN_FACTORY_RESET:
|
||||||
if (app.selected_sub_param == 0) { // Erase
|
if (app.selected_sub_param == 0) { // Erase
|
||||||
// Show bootsplash during slow erase operation.
|
|
||||||
Bootsplash();
|
|
||||||
stateManager.factoryReset(app);
|
stateManager.factoryReset(app);
|
||||||
InitGravity(app);
|
InitGravity(app);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Only mark dirty and reset selected_sub_param when leaving editing mode.
|
// Only mark dirty and reset selected_sub_param when leaving editing mode.
|
||||||
@ -313,6 +312,7 @@ void editMainParameter(int val) {
|
|||||||
}
|
}
|
||||||
// These changes are applied upon encoder button press.
|
// These changes are applied upon encoder button press.
|
||||||
case PARAM_MAIN_ENCODER_DIR:
|
case PARAM_MAIN_ENCODER_DIR:
|
||||||
|
case PARAM_MAIN_ROTATE_DISP:
|
||||||
updateSelection(app.selected_sub_param, val, 2);
|
updateSelection(app.selected_sub_param, val, 2);
|
||||||
break;
|
break;
|
||||||
case PARAM_MAIN_SAVE_DATA:
|
case PARAM_MAIN_SAVE_DATA:
|
||||||
@ -381,6 +381,7 @@ void InitGravity(AppState& app) {
|
|||||||
gravity.clock.SetTempo(app.tempo);
|
gravity.clock.SetTempo(app.tempo);
|
||||||
gravity.clock.SetSource(app.selected_source);
|
gravity.clock.SetSource(app.selected_source);
|
||||||
gravity.encoder.SetReverseDirection(app.encoder_reversed);
|
gravity.encoder.SetReverseDirection(app.encoder_reversed);
|
||||||
|
gravity.display.setFlipMode(app.rotate_display ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetOutputs() {
|
void ResetOutputs() {
|
||||||
|
|||||||
@ -31,6 +31,7 @@ struct AppState {
|
|||||||
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
|
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
|
||||||
bool editing_param = false;
|
bool editing_param = false;
|
||||||
bool encoder_reversed = false;
|
bool encoder_reversed = false;
|
||||||
|
bool rotate_display = false;
|
||||||
bool refresh_screen = true;
|
bool refresh_screen = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,7 @@ class Channel {
|
|||||||
|
|
||||||
void setClockMod(int index) {
|
void setClockMod(int index) {
|
||||||
base_clock_mod_index = constrain(index, 0, MOD_CHOICE_SIZE - 1);
|
base_clock_mod_index = constrain(index, 0, MOD_CHOICE_SIZE - 1);
|
||||||
|
_recalculatePulses();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProbability(int prob) {
|
void setProbability(int prob) {
|
||||||
@ -94,13 +95,16 @@ class Channel {
|
|||||||
|
|
||||||
void setDutyCycle(int duty) {
|
void setDutyCycle(int duty) {
|
||||||
base_duty_cycle = constrain(duty, 1, 99);
|
base_duty_cycle = constrain(duty, 1, 99);
|
||||||
|
_recalculatePulses();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOffset(int off) {
|
void setOffset(int off) {
|
||||||
base_offset = constrain(off, 0, 99);
|
base_offset = constrain(off, 0, 99);
|
||||||
|
_recalculatePulses();
|
||||||
}
|
}
|
||||||
void setSwing(int val) {
|
void setSwing(int val) {
|
||||||
base_swing = constrain(val, 50, 95);
|
base_swing = constrain(val, 50, 95);
|
||||||
|
_recalculatePulses();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Euclidean
|
// Euclidean
|
||||||
|
|||||||
@ -104,6 +104,7 @@ enum ParamsMainPage : uint8_t {
|
|||||||
PARAM_MAIN_RESET,
|
PARAM_MAIN_RESET,
|
||||||
PARAM_MAIN_PULSE,
|
PARAM_MAIN_PULSE,
|
||||||
PARAM_MAIN_ENCODER_DIR,
|
PARAM_MAIN_ENCODER_DIR,
|
||||||
|
PARAM_MAIN_ROTATE_DISP,
|
||||||
PARAM_MAIN_SAVE_DATA,
|
PARAM_MAIN_SAVE_DATA,
|
||||||
PARAM_MAIN_LOAD_DATA,
|
PARAM_MAIN_LOAD_DATA,
|
||||||
PARAM_MAIN_FACTORY_RESET,
|
PARAM_MAIN_FACTORY_RESET,
|
||||||
@ -256,9 +257,6 @@ void DisplayMainPage() {
|
|||||||
case Clock::SOURCE_EXTERNAL_PPQN_4:
|
case Clock::SOURCE_EXTERNAL_PPQN_4:
|
||||||
subText = F("4 PPQN");
|
subText = F("4 PPQN");
|
||||||
break;
|
break;
|
||||||
case Clock::SOURCE_EXTERNAL_PPQN_2:
|
|
||||||
subText = F("2 PPQN");
|
|
||||||
break;
|
|
||||||
case Clock::SOURCE_EXTERNAL_PPQN_1:
|
case Clock::SOURCE_EXTERNAL_PPQN_1:
|
||||||
subText = F("1 PPQN");
|
subText = F("1 PPQN");
|
||||||
break;
|
break;
|
||||||
@ -316,6 +314,10 @@ void DisplayMainPage() {
|
|||||||
mainText = F("DIR");
|
mainText = F("DIR");
|
||||||
subText = app.selected_sub_param == 0 ? F("DEFAULT") : F("REVERSED");
|
subText = app.selected_sub_param == 0 ? F("DEFAULT") : F("REVERSED");
|
||||||
break;
|
break;
|
||||||
|
case PARAM_MAIN_ROTATE_DISP:
|
||||||
|
mainText = F("ROT");
|
||||||
|
subText = app.selected_sub_param == 0 ? F("DEFAULT") : F("FLIPPED");
|
||||||
|
break;
|
||||||
case PARAM_MAIN_SAVE_DATA:
|
case PARAM_MAIN_SAVE_DATA:
|
||||||
case PARAM_MAIN_LOAD_DATA:
|
case PARAM_MAIN_LOAD_DATA:
|
||||||
if (app.selected_sub_param == StateManager::MAX_SAVE_SLOTS) {
|
if (app.selected_sub_param == StateManager::MAX_SAVE_SLOTS) {
|
||||||
@ -347,7 +349,7 @@ void DisplayMainPage() {
|
|||||||
drawCenteredText(subText.c_str(), SUB_TEXT_Y, TEXT_FONT);
|
drawCenteredText(subText.c_str(), SUB_TEXT_Y, TEXT_FONT);
|
||||||
|
|
||||||
// Draw Main Page menu items
|
// Draw Main Page menu items
|
||||||
String menu_items[PARAM_MAIN_LAST] = {F("TEMPO"), F("SOURCE"), F("CLK RUN"), F("CLK RESET"), F("PULSE OUT"), F("ENCODER DIR"), F("SAVE"), F("LOAD"), F("ERASE")};
|
String menu_items[PARAM_MAIN_LAST] = {F("TEMPO"), F("SOURCE"), F("CLK RUN"), F("CLK RESET"), F("PULSE OUT"), F("ENCODER DIR"), F("ROTATE DISP"), F("SAVE"), F("LOAD"), F("ERASE")};
|
||||||
drawMenuItems(menu_items, PARAM_MAIN_LAST);
|
drawMenuItems(menu_items, PARAM_MAIN_LAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +371,7 @@ void DisplayChannelPage() {
|
|||||||
|
|
||||||
switch (app.selected_param) {
|
switch (app.selected_param) {
|
||||||
case PARAM_CH_MOD: {
|
case PARAM_CH_MOD: {
|
||||||
int mod_value = withCvMod ? ch.getClockModWithMod(cv1, cv2): ch.getClockMod();
|
int mod_value = withCvMod ? ch.getClockModWithMod(cv1, cv2) : ch.getClockMod();
|
||||||
if (mod_value > 1) {
|
if (mod_value > 1) {
|
||||||
mainText = F("/");
|
mainText = F("/");
|
||||||
mainText += String(mod_value);
|
mainText += String(mod_value);
|
||||||
@ -497,22 +499,4 @@ void UpdateDisplay() {
|
|||||||
} while (gravity.display.nextPage());
|
} while (gravity.display.nextPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bootsplash() {
|
|
||||||
gravity.display.firstPage();
|
|
||||||
do {
|
|
||||||
int textWidth;
|
|
||||||
String loadingText = F("LOADING....");
|
|
||||||
gravity.display.setFont(TEXT_FONT);
|
|
||||||
|
|
||||||
textWidth = gravity.display.getStrWidth(StateManager::SKETCH_NAME);
|
|
||||||
gravity.display.drawStr(16 + (textWidth / 2), 20, StateManager::SKETCH_NAME);
|
|
||||||
|
|
||||||
textWidth = gravity.display.getStrWidth(StateManager::SEMANTIC_VERSION);
|
|
||||||
gravity.display.drawStr(16 + (textWidth / 2), 32, StateManager::SEMANTIC_VERSION);
|
|
||||||
|
|
||||||
textWidth = gravity.display.getStrWidth(loadingText.c_str());
|
|
||||||
gravity.display.drawStr(26 + (textWidth / 2), 44, loadingText.c_str());
|
|
||||||
} while (gravity.display.nextPage());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DISPLAY_H
|
#endif // DISPLAY_H
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
// Define the constants for the current firmware.
|
// Define the constants for the current firmware.
|
||||||
const char StateManager::SKETCH_NAME[] = "ALT GRAVITY";
|
const char StateManager::SKETCH_NAME[] = "ALT GRAVITY";
|
||||||
const char StateManager::SEMANTIC_VERSION[] = "V2.0.0BETA4"; // NOTE: This should match the version in the library.properties file.
|
const char StateManager::SEMANTIC_VERSION[] = "2.0.0"; // NOTE: This should match the version in the library.properties file.
|
||||||
|
|
||||||
// Number of available save slots.
|
// Number of available save slots.
|
||||||
const byte StateManager::MAX_SAVE_SLOTS = 10;
|
const byte StateManager::MAX_SAVE_SLOTS = 10;
|
||||||
@ -212,6 +212,7 @@ void StateManager::_saveMetadata(const AppState& app) {
|
|||||||
// Global user settings
|
// Global user settings
|
||||||
current_meta.selected_save_slot = app.selected_save_slot;
|
current_meta.selected_save_slot = app.selected_save_slot;
|
||||||
current_meta.encoder_reversed = app.encoder_reversed;
|
current_meta.encoder_reversed = app.encoder_reversed;
|
||||||
|
current_meta.rotate_display = app.rotate_display;
|
||||||
|
|
||||||
EEPROM.put(METADATA_START_ADDR, current_meta);
|
EEPROM.put(METADATA_START_ADDR, current_meta);
|
||||||
interrupts();
|
interrupts();
|
||||||
|
|||||||
@ -52,11 +52,12 @@ class StateManager {
|
|||||||
|
|
||||||
// This struct holds the data that identifies the firmware version.
|
// This struct holds the data that identifies the firmware version.
|
||||||
struct Metadata {
|
struct Metadata {
|
||||||
char sketch_name[16];
|
char sketch_name[12];
|
||||||
char version[16];
|
char version[5];
|
||||||
// Additional global/hardware settings
|
// Additional global/hardware settings
|
||||||
byte selected_save_slot;
|
byte selected_save_slot;
|
||||||
bool encoder_reversed;
|
bool encoder_reversed;
|
||||||
|
bool rotate_display;
|
||||||
};
|
};
|
||||||
struct ChannelState {
|
struct ChannelState {
|
||||||
byte base_clock_mod_index;
|
byte base_clock_mod_index;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name=libGravity
|
name=libGravity
|
||||||
version=2.0.0beta3
|
version=2.0.0
|
||||||
author=Adam Wonak
|
author=Adam Wonak
|
||||||
maintainer=awonak <github.com/awonak>
|
maintainer=awonak <github.com/awonak>
|
||||||
sentence=Hardware abstraction library for Sitka Instruments Gravity eurorack module
|
sentence=Hardware abstraction library for Sitka Instruments Gravity eurorack module
|
||||||
|
|||||||
@ -35,7 +35,6 @@ class Clock {
|
|||||||
SOURCE_INTERNAL,
|
SOURCE_INTERNAL,
|
||||||
SOURCE_EXTERNAL_PPQN_24,
|
SOURCE_EXTERNAL_PPQN_24,
|
||||||
SOURCE_EXTERNAL_PPQN_4,
|
SOURCE_EXTERNAL_PPQN_4,
|
||||||
SOURCE_EXTERNAL_PPQN_2,
|
|
||||||
SOURCE_EXTERNAL_PPQN_1,
|
SOURCE_EXTERNAL_PPQN_1,
|
||||||
SOURCE_EXTERNAL_MIDI,
|
SOURCE_EXTERNAL_MIDI,
|
||||||
SOURCE_LAST,
|
SOURCE_LAST,
|
||||||
@ -43,9 +42,9 @@ class Clock {
|
|||||||
|
|
||||||
enum Pulse {
|
enum Pulse {
|
||||||
PULSE_NONE,
|
PULSE_NONE,
|
||||||
PULSE_PPQN_1,
|
|
||||||
PULSE_PPQN_4,
|
|
||||||
PULSE_PPQN_24,
|
PULSE_PPQN_24,
|
||||||
|
PULSE_PPQN_4,
|
||||||
|
PULSE_PPQN_1,
|
||||||
PULSE_LAST,
|
PULSE_LAST,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,10 +97,6 @@ class Clock {
|
|||||||
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
||||||
uClock.setInputPPQN(uClock.PPQN_4);
|
uClock.setInputPPQN(uClock.PPQN_4);
|
||||||
break;
|
break;
|
||||||
case SOURCE_EXTERNAL_PPQN_2:
|
|
||||||
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
|
||||||
uClock.setInputPPQN(uClock.PPQN_2);
|
|
||||||
break;
|
|
||||||
case SOURCE_EXTERNAL_PPQN_1:
|
case SOURCE_EXTERNAL_PPQN_1:
|
||||||
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
uClock.setClockMode(uClock.EXTERNAL_CLOCK);
|
||||||
uClock.setInputPPQN(uClock.PPQN_1);
|
uClock.setInputPPQN(uClock.PPQN_1);
|
||||||
|
|||||||
Reference in New Issue
Block a user