From 90f94c30774a1bcbe107ffa8b06a392416936fe6 Mon Sep 17 00:00:00 2001 From: Adam Wonak Date: Sun, 4 May 2025 12:00:46 -0700 Subject: [PATCH] Add a trivial example script and some formatting. --- README.md | 103 +++++++++++++++++++++++ button.h | 2 + examples/hardware_test/hardware_test.ino | 86 +++++++++++++++++++ gravity.cpp | 2 - 4 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 examples/hardware_test/hardware_test.ino diff --git a/README.md b/README.md index adb9e8a..aac097a 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,112 @@ 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. +## Installation + +Download or git clone this repository into your Arduino > libraries folder. + +Common directory locations: + +* [Windows] `C:\Users\{username}\Documents\Arduino\libraries` +* [Mac] `/Users/{username}/Documents/Arduino/libraries` +* [Linux] `~/Arduino/libraries` + ## Required Third-party Libraries * [uClock](https://github.com/midilab/uClock) [MIT] - 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. * [Adafruit_GFX](https://github.com/adafruit/Adafruit-GFX-Library) [BSD] - Graphics helper library. * [Adafruit_SSD1306](https://github.com/adafruit/Adafruit_SSD1306) [BSD] - Library for interacting with the SSD1306 OLED display. + +## 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. + +```cpp +#include "gravity.h" + +byte idx = 0; +bool reversed = false; +bool freeze = false; +byte selected_param = 0; + +// Initialize the gravity library and attach your handlers in the setup method. +void setup() { + // Initialize Gravity. + gravity.Init(); + + // Attach handlers. + gravity.clock.AttachIntHandler(IntClock); + gravity.encoder.AttachRotateHandler(HandleRotate); + gravity.encoder.AttachPressHandler(ChangeSelectedParam); + gravity.play_button.AttachPressHandler(HandlePlayPressed); + + // Initial state. + gravity.outputs[idx].High(); +} + +// The loop method must always call `gravity.Process()` to read any peripherial state changes. +void loop() { + gravity.Process(); + freeze = gravity.shift_button.On(); + UpdateDisplay(); +} + +// The rest of the code is your apps logic! + +void IntClock(uint32_t tick) { + if (tick % 12 == 0 && ! freeze) { + gravity.outputs[idx].Low(); + if (reversed) { + idx = (idx == 0) ? OUTPUT_COUNT - 1 : idx - 1; + } else { + idx = (idx + 1) % OUTPUT_COUNT; + } + gravity.outputs[idx].High(); + } +} + +void HandlePlayPressed() { + gravity.clock.Pause(); + if (gravity.clock.IsPaused()) { + for (int i = 0; i < OUTPUT_COUNT; i++) { + gravity.outputs[i].Low(); + } + } +} + +void HandleRotate(Direction dir, int val) { + if (selected_param == 0) { + gravity.clock.SetTempo(gravity.clock.Tempo() + val); + } else if (selected_param == 1) { + reversed = (dir == DIRECTION_DECREMENT); + } +} + +void ChangeSelectedParam() { + selected_param = (selected_param + 1) % 2; +} + +void UpdateDisplay() { + gravity.display.clearDisplay(); + + if (freeze) { + gravity.display.setCursor(42, 30); + gravity.display.print("FREEZE!"); + gravity.display.display(); + return; + } + + gravity.display.setCursor(10, 0); + gravity.display.print("Tempo: "); + gravity.display.print(gravity.clock.Tempo()); + + gravity.display.setCursor(10, 10); + gravity.display.print("Direction: "); + gravity.display.print((reversed) ? "Backward" : "Forward"); + + gravity.display.drawChar(0, selected_param * 10, 0x10, 1, 0, 1); + + gravity.display.display(); +} +``` diff --git a/button.h b/button.h index 631f094..1235afa 100644 --- a/button.h +++ b/button.h @@ -15,6 +15,8 @@ #include +const uint8_t DEBOUNCE_MS = 10; + class Button { protected: typedef void (*CallbackFunction)(void); diff --git a/examples/hardware_test/hardware_test.ino b/examples/hardware_test/hardware_test.ino new file mode 100644 index 0000000..5f3d345 --- /dev/null +++ b/examples/hardware_test/hardware_test.ino @@ -0,0 +1,86 @@ +#include "gravity.h" + +byte idx = 0; +bool reversed = false; +bool freeze = false; +byte selected_param = 0; + +// Initialize the gravity library and attach your handlers in the setup method. +void setup() { + // Initialize Gravity. + gravity.Init(); + + // Attach handlers. + gravity.clock.AttachIntHandler(IntClock); + gravity.encoder.AttachRotateHandler(HandleRotate); + gravity.encoder.AttachPressHandler(ChangeSelectedParam); + gravity.play_button.AttachPressHandler(HandlePlayPressed); + + // Initial state. + gravity.outputs[idx].High(); +} + +// The loop method must always call `gravity.Process()` to read any peripherial state changes. +void loop() { + gravity.Process(); + freeze = gravity.shift_button.On(); + UpdateDisplay(); +} + +// The rest of the code is your apps logic! + +void IntClock(uint32_t tick) { + if (tick % 12 == 0 && ! freeze) { + gravity.outputs[idx].Low(); + if (reversed) { + idx = (idx == 0) ? OUTPUT_COUNT - 1 : idx - 1; + } else { + idx = (idx + 1) % OUTPUT_COUNT; + } + gravity.outputs[idx].High(); + } +} + +void HandlePlayPressed() { + gravity.clock.Pause(); + if (gravity.clock.IsPaused()) { + for (int i = 0; i < OUTPUT_COUNT; i++) { + gravity.outputs[i].Low(); + } + } +} + +void HandleRotate(Direction dir, int val) { + if (selected_param == 0) { + gravity.clock.SetTempo(gravity.clock.Tempo() + val); + } else if (selected_param == 1) { + reversed = (dir == DIRECTION_DECREMENT); + } +} + +void ChangeSelectedParam() { + selected_param = (selected_param + 1) % 2; +} + +void UpdateDisplay() { + gravity.display.clearDisplay(); + + if (freeze) { + gravity.display.setCursor(42, 30); + gravity.display.print("FREEZE!"); + gravity.display.display(); + return; + } + + gravity.display.setCursor(10, 0); + gravity.display.print("Tempo: "); + gravity.display.print(gravity.clock.Tempo()); + + gravity.display.setCursor(10, 10); + gravity.display.print("Direction: "); + gravity.display.print((reversed) ? "Backward" : "Forward"); + + gravity.display.drawChar(0, selected_param * 10, 0x10, 1, 0, 1); + + gravity.display.display(); +} \ No newline at end of file diff --git a/gravity.cpp b/gravity.cpp index b62fac8..5851fd6 100644 --- a/gravity.cpp +++ b/gravity.cpp @@ -57,8 +57,6 @@ void Gravity::InitDisplay() { display.display(); } - - void Gravity::Process() { // Read peripherials for changes. shift_button.Process();