summary refs log tree commit diff
diff options
context:
space:
mode:
authorStarfall <us@starfall.systems>2025-03-04 12:34:48 -0600
committerStarfall <us@starfall.systems>2025-03-04 12:34:48 -0600
commit5a4bf238055478d8ec53b5ff59f1f150cfc6a01e (patch)
treee9f7761ad8eb4cd10eeed197cb02e16234a3eb9b
Initial commit HEAD main
-rw-r--r--blade.cpp80
-rw-r--r--blade.h11
-rw-r--r--input.cpp74
-rw-r--r--input.h13
-rw-r--r--saber.ino29
5 files changed, 207 insertions, 0 deletions
diff --git a/blade.cpp b/blade.cpp
new file mode 100644
index 0000000..381c1dc
--- /dev/null
+++ b/blade.cpp
@@ -0,0 +1,80 @@
+#include "blade.h"
+
+#define LED_PIN 6
+#define LED_COUNT 32
+
+CRGB blade[LED_COUNT];
+CHSV blade_color = CHSV(144, 255, 216);
+
+#define BLADE_OFF 0
+#define BLADE_ON 1
+#define BLADE_IGNITE 2
+#define BLADE_RETRACT 3
+uint8_t state = BLADE_OFF;
+
+#define IGNITE_MS 200
+#define RETRACT_MS 400
+uint32_t anim_start;
+
+void setup_blade() {
+  FastLED.addLeds<WS2812B, LED_PIN, GRB>(blade, LED_COUNT);
+}
+
+void draw_blade() {
+  int num_lit = LED_COUNT;
+
+  switch(state) {
+    case BLADE_OFF:
+      return;
+
+    case BLADE_ON:
+      if(blade[0] == blade_color) return;
+      break;
+
+    case BLADE_IGNITE:
+      num_lit = (millis() - anim_start) * LED_COUNT / IGNITE_MS;
+      if(num_lit >= LED_COUNT) {
+        state = BLADE_ON;
+        num_lit = LED_COUNT;
+      }
+      break;
+
+    case BLADE_RETRACT:
+      num_lit = LED_COUNT - (millis() - anim_start) * LED_COUNT / RETRACT_MS;
+      if(num_lit <= 0) {
+        state = BLADE_OFF;
+        num_lit = 0;
+      }
+      break;
+  }
+
+  for(int i = 0; i < num_lit; i++) {
+    blade[i] = CHSV(blade_color.h - i, blade_color.s - i - i, blade_color.v);
+  }
+  for(int i = num_lit; i < LED_COUNT; i++) {
+    blade[i] = CRGB::Black;
+  }
+  FastLED.show();
+}
+
+// good blade colors
+// skywalker blue (136, 255, 216)
+// blue-white (144, 128, 216)
+
+void change_hue(uint8_t d) {
+  blade_color.h += d;
+}
+
+void change_sat(uint8_t d) {
+  blade_color.s += d;
+}
+
+void toggle_blade() {
+  if(state == BLADE_OFF) {
+    state = BLADE_IGNITE;
+    anim_start = millis();
+  } else {
+    state = BLADE_RETRACT;
+    anim_start = millis();
+  }
+}
\ No newline at end of file
diff --git a/blade.h b/blade.h
new file mode 100644
index 0000000..0e7e34a
--- /dev/null
+++ b/blade.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <Arduino.h>
+#include <FastLED.h>
+
+void setup_blade();
+void draw_blade();
+
+void toggle_blade();
+void change_hue(uint8_t);
+void change_sat(uint8_t);
diff --git a/input.cpp b/input.cpp
new file mode 100644
index 0000000..ec95b1e
--- /dev/null
+++ b/input.cpp
@@ -0,0 +1,74 @@
+#include "input.h"
+
+#define CLK_PIN 2
+#define DT_PIN 3
+#define SW_PIN 4
+
+Encoder encoder(CLK_PIN, DT_PIN);
+void setup_input() {
+  pinMode(SW_PIN, INPUT_PULLUP);
+}
+
+#define KNOB_DIR -1
+uint32_t read_knob() {
+  uint32_t rotate = KNOB_DIR * encoder.read() / 4;
+  if(rotate != 0) {
+    encoder.write(0);
+  }
+  return rotate;
+}
+
+// button physical state
+#define PRESSED 0
+#define RELEASED 1
+uint8_t pstate = RELEASED;
+#define DEBOUNCE_CYCLES 3
+uint8_t debounce;
+
+uint8_t debounced_read() {
+  byte read = digitalRead(SW_PIN);
+  if (read != pstate && ++debounce >= DEBOUNCE_CYCLES) {
+    pstate = read;
+    debounce = 0;
+  }
+  return pstate;
+}
+
+// button logical state
+#define DOWN 0
+#define UP 1
+#define LONGPRESS 2
+uint8_t lstate = UP;
+
+#define LONGPRESS_MS 500
+uint32_t pressed_at;
+
+uint8_t read_button() {
+  uint8_t read = debounced_read();
+
+  switch(lstate) {
+    case UP:
+      if(read == PRESSED) {
+        lstate = DOWN;
+        pressed_at = millis();
+      }
+      break;
+
+    case DOWN:
+      if(millis() - pressed_at >= LONGPRESS_MS) {
+        lstate = LONGPRESS;
+        return EVENT_HOLD;
+      }
+      if(read == RELEASED) {
+        lstate = UP;
+        return EVENT_CLICK;
+      }
+      break;
+
+    case LONGPRESS:
+      if(read == RELEASED) lstate = UP;
+      break;
+  }
+
+  return EVENT_NONE;
+}
diff --git a/input.h b/input.h
new file mode 100644
index 0000000..a772835
--- /dev/null
+++ b/input.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <Arduino.h>
+#include <Encoder.h>
+
+void setup_input();
+
+uint32_t read_knob();
+
+#define EVENT_NONE 0
+#define EVENT_CLICK 1
+#define EVENT_HOLD 2
+uint8_t read_button();
diff --git a/saber.ino b/saber.ino
new file mode 100644
index 0000000..3ee7ca4
--- /dev/null
+++ b/saber.ino
@@ -0,0 +1,29 @@
+#include "input.h"
+#include "blade.h"
+
+// TODO for simple storage #include <EEPROM.h>
+
+void setup() {
+  Serial.begin(9600);
+  Serial.println("Hello, world!");
+
+  setup_input();
+  setup_blade();
+}
+
+void loop() {
+  uint32_t knob = read_knob();
+  if(knob != 0) {
+    change_hue(16 * knob);
+  }
+
+  byte button = read_button();
+  if(button == EVENT_CLICK) {
+    toggle_blade();
+  }
+  if(button == EVENT_HOLD) {
+    Serial.println("long press");
+  }
+
+  draw_blade();
+}