forked from W4D/soundcube-firmware
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a16ac73d9b | |||
| ad9c71363b | |||
| 8691bc05eb | |||
| 6ceefa6705 | |||
| ab0dbe98a1 | |||
| 202b862280 | |||
| 4b728fffd0 | |||
| 559d92e526 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build
|
||||
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Soundcube Firmware
|
||||
|
||||
## Install new firmware
|
||||
|
||||
1. Go to [Releases](https://code.w4d.dev/W4D/soundcube-firmware/releases)
|
||||
2. Download latest `soundcube-firmware.ino.uf2` file
|
||||
3. On your Soundcube Board: Press and hold the outer most white button as seen from the USB-C socket
|
||||
4. While holding the button press the inner most white button once
|
||||
- the board will go into bootloader mode and appear as USB thumb drive
|
||||
6. Copy the `soundcube-firmware.ino.uf2` file to the thumb drive and wait for the board to restart
|
||||
|
||||
## Code something yourself
|
||||
|
||||
1. Download [Arduino IDE](https://www.arduino.cc/en/software/)
|
||||
2. Install and open "Preferences"
|
||||
3. Follow these [instructions](https://github.com/earlephilhower/arduino-pico?tab=readme-ov-file#installation)
|
||||
- No need to change checkboxes, only paste the URL and hit OK.
|
||||
4. Install **Raspberry Pi Pico** boards ([HowTo install boards](https://support.arduino.cc/hc/en-us/articles/360016119519-Add-boards-to-Arduino-IDE))
|
||||
5. Install libraries ([HowTo install libraries](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-installing-a-library/)): Adafruit Neopixel, AS5600 (Rob Tillaart), TCA9555 (Rob Tillaart)
|
||||
6. Choose **Generic RP2350** as board and the correct port (Mac: /dev/cu.usbmodemXXXX, PC: COMXX Serial Port)
|
||||
7. Go to **Tools -> Flash Size** and choose **16MB (no FS)**
|
||||
8. Make your changes in the code
|
||||
9. Hit Upload Button (Arduino IDE compiles and uploads it automatically)
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <ArduinoJson.hpp>
|
||||
|
||||
#include <TCA9555.h>
|
||||
#include <AS5600.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include <PWMAudio.h>
|
||||
#include <I2S.h>
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
|
||||
#define HAPTIC 0
|
||||
#define AURAL 0
|
||||
#define HAPTIC 1
|
||||
#define AURAL 1
|
||||
#define UI_SAMPLERATE 22050
|
||||
|
||||
I2S i2s(INPUT_PULLUP);
|
||||
@ -17,8 +22,8 @@ PWMAudio ui_snd(8);
|
||||
|
||||
enum BUTTON {CVINL, CVINR, INL, INR, OUTR, OUTL, CVOUTR, CVOUTL, RIGHT, LEFT, SELECT, DEBUG1, DEBUG2, DEBUG3};
|
||||
|
||||
Adafruit_NeoPixel edge_pixels(8, 4, NEO_GRB + NEO_KHZ800);
|
||||
Adafruit_NeoPixel ui_pixels(72, 5, NEO_GRB + NEO_KHZ800);
|
||||
Adafruit_NeoPixel edge_pixels(11, 4, NEO_GRB + NEO_KHZ800);
|
||||
Adafruit_NeoPixel ui_pixels(74, 5, NEO_GRB + NEO_KHZ800);
|
||||
|
||||
volatile bool flag = false;
|
||||
volatile bool click = false;
|
||||
@ -29,16 +34,48 @@ int counter = 0;
|
||||
bool buttons[16] = {false};
|
||||
|
||||
int16_t beep[UI_SAMPLERATE];
|
||||
uint16_t beep_length = 0;
|
||||
|
||||
bool sd_card_detected = false;
|
||||
|
||||
struct Config {
|
||||
int edge_color_r = 0;
|
||||
int edge_color_g = 0;
|
||||
int edge_color_b = 50;
|
||||
};
|
||||
|
||||
Config config;
|
||||
|
||||
void loadConfiguration(Config& config) {
|
||||
File file = SD.open("/config.txt");
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError error = deserializeJson(doc, file);
|
||||
if(error) Serial.println(F("Failed to read file, using default configuration"));
|
||||
|
||||
config.edge_color_r = doc["edge"]["color"]["r"] | 0;
|
||||
config.edge_color_g = doc["edge"]["color"]["g"] | 0;
|
||||
config.edge_color_b = doc["edge"]["color"]["b"] | 50;
|
||||
|
||||
//strlcpy(config.hostname, doc["hostname"] | "example.com", sizeof(config.hostname));
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
void pwm_audio_callback() {
|
||||
while (ui_snd.availableForWrite()) {
|
||||
if(click) {
|
||||
ui_snd.write(beep[counter]);
|
||||
counter++;
|
||||
if(counter == beep_length) {
|
||||
counter = 0;
|
||||
click = false;
|
||||
}
|
||||
} else {
|
||||
digitalWriteFast(7, LOW);
|
||||
ui_snd.write(0);
|
||||
counter = 0;
|
||||
}
|
||||
counter++;
|
||||
if(counter == UI_SAMPLERATE) counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,14 +84,45 @@ void tca_irq() {
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
for(int i = 0; i < UI_SAMPLERATE; i++){
|
||||
float sine_pos = (2.0f * M_PI * 8000.0f * (float)i) / (float)UI_SAMPLERATE;
|
||||
beep[i] = (int16_t)(sin(sine_pos) * 8000.0f);
|
||||
Serial.begin();
|
||||
while (!Serial) {
|
||||
;
|
||||
}
|
||||
|
||||
Serial.begin();
|
||||
delay(2000);
|
||||
pinMode(21, INPUT_PULLUP);
|
||||
sd_card_detected = !digitalRead(21);
|
||||
delay(500);
|
||||
|
||||
bool sdInitialized = SD.begin(22, 23, 24);
|
||||
delay(100);
|
||||
if(!sdInitialized) sdInitialized = SD.begin(22, 23, 24); // hack to prevent SD card from not initializing after soft reset
|
||||
|
||||
if(sdInitialized) loadConfiguration(config);
|
||||
|
||||
if(sdInitialized && SD.exists("/click.wav")) {
|
||||
File click = SD.open("/click.wav");
|
||||
int click_bytes = 0;
|
||||
int32_t maxv = 0;
|
||||
while (click.available() || click_bytes == UI_SAMPLERATE-1) {
|
||||
int32_t sample = click.read() * 64;
|
||||
if(abs(sample) > maxv) maxv = sample;
|
||||
beep[click_bytes] = sample;
|
||||
click_bytes++;
|
||||
}
|
||||
|
||||
beep_length = click_bytes;
|
||||
Serial.print("Read ");
|
||||
Serial.print(beep_length);
|
||||
Serial.print(" bytes from click.wav into beep array. maxV was ");
|
||||
Serial.println(maxv);
|
||||
} else {
|
||||
for(int i = 0; i < UI_SAMPLERATE; i++){
|
||||
float sine_pos = (2.0f * M_PI * 8000.0f * (float)i) / (float)UI_SAMPLERATE;
|
||||
beep[i] = (int16_t)(sin(sine_pos) * 8000.0f);
|
||||
}
|
||||
Serial.println("Synthesized 8kHz sine into beep array.");
|
||||
beep_length = UI_SAMPLERATE;
|
||||
}
|
||||
|
||||
Serial.println("INIT WIRE");
|
||||
Wire1.setSDA(2);
|
||||
@ -74,7 +142,8 @@ void setup() {
|
||||
Serial.println("SUCCESS");
|
||||
|
||||
// Rotary Encoder
|
||||
ENC.begin(4); // set direction pin.
|
||||
ENC.begin(); // set direction pin.
|
||||
ENC.setDirection(AS5600_COUNTERCLOCK_WISE);
|
||||
|
||||
pinMode(6, OUTPUT); // Vibration Motor
|
||||
pinMode(7, OUTPUT); // UI Amp Enable
|
||||
@ -104,6 +173,8 @@ void setup() {
|
||||
// }
|
||||
}
|
||||
int active = 0;
|
||||
int active_led_ring = 0;
|
||||
|
||||
uint32_t lastTime = 0;
|
||||
int32_t position = 0;
|
||||
|
||||
@ -111,6 +182,10 @@ void loop() {
|
||||
edge_pixels.clear(); // Set all pixel colors to 'off'
|
||||
ui_pixels.clear(); // Set all pixel colors to 'off'
|
||||
|
||||
sd_card_detected = !digitalRead(21);
|
||||
if(sd_card_detected) edge_pixels.setPixelColor(8, edge_pixels.Color(0,25,0));
|
||||
if(!sd_card_detected) edge_pixels.setPixelColor(8, edge_pixels.Color(25,0,0));
|
||||
|
||||
position = ENC.getCumulativePosition();
|
||||
|
||||
// if (millis() - lastTime >= 100)
|
||||
@ -121,10 +196,12 @@ void loop() {
|
||||
// Serial.println(ENC.getRevolutions());
|
||||
// }
|
||||
|
||||
// LED Ring leeren
|
||||
for (int i = 0; i < 48; i++) {
|
||||
ui_pixels.setPixelColor(i + 3, edge_pixels.Color(0, 0, 0));
|
||||
}
|
||||
|
||||
// flag = true when a button is pushed
|
||||
if (flag) {
|
||||
int val = TCA.read16();
|
||||
Serial.println(val, BIN);
|
||||
@ -140,30 +217,30 @@ void loop() {
|
||||
if (HAPTIC) {
|
||||
digitalWrite(6, HIGH);
|
||||
}
|
||||
// Make beep
|
||||
if (AURAL) {
|
||||
digitalWrite(7, HIGH);
|
||||
digitalWriteFast(7, HIGH);
|
||||
click = true;
|
||||
delay(50);
|
||||
click = false;
|
||||
digitalWrite(7, LOW);
|
||||
}
|
||||
// Make vibration
|
||||
if(HAPTIC){
|
||||
if(!AURAL) delay(50);
|
||||
digitalWrite(6, LOW);
|
||||
}
|
||||
// Flash led ring
|
||||
for (int i = 0; i < 48; i++) {
|
||||
ui_pixels.setPixelColor(i + 3, edge_pixels.Color(0, 15, 0));
|
||||
}
|
||||
// Switch through LED matrix
|
||||
if(buttons[RIGHT]) active++;
|
||||
if(buttons[LEFT]) active--;
|
||||
if(active == 13) active = 0;
|
||||
if(active == -1) active = 12;
|
||||
}
|
||||
// 11111110 11111111 button right
|
||||
|
||||
// EDGE LEDs
|
||||
for (int i = 0; i < 8; i++) {
|
||||
edge_pixels.setPixelColor(i, edge_pixels.Color(15, 15, 15));
|
||||
edge_pixels.setPixelColor(i, edge_pixels.Color(config.edge_color_r, config.edge_color_g, config.edge_color_b));
|
||||
}
|
||||
|
||||
// Rotary button LEDs
|
||||
@ -171,16 +248,22 @@ void loop() {
|
||||
ui_pixels.setPixelColor(1, edge_pixels.Color(0, 0, 15));
|
||||
ui_pixels.setPixelColor(2, edge_pixels.Color(0, 0, 15));
|
||||
|
||||
// empty LED matrix
|
||||
for (int i = 0; i < 13; i++) {
|
||||
ui_pixels.setPixelColor(i + 3 + 48 + 4, edge_pixels.Color(0, 0, 0));
|
||||
}
|
||||
|
||||
// set active LED matrix LED
|
||||
ui_pixels.setPixelColor(active + 3 + 48 + 4, edge_pixels.Color(255, 255, 255));
|
||||
|
||||
// put your main code here, to run repeatedly:
|
||||
if(position < 0) position += 4096;
|
||||
active_led_ring = (position / 32) % 48;
|
||||
|
||||
edge_pixels.show(); // Set all pixel colors to 'off'
|
||||
ui_pixels.show(); // Set all pixel colors to 'off'
|
||||
// set active LED ring LED
|
||||
ui_pixels.setPixelColor(active_led_ring + 3, edge_pixels.Color(255, 255, 255));
|
||||
|
||||
delay(1);
|
||||
edge_pixels.show();
|
||||
ui_pixels.show();
|
||||
|
||||
delay(1); // wait 1ms
|
||||
}
|
||||
Reference in New Issue
Block a user