Refactored for teensy 4.0, xEvi hardware

- Switched to platformio, ino -> cpp
- MPRLS for pressure sensor
- Added basic ICM support
- Removed widi, battery, other features not supported in xEvi
- Removed legacy options/processing
- Added LED strip support
- Added encoder support
- Reworked menu code to use encoders/be more flexible
This commit is contained in:
Brian Hrebec 2023-08-27 11:52:08 -05:00
parent c58c3f9e46
commit 01d193c9b3
92 changed files with 69119 additions and 73272 deletions

6
.gitignore vendored
View file

@ -1,3 +1,3 @@
.vscode/ipch
*.o
.DS_STORE
.vscode/ipch
*.o
.DS_STORE

8
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "simulation/imgui"]
path = simulation/imgui
url = https://github.com/ocornut/imgui.git
ignore = untracked
[submodule "simulation/imgui"]
path = simulation/imgui
url = https://github.com/ocornut/imgui.git
ignore = untracked

1348
LICENSE.md

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

5
NuEVI/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
NuEVI/.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

9
NuEVI/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"files.associations": {
"type_traits": "cpp",
"*.tcc": "cpp",
"ostream": "cpp",
"streambuf": "cpp",
"array": "cpp"
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,593 +0,0 @@
#include <Arduino.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_MPR121.h>
#include "menu.h"
#include "numenu.h"
#include "globals.h"
#include "config.h"
#include "hardware.h"
#include "settings.h"
//***********************************************************
extern Adafruit_SSD1306 display;
#if defined(NURAD)
extern Adafruit_MPR121 touchSensorRH;
extern Adafruit_MPR121 touchSensorLH;
extern Adafruit_MPR121 touchSensorRollers;
#else
extern Adafruit_MPR121 touchSensor;
#endif
extern byte cursorNow;
#if defined(NURAD)
extern int calOffsetRollers[6];
extern int calOffsetRH[12];
extern int calOffsetLH[12];
#endif
//***********************************************************
// Variables used for the adjust menu
static byte forcePix;
static uint16_t pos1;
static uint16_t pos2;
static int16_t adjustOption;
static int16_t adjustCurrent;
static int16_t sensorPixelPos1 = -1;
static int16_t sensorPixelPos2 = -1;
static bool refreshScreen;
//***********************************************************
static void breathSave(const AdjustMenuEntry& e) {
writeSetting(BREATH_THR_ADDR, *e.entries[0].value);
writeSetting(BREATH_MAX_ADDR, *e.entries[1].value);
}
const AdjustMenuEntry breathAdjustMenu = {
"BREATH",
{
{ &breathThrVal, breathLoLimit, breathHiLimit },
{ &breathMaxVal, breathLoLimit, breathHiLimit }
},
breathSave
};
static void portamentoSave(const AdjustMenuEntry& e) {
writeSetting(PORTAM_THR_ADDR, *e.entries[0].value);
writeSetting(PORTAM_MAX_ADDR, *e.entries[1].value);
}
const AdjustMenuEntry portamentoAdjustMenu = {
"BITE",
{
{ &portamThrVal, portamLoLimit, portamHiLimit },
{ &portamMaxVal, portamLoLimit, portamHiLimit }
},
portamentoSave
};
static void pbSave(const AdjustMenuEntry& e) {
writeSetting(PITCHB_THR_ADDR, *e.entries[0].value);
writeSetting(PITCHB_MAX_ADDR, *e.entries[1].value);
}
const AdjustMenuEntry pitchBendAdjustMenu = {
"BEND",
{
{ &pitchbThrVal, pitchbLoLimit, pitchbHiLimit },
{ &pitchbMaxVal, pitchbLoLimit, pitchbHiLimit }
},
pbSave
};
static void extracSave(const AdjustMenuEntry& e) {
writeSetting(EXTRAC_THR_ADDR, *e.entries[0].value);
writeSetting(EXTRAC_MAX_ADDR, *e.entries[1].value);
}
const AdjustMenuEntry extraSensorAdjustMenu = {
"LIP/EC",
{
{ &extracThrVal, extracLoLimit, extracHiLimit },
{ &extracMaxVal, extracLoLimit, extracHiLimit }
},
extracSave
};
static void ctouchThrSave(const AdjustMenuEntry& e) {
writeSetting(CTOUCH_THR_ADDR, *e.entries[0].value);
}
const AdjustMenuEntry ctouchAdjustMenu = {
"TOUCH",
{
{ &ctouchThrVal, ctouchLoLimit, ctouchHiLimit },
{ nullptr, 0, 0 }
},
ctouchThrSave
};
static void leverSave(const AdjustMenuEntry& e) {
writeSetting(LEVER_THR_ADDR, *e.entries[0].value);
writeSetting(LEVER_MAX_ADDR, *e.entries[1].value);
}
const AdjustMenuEntry leverAdjustMenu = {
"LEVER",
{
{ &leverThrVal, leverLoLimit, leverHiLimit },
{ &leverMaxVal, leverLoLimit, leverHiLimit }
},
leverSave
};
const AdjustMenuEntry* adjustMenuEntries[] = {
&breathAdjustMenu,
&portamentoAdjustMenu,
&pitchBendAdjustMenu,
&extraSensorAdjustMenu,
&ctouchAdjustMenu,
&leverAdjustMenu,
};
static const int numAdjustEntries = ARR_LEN(adjustMenuEntries);
//***********************************************************
void autoCalSelected() {
int calRead;
int calReadNext;
// NuRAD/NuEVI sensor calibration
// Extra Controller
if(adjustOption == 3) {
calRead = touchRead(extraPin);
extracThrVal = constrain(calRead+200, extracLoLimit, extracHiLimit);
extracMaxVal = constrain(extracThrVal+600, extracLoLimit, extracHiLimit);
writeSetting(EXTRAC_THR_ADDR, extracThrVal);
writeSetting(EXTRAC_MAX_ADDR, extracMaxVal);
}
// Breath sensor
if(adjustOption == 0) {
calRead = analogRead(breathSensorPin);
breathThrVal = constrain(calRead+200, breathLoLimit, breathHiLimit);
breathMaxVal = constrain(breathThrVal+1500, breathLoLimit, breathHiLimit);
writeSetting(BREATH_THR_ADDR, breathThrVal);
writeSetting(BREATH_MAX_ADDR, breathMaxVal);
}
// Pitch Bend
if(adjustOption == 2) {
calRead = touchRead(pbUpPin);
calReadNext = touchRead(pbDnPin);
if (calReadNext > calRead) calRead = calReadNext; //use highest value
pitchbThrVal = constrain(calRead+200, pitchbLoLimit, pitchbHiLimit);
pitchbMaxVal = constrain(pitchbThrVal+800, pitchbLoLimit, pitchbHiLimit);
writeSetting(PITCHB_THR_ADDR, pitchbThrVal);
writeSetting(PITCHB_MAX_ADDR, pitchbMaxVal);
}
// Lever
if(adjustOption == 5) {
#if defined(SEAMUS)
calRead = touchRead(vibratoPin);
#else
calRead = 3000-touchRead(vibratoPin);
#endif
leverThrVal = constrain(calRead+60, leverLoLimit, leverHiLimit);
leverMaxVal = constrain(calRead+120, leverLoLimit, leverHiLimit);
writeSetting(LEVER_THR_ADDR, leverThrVal);
writeSetting(LEVER_MAX_ADDR, leverMaxVal);
}
#if defined(NURAD) // NuRAD sensor calibration
// Bite Pressure sensor
if(adjustOption == 1) {
calRead = analogRead(bitePressurePin);
portamThrVal = constrain(calRead+300, portamLoLimit, portamHiLimit);
portamMaxVal = constrain(portamThrVal+600, portamLoLimit, portamHiLimit);
writeSetting(PORTAM_THR_ADDR, portamThrVal);
writeSetting(PORTAM_MAX_ADDR, portamMaxVal);
}
// Touch sensors
if(adjustOption == 4) {
calRead = ctouchHiLimit;
for (byte i = 0; i < 6; i++) {
calReadNext = touchSensorRollers.filteredData(i) * (300-calOffsetRollers[i])/300;
if (calReadNext < calRead) calRead = calReadNext; //use lowest value
}
for (byte i = 0; i < 12; i++) {
calReadNext = touchSensorRH.filteredData(i) * (300-calOffsetRH[i])/300;
if (calReadNext < calRead) calRead = calReadNext; //use lowest value
}
for (byte i = 0; i < 12; i++) {
calReadNext = touchSensorLH.filteredData(i) * (300-calOffsetLH[i])/300;
if (calReadNext < calRead) calRead = calReadNext; //use lowest value
}
ctouchThrVal = constrain(calRead-20, ctouchLoLimit, ctouchHiLimit);
touch_Thr = map(ctouchThrVal,ctouchHiLimit,ctouchLoLimit,ttouchLoLimit,ttouchHiLimit);
writeSetting(CTOUCH_THR_ADDR, ctouchThrVal);
}
#else // NuEVI sensor calibration
// Bite sensor
if(adjustOption == 1) {
if (digitalRead(biteJumperPin)){ //PBITE (if pulled low with jumper, pressure sensor is used instead of capacitive bite sensing)
// Capacitive sensor
calRead = touchRead(bitePin);
portamThrVal = constrain(calRead+200, portamLoLimit, portamHiLimit);
portamMaxVal = constrain(portamThrVal+600, portamLoLimit, portamHiLimit);
writeSetting(PORTAM_THR_ADDR, portamThrVal);
writeSetting(PORTAM_MAX_ADDR, portamMaxVal);
} else {
// Pressure sensor
calRead = analogRead(bitePressurePin);
portamThrVal = constrain(calRead+300, portamLoLimit, portamHiLimit);
portamMaxVal = constrain(portamThrVal+600, portamLoLimit, portamHiLimit);
writeSetting(PORTAM_THR_ADDR, portamThrVal);
writeSetting(PORTAM_MAX_ADDR, portamMaxVal);
}
}
// Touch sensors
if(adjustOption == 4) {
calRead = ctouchHiLimit;
for (byte i = 0; i < 12; i++) {
calReadNext = touchSensor.filteredData(i);
if (calReadNext < calRead) calRead = calReadNext; //use lowest value
}
calReadNext=map(touchRead(halfPitchBendKeyPin),ttouchLoLimit,ttouchHiLimit,ctouchHiLimit,ctouchLoLimit);
if (calReadNext < calRead) calRead = calReadNext; //use lowest value
calReadNext=map(touchRead(specialKeyPin),ttouchLoLimit,ttouchHiLimit,ctouchHiLimit,ctouchLoLimit);
if (calReadNext < calRead) calRead = calReadNext; //use lowest value
ctouchThrVal = constrain(calRead-20, ctouchLoLimit, ctouchHiLimit);
touch_Thr = map(ctouchThrVal,ctouchHiLimit,ctouchLoLimit,ttouchLoLimit,ttouchHiLimit);
writeSetting(CTOUCH_THR_ADDR, ctouchThrVal);
}
#endif
}
//***********************************************************
static void drawAdjCursor(byte color) {
display.drawTriangle(16,4,20,4,18,1,color);
display.drawTriangle(16,6,20,6,18,9,color);
}
static void drawAdjustFrame(int line) {
display.drawLine(25,line,120,line,WHITE); // Top line
display.drawLine(25,line+12,120,line+12,WHITE); // Bottom line
display.drawLine(25,line+1,25,line+2,WHITE);
display.drawLine(120,line+1,120,line+2,WHITE);
display.drawLine(120,line+10,120,line+11,WHITE);
display.drawLine(25,line+10,25,line+11,WHITE);
}
static void drawAdjustBase(const char* title, bool all) {
display.clearDisplay();
drawAdjustFrame(17);
// sensor marker lines.
display.drawLine(25,36,25,40,WHITE);
display.drawLine(120,36,120,40,WHITE);
display.setTextSize(1);
display.setCursor(25,2);
display.println(title);
display.setCursor(0,20);
display.println("THR");
display.setCursor(0,35);
display.println("SNS");
if(all) {
drawAdjustFrame(47);
display.setCursor(0,50);
display.println("MAX");
}
cursorNow = WHITE;
drawAdjCursor(WHITE);
}
static void drawLineCursor(uint16_t hPos, uint16_t vPos, int color) {
display.drawLine(hPos, vPos,hPos, vPos+6, color);
}
static bool updateAdjustLineCursor(uint32_t timeNow, uint16_t hPos, uint16_t vPos ) {
if ((timeNow - cursorBlinkTime) > cursorBlinkInterval) {
if (cursorNow == WHITE) cursorNow = BLACK; else cursorNow = WHITE;
drawLineCursor(hPos, vPos, cursorNow);
cursorBlinkTime = timeNow;
return true;
}
return false;
}
static void drawAdjustMenu(const AdjustMenuEntry *menu) {
bool haveSecondValue = menu->entries[1].value != nullptr;
drawAdjustBase( menu->title, haveSecondValue );
pos1 = map( *menu->entries[0].value, menu->entries[0].limitLow, menu->entries[0].limitHigh, 27, 119);
display.drawLine( pos1, 20, pos1, 26, WHITE );
if(haveSecondValue) {
pos2 = map( *menu->entries[1].value, menu->entries[1].limitLow, menu->entries[1].limitHigh, 27, 119);
display.drawLine( pos2, 50, pos2, 56, WHITE );
}
display.fillRect(64,0,64,9,BLACK);
display.setTextSize(1);
if(haveSecondValue) {
display.setCursor(68,2);
display.print(*menu->entries[0].value);
display.print("|");
display.print(*menu->entries[1].value);
} else {
display.setCursor(104,2);
display.print(*menu->entries[0].value);
}
}
//***********************************************************
static bool updateSensorPixel(int pos, int pos2) {
bool update = pos != sensorPixelPos1 || pos2 != sensorPixelPos2;
if(update) {
display.drawFastHLine(28, 38, 119-28, BLACK); // Clear old line
display.drawPixel(pos, 38, WHITE);
if( pos2 >= 0) display.drawPixel(pos2, 38, WHITE);
sensorPixelPos1 = pos;
sensorPixelPos2 = pos2;
}
return update;
}
void plotSensorPixels(){
int redraw = 0;
if(forcePix)
sensorPixelPos1 = -1;
// This is hacky. It depends on the order of items in the adjust menu list.
if(adjustOption == 0) {
int pos = map(constrain(pressureSensor, breathLoLimit, breathHiLimit), breathLoLimit, breathHiLimit, 28, 118);
redraw = updateSensorPixel(pos, -1);
}
else if(adjustOption == 1) {
if (biteJumper) { //PBITE (if pulled low with jumper or if on a NuRAD, use pressure sensor instead of capacitive bite sensor)
biteSensor=analogRead(bitePressurePin); // alternative kind bite sensor (air pressure tube and sensor) PBITE
} else {
biteSensor = touchRead(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
}
int pos = map(constrain(biteSensor,portamLoLimit,portamHiLimit), portamLoLimit, portamHiLimit, 28, 118);
redraw = updateSensorPixel(pos, -1);
}
else if(adjustOption == 2) {
int pos = map(constrain(pbUp, pitchbLoLimit, pitchbHiLimit), pitchbLoLimit, pitchbHiLimit, 28, 118);
int pos2 = map(constrain(pbDn, pitchbLoLimit, pitchbHiLimit), pitchbLoLimit, pitchbHiLimit, 28, 118);
redraw = updateSensorPixel(pos, pos2);
}
else if(adjustOption == 3) {
int pos = map(constrain(exSensor, extracLoLimit, extracHiLimit), extracLoLimit, extracHiLimit, 28, 118);
redraw = updateSensorPixel(pos, -1);
}
#if defined(NURAD)
else if(adjustOption == 4) {
display.drawLine(28,37,118,37,BLACK);
for (byte i=0; i<12; i++){
//int pos = map(constrain(touchSensorRH.filteredData(i) - calOffsetRH[i], ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
int pos = map(constrain(touchSensorRH.filteredData(i) * (300-calOffsetRH[i])/300, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
display.drawPixel(pos, 37, WHITE);
}
display.drawLine(28,38,118,38,BLACK);
for (byte i=0; i<12; i++){
//int pos = map(constrain(touchSensorLH.filteredData(i) - calOffsetLH[i], ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
int pos = map(constrain(touchSensorLH.filteredData(i) * (300-calOffsetLH[i])/300, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
display.drawPixel(pos, 38, WHITE);
}
display.drawLine(28,39,118,39,BLACK);
for (byte i=0; i<6; i++){
//int pos = map(constrain(touchSensorRollers.filteredData(i) - calOffsetRollers[i], ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
int pos = map(constrain(touchSensorRollers.filteredData(i) * (300-calOffsetRollers[i])/300, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
display.drawPixel(pos, 39, WHITE);
}
redraw = 1;
}
#else //NuEVI
else if(adjustOption == 4) {
display.drawLine(28,39,118,39,BLACK);
for (byte i=0; i<12; i++){
int pos = map(constrain(touchSensor.filteredData(i), ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
display.drawPixel(pos, 39, WHITE);
}
int posRead = map(touchRead(halfPitchBendKeyPin),ttouchLoLimit,ttouchHiLimit,ctouchHiLimit,ctouchLoLimit);
int pos = map(constrain(posRead, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
posRead = map(touchRead(specialKeyPin),ttouchLoLimit,ttouchHiLimit,ctouchHiLimit,ctouchLoLimit);
int pos2 = map(constrain(posRead, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
updateSensorPixel(pos, pos2);
redraw = 1;
}
#endif
else if(adjustOption == 5) {
#if defined(SEAMUS)
int pos = map(constrain(touchRead(vibratoPin), leverLoLimit, leverHiLimit), leverLoLimit, leverHiLimit, 28, 118);
#else
int pos = map(constrain(3000-touchRead(vibratoPin), leverLoLimit, leverHiLimit), leverLoLimit, leverHiLimit, 28, 118);
#endif
redraw = updateSensorPixel(pos, -1);
}
if (redraw){
display.display();
}
forcePix = 0;
}
//***********************************************************
static bool drawAdjustBar(uint16_t buttons, int row, const AdjustValue* entry, uint16_t *pos) {
bool updated = false;
uint16_t step = (entry->limitHigh-entry->limitLow)/92;
int val = *entry->value;
switch(buttons) {
case BTN_UP:
val += step;
updated = true;
break;
case BTN_DOWN:
val -= step;
updated = true;
break;
}
if(updated) {
*entry->value = constrain(val, entry->limitLow, entry->limitHigh);
auto p = *pos;
display.drawLine(p, row, p, row+6, BLACK);
*pos = p = map(*entry->value, entry->limitLow, entry->limitHigh, 27, 119);
display.drawLine(p, row, p, row+6, WHITE);
cursorNow = BLACK;
}
return updated;
}
static bool updateAdjustCursor(uint32_t timeNow) {
if ((timeNow - cursorBlinkTime) > cursorBlinkInterval) {
if (cursorNow == WHITE) cursorNow = BLACK; else cursorNow = WHITE;
drawAdjCursor(cursorNow);
cursorBlinkTime = timeNow;
return true;
}
return false;
}
static bool handleInput(const AdjustMenuEntry *currentMenu, uint32_t timeNow, uint8_t buttons, uint16_t *xpos, int ypos, int index) {
bool haveSecondValue = currentMenu->entries[1].value != nullptr;
if (buttons) {
if (buttons == BTN_DOWN+BTN_UP){
display.fillRect(26,35,90,7,BLACK);
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(53,35);
display.println("AUTOCAL");
display.display();
delay(2000);
autoCalSelected();
display.fillRect(26,35,90,7,BLACK);
display.display();
drawAdjustMenu(currentMenu);
forcePix = 1;
plotSensorPixels();
} else
drawAdjustBar( buttons, ypos, &currentMenu->entries[index], xpos );
display.fillRect(64,0,64,9,BLACK);
display.setTextSize(1);
if(haveSecondValue) {
display.setCursor(68,2);
display.print(*currentMenu->entries[0].value);
display.print("|");
display.print(*currentMenu->entries[1].value);
} else {
display.setCursor(104,2);
display.print(*currentMenu->entries[0].value);
}
int last = adjustCurrent;
if(buttons == BTN_ENTER) adjustCurrent += 1;
else if( buttons == BTN_MENU) adjustCurrent = 0;
if(last != adjustCurrent) drawLineCursor(*xpos, ypos, WHITE);
return true;
} else {
return updateAdjustLineCursor(timeNow, *xpos, ypos);
}
}
int updateAdjustMenu(uint32_t timeNow, KeyState &input, bool firstRun, bool drawSensor) {
bool redraw = false;
int result = 0;
const AdjustMenuEntry *currentMenu = adjustMenuEntries[adjustOption];
uint8_t buttons = input.changed ? input.current : 0;
if(firstRun || refreshScreen) {
adjustCurrent = 0;
refreshScreen = false;
drawAdjustMenu(currentMenu);
// the sensor pixel stuff should be refactored (to work again)
forcePix = 1;
sensorPixelPos1 = -1; // Force draw of sensor pixels
}
if(adjustCurrent == 0) {
// Currently selecting what option to modify
redraw |= updateAdjustCursor(timeNow);
bool save = false;
if( buttons == BTN_DOWN ) {
adjustOption += 1;
refreshScreen = 1;
save = true;
}
else if ( buttons == BTN_UP ) {
adjustOption -= 1;
refreshScreen = 1;
save = true;
}
else if ( buttons == BTN_ENTER ) {
adjustCurrent = 1;
}
else if (buttons == BTN_MENU ) {
adjustCurrent = 0;
result = -1;
save = true;
}
if(save && currentMenu->saveFunc)
currentMenu->saveFunc(*currentMenu);
} else if( adjustCurrent == 1) {
redraw |= handleInput(currentMenu, timeNow, buttons, &pos1, 20, 0);
} else {
redraw |= handleInput(currentMenu, timeNow, buttons, &pos2, 50, 1);
}
// Keep adjustCurrent in range
if( (adjustCurrent > 2) || ((adjustCurrent == 2) && (currentMenu->entries[1].value == nullptr)))
adjustCurrent = 0;
// Keep adjust option in range.
if(adjustOption < 0)
adjustOption = numAdjustEntries-1;
else if (adjustOption >= numAdjustEntries)
adjustOption = 0;
if(drawSensor) {
plotSensorPixels();
}
if(result >= 0)
result = redraw;
return result;
}

View file

@ -1,57 +0,0 @@
#ifndef __CONFIG_H
#define __CONFIG_H
// Compile options, comment/uncomment to change
#define FIRMWARE_VERSION "1.6.0" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<<
#define ON_Delay 20 // Set Delay after ON threshold before velocity is checked (wait for tounging peak)
#define CCN_Port 5 // Controller number for portamento level
#define CCN_PortOnOff 65// Controller number for portamento on/off
#define CCN_PortSE02 9 // Controller number for portamento type on Roland SE-02
// Send breath CC data no more than every CC_BREATH_INTERVAL
// milliseconds
#define CC_BREATH_INTERVAL 5
#define SLOW_MIDI_ADD 7
#define CC_INTERVAL 9
#define CC_INTERVAL2 13
#define CC_INTERVAL3 37
#define LVL_TIMER_INTERVAL 15
#define CVPORTATUNE 2
#define maxSamplesNum 120
#define breathLoLimit 0
#define breathHiLimit 4095
#define portamLoLimit 700
#define portamHiLimit 4700
#define pitchbLoLimit 500
#define pitchbHiLimit 4000
#define extracLoLimit 500
#define extracHiLimit 4000
#define ctouchLoLimit 50
#define ctouchHiLimit 350
#define ttouchLoLimit 50
#define ttouchHiLimit 1900
#define leverLoLimit 1400
#define leverHiLimit 2000
#define MIN_LED_BRIGHTNESS 5 // lowest PWM value that still is visible
#define BREATH_LED_BRIGHTNESS 600 // up to 4095, PWM
#define PORTAM_LED_BRIGHTNESS 300 // up to 4095, PWM
#define EXTCON_LED_BRIGHTNESS 300 // up to 4095, PWM
#define SPCKEY_LED_BRIGHTNESS 700 // up to 4095, PWM
#define ALK_BAT_FULL 2800 // about 4.6V
#define NMH_BAT_FULL 2380 // about 3.9V
#define LIP_BAT_FULL 2540 // about 4.2V
#define ALK_BAT_LOW 2300 // about 3.8V
#define NMH_BAT_LOW 2200 // about 3.6V
#define LIP_BAT_LOW 2250 // about 3.7V
#endif

View file

@ -1,234 +0,0 @@
#ifndef __GLOBALS_H
#define __GLOBALS_H
#include "wiring.h"
// The three states of our main state machine
// No note is sounding
#define NOTE_OFF 1
// We've observed a transition from below to above the
// threshold value. We wait a while to see how fast the
// breath velocity is increasing
#define RISE_WAIT 2
// A note is sounding
#define NOTE_ON 3
//Magic values
#define PBD 12
#define EC2 25
#define ECSW 26
#define LVL 27
#define LVLP 28
#define GLD 29
#define ECH 30
#define QTN 31
#define MOD 13
//Vibrato direction
#define UPWD 1
#define DNWD 0
enum PolySelect : unsigned short {
EHarmonizerOff = 0,
ETriadMajorGospelRoot = 1, // MGR
ETriadMajorGospelDominant = 2, // MGD
EMajorAddNine = 3,
EMinorDorian = 4,
EMinorAeolian = 5,
EMinorFourVoiceHip = 6,
EFourWayCloseHarmonizer = 7,
ERotatorA = 8,
ERotatorB = 9,
ERotatorC = 10,
};
struct Rotator
{
uint16_t parallel; // Semitones
uint16_t rotations[4];
};
extern const unsigned short* const curves[];
extern const unsigned short curveIn[];
#if defined(NURAD)
extern int calOffsetRollers[6];
extern int calOffsetRH[12];
extern int calOffsetLH[12];
#endif
extern unsigned short breathThrVal;
extern unsigned short breathMaxVal;
extern unsigned short portamThrVal;
extern unsigned short portamMaxVal;
extern unsigned short pitchbThrVal;
extern unsigned short pitchbMaxVal;
extern unsigned short extracThrVal;
extern unsigned short extracMaxVal;
extern unsigned short ctouchThrVal;
extern unsigned short leverThrVal;
extern unsigned short leverMaxVal;
extern unsigned short transpose;
extern unsigned short MIDIchannel;
extern unsigned short breathCC; // OFF:MW:BR:VL:EX:MW+:BR+:VL+:EX+:CF:UNO
extern unsigned short breathCC2; // OFF:1-127
extern unsigned short breathCC2Rise; // 1X:2X:3X:4X:5X
extern unsigned short breathAT;
extern unsigned short velocity;
extern unsigned short portamento;// switching on cc65? just cc5 enabled? SW:ON:OFF
extern unsigned short portLimit; // 1-127
extern unsigned short PBdepth; // OFF:1-12 divider
extern unsigned short extraCT; // OFF:MW:FP:CF:SP
extern unsigned short vibrato; // OFF:1-9
extern unsigned short deglitch; // 0-70 ms in steps of 5
extern unsigned short patch; // 1-128
extern unsigned short octave;
extern unsigned short curve;
extern unsigned short velSmpDl; // 0-30 ms
extern unsigned short velBias; // 0-9
extern unsigned short pinkySetting; // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 24 (QuickTranspose +1 to +12), 25 (EC2), 26 (ECSW), 27 (LVL), 28 (LVLP)
extern unsigned short dipSwBits; // virtual dip switch settings for special modes (work in progress)
extern unsigned short priority; // mono priority for rotator chords
extern unsigned short vibSens; // vibrato sensitivity
extern unsigned short vibRetn; // vibrato return speed
extern unsigned short vibSquelch; //vibrato signal squelch
extern unsigned short vibDirection; //direction of first vibrato wave UPWD or DNWD
extern unsigned short vibSensBite; // vibrato sensitivity (bite)
extern unsigned short vibSquelchBite; //vibrato signal squelch (bite)
extern unsigned short vibControl;
extern unsigned short fastPatch[7];
extern unsigned short extraCT2; // OFF:1-127
extern unsigned short levelCC; // 0-127
extern unsigned short levelVal; // 0-127
extern unsigned short fingering; // 0-4 EWI,EWX,SAX,EVI,EVR
extern unsigned short rollerMode; //0-2
extern unsigned short lpinky3; // 0-25 (OFF, -12 - MOD - +12)
extern unsigned short batteryType; // 0-2 ALK,NIM,LIP
extern unsigned short harmSetting; // 0-7
extern unsigned short harmSelect; // 0-5
extern unsigned short brHarmSetting; // 0-7
extern unsigned short brHarmSelect; // 0-3
extern PolySelect polySelect; // OFF, MGR, MGD, MND, MNH, FWC, RTA, RTB or RTC
extern unsigned short fwcType; // 6, m6, 7, m7
extern unsigned short fwcLockH; // OFF:ON
extern unsigned short fwcDrop2; // OFF:ON
extern unsigned short hmzKey; // 0-11 (0 is C)
extern unsigned short hmzLimit; // 2-5
extern unsigned short otfKey; //OFF:ON
extern unsigned short breathInterval; // 3-15
extern unsigned short biteControl; // OFF, VIB, GLD, CC
extern unsigned short leverControl; // OFF, VIB, GLD, CC
extern unsigned short biteCC; // 0 - 127
extern unsigned short leverCC; // 0 -127
extern unsigned short cvTune; // 1 - 199 representing -99 to +99 in menu (offset of 100 to keep postitive)
extern unsigned short cvScale; // 1 - 199 representing -99 to +99 in menu (offset of 100 to keep postitive)
extern unsigned short cvVibRate; // OFF, 1 - 8 CV extra controller LFO vibrato rate 4.5Hz to 8Hz
extern uint16_t gateOpenEnable;
extern uint16_t specialKeyEnable;
extern byte rotatorOn;
extern byte currentRotation;
extern Rotator rotations_a;
extern Rotator rotations_b;
extern Rotator rotations_c;
extern uint16_t bcasMode; //Legacy CASSIDY compile flag
extern uint16_t trill3_interval;
extern uint16_t fastBoot;
extern uint16_t dacMode;
extern int touch_Thr;
extern int leverPortZero;
extern unsigned long cursorBlinkTime; // the last time the cursor was toggled
extern byte activePatch;
extern byte doPatchUpdate;
extern uint16_t legacy;
extern uint16_t legacyBrAct;
extern byte widiOn;
extern int pressureSensor; // pressure data from breath sensor, for midi breath cc and breath threshold checks
extern int lastPressure;
extern int biteSensor; // capacitance data from bite sensor, for midi cc and threshold checks
extern int lastBite;
extern byte biteJumper;
extern byte widiJumper;
extern int exSensor;
extern int exSensorIndicator;
extern int pitchBend;
extern int pbSend;
extern int pbUp;
extern int pbDn;
extern byte vibLedOff;
extern byte oldpkey;
extern int vibThr; // this gets auto calibrated in setup
extern int vibThrLo;
extern int vibZero;
extern int vibZeroBite;
extern int vibThrBite;
extern int vibThrBiteLo;
extern int battAvg;
extern int breathLevel;
extern byte portIsOn;
extern int oldport;
#if defined(NURAD)
// Key variables, TRUE (1) for pressed, FALSE (0) for not pressed
extern byte LHs;
extern byte LH1; // Left Hand key 1 (pitch change -2)
extern byte LHb; // Left Hand bis key (pitch change -1 unless both LH1 and LH2 are pressed)
extern byte LH2; // Left Hand key 2 (with LH1 also pressed pitch change is -2, otherwise -1)
extern byte LH3; // Left Hand key 3 (pitch change -2)
extern byte LHp1; // Left Hand pinky key 1 (pitch change +1)
extern byte LHp2; // Left Hand pinky key 2 (pitch change -1)
extern byte LHp3;
extern byte RHs; // Right Hand side key (pitch change -2 unless LHp1 is pressed)
extern byte RH1; // Right Hand key 1 (with LH3 also pressed pitch change is -2, otherwise -1)
extern byte RH2; // Right Hand key 2 (pitch change -1)
extern byte RH3; // Right Hand key 3 (pitch change -2)
extern byte RHp1; // Right Hand pinky key 1 (pitch change +1)
extern byte RHp2; // Right Hand pinky key 2 (pitch change -1)
extern byte RHp3; // Right Hand pinky key 3 (pitch change -2)
extern byte Tr1; // Trill key 1 (pitch change +2) (EVI fingering)
extern byte Tr2; // Trill key 2 (pitch change +1)
extern byte Tr3; // Trill key 3 (pitch change +4)
#endif
// Key variables, TRUE (1) for pressed, FALSE (0) for not pressed
extern byte K1; // Valve 1 (pitch change -2)
extern byte K2; // Valve 2 (pitch change -1)
extern byte K3; // Valve 3 (pitch change -3)
extern byte K4; // Left Hand index finger (pitch change -5)
extern byte K5; // Trill key 1 (pitch change +2)
extern byte K6; // Trill key 2 (pitch change +1)
extern byte K7; // Trill key 3 (pitch change +4)
extern byte halfPitchBendKey;
extern byte quarterToneTrigger;
extern byte specialKey;
extern byte pinkyKey;
extern byte patchKey;
extern unsigned int multiMap(unsigned short val, const unsigned short * _in, const unsigned short * _out, uint8_t size);
#endif

View file

@ -1,199 +0,0 @@
#ifndef __HARDWARE_H
#define __HARDWARE_H
#define REVB
//#define NURAD
//#define SEAMUS
//#define I2CSCANNER
#if defined(NURAD) //NuRAD <<<<<<<<<<<<<<<<<<<<<<<
// Pin definitions
// Teensy pins
//Capacitive sensor pins (on-board teensy)
#define bitePin 17
#define extraPin 16
#define pbUpPin 1
#define pbDnPin 0
#define vibratoPin 15
//Analog pressure sensors. Breath and optional bite
#define breathSensorPin A0
#define bitePressurePin A7
//Digital pins for menu buttons
#define dPin 3
#define ePin 4
#define uPin 5
#define mPin 6
//Output pins for LEDs (breath, power, status)
#define bLedPin 10
#define pLedPin 9
#define eLedPin 22
#define sLedPin 23
#define statusLedPin 13
//Pins for WIDI board management
#define widiJumperPin 28
#define widiJumperGndPin 27
#define widiPowerPin 33
//Analog input for measuring voltage
#define vMeterPin A11
//DAC outputs for analog and pwm
#define dacPin A14
#define pwmDacPin A6
//Which serial port to use for MIDI
#define MIDI_SERIAL Serial3
#define WIDI_SERIAL Serial2
// MPR121 Rollers 0x5D
#define rPin1 0
#define rPin2 1
#define rPin3 2
#define rPin4 3
#define rPin5 4
#define rPin6 5
// MPR121 RH 0x5C
#define RHsPin 3
#define RH1Pin 4
#define RH2Pin 2
#define RH3Pin 1
#define RHp1Pin 0
#define RHp2Pin 8
#define RHp3Pin 7
#define spec1Pin 10
#define spec2Pin 9
#define patchPin 5
// MPR121 LH 0x5B
#define LHsPin 8
#define LH1Pin 7
#define LHbPin 1
#define LH2Pin 9
#define LH3Pin 10
#define LHp1Pin 11
#define LHp2Pin 3
#define LHp3Pin 4
#else //NuEVI <<<<<<<<<<<<<<<<<<<<<<<
// Pin definitions
// Teensy pins
#define specialKeyPin 0 // SK or S2
#define halfPitchBendKeyPin 1 // PD or S1
//Capacitive sensor pins (on-board teensy)
#define bitePin 17
#define extraPin 16
#define pbUpPin 23
#define pbDnPin 22
#define vibratoPin 15
//Pins jumpered to enable bite pressure sensor
#define biteJumperPin 11
#define biteJumperGndPin 12
//Pins for WIDI board management
#define widiJumperPin 28
#define widiJumperGndPin 27
#define widiPowerPin 33
//Analog pressure sensors. Breath and optional bite
#define breathSensorPin A0
#define bitePressurePin A7
//Digital pins for menu buttons
#define dPin 3
#define ePin 4
#define uPin 5
#define mPin 6
//Output pins for LEDs (breath, power, status)
#define bLedPin 10
#define pLedPin 9
#define statusLedPin 13
//Analog input for measuring voltage
#define vMeterPin A11
//DAC outputs for analog and pwm
#define dacPin A14
#define pwmDacPin 20
//Which serial port to use for MIDI
#define MIDI_SERIAL Serial3
#define WIDI_SERIAL Serial2
#if defined(REVB)
// MPR121 pins Rev B (angled pins at top edge for main keys and rollers)
#define R1Pin 0
#define R2Pin 2
#define R3Pin 4
#define R4Pin 6
#define R5Pin 8
#define K4Pin 10
#define K1Pin 1
#define K2Pin 3
#define K3Pin 5
#define K5Pin 7
#define K6Pin 9
#define K7Pin 11
/*
* PINOUT ON PCB vs PINS ON MPR121 - Rev. B
*
* (R1) (R2) (R3/6) (R4) (R5) (K4) <-> (00) (02) (04) (06) (08) (10)
*
* (K1) (K2) (K3) (K5) (K6) (K7) <-> (01) (03) (05) (07) (09) (11)
*
*/
# else //REV A
// MPR121 pins Rev A (upright pins below MPR121 for main keys and rollers)
#define R1Pin 10
#define R2Pin 11
#define R3Pin 8
#define R4Pin 9
#define R5Pin 6
#define K4Pin 7
#define K1Pin 4
#define K2Pin 5
#define K3Pin 2
#define K5Pin 3
#define K6Pin 0
#define K7Pin 1
/*
* PINOUT ON PCB vs PINS ON MPR121 - Rev. A
*
* (R2) (R4) (K4) (K2) (K5) (K7) <-> (11) (09) (07) (05) (03) (01)
*
* (R1) (R3/6) (R5) (K1) (K3) (K6) <-> (10) (08) (06) (04) (02) (00)
*
*/
#endif //REVB
#endif //NURAD
#endif

View file

@ -1,72 +0,0 @@
#include <Arduino.h>
#include "hardware.h"
#include "globals.h"
#include "config.h"
// Do things with status LED.
void statusLedOn() {
digitalWrite(statusLedPin, HIGH);
#if defined(SEAMUS)
analogWrite(sLedPin, SPCKEY_LED_BRIGHTNESS);
#endif
}
void statusLedOff() {
digitalWrite(statusLedPin, LOW);
#if defined(SEAMUS)
analogWrite(sLedPin, 0);
#endif
}
void statusLed(bool state) {
digitalWrite(statusLedPin, state);
#if defined(SEAMUS)
analogWrite(sLedPin, state*SPCKEY_LED_BRIGHTNESS);
#endif
}
void statusLedFlip() {
digitalWrite(statusLedPin, !digitalRead(statusLedPin));
#if defined(SEAMUS)
if (digitalRead(statusLedPin)) analogWrite(sLedPin, SPCKEY_LED_BRIGHTNESS); else analogWrite(sLedPin, 0);
#endif
}
void statusLedFlash(uint16_t delayTime) {
statusLedOff();
delay(delayTime/2);
statusLedOn();
delay(delayTime/2);
}
void statusLedBlink() {
statusLedFlash(300);
statusLedFlash(300);
}
void updateSensorLEDs() {
if (breathLevel > breathThrVal) { // breath indicator LED, labeled "B" on PCB
//analogWrite(bLedPin, map(breathLevel,0,4096,5,breathLedBrightness));
analogWrite(bLedPin, map(constrain(breathLevel, breathThrVal, breathMaxVal), breathThrVal, breathMaxVal, MIN_LED_BRIGHTNESS, BREATH_LED_BRIGHTNESS));
} else {
analogWrite(bLedPin, 0);
}
if (portIsOn) { // portamento indicator LED, labeled "P" on PCB
//analogWrite(pLedPin, map(biteSensor,0,4096,5,portamLedBrightness));
analogWrite(pLedPin, map(constrain(oldport, 0, 127), 0, 127, MIN_LED_BRIGHTNESS, PORTAM_LED_BRIGHTNESS));
} else {
analogWrite(pLedPin, 0);
}
#if defined(NURAD)
if (exSensorIndicator){
analogWrite(eLedPin, map(constrain(exSensorIndicator, 0, 127), 0, 127, MIN_LED_BRIGHTNESS, EXTCON_LED_BRIGHTNESS));
} else {
analogWrite(eLedPin, 0);
}
#endif
}
void ledMeter(byte indicatedValue){
analogWrite(bLedPin, map(constrain(indicatedValue, 0, 127), 0, 127, 0, BREATH_LED_BRIGHTNESS)); // full glow at maximum value
analogWrite(pLedPin, map(constrain(indicatedValue, 0, 127), 127, 0, 0, PORTAM_LED_BRIGHTNESS)); // full glow at minimum value
}

File diff suppressed because it is too large Load diff

View file

@ -1,52 +0,0 @@
#ifndef __MENU_H
#define __MENU_H
#include "wiring.h"
#include "numenu.h"
#define MENU_ROW_HEIGHT 9
#define MENU_HEADER_OFFSET 12
#define MENU_NUM_ROWS 6
//display states
#define DISPLAYOFF_IDL 0
#define MAIN_MENU 1
#define PATCH_VIEW 2
#define ADJUST_MENU 3
#define SETUP_BR_MENU 4
#define SETUP_CT_MENU 5
#define ROTATOR_MENU 6
#define VIBRATO_MENU 7
#define ABOUT_MENU 8
#define EXTRAS_MENU 9
#define ROTA_MENU 10
#define ROTB_MENU 11
#define ROTC_MENU 12
#define ARR_LEN(a) (sizeof (a) / sizeof (a[0]))
#define BTN_DOWN 1
#define BTN_ENTER 2
#define BTN_UP 4
#define BTN_MENU 8
extern const unsigned long debounceDelay; // the debounce time; increase if the output flickers
extern const unsigned long buttonRepeatInterval;
extern const unsigned long buttonRepeatDelay;
extern const unsigned long cursorBlinkInterval; // the cursor blink toggle interval time
extern const unsigned long patchViewTimeUp; // ms until patch view shuts off
extern const unsigned long menuTimeUp; // menu shuts off after one minute of button inactivity
extern byte subVibSquelch; // TODO: This is broken <- subVibSquelch is never set, we need another way to expose what menu is open.
void initDisplay();
void showVersion();
void menu();
void drawSensorPixels();
void i2cScanDisplay();
int updateAdjustMenu(uint32_t timeNow, KeyState &input, bool firstRun, bool drawSensor);
bool adjustPageUpdate(KeyState &input, uint32_t timeNow);
#endif

View file

@ -1,37 +0,0 @@
/*
Notes on the original menu implementation
# Menus
## Main Menu
### Transpose
Sub menu with values -12 to 12.
### Octave
Sub menu with values -3 to +3
### MIDI CH
Sub menu with values 0 to 16. Should be 1 to 16, but there might be a bug
either in my simulation code, my changes to the menu or a bug in the original
menu.
### Adjust
This is a special option where the Adjust menu mode is entered. It take over
the display and draw horizontal indicators for threshold and such. More on
this in a later section.
### SETUP BR
Breath setup. Opens a new menu with breath specific stuff.
### SETUP CTL
Controls setup. Opens a new menu.
*/

View file

@ -1,90 +0,0 @@
#ifndef __NUMENU_H
#define __NUMENU_H
#include <stdint.h>
//***********************************************************
struct KeyState {
uint8_t current;
uint8_t changed;
};
//***********************************************************
enum MenuType {
ESub,
EStateChange,
};
enum MenuEntryFlags {
ENone = 0,
EMenuEntryWrap = (1u<<0),
EMenuEntryCustom = (1u<<1),
EMenuEntryEnterHandler = (1u<<2),
};
enum MenuPageFlags {
EMenuPageCustom = (1u<<0),
EMenuPageRoot = (1u<<1),
EMenuCustomTitle = (1u << 2),
};
struct MenuEntry {
enum MenuType type;
const char* title;
};
struct MenuEntrySub;
typedef const MenuEntrySub& SubMenuRef;
struct MenuEntrySub {
enum MenuType type;
const char* title;
const char* subTitle;
uint16_t* valuePtr;
uint16_t min;
uint16_t max;
uint16_t flags;
void (*getSubTextFunc)(SubMenuRef, char*textBuffer, const char**label);
void (*applyFunc)(SubMenuRef);
bool (*onEnterFunc)(void);
};
struct MenuEntryStateCh {
enum MenuType type;
const char* title;
uint8_t state;
};
struct MenuPage {
const char* title;
uint16_t flags;
uint8_t cursor;
uint8_t parentPage;
uint8_t numEntries;
const MenuEntry** entries;
};
struct MenuPageCustom {
const char* title;
uint16_t flags;
bool (*menuUpdateFunc)(KeyState &input, uint32_t timeNow);
};
//***********************************************************
struct AdjustValue {
uint16_t *value;
uint16_t limitLow;
uint16_t limitHigh;
};
struct AdjustMenuEntry {
const char* title;
AdjustValue entries[2];
void (*saveFunc)(const AdjustMenuEntry&);
};
#endif

View file

@ -1,16 +1,23 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:teensy31]
platform = teensy
board = teensy31
framework = arduino
build_flags = -D USB_MIDI -D TEENSY_OPT_FASTER
board_build.f_cpu = 96000000L
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:teensy40]
platform = teensy
board = teensy40
framework = arduino
build_flags = -D USB_MIDI_SERIAL -D TEENSY_OPT_FASTER
board_build.f_cpu = 528000000L
lib_deps =
adafruit/Adafruit MPR121@^1.1.1
adafruit/Adafruit MPRLS Library@^1.2.0
adafruit/Adafruit SSD1306@^2.5.7
mkalkbrenner/WS2812Serial@^0.1.0
adafruit/Adafruit ICM20X@^2.0.5
paulstoffregen/Encoder@^1.4.2

View file

@ -1,618 +0,0 @@
#include <Arduino.h>
#include <EEPROM.h>
#include <Adafruit_SSD1306.h>
#include "settings.h"
#include "globals.h"
#include "menu.h"
#include "hardware.h"
#include "config.h"
#include "midi.h"
#include "led.h"
//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade)
void readEEPROM(const bool factoryReset) {
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
uint16_t settingsVersion = readSetting(VERSION_ADDR);
// blank eeprom will be 0xFFFF. For a full reset, call it "version 0" so everything gets overwritten.
if (factoryReset || settingsVersion == 0xffffu) {
settingsVersion = 0;
}
if(settingsVersion != EEPROM_VERSION) {
if(settingsVersion < 24) { //Oldest version from which any settings are recognized
writeSetting(BREATH_THR_ADDR, BREATH_THR_FACTORY);
writeSetting(BREATH_MAX_ADDR, BREATH_MAX_FACTORY);
#if defined(NURAD)
writeSetting(PORTAM_THR_ADDR, PORTPR_THR_FACTORY);
writeSetting(PORTAM_MAX_ADDR, PORTPR_MAX_FACTORY);
#else
if (digitalRead(biteJumperPin)){ //PBITE (if pulled low with jumper, pressure sensor is used instead of capacitive bite sensing)
writeSetting(PORTAM_THR_ADDR, PORTAM_THR_FACTORY);
writeSetting(PORTAM_MAX_ADDR, PORTAM_MAX_FACTORY);
} else {
writeSetting(PORTAM_THR_ADDR, PORTPR_THR_FACTORY);
writeSetting(PORTAM_MAX_ADDR, PORTPR_MAX_FACTORY);
}
#endif
writeSetting(PITCHB_THR_ADDR, PITCHB_THR_FACTORY);
writeSetting(PITCHB_MAX_ADDR, PITCHB_MAX_FACTORY);
writeSetting(EXTRAC_THR_ADDR, EXTRAC_THR_FACTORY);
writeSetting(EXTRAC_MAX_ADDR, EXTRAC_MAX_FACTORY);
writeSetting(CTOUCH_THR_ADDR, CTOUCH_THR_FACTORY);
writeSetting(TRANSP_ADDR, TRANSP_FACTORY);
writeSetting(MIDI_ADDR, MIDI_FACTORY);
writeSetting(BREATH_CC_ADDR, BREATH_CC_FACTORY);
writeSetting(BREATH_AT_ADDR, BREATH_AT_FACTORY);
writeSetting(VELOCITY_ADDR, VELOCITY_FACTORY);
writeSetting(PORTAM_ADDR, PORTAM_FACTORY);
writeSetting(PB_ADDR, PB_FACTORY);
writeSetting(EXTRA_ADDR, EXTRA_FACTORY);
writeSetting(VIBRATO_ADDR, VIBRATO_FACTORY);
writeSetting(DEGLITCH_ADDR, DEGLITCH_FACTORY);
writeSetting(PATCH_ADDR, PATCH_FACTORY);
writeSetting(OCTAVE_ADDR, OCTAVE_FACTORY);
writeSetting(BREATHCURVE_ADDR, BREATHCURVE_FACTORY);
writeSetting(VEL_SMP_DL_ADDR, VEL_SMP_DL_FACTORY);
writeSetting(VEL_BIAS_ADDR, VEL_BIAS_FACTORY);
writeSetting(PINKY_KEY_ADDR, PINKY_KEY_FACTORY);
}
if(settingsVersion < 26) {
writeSetting(FP1_ADDR, 0);
writeSetting(FP2_ADDR, 0);
writeSetting(FP3_ADDR, 0);
writeSetting(FP4_ADDR, 0);
writeSetting(FP5_ADDR, 0);
writeSetting(FP6_ADDR, 0);
writeSetting(FP7_ADDR, 0);
writeSetting(DIPSW_BITS_ADDR, DIPSW_BITS_FACTORY);
}
if(settingsVersion < 28) {
writeSetting(PARAL_ADDR, PARAL_FACTORY);
writeSetting(ROTN1_ADDR, ROTN1_FACTORY);
writeSetting(ROTN2_ADDR, ROTN2_FACTORY);
writeSetting(ROTN3_ADDR, ROTN3_FACTORY);
writeSetting(ROTN4_ADDR, ROTN4_FACTORY);
writeSetting(PRIO_ADDR, PRIO_FACTORY);
}
if(settingsVersion < 29) {
writeSetting(VIB_SENS_ADDR, VIB_SENS_FACTORY);
writeSetting(VIB_RETN_ADDR, VIB_RETN_FACTORY);
}
if(settingsVersion < 31) {
writeSetting(VIB_SQUELCH_ADDR, VIB_SQUELCH_FACTORY);
writeSetting(VIB_DIRECTION_ADDR, VIB_DIRECTION_FACTORY);
}
if(settingsVersion < 32) {
writeSetting(BREATH_CC2_ADDR, BREATH_CC2_FACTORY);
writeSetting(BREATH_CC2_RISE_ADDR, BREATH_CC2_RISE_FACTORY);
writeSetting(VIB_SENS_BITE_ADDR, VIB_SENS_BITE_FACTORY);
writeSetting(VIB_SQUELCH_BITE_ADDR, VIB_SQUELCH_BITE_FACTORY);
writeSetting(VIB_CONTROL_ADDR, VIB_CONTROL_FACTORY);
writeSetting(TRILL3_INTERVAL_ADDR, TRILL3_INTERVAL_FACTORY);
writeSetting(DAC_MODE_ADDR, DAC_MODE_FACTORY);
}
if(settingsVersion < 33) {
writeSetting(EXTRA2_ADDR, EXTRA2_FACTORY);
writeSetting(LEVEL_CC_ADDR, LEVEL_CC_FACTORY);
writeSetting(LEVEL_VAL_ADDR, LEVEL_VAL_FACTORY);
}
if(settingsVersion < 34) {
writeSetting(FINGER_ADDR, FINGER_FACTORY);
writeSetting(LPINKY3_ADDR, LPINKY3_FACTORY);
}
if(settingsVersion < 35) {
writeSetting(BATTYPE_ADDR, BATTYPE_FACTORY);
writeSetting(HARMSET_ADDR, HARMSET_FACTORY);
writeSetting(HARMSEL_ADDR, HARMSEL_FACTORY);
}
if(settingsVersion < 36) {
writeSetting(PARAB_ADDR, PARAB_FACTORY);
writeSetting(ROTB1_ADDR, ROTB1_FACTORY);
writeSetting(ROTB2_ADDR, ROTB2_FACTORY);
writeSetting(ROTB3_ADDR, ROTB3_FACTORY);
writeSetting(ROTB4_ADDR, ROTB4_FACTORY);
writeSetting(PARAC_ADDR, PARAC_FACTORY);
writeSetting(ROTC1_ADDR, ROTC1_FACTORY);
writeSetting(ROTC2_ADDR, ROTC2_FACTORY);
writeSetting(ROTC3_ADDR, ROTC3_FACTORY);
writeSetting(ROTC4_ADDR, ROTC4_FACTORY);
writeSetting(POLYSEL_ADDR, POLYSEL_FACTORY);
writeSetting(FWCTYPE_ADDR, FWCTYPE_FACTORY);
writeSetting(HMZKEY_ADDR, HMZKEY_FACTORY);
}
if(settingsVersion < 37) {
writeSetting(FWCLCH_ADDR, FWCLCH_FACTORY);
writeSetting(FWCDP2_ADDR, FWCDP2_FACTORY);
}
if(settingsVersion < 38) {
writeSetting(HMZLIMIT_ADDR, HMZLIMIT_FACTORY);
}
if(settingsVersion < 39) {
writeSetting(BRINTERV_ADDR, BRINTERV_FACTORY);
writeSetting(OTFKEY_ADDR, OTFKEY_FACTORY);
}
if(settingsVersion < 40) {
writeSetting(PORTLIMIT_ADDR, PORTLIMIT_FACTORY);
writeSetting(LEVER_THR_ADDR, LEVER_THR_FACTORY);
writeSetting(LEVER_MAX_ADDR, LEVER_MAX_FACTORY);
}
if(settingsVersion < 41) {
writeSetting(BRHARMSET_ADDR, BRHARMSET_FACTORY);
writeSetting(BRHARMSEL_ADDR, BRHARMSEL_FACTORY);
}
if(settingsVersion < 42) {
writeSetting(BITECTL_ADDR, BITECTL_FACTORY);
writeSetting(BITECC_ADDR, BITECC_FACTORY);
writeSetting(LEVERCTL_ADDR, LEVERCTL_FACTORY);
writeSetting(LEVERCC_ADDR, LEVERCC_FACTORY);
}
if(settingsVersion < 43) {
writeSetting(CVTUNE_ADDR, CVTUNE_FACTORY);
writeSetting(CVSCALE_ADDR, CVSCALE_FACTORY);
}
if(settingsVersion < 44) {
writeSetting(CVRATE_ADDR, CVRATE_FACTORY);
}
if(settingsVersion < 45) {
writeSetting(ROLLER_ADDR, ROLLER_FACTORY);
}
writeSetting(VERSION_ADDR, EEPROM_VERSION);
}
// read all settings from EEPROM
breathThrVal = readSettingBounded(BREATH_THR_ADDR, breathLoLimit, breathHiLimit, BREATH_THR_FACTORY);
breathMaxVal = readSettingBounded(BREATH_MAX_ADDR, breathLoLimit, breathHiLimit, BREATH_MAX_FACTORY);
portamThrVal = readSettingBounded(PORTAM_THR_ADDR, portamLoLimit, portamHiLimit, PORTAM_THR_FACTORY);
portamMaxVal = readSettingBounded(PORTAM_MAX_ADDR, portamLoLimit, portamHiLimit, PORTAM_MAX_FACTORY);
pitchbThrVal = readSettingBounded(PITCHB_THR_ADDR, pitchbLoLimit, pitchbHiLimit, PITCHB_THR_FACTORY);
pitchbMaxVal = readSettingBounded(PITCHB_MAX_ADDR, pitchbLoLimit, pitchbHiLimit, PITCHB_MAX_FACTORY);
transpose = readSettingBounded(TRANSP_ADDR, 0, 24, TRANSP_FACTORY);
MIDIchannel = readSettingBounded(MIDI_ADDR, 1, 16, MIDI_FACTORY);
breathCC = readSettingBounded(BREATH_CC_ADDR, 0, 10, BREATH_CC_FACTORY);
breathAT = readSettingBounded(BREATH_AT_ADDR, 0, 1, BREATH_AT_FACTORY);
velocity = readSettingBounded(VELOCITY_ADDR, 0, 127, VELOCITY_FACTORY);
portamento = readSettingBounded(PORTAM_ADDR, 0, 5, PORTAM_FACTORY);
PBdepth = readSettingBounded(PB_ADDR, 0, 12, PB_FACTORY);
extraCT = readSettingBounded(EXTRA_ADDR, 0, 4, EXTRA_FACTORY);
vibrato = readSettingBounded(VIBRATO_ADDR, 0, 9, VIBRATO_FACTORY);
deglitch = readSettingBounded(DEGLITCH_ADDR, 0, 70, DEGLITCH_FACTORY);
extracThrVal = readSettingBounded(EXTRAC_THR_ADDR, extracLoLimit, extracHiLimit, EXTRAC_THR_FACTORY);
extracMaxVal = readSettingBounded(EXTRAC_MAX_ADDR, extracLoLimit, extracHiLimit, EXTRAC_MAX_FACTORY);
patch = readSettingBounded(PATCH_ADDR, 0, 127, PATCH_FACTORY);
octave = readSettingBounded(OCTAVE_ADDR, 0, 6, OCTAVE_FACTORY);
ctouchThrVal = readSettingBounded(CTOUCH_THR_ADDR, ctouchLoLimit, ctouchHiLimit, CTOUCH_THR_FACTORY);
curve = readSettingBounded(BREATHCURVE_ADDR, 0, 12, BREATHCURVE_FACTORY);
velSmpDl = readSettingBounded(VEL_SMP_DL_ADDR, 0, 30, VEL_SMP_DL_FACTORY);
velBias = readSettingBounded(VEL_BIAS_ADDR, 0, 9, VEL_BIAS_FACTORY);
pinkySetting = readSettingBounded(PINKY_KEY_ADDR, 0, 31, PINKY_KEY_FACTORY);
fastPatch[0] = readSettingBounded(FP1_ADDR, 0, 127, 0);
fastPatch[1] = readSettingBounded(FP2_ADDR, 0, 127, 0);
fastPatch[2] = readSettingBounded(FP3_ADDR, 0, 127, 0);
fastPatch[3] = readSettingBounded(FP4_ADDR, 0, 127, 0);
fastPatch[4] = readSettingBounded(FP5_ADDR, 0, 127, 0);
fastPatch[5] = readSettingBounded(FP6_ADDR, 0, 127, 0);
fastPatch[6] = readSettingBounded(FP7_ADDR, 0, 127, 0);
dipSwBits = readSetting(DIPSW_BITS_ADDR);
rotations_a.parallel = readSettingBounded(PARAL_ADDR, 0, 48, PARAL_FACTORY);
rotations_a.rotations[0] = readSettingBounded(ROTN1_ADDR, 0, 48, ROTN1_FACTORY);
rotations_a.rotations[1] = readSettingBounded(ROTN2_ADDR, 0, 48, ROTN2_FACTORY);
rotations_a.rotations[2] = readSettingBounded(ROTN3_ADDR, 0, 48, ROTN3_FACTORY);
rotations_a.rotations[3] = readSettingBounded(ROTN4_ADDR, 0, 48, ROTN4_FACTORY);
priority = readSettingBounded(PRIO_ADDR, 0, 1, PRIO_FACTORY);
vibSens = readSettingBounded(VIB_SENS_ADDR, 1, 12, VIB_SENS_FACTORY);
vibRetn = readSettingBounded(VIB_RETN_ADDR, 0, 4, VIB_RETN_FACTORY);
vibSquelch = readSettingBounded(VIB_SQUELCH_ADDR, 1, 30, VIB_SQUELCH_FACTORY);
vibDirection = readSettingBounded(VIB_DIRECTION_ADDR, 0, 1, VIB_DIRECTION_FACTORY);
breathCC2 = readSettingBounded(BREATH_CC2_ADDR, 0, 127, BREATH_CC2_FACTORY);
breathCC2Rise = readSettingBounded(BREATH_CC2_RISE_ADDR, 1, 10, BREATH_CC2_RISE_FACTORY);
vibSensBite = readSettingBounded(VIB_SENS_BITE_ADDR, 1, 17, VIB_SENS_BITE_FACTORY);
vibSquelchBite = readSettingBounded(VIB_SQUELCH_BITE_ADDR, 1, 30, VIB_SQUELCH_BITE_FACTORY);
vibControl = readSettingBounded(VIB_CONTROL_ADDR, 0, 2, VIB_CONTROL_FACTORY);
dacMode = readSettingBounded(DAC_MODE_ADDR, DAC_MODE_BREATH, DAC_MODE_PITCH, DAC_MODE_FACTORY);
trill3_interval = readSettingBounded(TRILL3_INTERVAL_ADDR, 3, 4, TRILL3_INTERVAL_FACTORY);
extraCT2 = readSettingBounded(EXTRA2_ADDR, 0, 127, EXTRA2_FACTORY);
levelCC = readSettingBounded(LEVEL_CC_ADDR, 0, 127, LEVEL_CC_FACTORY);
levelVal = readSettingBounded(LEVEL_VAL_ADDR, 0, 127, LEVEL_VAL_FACTORY);
#if defined(NURAD)
fingering = readSettingBounded(FINGER_ADDR, 0, 4, FINGER_FACTORY);
#else
fingering = readSettingBounded(FINGER_ADDR, 0, 3, FINGER_FACTORY);
#endif
lpinky3 = readSettingBounded(LPINKY3_ADDR, 0, 25, LPINKY3_FACTORY);
batteryType = readSettingBounded(BATTYPE_ADDR, 0, 2, BATTYPE_FACTORY);
harmSetting = readSettingBounded(HARMSET_ADDR, 0, 6, HARMSET_FACTORY);
harmSelect = readSettingBounded(HARMSEL_ADDR, 0, 7, HARMSEL_FACTORY);
polySelect = (PolySelect)readSettingBounded(POLYSEL_ADDR, 0, 10, POLYSEL_FACTORY);
fwcType = readSettingBounded(FWCTYPE_ADDR, 0, 4, FWCTYPE_FACTORY);
fwcLockH = readSettingBounded(FWCLCH_ADDR, 0, 1, FWCLCH_FACTORY);
fwcDrop2 = readSettingBounded(FWCDP2_ADDR, 0, 1, FWCDP2_FACTORY);
hmzKey = readSettingBounded(HMZKEY_ADDR, 0, 11, HMZKEY_FACTORY);
hmzLimit = readSettingBounded(HMZLIMIT_ADDR, 2, 5, HMZLIMIT_FACTORY);
rotations_b.parallel = readSettingBounded(PARAB_ADDR, 0, 48, PARAB_FACTORY);
rotations_b.rotations[0] = readSettingBounded(ROTB1_ADDR, 0, 48, ROTB1_FACTORY);
rotations_b.rotations[1] = readSettingBounded(ROTB2_ADDR, 0, 48, ROTB2_FACTORY);
rotations_b.rotations[2] = readSettingBounded(ROTB3_ADDR, 0, 48, ROTB3_FACTORY);
rotations_b.rotations[3] = readSettingBounded(ROTB4_ADDR, 0, 48, ROTB4_FACTORY);
rotations_c.parallel = readSettingBounded(PARAC_ADDR, 0, 48, PARAC_FACTORY);
rotations_c.rotations[0] = readSettingBounded(ROTC1_ADDR, 0, 48, ROTC1_FACTORY);
rotations_c.rotations[1] = readSettingBounded(ROTC2_ADDR, 0, 48, ROTC2_FACTORY);
rotations_c.rotations[2] = readSettingBounded(ROTC3_ADDR, 0, 48, ROTC3_FACTORY);
rotations_c.rotations[3] = readSettingBounded(ROTC4_ADDR, 0, 48, ROTC4_FACTORY);
otfKey = readSettingBounded(OTFKEY_ADDR, 0, 1, OTFKEY_FACTORY);
breathInterval = readSettingBounded(BRINTERV_ADDR, 3, 15, BRINTERV_FACTORY);
portLimit = readSettingBounded(PORTLIMIT_ADDR, 1, 127, PORTLIMIT_FACTORY);
leverThrVal = readSettingBounded(LEVER_THR_ADDR, leverLoLimit, leverHiLimit, LEVER_THR_FACTORY);
leverMaxVal = readSettingBounded(LEVER_MAX_ADDR, leverLoLimit, leverHiLimit, LEVER_MAX_FACTORY);
brHarmSetting = readSettingBounded(BRHARMSET_ADDR, 0, 6, BRHARMSET_FACTORY);
brHarmSelect = readSettingBounded(BRHARMSEL_ADDR, 0, 3, BRHARMSEL_FACTORY);
biteControl = readSettingBounded(BITECTL_ADDR, 0, 3, BITECTL_FACTORY);
leverControl = readSettingBounded(LEVERCTL_ADDR, 0, 3, LEVERCTL_FACTORY);
biteCC = readSettingBounded(BITECC_ADDR, 0, 127, BITECC_FACTORY);
leverCC = readSettingBounded(LEVERCC_ADDR, 0, 127, LEVERCC_FACTORY);
cvTune = readSettingBounded(CVTUNE_ADDR, 1, 199, CVTUNE_FACTORY);
cvScale = readSettingBounded(CVSCALE_ADDR, 1, 199, CVSCALE_FACTORY);
cvVibRate = readSettingBounded(CVRATE_ADDR, 0, 8, CVRATE_FACTORY);
rollerMode = readSettingBounded(ROLLER_ADDR, 0, 3, ROLLER_FACTORY);
//Flags stored in bit field
fastBoot = (dipSwBits & (1<<DIPSW_FASTBOOT))?1:0;
legacy = (dipSwBits & (1<<DIPSW_LEGACY))?1:0;
legacyBrAct = (dipSwBits & (1<<DIPSW_LEGACYBRACT))?1:0;
widiOn = (dipSwBits & (1<<DIPSW_WIDION))?1:0;
gateOpenEnable = (dipSwBits & (1<<DIPSW_GATEOPEN))?1:0;
specialKeyEnable = (dipSwBits & (1<<DIPSW_SPKEYENABLE))?1:0;
bcasMode = (dipSwBits & (1<<DIPSW_BCASMODE))?1:0;
}
//Poke at a certain bit in a bit field
void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value) {
bitfield = (bitfield & ~(1<<pos)) | ((value?1:0)<<pos);
}
//Read and write EEPROM data
void writeSetting(const uint16_t address, const uint16_t value) {
union {
uint8_t v[2];
uint16_t val;
} data;
data.val = value;
EEPROM.update(address, data.v[0]);
EEPROM.update(address+1, data.v[1]);
}
uint16_t readSetting(const uint16_t address) {
union {
uint8_t v[2];
uint16_t val;
} data;
data.v[0] = EEPROM.read(address);
data.v[1] = EEPROM.read(address+1);
return data.val;
}
uint16_t readSettingBounded(const uint16_t address, const uint16_t min, const uint16_t max, const uint16_t defaultValue) {
uint16_t val = readSetting(address);
if(val < min || val > max) {
val = defaultValue;
writeSetting(address, val);
}
return val;
}
//Functions to send and receive config (and other things) via USB MIDI SysEx messages
uint32_t crc32(const uint8_t *message, const size_t length) {
size_t pos=0;
uint32_t crc=0xFFFFFFFF;
while (pos<length) {
crc ^= message[pos++]; //Get next byte and increment position
for (uint8_t j=0; j<8; ++j) { //Mask off 8 next bits
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
}
}
return ~crc;
}
/*
Send EEPROM config dump as sysex message. Message format is structured like this:
+------------------------------------------------------------------------------------+
| vendor(3) | "NuEVIc01" (8) | Payload size (2) | EEPROM data (variable) | crc32 (4) |
+------------------------------------------------------------------------------------+
Payload size is for the EEPROM data chunk (not including anything else before or after
CRC32 covers the entire buffer up to and including the eeprom data (but not the checksum itself)
This currently operates under the assumption that the whole EEPROM chunk only consists of unsigned 16 bit ints, only using the range 0-16383
*/
void sendSysexSettings() {
const char *header = "NuEVIc01"; //NuEVI config dump 01
//Build a send buffer of all the things
size_t sysex_size = 3 + strlen(header) + 2 + EEPROM_SIZE + 4;
uint8_t *sysex_data = (uint8_t*)malloc(sysex_size);
//Positions (offsets) of parts in send buffer
int header_pos = 3;
int size_pos = header_pos + strlen(header);
int payload_pos = size_pos + 2;
int checksum_pos = payload_pos + EEPROM_SIZE;
//SysEX manufacturer ID
memcpy(sysex_data, sysex_id, 3);
//Header with command code
memcpy(sysex_data+header_pos, header, strlen(header));
//Payload length
*(uint16_t*)(sysex_data+size_pos) = convertToMidiValue(EEPROM_SIZE);
//Config data
uint16_t* config_buffer_start = (uint16_t*)(sysex_data+payload_pos);
//Read one settings item at a time, change data format, and put in send buffer
for(uint16_t idx=0; idx<EEPROM_SIZE/2; idx++) {
uint16_t eepromval = readSetting(idx*2);
config_buffer_start[idx] = convertToMidiValue(eepromval);
}
uint32_t checksum = crc32(sysex_data, checksum_pos);
*(uint32_t*)(sysex_data+checksum_pos) = convertToMidiCRC(checksum);
usbMIDI.sendSysEx(sysex_size, sysex_data);
free(sysex_data);
}
//Send a simple 3-byte message code as sysex
void sendSysexMessage(const char* messageCode) {
char sysexMessage[] = "vvvNuEVIccc"; //Placeholders for vendor and code
memcpy(sysexMessage, sysex_id, 3);
memcpy(sysexMessage+8, messageCode, 3);
usbMIDI.sendSysEx(11, (const uint8_t *)sysexMessage);
}
bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
//Expected size of data (vendor+NuEVIc02+len+payload+crc32)
uint16_t expected_size = 3 + 8 + 2 + EEPROM_SIZE + 4;
//Positions (offsets) of parts in buffer
int size_pos = 11;
int payload_pos = size_pos + 2;
int checksum_pos = payload_pos + EEPROM_SIZE;
//Make sure length of receive buffer is enough to read all we need to. We can accept extra junk at the end though.
if(length<expected_size) {
configShowMessage("Invalid config format");
return false;
}
//No need to verify vendor or header/command, already done before we get here.
//Calculate checksum of stuff received (everything before checksum), transform to midi format
//(being a one-way operation, we can't do the reverse anyway)
uint32_t crc=convertToMidiCRC(crc32(data, checksum_pos));
uint32_t crc_rcv;
memcpy(&crc_rcv, data+checksum_pos, 4);
if(crc != crc_rcv && crc_rcv != NO_CHECKSUM) {
configShowMessage("Invalid checksum");
return false;
}
//Verify that payload size matches the size of our EEPROM config
uint16_t payload_size = convertFromMidiValue(data+size_pos);
if(payload_size != EEPROM_SIZE) {
configShowMessage("Invalid config size");
return false;
}
uint16_t eeprom_version_rcv = convertFromMidiValue(data+(payload_pos+VERSION_ADDR));
if(eeprom_version_rcv != EEPROM_VERSION) {
configShowMessage("Invalid config version");
return false;
}
//Grab all the items in payload and save to EEPROM
for(uint16_t i=0; i<payload_size/2; i++) {
uint16_t addr = i*2;
uint16_t val;
val = convertFromMidiValue(data+(payload_pos+addr));
//Skip sensor calibration values if they are "out of bounds". This makes it possible to send a config that does
//not overwrite sensor calibration.
if(addr == BREATH_THR_ADDR || addr == BREATH_MAX_ADDR) {
if(val<breathLoLimit || val>breathHiLimit) continue;
}
if(addr == PORTAM_THR_ADDR || addr == PORTAM_MAX_ADDR) {
if(val<portamLoLimit || val>portamHiLimit) continue;
}
if(addr == PITCHB_THR_ADDR || addr == PITCHB_MAX_ADDR) {
if(val<pitchbLoLimit || val>pitchbHiLimit) continue;
}
if(addr == EXTRAC_THR_ADDR || addr == EXTRAC_MAX_ADDR) {
if(val<extracLoLimit || val>extracHiLimit) continue;
}
if(addr == CTOUCH_THR_ADDR) {
if(val<ctouchLoLimit || val>ctouchHiLimit) continue;
}
writeSetting(addr, val);
}
//All went well
return true;
}
//Send EEPROM and firmware versions
void sendSysexVersion() {
char sysexMessage[] = "vvvNuEVIc04eevvvvvvvv"; //Placeholders for vendor and code
uint8_t fwStrLen = min(strlen(FIRMWARE_VERSION), 8); //Limit firmware version string to 8 bytes
memcpy(sysexMessage, sysex_id, 3);
memcpy(sysexMessage+13, FIRMWARE_VERSION, fwStrLen);
*(uint16_t*)(sysexMessage+11) = convertToMidiValue(EEPROM_VERSION);
uint8_t message_length = 13+fwStrLen;
usbMIDI.sendSysEx(message_length, (const uint8_t *)sysexMessage);
}
extern Adafruit_SSD1306 display;
void configShowMessage(const char* message) {
display.fillRect(0,32,128,64,BLACK);
display.setCursor(0,32);
display.setTextColor(WHITE);
display.print(message);
display.display();
}
uint8_t* sysex_rcv_buffer = NULL;
uint16_t sysex_buf_size = 0;
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last) {
uint16_t pos;
if(!sysex_rcv_buffer) {
//Start out with an empty buffer
pos = 0;
sysex_buf_size = length;
sysex_rcv_buffer = (uint8_t *)malloc(sysex_buf_size);
} else {
//Increase size of current buffer
pos = sysex_buf_size;
sysex_buf_size += length;
sysex_rcv_buffer = (uint8_t *)realloc(sysex_rcv_buffer, sysex_buf_size);
}
//Append this chunk to buffer
memcpy(sysex_rcv_buffer + pos, data, length);
//If it's the last one, call the regular handler to process it
if(last) {
handleSysex(sysex_rcv_buffer, sysex_buf_size);
//Discard the buffer
free(sysex_rcv_buffer);
sysex_rcv_buffer = NULL;
sysex_buf_size = 0;
}
}
void handleSysex(uint8_t *data, unsigned int length) {
//Note: Sysex data as received here contains sysex start and end markers (0xF0 and 0xF7)
//Too short to even contain a 3-byte vendor id is not for us.
if(length<4) return;
//Verify vendor
if(strncmp((char*)(data+1), sysex_id, 3)) return; //Silently ignore different vendor id
//Verify header. Min length is 3+5+3 bytes (vendor+header+message code)
if(length<12 || strncmp((char*)(data+4), "NuEVI", 5)) {
configShowMessage("Invalid message.");
sendSysexMessage("e00");
return;
}
//Get message code
char messageCode[3];
strncpy(messageCode, (char*)(data+9), 3);
if(!strncmp(messageCode, "c00", 3)) { //Config dump request
configShowMessage("Sending config...");
sendSysexSettings();
configShowMessage("Config sent.");
} else if(!strncmp(messageCode, "c03", 3)) { //Version info request
configShowMessage("Sending version.");
sendSysexVersion();
} else if(!strncmp(messageCode, "c02", 3)) { //New config incoming
configShowMessage("Receiving config...");
//Tell receiveSysexSettings about what's between sysex start and end markers
if(receiveSysexSettings(data+1, length-2)) configShowMessage("New config saved.");
} else {
configShowMessage("Unknown message.");
sendSysexMessage("e01"); //Unimplemented message code
}
}
void configModeSetup() {
statusLedFlash(500);
display.clearDisplay();
display.setCursor(0,0);
display.setTextColor(WHITE);
display.setTextSize(0);
display.println("Config mgmt");
display.println("Power off NuEVI");
display.println("to exit");
display.display();
usbMIDI.setHandleSystemExclusive(handleSysexChunk);
statusLedFlash(500);
sendSysexVersion(); //Friendly hello
configShowMessage("Ready.");
}
//"Main loop". Just sits and wait for midi messages and lets the sysex handler do all the work.
void configModeLoop() {
usbMIDI.read();
}

View file

@ -1,226 +0,0 @@
#ifndef __SETTINGS_H
#define __SETTINGS_H
#include <stdint.h>
// EEPROM addresses for settings
#define VERSION_ADDR 0
#define BREATH_THR_ADDR 2
#define BREATH_MAX_ADDR 4
#define PORTAM_THR_ADDR 6
#define PORTAM_MAX_ADDR 8
#define PITCHB_THR_ADDR 10
#define PITCHB_MAX_ADDR 12
#define TRANSP_ADDR 14
#define MIDI_ADDR 16
#define BREATH_CC_ADDR 18
#define BREATH_AT_ADDR 20
#define VELOCITY_ADDR 22
#define PORTAM_ADDR 24
#define PB_ADDR 26
#define EXTRA_ADDR 28
#define VIBRATO_ADDR 30
#define DEGLITCH_ADDR 32
#define EXTRAC_THR_ADDR 34
#define EXTRAC_MAX_ADDR 36
#define PATCH_ADDR 38
#define OCTAVE_ADDR 40
#define CTOUCH_THR_ADDR 42
#define BREATHCURVE_ADDR 44
#define VEL_SMP_DL_ADDR 46
#define VEL_BIAS_ADDR 48
#define PINKY_KEY_ADDR 50
#define FP1_ADDR 52
#define FP2_ADDR 54
#define FP3_ADDR 56
#define FP4_ADDR 58
#define FP5_ADDR 60
#define FP6_ADDR 62
#define FP7_ADDR 64
#define DIPSW_BITS_ADDR 66
#define PARAL_ADDR 68
#define ROTN1_ADDR 70
#define ROTN2_ADDR 72
#define ROTN3_ADDR 74
#define ROTN4_ADDR 76
#define PRIO_ADDR 78
#define VIB_SENS_ADDR 80
#define VIB_RETN_ADDR 82
#define VIB_SQUELCH_ADDR 84
#define VIB_DIRECTION_ADDR 86
#define BREATH_CC2_ADDR 88
#define BREATH_CC2_RISE_ADDR 90
#define VIB_SENS_BITE_ADDR 92
#define VIB_SQUELCH_BITE_ADDR 94
#define VIB_CONTROL_ADDR 96
#define TRILL3_INTERVAL_ADDR 98
#define DAC_MODE_ADDR 100
#define EXTRA2_ADDR 102
#define LEVEL_CC_ADDR 104
#define LEVEL_VAL_ADDR 106
#define FINGER_ADDR 108
#define LPINKY3_ADDR 110
#define BATTYPE_ADDR 112
#define HARMSET_ADDR 114
#define HARMSEL_ADDR 116
#define PARAB_ADDR 118
#define ROTB1_ADDR 120
#define ROTB2_ADDR 122
#define ROTB3_ADDR 124
#define ROTB4_ADDR 126
#define PARAC_ADDR 128
#define ROTC1_ADDR 130
#define ROTC2_ADDR 132
#define ROTC3_ADDR 134
#define ROTC4_ADDR 136
#define POLYSEL_ADDR 138
#define FWCTYPE_ADDR 140
#define HMZKEY_ADDR 150
#define FWCLCH_ADDR 152
#define FWCDP2_ADDR 154
#define HMZLIMIT_ADDR 156
#define BRINTERV_ADDR 158
#define OTFKEY_ADDR 160
#define PORTLIMIT_ADDR 162
#define LEVER_THR_ADDR 164
#define LEVER_MAX_ADDR 166
#define BRHARMSET_ADDR 168
#define BRHARMSEL_ADDR 170
#define BITECTL_ADDR 172
#define BITECC_ADDR 174
#define LEVERCTL_ADDR 176
#define LEVERCC_ADDR 178
#define CVTUNE_ADDR 180
#define CVSCALE_ADDR 182
#define CVRATE_ADDR 184
#define ROLLER_ADDR 186
#define EEPROM_SIZE 188 //Last address +2
//DAC output modes
#define DAC_MODE_BREATH 0
#define DAC_MODE_PITCH 1
#define DIPSW_FASTBOOT 0
#define DIPSW_LEGACY 1
#define DIPSW_LEGACYBRACT 2
#define DIPSW_WIDION 3
#define DIPSW_GATEOPEN 4
#define DIPSW_SPKEYENABLE 5
#define DIPSW_BCASMODE 6
//"factory" values for settings
#define EEPROM_VERSION 45
#define BREATH_THR_FACTORY 1400
#define BREATH_MAX_FACTORY 4000
#define PORTAM_THR_FACTORY 2600
#define PORTAM_MAX_FACTORY 3300
#define PORTPR_THR_FACTORY 1200
#define PORTPR_MAX_FACTORY 2000
#define PITCHB_THR_FACTORY 2000
#define PITCHB_MAX_FACTORY 3000
#define EXTRAC_THR_FACTORY 2800
#define EXTRAC_MAX_FACTORY 3500
#define LEVER_THR_FACTORY 1700
#define LEVER_MAX_FACTORY 1800
#define TRANSP_FACTORY 12 // 12 is 0 transpose
#define MIDI_FACTORY 1 // 1-16
#define BREATH_CC_FACTORY 2 //thats CC#2, see ccList
#define BREATH_AT_FACTORY 0 //aftertouch default off
#define VELOCITY_FACTORY 0 // 0 is dynamic/breath controlled velocity
#define PORTAM_FACTORY 2 // 0 - OFF, 1 - ON, 2 - SW
#define PB_FACTORY 1 // 0 - OFF, 1 - 12
#define EXTRA_FACTORY 1 // 0 - OFF, 1 - Modulation wheel, 2 - Foot pedal, 3 - Filter Cutoff, 4 - Sustain pedal
#define VIBRATO_FACTORY 4 // 0 - OFF, 1 - 9 depth
#define DEGLITCH_FACTORY 20 // 0 - OFF, 5 to 70 ms in steps of 5
#define PATCH_FACTORY 1 // MIDI program change 1-128
#define OCTAVE_FACTORY 3 // 3 is 0 octave change
#define CTOUCH_THR_FACTORY 125 // MPR121 touch threshold
#define BREATHCURVE_FACTORY 4 // 0 to 12 (-4 to +4, S1 to S4)
#define VEL_SMP_DL_FACTORY 20 // 0 to 30
#define VEL_BIAS_FACTORY 0 // 0 to 9
#define PINKY_KEY_FACTORY 12 // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 22 (QuickTranspose +1 to +12)
#define DIPSW_BITS_FACTORY 0 // virtual dip switch settings for special modes (work in progress)
#define PARAL_FACTORY 31 // 7 (+ 24) Rotator parallel
#define ROTN1_FACTORY 19 // -5 (+24) Rotation 1
#define ROTN2_FACTORY 14 // -10 (+24) Rotation 2
#define ROTN3_FACTORY 17 // -7 (+24) Rotation 3
#define ROTN4_FACTORY 10 // -14 (+24) Rotation 4
#define PRIO_FACTORY 0 // Mono priority 0 - BAS(e note), 1 - ROT(ating note)
#define VIB_SENS_FACTORY 6 // 1 least sensitive, higher more sensitive
#define VIB_RETN_FACTORY 2 // 0, no return, 1 slow return, higher faster return
#define VIB_SQUELCH_FACTORY 12 // 0 to 30, vib signal squelch
#define VIB_DIRECTION_FACTORY 0
#define BREATH_CC2_FACTORY 0 //OFF,1-127
#define BREATH_CC2_RISE_FACTORY 1
#define VIB_SENS_BITE_FACTORY 8
#define VIB_SQUELCH_BITE_FACTORY 15
#define VIB_CONTROL_FACTORY 0
#define TRILL3_INTERVAL_FACTORY 4
#define DAC_MODE_FACTORY DAC_MODE_PITCH
#define EXTRA2_FACTORY 0
#define LEVEL_CC_FACTORY 11
#define LEVEL_VAL_FACTORY 127
#define FINGER_FACTORY 0
#define LPINKY3_FACTORY 0
#define BATTYPE_FACTORY 0
#define HARMSET_FACTORY 0
#define HARMSEL_FACTORY 0
#define PARAB_FACTORY 31 // 7 (+ 24) Rotator parallel
#define ROTB1_FACTORY 19 // -5 (+24) Rotation 1
#define ROTB2_FACTORY 14 // -10 (+24) Rotation 2
#define ROTB3_FACTORY 17 // -7 (+24) Rotation 3
#define ROTB4_FACTORY 10 // -14 (+24) Rotation 4
#define PARAC_FACTORY 31 // 7 (+ 24) Rotator parallel
#define ROTC1_FACTORY 19 // -5 (+24) Rotation 1
#define ROTC2_FACTORY 14 // -10 (+24) Rotation 2
#define ROTC3_FACTORY 17 // -7 (+24) Rotation 3
#define ROTC4_FACTORY 10 // -14 (+24) Rotation 4
#define POLYSEL_FACTORY 0
#define FWCTYPE_FACTORY 0
#define HMZKEY_FACTORY 0
#define FWCLCH_FACTORY 0
#define FWCDP2_FACTORY 0
#define HMZLIMIT_FACTORY 5
#define BRINTERV_FACTORY 6
#define OTFKEY_FACTORY 0
#define PORTLIMIT_FACTORY 127
#define BRHARMSET_FACTORY 0
#define BRHARMSEL_FACTORY 0
#define BITECTL_FACTORY 2 // GLD
#define LEVERCTL_FACTORY 1 // VIB
#define BITECC_FACTORY 1 //Mod Wheel
#define LEVERCC_FACTORY 11 //Expression
#define CVTUNE_FACTORY 100 // 100 is zero tuning
#define CVSCALE_FACTORY 100 // 100 is zero scaling
#define CVRATE_FACTORY 3 // 3 is 5.5Hz
#define ROLLER_FACTORY 1
#define NO_CHECKSUM 0x7F007F00
void readEEPROM(const bool factoryReset);
void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value);
uint16_t readSetting(const uint16_t address);
void writeSetting(const uint16_t address, const uint16_t value);
uint16_t readSettingBounded(const uint16_t address, const uint16_t min, const uint16_t max, const uint16_t defaultValue);
//Functions for config management mode
void sendSysexSettings();
void sendSysexMessage(const char* messageCode);
void sendSysexVersion();
void handleSysex(uint8_t *data, unsigned int length);
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last);
uint32_t crc32(const uint8_t *message, const size_t length);
void configInitScreen();
void configShowMessage(const char* message);
void configModeSetup();
void configModeLoop();
#endif

478
NuEVI/FilterOnePole.cpp → NuEVI/src/FilterOnePole.cpp Executable file → Normal file
View file

@ -1,240 +1,238 @@
// Copyright 2014 Jonathan Driscoll
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FilterOnePole.h"
#include <Arduino.h>
FilterOnePole::FilterOnePole( FILTER_TYPE ft, float fc, float initialValue ) {
setFilter( ft, fc, initialValue );
}
void FilterOnePole::setFilter( FILTER_TYPE ft, float fc, float initialValue ) {
FT = ft;
setFrequency( fc );
Y = initialValue;
Ylast = initialValue;
X = initialValue;
LastUS = micros();
}
float FilterOnePole::input( float inVal ) {
long time = micros();
ElapsedUS = float(time - LastUS); // cast to float here, for math
LastUS = time; // update this now
// shift the data values
Ylast = Y;
X = inVal; // this is now the most recent input value
// filter value is controlled by a parameter called X
// tau is set by the user in microseconds, but must be converted to samples here
TauSamps = TauUS / ElapsedUS;
float ampFactor;
#ifdef ARM_FLOAT
ampFactor = expf( -1.0 / TauSamps ); // this is 1 if called quickly
#else
ampFactor = exp( -1.0 / TauSamps ); // this is 1 if called quickly
#endif
Y = (1.0-ampFactor)*X + ampFactor*Ylast; // set the new value
return output();
}
void FilterOnePole::setFrequency( float newFrequency ) {
setTau( 1.0/(TWO_PI*newFrequency ) ); // τ=1/ω
}
void FilterOnePole::setTau( float newTau ) {
TauUS = newTau * 1e6;
}
float FilterOnePole::output() {
// figure out which button to read
switch (FT) {
case LOWPASS:
// return the last value
return Y;
break;
case INTEGRATOR:
// using a lowpass, but normaize
return Y * (TauUS/1.0e6);
break;
case HIGHPASS:
// highpass is the _difference_
return X-Y;
break;
case DIFFERENTIATOR:
// like a highpass, but normalize
return (X-Y)/(TauUS/1.0e6);
break;
default:
// should never get to here, return 0 just in case
return 0;
}
}
void FilterOnePole::print() {
Serial.println("");
Serial.print(" Y: "); Serial.print( Y );
Serial.print(" Ylast: "); Serial.print( Ylast );
Serial.print(" X "); Serial.print( X );
Serial.print(" ElapsedUS "); Serial.print( ElapsedUS );
Serial.print(" TauSamps: "); Serial.print( TauSamps );
//Serial.print(" ampFactor " ); Serial.print( ampFactor );
Serial.print(" TauUS: "); Serial.print( TauUS );
Serial.println("");
}
void FilterOnePole::test() {
float tau = 10;
float updateInterval = 1;
float nextupdateTime = millis()*1e-3;
float inputValue = 0;
FilterOnePole hp( HIGHPASS, tau, inputValue );
FilterOnePole lp( LOWPASS, tau, inputValue );
while( true ) {
float now = millis()*1e-3;
// switch input values on a 20 second cycle
if( round(now/20.0)-(now/20.0) < 0 )
inputValue = 0;
else
inputValue = 100;
hp.input(inputValue);
lp.input(inputValue);
if( now > nextupdateTime ) {
nextupdateTime += updateInterval;
Serial.print("inputValue: "); Serial.print( inputValue );
Serial.print("\t high-passed: "); Serial.print( hp.output() );
Serial.print("\t low-passed: "); Serial.print( lp.output() );
Serial.println();
}
}
}
void FilterOnePole::setToNewValue( float newVal ) {
Y = Ylast = X = newVal;
}
// stuff for filter2 (lowpass only)
// should be able to set a separate fall time as well
FilterOnePoleCascade::FilterOnePoleCascade( float riseTime, float initialValue ) {
setRiseTime( riseTime );
setToNewValue( initialValue );
}
void FilterOnePoleCascade::setRiseTime( float riseTime ) {
float tauScale = 3.36; // found emperically, by running test();
Pole1.setTau( riseTime / tauScale );
Pole2.setTau( riseTime / tauScale );
}
float FilterOnePoleCascade::input( float inVal ) {
Pole2.input( Pole1.input( inVal ));
return output();
}
// clears out the values in the filter
void FilterOnePoleCascade::setToNewValue( float newVal ) {
Pole1.setToNewValue( newVal );
Pole2.setToNewValue( newVal );
}
float FilterOnePoleCascade::output() {
return Pole2.output();
}
void FilterOnePoleCascade::test() {
// make a filter, how fast does it run:
float rise = 1.0;
FilterOnePoleCascade myFilter( rise );
// first, test the filter speed ...
long nLoops = 1000;
Serial.print( "testing filter with a rise time of ");
Serial.print( rise ); Serial.print( "s" );
Serial.print( "\n running filter speed loop ... ");
float startTime, stopTime;
startTime = millis()*1e-3;
for( long i=0; i<nLoops; ++i ) {
myFilter.input( PI ); // use pi, so it will actually do a full calculation
}
stopTime = millis()*1e-3;
Serial.print( "done, filter runs at " );
Serial.print( float(nLoops) / (stopTime - startTime) );
Serial.print( " hz " );
Serial.print( "\n filter value: " ); Serial.print( myFilter.output() );
myFilter.setToNewValue( 0.0 );
Serial.print( "\n after reset to 0: "); Serial.print( myFilter.output() );
Serial.print( "\n testing rise time (10% to 90%) ...");
bool crossedTenPercent = false;
while( myFilter.output() < 0.9 ) {
myFilter.input( 1.0 );
if( myFilter.output() > 0.1 && !crossedTenPercent ) {
// filter first crossed the 10% point
startTime = millis()*1e-3;
crossedTenPercent = true;
}
}
stopTime = millis()*1e-3;
Serial.print( "done, rise time: " ); Serial.print( stopTime-startTime );
Serial.print( "testing attenuation at f = 1/risetime" );
myFilter.setToNewValue( 0.0 );
float maxVal = 0;
float valWasOutputThisCycle = true;
__unused float lastFilterVal = 0;
while( true ) {
float now = 1e-3*millis();
float currentFilterVal = myFilter.input( sin( TWO_PI*now) );
if( currentFilterVal < 0.0 ) {
if( !valWasOutputThisCycle ) {
// just crossed below zero, output the max
Serial.print( maxVal*100 ); Serial.print( " %\n" );
valWasOutputThisCycle = true;
}
}
}
}
// Copyright 2014 Jonathan Driscoll
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FilterOnePole.h"
#include <Arduino.h>
FilterOnePole::FilterOnePole( FILTER_TYPE ft, float fc, float initialValue ) {
setFilter( ft, fc, initialValue );
}
void FilterOnePole::setFilter( FILTER_TYPE ft, float fc, float initialValue ) {
FT = ft;
setFrequency( fc );
Y = initialValue;
Ylast = initialValue;
X = initialValue;
LastUS = micros();
}
float FilterOnePole::input( float inVal ) {
long time = micros();
ElapsedUS = float(time - LastUS); // cast to float here, for math
LastUS = time; // update this now
// shift the data values
Ylast = Y;
X = inVal; // this is now the most recent input value
// filter value is controlled by a parameter called X
// tau is set by the user in microseconds, but must be converted to samples here
TauSamps = TauUS / ElapsedUS;
float ampFactor;
#ifdef ARM_FLOAT
ampFactor = expf( -1.0 / TauSamps ); // this is 1 if called quickly
#else
ampFactor = exp( -1.0 / TauSamps ); // this is 1 if called quickly
#endif
Y = (1.0-ampFactor)*X + ampFactor*Ylast; // set the new value
return output();
}
void FilterOnePole::setFrequency( float newFrequency ) {
setTau( 1.0/(TWO_PI*newFrequency ) ); // τ=1/ω
}
void FilterOnePole::setTau( float newTau ) {
TauUS = newTau * 1e6;
}
float FilterOnePole::output() {
// figure out which button to read
switch (FT) {
case LOWPASS:
// return the last value
return Y;
break;
case INTEGRATOR:
// using a lowpass, but normaize
return Y * (TauUS/1.0e6);
break;
case HIGHPASS:
// highpass is the _difference_
return X-Y;
break;
case DIFFERENTIATOR:
// like a highpass, but normalize
return (X-Y)/(TauUS/1.0e6);
break;
default:
// should never get to here, return 0 just in case
return 0;
}
}
void FilterOnePole::print() {
Serial.println("");
Serial.print(" Y: "); Serial.print( Y );
Serial.print(" Ylast: "); Serial.print( Ylast );
Serial.print(" X "); Serial.print( X );
Serial.print(" ElapsedUS "); Serial.print( ElapsedUS );
Serial.print(" TauSamps: "); Serial.print( TauSamps );
//Serial.print(" ampFactor " ); Serial.print( ampFactor );
Serial.print(" TauUS: "); Serial.print( TauUS );
Serial.println("");
}
void FilterOnePole::test() {
float tau = 10;
float updateInterval = 1;
float nextupdateTime = millis()*1e-3;
float inputValue = 0;
FilterOnePole hp( HIGHPASS, tau, inputValue );
FilterOnePole lp( LOWPASS, tau, inputValue );
while( true ) {
float now = millis()*1e-3;
// switch input values on a 20 second cycle
if( round(now/20.0)-(now/20.0) < 0 )
inputValue = 0;
else
inputValue = 100;
hp.input(inputValue);
lp.input(inputValue);
if( now > nextupdateTime ) {
nextupdateTime += updateInterval;
Serial.print("inputValue: "); Serial.print( inputValue );
Serial.print("\t high-passed: "); Serial.print( hp.output() );
Serial.print("\t low-passed: "); Serial.print( lp.output() );
Serial.println();
}
}
}
void FilterOnePole::setToNewValue( float newVal ) {
Y = Ylast = X = newVal;
}
// stuff for filter2 (lowpass only)
// should be able to set a separate fall time as well
FilterOnePoleCascade::FilterOnePoleCascade( float riseTime, float initialValue ) {
setRiseTime( riseTime );
setToNewValue( initialValue );
}
void FilterOnePoleCascade::setRiseTime( float riseTime ) {
float tauScale = 3.36; // found emperically, by running test();
Pole1.setTau( riseTime / tauScale );
Pole2.setTau( riseTime / tauScale );
}
float FilterOnePoleCascade::input( float inVal ) {
Pole2.input( Pole1.input( inVal ));
return output();
}
// clears out the values in the filter
void FilterOnePoleCascade::setToNewValue( float newVal ) {
Pole1.setToNewValue( newVal );
Pole2.setToNewValue( newVal );
}
float FilterOnePoleCascade::output() {
return Pole2.output();
}
void FilterOnePoleCascade::test() {
// make a filter, how fast does it run:
float rise = 1.0;
FilterOnePoleCascade myFilter( rise );
// first, test the filter speed ...
long nLoops = 1000;
Serial.print( "testing filter with a rise time of ");
Serial.print( rise ); Serial.print( "s" );
Serial.print( "\n running filter speed loop ... ");
float startTime, stopTime;
startTime = millis()*1e-3;
for( long i=0; i<nLoops; ++i ) {
myFilter.input( PI ); // use pi, so it will actually do a full calculation
}
stopTime = millis()*1e-3;
Serial.print( "done, filter runs at " );
Serial.print( float(nLoops) / (stopTime - startTime) );
Serial.print( " hz " );
Serial.print( "\n filter value: " ); Serial.print( myFilter.output() );
myFilter.setToNewValue( 0.0 );
Serial.print( "\n after reset to 0: "); Serial.print( myFilter.output() );
Serial.print( "\n testing rise time (10% to 90%) ...");
bool crossedTenPercent = false;
while( myFilter.output() < 0.9 ) {
myFilter.input( 1.0 );
if( myFilter.output() > 0.1 && !crossedTenPercent ) {
// filter first crossed the 10% point
startTime = millis()*1e-3;
crossedTenPercent = true;
}
}
stopTime = millis()*1e-3;
Serial.print( "done, rise time: " ); Serial.print( stopTime-startTime );
Serial.print( "testing attenuation at f = 1/risetime" );
myFilter.setToNewValue( 0.0 );
float maxVal = 0;
float valWasOutputThisCycle = true;
while( true ) {
float now = 1e-3*millis();
float currentFilterVal = myFilter.input( sin( TWO_PI*now) );
if( currentFilterVal < 0.0 ) {
if( !valWasOutputThisCycle ) {
// just crossed below zero, output the max
Serial.print( maxVal*100 ); Serial.print( " %\n" );
valWasOutputThisCycle = true;
}
}
}
}

178
NuEVI/FilterOnePole.h → NuEVI/src/FilterOnePole.h Executable file → Normal file
View file

@ -1,89 +1,89 @@
// Copyright 2014 Jonathan Driscoll
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// FilterOnePole has been copied from https://github.com/JonHub/Filters
#ifndef FilterOnePole_h
#define FilterOnePole_h
enum FILTER_TYPE {
HIGHPASS,
LOWPASS,
INTEGRATOR,
DIFFERENTIATOR
};
// the recursive filter class implements a recursive filter (low / pass / highpass
// note that this must be updated in a loop, using the most recent acquired values and the time acquired
// Y = a0*X + a1*Xm1
// + b1*Ylast
struct FilterOnePole {
FILTER_TYPE FT;
float TauUS; // decay constant of the filter, in US
float TauSamps; // tau, measued in samples (this changes, depending on how long between input()s
// filter values - these are public, but should not be set externally
float Y; // most recent output value (gets computed on update)
float Ylast; // prevous output value
float X; // most recent input value
// elapsed times are kept in long, and will wrap every
// 35 mins, 47 seconds ... however, the wrap does not matter,
// because the delta will still be correct (always positive and small)
float ElapsedUS; // time since last update
long LastUS; // last time measured
FilterOnePole( FILTER_TYPE ft=LOWPASS, float fc=1.0, float initialValue=0 );
// sets or resets the parameters and state of the filter
void setFilter( FILTER_TYPE ft, float tauS, float initialValue );
void setFrequency( float newFrequency );
void setTau( float newTau );
float input( float inVal );
float output();
void print();
void test();
void setToNewValue( float newVal ); // resets the filter to a new value
};
// two pole filter, these are very useful
struct FilterOnePoleCascade {
FilterOnePole Pole1;
FilterOnePole Pole2;
FilterOnePoleCascade( float riseTime=1.0, float initialValue=0 ); // rise time to step function, 10% to 90%
// rise time is 10% to 90%, for a step input
void setRiseTime( float riseTime );
void setToNewValue( float newVal );
float input( float inVal );
float output();
void test();
};
#endif
// Copyright 2014 Jonathan Driscoll
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// FilterOnePole has been copied from https://github.com/JonHub/Filters
#ifndef FilterOnePole_h
#define FilterOnePole_h
enum FILTER_TYPE {
HIGHPASS,
LOWPASS,
INTEGRATOR,
DIFFERENTIATOR
};
// the recursive filter class implements a recursive filter (low / pass / highpass
// note that this must be updated in a loop, using the most recent acquired values and the time acquired
// Y = a0*X + a1*Xm1
// + b1*Ylast
struct FilterOnePole {
FILTER_TYPE FT;
float TauUS; // decay constant of the filter, in US
float TauSamps; // tau, measued in samples (this changes, depending on how long between input()s
// filter values - these are public, but should not be set externally
float Y; // most recent output value (gets computed on update)
float Ylast; // prevous output value
float X; // most recent input value
// elapsed times are kept in long, and will wrap every
// 35 mins, 47 seconds ... however, the wrap does not matter,
// because the delta will still be correct (always positive and small)
float ElapsedUS; // time since last update
long LastUS; // last time measured
FilterOnePole( FILTER_TYPE ft=LOWPASS, float fc=1.0, float initialValue=0 );
// sets or resets the parameters and state of the filter
void setFilter( FILTER_TYPE ft, float tauS, float initialValue );
void setFrequency( float newFrequency );
void setTau( float newTau );
float input( float inVal );
float output();
void print();
void test();
void setToNewValue( float newVal ); // resets the filter to a new value
};
// two pole filter, these are very useful
struct FilterOnePoleCascade {
FilterOnePole Pole1;
FilterOnePole Pole2;
FilterOnePoleCascade( float riseTime=1.0, float initialValue=0 ); // rise time to step function, 10% to 90%
// rise time is 10% to 90%, for a step input
void setRiseTime( float riseTime );
void setToNewValue( float newVal );
float input( float inVal );
float output();
void test();
};
#endif

8
NuEVI/src/TODO Normal file
View file

@ -0,0 +1,8 @@
1. LED abstraction code
2. Encoder code
3. Menu refactor
4. Refactor note play behavior into module
5. Refactor CV behavior into module
6. 9dof sensor code
7. Alternate fingerings
8. Encoder midi

175
NuEVI/src/adjustmenu.cpp Normal file
View file

@ -0,0 +1,175 @@
#include <array>
#include <Arduino.h>
#include <Adafruit_SSD1306.h>
#include "menu.h"
#include "globals.h"
#include "config.h"
#include "hardware.h"
#include "settings.h"
//***********************************************************
extern Adafruit_SSD1306 display;
extern Adafruit_MPR121 touchSensorUtil;
extern Adafruit_MPR121 touchSensorKeys;
extern Adafruit_MPRLS pressureSensorMain;
extern Adafruit_MPRLS pressureSensorAlt;
extern byte cursorNow;
int16_t ctouchVal = 0;
// Track pixels for faster redrawing
struct AdjustDrawing {
int row;
int thrX;
int maxX;
int valX;
};
struct AdjustValue {
const char *title;
const int16_t &value;
int16_t &thrVal;
int16_t &maxVal;
const int16_t limitLow;
const int16_t limitHigh;
// If not null, thr and max are relative to zeroPoint
const int16_t *zeroPoint;
};
template<size_t N>
class AdjustMenuScreen : public MenuScreen {
public:
AdjustMenuScreen(const char* title, std::array<AdjustValue, N> entries) : _title(title), _entries(entries) { }
void update(InputState input, bool redraw) {
bool redrawIndicators = false;
if (input.changed) {
if (input.knobMenu) {
_selectedEntry = _selectedEntry + input.knobMenu;
redraw = true;
}
AdjustValue value = _entries[_selectedEntry];
if (input.knobVal1) {
value.thrVal += input.knobVal1;
redrawIndicators = true;
}
if (input.knobVal2) {
value.maxVal += input.knobVal2;
redrawIndicators = true;
}
} else {
draw(redrawIndicators, redraw);
}
}
const char *title() {
return _title;
}
private:
void draw(bool redrawIndicators, bool redraw) {
for (size_t i = 0; i < N; i++) {
if (redraw) {
drawAdjustRow(_entries[i], _rowDrawings[i], i == _selectedEntry);
} else if (redrawIndicators && i == _selectedEntry) {
drawAdjustIndicators(_entries[i], _rowDrawings[i]);
} else {
drawAdjustValues(_entries[i], _rowDrawings[i]);
}
}
}
const char* _title;
size_t _selectedEntry = 0;
std::array<AdjustValue, N> _entries;
std::array<AdjustDrawing, N> _rowDrawings;
};
std::array<AdjustValue, 8> adjustValues = {{
{"BREATH", state.breathSignal, calibration.breathThrValOffset, calibration.breathMaxValOffset,
BREATH_LO_LIMIT, BREATH_HI_LIMIT, &state.breathZero},
{"BR ALT", state.breathAltSignal, calibration.breathAltThrValOffset, calibration.breathAltMaxValOffset,
BREATH_LO_LIMIT, BREATH_HI_LIMIT, &state.breathAltZero},
{"BITE",state.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, BITE_LO_LIMIT, BITE_HI_LIMIT, NULL},
{"PB DOWN",state.pbDnSignal, calibration.pbDnThrVal, calibration.pbDnMaxVal, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL},
{"PB UP", state.pbUpSignal, calibration.pbUpThrVal, calibration.pbUpMaxVal, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL},
{"EXTRA", state.extraSignal, calibration.extraThrVal, calibration.extraMaxVal, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT, NULL},
{"LEVER", state.leverSignal, calibration.leverThrVal, calibration.leverMaxVal, LEVER_LO_LIMIT, LEVER_HI_LIMIT, NULL},
{"TOUCH", ctouchVal, calibration.ctouchThrVal, calibration.ctouchThrVal, CTOUCH_LO_LIMIT, CTOUCH_HI_LIMIT, NULL},
}};
const MenuScreen adjustMenu = AdjustMenuScreen<8>("ADJUST", adjustValues);
void autoCalSelected() {
}
//***********************************************************
static void drawIndicator(int x, int row, int color) {
display.fillTriangle(x-2, row+1, x+2, row+1, x, row+3, color);
display.fillTriangle(x-2, row+10, x+2, row+10, x, row+7, color);
}
static void drawAdjustIndicators(const AdjustValue &value, AdjustDrawing &drawing) {
const int thrX = mapConstrain(value.thrVal, value.limitLow, value.limitHigh, 1, 127);
const int maxX = mapConstrain(value.maxVal, value.limitLow, value.limitHigh, 1, 127);
if (drawing.maxX != maxX) {
drawIndicator(drawing.thrX, drawing.row, BLACK);
drawIndicator(thrX, drawing.row, WHITE);
drawing.maxX = maxX;
}
if (drawing.thrX != thrX) {
drawIndicator(drawing.thrX, drawing.row, BLACK);
drawIndicator(thrX, drawing.row, WHITE);
drawing.thrX = thrX;
}
}
static void drawAdjustTitle(const AdjustValue &value, AdjustDrawing &drawing, bool highlight) {
display.setTextSize(1);
if (highlight) {
display.setTextColor(BLACK, WHITE);
} else {
display.setTextColor(WHITE, BLACK);
}
display.setCursor(0, drawing.row);
display.println(value.title);
}
static void drawAdjustValues(const AdjustValue &value, AdjustDrawing &drawing) {
char buffer[13];
snprintf(buffer, 13, "%d>%d<%d", value.thrVal, value.value, value.maxVal);
display.setTextSize(1);
display.setCursor(128 - 6 * strlen(buffer), drawing.row);
display.println(buffer);
const int valX = mapConstrain(value.value, value.limitLow, value.limitHigh, 1, 127);
if (drawing.valX != valX) {
display.drawFastVLine(drawing.valX, drawing.row+4, 4, BLACK);
display.drawFastVLine(valX, drawing.row+4, 4, WHITE);
drawing.valX = valX;
}
}
static void drawAdjustFrame(int line) {
display.drawLine(25,line,120,line,WHITE); // Top line
display.drawLine(25,line+12,120,line+12,WHITE); // Bottom line
display.drawLine(25,line+1,25,line+2,WHITE);
display.drawLine(120,line+1,120,line+2,WHITE);
display.drawLine(120,line+10,120,line+11,WHITE);
display.drawLine(25,line+10,25,line+11,WHITE);
}
static void drawAdjustRow(const AdjustValue &value, AdjustDrawing &drawing, bool highlight) {
display.fillRect(0, drawing.row, 128, 21, BLACK);
drawAdjustFrame(drawing.row);
drawAdjustTitle(value, drawing, highlight);
drawAdjustValues(value, drawing);
drawAdjustIndicators(value, drawing);
}

57
NuEVI/src/config.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef __CONFIG_H
#define __CONFIG_H
// Compile options, comment/uncomment to change
#define FIRMWARE_VERSION "0.0.1" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<<
#define ON_Delay 20 // Set Delay after ON threshold before velocity is checked (wait for tounging peak)
#define CCN_Port 5 // Controller number for portamento level
#define CCN_PortOnOff 65// Controller number for portamento on/off
#define START_NOTE 24 // set startNote to C (change this value in steps of 12 to start in other octaves)
#define FILTER_FREQ 30.0
#define CAP_SENS_ABSOLUTE_MAX 1000 // For inverting capacitive sensors
#define PRESSURE_SENS_MULTIPLIER 10 // Multiply pressure sens so it's not a float
#define CALIBRATE_SAMPLE_COUNT 4
// Statup buttons
#define STARTUP_FACTORY_RESET 0x3
#define STARTUP_CONFIG 0xC
#define TEST_CONFIG 0xA
#define DEBUG_CONFIG 0x1
// Buttons
#define BTN_MENU 0x1
#define BTN_VAL1 0x2
#define BTN_VAL2 0x4
#define BTN_PRESET 0x8
// Send breath CC data no more than every CC_BREATH_INTERVAL
// milliseconds
#define CC_BREATH_INTERVAL 5
#define SLOW_MIDI_ADD 7
#define CC_INTERVAL_PRIMARY 9
#define CC_INTERVAL_PORT 13
#define CC_INTERVAL_OTHER 37
#define LVL_TIMER_INTERVAL 15
#define CVPORTATUNE 2
#define maxSamplesNum 120
#define BREATH_LO_LIMIT 8000
#define BREATH_HI_LIMIT 10000
#define BITE_LO_LIMIT 0
#define BITE_HI_LIMIT 1000
#define PITCHB_LO_LIMIT 0
#define PITCHB_HI_LIMIT 1000
#define EXTRA_LO_LIMIT 0
#define EXTRA_HI_LIMIT 1000
#define CTOUCH_LO_LIMIT 0
#define CTOUCH_HI_LIMIT 1000
#define LEVER_LO_LIMIT 0
#define LEVER_HI_LIMIT 1000
#endif

139
NuEVI/src/globals.h Normal file
View file

@ -0,0 +1,139 @@
#ifndef __GLOBALS_H
#define __GLOBALS_H
#include "wiring.h"
#include <array>
// The three states of our main state machine
// No note is sounding
#define NOTE_OFF 1
// We've observed a transition from below to above the
// threshold value. We wait a while to see how fast the
// breath velocity is increasing
#define RISE_WAIT 2
// A note is sounding
#define NOTE_ON 3
enum PinkyMode : uint8_t {
PBD = 12,
GLD = 25,
MOD = 26,
QTN = 27,
};
enum FingeringMode : uint8_t {
EVI = 0,
EVR = 1,
TPT = 2,
HRN = 3,
};
enum RollerMode : uint8_t {
HIGHEST = 1,
HIGHEST_EXTEND = 2,
HIGHEST_PAIR = 3,
HIGHEST_PAIR_EXTEND = 4, // Releasing the roller from the highest/lowest moves
PARTIAL = 5,
PARTIAL_EXTEND = 6,
};
enum VibratoMode : uint8_t {
VSTART_DOWN = 0,
VSTART_UP = 1,
};
enum BreathMode : uint8_t {
BREATH_STD = 0,
BREATH_LSB = 1,
BREATH_AT = 2,
BREATH_LSB_AT = 3,
};
enum ExtraControl : uint8_t {
OFF = 0,
VIBRATO = 1,
GLIDE = 2,
CC = 3,
};
enum PolySelect : uint8_t {
EHarmonizerOff = 0,
EDuo = 1,
EChord = 2,
};
enum PortamentoMode : uint8_t {
POFF = 0,
PON = 1,
PSWITCH_ONLY = 2,
PGLIDE_ONLY = 3,
};
struct instrument_state_t {
int mainState; // The state of the main state machine
uint8_t patch; // 1-128
byte activeMIDIchannel = 1; // MIDI channel
byte activeNote = 0; // note playing
byte activePatch = 0;
byte doPatchUpdate = 0;
int8_t transpose = 0;
uint8_t octave = 0;
PolySelect polyMode = PolySelect::EHarmonizerOff;
// Raw sensor signals
int16_t breathSignal = 0; // breath level (smoothed) not mapped to CC value
int16_t breathAltSignal = 0;
int16_t biteSignal = 0; // capacitance data from bite sensor, for midi cc and threshold checks
int16_t leverSignal = 0;
int16_t pbUpSignal = 0;
int16_t pbDnSignal = 0;
int16_t extraSignal = 0;
int16_t vibSignal = 0;
// MIDI values
int breathCCVal = 0;
byte biteVal = 0; // keep track and make sure we send CC with 0 value when off threshold
byte portamentoVal = 0; // keep track and make sure we send CC with 0 value when off threshold
byte extraVal = 0; // keep track and make sure we send CC with 0 value when off threshold
byte leverVal = 0; // keep track and make sure we send CC with 0 value when off threshold
int pitchBend = 8192;
int pbSend = 8192; // Pitch bend actually sent, modified by vibrato, etc
// Key states
byte quarterToneTrigger;
byte pinkyKey = 0;
// CV values
int cvPitch;
int targetPitch;
// Calibration
int16_t breathZero; // this gets auto calibrated in setup
int16_t breathThrVal; // this gets auto calibrated in setup
int16_t breathMaxVal; // this gets auto calibrated in setup
int16_t breathAltZero; // this gets auto calibrated in setup
int16_t breathAltThrVal; // this gets auto calibrated in setup
int16_t breathAltMaxVal; // this gets auto calibrated in setup
int16_t vibThr; // this gets auto calibrated in setup
int16_t vibThrLo;
int16_t vibZero;
int16_t vibZeroBite;
int16_t vibThrBite;
int16_t vibThrBiteLo;
};
extern instrument_state_t state;
extern const std::array<const unsigned short*, 13> curves;
extern const unsigned short curveIn[];
extern unsigned int multiMap(unsigned short val, const unsigned short * _in, const unsigned short * _out, uint8_t size);
#define mapConstrain(val, in_lo, in_hi, out_lo, out_hi) map(constrain(val, in_lo, in_hi), in_lo, in_hi, out_lo, out_hi)
#endif

131
NuEVI/src/hardware.cpp Normal file
View file

@ -0,0 +1,131 @@
#include <Wire.h>
#include "hardware.h"
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h>
#include "menu.h"
#include "config.h"
#include "FilterOnePole.h" // for the breath signal low-pass filtering, from https://github.com/JonHub/Filters
FilterOnePole breathFilter;
FilterOnePole breathAltFilter;
Adafruit_MPR121 touchSensorKeys = Adafruit_MPR121();
Adafruit_MPR121 touchSensorUtil = Adafruit_MPR121();
Adafruit_MPRLS pressureSensorMain = Adafruit_MPRLS();
Adafruit_MPRLS pressureSensorAlt = Adafruit_MPRLS();
byte drawingMemory[numLeds*3]; // 3 bytes per LED
DMAMEM byte displayMemory[numLeds*12]; // 12 bytes per LED
WS2812Serial ledStrip(numLeds, displayMemory, drawingMemory, ledStripPin, WS2812_GRB);
Adafruit_ICM20948 icmSensor;
Adafruit_Sensor *accelSensor;
Encoder knobs[] = {
Encoder(e1aPin, e1bPin),
Encoder(e2aPin, e2bPin),
Encoder(e3aPin, e3bPin),
Encoder(e4aPin, e4bPin),
};
void errorWait();
void initHardware() {
MainI2CBus.setClock(1000000);
AuxI2CBus.setClock(1000000);
pinMode(statusLedPin,OUTPUT); // Teensy onboard LED
// Buttons
pinMode(b1Pin,INPUT_PULLUP);
pinMode(b2Pin,INPUT_PULLUP);
pinMode(b3Pin,INPUT_PULLUP);
pinMode(b4Pin,INPUT_PULLUP);
breathFilter.setFilter(LOWPASS, FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter
breathAltFilter.setFilter(LOWPASS, FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter
ledStrip.begin();
if (!touchSensorKeys.begin(KeysI2CAddr, &MainI2CBus)) {
displayError("Keys touch error");
errorWait();
}
if (!touchSensorUtil.begin(UtilI2CAddr, &MainI2CBus)) {
displayError("Roller/Util touch error");
errorWait();
}
if (!pressureSensorMain.begin(MPRLS_DEFAULT_ADDR, &MainI2CBus)) {
displayError("Main pressure sensor error");
errorWait();
}
if (!pressureSensorAlt.begin(MPRLS_DEFAULT_ADDR, &AuxI2CBus)) {
displayError("Alt pressure sensor error");
errorWait();
}
if (!icmSensor.begin_I2C(ICM20948_I2CADDR_DEFAULT, &MainI2CBus)) {
displayError("ICM sensor error");
errorWait();
}
}
/*
Return true if the given button bitmask is pressed
*/
bool checkButtonState(uint8_t mask) {
return buttonState() & mask;
}
/**
* Read the state of the switches (note that they are active low, so we invert the values)
*/
uint8_t buttonState() {
return 0x0f ^(digitalRead(b1Pin)
| (digitalRead(b2Pin) << 1)
| (digitalRead(b3Pin) << 2)
| (digitalRead(b4Pin) << 3));
}
void errorWait() {
while (1) {
if (!digitalRead(b1Pin)) {
_reboot_Teensyduino_(); // reboot to program mode if b1 pressed
}
if (!digitalRead(b4Pin)) {
break; // Continue if b4 pressed
}
delay(10);
}
}
int readKnob(uint8_t n) {
return knobs[n].readAndReset();
}
int readTouchKey(uint8_t n) {
return CAP_SENS_ABSOLUTE_MAX - touchSensorKeys.filteredData(n);
}
int readTouchUtil(uint8_t n) {
return CAP_SENS_ABSOLUTE_MAX - touchSensorUtil.filteredData(n);
}
uint16_t keysTouched() {
return touchSensorKeys.touched();
}
uint16_t utilTouched() {
return touchSensorKeys.touched();
}
int readPressure() {
return breathFilter.input(pressureSensorMain.readPressure()) * PRESSURE_SENS_MULTIPLIER;
}
int readAltPressure() {
return breathAltFilter.input(pressureSensorAlt.readPressure()) * PRESSURE_SENS_MULTIPLIER;
}

96
NuEVI/src/hardware.h Normal file
View file

@ -0,0 +1,96 @@
#ifndef __HARDWARE_H
#define __HARDWARE_H
#include <Adafruit_MPR121.h>
#include <Adafruit_MPRLS.h>
#include <Adafruit_ICM20X.h>
#include <WS2812Serial.h>
#include <Adafruit_ICM20948.h>
#include <stdint.h>
// Hardware sensor definitions
// TODO: remove these
extern WS2812Serial ledStrip;
extern Adafruit_Sensor *accelSensor;
extern Adafruit_ICM20948 icmSensor;
void initHardware();
bool checkButtonState(uint8_t mask); // return true if the given buttons are pressed
uint8_t buttonState(); // return true if the given buttons are pressed
int readKnob(uint8_t n);
int readTouchKey(uint8_t n);
int readTouchUtil(uint8_t n);
uint16_t keysTouched();
uint16_t utilTouched();
int readPressure();
int readAltPressure();
// xEVI hardware setup
// I2C
#define MainI2CBus Wire1
#define AuxI2CBus Wire
#define KeysI2CAddr 0x5B
#define UtilI2CAddr 0x5A
// Digital pins for encoder buttons
#define b1Pin 0
#define b2Pin 2
#define b3Pin 3
#define b4Pin 4
// Digital pins for encoder quadrature
#define e1aPin 5
#define e2aPin 6
#define e3aPin 7
#define e4aPin 8
#define e1bPin 20
#define e2bPin 21
#define e3bPin 22
#define e4bPin 23
// CV pins
#define cvGatePin 9
#define cvPitchPin 10
#define cvBreathPin 11
#define cvBitePin 12
//Output pins for LEDs
#define statusLedPin 13
#define ledStripPin 1
#define numLeds 8
// Key pins
// RH keys
#define K1Pin 0
#define K2Pin 1
#define K3Pin 2
#define K4Pin 3
#define K5Pin 4
#define K6Pin 5
#define K7Pin 6
#define K8Pin 7
// LH keys
#define K9Pin 8
#define K10Pin 9
#define K11Pin 10
#define K12Pin 11
// Octave roller pins
#define R1Pin 0
#define R2Pin 1
#define R3Pin 2
#define R4Pin 3
#define R5Pin 4
#define R6Pin 5
// Additional pins
#define bitePin 6
#define pbUpPin 7
#define pbDnPin 8
#define vibratoPin 9
#endif

49
NuEVI/src/led.cpp Normal file
View file

@ -0,0 +1,49 @@
#include <Arduino.h>
#include "hardware.h"
#include "globals.h"
#include "config.h"
void singleLED(int n, int color) {
}
void ledFullMeter(byte indicatedValue, int color){
}
void ledHalfMeter(int n, byte indicatedValue, int color){
}
void ledQuarterMeter(int n, byte indicatedValue, int color){
}
void statusLedOn() {
digitalWrite(statusLedPin, HIGH);
}
void statusLedOff() {
digitalWrite(statusLedPin, LOW);
}
void statusLed(bool state) {
digitalWrite(statusLedPin, state);
}
void statusLedFlip() {
digitalWrite(statusLedPin, !digitalRead(statusLedPin));
}
void statusLedFlash(uint16_t delayTime) {
statusLedOff();
delay(delayTime/2);
statusLedOn();
delay(delayTime/2);
}
void statusLedBlink() {
statusLedFlash(300);
statusLedFlash(300);
}
void updateSensorLEDs() {
ledHalfMeter(1, state.breathCCVal, 0x00FF00);
ledQuarterMeter(3, state.biteVal, 0x0000FF);
}

26
NuEVI/led.h → NuEVI/src/led.h Executable file → Normal file
View file

@ -1,13 +1,13 @@
#ifndef __LED_H
#define __LED_H
void statusLedOn();
void statusLedOff();
void statusLedFlip();
void statusLed(bool state);
void statusLedFlash(uint16_t delayTime);
void statusLedBlink();
void updateSensorLEDs();
void ledMeter(byte indicatedValue);
#endif
#ifndef __LED_H
#define __LED_H
void statusLedOn();
void statusLedOff();
void statusLedFlip();
void statusLed(bool state);
void statusLedFlash(uint16_t delayTime);
void statusLedBlink();
void updateSensorLEDs();
void ledMeter(byte indicatedValue);
#endif

755
NuEVI/src/menu.cpp Normal file
View file

@ -0,0 +1,755 @@
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_MPR121.h>
#include <Arduino.h>
#include <array>
#include <cstdio>
#include "menu.h"
#include "hardware.h"
#include "config.h"
#include "settings.h"
#include "globals.h"
#include "midi.h"
#include "led.h"
// constants
const unsigned long debounceDelay = 30; // the debounce time; increase if the output flickers
const unsigned long buttonRepeatInterval = 50;
const unsigned long buttonRepeatDelay = 400;
const unsigned long cursorBlinkInterval = 300; // the cursor blink toggle interval time
const unsigned long patchViewTimeUp = 2000; // ms until patch view shuts off
const unsigned long menuTimeUp = 60000; // menu shuts off after one minute of button inactivity
static unsigned long menuTime = 0;
static unsigned long patchViewTime = 0;
unsigned long cursorBlinkTime = 0; // the last time the cursor was toggled
std::array<const char *, 128> CC_NAMES = {
"Bank Select", // 0
"Mod Wheel", // 1
"Breath", // 2
"Undefined", // 3
"Foot Pedal", // 4
"Port. Time", // 5
"Data Entry", // 6
"Volume", // 7
"Balance", // 8
"Undefined", // 9
"Pan", // 10
"Expression", // 11
"Effect 1", // 12
"Effect 2", // 13
"Undefined", // 14
"Undefined", // 15
"GP 1", // 16
"GP 2", // 17
"GP 3", // 18
"GP 3", // 19
"Undefined", // 20
"Undefined", // 21
"Undefined", // 22
"Undefined", // 23
"Undefined", // 24
"Undefined", // 25
"Undefined", // 26
"Undefined", // 27
"Undefined", // 28
"Undefined", // 29
"Undefined", // 30
"Undefined", // 31
"LSB 0", // 32
"LSB 1", // 33
"LSB 2", // 34
"LSB 3", // 35
"LSB 4", // 36
"LSB 5", // 37
"LSB 6", // 38
"LSB 7", // 39
"LSB 8", // 40
"LSB 9", // 41
"LSB 10", // 42
"LSB 11", // 43
"LSB 12", // 44
"LSB 13", // 45
"LSB 14", // 46
"LSB 15", // 47
"LSB 16", // 48
"LSB 17", // 49
"LSB 18", // 50
"LSB 19", // 51
"LSB 20", // 52
"LSB 21", // 53
"LSB 22", // 54
"LSB 23", // 55
"LSB 24", // 56
"LSB 25", // 57
"LSB 26", // 58
"LSB 27", // 59
"LSB 28", // 60
"LSB 29", // 61
"LSB 30", // 62
"LSB 31", // 63
"Sustain", // 64
"Portamento", // 65
"Sostenuto", // 66
"Soft Pedal", // 67
"Legato", // 68
"Hold 2", // 69
"Variation", // 70
"Resonance", // 71
"Release", // 72
"Attack", // 73
"Cutoff", // 74
"Sound 6", // 75
"Sound 7", // 76
"Sound 8", // 77
"Sound 9", // 78
"Sound 10", // 79
"Decay", // 80
"Hi Pass", // 81
"GP Button 3", // 82
"GP Button 4", // 83
"Port. Amount", // 84
"Undefined", // 85
"Undefined", // 86
"Undefined", // 87
"Undefined", // 88
"Undefined", // 89
"Undefined", // 90
"Reverb", // 91
"Tremolo", // 92
"Chorus", // 93
"Detune", // 94
"Phaser", // 95
"Data Inc", // 96
"Data Dec", // 97
"NRPN LSB", // 98
"NRPN MSB", // 99
"RP LSB", // 100
"RP MSB", // 101
"Undefined", // 102
"Undefined", // 103
"Undefined", // 104
"Undefined", // 105
"Undefined", // 106
"Undefined", // 107
"Undefined", // 108
"Undefined", // 109
"Undefined", // 110
"Undefined", // 111
"Undefined", // 112
"Undefined", // 113
"Undefined", // 114
"Undefined", // 115
"Undefined", // 116
"Undefined", // 117
"Undefined", // 118
"Undefined", // 119
"All Sound Off", // 120
"All CCC Off", // 121
"Keyboard On", // 122
"All Notes Off", // 123
"Omni Mode Off", // 124
"Omni Mode On", // 125
"Mono", // 126
"Poly Mode", // 127
};
// 'NuEVI' or 'NuRAD' logo
#define LOGO16_GLCD_WIDTH 128
#define LOGO16_GLCD_HEIGHT 64
static const unsigned char PROGMEM nuevi_logo_bmp[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x07, 0x73, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x0e, 0xe3, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x60, 0x00, 0x1d, 0xc3, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x3b, 0x83, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x77, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x00, 0xee, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x01, 0xdc, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x03, 0xb8, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x07, 0x70, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x0e, 0xe0, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x1d, 0xc0, 0x03, 0x60, 0x00,
0x00, 0x03, 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x60, 0x3b, 0x80, 0x03, 0x60, 0x00,
0x00, 0x03, 0x00, 0xe0, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x77, 0x00, 0x03, 0x60, 0x00,
0x00, 0x03, 0x00, 0xc0, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0xee, 0x00, 0x03, 0x60, 0x00,
0x00, 0x03, 0x80, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x61, 0xdc, 0x00, 0x03, 0x60, 0x00,
0x00, 0x07, 0x80, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x63, 0xb8, 0x00, 0x03, 0x60, 0x00,
0x00, 0x07, 0xc0, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x67, 0x70, 0x00, 0x03, 0x60, 0x00,
0x00, 0x06, 0xc0, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x6e, 0xe0, 0x00, 0x03, 0x60, 0x00,
0x00, 0x06, 0x60, 0xc1, 0x01, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x7d, 0xc0, 0x00, 0x03, 0x60, 0x00,
0x00, 0x06, 0x30, 0xc3, 0x03, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x7b, 0x80, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x30, 0xc3, 0x07, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x77, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x1c, 0xc3, 0x06, 0x01, 0x80, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x0c, 0xc2, 0x0e, 0x01, 0xff, 0xff, 0xff, 0xe3, 0xfc, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x0e, 0xc6, 0x1e, 0x01, 0xff, 0xff, 0xff, 0xe3, 0xf8, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x07, 0xc6, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x03, 0xc6, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x01, 0xc7, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0xc7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
extern void readSwitches(void);
#define OLED_RESET 4
Adafruit_SSD1306 display(128, 64, &Wire1, OLED_RESET,1000000,1000000);
MenuScreen *currentMenu = NULL;
void plotSubOption(const char* label, const char* unit = nullptr) {
int text_x, unit_x;
int label_pixel_width = strlen(label)*12;
if(unit == nullptr) {
text_x = 96 - (label_pixel_width/2);
} else {
int unit_pixel_width = strlen(unit)*6;
int halfSum = (label_pixel_width + unit_pixel_width)/2;
text_x = 96 - halfSum;
unit_x = 96 + halfSum - unit_pixel_width;
display.setCursor(unit_x,40);
display.setTextSize(1);
display.println(unit);
}
display.setTextSize(2);
display.setCursor(text_x,33);
display.println(label);
}
template<size_t N>
static void plotMenuEntries(std::array<MenuScreen, N> entries, size_t cursorPos) {
display.fillRect(0, MENU_HEADER_OFFSET, 63, 64-MENU_HEADER_OFFSET, BLACK);
display.setTextSize(1);
size_t scrollPos = 0;
if (entries.size() >= MENU_NUM_ROWS) {
if ((cursorPos - scrollPos) > (MENU_NUM_ROWS-2) ) {
scrollPos = cursorPos - (MENU_NUM_ROWS-2);
} else if( (cursorPos - scrollPos) < 1) {
scrollPos = cursorPos - 1;
}
scrollPos = constrain(scrollPos, 0, entries.size() - MENU_NUM_ROWS);
}
int row = 0;
int end = constrain(scrollPos + MENU_NUM_ROWS, 0, entries.size());
for (size_t i = scrollPos; i < end; i++, row++) {
int rowPixel = (row)*MENU_ROW_HEIGHT + MENU_HEADER_OFFSET;
display.setCursor(0,rowPixel);
if (cursorPos == i) {
display.setTextColor(BLACK, WHITE);
} else {
display.setTextColor(WHITE);
}
display.println(entries[i].title());
}
}
class AboutMenu : public MenuScreen {
const char *title() {
return "ABOUT";
}
void update(InputState input, bool redraw) {
if (redraw) {
display.clearDisplay();
display.setCursor(49,0);
display.setTextColor(WHITE);
display.setTextSize(0);
display.println("xEVI");
display.setCursor(16,12);
display.print("firmware v.");
display.println(FIRMWARE_VERSION);
display.print("eeprom v.");
display.println(EEPROM_VERSION);
}
}
};
template<size_t N>
class MainMenu : public MenuScreen {
public:
MainMenu(std::array<MenuScreen, N> entries) : _entries(entries) { }
void update(InputState input, bool redraw) {
if (input.changed && input.btnMenu) {
_inSubMenu = !_inSubMenu;
input.changed = false;
redraw = true;
}
if (_inSubMenu) {
_entries[_cursorPos].update(input, redraw);
return;
}
if (input.changed) {
_cursorPos = (_cursorPos + input.knobMenu) % _entries.size();
}
draw(redraw);
};
const char *title() {
return "MENU";
}
private:
void draw(bool redraw) {
if (redraw) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.drawLine(0, MENU_ROW_HEIGHT, 127, MENU_ROW_HEIGHT, WHITE);
display.println("MENU");
}
plotMenuEntries(_entries, _cursorPos);
}
bool _inSubMenu = false;
size_t _cursorPos;
std::array<MenuScreen, N> _entries;
};
template<size_t N>
class SubMenu : public MenuScreen {
public:
SubMenu(const char *title, std::array<MenuScreen, N> entries) : _title(title), _entries(entries) {}
void update(InputState input, bool redraw) {
bool redrawValue = false;
if (input.changed && input.knobMenu != 0) {
_cursorPos = (_cursorPos + input.knobMenu) % _entries.size();
draw(false);
input.changed = false;
redrawValue = true;
}
if (redraw) {
draw(redraw);
}
_entries[_cursorPos].update(input, redrawValue);
};
const char *title() {
return _title;
}
private:
void draw(bool redraw) {
if (redraw) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.drawLine(0, MENU_ROW_HEIGHT, 127, MENU_ROW_HEIGHT, WHITE);
display.println(_title);
}
plotMenuEntries(_entries, _cursorPos);
}
const char *_title;
size_t _cursorPos;
std::array<MenuScreen, N> _entries;
};
template<
size_t L,
typename T
>
class ValueMenu : public MenuScreen {
public:
ValueMenu(
const char * title, T &value,
const T min, const T max, const bool wrap = false,
const std::array<const char *, L> labels = {}
) : _title(title), _value(value), _min(min), _max(max), _wrap(wrap), _labels(labels) {}
void update(InputState input, bool redraw) {
if (input.knobMenu) {
_value = (_value + input.knobMenu);
if (_value > _max) {
_value = _min;
} else if (_value < _min) {
_value = _max;
}
draw(redraw);
} else if (redraw) {
draw(redraw);
}
}
private:
T &_value;
const T _min;
const T _max;
const bool _wrap;
const std::array<const char *, L> _labels;
const char *_title;
void draw(bool redraw) {
if (redraw) {
display.fillRect(63,11,64,52,BLACK);
display.drawRect(63,11,64,52,WHITE);
display.setTextSize(1);
int len = strlen(this->_title);
display.setCursor(95-len*3,15);
display.println(this->_title);
}
char buffer[12];
snprintf(buffer, 12, "%+d", _value);
size_t label_idx = _value - _min;
if (_labels.size() > 0 && label_idx >= 0 && label_idx <= _labels.size()) {
plotSubOption(_labels[label_idx], buffer);
} else {
plotSubOption(buffer);
}
}
};
void drawFlash(int x, int y){
display.drawLine(x+5,y,x,y+6,WHITE);
display.drawLine(x,y+6,x+5,y+6,WHITE);
display.drawLine(x,y+12,x+5,y+6,WHITE);
}
void initDisplay() {
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // initialize with the I2C addr 0x3D (for the 128x64)
// Show image buffer on the display hardware.
// Since the buffer is intialized with an Adafruit splashscreen
// internally, this will display the splashscreen.
display.clearDisplay();
#if defined(NURAD)
display.drawBitmap(0,0,nurad_logo_bmp,LOGO16_GLCD_WIDTH,LOGO16_GLCD_HEIGHT,1);
#else
display.drawBitmap(0,0,nuevi_logo_bmp,LOGO16_GLCD_WIDTH,LOGO16_GLCD_HEIGHT,1);
#endif
display.display();
}
void displayError(const char *error) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.println(error);
display.display();
Serial.print("ERROR: ");
Serial.println(error);
}
void showVersion() {
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(85,52);
display.print("v.");
display.println(FIRMWARE_VERSION);
display.display();
}
static void clearSub(){
display.fillRect(63,11,64,52,BLACK);
}
static void clearSubValue() {
display.fillRect(65, 24, 60, 37, BLACK);
}
//***********************************************************
const MenuScreen breathModeMenu = ValueMenu<4, BreathMode>("BREATH MODE", currentPreset->breathMode, 0, 1, true, {{ "STANDARD", "LSB", "AT", "LSB_AT" }});
const MenuScreen breathCCMenu = ValueMenu<128, uint8_t>("BREATH CC", currentPreset->breathCC, 0, 127, true, CC_NAMES);
const MenuScreen velocityMenu = ValueMenu<1, uint8_t>("VELOCITY", currentPreset->fixedVelocity, 0, 127, true, {{ "DYN" }});
const MenuScreen curveMenu = ValueMenu<0, uint8_t>("CURVE", currentPreset->breathCurve, 0, 12); // TODO: curve display
const MenuScreen velSmpDlMenu = ValueMenu<1, uint8_t>("VEL DELAY", currentPreset->velSmpDl, 0, 30, true, { "OFF" }); // TODO: unit ms
const MenuScreen velBiasMenu = ValueMenu<1, uint8_t>("VEL BOOST", currentPreset->velBias, 0, 30, true, { "OFF" });
const MenuScreen breathIntervalMenu = ValueMenu<0, uint8_t>("BR INTERV", currentPreset->breathInterval, 0, 30, true);
const MenuScreen trill3Menu = ValueMenu<0, int8_t>("TRILL3", currentPreset->trill3_interval, 3, 4, true, {});
const MenuScreen cvTuneMenu = ValueMenu<0, int8_t>("CV Tune", currentPreset->cvTune, -100, 100, false, {});
const MenuScreen cvVibMenu = ValueMenu<0, uint8_t>("CV Vib LFO", currentPreset->cvVibRate, 0, 8, false, {});
const MenuScreen cvScaleMenu = ValueMenu<0, int8_t>("CV SCALING", currentPreset->cvScale, -100, 100, false, {});
const std::array<const char *, 25> transposeLabels = {
"C>", "C#>", "D>", "D#>", "E>", "F>", "F#>", "G>", "G#>", "A>", "Bb>", "B>",
">C<", "<C#", "<D", "<D#", "<E", "<F", "<F#", "<G", "<G#", "<A", "<Bb", "<B", "<C"
};
const MenuScreen transposeMenu = ValueMenu<25, int8_t>("TRANSPOSE", state.transpose, -12, 12, true, transposeLabels);
const MenuScreen octaveMenu = ValueMenu<0, uint8_t>("OCTAVE", state.octave, -3, 3, true, {});
const MenuScreen midiMenu = ValueMenu<0, byte>("MIDI CH", currentPreset->MIDIchannel, 1, 16, false, {});
const MenuScreen vibDepthMenu = ValueMenu<1, uint8_t>("DEPTH", currentPreset->vibratoDepth, 0, 9, true, {"OFF"});
const MenuScreen vibRetnMenu = ValueMenu<1, uint8_t>("RETURN", currentPreset->vibRetn, 0, 4, true, {"OFF"});
const MenuScreen vibSenseMenu = ValueMenu<0, uint8_t>("SENSE LVR", currentPreset->vibSens, 0, 12);
const MenuScreen vibSquelchMenu = ValueMenu<0, uint8_t>("SQUELCH LVR", currentPreset->vibSquelch, 0, 12);
const MenuScreen vibDirMenu = ValueMenu<2, VibratoMode>("DIRECTION", currentPreset->vibratoMode, VSTART_DOWN, VSTART_UP, true, { "START DOWN", "START UP"});
const MenuScreen biteCtlMenu = ValueMenu<4, ExtraControl>("BITE CTL", currentPreset->biteControl, 0, 3, true, {{
"OFF",
"VIBRATO",
"GLIDE",
"CC"
}});
const MenuScreen biteCCMenu = ValueMenu<128, uint8_t>("BITE CC", currentPreset->biteCC, 0, 127, true, CC_NAMES);
const MenuScreen leverCtlMenu = ValueMenu<4, ExtraControl>("LEVER CTL", currentPreset->leverControl, 0, 3, true, {
"OFF",
"VIBRATO",
"GLIDE",
"CC"
});
const MenuScreen leverCCMenu = ValueMenu<128, uint8_t>("LEVER CC", currentPreset->leverCC, 0, 127, true, CC_NAMES);
const MenuScreen portMenu = ValueMenu<4, PortamentoMode>("GLIDE MOD", currentPreset->portamentoMode, 0, 3, true, {
"OFF",
"ON",
"SWITCH_ONLY",
"GLIDE_ONLY",
});
const MenuScreen portLimitMenu = ValueMenu<0, uint8_t>("GLIDE LMT", currentPreset->portamentoLimit, 1, 127, true);
const MenuScreen pitchBendMenu = ValueMenu<0, uint8_t>("PITCHBEND", currentPreset->PBdepth, 0, 12, true);
const MenuScreen extraCtlMenu = ValueMenu<4, ExtraControl>("EXCT CC A", currentPreset->extraControl, 0,4, true, {
"OFF",
"ON",
"SWITCH_ONLY",
"GLIDE_ONLY",
});
const MenuScreen extraCCMenu = ValueMenu<128, uint8_t>("EXCT CC", currentPreset->extraCC, 0,4, true, CC_NAMES);
const MenuScreen deglitchMenu = ValueMenu<1, uint8_t>("DEGLITCH", currentPreset->deglitch, 0, 70, true, {"OFF"});
const MenuScreen pinkyMenu = ValueMenu<29, uint8_t>("PINKY KEY", currentPreset->pinkySetting, 0, 29, true, {
"-12", "-11", "-10", "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
"PB/2",
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
"PBD", "GLD", "MOD", "QTN"
});
const MenuScreen fingeringMenu = ValueMenu<4, FingeringMode>("FINGERING", currentPreset->fingering, 0, 3, true, {
"EVI",
"EVR",
"TPT",
"HRN",
});
const MenuScreen rollerMenu = ValueMenu<6, RollerMode>("ROLLRMODE", currentPreset->rollerMode, 1, 6, true, {
"HIGHEST",
"HIGHEST_EXTEND",
"HIGHEST_PAIR",
"HIGHEST_PAIR_EXTEND",
"PARTIAL",
"PARTIAL_EXTEND",
});
std::array<MenuScreen, 7> breathMenuEntries = {
breathModeMenu,
breathCCMenu,
velocityMenu,
curveMenu,
velSmpDlMenu,
velBiasMenu,
breathIntervalMenu,
};
const MenuScreen breathMenu = SubMenu<7>("BREATH SETUP", breathMenuEntries);
const std::array<MenuScreen, 13> controlMenuEntries = {
fingeringMenu,
rollerMenu,
biteCtlMenu,
biteCCMenu,
leverCtlMenu,
leverCCMenu,
extraCtlMenu,
extraCCMenu,
portMenu,
portLimitMenu,
deglitchMenu,
pinkyMenu,
pitchBendMenu
};
const MenuScreen controlMenu = SubMenu<13>("CONTROL SETUP", controlMenuEntries);
const std::array<MenuScreen, 5> vibratoMenuEntries = {
vibDepthMenu,
vibRetnMenu,
vibDirMenu,
vibSenseMenu,
vibSquelchMenu,
};
const MenuScreen vibratoMenu = SubMenu<5>("VIBRATO", vibratoMenuEntries);
const MenuScreen aboutMenu = AboutMenu();
std::array <MenuScreen, 4> extrasMenuEntries = {
trill3Menu,
cvTuneMenu,
cvScaleMenu,
cvVibMenu,
};
const MenuScreen extrasMenu = SubMenu<4>("EXTRAS", extrasMenuEntries);
// Top-level screens
const std::array<MenuScreen, 9> mainMenuEntries = {
transposeMenu,
octaveMenu,
midiMenu,
breathMenu,
controlMenu,
vibratoMenu,
adjustMenu,
extrasMenu,
aboutMenu,
};
const MenuScreen mainMenuPage = MainMenu<9>(mainMenuEntries);
// const MenuScreen patchPage
// const MenuScreen presetPage
// const MenuScreen ccPage
static void curveCustomDraw() {
const char* curveMenuLabels[] = {"-4", "-3", "-2", "-1", "LIN", "+1", "+2",
"+3", "+4", "S1", "S2", "Z1", "Z2" };
int y0 = 0, x0 = 0;
int scale = ((1<<14)-1)/60;
for(int x = x0; x < 60; x+=1) {
int y = multiMap(x*scale, curveIn, curves[currentPreset->breathCurve], 17);
y = (y*37) / ((1<<14)-1);
display.drawLine(x0 + 65, 60 - y0, x + 65, 60 - y, WHITE);
x0 = x; y0 = y;
}
display.setCursor(125 - 3*6, 60-8 );
display.setTextSize(0);
display.print(curveMenuLabels[currentPreset->breathCurve]);
}
static bool updateSensorPixelsFlag = false;
void drawSensorPixels() {
updateSensorPixelsFlag = true;
}
//***********************************************************
static InputState readInput(uint32_t timeNow) {
static uint32_t lastDebounceTime = 0; // the last time the output pin was toggled
static uint32_t buttonRepeatTime = 0;
static uint32_t buttonPressedTime = 0;
static uint8_t lastDeumButtons = 0;
static uint8_t deumButtonState = 0;
static int lastKnobs[] = {0, 0, 0, 0};
InputState input;
uint8_t deumButtons = buttonState();
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited long enough
// since the last press to ignore any noise:
// If the switch changed, due to noise or pressing:
if (deumButtons != lastDeumButtons) {
// reset the debouncing timer
lastDebounceTime = timeNow;
}
if ((timeNow - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (deumButtons != deumButtonState) {
// keys.current = deumButtons;
input.btnMenu = deumButtons & BTN_MENU;
input.btnVal1 = deumButtons & BTN_VAL1;
input.btnVal2 = deumButtons & BTN_VAL2;
input.btnPreset = deumButtons & BTN_PRESET;
input.changed = true;
deumButtonState = deumButtons;
menuTime = timeNow;
buttonPressedTime = timeNow;
}
}
for (int i = 0; i < 4; i++) {
int val = readKnob(i);
if (val != lastKnobs[i]) {
input.changed = 1;
switch (i) {
case 0:
input.knobMenu = val;
break;
case 1:
input.knobVal1 = val;
break;
case 2:
input.knobVal2 = val;
break;
case 3:
input.knobPreset = val;
break;
}
}
}
// save the reading. Next time through the loop, it'll be the lastButtonState:
lastDeumButtons = deumButtons;
return input;
}
void handleMenu(bool draw) {
unsigned long timeNow = millis();
InputState input = readInput(timeNow);
// shut off menu system if not used for a while (changes not stored by exiting a setting manually will not be stored in EEPROM)
if (currentMenu && ((timeNow - menuTime) > menuTimeUp)) {
display.ssd1306_command(SSD1306_DISPLAYOFF);
display.clearDisplay();
currentMenu = NULL;
}
if (currentMenu && (draw || input.changed)) {
currentMenu->update(input, false);
}
}

44
NuEVI/src/menu.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef __MENU_H
#define __MENU_H
#include "wiring.h"
#define MENU_ROW_HEIGHT 9
#define MENU_HEADER_OFFSET 12
#define MENU_NUM_ROWS 6
#define ADJUST_NUM_ROWS 3
#define ADJUST_ROW_HEIGHT 21
extern const unsigned long debounceDelay; // the debounce time; increase if the output flickers
extern const unsigned long buttonRepeatInterval;
extern const unsigned long buttonRepeatDelay;
extern const unsigned long cursorBlinkInterval; // the cursor blink toggle interval time
extern const unsigned long patchViewTimeUp; // ms until patch view shuts off
extern const unsigned long menuTimeUp; // menu shuts off after one minute of button inactivity
struct InputState {
bool changed = false;
bool btnMenu = false;
bool btnVal1 = false;
bool btnVal2 = false;
bool btnPreset = false;
int knobMenu = 0;
int knobVal1 = 0;
int knobVal2 = 0;
int knobPreset = 0;
};
struct MenuScreen {
MenuScreen() {};
virtual const char *title() { return ""; };
virtual void update(InputState input, bool redraw) {};
virtual ~MenuScreen() {};
};
extern const MenuScreen adjustMenu;
void initDisplay();
void showVersion();
void displayError(const char *error);
void handleMenu(bool draw);
#endif

View file

@ -1,226 +1,232 @@
#include <Arduino.h>
#include "midi.h"
#include "hardware.h"
#include "globals.h"
int midiChannel;
void midiSetChannel(uint8_t channel) {
midiChannel = constrain(channel, 1, 16);
}
byte midiGetChannel() {
return midiChannel;
}
void midiSendProgramChange(uint8_t patch) {
usbMIDI.sendProgramChange(patch-1, midiChannel);
dinMIDIsendProgramChange(patch-1, midiChannel-1);
}
void midiSendControlChange(uint8_t ccParam, uint8_t ccValue) {
usbMIDI.sendControlChange(ccParam, ccValue, midiChannel);
dinMIDIsendControlChange(ccParam, ccValue, midiChannel - 1);
}
void midiSendNoteOn(uint8_t note, uint8_t velocity) {
usbMIDI.sendNoteOn(note, velocity, midiChannel);
dinMIDIsendNoteOn(note, velocity, midiChannel - 1);
}
void midiSendNoteOff(uint8_t note) {
//Always send velocity 0 on note off to avoid confusing some synthesizers
usbMIDI.sendNoteOn(note, 0, midiChannel);
dinMIDIsendNoteOn(note, 0, midiChannel - 1);
}
void midiSendAfterTouch(uint8_t value) {
usbMIDI.sendAfterTouch(value, midiChannel);
dinMIDIsendAfterTouch(value, midiChannel - 1);
}
void midiSendPitchBend(uint16_t value) {
#if (TEENSYDUINO >= 141)
usbMIDI.sendPitchBend(value-8192, midiChannel); // newer teensyduino "pitchBend-8192" older just "pitchBend"... strange thing to change
#else
usbMIDI.sendPitchBend(value, midiChannel);
#endif
dinMIDIsendPitchBend(value, midiChannel - 1);
}
void midiDiscardInput()
{
while (usbMIDI.read()) {
// read & ignore incoming messages
}
}
void midiReset() { // reset controllers
midiSendControlChange(7, 100);
midiSendControlChange(11, 127);
}
void midiPanic() { // all notes off
midiSendControlChange(123, 0);
for (int i = 0; i < 128; i++){
midiSendNoteOff(i);
delay(2);
}
}
void midiInitialize(uint8_t channel) {
MIDI_SERIAL.begin(31250); // start serial with midi baudrate 31250
MIDI_SERIAL.flush();
if(widiJumper){
WIDI_SERIAL.begin(31250); // start serial with midi baudrate 31250
WIDI_SERIAL.flush();
}
midiSetChannel(channel);
}
//Serial midi functions
// Send a three byte din midi message
void midiSend3B(uint8_t midistatus, uint8_t data1, uint8_t data2) {
MIDI_SERIAL.write(midistatus);
MIDI_SERIAL.write(data1);
MIDI_SERIAL.write(data2);
if (widiJumper && widiOn){
WIDI_SERIAL.write(midistatus);
WIDI_SERIAL.write(data1);
WIDI_SERIAL.write(data2);
}
}
//**************************************************************
// Send a two byte din midi message
void midiSend2B(uint8_t midistatus, uint8_t data) {
MIDI_SERIAL.write(midistatus);
MIDI_SERIAL.write(data);
if (widiJumper && widiOn){
WIDI_SERIAL.write(midistatus);
WIDI_SERIAL.write(data);
}
}
//**************************************************************
// Send din pitchbend
void dinMIDIsendPitchBend(uint16_t pb, uint8_t ch) {
int pitchLSB = pb & 0x007F;
int pitchMSB = (pb >>7) & 0x007F;
midiSend3B((0xE0 | ch), pitchLSB, pitchMSB);
}
//**************************************************************
// Send din control change
void dinMIDIsendControlChange(uint8_t ccNumber, uint8_t cc, uint8_t ch) {
midiSend3B((0xB0 | ch), ccNumber, cc);
}
//**************************************************************
// Send din note on
void dinMIDIsendNoteOn(uint8_t note, uint8_t vel, uint8_t ch) {
midiSend3B((0x90 | ch), note, vel);
}
//**************************************************************
// Send din note off
void dinMIDIsendNoteOff(uint8_t note, uint8_t vel, uint8_t ch) {
midiSend3B((0x80 | ch), note, vel);
}
//**************************************************************
// Send din aftertouch
void dinMIDIsendAfterTouch(uint8_t value, uint8_t ch) {
midiSend2B((0xD0 | ch), value);
}
//**************************************************************
// Send din program change
void dinMIDIsendProgramChange(uint8_t value, uint8_t ch) {
midiSend2B((0xC0 | ch), value);
}
// Send sysex commands to wireless module
void dinMIDIsendSysex(const uint8_t data[], const uint8_t length) {
MIDI_SERIAL.write(0xF0); //Sysex command
for(int i=0; i<length; ++i) {
MIDI_SERIAL.write(data[i]);
}
MIDI_SERIAL.write(0xF7); //Sysex end
}
void sendWLPower(const uint8_t level) {
uint8_t buf[6] = {
0x00, 0x21, 0x11, //Manufacturer id
0x02, //TX02
0x02, //Set power level
0x00 //Power level value (0-3)
};
if(level>3) return; //Don't send invalid values
buf[5] = level;
dinMIDIsendSysex(buf, 6);
}
void sendWLChannel(const uint8_t channel) {
uint8_t buf[6] = {
0x00, 0x21, 0x11, //Manufacturer id
0x02, //TX02
0x05, //Set channel
0x04 //Channel value (4-80)
};
if(channel<4 || channel>80) return; //Don't send invalid values
buf[5] = channel;
dinMIDIsendSysex(buf, 6);
}
//Translate between "midi data" (only use 7 LSB per byte, big endian) and "teensy data" (little endian)
//Only 14 LSB of int value are used (2MSB are discarded), so only works for unsigned data 0-16383
//NOTE: This assumes code is running on a little-endian CPU, both for real device (Teensy) and simulator.
uint16_t convertToMidiValue(const uint16_t realdata) {
return (realdata & 0x3F80) >>7 | (realdata & 0x007F) <<8;
}
uint16_t convertFromMidiValue(const uint16_t mididata) {
return (mididata & 0x7F00) >> 8 | (mididata & 0x007F) <<7 ;
}
//Read from a memory location, such as MIDI receive buffer
uint16_t convertFromMidiValue(const uint8_t* mididata) {
uint8_t msb = *mididata;
uint8_t lsb = *(mididata+1);
return (msb & 0x007F) <<7 | (lsb & 0x007F);
}
//This is a bit different. MSB of each byte is just discarded (instead of discarding MSB for whole value). Just used for CRC (easier to compare)
uint32_t convertToMidiCRC(const uint32_t realdata) {
uint8_t* p = (uint8_t*)&realdata;
uint32_t r=0;
for(int i=0; i<4; ++i) {
r = r<<8 | (p[i] & 0x7F);
}
return r;
}
#include <Arduino.h>
#include "midi.h"
#include "hardware.h"
#include "globals.h"
int midiChannel;
void midiSetChannel(uint8_t channel) {
midiChannel = constrain(channel, 1, 16);
}
byte midiGetChannel() {
return midiChannel;
}
void midiSendProgramChange(uint8_t patch) {
usbMIDI.sendProgramChange(patch-1, midiChannel);
dinMIDIsendProgramChange(patch-1, midiChannel-1);
}
void midiSendControlChange(uint8_t ccParam, uint8_t ccValue) {
usbMIDI.sendControlChange(ccParam, ccValue, midiChannel);
dinMIDIsendControlChange(ccParam, ccValue, midiChannel - 1);
}
void midiSendNoteOn(uint8_t note, uint8_t velocity) {
usbMIDI.sendNoteOn(note, velocity, midiChannel);
dinMIDIsendNoteOn(note, velocity, midiChannel - 1);
}
void midiSendNoteOff(uint8_t note) {
//Always send velocity 0 on note off to avoid confusing some synthesizers
usbMIDI.sendNoteOn(note, 0, midiChannel);
dinMIDIsendNoteOn(note, 0, midiChannel - 1);
}
void midiSendAfterTouch(uint8_t value) {
usbMIDI.sendAfterTouch(value, midiChannel);
dinMIDIsendAfterTouch(value, midiChannel - 1);
}
void midiSendPitchBend(uint16_t value) {
#if (TEENSYDUINO >= 141)
usbMIDI.sendPitchBend(value-8192, midiChannel); // newer teensyduino "pitchBend-8192" older just "pitchBend"... strange thing to change
#else
usbMIDI.sendPitchBend(value, midiChannel);
#endif
dinMIDIsendPitchBend(value, midiChannel - 1);
}
void midiDiscardInput()
{
while (usbMIDI.read()) {
// read & ignore incoming messages
}
}
void midiReset() { // reset controllers
midiSendControlChange(7, 100);
midiSendControlChange(11, 127);
}
void midiPanic() { // all notes off
midiSendControlChange(123, 0);
for (int i = 0; i < 128; i++){
midiSendNoteOff(i);
delay(2);
}
}
void midiInitialize(uint8_t channel) {
/*
MIDI_SERIAL.begin(31250); // start serial with midi baudrate 31250
MIDI_SERIAL.flush();
if(widiJumper){
WIDI_SERIAL.begin(31250); // start serial with midi baudrate 31250
WIDI_SERIAL.flush();
}
*/
midiSetChannel(channel);
}
//Serial midi functions
// Send a three byte din midi message
void midiSend3B(uint8_t midistatus, uint8_t data1, uint8_t data2) {
/*
MIDI_SERIAL.write(midistatus);
MIDI_SERIAL.write(data1);
MIDI_SERIAL.write(data2);
if (widiJumper && widiOn){
WIDI_SERIAL.write(midistatus);
WIDI_SERIAL.write(data1);
WIDI_SERIAL.write(data2);
}
*/
}
//**************************************************************
// Send a two byte din midi message
void midiSend2B(uint8_t midistatus, uint8_t data) {
/*
MIDI_SERIAL.write(midistatus);
MIDI_SERIAL.write(data);
if (widiJumper && widiOn){
WIDI_SERIAL.write(midistatus);
WIDI_SERIAL.write(data);
}
*/
}
//**************************************************************
// Send din pitchbend
void dinMIDIsendPitchBend(uint16_t pb, uint8_t ch) {
int pitchLSB = pb & 0x007F;
int pitchMSB = (pb >>7) & 0x007F;
midiSend3B((0xE0 | ch), pitchLSB, pitchMSB);
}
//**************************************************************
// Send din control change
void dinMIDIsendControlChange(uint8_t ccNumber, uint8_t cc, uint8_t ch) {
midiSend3B((0xB0 | ch), ccNumber, cc);
}
//**************************************************************
// Send din note on
void dinMIDIsendNoteOn(uint8_t note, uint8_t vel, uint8_t ch) {
midiSend3B((0x90 | ch), note, vel);
}
//**************************************************************
// Send din note off
void dinMIDIsendNoteOff(uint8_t note, uint8_t vel, uint8_t ch) {
midiSend3B((0x80 | ch), note, vel);
}
//**************************************************************
// Send din aftertouch
void dinMIDIsendAfterTouch(uint8_t value, uint8_t ch) {
midiSend2B((0xD0 | ch), value);
}
//**************************************************************
// Send din program change
void dinMIDIsendProgramChange(uint8_t value, uint8_t ch) {
midiSend2B((0xC0 | ch), value);
}
// Send sysex commands to wireless module
void dinMIDIsendSysex(const uint8_t data[], const uint8_t length) {
//MIDI_SERIAL.write(0xF0); //Sysex command
for(int i=0; i<length; ++i) {
//MIDI_SERIAL.write(data[i]);
}
//MIDI_SERIAL.write(0xF7); //Sysex end
}
void sendWLPower(const uint8_t level) {
uint8_t buf[6] = {
0x00, 0x21, 0x11, //Manufacturer id
0x02, //TX02
0x02, //Set power level
0x00 //Power level value (0-3)
};
if(level>3) return; //Don't send invalid values
buf[5] = level;
dinMIDIsendSysex(buf, 6);
}
void sendWLChannel(const uint8_t channel) {
uint8_t buf[6] = {
0x00, 0x21, 0x11, //Manufacturer id
0x02, //TX02
0x05, //Set channel
0x04 //Channel value (4-80)
};
if(channel<4 || channel>80) return; //Don't send invalid values
buf[5] = channel;
dinMIDIsendSysex(buf, 6);
}
//Translate between "midi data" (only use 7 LSB per byte, big endian) and "teensy data" (little endian)
//Only 14 LSB of int value are used (2MSB are discarded), so only works for unsigned data 0-16383
//NOTE: This assumes code is running on a little-endian CPU, both for real device (Teensy) and simulator.
uint16_t convertToMidiValue(const uint16_t realdata) {
return (realdata & 0x3F80) >>7 | (realdata & 0x007F) <<8;
}
uint16_t convertFromMidiValue(const uint16_t mididata) {
return (mididata & 0x7F00) >> 8 | (mididata & 0x007F) <<7 ;
}
//Read from a memory location, such as MIDI receive buffer
uint16_t convertFromMidiValue(const uint8_t* mididata) {
uint8_t msb = *mididata;
uint8_t lsb = *(mididata+1);
return (msb & 0x007F) <<7 | (lsb & 0x007F);
}
//This is a bit different. MSB of each byte is just discarded (instead of discarding MSB for whole value). Just used for CRC (easier to compare)
uint32_t convertToMidiCRC(const uint32_t realdata) {
uint8_t* p = (uint8_t*)&realdata;
uint32_t r=0;
for(int i=0; i<4; ++i) {
r = r<<8 | (p[i] & 0x7F);
}
return r;
}

92
NuEVI/midi.h → NuEVI/src/midi.h Executable file → Normal file
View file

@ -1,46 +1,46 @@
#ifndef __MIDI_H
#define __MIDI_H
//This is a completely made up "European" SysEx manufacturer ID.
static const char sysex_id[] = { 0x00, 0x3e, 0x7f };
//Enable use of USB and serial MIDI
#define USE_MIDI_USB
#define USE_MIDI_SERIAL
//Set / get current midi channel
void midiSetChannel(uint8_t channel);
uint8_t midiGetChannel();
void midiSendProgramChange(uint8_t patch);
void midiSendControlChange(uint8_t ccParam, uint8_t ccValue);
void midiSendNoteOn(uint8_t note, uint8_t velocity);
void midiSendNoteOff(uint8_t note);
void midiSendAfterTouch(uint8_t value);
void midiSendPitchBend(uint16_t value);
void midiDiscardInput(void);
void midiReset(); // reset controllers
void midiPanic(); // turn all notes off
void midiInitialize(uint8_t channel=1);
void dinMIDIsendControlChange(uint8_t ccNumber, uint8_t cc, uint8_t ch);
void dinMIDIsendNoteOn(uint8_t note, uint8_t vel, uint8_t ch);
void dinMIDIsendNoteOff(uint8_t note, uint8_t vel, uint8_t ch);
void dinMIDIsendAfterTouch(uint8_t value, uint8_t ch);
void dinMIDIsendProgramChange(uint8_t value, uint8_t ch);
void dinMIDIsendPitchBend(uint16_t pb, uint8_t ch);
void dinMIDIsendSysex(const uint8_t data[], const uint8_t length);
void sendWLPower(const uint8_t level);
void sendWLChannel(const uint8_t channel);
//Convert things between "regular data" and MIDI data (byte order and 7-bits-per-byte)
uint16_t convertToMidiValue(const uint16_t realdata);
uint16_t convertFromMidiValue(const uint16_t mididata);
uint16_t convertFromMidiValue(const uint8_t* mididata);
uint32_t convertToMidiCRC(const uint32_t realdata);
#endif
#ifndef __MIDI_H
#define __MIDI_H
//This is a completely made up "European" SysEx manufacturer ID.
static const char sysex_id[] = { 0x00, 0x3e, 0x7f };
//Enable use of USB and serial MIDI
#define USE_MIDI_USB
#define USE_MIDI_SERIAL
//Set / get current midi channel
void midiSetChannel(uint8_t channel);
uint8_t midiGetChannel();
void midiSendProgramChange(uint8_t patch);
void midiSendControlChange(uint8_t ccParam, uint8_t ccValue);
void midiSendNoteOn(uint8_t note, uint8_t velocity);
void midiSendNoteOff(uint8_t note);
void midiSendAfterTouch(uint8_t value);
void midiSendPitchBend(uint16_t value);
void midiDiscardInput(void);
void midiReset(); // reset controllers
void midiPanic(); // turn all notes off
void midiInitialize(uint8_t channel=1);
void dinMIDIsendControlChange(uint8_t ccNumber, uint8_t cc, uint8_t ch);
void dinMIDIsendNoteOn(uint8_t note, uint8_t vel, uint8_t ch);
void dinMIDIsendNoteOff(uint8_t note, uint8_t vel, uint8_t ch);
void dinMIDIsendAfterTouch(uint8_t value, uint8_t ch);
void dinMIDIsendProgramChange(uint8_t value, uint8_t ch);
void dinMIDIsendPitchBend(uint16_t pb, uint8_t ch);
void dinMIDIsendSysex(const uint8_t data[], const uint8_t length);
void sendWLPower(const uint8_t level);
void sendWLChannel(const uint8_t channel);
//Convert things between "regular data" and MIDI data (byte order and 7-bits-per-byte)
uint16_t convertToMidiValue(const uint16_t realdata);
uint16_t convertFromMidiValue(const uint16_t mididata);
uint16_t convertFromMidiValue(const uint8_t* mididata);
uint32_t convertToMidiCRC(const uint32_t realdata);
#endif

38
NuEVI/name.c → NuEVI/src/name.c Executable file → Normal file
View file

@ -1,22 +1,16 @@
// To give your project a unique name, this code must be
// placed into a .c file (its own tab). It can not be in
// a .cpp file or your main sketch (the .ino file).
#include "usb_names.h"
// Edit these lines to create your own name. The length must
// match the number of characters in your custom name.
#if defined(NURAD)
#define MIDI_NAME {'N','u','R','A','D',' ','M','I','D','I'}
#else
#define MIDI_NAME {'N','u','E','V','I',' ','M','I','D','I'}
#endif
#define MIDI_NAME_LEN 10
// Do not change this part. This exact format is required by USB.
struct usb_string_descriptor_struct usb_string_product_name = {
2 + MIDI_NAME_LEN * 2,
3,
MIDI_NAME
};
// To give your project a unique name, this code must be
// placed into a .c file (its own tab). It can not be in
// a .cpp file or your main sketch (the .ino file).
#include "usb_names.h"
#define MIDI_NAME {'x','E','V','I',' ','M','I','D','I'}
#define MIDI_NAME_LEN 9
// Do not change this part. This exact format is required by USB.
struct usb_string_descriptor_struct usb_string_product_name = {
2 + MIDI_NAME_LEN * 2,
3,
MIDI_NAME
};

342
NuEVI/src/settings.cpp Normal file
View file

@ -0,0 +1,342 @@
#include <Arduino.h>
#include <EEPROM.h>
#include <Adafruit_SSD1306.h>
#include "settings.h"
#include "globals.h"
#include "menu.h"
#include "hardware.h"
#include "config.h"
#include "midi.h"
#include "led.h"
//Read and write EEPROM data
void writeInt(const uint16_t address, const uint16_t value) {
union {
uint8_t v[2];
uint16_t val;
} data;
data.val = value;
EEPROM.update(address, data.v[0]);
EEPROM.update(address+1, data.v[1]);
}
uint16_t readInt(uint16_t address) {
union {
uint8_t v[2];
uint16_t val;
} data;
data.v[0] = EEPROM.read(address);
data.v[1] = EEPROM.read(address+1);
return data.val;
}
void writeCalibration() {
EEPROM.put(SETTINGS_OFFSET, calibration);
}
void readCalibration() {
EEPROM.get(SETTINGS_OFFSET, calibration);
}
void writePreset(uint8_t preset) {
EEPROM.put(SETTINGS_OFFSET + preset * sizeof(preset_t), presets[preset]);
}
void writePresets() {
EEPROM.put(SETTINGS_OFFSET + CALIBRATION_MAX_SIZE, presets);
}
void readPresets() {
EEPROM.get(SETTINGS_OFFSET + CALIBRATION_MAX_SIZE, presets);
}
//Functions to send and receive config (and other things) via USB MIDI SysEx messages
uint32_t crc32(const uint8_t *message, const size_t length) {
size_t pos=0;
uint32_t crc=0xFFFFFFFF;
while (pos<length) {
crc ^= message[pos++]; //Get next byte and increment position
for (uint8_t j=0; j<8; ++j) { //Mask off 8 next bits
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
}
}
return ~crc;
}
/*
Send EEPROM config dump as sysex message. Message format is structured like this:
+------------------------------------------------------------------------------------+
| vendor(3) | "NuEVIc01" (8) | Payload size (2) | EEPROM data (variable) | crc32 (4) |
+------------------------------------------------------------------------------------+
Payload size is for the EEPROM data chunk (not including anything else before or after
CRC32 covers the entire buffer up to and including the eeprom data (but not the checksum itself)
This currently operates under the assumption that the whole EEPROM chunk only consists of unsigned 16 bit ints, only using the range 0-16383
*/
void sendSysexSettings() {
const char *header = "NuEVIc01"; //NuEVI config dump 01
//Build a send buffer of all the things
size_t sysex_size = 3 + strlen(header) + 2 + EEPROM_SIZE + 4;
uint8_t *sysex_data = (uint8_t*)malloc(sysex_size);
//Positions (offsets) of parts in send buffer
int header_pos = 3;
int size_pos = header_pos + strlen(header);
int payload_pos = size_pos + 2;
int checksum_pos = payload_pos + EEPROM_SIZE;
//SysEX manufacturer ID
memcpy(sysex_data, sysex_id, 3);
//Header with command code
memcpy(sysex_data+header_pos, header, strlen(header));
//Payload length
*(uint16_t*)(sysex_data+size_pos) = convertToMidiValue(EEPROM_SIZE);
//Config data
uint16_t* config_buffer_start = (uint16_t*)(sysex_data+payload_pos);
//Read one settings item at a time, change data format, and put in send buffer
readPresets();
uint16_t *preset_buffer = (uint16_t*)presets;
for(uint16_t idx=0; idx<EEPROM_SIZE/2; idx++) {
uint16_t eepromval = preset_buffer[idx];
config_buffer_start[idx] = convertToMidiValue(eepromval);
}
uint32_t checksum = crc32(sysex_data, checksum_pos);
*(uint32_t*)(sysex_data+checksum_pos) = convertToMidiCRC(checksum);
usbMIDI.sendSysEx(sysex_size, sysex_data);
free(sysex_data);
}
//Send a simple 3-byte message code as sysex
void sendSysexMessage(const char* messageCode) {
char sysexMessage[] = "vvvNuEVIccc"; //Placeholders for vendor and code
memcpy(sysexMessage, sysex_id, 3);
memcpy(sysexMessage+8, messageCode, 3);
usbMIDI.sendSysEx(11, (const uint8_t *)sysexMessage);
}
bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
//Expected size of data (vendor+NuEVIc02+len+payload+crc32)
uint16_t expected_size = 3 + 8 + 2 + EEPROM_SIZE + 4;
//Positions (offsets) of parts in buffer
int size_pos = 11;
int payload_pos = size_pos + 2;
int checksum_pos = payload_pos + EEPROM_SIZE;
//Make sure length of receive buffer is enough to read all we need to. We can accept extra junk at the end though.
if(length<expected_size) {
configShowMessage("Invalid config format");
return false;
}
//No need to verify vendor or header/command, already done before we get here.
//Calculate checksum of stuff received (everything before checksum), transform to midi format
//(being a one-way operation, we can't do the reverse anyway)
uint32_t crc=convertToMidiCRC(crc32(data, checksum_pos));
uint32_t crc_rcv;
memcpy(&crc_rcv, data+checksum_pos, 4);
if(crc != crc_rcv && crc_rcv != NO_CHECKSUM) {
configShowMessage("Invalid checksum");
return false;
}
//Verify that payload size matches the size of our EEPROM config
uint16_t payload_size = convertFromMidiValue(data+size_pos);
if(payload_size != EEPROM_SIZE) {
configShowMessage("Invalid config size");
return false;
}
uint16_t eeprom_version_rcv = convertFromMidiValue(data+(payload_pos+EEPROM_VERSION_ADDR));
if(eeprom_version_rcv != EEPROM_VERSION) {
configShowMessage("Invalid config version");
return false;
}
//Grab all the items in payload and save to EEPROM
uint16_t *preset_buffer = (uint16_t*)presets;
for(uint16_t i=0; i<payload_size/2; i++) {
uint16_t addr = i*2;
uint16_t val;
preset_buffer[addr] = convertFromMidiValue(data+(payload_pos+addr));
}
writePresets();
//All went well
return true;
}
//Send EEPROM and firmware versions
void sendSysexVersion() {
char sysexMessage[] = "vvvNuEVIc04eevvvvvvvv"; //Placeholders for vendor and code
uint8_t fwStrLen = min(strlen(FIRMWARE_VERSION), 8); //Limit firmware version string to 8 bytes
memcpy(sysexMessage, sysex_id, 3);
memcpy(sysexMessage+13, FIRMWARE_VERSION, fwStrLen);
*(uint16_t*)(sysexMessage+11) = convertToMidiValue(EEPROM_VERSION);
uint8_t message_length = 13+fwStrLen;
usbMIDI.sendSysEx(message_length, (const uint8_t *)sysexMessage);
}
extern Adafruit_SSD1306 display;
void configShowMessage(const char* message) {
display.fillRect(0,32,128,64,BLACK);
display.setCursor(0,32);
display.setTextColor(WHITE);
display.print(message);
display.display();
}
uint8_t* sysex_rcv_buffer = NULL;
uint16_t sysex_buf_size = 0;
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last) {
uint16_t pos;
if(!sysex_rcv_buffer) {
//Start out with an empty buffer
pos = 0;
sysex_buf_size = length;
sysex_rcv_buffer = (uint8_t *)malloc(sysex_buf_size);
} else {
//Increase size of current buffer
pos = sysex_buf_size;
sysex_buf_size += length;
sysex_rcv_buffer = (uint8_t *)realloc(sysex_rcv_buffer, sysex_buf_size);
}
//Append this chunk to buffer
memcpy(sysex_rcv_buffer + pos, data, length);
//If it's the last one, call the regular handler to process it
if(last) {
handleSysex(sysex_rcv_buffer, sysex_buf_size);
//Discard the buffer
free(sysex_rcv_buffer);
sysex_rcv_buffer = NULL;
sysex_buf_size = 0;
}
}
void handleSysex(uint8_t *data, unsigned int length) {
//Note: Sysex data as received here contains sysex start and end markers (0xF0 and 0xF7)
//Too short to even contain a 3-byte vendor id is not for us.
if(length<4) return;
//Verify vendor
if(strncmp((char*)(data+1), sysex_id, 3)) return; //Silently ignore different vendor id
//Verify header. Min length is 3+5+3 bytes (vendor+header+message code)
if(length<12 || strncmp((char*)(data+4), "NuEVI", 5)) {
configShowMessage("Invalid message.");
sendSysexMessage("e00");
return;
}
//Get message code
char messageCode[3];
strncpy(messageCode, (char*)(data+9), 3);
if(!strncmp(messageCode, "c00", 3)) { //Config dump request
configShowMessage("Sending config...");
sendSysexSettings();
configShowMessage("Config sent.");
} else if(!strncmp(messageCode, "c03", 3)) { //Version info request
configShowMessage("Sending version.");
sendSysexVersion();
} else if(!strncmp(messageCode, "c02", 3)) { //New config incoming
configShowMessage("Receiving config...");
//Tell receiveSysexSettings about what's between sysex start and end markers
if(receiveSysexSettings(data+1, length-2)) configShowMessage("New config saved.");
} else {
configShowMessage("Unknown message.");
sendSysexMessage("e01"); //Unimplemented message code
}
}
void configModeSetup() {
statusLedFlash(500);
display.clearDisplay();
display.setCursor(0,0);
display.setTextColor(WHITE);
display.setTextSize(0);
display.println("Config mgmt");
display.println("Power off NuEVI");
display.println("to exit");
display.display();
usbMIDI.setHandleSystemExclusive(handleSysexChunk);
statusLedFlash(500);
sendSysexVersion(); //Friendly hello
configShowMessage("Ready.");
}
//"Main loop". Just sits and wait for midi messages and lets the sysex handler do all the work.
void configModeLoop() {
usbMIDI.read();
}
//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade)
void readEEPROM(const bool factoryReset) {
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
uint16_t settings_version = readInt(EEPROM_VERSION_ADDR);
// blank eeprom will be 0xFFFF. For a full reset, call it "version 0" so everything gets overwritten.
if (factoryReset || settings_version == 0xffffu) {
settings_version = 0;
} else {
readPresets();
readCalibration();
}
if(settings_version != EEPROM_VERSION) {
// Add default settings here
writePresets();
writeCalibration();
writeInt(EEPROM_VERSION_ADDR, EEPROM_VERSION);
}
}

123
NuEVI/src/settings.h Normal file
View file

@ -0,0 +1,123 @@
#ifndef __SETTINGS_H
#define __SETTINGS_H
#include <stdint.h>
#include "globals.h"
#define EEPROM_VERSION 1
#define EEPROM_VERSION_ADDR 0
#define SETTINGS_OFFSET 2
#define PRESET_MAX_SIZE 128 // Leave extra space for future settings
#define PRESET_COUNT 8
#define CALIBRATION_MAX_SIZE 54 // Leave extra space for future settings
#define EEPROM_SIZE 1080 // Cannot exceed this amount of EEPROM space
static_assert(SETTINGS_OFFSET + PRESET_MAX_SIZE * PRESET_COUNT + CALIBRATION_MAX_SIZE <= EEPROM_SIZE,
"Not enough EEPROM");
/**
* Sensor calibration is global across presets
*/
struct calibration_t {
int16_t breathThrValOffset = 5;
int16_t breathMaxValOffset = 1500;
int16_t breathAltThrValOffset = 5;
int16_t breathAltMaxValOffset = 1500;
int16_t biteThrVal = 50;
int16_t biteMaxVal = 150;
int16_t pbDnThrVal = 50;
int16_t pbDnMaxVal = 150;
int16_t pbUpThrVal = 50;
int16_t pbUpMaxVal = 150;
int16_t leverThrVal = 50;
int16_t leverMaxVal = 150;
int16_t extraThrVal = 50;
int16_t extraMaxVal = 150;
int16_t ctouchThrVal = 80;
uint8_t _reserved[24];
};
static_assert(sizeof(calibration_t) == CALIBRATION_MAX_SIZE, "calibration data wrong size");
/**
* Per-preset config
*/
struct preset_t {
uint8_t MIDIchannel = 1; // Midi channel to send on
uint8_t breathCC = 2; // breath CC selection
uint8_t altBreathCC = 70; // breath CC selection
uint8_t extraCC = 1; // extra CC selection
uint8_t leverCC = 7; // "lever" CC selection
uint8_t biteCC = 11; // bite CC selection
uint8_t fixedVelocity = 0; // Zero = not fixed
uint8_t portamentoLimit = 127; // 1-127 - max portamento level
uint8_t PBdepth = 1; // OFF:1-12 divider
uint8_t deglitch = 20; // debounes time for key/roller inputs
uint8_t breathCurve = 4; // breath curve selection
uint8_t velSmpDl = 20; // velocity sample delay
uint8_t velBias = 0; // velocity bias
uint8_t pinkySetting = 12; // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 24 (QuickTranspose +1 to +12), 25 (EC2), 26 (ECSW), 27 (LVL), 28 (LVLP)
uint8_t breathInterval; // 3-15
int8_t trill3_interval = 4;
uint8_t vibSquelch = 12; // vibrato signal squelch
uint8_t cvVibRate = 0; // OFF, 1 - 8 CV extra controller LFO vibrato rate 4.5Hz to 8Hz
int8_t cvTune = 0;
int8_t cvScale = 0;
PortamentoMode portamentoMode;
BreathMode breathMode = BreathMode::BREATH_LSB_AT;
FingeringMode fingering = FingeringMode::EVI;
RollerMode rollerMode = RollerMode::HIGHEST;
ExtraControl biteControl = ExtraControl::GLIDE;
ExtraControl leverControl = ExtraControl::VIBRATO;
ExtraControl extraControl = ExtraControl::CC;
VibratoMode vibratoMode = VSTART_DOWN; // direction of first vibrato wave UPWD or DNWD
uint8_t vibratoDepth = 1; // OFF:1-9
uint8_t vibSens = 2; // vibrato sensitivity
uint8_t vibRetn = 2; // vibrato return speed
uint8_t knob1CC = 71;
uint8_t knob2CC = 72;
uint8_t knob3CC = 73;
uint8_t knob4CC = 74;
uint8_t icmAccelMode;
uint8_t icmAccelCC;
uint8_t icmTiltMode;
uint8_t icmTiltCC;
uint8_t icmRotationMode;
uint8_t icmRotationCC;
uint8_t _reserved[87];
};
static_assert(sizeof(preset_t) == PRESET_MAX_SIZE, "preset_t must be 128 bytes");
extern preset_t presets[PRESET_COUNT];
extern calibration_t calibration;
extern preset_t *currentPreset;
#define NO_CHECKSUM 0x7F007F00
void readEEPROM(const bool factoryReset);
void writePreset(uint8_t preset);
void writeCalibration();
//Functions for config management mode
void sendSysexSettings();
void sendSysexMessage(const char* messageCode);
void sendSysexVersion();
void handleSysex(uint8_t *data, unsigned int length);
void handleSysexChunk(const uint8_t *data, uint16_t length, bool last);
uint32_t crc32(const uint8_t *message, const size_t length);
void configInitScreen();
void configShowMessage(const char* message);
void configModeSetup();
void configModeLoop();
#endif

57
NuEVI/src/test.cpp Normal file
View file

@ -0,0 +1,57 @@
#include "hardware.h"
uint8_t oldButtons = 0;
uint16_t oldKeys = 0;
uint16_t oldUtil = 0;
bool plotCap = false;
void handleTestMode() {
uint8_t buttons = buttonState();
if (buttons != oldButtons) {
oldButtons = buttons;
Serial.print("Buttons:");
Serial.println(buttons, HEX);
}
for (int i = 0; i < 4; i++) {
int k = readKnob(i);
if (k != 0) {
Serial.print("Knob");
Serial.print(i);
Serial.print(":");
Serial.println(k);
}
}
uint16_t keys = keysTouched();
if (keys != oldKeys) {
Serial.print("Keys:");
Serial.println(keys, HEX);
}
uint16_t util = utilTouched();
if (util != oldUtil) {
Serial.print("Util:");
Serial.println(util, HEX);
}
if (buttons == 0x01) {
plotCap = !plotCap;
}
if (plotCap) {
for (int i = 0; i < 12; i++) {
Serial.print(">key");
Serial.print(i);
Serial.print(":");
Serial.println(readTouchKey(i));
}
for (int i = 0; i < 12; i++) {
Serial.print(">util");
Serial.print(i);
Serial.print(":");
Serial.println(readTouchUtil(i));
}
}
}

6
NuEVI/src/test.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef __TEST_H
#define __TEST_H
void handleTestMode();
#endif

823
NuEVI/src/xEVI.cpp Normal file
View file

@ -0,0 +1,823 @@
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>
#include <array>
#include "globals.h"
#include "hardware.h"
#include "midi.h"
#include "menu.h"
#include "config.h"
#include "settings.h"
#include "led.h"
/*
NAME: xEVI
WRITTEN BY: BRIAN HREBEC
BASED ON: NuEVI by JOHAN BERGLUND
DATE: 2023-8-23
FOR: PJRC Teensy 4.0 and 2x MPR121 capactive touch sensor board.
Uses an SSD1306 controlled OLED display communicating over I2C and a NeoPixel LED strip for status display
ICM20948 for intertial measurement.
FUNCTION: EVI Wind Controller using MPRLS pressure sensors and capacitive touch keys. Output to USB MIDI and CV.
*/
#if !defined(USB_MIDI) && !defined(USB_MIDI_SERIAL)
#error "USB MIDI not enabled. Please set USB type to 'MIDI' or 'Serial + MIDI'."
#endif
preset_t presets[PRESET_COUNT];
instrument_state_t state;
preset_t *currentPreset;
calibration_t calibration;
static const int pbDepthList[13] = { 8192, 8192, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 744, 683 };
static const float vibDepth[10] = { 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.40, 0.45 }; // max pitch bend values (+/-) for the vibrato settings
static const short vibMaxBiteList[16] = { 1400, 1200, 1000, 900, 800, 700, 600, 500, 400, 300, 250, 200, 150, 100, 50, 25 };
static const short vibMaxList[12] = { 300, 275, 250, 225, 200, 175, 150, 125, 100, 75, 50, 25 };
static const int timeDividerList[9] = { 0, 222, 200, 181, 167, 152, 143, 130, 125 }; // For CV vibrato - 222 is 4.5Hz, 200 is 5Hz, 181 is 5.5Hz 167 is 6Hz, 152 is 6.5Hz, 143 is 7Hz, 130 is 7.5Hz, 125 is 8Hz
static const unsigned short curveM4[] = { 0, 4300, 7000, 8700, 9900, 10950, 11900, 12600, 13300, 13900, 14500, 15000, 15450, 15700, 16000, 16250, 16383 };
static const unsigned short curveM3[] = { 0, 2900, 5100, 6650, 8200, 9500, 10550, 11500, 12300, 13100, 13800, 14450, 14950, 15350, 15750, 16150, 16383 };
static const unsigned short curveM2[] = { 0, 2000, 3600, 5000, 6450, 7850, 9000, 10100, 11100, 12100, 12900, 13700, 14400, 14950, 15500, 16000, 16383 };
static const unsigned short curveM1[] = { 0, 1400, 2850, 4100, 5300, 6450, 7600, 8700, 9800, 10750, 11650, 12600, 13350, 14150, 14950, 15650, 16383 };
const unsigned short curveIn[] = { 0, 1023, 2047, 3071, 4095, 5119, 6143, 7167, 8191, 9215, 10239, 11263, 12287, 13311, 14335, 15359, 16383 };
static const unsigned short curveP1[] = { 0, 600, 1350, 2150, 2900, 3800, 4700, 5600, 6650, 7700, 8800, 9900, 11100, 12300, 13500, 14850, 16383 };
static const unsigned short curveP2[] = { 0, 400, 800, 1300, 2000, 2650, 3500, 4300, 5300, 6250, 7400, 8500, 9600, 11050, 12400, 14100, 16383 };
static const unsigned short curveP3[] = { 0, 200, 500, 900, 1300, 1800, 2350, 3100, 3800, 4600, 5550, 6550, 8000, 9500, 11250, 13400, 16383 };
static const unsigned short curveP4[] = { 0, 100, 200, 400, 700, 1050, 1500, 1950, 2550, 3200, 4000, 4900, 6050, 7500, 9300, 12100, 16383 };
static const unsigned short curveS1[] = { 0, 600, 1350, 2150, 2900, 3800, 4700, 6000, 8700, 11000, 12400, 13400, 14300, 14950, 15500, 16000, 16383 };
static const unsigned short curveS2[] = { 0, 600, 1350, 2150, 2900, 4000, 6100, 9000, 11000, 12100, 12900, 13700, 14400, 14950, 15500, 16000, 16383 };
// static const unsigned short curveS3[] = {0,600,1350,2300,3800,6200,8700,10200,11100,12100,12900,13700,14400,14950,15500,16000,16383};
// static const unsigned short curveS4[] = {0,600,1700,4000,6600,8550,9700,10550,11400,12200,12900,13700,14400,14950,15500,16000,16383};
static const unsigned short curveZ1[] = { 0, 1400, 2100, 2900, 3200, 3900, 4700, 5600, 6650, 7700, 8800, 9900, 11100, 12300, 13500, 14850, 16383 };
static const unsigned short curveZ2[] = { 0, 2000, 3200, 3800, 4096, 4800, 5100, 5900, 6650, 7700, 8800, 9900, 11100, 12300, 13500, 14850, 16383 };
const std::array<const unsigned short*, 13> curves = {
curveM4, curveM3, curveM2, curveM1, curveIn, curveP1, curveP2,
curveP3, curveP4, curveS1, curveS2, curveZ1, curveZ2 };
static int waveformsTable[maxSamplesNum] = {
// Sine wave
0x7ff, 0x86a, 0x8d5, 0x93f, 0x9a9, 0xa11, 0xa78, 0xadd, 0xb40, 0xba1,
0xbff, 0xc5a, 0xcb2, 0xd08, 0xd59, 0xda7, 0xdf1, 0xe36, 0xe77, 0xeb4,
0xeec, 0xf1f, 0xf4d, 0xf77, 0xf9a, 0xfb9, 0xfd2, 0xfe5, 0xff3, 0xffc,
0xfff, 0xffc, 0xff3, 0xfe5, 0xfd2, 0xfb9, 0xf9a, 0xf77, 0xf4d, 0xf1f,
0xeec, 0xeb4, 0xe77, 0xe36, 0xdf1, 0xda7, 0xd59, 0xd08, 0xcb2, 0xc5a,
0xbff, 0xba1, 0xb40, 0xadd, 0xa78, 0xa11, 0x9a9, 0x93f, 0x8d5, 0x86a,
0x7ff, 0x794, 0x729, 0x6bf, 0x655, 0x5ed, 0x586, 0x521, 0x4be, 0x45d,
0x3ff, 0x3a4, 0x34c, 0x2f6, 0x2a5, 0x257, 0x20d, 0x1c8, 0x187, 0x14a,
0x112, 0xdf, 0xb1, 0x87, 0x64, 0x45, 0x2c, 0x19, 0xb, 0x2,
0x0, 0x2, 0xb, 0x19, 0x2c, 0x45, 0x64, 0x87, 0xb1, 0xdf,
0x112, 0x14a, 0x187, 0x1c8, 0x20d, 0x257, 0x2a5, 0x2f6, 0x34c, 0x3a4,
0x3ff, 0x45d, 0x4be, 0x521, 0x586, 0x5ed, 0x655, 0x6bf, 0x729, 0x794 };
const int rollerHarmonic[2][7] = { {0, 7, 12, 16, 19, 24, 26}, // F horn 2,3,4,5,6,8,9 hrm
{7, 12, 16, 19, 24, 26, 31} }; // Bb horn 3,4,5,6,8,9,12 hrm
const int trumpetHarmonic[2][7] = { {0, 7, 12, 16, 19, 26, 31}, //! K4: hrm 8->9, 10->12
{0, 7, 12, 16, 19, 24, 28} }; // trumpet 2,3,4,5,6,8,10 hrm
bool configManagementMode = false;
bool testMode = false;
//_______________________________________________________________________________________________ SETUP
// MIDI note value check with out of range octave repeat
inline int noteValueCheck(int note) {
if (note > 127) {
note = 115 + (note - 127) % 12;
} else if (note < 0) {
note = 12 - abs(note) % 12;
}
return note;
}
//***********************************************************
void port(int portCC) {
if (portCC == state.portamentoVal) {
return;
}
if (currentPreset->portamentoMode == PortamentoMode::PON || currentPreset->portamentoMode == PortamentoMode::PGLIDE_ONLY) {
if (state.portamentoVal > 0 && portCC == 0) {
midiSendControlChange(CCN_PortOnOff, 0);
} else if (state.portamentoVal == 0 && portCC > 0) {
midiSendControlChange(CCN_PortOnOff, 127);
}
}
midiSendControlChange(CCN_Port, portCC);
state.portamentoVal = portCC;
}
// Update CV output pin, run from timer.
void cvUpdate() {
static byte cvPortaTuneCount = 0;
uint32_t currentTime = millis();
int cvPressure = readPressure();
analogWrite(cvBreathPin, cvPressure);
state.targetPitch = (state.activeNote - 24) * 42;
state.targetPitch += map(state.pitchBend, 0, 16383, -84, 84);
state.targetPitch -= state.quarterToneTrigger * 21;
if (state.portamentoVal > 0) {
if (state.targetPitch > state.cvPitch) {
if (!cvPortaTuneCount) {
state.cvPitch += 1 + (127 - state.portamentoVal) / 4;
} else {
cvPortaTuneCount++;
if (cvPortaTuneCount > CVPORTATUNE)
cvPortaTuneCount = 0;
}
if (state.cvPitch > state.targetPitch)
state.cvPitch = state.targetPitch;
} else if (state.targetPitch < state.cvPitch) {
if (!cvPortaTuneCount) {
state.cvPitch -= 1 + (127 - state.portamentoVal) / 4;
} else {
cvPortaTuneCount++;
if (cvPortaTuneCount > CVPORTATUNE)
cvPortaTuneCount = 0;
}
if (state.cvPitch < state.targetPitch)
state.cvPitch = state.targetPitch;
} else {
state.cvPitch = state.targetPitch;
}
} else {
state.cvPitch = state.targetPitch;
}
if (currentPreset->cvVibRate) {
int timeDivider = timeDividerList[currentPreset->cvVibRate];
int cvVib = map(((waveformsTable[map(currentTime % timeDivider, 0, timeDivider, 0, maxSamplesNum - 1)] - 2047)), -259968, 259969, -11, 11);
state.cvPitch += cvVib;
}
int cvPitchTuned = 2 * (currentPreset->cvTune) + map(state.cvPitch, 0, 4032, 0, 4032 + 2 * (currentPreset->cvScale));
analogWrite(cvPitchPin, constrain(cvPitchTuned, 0, 4095));
}
//**************************************************************
// non linear mapping function (http://playground.arduino.cc/Main/MultiMap)
// note: the _in array should have increasing values
unsigned int multiMap(unsigned short val, const unsigned short* _in, const unsigned short* _out, uint8_t size) {
// take care the value is within range
// val = constrain(val, _in[0], _in[size-1]);
if (val <= _in[0])
return _out[0];
if (val >= _in[size - 1])
return _out[size - 1];
// search right interval
uint8_t pos = 1; // _in[0] allready tested
while (val > _in[pos])
pos++;
// this will handle all exact "points" in the _in array
if (val == _in[pos])
return _out[pos];
// interpolate in the right segment for the rest
return (val - _in[pos - 1]) * (_out[pos] - _out[pos - 1]) / (_in[pos] - _in[pos - 1]) + _out[pos - 1];
}
//**************************************************************
// map breath values to selected curve
unsigned int breathCurve(unsigned int inputVal) {
if (currentPreset->breathCurve >= curves.size())
return inputVal;
return multiMap(inputVal, curveIn, curves[currentPreset->breathCurve], 17);
}
//**************************************************************
int patchLimit(int value) {
return constrain(value, 1, 128);
}
//**************************************************************
int breath() {
static int oldbreath = 0;
static unsigned int oldbreathhires = 0;
int breathCCval, breathCCvalFine;
unsigned int breathCCvalHires;
breathCCvalHires = breathCurve(mapConstrain(state.breathSignal, state.breathThrVal, state.breathMaxVal, 0, 16383));
breathCCval = (breathCCvalHires >> 7) & 0x007F;
breathCCvalFine = breathCCvalHires & 0x007F;
if (breathCCval != oldbreath) { // only send midi data if breath has changed from previous value
if (currentPreset->breathCC) {
// send midi cc
midiSendControlChange(currentPreset->breathCC, breathCCval);
}
if (currentPreset->breathMode == BreathMode::BREATH_AT || currentPreset->breathMode == BreathMode::BREATH_LSB_AT) {
// send aftertouch
midiSendAfterTouch(breathCCval);
}
oldbreath = breathCCval;
}
if (breathCCvalHires != oldbreathhires
&& (currentPreset->breathMode == BreathMode::BREATH_LSB || currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) {
midiSendControlChange(currentPreset->breathCC + 32, breathCCvalFine);
}
oldbreathhires = breathCCvalHires;
return breathCCval;
}
//**************************************************************
void pitch_bend() {
// handle input from pitchbend touchpads and
// on-pcb variable capacitor for vibrato.
static int oldpb = 0;
int vibMax;
int vibMaxBite;
int calculatedPBdepth;
byte pbTouched = 0;
int vibRead = 0;
int vibReadBite = 0;
state.pbUpSignal = readTouchUtil(pbUpPin); // PCB PIN "Pu"
state.pbDnSignal = readTouchUtil(pbDnPin); // PCB PIN "Pd"
bool halfPitchBendKey = (currentPreset->pinkySetting == PBD) && state.pinkyKey; // hold pinky key for 1/2 pitchbend value
state.quarterToneTrigger = (currentPreset->pinkySetting == QTN) && state.pinkyKey; // pinky key for a quarter tone down using pitch bend (assuming PB range on synth is set to 2 semitones)
calculatedPBdepth = pbDepthList[currentPreset->PBdepth];
if (halfPitchBendKey)
calculatedPBdepth = calculatedPBdepth * 0.5;
vibMax = vibMaxList[currentPreset->vibSens - 1];
float calculatedDepth = 0;
if (currentPreset->vibratoMode == VibratoMode::VSTART_DOWN) {
calculatedDepth = calculatedPBdepth * vibDepth[currentPreset->vibratoDepth];
} else {
calculatedDepth = (0 - calculatedPBdepth * vibDepth[currentPreset->vibratoDepth]);
}
if (ExtraControl::VIBRATO == currentPreset->biteControl) { // bite vibrato
vibMaxBite = vibMaxBiteList[currentPreset->vibSens - 1];
vibReadBite = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
if (vibReadBite < state.vibThrBite) {
state.vibSignal = (state.vibSignal + mapConstrain(
vibReadBite, (state.vibZeroBite - vibMaxBite), state.vibThrBite, calculatedDepth, 0)
) / 2;
} else if (vibReadBite > state.vibThrBiteLo) {
state.vibSignal = (state.vibSignal + mapConstrain(
vibReadBite, (state.vibZeroBite + vibMaxBite), state.vibThrBite, calculatedDepth, 0)
) / 2;
} else {
state.vibSignal = state.vibSignal / 2;
}
}
if (ExtraControl::VIBRATO == currentPreset->leverControl) { // lever vibrato
vibRead = readTouchUtil(vibratoPin);
if (vibRead < state.vibThr) {
state.vibSignal = (state.vibSignal +
mapConstrain(vibRead, (state.vibZero - vibMax), state.vibThr, calculatedDepth, 0)
) / 2;
} else if (vibRead > state.vibThrLo) {
state.vibSignal = (state.vibSignal +
mapConstrain(vibRead, (state.vibZero + vibMax), state.vibThr, calculatedDepth, 0)
) / 2;
} else {
state.vibSignal = state.vibSignal / 2;
}
}
switch (currentPreset->vibRetn) { // moving baseline
case 0:
// keep vibZero value
break;
case 1:
state.vibZero = state.vibZero * 0.95 + vibRead * 0.05;
state.vibZeroBite = state.vibZeroBite * 0.95 + vibReadBite * 0.05;
break;
case 2:
state.vibZero = state.vibZero * 0.9 + vibRead * 0.1;
state.vibZeroBite = state.vibZeroBite * 0.9 + vibReadBite * 0.1;
break;
case 3:
state.vibZero = state.vibZero * 0.8 + vibRead * 0.2;
state.vibZeroBite = state.vibZeroBite * 0.8 + vibReadBite * 0.2;
break;
case 4:
state.vibZero = state.vibZero * 0.6 + vibRead * 0.4;
state.vibZeroBite = state.vibZeroBite * 0.6 + vibReadBite * 0.4;
}
state.vibThr = state.vibZero - currentPreset->vibSquelch;
state.vibThrLo = state.vibZero + currentPreset->vibSquelch;
state.vibThrBite = state.vibZeroBite - currentPreset->vibSquelch;
state.vibThrBiteLo = state.vibZeroBite + currentPreset->vibSquelch;
int pbPos = mapConstrain(state.pbUpSignal, calibration.pbUpMaxVal, calibration.pbUpThrVal, calculatedPBdepth, 0);
int pbNeg = mapConstrain(state.pbDnSignal, calibration.pbDnMaxVal, calibration.pbDnThrVal, calculatedPBdepth, 0);
int pbSum = 8193 + pbPos - pbNeg;
int pbDif = abs(pbPos - pbNeg);
if ((state.pbUpSignal < calibration.pbUpThrVal || state.pbDnSignal < calibration.pbDnThrVal) && currentPreset->PBdepth) {
if (pbDif < 10) {
state.pitchBend = 8192;
} else {
state.pitchBend = state.pitchBend * 0.6 + 0.4 * pbSum;
}
pbTouched = 1;
}
if (!pbTouched) {
state.pitchBend = state.pitchBend * 0.6 + 8192 * 0.4; // released, so smooth your way back to zero
if ((state.pitchBend > 8187) && (state.pitchBend < 8197))
state.pitchBend = 8192; // 8192 is 0 pitch bend, don't miss it bc of smoothing
}
state.pitchBend = state.pitchBend + state.vibSignal;
state.pitchBend = constrain(state.pitchBend, 0, 16383);
state.pbSend = state.pitchBend - state.quarterToneTrigger * calculatedPBdepth * 0.25;
state.pbSend = constrain(state.pbSend, 0, 16383);
if (state.pbSend != oldpb) { // only send midi data if pitch bend has changed from previous value
midiSendPitchBend(state.pbSend);
oldpb = state.pbSend;
}
}
//***********************************************************
void portamento_() {
if (currentPreset->portamentoMode == PortamentoMode::POFF) {
port(0); // ensure it's off
return;
}
int portSumCC = 0;
if (currentPreset->pinkySetting == GLD) {
if (state.pinkyKey) {
portSumCC += currentPreset->portamentoLimit;
}
}
if (ExtraControl::GLIDE == currentPreset->biteControl) {
// Portamento is controlled with the bite sensor in the mouthpiece
state.biteSignal = readTouchUtil(bitePin);
if (state.biteSignal >= calibration.biteThrVal) { // if we are enabled and over the threshold, send portamento
portSumCC += mapConstrain(state.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, currentPreset->portamentoLimit);
}
}
if (ExtraControl::GLIDE == currentPreset->leverControl) {
// Portamento is controlled with thumb lever
state.leverSignal = readTouchUtil(vibratoPin);
if (((3000 - state.leverSignal) >= calibration.leverThrVal)) { // if we are enabled and over the threshold, send portamento
portSumCC += mapConstrain((3000 - state.leverSignal), calibration.leverThrVal, calibration.leverMaxVal, 0, currentPreset->portamentoLimit);
}
}
port(constrain(portSumCC, 0, currentPreset->portamentoLimit)); // Total output glide rate limited to glide max setting
}
//***********************************************************
void biteCC_() {
int biteVal = 0;
if (ExtraControl::CC == currentPreset->biteControl) {
state.biteSignal = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
if (state.biteSignal >= calibration.biteThrVal) { // we are over the threshold, calculate CC value
biteVal = mapConstrain(state.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127);
}
if (biteVal != state.biteVal) {
midiSendControlChange(currentPreset->biteCC, biteVal);
}
state.biteVal = biteVal;
}
}
void autoCal() {
state.vibZero = state.vibZeroBite = 0;
for(int i = 1 ; i <= CALIBRATE_SAMPLE_COUNT; ++i) {
state.breathZero += readPressure();
state.breathAltZero += readAltPressure();
state.vibZero += readTouchUtil(vibratoPin);
state.vibZeroBite += readTouchUtil(bitePin);
}
state.breathZero /= CALIBRATE_SAMPLE_COUNT;
state.breathAltZero /= CALIBRATE_SAMPLE_COUNT;
state.vibZero /= CALIBRATE_SAMPLE_COUNT;
state.vibZeroBite /= CALIBRATE_SAMPLE_COUNT;
state.vibThr = state.vibZero - currentPreset->vibSquelch;
state.vibThrLo = state.vibZero + currentPreset->vibSquelch;
state.vibThrBite = state.vibZeroBite - currentPreset->vibSquelch;
state.vibThrBiteLo = state.vibZeroBite + currentPreset->vibSquelch;
state.breathThrVal = state.breathZero + calibration.breathThrValOffset;
state.breathMaxVal = state.breathThrVal + calibration.breathMaxValOffset;
state.breathAltThrVal = state.breathAltZero + calibration.breathAltThrValOffset;
state.breathAltMaxVal = state.breathAltThrVal + calibration.breathAltMaxValOffset;
}
void fullAutoCal() {
int calRead;
int calReadNext;
autoCal();
// Lever
calRead = 3000 - readTouchUtil(vibratoPin);
calibration.leverThrVal = constrain(calRead + 60, LEVER_LO_LIMIT, LEVER_HI_LIMIT);
calibration.leverMaxVal = constrain(calRead + 120, LEVER_LO_LIMIT, LEVER_HI_LIMIT);
// Bite sensor
calRead = readTouchUtil(bitePin);
calibration.biteThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT);
calibration.biteMaxVal = constrain(calibration.biteThrVal + 300, BITE_LO_LIMIT, BITE_HI_LIMIT);
// Touch sensors
calRead = CTOUCH_HI_LIMIT;
for (byte i = 0; i < 12; i++) {
calReadNext = readTouchKey(i);
if (calReadNext < calRead)
calRead = calReadNext; // use lowest value
}
calibration.ctouchThrVal = calRead - 20;
}
//***********************************************************
/*
* Read octave and return offset
*/
int readOctave() {
static byte lastOctaveR = 0;
// Roller modes
// 1: Highest touched roller, release memory
// 2: Highest touched roller, extend range on top/bottom release
// 3: Touched roller pair
// 4: Touched roller pair, extend range
// 5: Touched roller, pair = 5th partial
// 6: Touched roller, pair = 5th partial, extend range
RollerMode rollerMode = currentPreset->rollerMode;
byte extend = rollerMode == RollerMode::HIGHEST_EXTEND || rollerMode == RollerMode::HIGHEST_PAIR_EXTEND || rollerMode == RollerMode::PARTIAL_EXTEND ? 1 : 0;
byte rollers[6];
uint16_t ctouchThrVal = calibration.ctouchThrVal;
rollers[0] = readTouchUtil(R1Pin) < ctouchThrVal;
rollers[1] = readTouchUtil(R2Pin) < ctouchThrVal;
rollers[2] = readTouchUtil(R3Pin) < ctouchThrVal;
rollers[3] = readTouchUtil(R4Pin) < ctouchThrVal;
rollers[4] = readTouchUtil(R5Pin) < ctouchThrVal;
rollers[5] = readTouchUtil(R6Pin) < ctouchThrVal;
byte offset = 0;
byte octaveR = 0;
if (rollerMode == RollerMode::HIGHEST || rollerMode == RollerMode::HIGHEST_EXTEND) {
for (int i = 5; i >= 0; i--) {
if (rollers[i]) {
octaveR = i + 1;
break;
}
}
} else if (rollerMode == RollerMode::HIGHEST_PAIR || rollerMode == RollerMode::HIGHEST_PAIR_EXTEND) {
for (int i = 5; i >= 1; i--) {
if (rollers[i] && rollers[i - 1]) {
octaveR = i + 1;
break;
}
}
// Allow top/bottom single rollers in extended mode
if (octaveR == 0 && extend) {
if (rollers[0]) {
octaveR = 1;
} else if (rollers[5]) {
octaveR = 6;
}
}
} else if (rollerMode == RollerMode::PARTIAL || rollerMode == RollerMode::PARTIAL_EXTEND) {
for (int i = 5; i >= 0; i--) {
if (rollers[i]) {
octaveR = i + 1;
if (i > 0 && rollers[i - 1]) {
offset = -5;
}
break;
}
}
// Go up/down a partial on top/bottom release
if (extend && octaveR == 0) {
if (lastOctaveR == 1) {
offset = -5;
} else if (lastOctaveR == 6) {
offset = 7;
}
octaveR = lastOctaveR;
}
}
// Handle extended release
if (extend && octaveR == 0) {
if (rollerMode == RollerMode::HIGHEST_EXTEND && lastOctaveR == 6) {
octaveR = 7;
} else if (rollerMode == RollerMode::PARTIAL_EXTEND && lastOctaveR == 7) {
octaveR = 8;
} else if (lastOctaveR == 1) {
octaveR = 0;
} else {
octaveR = lastOctaveR;
}
}
lastOctaveR = octaveR;
FingeringMode fingering = currentPreset->fingering;
byte K4 = readTouchKey(K4Pin) < calibration.ctouchThrVal;
if (FingeringMode::TPT == fingering) { // TPT fingering
return 24 + offset + trumpetHarmonic[K4][octaveR]; // roller harmonics
} else if (FingeringMode::HRN == fingering) { // HRN fingering
return 12 + offset + rollerHarmonic[K4][octaveR]; // roller harmonics
} else if (FingeringMode::EVR == fingering) { // HRN fingering
return 12 * (6 - octaveR) + offset;
} else { // EVI
return 12 * octaveR + offset;
}
}
int readSwitches() {
// Keep the last fingering value for debouncing
static int lastFingering = 0;
static int fingeredNote = 0;
static unsigned long lastDeglitchTime = 0; // The last time the fingering was changed
// Read touch pads (MPR121), compare against threshold value
bool touchKeys[12];
for (byte i = 0; i < 12; i++) {
touchKeys[i] = readTouchKey(i) < calibration.ctouchThrVal;
}
// Valves and trill keys, TRUE (1) for pressed, FALSE (0) for not pressed
byte K1 = touchKeys[K1Pin]; // Valve 1 (pitch change -2)
byte K2 = touchKeys[K2Pin]; // Valve 2 (pitch change -1)
byte K3 = touchKeys[K3Pin]; // Valve 3 (pitch change -3)
byte K4 = touchKeys[K4Pin]; // Left Hand index finger (pitch change -5)
byte K5 = touchKeys[K5Pin]; // Trill key 1 (pitch change +2)
byte K6 = touchKeys[K6Pin]; // Trill key 2 (pitch change +1)
byte K7 = touchKeys[K7Pin]; // Trill key 3 (pitch change +4)
state.pinkyKey = touchKeys[K8Pin];
int qTransp = (state.pinkyKey && (currentPreset->pinkySetting < 25)) ? currentPreset->pinkySetting - 12 : 0;
// Calculate midi note number from pressed keys
int fingeredNoteUntransposed = 0;
if (EVI == currentPreset->fingering) { // EVI fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 5 * K4 // Fifth key
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (EVR == currentPreset->fingering) { // EVR fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 5 * K4 // Fifth key
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (TPT == currentPreset->fingering) { // TPT fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 2 // Trumpet in B flat
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (HRN == currentPreset->fingering) { // HRN fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
+ 5 * K4 // Switch to Bb horn
+ 5 // Horn in F
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
}
if (K3 && K7) {
if (4 == currentPreset->trill3_interval)
fingeredNoteUntransposed += 2;
else
fingeredNoteUntransposed += 4;
}
int fingeredNoteRead = fingeredNoteUntransposed + state.transpose - 12 + qTransp;
if (fingeredNoteRead != lastFingering) { //
// reset the debouncing timer
lastDeglitchTime = millis();
}
if ((millis() - lastDeglitchTime) > currentPreset->deglitch) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current state
fingeredNote = fingeredNoteRead;
}
lastFingering = fingeredNoteRead;
return fingeredNote;
}
void noteOn(int fingeredNote, float pressureSensor, int initial_breath_value) {
// Yes, so calculate MIDI note and velocity, then send a note on event
// We should be at tonguing peak, so set velocity based on current pressureSensor value unless fixed velocity is set
state.breathSignal = constrain(max(pressureSensor, initial_breath_value), state.breathThrVal, state.breathMaxVal);
byte velocitySend;
if (!currentPreset->fixedVelocity) {
unsigned int breathValHires = breathCurve(mapConstrain(state.breathSignal, state.breathThrVal, state.breathMaxVal, 0, 16383));
velocitySend = (breathValHires >> 7) & 0x007F;
velocitySend = constrain(velocitySend + velocitySend * .1 * currentPreset->velBias, 1, 127);
} else {
velocitySend = currentPreset->fixedVelocity;
}
breath(); // send breath data
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
state.activeNote = fingeredNote;
}
void handleOffStateActions() {
if (state.activeMIDIchannel != currentPreset->MIDIchannel) {
state.activeMIDIchannel = currentPreset->MIDIchannel; // only switch channel if no active note
midiSetChannel(state.activeMIDIchannel);
}
if ((state.activePatch != state.patch) && state.doPatchUpdate) {
state.activePatch = state.patch;
midiSendProgramChange(state.activePatch);
state.doPatchUpdate = 0;
}
}
/*
Initialize the main instrument state
*/
void initState() {
state.activePatch = 0;
state.mainState = NOTE_OFF; // initialize main state machine
state.activeMIDIchannel = currentPreset->MIDIchannel;
midiInitialize(currentPreset->MIDIchannel);
}
/**
* Send CC data when needed
*/
void sendCCs() {
static unsigned long ccBreathSendTime = 0L; // The last time we sent breath CC values
static unsigned long ccSendTime = 0L; // The last time we sent CC values
static unsigned long ccSendTime2 = 0L; // The last time we sent CC values 2 (slower)
static unsigned long ccSendTime3 = 0L; // The last time we sent CC values 3 (and slower)
// Is it time to send more CC data?
uint32_t currentTime = millis();
if ((currentTime - ccBreathSendTime) > (currentPreset->breathInterval - 1u)) {
breath();
ccBreathSendTime = currentTime;
}
if (currentTime - ccSendTime > CC_INTERVAL_PRIMARY) {
// deal with Pitch Bend, Modulation, etc.
pitch_bend();
biteCC_();
ccSendTime = currentTime;
}
if (currentTime - ccSendTime2 > CC_INTERVAL_PORT) {
portamento_();
ccSendTime2 = currentTime;
}
if (currentTime - ccSendTime3 > CC_INTERVAL_OTHER) {
updateSensorLEDs();
ccSendTime3 = currentTime;
}
}
/**
* Main instrument state machine
*/
void runStateMachine() {
static unsigned long breath_on_time = 0L; // Time when breath sensor value went over the ON threshold
static int initial_breath_value = 0; // The breath value at the time we observed the transition
int fingeredNote = noteValueCheck(readSwitches() + readOctave());
if (state.mainState == NOTE_OFF) {
handleOffStateActions();
if ((state.breathSignal > state.breathThrVal)) {
// Value has risen above threshold. Move to the RISE_WAIT
// state. Record time and initial breath value.
breath_on_time = millis();
initial_breath_value = state.breathSignal;
state.mainState = RISE_WAIT; // Go to next state
}
} else if (state.mainState == RISE_WAIT) {
if ((state.breathSignal > state.breathThrVal)) {
// Has enough time passed for us to collect our second sample?
if ((millis() - breath_on_time > currentPreset->velSmpDl) || (0 == currentPreset->velSmpDl) || currentPreset->fixedVelocity) {
noteOn(fingeredNote, state.breathSignal, initial_breath_value);
state.mainState = NOTE_ON;
}
} else {
// Value fell below threshold before velocity sample delay time passed. Return to NOTE_OFF state
state.mainState = NOTE_OFF;
}
} else if (state.mainState == NOTE_ON) {
if ((state.breathSignal < state.breathThrVal)) {
// Value has fallen below threshold - turn the note off
midiSendNoteOff(state.activeNote); // send Note Off message
state.breathSignal = 0;
state.mainState = NOTE_OFF;
} else {
if (fingeredNote != state.activeNote) {
// Player has moved to a new fingering while still blowing.
// Send a note off for the current note and a note on for
// the new note.
noteOn(fingeredNote, state.breathSignal, 0);
delayMicroseconds(2000); // delay for midi recording fix
midiSendNoteOff(state.activeNote); // send Note Off message
}
}
}
}
void setup() {
if (checkButtonState(DEBUG_CONFIG)) {
Serial.begin(9600); // debug
Serial.println("Debug Startup");
}
bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET);
configManagementMode = checkButtonState(STARTUP_CONFIG);
testMode = checkButtonState(TEST_CONFIG);
initDisplay(); // Start up display and show logo
initHardware();
// If going into config management mode, stop here before we even touch the EEPROM.
if (configManagementMode) {
configModeSetup();
return;
}
// Read eeprom data into global vars
readEEPROM(factoryReset);
statusLedFlash(500);
statusLedOff();
if (factoryReset) {
// Full calibration
fullAutoCal();
} else {
// Minimal startup calibration (atmo pressure)
autoCal();
}
showVersion();
delay(1000);
initState(); // Set up midi/etc
statusLedOn(); // Switch on the onboard LED to indicate power on/ready
}
//_______________________________________________________________________________________________ MAIN LOOP
void loop() {
static unsigned long pixelUpdateTime = 0;
static const unsigned long pixelUpdateInterval = 80;
// If in config mgmt loop, do that and nothing else
if (configManagementMode) {
configModeLoop();
return;
}
if (testMode) {
}
state.breathSignal = constrain(readPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP
runStateMachine();
sendCCs();
// cvUpdate();
midiDiscardInput();
if (millis() - pixelUpdateTime > pixelUpdateInterval) {
// even if we just alter a pixel, the whole display is redrawn (35ms of MPU lockup) and we can't do that all the time
// this is one of the big reasons the display is for setup use only
// TODO: is this still true on teensy 4?
pixelUpdateTime = millis();
handleMenu(true);
} else {
handleMenu(false);
}
}

70
README.md Executable file → Normal file
View file

@ -1,35 +1,35 @@
# NuEVI
A project by wind controller enthusiasts wanting to save the endangered Electronic Valve Instrument.
Follow the project at https://hackaday.io/project/25756-diy-evi-style-windcontroller
## Building NuEVI
NuEVI is easiest to build using the Arduino IDE. You will also need to download and install
[Teensyduino](https://www.pjrc.com/teensy/td_download.html) to build for and upload to the Teensy.
### Libraries
A few libraries need to be added that are not part of the default Arduino install. These can be
added directly via the Library Manager in the Arduino IDE:
* Adafruit MPR121
* Adafruit GFX
* Adafruit SSD1306 (version 1.2.9 or above)
* NuEVI also includes on the [Filters](https://github.com/JonHub/Filters) library by Jonathan Driscoll, but that is no longer an external dependency.
### Compile options
Open NuEVI.ino in the Arduino IDE. Under "Tools -> Board", select "Teensy 3.2 / 3.1". Then set
"Tools -> USB Type" to "MIDI".
### Building and uploading
Connect the NuEVI via USB to your computer, open the Teensy application and make sure the "Auto"
option is selected (the green round icon). In Arduino IDE, select "Sketch -> Verify/Compile" and
once that is complete press the reset button on the Teensy chip (you have to remove the top cover
on the NuEVI to access this). Upon resetting, it should upload the new firmware onto the NuEVI.
After uploading new firmware, you may need to reset the config memory of the NuEVI. To do this, press
and hold the MENU and ENTER buttons while turning on the NuEVI. Note that this resets all sensor calibrations too. For new versions this should not be neccessary, as value and version checks are in place.
# NuEVI
A project by wind controller enthusiasts wanting to save the endangered Electronic Valve Instrument.
Follow the project at https://hackaday.io/project/25756-diy-evi-style-windcontroller
## Building NuEVI
NuEVI is easiest to build using the Arduino IDE. You will also need to download and install
[Teensyduino](https://www.pjrc.com/teensy/td_download.html) to build for and upload to the Teensy.
### Libraries
A few libraries need to be added that are not part of the default Arduino install. These can be
added directly via the Library Manager in the Arduino IDE:
* Adafruit MPR121
* Adafruit GFX
* Adafruit SSD1306 (version 1.2.9 or above)
* NuEVI also includes on the [Filters](https://github.com/JonHub/Filters) library by Jonathan Driscoll, but that is no longer an external dependency.
### Compile options
Open NuEVI.ino in the Arduino IDE. Under "Tools -> Board", select "Teensy 3.2 / 3.1". Then set
"Tools -> USB Type" to "MIDI".
### Building and uploading
Connect the NuEVI via USB to your computer, open the Teensy application and make sure the "Auto"
option is selected (the green round icon). In Arduino IDE, select "Sketch -> Verify/Compile" and
once that is complete press the reset button on the Teensy chip (you have to remove the top cover
on the NuEVI to access this). Upon resetting, it should upload the new firmware onto the NuEVI.
After uploading new firmware, you may need to reset the config memory of the NuEVI. To do this, press
and hold the MENU and ENTER buttons while turning on the NuEVI. Note that this resets all sensor calibrations too. For new versions this should not be neccessary, as value and version checks are in place.

View file

@ -1,153 +1,153 @@
#include <Wire.h>
#include <Adafruit_MPR121.h>
/*
Tool to test an MPR121 unit. Wires each touch input to a teensy pin to simulate touch sensor signals.
Used to detect faulty MPR121 units before assembly.
*/
Adafruit_MPR121 touchSensor = Adafruit_MPR121(); // This is the 12-input touch sensor
//LED output pins. Red is the onboard one, other two are just wired via resistors to +3.3V
#define RED_LED 13
#define GREEN_LED 20
#define YELLOW_LED 21
void setup(void) {
Serial.begin(115200);
//Set all "test ports" to high-impedance for now
for(int i=0; i<=11; i++) {
pinMode(i, INPUT);
}
pinMode(RED_LED, OUTPUT); led(RED_LED, true);
pinMode(GREEN_LED, OUTPUT); led(GREEN_LED, true);
pinMode(YELLOW_LED, OUTPUT); led(YELLOW_LED, true);
delay(100);
int serialwait = 100; //Max time to wait for serial port initialization
while(!Serial && serialwait)
{
delay(1);
--serialwait;
}
led(RED_LED, false);
led(GREEN_LED, false);
led(YELLOW_LED, false);
Serial.println("Running MPR121 test");
if (!touchSensor.begin(0x5A)) {
//Bail out if MPR121 cannot be initialized (chip broken or not present).
Serial.println("MPR121 initialization failed!");
failure();
return;
} else {
Serial.println("MPR121 init ok.");
}
//Attempt some kind of random seeding
srandom(analogRead(0) ^ millis());
delay(200);
run_all();
}
void led(uint8_t pin, bool state) {
//Onboard red LED is wired a bit differently
if(pin != RED_LED) {
digitalWrite(pin, state?LOW:HIGH);
} else {
digitalWrite(pin, state?HIGH:LOW);
}
}
void set_touch_pins(uint16_t value)
{
for(int pin=0; pin<12; ++pin)
{
uint16_t touched = (value >> pin) & 0x0001; //Mask out bit value of pin
int teensyPin = 11-pin; //Row of teensy pins is 0-11 but mirrored vs MPR121
if(touched) {
pinMode(teensyPin, INPUT_PULLDOWN);
} else {
pinMode(teensyPin, INPUT_PULLUP);
}
}
}
bool run_test(const char* name, uint16_t value) {
Serial.print(name);
set_touch_pins(value);
led(YELLOW_LED, true);
delay(50);
led(YELLOW_LED, false);
delay(50);
uint16_t t = touchSensor.touched();
Serial.print(", expected: ");
Serial.print(value, HEX);
Serial.print(" got: ");
Serial.print(t, HEX);
if(value == t) {
Serial.println(" PASS");
return true;
} else {
Serial.println(" FAIL");
return false;
}
}
void failure() {
led(RED_LED, true);
led(GREEN_LED, false);
Serial.println("Test failed!");
}
void success() {
led(RED_LED, false);
led(GREEN_LED, true);
Serial.println("All tests succeeded!");
}
//Macro to bail out on any failure
#define TEST(x) if(!x) { failure(); return; }
void run_all(void) {
TEST(run_test("ALL_OFF", 0x000));
TEST(run_test("ALL_ON", 0xFFF));
//Pins one at a time
TEST(run_test("P0", 0x001 << 0));
TEST(run_test("P1", 0x001 << 1));
TEST(run_test("P2", 0x001 << 2));
TEST(run_test("P3", 0x001 << 3));
TEST(run_test("P4", 0x001 << 4));
TEST(run_test("P5", 0x001 << 5));
TEST(run_test("P6", 0x001 << 6));
TEST(run_test("P7", 0x001 << 7));
TEST(run_test("P8", 0x001 << 8));
TEST(run_test("P9", 0x001 << 9));
TEST(run_test("P10", 0x001 << 10));
TEST(run_test("P11", 0x001 << 11));
//Alternating pattern
TEST(run_test("ODD", 0x0555));
TEST(run_test("EVEN", 0x0AAA));
//A few random ones for good measure
for(int i=0; i<8; i++) {
TEST(run_test("RANDOM", random() % 0x1000));
}
success();
}
//Dummy loop does nothing
void loop() {
delay(100);
#include <Wire.h>
#include <Adafruit_MPR121.h>
/*
Tool to test an MPR121 unit. Wires each touch input to a teensy pin to simulate touch sensor signals.
Used to detect faulty MPR121 units before assembly.
*/
Adafruit_MPR121 touchSensor = Adafruit_MPR121(); // This is the 12-input touch sensor
//LED output pins. Red is the onboard one, other two are just wired via resistors to +3.3V
#define RED_LED 13
#define GREEN_LED 20
#define YELLOW_LED 21
void setup(void) {
Serial.begin(115200);
//Set all "test ports" to high-impedance for now
for(int i=0; i<=11; i++) {
pinMode(i, INPUT);
}
pinMode(RED_LED, OUTPUT); led(RED_LED, true);
pinMode(GREEN_LED, OUTPUT); led(GREEN_LED, true);
pinMode(YELLOW_LED, OUTPUT); led(YELLOW_LED, true);
delay(100);
int serialwait = 100; //Max time to wait for serial port initialization
while(!Serial && serialwait)
{
delay(1);
--serialwait;
}
led(RED_LED, false);
led(GREEN_LED, false);
led(YELLOW_LED, false);
Serial.println("Running MPR121 test");
if (!touchSensor.begin(0x5A)) {
//Bail out if MPR121 cannot be initialized (chip broken or not present).
Serial.println("MPR121 initialization failed!");
failure();
return;
} else {
Serial.println("MPR121 init ok.");
}
//Attempt some kind of random seeding
srandom(analogRead(0) ^ millis());
delay(200);
run_all();
}
void led(uint8_t pin, bool state) {
//Onboard red LED is wired a bit differently
if(pin != RED_LED) {
digitalWrite(pin, state?LOW:HIGH);
} else {
digitalWrite(pin, state?HIGH:LOW);
}
}
void set_touch_pins(uint16_t value)
{
for(int pin=0; pin<12; ++pin)
{
uint16_t touched = (value >> pin) & 0x0001; //Mask out bit value of pin
int teensyPin = 11-pin; //Row of teensy pins is 0-11 but mirrored vs MPR121
if(touched) {
pinMode(teensyPin, INPUT_PULLDOWN);
} else {
pinMode(teensyPin, INPUT_PULLUP);
}
}
}
bool run_test(const char* name, uint16_t value) {
Serial.print(name);
set_touch_pins(value);
led(YELLOW_LED, true);
delay(50);
led(YELLOW_LED, false);
delay(50);
uint16_t t = touchSensor.touched();
Serial.print(", expected: ");
Serial.print(value, HEX);
Serial.print(" got: ");
Serial.print(t, HEX);
if(value == t) {
Serial.println(" PASS");
return true;
} else {
Serial.println(" FAIL");
return false;
}
}
void failure() {
led(RED_LED, true);
led(GREEN_LED, false);
Serial.println("Test failed!");
}
void success() {
led(RED_LED, false);
led(GREEN_LED, true);
Serial.println("All tests succeeded!");
}
//Macro to bail out on any failure
#define TEST(x) if(!x) { failure(); return; }
void run_all(void) {
TEST(run_test("ALL_OFF", 0x000));
TEST(run_test("ALL_ON", 0xFFF));
//Pins one at a time
TEST(run_test("P0", 0x001 << 0));
TEST(run_test("P1", 0x001 << 1));
TEST(run_test("P2", 0x001 << 2));
TEST(run_test("P3", 0x001 << 3));
TEST(run_test("P4", 0x001 << 4));
TEST(run_test("P5", 0x001 << 5));
TEST(run_test("P6", 0x001 << 6));
TEST(run_test("P7", 0x001 << 7));
TEST(run_test("P8", 0x001 << 8));
TEST(run_test("P9", 0x001 << 9));
TEST(run_test("P10", 0x001 << 10));
TEST(run_test("P11", 0x001 << 11));
//Alternating pattern
TEST(run_test("ODD", 0x0555));
TEST(run_test("EVEN", 0x0AAA));
//A few random ones for good measure
for(int i=0; i<8; i++) {
TEST(run_test("RANDOM", random() % 0x1000));
}
success();
}
//Dummy loop does nothing
void loop() {
delay(100);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,35 +1,35 @@
w=8; //key width, default 8
wc=10; //mounting end outer diameter, default 10
l=15; //key length (range normally increase in steps of 5)
hght=3; //key height, default 3,3.5 and 4
angl=25; //key end angle, default 25
estp=1.5; //end step before angle, default 0.5
noext=0; //set to 1 to get just the round part
// 20/3,26/3.5,32/4 sk: 15/3 (estp 1.5)
module prism(l, w, h){
polyhedron(
points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
);
}
difference(){
translate([0,0,0]) cylinder(h=hght,r=wc/2,,$fn=100);
translate([0,0,hght-2]) cylinder(h=2.2,r1=1.6,r2=3.2,$fn=100);
translate([0,0,-1]) cylinder(h=hght+5,r=1.6,$fn=100);
//translate([-8,-8,0]) cube([16,2,3]);
}
if (!noext){
difference(){
translate([-w/2,-l+1,0]) cube([w,l,hght]);
translate([0,0,hght-2]) cylinder(h=2.2,r1=1.6,r2=3.2,$fn=100);
translate([0,0,-1]) cylinder(h=hght+5,r=1.6,$fn=100);
translate([w/2-1,-l,hght]) rotate([180,0,90]) prism(l,1,1);
translate([-w/2-0.001,-l,hght-1]) rotate([90,0,90]) prism(l,1,1);
//translate([-w/2,-l+5.5,hght]) rotate([160,0,0])prism(w,15,15);
translate([-w/2,-l,estp]) rotate([angl,0,0])cube(40,40,40);
}
}
w=8; //key width, default 8
wc=10; //mounting end outer diameter, default 10
l=15; //key length (range normally increase in steps of 5)
hght=3; //key height, default 3,3.5 and 4
angl=25; //key end angle, default 25
estp=1.5; //end step before angle, default 0.5
noext=0; //set to 1 to get just the round part
// 20/3,26/3.5,32/4 sk: 15/3 (estp 1.5)
module prism(l, w, h){
polyhedron(
points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]],
faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
);
}
difference(){
translate([0,0,0]) cylinder(h=hght,r=wc/2,,$fn=100);
translate([0,0,hght-2]) cylinder(h=2.2,r1=1.6,r2=3.2,$fn=100);
translate([0,0,-1]) cylinder(h=hght+5,r=1.6,$fn=100);
//translate([-8,-8,0]) cube([16,2,3]);
}
if (!noext){
difference(){
translate([-w/2,-l+1,0]) cube([w,l,hght]);
translate([0,0,hght-2]) cylinder(h=2.2,r1=1.6,r2=3.2,$fn=100);
translate([0,0,-1]) cylinder(h=hght+5,r=1.6,$fn=100);
translate([w/2-1,-l,hght]) rotate([180,0,90]) prism(l,1,1);
translate([-w/2-0.001,-l,hght-1]) rotate([90,0,90]) prism(l,1,1);
//translate([-w/2,-l+5.5,hght]) rotate([160,0,0])prism(w,15,15);
translate([-w/2,-l,estp]) rotate([angl,0,0])cube(40,40,40);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,85 +1,85 @@
CC=clang
CXX=clang++
CFLAGS=-Wall -Wextra -Wpedantic -Wno-gnu -mmacosx-version-min=10.9 -F/Library/Frameworks
CFLAGS += -DARDUINO=10808 -DTEENSYDUINO=146 -D__MK20DX256__ -DUSB_MIDI
RELEASE ?= 0
ifeq ($(RELEASE), 1)
CFLAGS +=-O3
else
CFLAGS += -O0 -g
endif
CXXFLAGS= $(CFLAGS) -std=c++14
LIBS=-framework SDL2 -lc++ -lc -framework OpenGL
LDFLAGS=-macosx_version_min 10.9 -rpath @executable_path/../Frameworks
SYSINC = ./include
INCS = ../NuEVI ./include ./imgui ./gl3w
INCDIRS = $(addprefix -isystem ,$(SYSINC))
INCDIRS += $(addprefix -I,$(INCS))
TARGET=nuevisim
CXXFILES= ../NuEVI/menu.cpp \
../NuEVI/adjustmenu.cpp \
../NuEVI/midi.cpp \
../NuEVI/settings.cpp \
../NuEVI/led.cpp \
src/nuevisim.cpp \
src/simeeprom.cpp \
src/Print.cpp \
src/simserial.cpp \
src/simwire.cpp \
src/simusbmidi.cpp \
src/filters.cpp \
src/Adafruit_GFX_sim.cpp \
src/Adafruit_SSD1306_sim.cpp \
src/Adafruit_MPR121_sim.cpp \
imgui/imgui.cpp \
imgui/imgui_draw.cpp \
imgui/imgui_widgets.cpp \
imgui/examples/imgui_impl_sdl.cpp \
imgui/examples/imgui_impl_opengl3.cpp
CFILES= gl3w/gl3w.c
OBJS=$(CXXFILES:.cpp=.o) $(CFILES:.c=.o)
all: $(TARGET)
nuevisim: $(OBJS)
$(LD) $(LDFLAGS) -o $(TARGET) $(LIBS) $^
%.o: %.c
$(CC) $(CFLAGS) $(INCDIRS) -c -o $@ $<
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(INCDIRS) -c -o $@ $<
clean:
rm -f $(TARGET) $(OBJS) *.o
.PHONY: all
# Dependecies
DEPS=make.deps
.PHONY: deps mrproper
mrproper: clean
rm $(DEPS)
deps: $(DEPS)
H_DEPS=$(wildcard src/*.h) $(wildcard ../NuEVI/*.h)
make.deps: $(CXXFILES) $(H_DEPS)
$(CXX) $(CXXFLAGS) -Wno-deprecated $(INCDIRS) -MM $(DEPS_HS) $^ > $@
-include .deps/*
CC=clang
CXX=clang++
CFLAGS=-Wall -Wextra -Wpedantic -Wno-gnu -mmacosx-version-min=10.9 -F/Library/Frameworks
CFLAGS += -DARDUINO=10808 -DTEENSYDUINO=146 -D__MK20DX256__ -DUSB_MIDI
RELEASE ?= 0
ifeq ($(RELEASE), 1)
CFLAGS +=-O3
else
CFLAGS += -O0 -g
endif
CXXFLAGS= $(CFLAGS) -std=c++14
LIBS=-framework SDL2 -lc++ -lc -framework OpenGL
LDFLAGS=-macosx_version_min 10.9 -rpath @executable_path/../Frameworks
SYSINC = ./include
INCS = ../NuEVI ./include ./imgui ./gl3w
INCDIRS = $(addprefix -isystem ,$(SYSINC))
INCDIRS += $(addprefix -I,$(INCS))
TARGET=nuevisim
CXXFILES= ../NuEVI/menu.cpp \
../NuEVI/adjustmenu.cpp \
../NuEVI/midi.cpp \
../NuEVI/settings.cpp \
../NuEVI/led.cpp \
src/nuevisim.cpp \
src/simeeprom.cpp \
src/Print.cpp \
src/simserial.cpp \
src/simwire.cpp \
src/simusbmidi.cpp \
src/filters.cpp \
src/Adafruit_GFX_sim.cpp \
src/Adafruit_SSD1306_sim.cpp \
src/Adafruit_MPR121_sim.cpp \
imgui/imgui.cpp \
imgui/imgui_draw.cpp \
imgui/imgui_widgets.cpp \
imgui/examples/imgui_impl_sdl.cpp \
imgui/examples/imgui_impl_opengl3.cpp
CFILES= gl3w/gl3w.c
OBJS=$(CXXFILES:.cpp=.o) $(CFILES:.c=.o)
all: $(TARGET)
nuevisim: $(OBJS)
$(LD) $(LDFLAGS) -o $(TARGET) $(LIBS) $^
%.o: %.c
$(CC) $(CFLAGS) $(INCDIRS) -c -o $@ $<
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(INCDIRS) -c -o $@ $<
clean:
rm -f $(TARGET) $(OBJS) *.o
.PHONY: all
# Dependecies
DEPS=make.deps
.PHONY: deps mrproper
mrproper: clean
rm $(DEPS)
deps: $(DEPS)
H_DEPS=$(wildcard src/*.h) $(wildcard ../NuEVI/*.h)
make.deps: $(CXXFILES) $(H_DEPS)
$(CXX) $(CXXFLAGS) -Wno-deprecated $(INCDIRS) -MM $(DEPS_HS) $^ > $@
-include .deps/*

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,233 +1,233 @@
#ifndef _ADAFRUIT_GFX_H
#define _ADAFRUIT_GFX_H
#if ARDUINO >= 100
#include "Arduino.h"
#include "Print.h"
#else
#include "WProgram.h"
#endif
#include "gfxfont.h"
/// A generic graphics superclass that can handle all sorts of drawing. At a minimum you can subclass and provide drawPixel(). At a maximum you can do a ton of overriding to optimize. Used for any/all Adafruit displays!
class Adafruit_GFX : public Print {
public: // Exposed simulation variables...
bool dimmed_;
bool enabled_;
bool inverted_;
bool dirty_;
public:
Adafruit_GFX(int16_t w, int16_t h); // Constructor
// This MUST be defined by the subclass:
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0; ///< Virtual drawPixel() function to draw to the screen/framebuffer/etc, must be overridden in subclass. @param x X coordinate. @param y Y coordinate. @param color 16-bit pixel color.
// TRANSACTION API / CORE DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void startWrite(void);
virtual void writePixel(int16_t x, int16_t y, uint16_t color);
virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
virtual void endWrite(void);
// CONTROL API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void setRotation(uint8_t r);
virtual void invertDisplay(boolean i);
// BASIC DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void
// It's good to implement those, even if using transaction API
drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color),
drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color),
fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color),
fillScreen(uint16_t color),
// Optional and probably not necessary to change
drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color),
drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
// These exist only with Adafruit_GFX (no subclass overrides)
void
drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
uint16_t color),
fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
int16_t delta, uint16_t color),
drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color),
fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color),
drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color),
fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color),
drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color),
drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color, uint16_t bg),
drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color),
drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color, uint16_t bg),
drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color),
drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h),
drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h),
drawGrayscaleBitmap(int16_t x, int16_t y,
const uint8_t bitmap[], const uint8_t mask[],
int16_t w, int16_t h),
drawGrayscaleBitmap(int16_t x, int16_t y,
uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap,
int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y,
const uint16_t bitmap[], const uint8_t mask[],
int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y,
uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h),
drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
uint16_t bg, uint8_t size),
setCursor(int16_t x, int16_t y),
setTextColor(uint16_t c),
setTextColor(uint16_t c, uint16_t bg),
setTextSize(uint8_t s),
setTextWrap(boolean w),
cp437(boolean x=true),
setFont(const GFXfont *f = NULL),
getTextBounds(const char *string, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
getTextBounds(const String &str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
#if ARDUINO >= 100
virtual size_t write(uint8_t);
#else
virtual void write(uint8_t);
#endif
int16_t height(void) const;
int16_t width(void) const;
uint8_t getRotation(void) const;
// get current cursor position (get rotation safe maximum values, using: width() for x, height() for y)
int16_t getCursorX(void) const;
int16_t getCursorY(void) const;
protected:
void
charBounds(char c, int16_t *x, int16_t *y,
int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy);
const int16_t
WIDTH, ///< This is the 'raw' display width - never changes
HEIGHT; ///< This is the 'raw' display height - never changes
int16_t
_width, ///< Display width as modified by current rotation
_height, ///< Display height as modified by current rotation
cursor_x, ///< x location to start print()ing text
cursor_y; ///< y location to start print()ing text
uint16_t
textcolor, ///< 16-bit background color for print()
textbgcolor; ///< 16-bit text color for print()
uint8_t
textsize, ///< Desired magnification of text to print()
rotation; ///< Display rotation (0 thru 3)
boolean
wrap, ///< If set, 'wrap' text at right edge of display
_cp437; ///< If set, use correct CP437 charset (default is off)
GFXfont
*gfxFont; ///< Pointer to special font
};
/// A simple drawn button UI element
class Adafruit_GFX_Button {
public:
Adafruit_GFX_Button(void);
// "Classic" initButton() uses center & size
void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y,
uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize);
// New/alt initButton() uses upper-left corner & size
void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1,
uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize);
void drawButton(boolean inverted = false);
boolean contains(int16_t x, int16_t y);
void press(boolean p);
boolean isPressed();
boolean justPressed();
boolean justReleased();
private:
Adafruit_GFX *_gfx;
int16_t _x1, _y1; // Coordinates of top-left corner
uint16_t _w, _h;
uint8_t _textsize;
uint16_t _outlinecolor, _fillcolor, _textcolor;
char _label[10];
boolean currstate, laststate;
};
/// A GFX 1-bit canvas context for graphics
class GFXcanvas1 : public Adafruit_GFX {
public:
GFXcanvas1(uint16_t w, uint16_t h);
~GFXcanvas1(void);
void drawPixel(int16_t x, int16_t y, uint16_t color),
fillScreen(uint16_t color);
uint8_t *getBuffer(void);
private:
uint8_t *buffer;
};
/// A GFX 8-bit canvas context for graphics
class GFXcanvas8 : public Adafruit_GFX {
public:
GFXcanvas8(uint16_t w, uint16_t h);
~GFXcanvas8(void);
void drawPixel(int16_t x, int16_t y, uint16_t color),
fillScreen(uint16_t color),
writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t *getBuffer(void);
private:
uint8_t *buffer;
};
/// A GFX 16-bit canvas context for graphics
class GFXcanvas16 : public Adafruit_GFX {
public:
GFXcanvas16(uint16_t w, uint16_t h);
~GFXcanvas16(void);
void drawPixel(int16_t x, int16_t y, uint16_t color),
fillScreen(uint16_t color);
uint16_t *getBuffer(void);
private:
uint16_t *buffer;
};
#endif // _ADAFRUIT_GFX_H
#ifndef _ADAFRUIT_GFX_H
#define _ADAFRUIT_GFX_H
#if ARDUINO >= 100
#include "Arduino.h"
#include "Print.h"
#else
#include "WProgram.h"
#endif
#include "gfxfont.h"
/// A generic graphics superclass that can handle all sorts of drawing. At a minimum you can subclass and provide drawPixel(). At a maximum you can do a ton of overriding to optimize. Used for any/all Adafruit displays!
class Adafruit_GFX : public Print {
public: // Exposed simulation variables...
bool dimmed_;
bool enabled_;
bool inverted_;
bool dirty_;
public:
Adafruit_GFX(int16_t w, int16_t h); // Constructor
// This MUST be defined by the subclass:
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0; ///< Virtual drawPixel() function to draw to the screen/framebuffer/etc, must be overridden in subclass. @param x X coordinate. @param y Y coordinate. @param color 16-bit pixel color.
// TRANSACTION API / CORE DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void startWrite(void);
virtual void writePixel(int16_t x, int16_t y, uint16_t color);
virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
virtual void endWrite(void);
// CONTROL API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void setRotation(uint8_t r);
virtual void invertDisplay(boolean i);
// BASIC DRAW API
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void
// It's good to implement those, even if using transaction API
drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color),
drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color),
fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color),
fillScreen(uint16_t color),
// Optional and probably not necessary to change
drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color),
drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
// These exist only with Adafruit_GFX (no subclass overrides)
void
drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
uint16_t color),
fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
int16_t delta, uint16_t color),
drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color),
fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color),
drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color),
fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color),
drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color),
drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color, uint16_t bg),
drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color),
drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color, uint16_t bg),
drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h, uint16_t color),
drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
int16_t w, int16_t h),
drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap,
int16_t w, int16_t h),
drawGrayscaleBitmap(int16_t x, int16_t y,
const uint8_t bitmap[], const uint8_t mask[],
int16_t w, int16_t h),
drawGrayscaleBitmap(int16_t x, int16_t y,
uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap,
int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y,
const uint16_t bitmap[], const uint8_t mask[],
int16_t w, int16_t h),
drawRGBBitmap(int16_t x, int16_t y,
uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h),
drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
uint16_t bg, uint8_t size),
setCursor(int16_t x, int16_t y),
setTextColor(uint16_t c),
setTextColor(uint16_t c, uint16_t bg),
setTextSize(uint8_t s),
setTextWrap(boolean w),
cp437(boolean x=true),
setFont(const GFXfont *f = NULL),
getTextBounds(const char *string, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
getTextBounds(const String &str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
#if ARDUINO >= 100
virtual size_t write(uint8_t);
#else
virtual void write(uint8_t);
#endif
int16_t height(void) const;
int16_t width(void) const;
uint8_t getRotation(void) const;
// get current cursor position (get rotation safe maximum values, using: width() for x, height() for y)
int16_t getCursorX(void) const;
int16_t getCursorY(void) const;
protected:
void
charBounds(char c, int16_t *x, int16_t *y,
int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy);
const int16_t
WIDTH, ///< This is the 'raw' display width - never changes
HEIGHT; ///< This is the 'raw' display height - never changes
int16_t
_width, ///< Display width as modified by current rotation
_height, ///< Display height as modified by current rotation
cursor_x, ///< x location to start print()ing text
cursor_y; ///< y location to start print()ing text
uint16_t
textcolor, ///< 16-bit background color for print()
textbgcolor; ///< 16-bit text color for print()
uint8_t
textsize, ///< Desired magnification of text to print()
rotation; ///< Display rotation (0 thru 3)
boolean
wrap, ///< If set, 'wrap' text at right edge of display
_cp437; ///< If set, use correct CP437 charset (default is off)
GFXfont
*gfxFont; ///< Pointer to special font
};
/// A simple drawn button UI element
class Adafruit_GFX_Button {
public:
Adafruit_GFX_Button(void);
// "Classic" initButton() uses center & size
void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y,
uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize);
// New/alt initButton() uses upper-left corner & size
void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1,
uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
uint16_t textcolor, char *label, uint8_t textsize);
void drawButton(boolean inverted = false);
boolean contains(int16_t x, int16_t y);
void press(boolean p);
boolean isPressed();
boolean justPressed();
boolean justReleased();
private:
Adafruit_GFX *_gfx;
int16_t _x1, _y1; // Coordinates of top-left corner
uint16_t _w, _h;
uint8_t _textsize;
uint16_t _outlinecolor, _fillcolor, _textcolor;
char _label[10];
boolean currstate, laststate;
};
/// A GFX 1-bit canvas context for graphics
class GFXcanvas1 : public Adafruit_GFX {
public:
GFXcanvas1(uint16_t w, uint16_t h);
~GFXcanvas1(void);
void drawPixel(int16_t x, int16_t y, uint16_t color),
fillScreen(uint16_t color);
uint8_t *getBuffer(void);
private:
uint8_t *buffer;
};
/// A GFX 8-bit canvas context for graphics
class GFXcanvas8 : public Adafruit_GFX {
public:
GFXcanvas8(uint16_t w, uint16_t h);
~GFXcanvas8(void);
void drawPixel(int16_t x, int16_t y, uint16_t color),
fillScreen(uint16_t color),
writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
uint8_t *getBuffer(void);
private:
uint8_t *buffer;
};
/// A GFX 16-bit canvas context for graphics
class GFXcanvas16 : public Adafruit_GFX {
public:
GFXcanvas16(uint16_t w, uint16_t h);
~GFXcanvas16(void);
void drawPixel(int16_t x, int16_t y, uint16_t color),
fillScreen(uint16_t color);
uint16_t *getBuffer(void);
private:
uint16_t *buffer;
};
#endif // _ADAFRUIT_GFX_H

View file

@ -1,114 +1,114 @@
/*!
* @file Adafruit_MPR121.h
*
This is a library for the MPR121 12-Channel Capacitive Sensor
Designed specifically to work with the MPR121 breakout from Adafruit
----> https://www.adafruit.com/products/1982
These sensors use I2C to communicate, 2+ pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
*
*/
#ifndef ADAFRUIT_MPR121_H
#define ADAFRUIT_MPR121_H
// #if (ARDUINO >= 100)
#include "Arduino.h"
// #else
// #include "WProgram.h"
// #endif
#include <Wire.h>
// The default I2C address
#define MPR121_I2CADDR_DEFAULT 0x5A ///< default I2C address
/**
*****************************************************************************************
* @brief Device register map
****************************************************************************************/
enum {
MPR121_TOUCHSTATUS_L = 0x00,
MPR121_TOUCHSTATUS_H = 0x01,
MPR121_FILTDATA_0L = 0x04,
MPR121_FILTDATA_0H = 0x05,
MPR121_BASELINE_0 = 0x1E,
MPR121_MHDR = 0x2B,
MPR121_NHDR = 0x2C,
MPR121_NCLR = 0x2D,
MPR121_FDLR = 0x2E,
MPR121_MHDF = 0x2F,
MPR121_NHDF = 0x30,
MPR121_NCLF = 0x31,
MPR121_FDLF = 0x32,
MPR121_NHDT = 0x33,
MPR121_NCLT = 0x34,
MPR121_FDLT = 0x35,
MPR121_TOUCHTH_0 = 0x41,
MPR121_RELEASETH_0 = 0x42,
MPR121_DEBOUNCE = 0x5B,
MPR121_CONFIG1 = 0x5C,
MPR121_CONFIG2 = 0x5D,
MPR121_CHARGECURR_0 = 0x5F,
MPR121_CHARGETIME_1 = 0x6C,
MPR121_ECR = 0x5E,
MPR121_AUTOCONFIG0 = 0x7B,
MPR121_AUTOCONFIG1 = 0x7C,
MPR121_UPLIMIT = 0x7D,
MPR121_LOWLIMIT = 0x7E,
MPR121_TARGETLIMIT = 0x7F,
MPR121_GPIODIR = 0x76,
MPR121_GPIOEN = 0x77,
MPR121_GPIOSET = 0x78,
MPR121_GPIOCLR = 0x79,
MPR121_GPIOTOGGLE = 0x7A,
MPR121_SOFTRESET = 0x80,
};
//.. thru to 0x1C/0x1D
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with MPR121
proximity capacitive touch sensor controller.
*/
/**************************************************************************/
class Adafruit_MPR121 {
public:
// Hardware I2C
Adafruit_MPR121(void);
boolean begin(uint8_t i2caddr = MPR121_I2CADDR_DEFAULT);
uint16_t filteredData(uint8_t t);
uint16_t baselineData(uint8_t t);
uint8_t readRegister8(uint8_t reg);
uint16_t readRegister16(uint8_t reg);
void writeRegister(uint8_t reg, uint8_t value);
uint16_t touched(void);
// Add deprecated attribute so that the compiler shows a warning
void setThreshholds(uint8_t touch, uint8_t release) __attribute__((deprecated));
void setThresholds(uint8_t touch, uint8_t release);
uint8_t _registers[48];
// Simulator specific stuff
void mockFilteredData(int register, uint16_t value);
private:
int8_t _i2caddr;
};
#endif // ADAFRUIT_MPR121_H
/*!
* @file Adafruit_MPR121.h
*
This is a library for the MPR121 12-Channel Capacitive Sensor
Designed specifically to work with the MPR121 breakout from Adafruit
----> https://www.adafruit.com/products/1982
These sensors use I2C to communicate, 2+ pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
*
*/
#ifndef ADAFRUIT_MPR121_H
#define ADAFRUIT_MPR121_H
// #if (ARDUINO >= 100)
#include "Arduino.h"
// #else
// #include "WProgram.h"
// #endif
#include <Wire.h>
// The default I2C address
#define MPR121_I2CADDR_DEFAULT 0x5A ///< default I2C address
/**
*****************************************************************************************
* @brief Device register map
****************************************************************************************/
enum {
MPR121_TOUCHSTATUS_L = 0x00,
MPR121_TOUCHSTATUS_H = 0x01,
MPR121_FILTDATA_0L = 0x04,
MPR121_FILTDATA_0H = 0x05,
MPR121_BASELINE_0 = 0x1E,
MPR121_MHDR = 0x2B,
MPR121_NHDR = 0x2C,
MPR121_NCLR = 0x2D,
MPR121_FDLR = 0x2E,
MPR121_MHDF = 0x2F,
MPR121_NHDF = 0x30,
MPR121_NCLF = 0x31,
MPR121_FDLF = 0x32,
MPR121_NHDT = 0x33,
MPR121_NCLT = 0x34,
MPR121_FDLT = 0x35,
MPR121_TOUCHTH_0 = 0x41,
MPR121_RELEASETH_0 = 0x42,
MPR121_DEBOUNCE = 0x5B,
MPR121_CONFIG1 = 0x5C,
MPR121_CONFIG2 = 0x5D,
MPR121_CHARGECURR_0 = 0x5F,
MPR121_CHARGETIME_1 = 0x6C,
MPR121_ECR = 0x5E,
MPR121_AUTOCONFIG0 = 0x7B,
MPR121_AUTOCONFIG1 = 0x7C,
MPR121_UPLIMIT = 0x7D,
MPR121_LOWLIMIT = 0x7E,
MPR121_TARGETLIMIT = 0x7F,
MPR121_GPIODIR = 0x76,
MPR121_GPIOEN = 0x77,
MPR121_GPIOSET = 0x78,
MPR121_GPIOCLR = 0x79,
MPR121_GPIOTOGGLE = 0x7A,
MPR121_SOFTRESET = 0x80,
};
//.. thru to 0x1C/0x1D
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with MPR121
proximity capacitive touch sensor controller.
*/
/**************************************************************************/
class Adafruit_MPR121 {
public:
// Hardware I2C
Adafruit_MPR121(void);
boolean begin(uint8_t i2caddr = MPR121_I2CADDR_DEFAULT);
uint16_t filteredData(uint8_t t);
uint16_t baselineData(uint8_t t);
uint8_t readRegister8(uint8_t reg);
uint16_t readRegister16(uint8_t reg);
void writeRegister(uint8_t reg, uint8_t value);
uint16_t touched(void);
// Add deprecated attribute so that the compiler shows a warning
void setThreshholds(uint8_t touch, uint8_t release) __attribute__((deprecated));
void setThresholds(uint8_t touch, uint8_t release);
uint8_t _registers[48];
// Simulator specific stuff
void mockFilteredData(int register, uint16_t value);
private:
int8_t _i2caddr;
};
#endif // ADAFRUIT_MPR121_H

View file

@ -1,169 +1,169 @@
/*!
* @file Adafruit_SSD1306.h
*
* This is part of for Adafruit's SSD1306 library for monochrome
* OLED displays: http://www.adafruit.com/category/63_98
*
* These displays use I2C or SPI to communicate. I2C requires 2 pins
* (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK,
* select, data/command) and optionally a reset pin. Hardware SPI or
* 'bitbang' software SPI are both supported.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Limor Fried/Ladyada for Adafruit Industries, with
* contributions from the open source community.
*
* BSD license, all text above, and the splash screen header file,
* must be included in any redistribution.
*
*/
#ifndef _Adafruit_SSD1306_H_
#define _Adafruit_SSD1306_H_
#include <Wire.h>
#include <Adafruit_GFX.h>
typedef void* SPIClass;
// #if defined(__AVR__)
// typedef volatile uint8_t PortReg;
// typedef uint8_t PortMask;
// #define HAVE_PORTREG
// #elif defined(__SAM3X8E__)
// typedef volatile RwReg PortReg;
// typedef uint32_t PortMask;
// #define HAVE_PORTREG
// #elif defined(__arm__) || defined(ARDUINO_FEATHER52)
// typedef volatile uint32_t PortReg;
// typedef uint32_t PortMask;
// #define HAVE_PORTREG
// #endif
#define BLACK 0 ///< Draw 'off' pixels
#define WHITE 1 ///< Draw 'on' pixels
#define INVERSE 2 ///< Invert pixels
#define SSD1306_MEMORYMODE 0x20 ///< See datasheet
#define SSD1306_COLUMNADDR 0x21 ///< See datasheet
#define SSD1306_PAGEADDR 0x22 ///< See datasheet
#define SSD1306_SETCONTRAST 0x81 ///< See datasheet
#define SSD1306_CHARGEPUMP 0x8D ///< See datasheet
#define SSD1306_SEGREMAP 0xA0 ///< See datasheet
#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< See datasheet
#define SSD1306_DISPLAYALLON 0xA5 ///< Not currently used
#define SSD1306_NORMALDISPLAY 0xA6 ///< See datasheet
#define SSD1306_INVERTDISPLAY 0xA7 ///< See datasheet
#define SSD1306_SETMULTIPLEX 0xA8 ///< See datasheet
#define SSD1306_DISPLAYOFF 0xAE ///< See datasheet
#define SSD1306_DISPLAYON 0xAF ///< See datasheet
#define SSD1306_COMSCANINC 0xC0 ///< Not currently used
#define SSD1306_COMSCANDEC 0xC8 ///< See datasheet
#define SSD1306_SETDISPLAYOFFSET 0xD3 ///< See datasheet
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 ///< See datasheet
#define SSD1306_SETPRECHARGE 0xD9 ///< See datasheet
#define SSD1306_SETCOMPINS 0xDA ///< See datasheet
#define SSD1306_SETVCOMDETECT 0xDB ///< See datasheet
#define SSD1306_SETLOWCOLUMN 0x00 ///< Not currently used
#define SSD1306_SETHIGHCOLUMN 0x10 ///< Not currently used
#define SSD1306_SETSTARTLINE 0x40 ///< See datasheet
#define SSD1306_EXTERNALVCC 0x01 ///< External display voltage source
#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 ///< Init rt scroll
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 ///< Init left scroll
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 ///< Init diag scroll
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A ///< Init diag scroll
#define SSD1306_DEACTIVATE_SCROLL 0x2E ///< Stop scroll
#define SSD1306_ACTIVATE_SCROLL 0x2F ///< Start scroll
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 ///< Set scroll range
// Deprecated size stuff for backwards compatibility with old sketches
#if defined SSD1306_128_64
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_64 defined
#define SSD1306_LCDHEIGHT 64 ///< DEPRECATED: height w/SSD1306_128_64 defined
#endif
#if defined SSD1306_128_32
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_32 defined
#define SSD1306_LCDHEIGHT 32 ///< DEPRECATED: height w/SSD1306_128_32 defined
#endif
#if defined SSD1306_96_16
#define SSD1306_LCDWIDTH 96 ///< DEPRECATED: width w/SSD1306_96_16 defined
#define SSD1306_LCDHEIGHT 16 ///< DEPRECATED: height w/SSD1306_96_16 defined
#endif
/*!
@brief Class that stores state and functions for interacting with
SSD1306 OLED displays.
*/
class Adafruit_SSD1306 : public Adafruit_GFX {
public:
// NEW CONSTRUCTORS -- recommended for new projects
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi=&Wire, int8_t rst_pin=-1,
uint32_t clkDuring=400000UL, uint32_t clkAfter=100000UL);
Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin, int8_t sclk_pin,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate=8000000UL);
// DEPRECATED CONSTRUCTORS - for back compatibility, avoid in new projects
Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin, int8_t dc_pin,
int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(int8_t rst_pin = -1);
~Adafruit_SSD1306(void);
boolean begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC,
uint8_t i2caddr=0, boolean reset=true,
boolean periphBegin=true);
void display(void);
void clearDisplay(void);
void invertDisplay(boolean i);
void dim(boolean dim);
void drawPixel(int16_t x, int16_t y, uint16_t color);
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void startscrollright(uint8_t start, uint8_t stop);
void startscrollleft(uint8_t start, uint8_t stop);
void startscrolldiagright(uint8_t start, uint8_t stop);
void startscrolldiagleft(uint8_t start, uint8_t stop);
void stopscroll(void);
void ssd1306_command(uint8_t c);
boolean getPixel(int16_t x, int16_t y);
uint8_t *getBuffer(void);
private:
inline void SPIwrite(uint8_t d) __attribute__((always_inline));
void drawFastHLineInternal(int16_t x, int16_t y, int16_t w,
uint16_t color);
void drawFastVLineInternal(int16_t x, int16_t y, int16_t h,
uint16_t color);
void ssd1306_command1(uint8_t c);
void ssd1306_commandList(const uint8_t *c, uint8_t n);
SPIClass *spi;
TwoWire *wire;
uint8_t *buffer;
int8_t i2caddr, vccstate, page_end;
int8_t mosiPin , clkPin , dcPin , csPin, rstPin;
#ifdef HAVE_PORTREG
PortReg *mosiPort , *clkPort , *dcPort , *csPort;
PortMask mosiPinMask, clkPinMask, dcPinMask, csPinMask;
#endif
#if defined(SPI_HAS_TRANSACTION)
SPISettings spiSettings;
#endif
#if ARDUINO >= 157
uint32_t wireClk; // Wire speed for SSD1306 transfers
uint32_t restoreClk; // Wire speed following SSD1306 transfers
#endif
};
#endif // _Adafruit_SSD1306_H_
/*!
* @file Adafruit_SSD1306.h
*
* This is part of for Adafruit's SSD1306 library for monochrome
* OLED displays: http://www.adafruit.com/category/63_98
*
* These displays use I2C or SPI to communicate. I2C requires 2 pins
* (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK,
* select, data/command) and optionally a reset pin. Hardware SPI or
* 'bitbang' software SPI are both supported.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Limor Fried/Ladyada for Adafruit Industries, with
* contributions from the open source community.
*
* BSD license, all text above, and the splash screen header file,
* must be included in any redistribution.
*
*/
#ifndef _Adafruit_SSD1306_H_
#define _Adafruit_SSD1306_H_
#include <Wire.h>
#include <Adafruit_GFX.h>
typedef void* SPIClass;
// #if defined(__AVR__)
// typedef volatile uint8_t PortReg;
// typedef uint8_t PortMask;
// #define HAVE_PORTREG
// #elif defined(__SAM3X8E__)
// typedef volatile RwReg PortReg;
// typedef uint32_t PortMask;
// #define HAVE_PORTREG
// #elif defined(__arm__) || defined(ARDUINO_FEATHER52)
// typedef volatile uint32_t PortReg;
// typedef uint32_t PortMask;
// #define HAVE_PORTREG
// #endif
#define BLACK 0 ///< Draw 'off' pixels
#define WHITE 1 ///< Draw 'on' pixels
#define INVERSE 2 ///< Invert pixels
#define SSD1306_MEMORYMODE 0x20 ///< See datasheet
#define SSD1306_COLUMNADDR 0x21 ///< See datasheet
#define SSD1306_PAGEADDR 0x22 ///< See datasheet
#define SSD1306_SETCONTRAST 0x81 ///< See datasheet
#define SSD1306_CHARGEPUMP 0x8D ///< See datasheet
#define SSD1306_SEGREMAP 0xA0 ///< See datasheet
#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< See datasheet
#define SSD1306_DISPLAYALLON 0xA5 ///< Not currently used
#define SSD1306_NORMALDISPLAY 0xA6 ///< See datasheet
#define SSD1306_INVERTDISPLAY 0xA7 ///< See datasheet
#define SSD1306_SETMULTIPLEX 0xA8 ///< See datasheet
#define SSD1306_DISPLAYOFF 0xAE ///< See datasheet
#define SSD1306_DISPLAYON 0xAF ///< See datasheet
#define SSD1306_COMSCANINC 0xC0 ///< Not currently used
#define SSD1306_COMSCANDEC 0xC8 ///< See datasheet
#define SSD1306_SETDISPLAYOFFSET 0xD3 ///< See datasheet
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 ///< See datasheet
#define SSD1306_SETPRECHARGE 0xD9 ///< See datasheet
#define SSD1306_SETCOMPINS 0xDA ///< See datasheet
#define SSD1306_SETVCOMDETECT 0xDB ///< See datasheet
#define SSD1306_SETLOWCOLUMN 0x00 ///< Not currently used
#define SSD1306_SETHIGHCOLUMN 0x10 ///< Not currently used
#define SSD1306_SETSTARTLINE 0x40 ///< See datasheet
#define SSD1306_EXTERNALVCC 0x01 ///< External display voltage source
#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 ///< Init rt scroll
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 ///< Init left scroll
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 ///< Init diag scroll
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A ///< Init diag scroll
#define SSD1306_DEACTIVATE_SCROLL 0x2E ///< Stop scroll
#define SSD1306_ACTIVATE_SCROLL 0x2F ///< Start scroll
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 ///< Set scroll range
// Deprecated size stuff for backwards compatibility with old sketches
#if defined SSD1306_128_64
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_64 defined
#define SSD1306_LCDHEIGHT 64 ///< DEPRECATED: height w/SSD1306_128_64 defined
#endif
#if defined SSD1306_128_32
#define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_32 defined
#define SSD1306_LCDHEIGHT 32 ///< DEPRECATED: height w/SSD1306_128_32 defined
#endif
#if defined SSD1306_96_16
#define SSD1306_LCDWIDTH 96 ///< DEPRECATED: width w/SSD1306_96_16 defined
#define SSD1306_LCDHEIGHT 16 ///< DEPRECATED: height w/SSD1306_96_16 defined
#endif
/*!
@brief Class that stores state and functions for interacting with
SSD1306 OLED displays.
*/
class Adafruit_SSD1306 : public Adafruit_GFX {
public:
// NEW CONSTRUCTORS -- recommended for new projects
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi=&Wire, int8_t rst_pin=-1,
uint32_t clkDuring=400000UL, uint32_t clkAfter=100000UL);
Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin, int8_t sclk_pin,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi,
int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate=8000000UL);
// DEPRECATED CONSTRUCTORS - for back compatibility, avoid in new projects
Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin, int8_t dc_pin,
int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, int8_t cs_pin);
Adafruit_SSD1306(int8_t rst_pin = -1);
~Adafruit_SSD1306(void);
boolean begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC,
uint8_t i2caddr=0, boolean reset=true,
boolean periphBegin=true);
void display(void);
void clearDisplay(void);
void invertDisplay(boolean i);
void dim(boolean dim);
void drawPixel(int16_t x, int16_t y, uint16_t color);
virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void startscrollright(uint8_t start, uint8_t stop);
void startscrollleft(uint8_t start, uint8_t stop);
void startscrolldiagright(uint8_t start, uint8_t stop);
void startscrolldiagleft(uint8_t start, uint8_t stop);
void stopscroll(void);
void ssd1306_command(uint8_t c);
boolean getPixel(int16_t x, int16_t y);
uint8_t *getBuffer(void);
private:
inline void SPIwrite(uint8_t d) __attribute__((always_inline));
void drawFastHLineInternal(int16_t x, int16_t y, int16_t w,
uint16_t color);
void drawFastVLineInternal(int16_t x, int16_t y, int16_t h,
uint16_t color);
void ssd1306_command1(uint8_t c);
void ssd1306_commandList(const uint8_t *c, uint8_t n);
SPIClass *spi;
TwoWire *wire;
uint8_t *buffer;
int8_t i2caddr, vccstate, page_end;
int8_t mosiPin , clkPin , dcPin , csPin, rstPin;
#ifdef HAVE_PORTREG
PortReg *mosiPort , *clkPort , *dcPort , *csPort;
PortMask mosiPinMask, clkPinMask, dcPinMask, csPinMask;
#endif
#if defined(SPI_HAS_TRANSACTION)
SPISettings spiSettings;
#endif
#if ARDUINO >= 157
uint32_t wireClk; // Wire speed for SSD1306 transfers
uint32_t restoreClk; // Wire speed following SSD1306 transfers
#endif
};
#endif // _Adafruit_SSD1306_H_

View file

@ -1,105 +1,105 @@
#ifndef __ARDUINO_H__
#define __ARDUINO_H__
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include "Wiring.h"
#include "simusbmidi.h"
#include "core_pins.h"
#define PROGMEM
#ifndef _BV
#define _BV(x) (1u<<(x))
#endif
// SPI CTRL REG BITS
#define SPIE 7
#define SPE 6
#define DORD 5
#define MSTR 4
#define CPOL 3
#define CPHA 2
#define SPR1 1
#define SPR0 0
#define A11 (0xa1)
/*
SPCR
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| SPIE | SPE | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |
SPIE - Enables the SPI interrupt when 1
SPE - Enables the SPI when 1
DORD - Sends data least Significant Bit First when 1, most Significant Bit first when 0
MSTR - Sets the Arduino in master mode when 1, slave mode when 0
CPOL - Sets the data clock to be idle when high if set to 1, idle when low if set to 0
CPHA - Samples data on the falling edge of the data clock when 1, rising edge when 0
SPR1 and SPR0 - Sets the SPI speed, 00 is fastest (4MHz) 11 is slowest (250KHz)
*/
#define LSBFIRST 0
#define MSBFIRST 1
typedef bool boolean;
typedef uint8_t byte;
// class Print
// {
// public:
// };
class SimSerial
{
public:
void begin(uint32_t speed);
void println(uint32_t value);
void println();
void println(const char* str);
void print(const char* str);
void print(uint32_t intValue);
void write(const uint8_t str);
void flush();
};
extern SimSerial Serial;
extern SimSerial Serial3; //Used for MIDI serial putput with default hardware
extern SimUsbMidi usbMIDI;
//extern void putString(int row, int col, int color, const char* msg, const FONT_INFO fontInfo);
uint32_t micros();
uint32_t millis();
void delay(uint32_t millis);
void delayMicroseconds(uint32_t micros);
void pinMode(uint8_t, uint8_t);
#ifdef __cplusplus
extern "C" {
#endif
//void digitalWrite(uint8_t, uint8_t);
//int digitalRead(uint8_t);
#ifdef __cplusplus
}
#endif
int analogRead(uint8_t);
void analogReference(uint8_t mode);
void analogWrite(uint8_t, int);
#endif
#ifndef __ARDUINO_H__
#define __ARDUINO_H__
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include "Wiring.h"
#include "simusbmidi.h"
#include "core_pins.h"
#define PROGMEM
#ifndef _BV
#define _BV(x) (1u<<(x))
#endif
// SPI CTRL REG BITS
#define SPIE 7
#define SPE 6
#define DORD 5
#define MSTR 4
#define CPOL 3
#define CPHA 2
#define SPR1 1
#define SPR0 0
#define A11 (0xa1)
/*
SPCR
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| SPIE | SPE | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |
SPIE - Enables the SPI interrupt when 1
SPE - Enables the SPI when 1
DORD - Sends data least Significant Bit First when 1, most Significant Bit first when 0
MSTR - Sets the Arduino in master mode when 1, slave mode when 0
CPOL - Sets the data clock to be idle when high if set to 1, idle when low if set to 0
CPHA - Samples data on the falling edge of the data clock when 1, rising edge when 0
SPR1 and SPR0 - Sets the SPI speed, 00 is fastest (4MHz) 11 is slowest (250KHz)
*/
#define LSBFIRST 0
#define MSBFIRST 1
typedef bool boolean;
typedef uint8_t byte;
// class Print
// {
// public:
// };
class SimSerial
{
public:
void begin(uint32_t speed);
void println(uint32_t value);
void println();
void println(const char* str);
void print(const char* str);
void print(uint32_t intValue);
void write(const uint8_t str);
void flush();
};
extern SimSerial Serial;
extern SimSerial Serial3; //Used for MIDI serial putput with default hardware
extern SimUsbMidi usbMIDI;
//extern void putString(int row, int col, int color, const char* msg, const FONT_INFO fontInfo);
uint32_t micros();
uint32_t millis();
void delay(uint32_t millis);
void delayMicroseconds(uint32_t micros);
void pinMode(uint8_t, uint8_t);
#ifdef __cplusplus
extern "C" {
#endif
//void digitalWrite(uint8_t, uint8_t);
//int digitalRead(uint8_t);
#ifdef __cplusplus
}
#endif
int analogRead(uint8_t);
void analogReference(uint8_t mode);
void analogWrite(uint8_t, int);
#endif

View file

@ -1,55 +1,55 @@
#ifndef __EEPROM_H
#define __EEPROM_H
#include <stdint.h>
#include <stdio.h>
struct EEPROMClass
{
EEPROMClass();
//Basic user access methods.
// EERef operator[]( const int idx ) { return idx; }
uint8_t read( int idx ); // { return EERef( idx ); }
void write( int idx, uint8_t val ); // { (EERef( idx )) = val; }
void update( int idx, uint8_t val ); // { EERef( idx ).update( val ); }
//STL and C++11 iteration capability.
// EEPtr begin() { return 0x00; }
// EEPtr end() { return length(); } //Standards requires this to be the item after the last valid entry. The returned pointer is invalid.
uint16_t length(); // { return E2END + 1; }
//Functionality to 'get' and 'put' objects to and from EEPROM.
// template< typename T > T &get( int idx, T &t ){
// EEPtr e = idx;
// uint8_t *ptr = (uint8_t*) &t;
// for( int count = sizeof(T) ; count ; --count, ++e ) *ptr++ = *e;
// return t;
// }
// template< typename T > const T &put( int idx, const T &t ){
// const uint8_t *ptr = (const uint8_t*) &t;
// #ifdef __arm__
// eeprom_write_block(ptr, (void *)idx, sizeof(T));
// #else
// EEPtr e = idx;
// for( int count = sizeof(T) ; count ; --count, ++e ) (*e).update( *ptr++ );
// #endif
// return t;
// }
//Make EEPROM persistent by storing to a file
int16_t setStorage(const char* filename, bool write);
void closeStorage();
private:
uint8_t someFakeEEPROM_memory[2048]; //Teensy 3.2 size
FILE *storage;
bool autoUpdate;
};
extern EEPROMClass EEPROM __unused;
#endif
#ifndef __EEPROM_H
#define __EEPROM_H
#include <stdint.h>
#include <stdio.h>
struct EEPROMClass
{
EEPROMClass();
//Basic user access methods.
// EERef operator[]( const int idx ) { return idx; }
uint8_t read( int idx ); // { return EERef( idx ); }
void write( int idx, uint8_t val ); // { (EERef( idx )) = val; }
void update( int idx, uint8_t val ); // { EERef( idx ).update( val ); }
//STL and C++11 iteration capability.
// EEPtr begin() { return 0x00; }
// EEPtr end() { return length(); } //Standards requires this to be the item after the last valid entry. The returned pointer is invalid.
uint16_t length(); // { return E2END + 1; }
//Functionality to 'get' and 'put' objects to and from EEPROM.
// template< typename T > T &get( int idx, T &t ){
// EEPtr e = idx;
// uint8_t *ptr = (uint8_t*) &t;
// for( int count = sizeof(T) ; count ; --count, ++e ) *ptr++ = *e;
// return t;
// }
// template< typename T > const T &put( int idx, const T &t ){
// const uint8_t *ptr = (const uint8_t*) &t;
// #ifdef __arm__
// eeprom_write_block(ptr, (void *)idx, sizeof(T));
// #else
// EEPtr e = idx;
// for( int count = sizeof(T) ; count ; --count, ++e ) (*e).update( *ptr++ );
// #endif
// return t;
// }
//Make EEPROM persistent by storing to a file
int16_t setStorage(const char* filename, bool write);
void closeStorage();
private:
uint8_t someFakeEEPROM_memory[2048]; //Teensy 3.2 size
FILE *storage;
bool autoUpdate;
};
extern EEPROMClass EEPROM __unused;
#endif

View file

@ -1,136 +1,136 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef Print_h
#define Print_h
#include <inttypes.h>
#include <stdio.h> // for size_t - gives sprintf and other stuff to all sketches & libs
#include <stdarg.h>
#include "core_id.h"
#include "WString.h"
#include "Printable.h"
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
#ifndef isnan
#define isnan(__x) (__builtin_isnan (__x))
#endif
#ifndef isinf
#define isinf(__x) (__builtin_isinf (__x))
#endif
class __FlashStringHelper;
class Print
{
public:
constexpr Print() : write_error(0) {}
virtual size_t write(uint8_t b) = 0;
size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); }
virtual size_t write(const uint8_t *buffer, size_t size);
virtual int availableForWrite(void) { return 0; }
virtual void flush() { }
size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
// size_t print(const String &s);
size_t print(char c) { return write((uint8_t)c); }
size_t print(const char s[]) { return write(s); }
size_t print(const __FlashStringHelper *f) { return write((const char *)f); }
size_t print(uint8_t b) { return printNumber(b, 10, 0); }
size_t print(int n) { return print((long)n); }
size_t print(unsigned int n) { return printNumber(n, 10, 0); }
size_t print(long n);
size_t print(unsigned long n) { return printNumber(n, 10, 0); }
size_t print(unsigned char n, int base) { return printNumber(n, base, 0); }
size_t print(int n, int base) { return (base == 10) ? print(n) : printNumber(n, base, 0); }
size_t print(unsigned int n, int base) { return printNumber(n, base, 0); }
size_t print(long n, int base) { return (base == 10) ? print(n) : printNumber(n, base, 0); }
size_t print(unsigned long n, int base) { return printNumber(n, base, 0); }
size_t print(double n, int digits = 2) { return printFloat(n, digits); }
size_t print(const Printable &obj) { return obj.printTo(*this); }
size_t println(void);
// size_t println(const String &s) { return print(s) + println(); }
size_t println(char c) { return print(c) + println(); }
size_t println(const char s[]) { return print(s) + println(); }
size_t println(const __FlashStringHelper *f) { return print(f) + println(); }
size_t println(uint8_t b) { return print(b) + println(); }
size_t println(int n) { return print(n) + println(); }
size_t println(unsigned int n) { return print(n) + println(); }
size_t println(long n) { return print(n) + println(); }
size_t println(unsigned long n) { return print(n) + println(); }
size_t println(unsigned char n, int base) { return print(n, base) + println(); }
size_t println(int n, int base) { return print(n, base) + println(); }
size_t println(unsigned int n, int base) { return print(n, base) + println(); }
size_t println(long n, int base) { return print(n, base) + println(); }
size_t println(unsigned long n, int base) { return print(n, base) + println(); }
size_t println(double n, int digits = 2) { return print(n, digits) + println(); }
size_t println(const Printable &obj) { return obj.printTo(*this) + println(); }
int getWriteError() { return write_error; }
void clearWriteError() { setWriteError(0); }
int printf(const char *format, ...);
int printf(const __FlashStringHelper *format, ...);
protected:
void setWriteError(int err = 1) { write_error = err; }
private:
char write_error;
size_t printFloat(double n, uint8_t digits);
#ifdef __MKL26Z64__
size_t printNumberDec(unsigned long n, uint8_t sign);
size_t printNumberHex(unsigned long n);
size_t printNumberBin(unsigned long n);
size_t printNumberAny(unsigned long n, uint8_t base);
inline size_t printNumber(unsigned long n, uint8_t base, uint8_t sign) __attribute__((always_inline)) {
// when "base" is a constant (pretty much always), the
// compiler optimizes this to a single function call.
if (base == 0) return write((uint8_t)n);
if (base == 10 || base < 2) return printNumberDec(n, sign);
if (base == 16) return printNumberHex(n);
if (base == 2) return printNumberBin(n);
return printNumberAny(n, base);
}
#else
size_t printNumber(unsigned long n, uint8_t base, uint8_t sign);
#endif
};
#endif
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef Print_h
#define Print_h
#include <inttypes.h>
#include <stdio.h> // for size_t - gives sprintf and other stuff to all sketches & libs
#include <stdarg.h>
#include "core_id.h"
#include "WString.h"
#include "Printable.h"
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
#ifndef isnan
#define isnan(__x) (__builtin_isnan (__x))
#endif
#ifndef isinf
#define isinf(__x) (__builtin_isinf (__x))
#endif
class __FlashStringHelper;
class Print
{
public:
constexpr Print() : write_error(0) {}
virtual size_t write(uint8_t b) = 0;
size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); }
virtual size_t write(const uint8_t *buffer, size_t size);
virtual int availableForWrite(void) { return 0; }
virtual void flush() { }
size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
// size_t print(const String &s);
size_t print(char c) { return write((uint8_t)c); }
size_t print(const char s[]) { return write(s); }
size_t print(const __FlashStringHelper *f) { return write((const char *)f); }
size_t print(uint8_t b) { return printNumber(b, 10, 0); }
size_t print(int n) { return print((long)n); }
size_t print(unsigned int n) { return printNumber(n, 10, 0); }
size_t print(long n);
size_t print(unsigned long n) { return printNumber(n, 10, 0); }
size_t print(unsigned char n, int base) { return printNumber(n, base, 0); }
size_t print(int n, int base) { return (base == 10) ? print(n) : printNumber(n, base, 0); }
size_t print(unsigned int n, int base) { return printNumber(n, base, 0); }
size_t print(long n, int base) { return (base == 10) ? print(n) : printNumber(n, base, 0); }
size_t print(unsigned long n, int base) { return printNumber(n, base, 0); }
size_t print(double n, int digits = 2) { return printFloat(n, digits); }
size_t print(const Printable &obj) { return obj.printTo(*this); }
size_t println(void);
// size_t println(const String &s) { return print(s) + println(); }
size_t println(char c) { return print(c) + println(); }
size_t println(const char s[]) { return print(s) + println(); }
size_t println(const __FlashStringHelper *f) { return print(f) + println(); }
size_t println(uint8_t b) { return print(b) + println(); }
size_t println(int n) { return print(n) + println(); }
size_t println(unsigned int n) { return print(n) + println(); }
size_t println(long n) { return print(n) + println(); }
size_t println(unsigned long n) { return print(n) + println(); }
size_t println(unsigned char n, int base) { return print(n, base) + println(); }
size_t println(int n, int base) { return print(n, base) + println(); }
size_t println(unsigned int n, int base) { return print(n, base) + println(); }
size_t println(long n, int base) { return print(n, base) + println(); }
size_t println(unsigned long n, int base) { return print(n, base) + println(); }
size_t println(double n, int digits = 2) { return print(n, digits) + println(); }
size_t println(const Printable &obj) { return obj.printTo(*this) + println(); }
int getWriteError() { return write_error; }
void clearWriteError() { setWriteError(0); }
int printf(const char *format, ...);
int printf(const __FlashStringHelper *format, ...);
protected:
void setWriteError(int err = 1) { write_error = err; }
private:
char write_error;
size_t printFloat(double n, uint8_t digits);
#ifdef __MKL26Z64__
size_t printNumberDec(unsigned long n, uint8_t sign);
size_t printNumberHex(unsigned long n);
size_t printNumberBin(unsigned long n);
size_t printNumberAny(unsigned long n, uint8_t base);
inline size_t printNumber(unsigned long n, uint8_t base, uint8_t sign) __attribute__((always_inline)) {
// when "base" is a constant (pretty much always), the
// compiler optimizes this to a single function call.
if (base == 0) return write((uint8_t)n);
if (base == 10 || base < 2) return printNumberDec(n, sign);
if (base == 16) return printNumberHex(n);
if (base == 2) return printNumberBin(n);
return printNumberAny(n, base);
}
#else
size_t printNumber(unsigned long n, uint8_t base, uint8_t sign);
#endif
};
#endif

View file

@ -1,42 +1,42 @@
/*
Printable.h - Interface class that allows printing of complex types
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef Printable_h
#define Printable_h
#ifdef __cplusplus
// #include "new.h"
class Print;
/** The Printable class provides a way for new classes to allow themselves to be printed.
By deriving from Printable and implementing the printTo method, it will then be possible
for users to print out instances of this class by passing them into the usual
Print::print and Print::println methods.
*/
class Printable
{
public:
virtual size_t printTo(Print& p) const = 0;
};
#endif
#endif
/*
Printable.h - Interface class that allows printing of complex types
Copyright (c) 2011 Adrian McEwen. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef Printable_h
#define Printable_h
#ifdef __cplusplus
// #include "new.h"
class Print;
/** The Printable class provides a way for new classes to allow themselves to be printed.
By deriving from Printable and implementing the printTo method, it will then be possible
for users to print out instances of this class by passing them into the usual
Print::print and Print::println methods.
*/
class Printable
{
public:
virtual size_t printTo(Print& p) const = 0;
};
#endif
#endif

View file

@ -1,6 +1,6 @@
#if defined(__APPLE__)
#include <SDL2/SDL.h>
#else
#error "Platform not supported, fix include paths and stuff.."
#endif
#if defined(__APPLE__)
#include <SDL2/SDL.h>
#else
#error "Platform not supported, fix include paths and stuff.."
#endif

View file

@ -1 +1 @@
#include <SDL2/SDL_syswm.h>
#include <SDL2/SDL_syswm.h>

View file

@ -1,165 +1,165 @@
#ifndef __SPI_H__
#define __SPI_H__
#include <cstdint>
#define SPIClass SimSPI
#ifndef SPI_MODE0
#define SPI_MODE0 0
#endif
#ifndef SPI_CLOCK_DIV2
#define SPI_CLOCK_DIV2 0
#endif
#define SPDR *SPI.SPI_dataReg
#define SPCR *SPI.SPI_ctrlReg
#define SPSR *SPI.SPI_statusReg
//#define SPSR 0xff
#define SPIF (SPI.SPI_IF)
// Forward declare
class SimSPI;
class DataRegister;
class SPICtrlRegister;
class SPIStatusRegister;
extern SimSPI SPI;
class ISPIDevice
{
public:
virtual uint8_t spiSlaveWrite( uint8_t ) = 0;
};
class SimSPI
{
public:
SimSPI();
void AddDevice( ISPIDevice* device );
void begin();
void transfer( uint8_t something );
void setDataMode( uint8_t mode );
void setClockDivider( uint32_t divider );
void writeReg( DataRegister *reg, uint8_t data);
// Members
DataRegister* SPI_dataReg;
SPICtrlRegister* SPI_ctrlReg;
SPIStatusRegister* SPI_statusReg;
uint8_t SPI_IF;
private:
ISPIDevice** devices;
uint32_t devicesArraySize;
ISPIDevice* currentSlave;
};
class HWRegister
{
public:
HWRegister( SimSPI& owner) : spi_( owner ) {}
protected:
SimSPI& spi_;
};
class DataRegister : HWRegister
{
public:
DataRegister( SimSPI& owner) : HWRegister( owner ) {}
void operator=(uint8_t data)
{
spi_.writeReg( this, data );
}
operator uint8_t()
{
// TODO: Get last read byte
return 0xffu;
}
};
class SPIStatusRegister : HWRegister
{
public:
SPIStatusRegister( SimSPI& owner) : HWRegister( owner ) {}
void operator =(uint8_t value)
{
reg_ = value;
}
void operator |=(uint8_t value)
{
reg_ |= value;
}
void operator &=(uint8_t value)
{
reg_ &= value;
}
/*
operator uint8_t()
{
return reg_|0xff;
}
*/
operator uint32_t()
{
return reg_|0xff;
}
uint8_t reg_;
};
class SPICtrlRegister : HWRegister
{
public:
SPICtrlRegister( SimSPI& owner) : HWRegister( owner ) {}
void operator =(uint8_t value)
{
reg_ = value;
}
void operator |=(uint8_t value)
{
reg_ |= value;
}
void operator &=(uint8_t value)
{
reg_ &= value;
}
operator uint8_t()
{
return reg_;
}
uint8_t reg_;
};
#endif
#ifndef __SPI_H__
#define __SPI_H__
#include <cstdint>
#define SPIClass SimSPI
#ifndef SPI_MODE0
#define SPI_MODE0 0
#endif
#ifndef SPI_CLOCK_DIV2
#define SPI_CLOCK_DIV2 0
#endif
#define SPDR *SPI.SPI_dataReg
#define SPCR *SPI.SPI_ctrlReg
#define SPSR *SPI.SPI_statusReg
//#define SPSR 0xff
#define SPIF (SPI.SPI_IF)
// Forward declare
class SimSPI;
class DataRegister;
class SPICtrlRegister;
class SPIStatusRegister;
extern SimSPI SPI;
class ISPIDevice
{
public:
virtual uint8_t spiSlaveWrite( uint8_t ) = 0;
};
class SimSPI
{
public:
SimSPI();
void AddDevice( ISPIDevice* device );
void begin();
void transfer( uint8_t something );
void setDataMode( uint8_t mode );
void setClockDivider( uint32_t divider );
void writeReg( DataRegister *reg, uint8_t data);
// Members
DataRegister* SPI_dataReg;
SPICtrlRegister* SPI_ctrlReg;
SPIStatusRegister* SPI_statusReg;
uint8_t SPI_IF;
private:
ISPIDevice** devices;
uint32_t devicesArraySize;
ISPIDevice* currentSlave;
};
class HWRegister
{
public:
HWRegister( SimSPI& owner) : spi_( owner ) {}
protected:
SimSPI& spi_;
};
class DataRegister : HWRegister
{
public:
DataRegister( SimSPI& owner) : HWRegister( owner ) {}
void operator=(uint8_t data)
{
spi_.writeReg( this, data );
}
operator uint8_t()
{
// TODO: Get last read byte
return 0xffu;
}
};
class SPIStatusRegister : HWRegister
{
public:
SPIStatusRegister( SimSPI& owner) : HWRegister( owner ) {}
void operator =(uint8_t value)
{
reg_ = value;
}
void operator |=(uint8_t value)
{
reg_ |= value;
}
void operator &=(uint8_t value)
{
reg_ &= value;
}
/*
operator uint8_t()
{
return reg_|0xff;
}
*/
operator uint32_t()
{
return reg_|0xff;
}
uint8_t reg_;
};
class SPICtrlRegister : HWRegister
{
public:
SPICtrlRegister( SimSPI& owner) : HWRegister( owner ) {}
void operator =(uint8_t value)
{
reg_ = value;
}
void operator |=(uint8_t value)
{
reg_ |= value;
}
void operator &=(uint8_t value)
{
reg_ &= value;
}
operator uint8_t()
{
return reg_;
}
uint8_t reg_;
};
#endif

View file

@ -1,227 +1,227 @@
/*
WString.h - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef String_class_h
#define String_class_h
#ifdef __cplusplus
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//#include "avr_functions.h"
// Not needed here, but some libs assume WString.h or Print.h
// gives them PROGMEM and other AVR stuff.
//#include "avr/pgmspace.h"
// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
// -felide-constructors
// -std=c++0x
// Brian Cook's "no overhead" Flash String type (message on Dec 14, 2010)
// modified by Mikal Hart for his FlashString library
class __FlashStringHelper;
#ifndef F
#define F(string_literal) ((const __FlashStringHelper *)(string_literal))
#endif
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;
// The string class
class String
{
public:
// constructors
String(const char *cstr = (const char *)NULL);
String(const __FlashStringHelper *pgmstr);
String(const String &str);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String(String &&rval);
String(StringSumHelper &&rval);
#endif
String(char c);
String(unsigned char c);
String(int, unsigned char base=10);
String(unsigned int, unsigned char base=10);
String(long, unsigned char base=10);
String(unsigned long, unsigned char base=10);
String(float num, unsigned char digits=2);
String(double num, unsigned char digits=2) : String((float)num, digits) {}
~String(void);
// memory management
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const {return len;}
// copy and move
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *s) { return copy((const char *)s, strlen((const char *)s)); }
void move(String &rhs);
String & operator = (const String &rhs);
String & operator = (const char *cstr);
String & operator = (const __FlashStringHelper *pgmstr);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & operator = (String &&rval);
String & operator = (StringSumHelper &&rval);
#endif
String & operator = (char c);
// append
String & append(const String &str);
String & append(const char *cstr);
String & append(const __FlashStringHelper *s) {return append((const char *)s, strlen((const char *)s)); }
String & append(char c);
String & append(unsigned char c) {return append((int)c);}
String & append(int num);
String & append(unsigned int num);
String & append(long num);
String & append(unsigned long num);
String & append(float num);
String & append(double num) {return append((float)num);}
String & operator += (const String &rhs) {return append(rhs);}
String & operator += (const char *cstr) {return append(cstr);}
String & operator += (const __FlashStringHelper *pgmstr) {return append(pgmstr);}
String & operator += (char c) {return append(c);}
String & operator += (unsigned char c) {return append((int)c);}
String & operator += (int num) {return append(num);}
String & operator += (unsigned int num) {return append(num);}
String & operator += (long num) {return append(num);}
String & operator += (unsigned long num) {return append(num);}
String & operator += (float num) {return append(num);}
String & operator += (double num) {return append(num);}
// concatenate
friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *pgmstr);
friend StringSumHelper & operator + (const StringSumHelper &lhs, char c);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char c);
friend StringSumHelper & operator + (const StringSumHelper &lhs, int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, float num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, double num);
String & concat(const String &str) {return append(str);}
String & concat(const char *cstr) {return append(cstr);}
String & concat(const __FlashStringHelper *pgmstr) {return append(pgmstr);}
String & concat(char c) {return append(c);}
String & concat(unsigned char c) {return append((int)c);}
String & concat(int num) {return append(num);}
String & concat(unsigned int num) {return append(num);}
String & concat(long num) {return append(num);}
String & concat(unsigned long num) {return append(num);}
String & concat(float num) {return append(num);}
String & concat(double num) {return append(num);}
// comparison
int compareTo(const String &s) const;
unsigned char equals(const String &s) const;
unsigned char equals(const char *cstr) const;
//unsigned char equals(const __FlashStringHelper *pgmstr) const;
unsigned char operator == (const String &rhs) const {return equals(rhs);}
unsigned char operator == (const char *cstr) const {return equals(cstr);}
unsigned char operator == (const __FlashStringHelper *s) const {return equals((const char *)s);}
unsigned char operator != (const String &rhs) const {return !equals(rhs);}
unsigned char operator != (const char *cstr) const {return !equals(cstr);}
unsigned char operator != (const __FlashStringHelper *s) const {return !equals(s);}
unsigned char operator < (const String &rhs) const;
unsigned char operator > (const String &rhs) const;
unsigned char operator <= (const String &rhs) const;
unsigned char operator >= (const String &rhs) const;
unsigned char equalsIgnoreCase(const String &s) const;
unsigned char startsWith( const String &prefix) const;
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
// character acccess
char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c);
char operator [] (unsigned int index) const;
char& operator [] (unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
{getBytes((unsigned char *)buf, bufsize, index);}
const char * c_str() const { return buffer; }
// search
int indexOf( char ch ) const;
int indexOf( char ch, unsigned int fromIndex ) const;
int indexOf( const String &str ) const;
int indexOf( const String &str, unsigned int fromIndex ) const;
int lastIndexOf( char ch ) const;
int lastIndexOf( char ch, unsigned int fromIndex ) const;
int lastIndexOf( const String &str ) const;
int lastIndexOf( const String &str, unsigned int fromIndex ) const;
String substring( unsigned int beginIndex ) const;
String substring( unsigned int beginIndex, unsigned int endIndex ) const;
// modification
String & replace(char find, char replace);
String & replace(const String& find, const String& replace);
String & remove(unsigned int index);
String & remove(unsigned int index, unsigned int count);
String & toLowerCase(void);
String & toUpperCase(void);
String & trim(void);
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
protected:
char *buffer; // the actual char array
unsigned int capacity; // the array length minus one (for the '\0')
unsigned int len; // the String length (not counting the '\0')
unsigned char flags; // unused, for future features
protected:
void init(void);
unsigned char changeBuffer(unsigned int maxStrLen);
String & append(const char *cstr, unsigned int length);
private:
// allow for "if (s)" without the complications of an operator bool().
// for more information http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const {}
public:
operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; }
};
class StringSumHelper : public String
{
public:
StringSumHelper(const String &s) : String(s) {}
StringSumHelper(const char *p) : String(p) {}
StringSumHelper(const __FlashStringHelper *pgmstr) : String(pgmstr) {}
StringSumHelper(char c) : String(c) {}
StringSumHelper(unsigned char c) : String(c) {}
StringSumHelper(int num) : String(num, 10) {}
StringSumHelper(unsigned int num) : String(num, 10) {}
StringSumHelper(long num) : String(num, 10) {}
StringSumHelper(unsigned long num) : String(num, 10) {}
};
#endif // __cplusplus
#endif // String_class_h
/*
WString.h - String library for Wiring & Arduino
...mostly rewritten by Paul Stoffregen...
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
Copyright 2011, Paul Stoffregen, paul@pjrc.com
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef String_class_h
#define String_class_h
#ifdef __cplusplus
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//#include "avr_functions.h"
// Not needed here, but some libs assume WString.h or Print.h
// gives them PROGMEM and other AVR stuff.
//#include "avr/pgmspace.h"
// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
// -felide-constructors
// -std=c++0x
// Brian Cook's "no overhead" Flash String type (message on Dec 14, 2010)
// modified by Mikal Hart for his FlashString library
class __FlashStringHelper;
#ifndef F
#define F(string_literal) ((const __FlashStringHelper *)(string_literal))
#endif
// An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations.
class StringSumHelper;
// The string class
class String
{
public:
// constructors
String(const char *cstr = (const char *)NULL);
String(const __FlashStringHelper *pgmstr);
String(const String &str);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String(String &&rval);
String(StringSumHelper &&rval);
#endif
String(char c);
String(unsigned char c);
String(int, unsigned char base=10);
String(unsigned int, unsigned char base=10);
String(long, unsigned char base=10);
String(unsigned long, unsigned char base=10);
String(float num, unsigned char digits=2);
String(double num, unsigned char digits=2) : String((float)num, digits) {}
~String(void);
// memory management
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const {return len;}
// copy and move
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *s) { return copy((const char *)s, strlen((const char *)s)); }
void move(String &rhs);
String & operator = (const String &rhs);
String & operator = (const char *cstr);
String & operator = (const __FlashStringHelper *pgmstr);
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
String & operator = (String &&rval);
String & operator = (StringSumHelper &&rval);
#endif
String & operator = (char c);
// append
String & append(const String &str);
String & append(const char *cstr);
String & append(const __FlashStringHelper *s) {return append((const char *)s, strlen((const char *)s)); }
String & append(char c);
String & append(unsigned char c) {return append((int)c);}
String & append(int num);
String & append(unsigned int num);
String & append(long num);
String & append(unsigned long num);
String & append(float num);
String & append(double num) {return append((float)num);}
String & operator += (const String &rhs) {return append(rhs);}
String & operator += (const char *cstr) {return append(cstr);}
String & operator += (const __FlashStringHelper *pgmstr) {return append(pgmstr);}
String & operator += (char c) {return append(c);}
String & operator += (unsigned char c) {return append((int)c);}
String & operator += (int num) {return append(num);}
String & operator += (unsigned int num) {return append(num);}
String & operator += (long num) {return append(num);}
String & operator += (unsigned long num) {return append(num);}
String & operator += (float num) {return append(num);}
String & operator += (double num) {return append(num);}
// concatenate
friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *pgmstr);
friend StringSumHelper & operator + (const StringSumHelper &lhs, char c);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char c);
friend StringSumHelper & operator + (const StringSumHelper &lhs, int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, float num);
friend StringSumHelper & operator + (const StringSumHelper &lhs, double num);
String & concat(const String &str) {return append(str);}
String & concat(const char *cstr) {return append(cstr);}
String & concat(const __FlashStringHelper *pgmstr) {return append(pgmstr);}
String & concat(char c) {return append(c);}
String & concat(unsigned char c) {return append((int)c);}
String & concat(int num) {return append(num);}
String & concat(unsigned int num) {return append(num);}
String & concat(long num) {return append(num);}
String & concat(unsigned long num) {return append(num);}
String & concat(float num) {return append(num);}
String & concat(double num) {return append(num);}
// comparison
int compareTo(const String &s) const;
unsigned char equals(const String &s) const;
unsigned char equals(const char *cstr) const;
//unsigned char equals(const __FlashStringHelper *pgmstr) const;
unsigned char operator == (const String &rhs) const {return equals(rhs);}
unsigned char operator == (const char *cstr) const {return equals(cstr);}
unsigned char operator == (const __FlashStringHelper *s) const {return equals((const char *)s);}
unsigned char operator != (const String &rhs) const {return !equals(rhs);}
unsigned char operator != (const char *cstr) const {return !equals(cstr);}
unsigned char operator != (const __FlashStringHelper *s) const {return !equals(s);}
unsigned char operator < (const String &rhs) const;
unsigned char operator > (const String &rhs) const;
unsigned char operator <= (const String &rhs) const;
unsigned char operator >= (const String &rhs) const;
unsigned char equalsIgnoreCase(const String &s) const;
unsigned char startsWith( const String &prefix) const;
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
// character acccess
char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c);
char operator [] (unsigned int index) const;
char& operator [] (unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
{getBytes((unsigned char *)buf, bufsize, index);}
const char * c_str() const { return buffer; }
// search
int indexOf( char ch ) const;
int indexOf( char ch, unsigned int fromIndex ) const;
int indexOf( const String &str ) const;
int indexOf( const String &str, unsigned int fromIndex ) const;
int lastIndexOf( char ch ) const;
int lastIndexOf( char ch, unsigned int fromIndex ) const;
int lastIndexOf( const String &str ) const;
int lastIndexOf( const String &str, unsigned int fromIndex ) const;
String substring( unsigned int beginIndex ) const;
String substring( unsigned int beginIndex, unsigned int endIndex ) const;
// modification
String & replace(char find, char replace);
String & replace(const String& find, const String& replace);
String & remove(unsigned int index);
String & remove(unsigned int index, unsigned int count);
String & toLowerCase(void);
String & toUpperCase(void);
String & trim(void);
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
protected:
char *buffer; // the actual char array
unsigned int capacity; // the array length minus one (for the '\0')
unsigned int len; // the String length (not counting the '\0')
unsigned char flags; // unused, for future features
protected:
void init(void);
unsigned char changeBuffer(unsigned int maxStrLen);
String & append(const char *cstr, unsigned int length);
private:
// allow for "if (s)" without the complications of an operator bool().
// for more information http://www.artima.com/cppsource/safebool.html
typedef void (String::*StringIfHelperType)() const;
void StringIfHelper() const {}
public:
operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; }
};
class StringSumHelper : public String
{
public:
StringSumHelper(const String &s) : String(s) {}
StringSumHelper(const char *p) : String(p) {}
StringSumHelper(const __FlashStringHelper *pgmstr) : String(pgmstr) {}
StringSumHelper(char c) : String(c) {}
StringSumHelper(unsigned char c) : String(c) {}
StringSumHelper(int num) : String(num, 10) {}
StringSumHelper(unsigned int num) : String(num, 10) {}
StringSumHelper(long num) : String(num, 10) {}
StringSumHelper(unsigned long num) : String(num, 10) {}
};
#endif // __cplusplus
#endif // String_class_h

View file

@ -1,38 +1,38 @@
#ifndef __WIRE_H__
#define __WIRE_H__
#include <cstdint>
#define TwoWire SimWire
class SimWire
{
public:
SimWire(bool verbose = false);
void begin();
void begin( uint8_t address ); // For slaves.
void end();
void setClock(uint32_t);
void beginTransmission(uint8_t address);
void beginTransmission(int);
uint8_t endTransmission();
uint8_t endTransmission(uint8_t);
uint8_t requestFrom(uint8_t address, uint8_t count);
uint8_t requestFrom(uint8_t, uint8_t, uint8_t);
uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t);
size_t write(uint8_t);
size_t write(const uint8_t *, size_t);
int available();
int read();
int peek();
void flush();
void onReceive( void (*)(int) );
void onRequest( void (*)(void) );
private:
bool verbose_;
};
extern SimWire Wire;
#endif
#ifndef __WIRE_H__
#define __WIRE_H__
#include <cstdint>
#define TwoWire SimWire
class SimWire
{
public:
SimWire(bool verbose = false);
void begin();
void begin( uint8_t address ); // For slaves.
void end();
void setClock(uint32_t);
void beginTransmission(uint8_t address);
void beginTransmission(int);
uint8_t endTransmission();
uint8_t endTransmission(uint8_t);
uint8_t requestFrom(uint8_t address, uint8_t count);
uint8_t requestFrom(uint8_t, uint8_t, uint8_t);
uint8_t requestFrom(uint8_t, uint8_t, uint32_t, uint8_t, uint8_t);
size_t write(uint8_t);
size_t write(const uint8_t *, size_t);
int available();
int read();
int peek();
void flush();
void onReceive( void (*)(int) );
void onRequest( void (*)(void) );
private:
bool verbose_;
};
extern SimWire Wire;
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
// This header file is in the public domain.
#ifndef CORE_TEENSY
#define CORE_TEENSY
#endif
// This header file is in the public domain.
#ifndef CORE_TEENSY
#define CORE_TEENSY
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,29 +1,29 @@
// Font structures for newer Adafruit_GFX (1.1 and later).
// Example fonts are included in 'Fonts' directory.
// To use a font in your Arduino sketch, #include the corresponding .h
// file and pass address of GFXfont struct to setFont(). Pass NULL to
// revert to 'classic' fixed-space bitmap font.
#ifndef _GFXFONT_H_
#define _GFXFONT_H_
/// Font data stored PER GLYPH
typedef struct {
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint8_t first; ///< ASCII extents (first char)
uint8_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;
#endif // _GFXFONT_H_
// Font structures for newer Adafruit_GFX (1.1 and later).
// Example fonts are included in 'Fonts' directory.
// To use a font in your Arduino sketch, #include the corresponding .h
// file and pass address of GFXfont struct to setFont(). Pass NULL to
// revert to 'classic' fixed-space bitmap font.
#ifndef _GFXFONT_H_
#define _GFXFONT_H_
/// Font data stored PER GLYPH
typedef struct {
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint8_t first; ///< ASCII extents (first char)
uint8_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;
#endif // _GFXFONT_H_

View file

@ -1,17 +1,17 @@
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
//Dummy functions, used by macros for interrupts() / noInterrupts()
void __enable_irq() {}
void __disable_irq() {}
struct IntervalTimer
{
public:
IntervalTimer() {};
bool begin(void (*funct)(), unsigned int microseconds) { };
};
#endif
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
//Dummy functions, used by macros for interrupts() / noInterrupts()
void __enable_irq() {}
void __disable_irq() {}
struct IntervalTimer
{
public:
IntervalTimer() {};
bool begin(void (*funct)(), unsigned int microseconds) { };
};
#endif

View file

@ -1,283 +1,283 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef pins_macros_for_arduino_compatibility_h
#define pins_macros_for_arduino_compatibility_h
#include <stdint.h>
// A0-A9 are always digital 14-23, for Arduino compatibility
#define PIN_A0 (14)
#define PIN_A1 (15)
#define PIN_A2 (16)
#define PIN_A3 (17)
#define PIN_A4 (18)
#define PIN_A5 (19)
#define PIN_A6 (20)
#define PIN_A7 (21)
#define PIN_A8 (22)
#define PIN_A9 (23)
const static uint8_t A0 = PIN_A0;
const static uint8_t A1 = PIN_A1;
const static uint8_t A2 = PIN_A2;
const static uint8_t A3 = PIN_A3;
const static uint8_t A4 = PIN_A4;
const static uint8_t A5 = PIN_A5;
const static uint8_t A6 = PIN_A6;
const static uint8_t A7 = PIN_A7;
const static uint8_t A8 = PIN_A8;
const static uint8_t A9 = PIN_A9;
#if defined(__MK20DX128__)
#define PIN_A10 (34)
#define PIN_A11 (35)
#define PIN_A12 (36)
#define PIN_A13 (37)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
const static uint8_t A13 = PIN_A13;
#elif defined(__MK20DX256__)
#define PIN_A10 (34)
#define PIN_A11 (35)
#define PIN_A12 (36)
#define PIN_A13 (37)
#define PIN_A14 (40)
#define PIN_A15 (26)
#define PIN_A16 (27)
#define PIN_A17 (28)
#define PIN_A18 (29)
#define PIN_A19 (30)
#define PIN_A20 (31)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
const static uint8_t A13 = PIN_A13;
const static uint8_t A14 = PIN_A14;
const static uint8_t A15 = PIN_A15;
const static uint8_t A16 = PIN_A16;
const static uint8_t A17 = PIN_A17;
const static uint8_t A18 = PIN_A18;
const static uint8_t A19 = PIN_A19;
const static uint8_t A20 = PIN_A20;
#elif defined(__MKL26Z64__)
#define PIN_A10 (24)
#define PIN_A11 (25)
#define PIN_A12 (26)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define PIN_A10 (64)
#define PIN_A11 (65)
#define PIN_A12 (31)
#define PIN_A13 (32)
#define PIN_A14 (33)
#define PIN_A15 (34)
#define PIN_A16 (35)
#define PIN_A17 (36)
#define PIN_A18 (37)
#define PIN_A19 (38)
#define PIN_A20 (39)
#define PIN_A21 (66)
#define PIN_A22 (67)
#define PIN_A23 (49)
#define PIN_A24 (50)
#define PIN_A25 (68)
#define PIN_A26 (69)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
const static uint8_t A13 = PIN_A13;
const static uint8_t A14 = PIN_A14;
const static uint8_t A15 = PIN_A15;
const static uint8_t A16 = PIN_A16;
const static uint8_t A17 = PIN_A17;
const static uint8_t A18 = PIN_A18;
const static uint8_t A19 = PIN_A19;
const static uint8_t A20 = PIN_A20;
const static uint8_t A21 = PIN_A21;
const static uint8_t A22 = PIN_A22;
const static uint8_t A23 = PIN_A23;
const static uint8_t A24 = PIN_A24;
const static uint8_t A25 = PIN_A25;
const static uint8_t A26 = PIN_A26;
#endif
#define LED_BUILTIN (13)
#define PIN_SPI_SS (10)
#define PIN_SPI_MOSI (11)
#define PIN_SPI_MISO (12)
#define PIN_SPI_SCK (13)
const static uint8_t SS = 10;
const static uint8_t MOSI = 11;
const static uint8_t MISO = 12;
const static uint8_t SCK = 13;
#define PIN_WIRE_SDA (18)
#define PIN_WIRE_SCL (19)
const static uint8_t SDA = 18;
const static uint8_t SCL = 19;
#define PIN_SERIAL_RX (0)
#define PIN_SERIAL_TX (1)
#define NUM_DIGITAL_PINS CORE_NUM_DIGITAL
#define NUM_ANALOG_INPUTS CORE_NUM_ANALOG
#define NOT_AN_INTERRUPT -1
#if defined(__MK20DX128__)
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) <= 13) ? (p) + 24 : -1))
#define digitalPinHasPWM(p) (((p) >= 3 && (p) <= 6) || (p) == 9 || (p) == 10 || ((p) >= 20 && (p) <= 23))
#define digitalPinToInterrupt(p) ((p) < NUM_DIGITAL_PINS ? (p) : -1)
#elif defined(__MK20DX256__)
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) <= 13) ? (p) + 24 : (((p) == 14) ? 40 : (((p) <= 20) ? (p) + 11 : -1))))
#define digitalPinHasPWM(p) (((p) >= 3 && (p) <= 6) || (p) == 9 || (p) == 10 || ((p) >= 20 && (p) <= 23) || (p) == 25 || (p) == 32)
#define digitalPinToInterrupt(p) ((p) < NUM_DIGITAL_PINS ? (p) : -1)
#elif defined(__MKL26Z64__)
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) <= 12) ? (p) + 14 : -1))
#define digitalPinHasPWM(p) ((p) == 3 || (p) == 4 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 16 || (p) == 17 || (p) == 20 || (p) == 22 || (p) == 23)
#define digitalPinToInterrupt(p) ((((p) >= 2 && (p) <= 15) || ((p) >= 20 && (p) <= 23)) ? (p) : -1)
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
// TODO analogInputToDigitalPin needs update...
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) >= 12 && (p) <= 20) ? (p) + 19 : -1))
#define digitalPinHasPWM(p) (((p) >= 2 && (p) <= 10) || (p) == 14 || ((p) >= 20 && (p) <= 23) || (p) == 29 || (p) == 30 || ((p) >= 35 && (p) <= 38))
#define digitalPinToInterrupt(p) ((p) < NUM_DIGITAL_PINS ? (p) : -1)
#endif
#define digitalPinToPCICR(p) ((volatile uint8_t *)0)
#define digitalPinToPCICRbit(p) (0)
#define digitalPinToPCIFR(p) ((volatile uint8_t *)0)
#define digitalPinToPCIFRbit(p) (0)
#define digitalPinToPCMSK(p) ((volatile uint8_t *)0)
#define digitalPinToPCMSKbit(p) (0)
#if defined(KINETISK)
struct digital_pin_bitband_and_config_table_struct {
volatile uint32_t *reg;
volatile uint32_t *config;
};
extern const struct digital_pin_bitband_and_config_table_struct digital_pin_to_info_PGM[];
// compatibility macros
#define digitalPinToPort(pin) (pin)
#define digitalPinToBitMask(pin) (1)
#define portOutputRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 0))
#define portSetRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 32))
#define portClearRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 64))
#define portToggleRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 96))
#define portInputRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 128))
#define portModeRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 160))
#define portConfigRegister(pin) ((volatile uint32_t *)(digital_pin_to_info_PGM[(pin)].config))
#define digitalPinToPortReg(pin) (portOutputRegister(pin))
#define digitalPinToBit(pin) (1)
#elif defined(KINETISL)
struct digital_pin_bitband_and_config_table_struct {
volatile uint8_t *reg;
volatile uint32_t *config;
uint8_t mask;
};
extern const struct digital_pin_bitband_and_config_table_struct digital_pin_to_info_PGM[];
// compatibility macros
#define digitalPinToPort(pin) (pin)
#define digitalPinToBitMask(pin) (digital_pin_to_info_PGM[(pin)].mask)
#define portOutputRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 0))
#define portSetRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 4))
#define portClearRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 8))
#define portToggleRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 12))
#define portInputRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 16))
#define portModeRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 20))
#define portConfigRegister(pin) ((digital_pin_to_info_PGM[(pin)].config))
#define digitalPinToPortReg(pin) (portOutputRegister(pin))
//#define digitalPinToBit(pin) (1)
#endif
#define NOT_ON_TIMER 0
static inline uint8_t digitalPinToTimer(uint8_t) __attribute__((always_inline, unused));
static inline uint8_t digitalPinToTimer(uint8_t pin)
{
if (pin >= 3 && pin <= 6) return pin - 2;
if (pin >= 9 && pin <= 10) return pin - 4;
if (pin >= 20 && pin <= 23) return pin - 13;
return NOT_ON_TIMER;
}
// These serial port names are intended to allow libraries and architecture-neutral
// sketches to automatically default to the correct port name for a particular type
// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN,
// the first hardware serial port whose RX/TX pins are not dedicated to another use.
//
// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor
//
// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial
//
// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library
//
// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins.
//
// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX
// pins are NOT connected to anything by default.
//
#if F_CPU >= 20000000 && !defined(USB_DISABLED)
#define SERIAL_PORT_MONITOR Serial
#else
#define SERIAL_PORT_MONITOR Serial1
#endif
#define SERIAL_PORT_USBVIRTUAL Serial
#define SERIAL_PORT_HARDWARE Serial1
#define SERIAL_PORT_HARDWARE1 Serial2
#define SERIAL_PORT_HARDWARE2 Serial3
#define SERIAL_PORT_HARDWARE_OPEN Serial1
#define SERIAL_PORT_HARDWARE_OPEN1 Serial2
#define SERIAL_PORT_HARDWARE_OPEN2 Serial3
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define SERIAL_PORT_HARDWARE3 Serial4
#define SERIAL_PORT_HARDWARE4 Serial5
#define SERIAL_PORT_HARDWARE5 Serial6
#define SERIAL_PORT_HARDWARE_OPEN3 Serial4
#define SERIAL_PORT_HARDWARE_OPEN4 Serial5
#define SERIAL_PORT_HARDWARE_OPEN5 Serial6
#endif
#define SerialUSB Serial
#endif
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef pins_macros_for_arduino_compatibility_h
#define pins_macros_for_arduino_compatibility_h
#include <stdint.h>
// A0-A9 are always digital 14-23, for Arduino compatibility
#define PIN_A0 (14)
#define PIN_A1 (15)
#define PIN_A2 (16)
#define PIN_A3 (17)
#define PIN_A4 (18)
#define PIN_A5 (19)
#define PIN_A6 (20)
#define PIN_A7 (21)
#define PIN_A8 (22)
#define PIN_A9 (23)
const static uint8_t A0 = PIN_A0;
const static uint8_t A1 = PIN_A1;
const static uint8_t A2 = PIN_A2;
const static uint8_t A3 = PIN_A3;
const static uint8_t A4 = PIN_A4;
const static uint8_t A5 = PIN_A5;
const static uint8_t A6 = PIN_A6;
const static uint8_t A7 = PIN_A7;
const static uint8_t A8 = PIN_A8;
const static uint8_t A9 = PIN_A9;
#if defined(__MK20DX128__)
#define PIN_A10 (34)
#define PIN_A11 (35)
#define PIN_A12 (36)
#define PIN_A13 (37)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
const static uint8_t A13 = PIN_A13;
#elif defined(__MK20DX256__)
#define PIN_A10 (34)
#define PIN_A11 (35)
#define PIN_A12 (36)
#define PIN_A13 (37)
#define PIN_A14 (40)
#define PIN_A15 (26)
#define PIN_A16 (27)
#define PIN_A17 (28)
#define PIN_A18 (29)
#define PIN_A19 (30)
#define PIN_A20 (31)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
const static uint8_t A13 = PIN_A13;
const static uint8_t A14 = PIN_A14;
const static uint8_t A15 = PIN_A15;
const static uint8_t A16 = PIN_A16;
const static uint8_t A17 = PIN_A17;
const static uint8_t A18 = PIN_A18;
const static uint8_t A19 = PIN_A19;
const static uint8_t A20 = PIN_A20;
#elif defined(__MKL26Z64__)
#define PIN_A10 (24)
#define PIN_A11 (25)
#define PIN_A12 (26)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define PIN_A10 (64)
#define PIN_A11 (65)
#define PIN_A12 (31)
#define PIN_A13 (32)
#define PIN_A14 (33)
#define PIN_A15 (34)
#define PIN_A16 (35)
#define PIN_A17 (36)
#define PIN_A18 (37)
#define PIN_A19 (38)
#define PIN_A20 (39)
#define PIN_A21 (66)
#define PIN_A22 (67)
#define PIN_A23 (49)
#define PIN_A24 (50)
#define PIN_A25 (68)
#define PIN_A26 (69)
const static uint8_t A10 = PIN_A10;
const static uint8_t A11 = PIN_A11;
const static uint8_t A12 = PIN_A12;
const static uint8_t A13 = PIN_A13;
const static uint8_t A14 = PIN_A14;
const static uint8_t A15 = PIN_A15;
const static uint8_t A16 = PIN_A16;
const static uint8_t A17 = PIN_A17;
const static uint8_t A18 = PIN_A18;
const static uint8_t A19 = PIN_A19;
const static uint8_t A20 = PIN_A20;
const static uint8_t A21 = PIN_A21;
const static uint8_t A22 = PIN_A22;
const static uint8_t A23 = PIN_A23;
const static uint8_t A24 = PIN_A24;
const static uint8_t A25 = PIN_A25;
const static uint8_t A26 = PIN_A26;
#endif
#define LED_BUILTIN (13)
#define PIN_SPI_SS (10)
#define PIN_SPI_MOSI (11)
#define PIN_SPI_MISO (12)
#define PIN_SPI_SCK (13)
const static uint8_t SS = 10;
const static uint8_t MOSI = 11;
const static uint8_t MISO = 12;
const static uint8_t SCK = 13;
#define PIN_WIRE_SDA (18)
#define PIN_WIRE_SCL (19)
const static uint8_t SDA = 18;
const static uint8_t SCL = 19;
#define PIN_SERIAL_RX (0)
#define PIN_SERIAL_TX (1)
#define NUM_DIGITAL_PINS CORE_NUM_DIGITAL
#define NUM_ANALOG_INPUTS CORE_NUM_ANALOG
#define NOT_AN_INTERRUPT -1
#if defined(__MK20DX128__)
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) <= 13) ? (p) + 24 : -1))
#define digitalPinHasPWM(p) (((p) >= 3 && (p) <= 6) || (p) == 9 || (p) == 10 || ((p) >= 20 && (p) <= 23))
#define digitalPinToInterrupt(p) ((p) < NUM_DIGITAL_PINS ? (p) : -1)
#elif defined(__MK20DX256__)
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) <= 13) ? (p) + 24 : (((p) == 14) ? 40 : (((p) <= 20) ? (p) + 11 : -1))))
#define digitalPinHasPWM(p) (((p) >= 3 && (p) <= 6) || (p) == 9 || (p) == 10 || ((p) >= 20 && (p) <= 23) || (p) == 25 || (p) == 32)
#define digitalPinToInterrupt(p) ((p) < NUM_DIGITAL_PINS ? (p) : -1)
#elif defined(__MKL26Z64__)
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) <= 12) ? (p) + 14 : -1))
#define digitalPinHasPWM(p) ((p) == 3 || (p) == 4 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 16 || (p) == 17 || (p) == 20 || (p) == 22 || (p) == 23)
#define digitalPinToInterrupt(p) ((((p) >= 2 && (p) <= 15) || ((p) >= 20 && (p) <= 23)) ? (p) : -1)
#elif defined(__MK64FX512__) || defined(__MK66FX1M0__)
// TODO analogInputToDigitalPin needs update...
#define analogInputToDigitalPin(p) (((p) <= 9) ? (p) + 14 : (((p) >= 12 && (p) <= 20) ? (p) + 19 : -1))
#define digitalPinHasPWM(p) (((p) >= 2 && (p) <= 10) || (p) == 14 || ((p) >= 20 && (p) <= 23) || (p) == 29 || (p) == 30 || ((p) >= 35 && (p) <= 38))
#define digitalPinToInterrupt(p) ((p) < NUM_DIGITAL_PINS ? (p) : -1)
#endif
#define digitalPinToPCICR(p) ((volatile uint8_t *)0)
#define digitalPinToPCICRbit(p) (0)
#define digitalPinToPCIFR(p) ((volatile uint8_t *)0)
#define digitalPinToPCIFRbit(p) (0)
#define digitalPinToPCMSK(p) ((volatile uint8_t *)0)
#define digitalPinToPCMSKbit(p) (0)
#if defined(KINETISK)
struct digital_pin_bitband_and_config_table_struct {
volatile uint32_t *reg;
volatile uint32_t *config;
};
extern const struct digital_pin_bitband_and_config_table_struct digital_pin_to_info_PGM[];
// compatibility macros
#define digitalPinToPort(pin) (pin)
#define digitalPinToBitMask(pin) (1)
#define portOutputRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 0))
#define portSetRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 32))
#define portClearRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 64))
#define portToggleRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 96))
#define portInputRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 128))
#define portModeRegister(pin) ((volatile uint8_t *)(digital_pin_to_info_PGM[(pin)].reg + 160))
#define portConfigRegister(pin) ((volatile uint32_t *)(digital_pin_to_info_PGM[(pin)].config))
#define digitalPinToPortReg(pin) (portOutputRegister(pin))
#define digitalPinToBit(pin) (1)
#elif defined(KINETISL)
struct digital_pin_bitband_and_config_table_struct {
volatile uint8_t *reg;
volatile uint32_t *config;
uint8_t mask;
};
extern const struct digital_pin_bitband_and_config_table_struct digital_pin_to_info_PGM[];
// compatibility macros
#define digitalPinToPort(pin) (pin)
#define digitalPinToBitMask(pin) (digital_pin_to_info_PGM[(pin)].mask)
#define portOutputRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 0))
#define portSetRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 4))
#define portClearRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 8))
#define portToggleRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 12))
#define portInputRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 16))
#define portModeRegister(pin) ((digital_pin_to_info_PGM[(pin)].reg + 20))
#define portConfigRegister(pin) ((digital_pin_to_info_PGM[(pin)].config))
#define digitalPinToPortReg(pin) (portOutputRegister(pin))
//#define digitalPinToBit(pin) (1)
#endif
#define NOT_ON_TIMER 0
static inline uint8_t digitalPinToTimer(uint8_t) __attribute__((always_inline, unused));
static inline uint8_t digitalPinToTimer(uint8_t pin)
{
if (pin >= 3 && pin <= 6) return pin - 2;
if (pin >= 9 && pin <= 10) return pin - 4;
if (pin >= 20 && pin <= 23) return pin - 13;
return NOT_ON_TIMER;
}
// These serial port names are intended to allow libraries and architecture-neutral
// sketches to automatically default to the correct port name for a particular type
// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN,
// the first hardware serial port whose RX/TX pins are not dedicated to another use.
//
// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor
//
// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial
//
// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library
//
// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins.
//
// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX
// pins are NOT connected to anything by default.
//
#if F_CPU >= 20000000 && !defined(USB_DISABLED)
#define SERIAL_PORT_MONITOR Serial
#else
#define SERIAL_PORT_MONITOR Serial1
#endif
#define SERIAL_PORT_USBVIRTUAL Serial
#define SERIAL_PORT_HARDWARE Serial1
#define SERIAL_PORT_HARDWARE1 Serial2
#define SERIAL_PORT_HARDWARE2 Serial3
#define SERIAL_PORT_HARDWARE_OPEN Serial1
#define SERIAL_PORT_HARDWARE_OPEN1 Serial2
#define SERIAL_PORT_HARDWARE_OPEN2 Serial3
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define SERIAL_PORT_HARDWARE3 Serial4
#define SERIAL_PORT_HARDWARE4 Serial5
#define SERIAL_PORT_HARDWARE5 Serial6
#define SERIAL_PORT_HARDWARE_OPEN3 Serial4
#define SERIAL_PORT_HARDWARE_OPEN4 Serial5
#define SERIAL_PORT_HARDWARE_OPEN5 Serial6
#endif
#define SerialUSB Serial
#endif

View file

@ -1,35 +1,35 @@
#ifndef __SIMUSBMIDI_H__
#define __SIMUSBMIDI_H__
#include <string>
class SimUsbMidi
{
public:
void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0);
void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0);
void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0);
void sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0);
void sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t cable=0);
void sendProgramChange(uint8_t program, uint8_t channel, uint8_t cable=0);
void sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t cable=0);
void sendPitchBend(int value, uint8_t channel, uint8_t cable=0);
void sendSysEx(uint16_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0);
bool read(uint8_t channel=0);
void setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size));
void setHandleSystemExclusive(void (*fptr) (const uint8_t *data, uint16_t length, bool complete));
//Things not part of Teensy USBMidi, but used to simulate sending data to it
void receiveMidiData(const uint8_t *data, const uint16_t length); //Send midi data "into simulator"
void setMidiFile(std::string filename); //MIDI data to send to device
void triggerMidi(); //"Arm" so data is sent to device next time it tries to read anything
private:
//Handlers registered to receive MIDI
void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete);
void (*usb_midi_handleSysExComplete)(const uint8_t *data, unsigned int size);
std::string midiFile;
bool sendMidi;
};
#ifndef __SIMUSBMIDI_H__
#define __SIMUSBMIDI_H__
#include <string>
class SimUsbMidi
{
public:
void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0);
void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0);
void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0);
void sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0);
void sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t cable=0);
void sendProgramChange(uint8_t program, uint8_t channel, uint8_t cable=0);
void sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t cable=0);
void sendPitchBend(int value, uint8_t channel, uint8_t cable=0);
void sendSysEx(uint16_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0);
bool read(uint8_t channel=0);
void setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size));
void setHandleSystemExclusive(void (*fptr) (const uint8_t *data, uint16_t length, bool complete));
//Things not part of Teensy USBMidi, but used to simulate sending data to it
void receiveMidiData(const uint8_t *data, const uint16_t length); //Send midi data "into simulator"
void setMidiFile(std::string filename); //MIDI data to send to device
void triggerMidi(); //"Arm" so data is sent to device next time it tries to read anything
private:
//Handlers registered to receive MIDI
void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete);
void (*usb_midi_handleSysExComplete)(const uint8_t *data, unsigned int size);
std::string midiFile;
bool sendMidi;
};
#endif

View file

@ -1,197 +1,197 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef Wiring_h
#define Wiring_h
#include <stdint.h>
#include <stdlib.h>
// #include "binary.h"
#include "core_id.h"
// #include "core_pins.h"
// type_traits interferes with min() and other defines
// include it early, so we can define these later
// for Arduino compatibility
#ifdef __cplusplus
#include <type_traits>
// when the input number is an integer type, do all math as 32 bit signed long
template <class T, class A, class B, class C, class D>
long map(T _x, A _in_min, B _in_max, C _out_min, D _out_max, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
long x = _x, in_min = _in_min, in_max = _in_max, out_min = _out_min, out_max = _out_max;
// Arduino's traditional algorithm
//return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
// st42's suggestion: https://github.com/arduino/Arduino/issues/2466#issuecomment-69873889
// more conversation:
// https://forum.pjrc.com/threads/44503-map()-function-improvements
if ((in_max - in_min) > (out_max - out_min)) {
return (x - in_min) * (out_max - out_min+1) / (in_max - in_min+1) + out_min;
} else {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
}
// when the input is a float or double, do all math using the input's type
template <class T, class A, class B, class C, class D>
T map(T x, A in_min, B in_max, C out_min, D out_max, typename std::enable_if<std::is_floating_point<T>::value >::type* = 0)
{
return (x - (T)in_min) * ((T)out_max - (T)out_min) / ((T)in_max - (T)in_min) + (T)out_min;
}
//#include <algorithm> // this isn't really needed, is it? (slows down compiling)
#include <utility>
// https://forum.pjrc.com/threads/44596-Teensyduino-1-37-Beta-2-(Arduino-1-8-3-support)?p=145150&viewfull=1#post145150
template<class A, class B>
constexpr auto min(A&& a, B&& b) -> decltype(a < b ? std::forward<A>(a) : std::forward<B>(b)) {
return a < b ? std::forward<A>(a) : std::forward<B>(b);
}
template<class A, class B>
constexpr auto max(A&& a, B&& b) -> decltype(a < b ? std::forward<A>(a) : std::forward<B>(b)) {
return a >= b ? std::forward<A>(a) : std::forward<B>(b);
}
#else // not C++
#define min(a, b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
(_a < _b) ? _a : _b; \
})
#define max(a, b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
(_a > _b) ? _a : _b; \
})
#endif
#ifdef PI
#undef PI
#endif
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.4142135623730950488016887
#endif
#define SERIAL 0
#define DISPLAY 1
// undefine stdlib's abs if encountered
#ifdef abs
#undef abs
#endif
#if __cplusplus >= 201103L && defined(__STRICT_ANSI__)
#define typeof(a) decltype(a)
#endif
#define abs(x) ({ \
typeof(x) _x = (x); \
(_x > 0) ? _x : -_x; \
})
#define constrain(amt, low, high) ({ \
typeof(amt) _amt = (amt); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
#define round(x) ({ \
typeof(x) _x = (x); \
(_x>=0) ? (long)(_x+0.5) : (long)(_x-0.5); \
})
#define radians(deg) ((deg)*DEG_TO_RAD)
#define degrees(rad) ((rad)*RAD_TO_DEG)
#define sq(x) ({ \
typeof(x) _x = (x); \
_x * _x; \
})
#ifdef __cplusplus
extern "C"{
#endif
extern double exp10(double x);
extern float exp10f(float x);
extern long double exp10l(long double x);
extern double pow10(double x);
extern float pow10f(float x);
extern long double pow10l(long double x);
#define stricmp(a, b) strcasecmp(a, b)
#define sei() __enable_irq()
#define cli() __disable_irq()
#define interrupts() __enable_irq()
#define noInterrupts() __disable_irq()
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
#define lowByte(w) ((uint8_t)((w) & 0xFF))
#define highByte(w) ((uint8_t)((w) >> 8))
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet((value), (bit)) : bitClear((value), (bit)))
typedef unsigned int word;
#define bit(b) (1UL << (b))
typedef uint8_t byte;
uint32_t pulseIn(uint8_t pin, uint8_t state, uint32_t timeout);
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, byte val);
void setup(void);
void loop(void);
#ifdef __cplusplus
} // extern "C"
#endif
// fix C++ boolean issue
// https://github.com/arduino/Arduino/pull/2151
#ifdef __cplusplus
typedef bool boolean;
#else
typedef uint8_t boolean;
#define false 0
#define true (!false)
#endif
#endif
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef Wiring_h
#define Wiring_h
#include <stdint.h>
#include <stdlib.h>
// #include "binary.h"
#include "core_id.h"
// #include "core_pins.h"
// type_traits interferes with min() and other defines
// include it early, so we can define these later
// for Arduino compatibility
#ifdef __cplusplus
#include <type_traits>
// when the input number is an integer type, do all math as 32 bit signed long
template <class T, class A, class B, class C, class D>
long map(T _x, A _in_min, B _in_max, C _out_min, D _out_max, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
long x = _x, in_min = _in_min, in_max = _in_max, out_min = _out_min, out_max = _out_max;
// Arduino's traditional algorithm
//return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
// st42's suggestion: https://github.com/arduino/Arduino/issues/2466#issuecomment-69873889
// more conversation:
// https://forum.pjrc.com/threads/44503-map()-function-improvements
if ((in_max - in_min) > (out_max - out_min)) {
return (x - in_min) * (out_max - out_min+1) / (in_max - in_min+1) + out_min;
} else {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
}
// when the input is a float or double, do all math using the input's type
template <class T, class A, class B, class C, class D>
T map(T x, A in_min, B in_max, C out_min, D out_max, typename std::enable_if<std::is_floating_point<T>::value >::type* = 0)
{
return (x - (T)in_min) * ((T)out_max - (T)out_min) / ((T)in_max - (T)in_min) + (T)out_min;
}
//#include <algorithm> // this isn't really needed, is it? (slows down compiling)
#include <utility>
// https://forum.pjrc.com/threads/44596-Teensyduino-1-37-Beta-2-(Arduino-1-8-3-support)?p=145150&viewfull=1#post145150
template<class A, class B>
constexpr auto min(A&& a, B&& b) -> decltype(a < b ? std::forward<A>(a) : std::forward<B>(b)) {
return a < b ? std::forward<A>(a) : std::forward<B>(b);
}
template<class A, class B>
constexpr auto max(A&& a, B&& b) -> decltype(a < b ? std::forward<A>(a) : std::forward<B>(b)) {
return a >= b ? std::forward<A>(a) : std::forward<B>(b);
}
#else // not C++
#define min(a, b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
(_a < _b) ? _a : _b; \
})
#define max(a, b) ({ \
typeof(a) _a = (a); \
typeof(b) _b = (b); \
(_a > _b) ? _a : _b; \
})
#endif
#ifdef PI
#undef PI
#endif
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.4142135623730950488016887
#endif
#define SERIAL 0
#define DISPLAY 1
// undefine stdlib's abs if encountered
#ifdef abs
#undef abs
#endif
#if __cplusplus >= 201103L && defined(__STRICT_ANSI__)
#define typeof(a) decltype(a)
#endif
#define abs(x) ({ \
typeof(x) _x = (x); \
(_x > 0) ? _x : -_x; \
})
#define constrain(amt, low, high) ({ \
typeof(amt) _amt = (amt); \
typeof(low) _low = (low); \
typeof(high) _high = (high); \
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
#define round(x) ({ \
typeof(x) _x = (x); \
(_x>=0) ? (long)(_x+0.5) : (long)(_x-0.5); \
})
#define radians(deg) ((deg)*DEG_TO_RAD)
#define degrees(rad) ((rad)*RAD_TO_DEG)
#define sq(x) ({ \
typeof(x) _x = (x); \
_x * _x; \
})
#ifdef __cplusplus
extern "C"{
#endif
extern double exp10(double x);
extern float exp10f(float x);
extern long double exp10l(long double x);
extern double pow10(double x);
extern float pow10f(float x);
extern long double pow10l(long double x);
#define stricmp(a, b) strcasecmp(a, b)
#define sei() __enable_irq()
#define cli() __disable_irq()
#define interrupts() __enable_irq()
#define noInterrupts() __disable_irq()
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )
#define lowByte(w) ((uint8_t)((w) & 0xFF))
#define highByte(w) ((uint8_t)((w) >> 8))
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet((value), (bit)) : bitClear((value), (bit)))
typedef unsigned int word;
#define bit(b) (1UL << (b))
typedef uint8_t byte;
uint32_t pulseIn(uint8_t pin, uint8_t state, uint32_t timeout);
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, byte val);
void setup(void);
void loop(void);
#ifdef __cplusplus
} // extern "C"
#endif
// fix C++ boolean issue
// https://github.com/arduino/Arduino/pull/2151
#ifdef __cplusplus
typedef bool boolean;
#else
typedef uint8_t boolean;
#define false 0
#define true (!false)
#endif
#endif

View file

@ -1,22 +1,22 @@
# Simulator for NuEVI
This is a simple SDL2 based simulator that runs the NuEVI firmware compiled for MacOS. This is for testing the menu and is not supposed to produce any midi events.
## Requirements
* The Filters library must be installed in ~/Documents/Arduino/libraries/Filters.
* [SDL2.framework](https://www.libsdl.org/download-2.0.php) must be installed on your machine in /Library/Frameworks/ (or ~/Library/Frameworks/).
* You probably need to have XCode and XCodes command line tools installed as well, but using brew to install make and clang might be enough.
[Dear Imgui](https://github.com/ocornut/imgui) is pulled in as an git submodule. Run `git submodule init` and `git submodule update` to get the code. The code is tested with tag v1.70 of ImGui, so if you run into problems make sure that is the checked out version.
## Know limitations
Currently only some input is simulated, and the default values are not based on real hardware. There is also a limitation on some keyboards on how many buttons can be pressed at the same time. This means that all menu functions cannot be tested.
## Future plans
* Add simulation for all inputs
* Show MIDI status in UI
* Add in-app log window
* Fake real breath input by keypress
# Simulator for NuEVI
This is a simple SDL2 based simulator that runs the NuEVI firmware compiled for MacOS. This is for testing the menu and is not supposed to produce any midi events.
## Requirements
* The Filters library must be installed in ~/Documents/Arduino/libraries/Filters.
* [SDL2.framework](https://www.libsdl.org/download-2.0.php) must be installed on your machine in /Library/Frameworks/ (or ~/Library/Frameworks/).
* You probably need to have XCode and XCodes command line tools installed as well, but using brew to install make and clang might be enough.
[Dear Imgui](https://github.com/ocornut/imgui) is pulled in as an git submodule. Run `git submodule init` and `git submodule update` to get the code. The code is tested with tag v1.70 of ImGui, so if you run into problems make sure that is the checked out version.
## Know limitations
Currently only some input is simulated, and the default values are not based on real hardware. There is also a limitation on some keyboards on how many buttons can be pressed at the same time. This means that all menu functions cannot be tested.
## Future plans
* Add simulation for all inputs
* Show MIDI status in UI
* Add in-app log window
* Fake real breath input by keypress

File diff suppressed because it is too large Load diff

View file

@ -1,170 +1,170 @@
/*!
* @file Adafruit_MPR121.cpp
*
* @mainpage Adafruit MPR121 arduino driver
*
* @section intro_sec Introduction
*
This is a library for the MPR121 I2C 12-chan Capacitive Sensor
Designed specifically to work with the MPR121 sensor from Adafruit
----> https://www.adafruit.com/products/1982
These sensors use I2C to communicate, 2+ pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
*
* @section author Author
*
* Written by Limor Fried/Ladyada for Adafruit Industries.
*
* @section license License
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Adafruit_MPR121.h"
/**
*****************************************************************************************
* @brief Default constructor
****************************************************************************************/
Adafruit_MPR121::Adafruit_MPR121() {
}
/**
*****************************************************************************************
* @brief Begin an MPR121 object on a given I2C bus. This function resets the
* device and writes the default settings.
*
* @param i2caddr the i2c address the device can be found on. Defaults to 0x5A.
* @returns true on success, false otherwise
****************************************************************************************/
boolean Adafruit_MPR121::begin(uint8_t i2caddr) {
_i2caddr = i2caddr;
return true;
}
/**
*****************************************************************************************
* @brief DEPRECATED. Use Adafruit_MPR121::setThresholds(uint8_t touch, uint8_t release) instead.
*
* @param touch see Adafruit_MPR121::setThresholds(uint8_t touch, uint8_t release)
* @param release see Adafruit_MPR121::setThresholds(uint8_t touch, uint8_t release)
****************************************************************************************/
void Adafruit_MPR121::setThreshholds(uint8_t __attribute__((unused)) touch, uint8_t __attribute__((unused)) release) {
// setThresholds(touch, release);
}
/**
*****************************************************************************************
* @brief Set the touch and release thresholds for all 13 channels on the device to the
* passed values. The threshold is defined as a deviation value from the baseline value,
* so it remains constant even baseline value changes. Typically the touch
* threshold is a little bigger than the release threshold to touch debounce and hysteresis.
*
* For typical touch application, the value can be in range 0x05~0x30 for example. The setting
* of the threshold is depended on the actual application. For the operation details and how to set the threshold refer to
* application note AN3892 and MPR121 design guidelines.
*
* @param touch the touch threshold value from 0 to 255.
* @param release the release threshold from 0 to 255.
****************************************************************************************/
void Adafruit_MPR121::setThresholds(uint8_t __attribute__((unused)) touch, __attribute__((unused)) uint8_t release) {
// for (uint8_t i=0; i<12; i++) {
// writeRegister(MPR121_TOUCHTH_0 + 2*i, touch);
// writeRegister(MPR121_RELEASETH_0 + 2*i, release);
// }
}
/**
*****************************************************************************************
* @brief Read the filtered data from channel t. The ADC raw data outputs run through 3
* levels of digital filtering to filter out the high frequency and low frequency noise
* encountered. For detailed information on this filtering see page 6 of the device datasheet.
*
* @param t the channel to read
* @returns the filtered reading as a 10 bit unsigned value
****************************************************************************************/
uint16_t Adafruit_MPR121::filteredData(uint8_t t) {
if (t > 12) return 0;
return readRegister16(MPR121_FILTDATA_0L + t*2);
}
/**
*****************************************************************************************
* @brief Read the baseline value for the channel. The 3rd level filtered result is internally 10bit
* but only high 8 bits are readable from registers 0x1E~0x2A as the baseline value output for each channel.
*
* @param t the channel to read.
* @returns the baseline data that was read
****************************************************************************************/
uint16_t Adafruit_MPR121::baselineData(uint8_t t) {
if (t > 12) return 0;
uint16_t bl = readRegister8(MPR121_BASELINE_0 + t);
return (bl << 2);
}
/**
*****************************************************************************************
* @brief Read the touch status of all 13 channels as bit values in a 12 bit integer.
*
* @returns a 12 bit integer with each bit corresponding to the touch status of a sensor.
* For example, if bit 0 is set then channel 0 of the device is currently deemed to be touched.
****************************************************************************************/
uint16_t Adafruit_MPR121::touched(void) {
uint16_t t = readRegister16(MPR121_TOUCHSTATUS_L);
return t & 0x0FFF;
}
/*********************************************************************/
/**
*****************************************************************************************
* @brief Read the contents of an 8 bit device register.
*
* @param reg the register address to read from
* @returns the 8 bit value that was read.
****************************************************************************************/
uint8_t Adafruit_MPR121::readRegister8(uint8_t reg) {
return this->_registers[reg];
}
/**
*****************************************************************************************
* @brief Read the contents of a 16 bit device register.
*
* @param reg the register address to read from
* @returns the 16 bit value that was read.
****************************************************************************************/
uint16_t Adafruit_MPR121::readRegister16(uint8_t reg) {
return _registers[reg] | (_registers[reg+1] << 8);
}
/**************************************************************************/
/*!
@brief Writes 8-bits to the specified destination register
@param reg the register address to write to
@param value the value to write
*/
/**************************************************************************/
void Adafruit_MPR121::writeRegister(uint8_t reg, uint8_t value) {
_registers[reg] = value;
}
/*
Simulator specifics code..
*/
void Adafruit_MPR121::mockFilteredData(int reg, uint16_t value) {
_registers[MPR121_FILTDATA_0L + reg*2] = value & 0xffu;
_registers[MPR121_FILTDATA_0L + reg*2+1] = (value>>8) & 0xffu;
}
/*!
* @file Adafruit_MPR121.cpp
*
* @mainpage Adafruit MPR121 arduino driver
*
* @section intro_sec Introduction
*
This is a library for the MPR121 I2C 12-chan Capacitive Sensor
Designed specifically to work with the MPR121 sensor from Adafruit
----> https://www.adafruit.com/products/1982
These sensors use I2C to communicate, 2+ pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
*
* @section author Author
*
* Written by Limor Fried/Ladyada for Adafruit Industries.
*
* @section license License
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Adafruit_MPR121.h"
/**
*****************************************************************************************
* @brief Default constructor
****************************************************************************************/
Adafruit_MPR121::Adafruit_MPR121() {
}
/**
*****************************************************************************************
* @brief Begin an MPR121 object on a given I2C bus. This function resets the
* device and writes the default settings.
*
* @param i2caddr the i2c address the device can be found on. Defaults to 0x5A.
* @returns true on success, false otherwise
****************************************************************************************/
boolean Adafruit_MPR121::begin(uint8_t i2caddr) {
_i2caddr = i2caddr;
return true;
}
/**
*****************************************************************************************
* @brief DEPRECATED. Use Adafruit_MPR121::setThresholds(uint8_t touch, uint8_t release) instead.
*
* @param touch see Adafruit_MPR121::setThresholds(uint8_t touch, uint8_t release)
* @param release see Adafruit_MPR121::setThresholds(uint8_t touch, uint8_t release)
****************************************************************************************/
void Adafruit_MPR121::setThreshholds(uint8_t __attribute__((unused)) touch, uint8_t __attribute__((unused)) release) {
// setThresholds(touch, release);
}
/**
*****************************************************************************************
* @brief Set the touch and release thresholds for all 13 channels on the device to the
* passed values. The threshold is defined as a deviation value from the baseline value,
* so it remains constant even baseline value changes. Typically the touch
* threshold is a little bigger than the release threshold to touch debounce and hysteresis.
*
* For typical touch application, the value can be in range 0x05~0x30 for example. The setting
* of the threshold is depended on the actual application. For the operation details and how to set the threshold refer to
* application note AN3892 and MPR121 design guidelines.
*
* @param touch the touch threshold value from 0 to 255.
* @param release the release threshold from 0 to 255.
****************************************************************************************/
void Adafruit_MPR121::setThresholds(uint8_t __attribute__((unused)) touch, __attribute__((unused)) uint8_t release) {
// for (uint8_t i=0; i<12; i++) {
// writeRegister(MPR121_TOUCHTH_0 + 2*i, touch);
// writeRegister(MPR121_RELEASETH_0 + 2*i, release);
// }
}
/**
*****************************************************************************************
* @brief Read the filtered data from channel t. The ADC raw data outputs run through 3
* levels of digital filtering to filter out the high frequency and low frequency noise
* encountered. For detailed information on this filtering see page 6 of the device datasheet.
*
* @param t the channel to read
* @returns the filtered reading as a 10 bit unsigned value
****************************************************************************************/
uint16_t Adafruit_MPR121::filteredData(uint8_t t) {
if (t > 12) return 0;
return readRegister16(MPR121_FILTDATA_0L + t*2);
}
/**
*****************************************************************************************
* @brief Read the baseline value for the channel. The 3rd level filtered result is internally 10bit
* but only high 8 bits are readable from registers 0x1E~0x2A as the baseline value output for each channel.
*
* @param t the channel to read.
* @returns the baseline data that was read
****************************************************************************************/
uint16_t Adafruit_MPR121::baselineData(uint8_t t) {
if (t > 12) return 0;
uint16_t bl = readRegister8(MPR121_BASELINE_0 + t);
return (bl << 2);
}
/**
*****************************************************************************************
* @brief Read the touch status of all 13 channels as bit values in a 12 bit integer.
*
* @returns a 12 bit integer with each bit corresponding to the touch status of a sensor.
* For example, if bit 0 is set then channel 0 of the device is currently deemed to be touched.
****************************************************************************************/
uint16_t Adafruit_MPR121::touched(void) {
uint16_t t = readRegister16(MPR121_TOUCHSTATUS_L);
return t & 0x0FFF;
}
/*********************************************************************/
/**
*****************************************************************************************
* @brief Read the contents of an 8 bit device register.
*
* @param reg the register address to read from
* @returns the 8 bit value that was read.
****************************************************************************************/
uint8_t Adafruit_MPR121::readRegister8(uint8_t reg) {
return this->_registers[reg];
}
/**
*****************************************************************************************
* @brief Read the contents of a 16 bit device register.
*
* @param reg the register address to read from
* @returns the 16 bit value that was read.
****************************************************************************************/
uint16_t Adafruit_MPR121::readRegister16(uint8_t reg) {
return _registers[reg] | (_registers[reg+1] << 8);
}
/**************************************************************************/
/*!
@brief Writes 8-bits to the specified destination register
@param reg the register address to write to
@param value the value to write
*/
/**************************************************************************/
void Adafruit_MPR121::writeRegister(uint8_t reg, uint8_t value) {
_registers[reg] = value;
}
/*
Simulator specifics code..
*/
void Adafruit_MPR121::mockFilteredData(int reg, uint16_t value) {
_registers[MPR121_FILTDATA_0L + reg*2] = value & 0xffu;
_registers[MPR121_FILTDATA_0L + reg*2+1] = (value>>8) & 0xffu;
}

File diff suppressed because it is too large Load diff

View file

@ -1,337 +1,337 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Long ago this file contained code from Arduino.cc, which was
// Copyright (c) 2008 David A. Mellis. No substantial portion of
// Arduino's original code remains. In fact, several improvements
// developed for Teensyduino have made their way back into
// Arduino's code base. :-)
#include <Arduino.h>
#include "Print.h"
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t count = 0;
while (size--) count += write(*buffer++);
return count;
}
// size_t Print::print(const String &s)
// {
// uint8_t buffer[33];
// size_t count = 0;
// unsigned int index = 0;
// unsigned int len = s.length();
// while (len > 0) {
// s.getBytes(buffer, sizeof(buffer), index);
// unsigned int nbytes = len;
// if (nbytes > sizeof(buffer)-1) nbytes = sizeof(buffer)-1;
// index += nbytes;
// len -= nbytes;
// count += write(buffer, nbytes);
// }
// return count;
// }
size_t Print::print(long n)
{
uint8_t sign=0;
if (n < 0) {
sign = '-';
n = -n;
}
return printNumber(n, 10, sign);
}
size_t Print::println(void)
{
uint8_t buf[2]={'\r', '\n'};
return write(buf, 2);
}
extern "C" {
__attribute__((weak))
int _write(long file, char *ptr, int len)
{
((class Print *)file)->write((uint8_t *)ptr, len);
return len;
}
}
int Print::printf(const char *format, ...)
{
va_list ap;
va_start(ap, format);
#ifdef __STRICT_ANSI__
return 0; // TODO: make this work with -std=c++0x
#else
return vdprintf((int)this, format, ap);
#endif
}
int Print::printf(const __FlashStringHelper *format, ...)
{
va_list ap;
va_start(ap, format);
#ifdef __STRICT_ANSI__
return 0;
#else
return vdprintf((int)this, (const char *)format, ap);
#endif
}
#ifdef __MKL26Z64__
// optimized code inspired by Stimmer's optimization
// obviously a dit different, adapted to 32 bit Cortex-M0+
// http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679
// http://forum.arduino.cc/index.php?topic=167414.msg1309482#msg1309482
// equivelant code:
// mod = div % 10;
// div = div / 10;
// tmp1 = {random};
// tmp2 = 10;
#if 1
// https://forum.pjrc.com/threads/28932-LC-is-10-9-times-slower-than-T3-1?p=76072&viewfull=1#post76072
void inline divmod10_v2(uint32_t n,uint32_t *div,uint32_t *mod) {
uint32_t p,q;
/* Using 32.16 fixed point representation p.q */
/* p.q = (n+1)/512 */
q = (n&0xFFFF) + 1;
p = (n>>16);
/* p.q = 51*(n+1)/512 */
q = 13107*q;
p = 13107*p;
/* p.q = (1+1/2^8+1/2^16+1/2^24)*51*(n+1)/512 */
q = q + (q>>16) + (p&0xFFFF);
p = p + (p>>16) + (q>>16);
/* divide by 2 */
p = p>>1;
*div = p;
*mod = n-10*p;
}
#define divmod10_asm(div, mod, tmp1, tmp2, const3333) \
divmod10_v2(div, &div, &mod);
/*
#define divmod10_asm(div, mod, tmp1, tmp2, const3333) \
asm ( \
" lsr %2, %0, #16" "\n\t" \
" mul %2, %4" "\n\t" \
" uxth %1, %0" "\n\t" \
" mul %1, %4" "\n\t" \
" add %1, #1" "\n\t" \
" lsr %0, %2, #16" "\n\t" \
" lsl %2, %2, #16" "\n\t" \
" add %1, %2" "\n\t" \
" mov %3, #0" "\n\t" \
" adc %0, %3" "\n\t" \
" lsl %0, %0, #15" "\n\t" \
" lsr %2, %1, #17" "\n\t" \
" orr %0, %2" "\n\t" \
" lsl %1, %1, #15" "\n\t" \
" lsr %2, %1, #16" "\n\t" \
" lsl %3, %0, #16" "\n\t" \
" orr %2, %3" "\n\t" \
" lsr %3, %0, #16" "\n\t" \
" add %1, %0" "\n\t" \
" adc %0, %1" "\n\t" \
" sub %0, %1" "\n\t" \
" add %1, %2" "\n\t" \
" adc %0, %3" "\n\t" \
" lsr %1, %1, #4" "\n\t" \
" mov %3, #10" "\n\t" \
" mul %1, %3" "\n\t" \
" lsr %1, %1, #28" "\n\t" \
: "+l" (div), \
"=&l" (mod), \
"=&l" (tmp1), \
"=&l" (tmp2) \
: "l" (const3333) \
: \
)
*/
#else
#define divmod10_asm(_div, _mod, _tmp1, _tmp2, _const3333) \
({ _tmp1 = _div; _div = _div / 10; _mod = _tmp1 - _div * 10; })
// ({_mod = _div % 10, _div = _div / 10; })
#endif
size_t Print::printNumberDec(unsigned long n, uint8_t sign)
{
uint8_t buf[11], *p;
uint32_t digit;
//uint32_t t1, t2, c3333=0x3333;
p = buf + (sizeof(buf));
do {
uint32_t div;
divmod10_v2(n, &div, &digit);
n = div;
//divmod10_asm(n, digit, t1, t2, c3333);
*--p = digit + '0';
} while (n);
if (sign) *--p = '-';
return write(p, sizeof(buf) - (p - buf));
}
size_t Print::printNumberHex(unsigned long n)
{
uint8_t digit, buf[8], *p;
p = buf + (sizeof(buf));
do {
digit = n & 15;
*--p = (digit < 10) ? '0' + digit : 'A' + digit - 10;
n >>= 4;
} while (n);
return write(p, sizeof(buf) - (p - buf));
}
size_t Print::printNumberBin(unsigned long n)
{
uint8_t buf[32], *p;
p = buf + (sizeof(buf));
do {
*--p = '0' + ((uint8_t)n & 1);
n >>= 1;
} while (n);
return write(p, sizeof(buf) - (p - buf));
}
size_t Print::printNumberAny(unsigned long n, uint8_t base)
{
uint8_t digit, buf[21], *p;
uint32_t tmp;
p = buf + sizeof(buf);
do {
tmp = n;
n = n / base;
digit = tmp - n * base;
*--p = (digit < 10) ? '0' + digit : 'A' + digit - 10;
} while (n);
return write(p, sizeof(buf) - (p - buf));
}
#else
size_t Print::printNumber(unsigned long n, uint8_t base, uint8_t sign)
{
uint8_t buf[34];
uint8_t digit, i;
// TODO: make these checks as inline, since base is
// almost always a constant. base = 0 (BYTE) should
// inline as a call directly to write()
if (base == 0) {
return write((uint8_t)n);
} else if (base == 1) {
base = 10;
}
if (n == 0) {
buf[sizeof(buf) - 1] = '0';
i = sizeof(buf) - 1;
} else {
i = sizeof(buf) - 1;
while (1) {
digit = n % base;
buf[i] = ((digit < 10) ? '0' + digit : 'A' + digit - 10);
n /= base;
if (n == 0) break;
i--;
}
}
if (sign) {
i--;
buf[i] = '-';
}
return write(buf + i, sizeof(buf) - i);
}
#endif
size_t Print::printFloat(double number, uint8_t digits)
{
uint8_t sign=0;
size_t count=0;
if (isnan(number)) return print("nan");
if (isinf(number)) return print("inf");
if (number > 4294967040.0f) return print("ovf"); // constant determined empirically
if (number <-4294967040.0f) return print("ovf"); // constant determined empirically
// Handle negative numbers
if (number < 0.0) {
sign = 1;
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i) {
rounding *= 0.1;
}
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
count += printNumber(int_part, 10, sign);
// Print the decimal point, but only if there are digits beyond
if (digits > 0) {
uint8_t n, buf[16], count=1;
buf[0] = '.';
// Extract digits from the remainder one at a time
if (digits > sizeof(buf) - 1) digits = sizeof(buf) - 1;
while (digits-- > 0) {
remainder *= 10.0;
n = (uint8_t)(remainder);
buf[count++] = '0' + n;
remainder -= n;
}
count += write(buf, count);
}
return count;
}
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Long ago this file contained code from Arduino.cc, which was
// Copyright (c) 2008 David A. Mellis. No substantial portion of
// Arduino's original code remains. In fact, several improvements
// developed for Teensyduino have made their way back into
// Arduino's code base. :-)
#include <Arduino.h>
#include "Print.h"
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t count = 0;
while (size--) count += write(*buffer++);
return count;
}
// size_t Print::print(const String &s)
// {
// uint8_t buffer[33];
// size_t count = 0;
// unsigned int index = 0;
// unsigned int len = s.length();
// while (len > 0) {
// s.getBytes(buffer, sizeof(buffer), index);
// unsigned int nbytes = len;
// if (nbytes > sizeof(buffer)-1) nbytes = sizeof(buffer)-1;
// index += nbytes;
// len -= nbytes;
// count += write(buffer, nbytes);
// }
// return count;
// }
size_t Print::print(long n)
{
uint8_t sign=0;
if (n < 0) {
sign = '-';
n = -n;
}
return printNumber(n, 10, sign);
}
size_t Print::println(void)
{
uint8_t buf[2]={'\r', '\n'};
return write(buf, 2);
}
extern "C" {
__attribute__((weak))
int _write(long file, char *ptr, int len)
{
((class Print *)file)->write((uint8_t *)ptr, len);
return len;
}
}
int Print::printf(const char *format, ...)
{
va_list ap;
va_start(ap, format);
#ifdef __STRICT_ANSI__
return 0; // TODO: make this work with -std=c++0x
#else
return vdprintf((int)this, format, ap);
#endif
}
int Print::printf(const __FlashStringHelper *format, ...)
{
va_list ap;
va_start(ap, format);
#ifdef __STRICT_ANSI__
return 0;
#else
return vdprintf((int)this, (const char *)format, ap);
#endif
}
#ifdef __MKL26Z64__
// optimized code inspired by Stimmer's optimization
// obviously a dit different, adapted to 32 bit Cortex-M0+
// http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679
// http://forum.arduino.cc/index.php?topic=167414.msg1309482#msg1309482
// equivelant code:
// mod = div % 10;
// div = div / 10;
// tmp1 = {random};
// tmp2 = 10;
#if 1
// https://forum.pjrc.com/threads/28932-LC-is-10-9-times-slower-than-T3-1?p=76072&viewfull=1#post76072
void inline divmod10_v2(uint32_t n,uint32_t *div,uint32_t *mod) {
uint32_t p,q;
/* Using 32.16 fixed point representation p.q */
/* p.q = (n+1)/512 */
q = (n&0xFFFF) + 1;
p = (n>>16);
/* p.q = 51*(n+1)/512 */
q = 13107*q;
p = 13107*p;
/* p.q = (1+1/2^8+1/2^16+1/2^24)*51*(n+1)/512 */
q = q + (q>>16) + (p&0xFFFF);
p = p + (p>>16) + (q>>16);
/* divide by 2 */
p = p>>1;
*div = p;
*mod = n-10*p;
}
#define divmod10_asm(div, mod, tmp1, tmp2, const3333) \
divmod10_v2(div, &div, &mod);
/*
#define divmod10_asm(div, mod, tmp1, tmp2, const3333) \
asm ( \
" lsr %2, %0, #16" "\n\t" \
" mul %2, %4" "\n\t" \
" uxth %1, %0" "\n\t" \
" mul %1, %4" "\n\t" \
" add %1, #1" "\n\t" \
" lsr %0, %2, #16" "\n\t" \
" lsl %2, %2, #16" "\n\t" \
" add %1, %2" "\n\t" \
" mov %3, #0" "\n\t" \
" adc %0, %3" "\n\t" \
" lsl %0, %0, #15" "\n\t" \
" lsr %2, %1, #17" "\n\t" \
" orr %0, %2" "\n\t" \
" lsl %1, %1, #15" "\n\t" \
" lsr %2, %1, #16" "\n\t" \
" lsl %3, %0, #16" "\n\t" \
" orr %2, %3" "\n\t" \
" lsr %3, %0, #16" "\n\t" \
" add %1, %0" "\n\t" \
" adc %0, %1" "\n\t" \
" sub %0, %1" "\n\t" \
" add %1, %2" "\n\t" \
" adc %0, %3" "\n\t" \
" lsr %1, %1, #4" "\n\t" \
" mov %3, #10" "\n\t" \
" mul %1, %3" "\n\t" \
" lsr %1, %1, #28" "\n\t" \
: "+l" (div), \
"=&l" (mod), \
"=&l" (tmp1), \
"=&l" (tmp2) \
: "l" (const3333) \
: \
)
*/
#else
#define divmod10_asm(_div, _mod, _tmp1, _tmp2, _const3333) \
({ _tmp1 = _div; _div = _div / 10; _mod = _tmp1 - _div * 10; })
// ({_mod = _div % 10, _div = _div / 10; })
#endif
size_t Print::printNumberDec(unsigned long n, uint8_t sign)
{
uint8_t buf[11], *p;
uint32_t digit;
//uint32_t t1, t2, c3333=0x3333;
p = buf + (sizeof(buf));
do {
uint32_t div;
divmod10_v2(n, &div, &digit);
n = div;
//divmod10_asm(n, digit, t1, t2, c3333);
*--p = digit + '0';
} while (n);
if (sign) *--p = '-';
return write(p, sizeof(buf) - (p - buf));
}
size_t Print::printNumberHex(unsigned long n)
{
uint8_t digit, buf[8], *p;
p = buf + (sizeof(buf));
do {
digit = n & 15;
*--p = (digit < 10) ? '0' + digit : 'A' + digit - 10;
n >>= 4;
} while (n);
return write(p, sizeof(buf) - (p - buf));
}
size_t Print::printNumberBin(unsigned long n)
{
uint8_t buf[32], *p;
p = buf + (sizeof(buf));
do {
*--p = '0' + ((uint8_t)n & 1);
n >>= 1;
} while (n);
return write(p, sizeof(buf) - (p - buf));
}
size_t Print::printNumberAny(unsigned long n, uint8_t base)
{
uint8_t digit, buf[21], *p;
uint32_t tmp;
p = buf + sizeof(buf);
do {
tmp = n;
n = n / base;
digit = tmp - n * base;
*--p = (digit < 10) ? '0' + digit : 'A' + digit - 10;
} while (n);
return write(p, sizeof(buf) - (p - buf));
}
#else
size_t Print::printNumber(unsigned long n, uint8_t base, uint8_t sign)
{
uint8_t buf[34];
uint8_t digit, i;
// TODO: make these checks as inline, since base is
// almost always a constant. base = 0 (BYTE) should
// inline as a call directly to write()
if (base == 0) {
return write((uint8_t)n);
} else if (base == 1) {
base = 10;
}
if (n == 0) {
buf[sizeof(buf) - 1] = '0';
i = sizeof(buf) - 1;
} else {
i = sizeof(buf) - 1;
while (1) {
digit = n % base;
buf[i] = ((digit < 10) ? '0' + digit : 'A' + digit - 10);
n /= base;
if (n == 0) break;
i--;
}
}
if (sign) {
i--;
buf[i] = '-';
}
return write(buf + i, sizeof(buf) - i);
}
#endif
size_t Print::printFloat(double number, uint8_t digits)
{
uint8_t sign=0;
size_t count=0;
if (isnan(number)) return print("nan");
if (isinf(number)) return print("inf");
if (number > 4294967040.0f) return print("ovf"); // constant determined empirically
if (number <-4294967040.0f) return print("ovf"); // constant determined empirically
// Handle negative numbers
if (number < 0.0) {
sign = 1;
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i) {
rounding *= 0.1;
}
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
count += printNumber(int_part, 10, sign);
// Print the decimal point, but only if there are digits beyond
if (digits > 0) {
uint8_t n, buf[16], count=1;
buf[0] = '.';
// Extract digits from the remainder one at a time
if (digits > sizeof(buf) - 1) digits = sizeof(buf) - 1;
while (digits-- > 0) {
remainder *= 10.0;
n = (uint8_t)(remainder);
buf[count++] = '0' + n;
remainder -= n;
}
count += write(buf, count);
}
return count;
}

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,3 @@
#include <cmath>
#include "FilterOnePole.cpp"
#include <cmath>
#include "FilterOnePole.cpp"

View file

@ -1,276 +1,276 @@
// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
// See gfxfont.h for newer custom bitmap font info.
#ifndef FONT5X7_H
#define FONT5X7_H
#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#else
#define PROGMEM
#endif
// Standard ASCII 5x7 font
static const unsigned char font[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
0x18, 0x3C, 0x7E, 0x3C, 0x18,
0x1C, 0x57, 0x7D, 0x57, 0x1C,
0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
0x00, 0x18, 0x3C, 0x18, 0x00,
0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
0x00, 0x18, 0x24, 0x18, 0x00,
0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26,
0x40, 0x7F, 0x05, 0x05, 0x07,
0x40, 0x7F, 0x05, 0x25, 0x3F,
0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
0x7F, 0x3E, 0x1C, 0x1C, 0x08,
0x08, 0x1C, 0x1C, 0x3E, 0x7F,
0x14, 0x22, 0x7F, 0x22, 0x14,
0x5F, 0x5F, 0x00, 0x5F, 0x5F,
0x06, 0x09, 0x7F, 0x01, 0x7F,
0x00, 0x66, 0x89, 0x95, 0x6A,
0x60, 0x60, 0x60, 0x60, 0x60,
0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08,
0x10, 0x20, 0x7E, 0x20, 0x10,
0x08, 0x08, 0x2A, 0x1C, 0x08,
0x08, 0x1C, 0x2A, 0x08, 0x08,
0x1E, 0x10, 0x10, 0x10, 0x10,
0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
0x30, 0x38, 0x3E, 0x38, 0x30,
0x06, 0x0E, 0x3E, 0x0E, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C,
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02,
0x3C, 0x26, 0x23, 0x26, 0x3C,
0x1E, 0xA1, 0xA1, 0x61, 0x12,
0x3A, 0x40, 0x40, 0x20, 0x7A,
0x38, 0x54, 0x54, 0x55, 0x59,
0x21, 0x55, 0x55, 0x79, 0x41,
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
0x21, 0x55, 0x54, 0x78, 0x40,
0x20, 0x54, 0x55, 0x79, 0x40,
0x0C, 0x1E, 0x52, 0x72, 0x12,
0x39, 0x55, 0x55, 0x55, 0x59,
0x39, 0x54, 0x54, 0x54, 0x59,
0x39, 0x55, 0x54, 0x54, 0x58,
0x00, 0x00, 0x45, 0x7C, 0x41,
0x00, 0x02, 0x45, 0x7D, 0x42,
0x00, 0x01, 0x45, 0x7C, 0x40,
0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
0xF0, 0x28, 0x25, 0x28, 0xF0,
0x7C, 0x54, 0x55, 0x45, 0x00,
0x20, 0x54, 0x54, 0x7C, 0x54,
0x7C, 0x0A, 0x09, 0x7F, 0x49,
0x32, 0x49, 0x49, 0x49, 0x32,
0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
0x32, 0x4A, 0x48, 0x48, 0x30,
0x3A, 0x41, 0x41, 0x21, 0x7A,
0x3A, 0x42, 0x40, 0x20, 0x78,
0x00, 0x9D, 0xA0, 0xA0, 0x7D,
0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
0x3D, 0x40, 0x40, 0x40, 0x3D,
0x3C, 0x24, 0xFF, 0x24, 0x24,
0x48, 0x7E, 0x49, 0x43, 0x66,
0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
0xFF, 0x09, 0x29, 0xF6, 0x20,
0xC0, 0x88, 0x7E, 0x09, 0x03,
0x20, 0x54, 0x54, 0x79, 0x41,
0x00, 0x00, 0x44, 0x7D, 0x41,
0x30, 0x48, 0x48, 0x4A, 0x32,
0x38, 0x40, 0x40, 0x22, 0x7A,
0x00, 0x7A, 0x0A, 0x0A, 0x72,
0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28,
0x26, 0x29, 0x29, 0x29, 0x26,
0x30, 0x48, 0x4D, 0x40, 0x20,
0x38, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x38,
0x2F, 0x10, 0xC8, 0xAC, 0xBA,
0x2F, 0x10, 0x28, 0x34, 0xFA,
0x00, 0x00, 0x7B, 0x00, 0x00,
0x08, 0x14, 0x2A, 0x14, 0x22,
0x22, 0x14, 0x2A, 0x14, 0x08,
0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
0x00, 0x00, 0x00, 0xFF, 0x00,
0x10, 0x10, 0x10, 0xFF, 0x00,
0x14, 0x14, 0x14, 0xFF, 0x00,
0x10, 0x10, 0xFF, 0x00, 0xFF,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x14, 0x14, 0x14, 0xFC, 0x00,
0x14, 0x14, 0xF7, 0x00, 0xFF,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x14, 0x14, 0xF4, 0x04, 0xFC,
0x14, 0x14, 0x17, 0x10, 0x1F,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x10,
0x10, 0x10, 0x10, 0x1F, 0x10,
0x10, 0x10, 0x10, 0xF0, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0xFF, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x14,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x1F, 0x10, 0x17,
0x00, 0x00, 0xFC, 0x04, 0xF4,
0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4,
0x00, 0x00, 0xFF, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0xF7, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x17, 0x14,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0xF4, 0x14,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x00, 0x00, 0x1F, 0x10, 0x1F,
0x00, 0x00, 0x00, 0x1F, 0x14,
0x00, 0x00, 0x00, 0xFC, 0x14,
0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF,
0x14, 0x14, 0x14, 0xFF, 0x14,
0x10, 0x10, 0x10, 0x1F, 0x00,
0x00, 0x00, 0x00, 0xF0, 0x10,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x38, 0x44, 0x44, 0x38, 0x44,
0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
0x7E, 0x02, 0x02, 0x06, 0x06,
0x02, 0x7E, 0x02, 0x7E, 0x02,
0x63, 0x55, 0x49, 0x41, 0x63,
0x38, 0x44, 0x44, 0x3C, 0x04,
0x40, 0x7E, 0x20, 0x1E, 0x20,
0x06, 0x02, 0x7E, 0x02, 0x02,
0x99, 0xA5, 0xE7, 0xA5, 0x99,
0x1C, 0x2A, 0x49, 0x2A, 0x1C,
0x4C, 0x72, 0x01, 0x72, 0x4C,
0x30, 0x4A, 0x4D, 0x4D, 0x30,
0x30, 0x48, 0x78, 0x48, 0x30,
0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00,
0x7E, 0x01, 0x01, 0x01, 0x7E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x44, 0x44, 0x5F, 0x44, 0x44,
0x40, 0x51, 0x4A, 0x44, 0x40,
0x40, 0x44, 0x4A, 0x51, 0x40,
0x00, 0x00, 0xFF, 0x01, 0x03,
0xE0, 0x80, 0xFF, 0x00, 0x00,
0x08, 0x08, 0x6B, 0x6B, 0x08,
0x36, 0x12, 0x36, 0x24, 0x36,
0x06, 0x0F, 0x09, 0x0F, 0x06,
0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00,
0x30, 0x40, 0xFF, 0x01, 0x01,
0x00, 0x1F, 0x01, 0x01, 0x1E,
0x00, 0x19, 0x1D, 0x17, 0x12,
0x00, 0x3C, 0x3C, 0x3C, 0x3C,
0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
};
#endif // FONT5X7_H
// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
// See gfxfont.h for newer custom bitmap font info.
#ifndef FONT5X7_H
#define FONT5X7_H
#ifdef __AVR__
#include <avr/io.h>
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#else
#define PROGMEM
#endif
// Standard ASCII 5x7 font
static const unsigned char font[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00,
0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
0x18, 0x3C, 0x7E, 0x3C, 0x18,
0x1C, 0x57, 0x7D, 0x57, 0x1C,
0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
0x00, 0x18, 0x3C, 0x18, 0x00,
0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
0x00, 0x18, 0x24, 0x18, 0x00,
0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26,
0x40, 0x7F, 0x05, 0x05, 0x07,
0x40, 0x7F, 0x05, 0x25, 0x3F,
0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
0x7F, 0x3E, 0x1C, 0x1C, 0x08,
0x08, 0x1C, 0x1C, 0x3E, 0x7F,
0x14, 0x22, 0x7F, 0x22, 0x14,
0x5F, 0x5F, 0x00, 0x5F, 0x5F,
0x06, 0x09, 0x7F, 0x01, 0x7F,
0x00, 0x66, 0x89, 0x95, 0x6A,
0x60, 0x60, 0x60, 0x60, 0x60,
0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08,
0x10, 0x20, 0x7E, 0x20, 0x10,
0x08, 0x08, 0x2A, 0x1C, 0x08,
0x08, 0x1C, 0x2A, 0x08, 0x08,
0x1E, 0x10, 0x10, 0x10, 0x10,
0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
0x30, 0x38, 0x3E, 0x38, 0x30,
0x06, 0x0E, 0x3E, 0x0E, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C,
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02,
0x3C, 0x26, 0x23, 0x26, 0x3C,
0x1E, 0xA1, 0xA1, 0x61, 0x12,
0x3A, 0x40, 0x40, 0x20, 0x7A,
0x38, 0x54, 0x54, 0x55, 0x59,
0x21, 0x55, 0x55, 0x79, 0x41,
0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
0x21, 0x55, 0x54, 0x78, 0x40,
0x20, 0x54, 0x55, 0x79, 0x40,
0x0C, 0x1E, 0x52, 0x72, 0x12,
0x39, 0x55, 0x55, 0x55, 0x59,
0x39, 0x54, 0x54, 0x54, 0x59,
0x39, 0x55, 0x54, 0x54, 0x58,
0x00, 0x00, 0x45, 0x7C, 0x41,
0x00, 0x02, 0x45, 0x7D, 0x42,
0x00, 0x01, 0x45, 0x7C, 0x40,
0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
0xF0, 0x28, 0x25, 0x28, 0xF0,
0x7C, 0x54, 0x55, 0x45, 0x00,
0x20, 0x54, 0x54, 0x7C, 0x54,
0x7C, 0x0A, 0x09, 0x7F, 0x49,
0x32, 0x49, 0x49, 0x49, 0x32,
0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
0x32, 0x4A, 0x48, 0x48, 0x30,
0x3A, 0x41, 0x41, 0x21, 0x7A,
0x3A, 0x42, 0x40, 0x20, 0x78,
0x00, 0x9D, 0xA0, 0xA0, 0x7D,
0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
0x3D, 0x40, 0x40, 0x40, 0x3D,
0x3C, 0x24, 0xFF, 0x24, 0x24,
0x48, 0x7E, 0x49, 0x43, 0x66,
0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
0xFF, 0x09, 0x29, 0xF6, 0x20,
0xC0, 0x88, 0x7E, 0x09, 0x03,
0x20, 0x54, 0x54, 0x79, 0x41,
0x00, 0x00, 0x44, 0x7D, 0x41,
0x30, 0x48, 0x48, 0x4A, 0x32,
0x38, 0x40, 0x40, 0x22, 0x7A,
0x00, 0x7A, 0x0A, 0x0A, 0x72,
0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28,
0x26, 0x29, 0x29, 0x29, 0x26,
0x30, 0x48, 0x4D, 0x40, 0x20,
0x38, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x38,
0x2F, 0x10, 0xC8, 0xAC, 0xBA,
0x2F, 0x10, 0x28, 0x34, 0xFA,
0x00, 0x00, 0x7B, 0x00, 0x00,
0x08, 0x14, 0x2A, 0x14, 0x22,
0x22, 0x14, 0x2A, 0x14, 0x08,
0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
0x00, 0x00, 0x00, 0xFF, 0x00,
0x10, 0x10, 0x10, 0xFF, 0x00,
0x14, 0x14, 0x14, 0xFF, 0x00,
0x10, 0x10, 0xFF, 0x00, 0xFF,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x14, 0x14, 0x14, 0xFC, 0x00,
0x14, 0x14, 0xF7, 0x00, 0xFF,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x14, 0x14, 0xF4, 0x04, 0xFC,
0x14, 0x14, 0x17, 0x10, 0x1F,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x10,
0x10, 0x10, 0x10, 0x1F, 0x10,
0x10, 0x10, 0x10, 0xF0, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0xFF, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x14,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x1F, 0x10, 0x17,
0x00, 0x00, 0xFC, 0x04, 0xF4,
0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4,
0x00, 0x00, 0xFF, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0xF7, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x17, 0x14,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0xF4, 0x14,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x00, 0x00, 0x1F, 0x10, 0x1F,
0x00, 0x00, 0x00, 0x1F, 0x14,
0x00, 0x00, 0x00, 0xFC, 0x14,
0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF,
0x14, 0x14, 0x14, 0xFF, 0x14,
0x10, 0x10, 0x10, 0x1F, 0x00,
0x00, 0x00, 0x00, 0xF0, 0x10,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x38, 0x44, 0x44, 0x38, 0x44,
0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
0x7E, 0x02, 0x02, 0x06, 0x06,
0x02, 0x7E, 0x02, 0x7E, 0x02,
0x63, 0x55, 0x49, 0x41, 0x63,
0x38, 0x44, 0x44, 0x3C, 0x04,
0x40, 0x7E, 0x20, 0x1E, 0x20,
0x06, 0x02, 0x7E, 0x02, 0x02,
0x99, 0xA5, 0xE7, 0xA5, 0x99,
0x1C, 0x2A, 0x49, 0x2A, 0x1C,
0x4C, 0x72, 0x01, 0x72, 0x4C,
0x30, 0x4A, 0x4D, 0x4D, 0x30,
0x30, 0x48, 0x78, 0x48, 0x30,
0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00,
0x7E, 0x01, 0x01, 0x01, 0x7E,
0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x44, 0x44, 0x5F, 0x44, 0x44,
0x40, 0x51, 0x4A, 0x44, 0x40,
0x40, 0x44, 0x4A, 0x51, 0x40,
0x00, 0x00, 0xFF, 0x01, 0x03,
0xE0, 0x80, 0xFF, 0x00, 0x00,
0x08, 0x08, 0x6B, 0x6B, 0x08,
0x36, 0x12, 0x36, 0x24, 0x36,
0x06, 0x0F, 0x09, 0x0F, 0x06,
0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00,
0x30, 0x40, 0xFF, 0x01, 0x01,
0x00, 0x1F, 0x01, 0x01, 0x1E,
0x00, 0x19, 0x1D, 0x17, 0x12,
0x00, 0x3C, 0x3C, 0x3C, 0x3C,
0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
};
#endif // FONT5X7_H

File diff suppressed because it is too large Load diff

View file

@ -1,124 +1,124 @@
#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include "EEPROM.h"
// TODO: Fake eeprom a bit better, maybe even save to file.
EEPROMClass::EEPROMClass() {
memset(someFakeEEPROM_memory, 0xff, sizeof(someFakeEEPROM_memory));
storage = NULL;
autoUpdate = false;
}
uint8_t EEPROMClass::read( int idx )
{
printf("Reading EEPROM address %u: %u\n", idx, 0xff&someFakeEEPROM_memory[idx]);
return someFakeEEPROM_memory[idx];
}
void EEPROMClass::write( int idx, uint8_t val )
{
printf("Writing to EEPROM address %u = %u\n", idx, val);
if(val == someFakeEEPROM_memory[idx])
{
//Value unchanged, do nothing.
return;
}
someFakeEEPROM_memory[idx] = val;
if(autoUpdate && storage)
{
fseek(storage, idx, SEEK_SET);
fputc(val, storage);
fflush(storage);
}
}
void EEPROMClass::update( int idx, uint8_t val )
{
write(idx, val);
}
uint16_t EEPROMClass::length()
{
return sizeof(someFakeEEPROM_memory);
}
int16_t EEPROMClass::setStorage(const char* filename, bool write)
{
//Close any open storage file
if(storage)
{
fclose(storage);
storage = NULL;
}
autoUpdate = write;
storage = fopen(filename, "rb");
//If only reading, fail if file does not exist (makes no sense otherwise)
if(!storage && !autoUpdate) {
printf("Could not open EEPROM storage file: '%s'\n", filename);
return -1;
}
if(storage)
{
printf("Reading EEPROM storage file: '%s'\n", filename);
rewind(storage);
fread(someFakeEEPROM_memory, sizeof(someFakeEEPROM_memory), 1, storage);
}
if(!autoUpdate)
{
//No need for the file anymore, close it
fclose(storage);
storage = NULL;
}
//Create file if it doesn't exist (so we can write to it)
if(!storage && autoUpdate)
{
storage = fopen(filename, "wb");
if(!storage)
{
printf("Could not create EEPROM storage file: '%s'\n", filename);
autoUpdate = false;
return -2;
}
}
if(storage && autoUpdate)
{
//Reopen file for writing without overwriting it
storage = freopen(filename, "r+b", storage);
if(!storage)
{
printf("Could not access EEPROM storage file for writing: '%s'\n", filename);
autoUpdate = false;
return -3;
}
printf("Writing any EEPROM changes to '%s'\n", filename);
}
return 0;
}
void EEPROMClass::closeStorage() {
if(storage!=NULL)
{
fclose(storage);
storage=NULL;
}
}
#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include "EEPROM.h"
// TODO: Fake eeprom a bit better, maybe even save to file.
EEPROMClass::EEPROMClass() {
memset(someFakeEEPROM_memory, 0xff, sizeof(someFakeEEPROM_memory));
storage = NULL;
autoUpdate = false;
}
uint8_t EEPROMClass::read( int idx )
{
printf("Reading EEPROM address %u: %u\n", idx, 0xff&someFakeEEPROM_memory[idx]);
return someFakeEEPROM_memory[idx];
}
void EEPROMClass::write( int idx, uint8_t val )
{
printf("Writing to EEPROM address %u = %u\n", idx, val);
if(val == someFakeEEPROM_memory[idx])
{
//Value unchanged, do nothing.
return;
}
someFakeEEPROM_memory[idx] = val;
if(autoUpdate && storage)
{
fseek(storage, idx, SEEK_SET);
fputc(val, storage);
fflush(storage);
}
}
void EEPROMClass::update( int idx, uint8_t val )
{
write(idx, val);
}
uint16_t EEPROMClass::length()
{
return sizeof(someFakeEEPROM_memory);
}
int16_t EEPROMClass::setStorage(const char* filename, bool write)
{
//Close any open storage file
if(storage)
{
fclose(storage);
storage = NULL;
}
autoUpdate = write;
storage = fopen(filename, "rb");
//If only reading, fail if file does not exist (makes no sense otherwise)
if(!storage && !autoUpdate) {
printf("Could not open EEPROM storage file: '%s'\n", filename);
return -1;
}
if(storage)
{
printf("Reading EEPROM storage file: '%s'\n", filename);
rewind(storage);
fread(someFakeEEPROM_memory, sizeof(someFakeEEPROM_memory), 1, storage);
}
if(!autoUpdate)
{
//No need for the file anymore, close it
fclose(storage);
storage = NULL;
}
//Create file if it doesn't exist (so we can write to it)
if(!storage && autoUpdate)
{
storage = fopen(filename, "wb");
if(!storage)
{
printf("Could not create EEPROM storage file: '%s'\n", filename);
autoUpdate = false;
return -2;
}
}
if(storage && autoUpdate)
{
//Reopen file for writing without overwriting it
storage = freopen(filename, "r+b", storage);
if(!storage)
{
printf("Could not access EEPROM storage file for writing: '%s'\n", filename);
autoUpdate = false;
return -3;
}
printf("Writing any EEPROM changes to '%s'\n", filename);
}
return 0;
}
void EEPROMClass::closeStorage() {
if(storage!=NULL)
{
fclose(storage);
storage=NULL;
}
}

View file

@ -1,55 +1,55 @@
#include <cstdint>
#include <cstdio>
#include "Arduino.h"
/********************************
*
*/
void SimSerial::begin(uint32_t)
{
}
void SimSerial::print(const char* str)
{
printf( "[Serial::print] %s\n", str );
}
void SimSerial::print(uint32_t intValue)
{
printf( "[Serial::print] %d\n", intValue );
}
void SimSerial::println()
{
printf("\n");
}
void SimSerial::println(uint32_t intValue)
{
printf( "[Serial::println] %d\n", intValue );
}
void SimSerial::println(const char *str)
{
printf( "[Serial::println] %s\n", str );
}
//Used to send serial midi
void SimSerial::write(const uint8_t __unused str)
{
}
void SimSerial::flush()
{
}
#include <cstdint>
#include <cstdio>
#include "Arduino.h"
/********************************
*
*/
void SimSerial::begin(uint32_t)
{
}
void SimSerial::print(const char* str)
{
printf( "[Serial::print] %s\n", str );
}
void SimSerial::print(uint32_t intValue)
{
printf( "[Serial::print] %d\n", intValue );
}
void SimSerial::println()
{
printf("\n");
}
void SimSerial::println(uint32_t intValue)
{
printf( "[Serial::println] %d\n", intValue );
}
void SimSerial::println(const char *str)
{
printf( "[Serial::println] %s\n", str );
}
//Used to send serial midi
void SimSerial::write(const uint8_t __unused str)
{
}
void SimSerial::flush()
{
}

View file

@ -1,145 +1,145 @@
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <fstream>
#include "simusbmidi.h"
/*************************************
* Stub simulation of Teensy usbMidi
*/
void SimUsbMidi::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::noteOff] note %03d vel %03d ch %02d\n", note, velocity, channel);
}
void SimUsbMidi::sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::noteOn] note %03d vel %03d ch %02d\n", note, velocity, channel);
}
void SimUsbMidi::sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::polyPressure] note %03d p %03d ch %02d\n", note, pressure, channel);
}
void SimUsbMidi::sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::afterTouchPoly] note %03d p %03d ch %02d\n", note, pressure, channel);
}
void SimUsbMidi::sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::controlChange] cc %03d val %03d ch %02d\n", control, value, channel);
}
void SimUsbMidi::sendProgramChange(uint8_t program, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::programChange] prg %03d ch %02d\n", program, channel);
}
void SimUsbMidi::sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::afterTouch] p %03d ch %02d\n", pressure, channel);
}
void SimUsbMidi::sendPitchBend(int value, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::pitchBend] pb %05d ch %02d\n", value, channel);
}
void SimUsbMidi::sendSysEx(uint16_t length, const uint8_t *data, bool __unused hasTerm, uint8_t __unused cable)
{
printf( "[usbMIDI::sysEx] Sending %d bytes\n", length);
for(int i=0; i<length; i++) {
printf("%02x%c", data[i], (i==length-1)?'\n':':');
}
}
//Set a low chunk size on purpose just to let the receiver work for it
#define MIDI_SYSEX_CHUNK_SIZE 32
bool SimUsbMidi::read(uint8_t __unused channel) {
if(this->sendMidi) {
printf("[SimUsbMidi::read] Attempting to send midi data\n");
std::ifstream file(this->midiFile, std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
uint8_t *buffer = (uint8_t*)malloc(size);
if (file.read((char*)buffer, size))
{
printf("[SimUsbMidi::read] Sending %lu bytes.\n", size);
this->receiveMidiData(buffer, size);
}
free(buffer);
this->sendMidi = false;
}
return false;
}
//Provide midi data for simulation to receive
void SimUsbMidi::receiveMidiData(const uint8_t *data, const uint16_t length) {
if(length==0) return; //There is no data, what's even the point
uint8_t midi_message = data[0]; //First byte of data
if(midi_message != 0xF0) return; //Only sysex data supported (no other handlers available)
if(this->usb_midi_handleSysExPartial) {
//Chunked sysex receiver set, use that.
if(length<=MIDI_SYSEX_CHUNK_SIZE) {
//Send all in one go
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExPartial(complete) %d B\n", length);
(this->usb_midi_handleSysExPartial)(data, length, true);
} else {
uint8_t* buf = (uint8_t*)malloc(MIDI_SYSEX_CHUNK_SIZE);
int pos=0;
while(pos<length) {
int remaining = length-pos;
int bytesToSend = std::min(remaining, MIDI_SYSEX_CHUNK_SIZE);
bool complete = (bytesToSend == remaining);
memcpy(buf, data+pos, bytesToSend);
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExPartial(complete: %d) %d B\n", complete, bytesToSend);
(this->usb_midi_handleSysExPartial)(buf, bytesToSend, complete);
pos=pos+bytesToSend;
}
free(buf);
}
} else if(this->usb_midi_handleSysExComplete) {
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExComplete() %d B\n", length);
(this->usb_midi_handleSysExComplete)(data, length);
} else {
//Nobody listening
}
}
//MIDI SysEx handlers. Choice of data types is a bit odd, but done to match Arduino/Teensy libraries
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size)) {
this->usb_midi_handleSysExComplete = fptr;
}
//"Chunked" sysex handler (teensy extension), for large messages
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, uint16_t size, bool last)) {
this->usb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
}
void SimUsbMidi::setMidiFile(std::string filename) {
this->midiFile = filename;
}
void SimUsbMidi::triggerMidi() {
this->sendMidi = true;
}
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <fstream>
#include "simusbmidi.h"
/*************************************
* Stub simulation of Teensy usbMidi
*/
void SimUsbMidi::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::noteOff] note %03d vel %03d ch %02d\n", note, velocity, channel);
}
void SimUsbMidi::sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::noteOn] note %03d vel %03d ch %02d\n", note, velocity, channel);
}
void SimUsbMidi::sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::polyPressure] note %03d p %03d ch %02d\n", note, pressure, channel);
}
void SimUsbMidi::sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::afterTouchPoly] note %03d p %03d ch %02d\n", note, pressure, channel);
}
void SimUsbMidi::sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::controlChange] cc %03d val %03d ch %02d\n", control, value, channel);
}
void SimUsbMidi::sendProgramChange(uint8_t program, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::programChange] prg %03d ch %02d\n", program, channel);
}
void SimUsbMidi::sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::afterTouch] p %03d ch %02d\n", pressure, channel);
}
void SimUsbMidi::sendPitchBend(int value, uint8_t channel, uint8_t __unused cable)
{
printf( "[usbMIDI::pitchBend] pb %05d ch %02d\n", value, channel);
}
void SimUsbMidi::sendSysEx(uint16_t length, const uint8_t *data, bool __unused hasTerm, uint8_t __unused cable)
{
printf( "[usbMIDI::sysEx] Sending %d bytes\n", length);
for(int i=0; i<length; i++) {
printf("%02x%c", data[i], (i==length-1)?'\n':':');
}
}
//Set a low chunk size on purpose just to let the receiver work for it
#define MIDI_SYSEX_CHUNK_SIZE 32
bool SimUsbMidi::read(uint8_t __unused channel) {
if(this->sendMidi) {
printf("[SimUsbMidi::read] Attempting to send midi data\n");
std::ifstream file(this->midiFile, std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
uint8_t *buffer = (uint8_t*)malloc(size);
if (file.read((char*)buffer, size))
{
printf("[SimUsbMidi::read] Sending %lu bytes.\n", size);
this->receiveMidiData(buffer, size);
}
free(buffer);
this->sendMidi = false;
}
return false;
}
//Provide midi data for simulation to receive
void SimUsbMidi::receiveMidiData(const uint8_t *data, const uint16_t length) {
if(length==0) return; //There is no data, what's even the point
uint8_t midi_message = data[0]; //First byte of data
if(midi_message != 0xF0) return; //Only sysex data supported (no other handlers available)
if(this->usb_midi_handleSysExPartial) {
//Chunked sysex receiver set, use that.
if(length<=MIDI_SYSEX_CHUNK_SIZE) {
//Send all in one go
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExPartial(complete) %d B\n", length);
(this->usb_midi_handleSysExPartial)(data, length, true);
} else {
uint8_t* buf = (uint8_t*)malloc(MIDI_SYSEX_CHUNK_SIZE);
int pos=0;
while(pos<length) {
int remaining = length-pos;
int bytesToSend = std::min(remaining, MIDI_SYSEX_CHUNK_SIZE);
bool complete = (bytesToSend == remaining);
memcpy(buf, data+pos, bytesToSend);
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExPartial(complete: %d) %d B\n", complete, bytesToSend);
(this->usb_midi_handleSysExPartial)(buf, bytesToSend, complete);
pos=pos+bytesToSend;
}
free(buf);
}
} else if(this->usb_midi_handleSysExComplete) {
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExComplete() %d B\n", length);
(this->usb_midi_handleSysExComplete)(data, length);
} else {
//Nobody listening
}
}
//MIDI SysEx handlers. Choice of data types is a bit odd, but done to match Arduino/Teensy libraries
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size)) {
this->usb_midi_handleSysExComplete = fptr;
}
//"Chunked" sysex handler (teensy extension), for large messages
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, uint16_t size, bool last)) {
this->usb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
}
void SimUsbMidi::setMidiFile(std::string filename) {
this->midiFile = filename;
}
void SimUsbMidi::triggerMidi() {
this->sendMidi = true;
}

View file

@ -1,94 +1,94 @@
#include <stdio.h>
#include "Wire.h"
/********************************
*
*/
SimWire::SimWire( bool verbose )
: verbose_( verbose )
{
}
void SimWire::setClock(uint32_t)
{
// Ignore.. lol
}
void SimWire::begin()
{
}
void SimWire::beginTransmission(uint8_t address)
{
if( verbose_ )
printf("[SimWire::beginTransmission] $%02x\n", address);
}
void SimWire::beginTransmission(int address)
{
beginTransmission((uint8_t)address);
}
uint8_t SimWire::endTransmission()
{
if( verbose_ )
printf("[SimWire::endTransmission]\n");
return 0;
}
uint8_t SimWire::endTransmission(uint8_t what)
{
if( verbose_ )
printf("[SimWire::endTransmission %d]\n", what);
return 0;
}
uint8_t SimWire::requestFrom(uint8_t address, uint8_t count)
{
if( verbose_ )
printf("[SimWire::requestFrom] $%02x for %d bytes\n", address, count);
// TODO: We must check if there is an actual slave for that address.
return 0;
}
int SimWire::read()
{
// TODO: Verify that bus is in read mode.
// if( current_slave_ != NULL ) {
// return current_slave_->i2cReadData( );
// } else {
printf("No slave selected, returning ones\n");
return 0xffu;
// }
}
int SimWire::available()
{
// TODO: This needs to be implemented!!
return 0;
}
size_t SimWire::write(uint8_t __attribute__((unused)) data)
{
// // TODO: Verify that bus is in write mode.
// if( current_slave_ != NULL ) {
// current_slave_->i2cWriteData( data );
// } else {
printf("No slave selected i2c writes to the void.\n");
// }
return 1;
}
#include <stdio.h>
#include "Wire.h"
/********************************
*
*/
SimWire::SimWire( bool verbose )
: verbose_( verbose )
{
}
void SimWire::setClock(uint32_t)
{
// Ignore.. lol
}
void SimWire::begin()
{
}
void SimWire::beginTransmission(uint8_t address)
{
if( verbose_ )
printf("[SimWire::beginTransmission] $%02x\n", address);
}
void SimWire::beginTransmission(int address)
{
beginTransmission((uint8_t)address);
}
uint8_t SimWire::endTransmission()
{
if( verbose_ )
printf("[SimWire::endTransmission]\n");
return 0;
}
uint8_t SimWire::endTransmission(uint8_t what)
{
if( verbose_ )
printf("[SimWire::endTransmission %d]\n", what);
return 0;
}
uint8_t SimWire::requestFrom(uint8_t address, uint8_t count)
{
if( verbose_ )
printf("[SimWire::requestFrom] $%02x for %d bytes\n", address, count);
// TODO: We must check if there is an actual slave for that address.
return 0;
}
int SimWire::read()
{
// TODO: Verify that bus is in read mode.
// if( current_slave_ != NULL ) {
// return current_slave_->i2cReadData( );
// } else {
printf("No slave selected, returning ones\n");
return 0xffu;
// }
}
int SimWire::available()
{
// TODO: This needs to be implemented!!
return 0;
}
size_t SimWire::write(uint8_t __attribute__((unused)) data)
{
// // TODO: Verify that bus is in write mode.
// if( current_slave_ != NULL ) {
// current_slave_->i2cWriteData( data );
// } else {
printf("No slave selected i2c writes to the void.\n");
// }
return 1;
}

View file

@ -1,73 +1,73 @@
NuEVI and NuRAD Firmware 1.5b8 Notes
1.5b1:
* Improved polyphonic play functions. Rotator menu is now replaced with a Poly Play menu where you find a variety of harmonization options, including three rotator setups. (Se separate manual page).
* Automatic sensor calibration when performing factory restore (so do mind that you dont touch sensors when you do the reset).
* I2C communications speed issue solved.
* Adjustable MIDI CC time intervals for the breath messages, from 3ms to 15ms. For wireless play, lower setting than the default 6ms is not recommended. (BR INTERV setting in SETUP BR menu).
* Improved touch sensor reading equalization for NuRAD, for more coherent sensitivity between keys.
* Minor fix for NuRAD SAX fingering (LH2+RH2 now plays Bb).
* Gate hold function now works on NuRAD too.
1.5b2:
* Adjustment setting for thumb lever (for optimizing thumb portamento control). By setting THR (level of push force to activate) and MAX (level of push force to achieve maximum set portamento) values close to eachother at the desired point of activation, the new glide limit setting to desired rate will create a switching type set rate controller (similar to Crumar EVI glide key). With THR and MAX setup with separation to taste, a continous control up to level set by glide limit is achieved.
* Glide setting SWO, SWitching Only, sending only Glide on/off (CC#65) for use with synths where glide rate CC#5 is used in non standard ways, for example some DSI/Sequential synths like the Prophet REV2 or Prophet 12 where glide rates are set individually for each oscillator.
* Glide limit setting for portamento. Doubles as setting for portamento level sent using pinky key/mod key in GLD mode. (Can be changed both in menu and in GLD mode.)
* Setting of level for LVL, LVP and GLD now reqires touching both pinky/mod and third trill/RHp3 for setting mode activation, this to avoid accidental change of setting when pinky/mod key is touched.
* Rate of setting movement up and down for LVL, LVP and GLD has been adjusted. Became very much too fast after the timing issues were solved in 1.5b1
* A short delay before note offs in legato transitions is added to make playback of recorded midi behave correctly (keeping note on and note off from being registered on the same timestamp).
1.5b3:
* AUTOCAL is available for each ADJUST menu page. With cursor on THR or MAX bar, press UP and DOWN buttons at the same time (and keep off the sensors being calibrated). AUTOCAL will be displayed for two seconds, then updated settings will be shown.
1.5b4:
* Changed the destination setup for bite and lever to be less confusing and more powerful (custom CCs now possible). The VIB CTL item in the VIBRATO menu is now removed, and in SETUP CTL menu you will find BITE CTL, BITE CC, LEVER CTL and LEVER CC.
BITE CTL and LEVER CTL can both be set to any of these destinations:
OFF - no destination active for this controller
VIB - vibrato
GLD - glide/portamento (defined by GLIDE MOD and GLIDE LMT settings)
CC - custom CC output (defined in GLIDE CC and LEVER CC settings)
Settings in GLIDE CC and LEVER CC are only used when corresponding control is set to CC in its CTL setting as described above.
1.5b5:
* Changed names for ADJUST page titles (to shorten) and added numerical value readouts for THR and MAX settings.
* Corrected start position for rotators and added reset to first position when activated and on roller release (from top five octaves like for the otf key change).
1.5b6
* Added EVR to NuEVI fingering menu, for reversed roller action.
* Rebooting to program mode now possible when stuck at boot due to wrong version of firmware installed (NuEVI vs NuRAD version).
* Removed possibility to change portamento limit using pinky/mod key when in GLD setting (it was always getting altered in play or handling). This might come back if I find a better way to do it.
* Support for a CME WIDI Master Bluetooth MIDI board connected to Teensy underside pads. With this mod, if present it will be powered up by touching pinky/mod key and pressing enter in MIDI meny (what previously controlled the "slow midi" function that now is replaced by the CC interval menu item). "WIDI" will be indicated in the display below the MIDI channel number and the WIDI board will be powered up. Teensy pad connections: GND to GND (blue) of WIDI, 31 (TX2) via 47 ohm resistor to signal (green) of WIDI, 33 via 47 ohm resistor to power (red) of WIDI. 27 and 28 jumpered to indicate that the WIDI is connected.
1.5b7:
* Added three new settings in EXTRAS MENU CV TUNE, CV SCALE and CV EC LFO. The first two allow for software tuning of the CV output for 1V/Oct when using NuEVI CV, NuEVI Plus or NuEVI/NuRAD with external CV box or module. Also makes more simple versions of the CV boards possible (no potentiometers for adjustment). The CV EC LFO setting controls a new CV LFO vibrato function for the extra controller (lip sensor). It can be set to OFF (no extra controller LFO vibrato) or values 1 through 8, which represent vibrato freq from 4.5Hz to 8Hz. Default value is 3 (5.5Hz).
1.5b8:
* New quarter tone setting for Pinky Key (NuEVI) and Mod Key (NuRAD) QTN. This makes the Pinky/Mod key a key for playing one quarter tone down using pitchbend (MIDI) or directly affecting the built in note CV output (using CV module or NuEVI CV/Plus). Pitch bend range for MIDI synth needs to be two semitones up/dn (or compensated for in pitch bend divider setting in the controller to make the resulting range two semitones up/dn). On NuRAD, it is recommended to set the 3rd LH Pinky key (EXTRA PKEY setting) to mirror MOD key for playablility.
NuEVI and NuRAD Firmware 1.5b8 Notes
1.5b1:
* Improved polyphonic play functions. Rotator menu is now replaced with a Poly Play menu where you find a variety of harmonization options, including three rotator setups. (Se separate manual page).
* Automatic sensor calibration when performing factory restore (so do mind that you dont touch sensors when you do the reset).
* I2C communications speed issue solved.
* Adjustable MIDI CC time intervals for the breath messages, from 3ms to 15ms. For wireless play, lower setting than the default 6ms is not recommended. (BR INTERV setting in SETUP BR menu).
* Improved touch sensor reading equalization for NuRAD, for more coherent sensitivity between keys.
* Minor fix for NuRAD SAX fingering (LH2+RH2 now plays Bb).
* Gate hold function now works on NuRAD too.
1.5b2:
* Adjustment setting for thumb lever (for optimizing thumb portamento control). By setting THR (level of push force to activate) and MAX (level of push force to achieve maximum set portamento) values close to eachother at the desired point of activation, the new glide limit setting to desired rate will create a switching type set rate controller (similar to Crumar EVI glide key). With THR and MAX setup with separation to taste, a continous control up to level set by glide limit is achieved.
* Glide setting SWO, SWitching Only, sending only Glide on/off (CC#65) for use with synths where glide rate CC#5 is used in non standard ways, for example some DSI/Sequential synths like the Prophet REV2 or Prophet 12 where glide rates are set individually for each oscillator.
* Glide limit setting for portamento. Doubles as setting for portamento level sent using pinky key/mod key in GLD mode. (Can be changed both in menu and in GLD mode.)
* Setting of level for LVL, LVP and GLD now reqires touching both pinky/mod and third trill/RHp3 for setting mode activation, this to avoid accidental change of setting when pinky/mod key is touched.
* Rate of setting movement up and down for LVL, LVP and GLD has been adjusted. Became very much too fast after the timing issues were solved in 1.5b1
* A short delay before note offs in legato transitions is added to make playback of recorded midi behave correctly (keeping note on and note off from being registered on the same timestamp).
1.5b3:
* AUTOCAL is available for each ADJUST menu page. With cursor on THR or MAX bar, press UP and DOWN buttons at the same time (and keep off the sensors being calibrated). AUTOCAL will be displayed for two seconds, then updated settings will be shown.
1.5b4:
* Changed the destination setup for bite and lever to be less confusing and more powerful (custom CCs now possible). The VIB CTL item in the VIBRATO menu is now removed, and in SETUP CTL menu you will find BITE CTL, BITE CC, LEVER CTL and LEVER CC.
BITE CTL and LEVER CTL can both be set to any of these destinations:
OFF - no destination active for this controller
VIB - vibrato
GLD - glide/portamento (defined by GLIDE MOD and GLIDE LMT settings)
CC - custom CC output (defined in GLIDE CC and LEVER CC settings)
Settings in GLIDE CC and LEVER CC are only used when corresponding control is set to CC in its CTL setting as described above.
1.5b5:
* Changed names for ADJUST page titles (to shorten) and added numerical value readouts for THR and MAX settings.
* Corrected start position for rotators and added reset to first position when activated and on roller release (from top five octaves like for the otf key change).
1.5b6
* Added EVR to NuEVI fingering menu, for reversed roller action.
* Rebooting to program mode now possible when stuck at boot due to wrong version of firmware installed (NuEVI vs NuRAD version).
* Removed possibility to change portamento limit using pinky/mod key when in GLD setting (it was always getting altered in play or handling). This might come back if I find a better way to do it.
* Support for a CME WIDI Master Bluetooth MIDI board connected to Teensy underside pads. With this mod, if present it will be powered up by touching pinky/mod key and pressing enter in MIDI meny (what previously controlled the "slow midi" function that now is replaced by the CC interval menu item). "WIDI" will be indicated in the display below the MIDI channel number and the WIDI board will be powered up. Teensy pad connections: GND to GND (blue) of WIDI, 31 (TX2) via 47 ohm resistor to signal (green) of WIDI, 33 via 47 ohm resistor to power (red) of WIDI. 27 and 28 jumpered to indicate that the WIDI is connected.
1.5b7:
* Added three new settings in EXTRAS MENU CV TUNE, CV SCALE and CV EC LFO. The first two allow for software tuning of the CV output for 1V/Oct when using NuEVI CV, NuEVI Plus or NuEVI/NuRAD with external CV box or module. Also makes more simple versions of the CV boards possible (no potentiometers for adjustment). The CV EC LFO setting controls a new CV LFO vibrato function for the extra controller (lip sensor). It can be set to OFF (no extra controller LFO vibrato) or values 1 through 8, which represent vibrato freq from 4.5Hz to 8Hz. Default value is 3 (5.5Hz).
1.5b8:
* New quarter tone setting for Pinky Key (NuEVI) and Mod Key (NuRAD) QTN. This makes the Pinky/Mod key a key for playing one quarter tone down using pitchbend (MIDI) or directly affecting the built in note CV output (using CV module or NuEVI CV/Plus). Pitch bend range for MIDI synth needs to be two semitones up/dn (or compensated for in pitch bend divider setting in the controller to make the resulting range two semitones up/dn). On NuRAD, it is recommended to set the 3rd LH Pinky key (EXTRA PKEY setting) to mirror MOD key for playablility.
* Changes to extra controller harmonics settings (experimental). Reverse direction options (ending with an R) available following the ones going up. This can be used with experimental mouthpieces where increased lip tension creates less touched sensor area.

View file

@ -1,68 +1,68 @@
POLY PLAY
In the Poly Play menu you configure everything having to do with polyphonic playing functions.
POLY PLAY POLY MODE
Here you select which type of harmonization or which rotator setup will be activated by touching/holding MOD key (NuRAD) or Pinky Key (NuEVI) and then touching Special Key(s). Deactivate by just touching Special Key(s).
OFF disables all Special Key functionality, including the Parallel Chord, Slur Sustain and Sub Octave functions
MGR Major Gospel Root a gospel type triad voicing
MGD Major Gospel Dominant same as MGR, but with a dominant on V instead of an inversion of the root
MA9 Major add9 pop style add9 harmonization
MND minor Dorian jazz type minor
MNA minor Aeolian classical minor
MNH minor 4-voice Hip a more hip dorian minor
FWC Four Way Close old school block chord harmony (with additional options further down in the Poly Play menu)
RTA Rotator A
RTB Rotator B
RTC - Rotator C
POLY PLAY HMZ KEY
Selects the key you are playing in for the key based harmonizations.
POLY PLAY OTF KEY
Enables on the fly key change for the key based harmonizers. To change key on the fly while playing, finger the key (not blowing) and briefly lift your thumb off the rollers (when in octave two or higher). Key change on the fly will not be stored when powered off.
POLY PLAY HMZ LIMIT
Limits the number of voices to be played, starting elimination from lowest note and up. Applies to the key based harmonizations only, not Rotator, Parallel Chord or Slur Sustain.
POLY PLAY FWC TYPE
Type of block chord for the Four Way Close harmonization. (6, m6, 7 or m7)
POLY PLAY FWC LOCKH
"Lock Hands" (double melody) adds another melody note one octave down for the Four Way Close harmonizations. (OFF/ON)
POLY PLAY FWC DROP2
"Drop 2" moves the second note (the one below melody note) one octave down for the FWC. (OFF/ON)
POLY PLAY PRIORITY
Sets which note will get priority when playing mono patches (sounds really great to layer a mono patch with a poly patch for use with with the Poly Play functions). MEL will play the melody note as the mono lead and ROT will play the rotated note or last note of other poly modes.
POLY PLAY RTA PARAL
POLY PLAY RTA ROT 1
POLY PLAY RTA ROT 2
POLY PLAY RTA ROT 3
POLY PLAY RTA ROT 4
POLY PLAY RTB PARAL
POLY PLAY RTB ROT 1
POLY PLAY RTB ROT 2
POLY PLAY RTB ROT 3
POLY PLAY RTB ROT 4
POLY PLAY RTC PARAL
POLY PLAY RTC ROT 1
POLY PLAY RTC ROT 2
POLY PLAY RTC ROT 3
POLY PLAY RTC ROT 4
This last section of settings is for the three rotators. They create a three note chord using the melody note, a fixed interval parallel note and a third note taken from a rotating selection of four notes. For every new note triggered, the rotation is advanced one step, and a new combination of notes is played. For each uf the three selectable rotator setups you can configure the fixed interval with RTx PARAL and the four intervals to be rotated for the third note with RTx ROT 1 to RTx ROT4, where x represents rotators A through C.
POLY PLAY
In the Poly Play menu you configure everything having to do with polyphonic playing functions.
POLY PLAY POLY MODE
Here you select which type of harmonization or which rotator setup will be activated by touching/holding MOD key (NuRAD) or Pinky Key (NuEVI) and then touching Special Key(s). Deactivate by just touching Special Key(s).
OFF disables all Special Key functionality, including the Parallel Chord, Slur Sustain and Sub Octave functions
MGR Major Gospel Root a gospel type triad voicing
MGD Major Gospel Dominant same as MGR, but with a dominant on V instead of an inversion of the root
MA9 Major add9 pop style add9 harmonization
MND minor Dorian jazz type minor
MNA minor Aeolian classical minor
MNH minor 4-voice Hip a more hip dorian minor
FWC Four Way Close old school block chord harmony (with additional options further down in the Poly Play menu)
RTA Rotator A
RTB Rotator B
RTC - Rotator C
POLY PLAY HMZ KEY
Selects the key you are playing in for the key based harmonizations.
POLY PLAY OTF KEY
Enables on the fly key change for the key based harmonizers. To change key on the fly while playing, finger the key (not blowing) and briefly lift your thumb off the rollers (when in octave two or higher). Key change on the fly will not be stored when powered off.
POLY PLAY HMZ LIMIT
Limits the number of voices to be played, starting elimination from lowest note and up. Applies to the key based harmonizations only, not Rotator, Parallel Chord or Slur Sustain.
POLY PLAY FWC TYPE
Type of block chord for the Four Way Close harmonization. (6, m6, 7 or m7)
POLY PLAY FWC LOCKH
"Lock Hands" (double melody) adds another melody note one octave down for the Four Way Close harmonizations. (OFF/ON)
POLY PLAY FWC DROP2
"Drop 2" moves the second note (the one below melody note) one octave down for the FWC. (OFF/ON)
POLY PLAY PRIORITY
Sets which note will get priority when playing mono patches (sounds really great to layer a mono patch with a poly patch for use with with the Poly Play functions). MEL will play the melody note as the mono lead and ROT will play the rotated note or last note of other poly modes.
POLY PLAY RTA PARAL
POLY PLAY RTA ROT 1
POLY PLAY RTA ROT 2
POLY PLAY RTA ROT 3
POLY PLAY RTA ROT 4
POLY PLAY RTB PARAL
POLY PLAY RTB ROT 1
POLY PLAY RTB ROT 2
POLY PLAY RTB ROT 3
POLY PLAY RTB ROT 4
POLY PLAY RTC PARAL
POLY PLAY RTC ROT 1
POLY PLAY RTC ROT 2
POLY PLAY RTC ROT 3
POLY PLAY RTC ROT 4
This last section of settings is for the three rotators. They create a three note chord using the melody note, a fixed interval parallel note and a third note taken from a rotating selection of four notes. For every new note triggered, the rotation is advanced one step, and a new combination of notes is played. For each uf the three selectable rotator setups you can configure the fixed interval with RTx PARAL and the four intervals to be rotated for the third note with RTx ROT 1 to RTx ROT4, where x represents rotators A through C.