mirror of
https://github.com/bbswitzer/PianoProject
synced 2026-01-16 19:05:11 -05:00
reset repository cause it broke
This commit is contained in:
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
340
.gitignore
vendored
Normal file
340
.gitignore
vendored
Normal file
@@ -0,0 +1,340 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- Backup*.rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
BIN
ControlBox/ControlBox.ino
Normal file
BIN
ControlBox/ControlBox.ino
Normal file
Binary file not shown.
137
ControlBox/ControlBox.vcxproj
Normal file
137
ControlBox/ControlBox.vcxproj
Normal file
File diff suppressed because one or more lines are too long
57
ControlBox/ControlBox.vcxproj.filters
Normal file
57
ControlBox/ControlBox.vcxproj.filters
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ControlBox.ino" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="input.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="lcd.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="midi.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="serial.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="setting.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="input.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="lcd.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="midi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="serial.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="setting.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="__vm\.ControlBox.vsarduino.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
85
ControlBox/__vm/.ControlBox.vsarduino.h
Normal file
85
ControlBox/__vm/.ControlBox.vsarduino.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Editor: https://www.visualmicro.com/
|
||||
This file is for intellisense purpose only.
|
||||
Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten
|
||||
The contents of the _vm sub folder can be deleted prior to publishing a project
|
||||
All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!).
|
||||
Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again
|
||||
|
||||
Hardware: Arduino Leonardo, Platform=avr, Package=arduino
|
||||
*/
|
||||
|
||||
#if defined(_VMICRO_INTELLISENSE)
|
||||
|
||||
#ifndef _VSARDUINO_H_
|
||||
#define _VSARDUINO_H_
|
||||
#define __AVR_atmega32u4__
|
||||
#define __AVR_ATmega32U4__
|
||||
#define __AVR_ATmega32u4__
|
||||
#define F_CPU 16000000L
|
||||
#define ARDUINO 10809
|
||||
#define ARDUINO_AVR_LEONARDO
|
||||
#define ARDUINO_ARCH_AVR
|
||||
#define USB_VID 0x2341
|
||||
#define USB_PID 0x8036
|
||||
#define __cplusplus 201103L
|
||||
#define _Pragma(x)
|
||||
#define __AVR__
|
||||
#define __inline__
|
||||
#define __asm__(...)
|
||||
#define __extension__
|
||||
#define __inline__
|
||||
#define __volatile__
|
||||
#define GCC_VERSION 40902
|
||||
#define __cplusplus 201103L
|
||||
|
||||
#define volatile(va_arg)
|
||||
#define _CONST
|
||||
#define __builtin_va_start
|
||||
#define __builtin_va_end
|
||||
#define __attribute__(...)
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#define prog_void
|
||||
#define PGM_VOID_P int
|
||||
|
||||
|
||||
#ifndef __builtin_constant_p
|
||||
#define __builtin_constant_p __attribute__((__const__))
|
||||
#endif
|
||||
#ifndef __builtin_strlen
|
||||
#define __builtin_strlen __attribute__((__const__))
|
||||
#endif
|
||||
|
||||
|
||||
#define NEW_H
|
||||
typedef void *__builtin_va_list;
|
||||
//extern "C" void __cxa_pure_virtual() {;}
|
||||
|
||||
typedef int div_t;
|
||||
typedef int ldiv_t;
|
||||
|
||||
|
||||
typedef void *__builtin_va_list;
|
||||
//extern "C" void __cxa_pure_virtual() {;}
|
||||
|
||||
|
||||
|
||||
#include "arduino.h"
|
||||
#include <pins_arduino.h>
|
||||
//#undef F
|
||||
//#define F(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
#undef PSTR
|
||||
#define PSTR(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
|
||||
//typedef unsigned char uint8_t;
|
||||
//typedef unsigned int uint8_t;
|
||||
|
||||
#define pgm_read_byte(address_short) uint8_t()
|
||||
#define pgm_read_word(address_short) uint16_t()
|
||||
#define pgm_read_dword(address_short) uint32_t()
|
||||
#define pgm_read_float(address_short) float()
|
||||
#define pgm_read_ptr(address_short) short()
|
||||
|
||||
#include "ControlBox.ino"
|
||||
#endif
|
||||
#endif
|
||||
12
ControlBox/__vm/Compile.vmps.xml
Normal file
12
ControlBox/__vm/Compile.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
9
ControlBox/__vm/Configuration.Debug.vmps.xml
Normal file
9
ControlBox/__vm/Configuration.Debug.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
134
ControlBox/input.cpp
Normal file
134
ControlBox/input.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include <EEPROM.h>
|
||||
#include "input.h"
|
||||
#include "lcd.h"
|
||||
#include "serial.h"
|
||||
#include "setting.h"
|
||||
|
||||
void handleInput(int inputPin);
|
||||
|
||||
extern const int UP_PIN = 4;
|
||||
extern const int DOWN_PIN = 6;
|
||||
extern const int LEFT_PIN = 7;
|
||||
extern const int RIGHT_PIN = 5;
|
||||
extern const int RESET_PIN = 8;
|
||||
extern const int VOLUME_PIN = 10;
|
||||
|
||||
struct LastPressed
|
||||
{
|
||||
unsigned long up = 0;
|
||||
unsigned long down = 0;
|
||||
unsigned long left = 0;
|
||||
unsigned long right = 0;
|
||||
unsigned long reset1 = 0;
|
||||
}; LastPressed lastPressed;
|
||||
unsigned long lastPressedOverall = 0;
|
||||
int lastAnalog;
|
||||
|
||||
void initializeInputs()
|
||||
{
|
||||
pinMode(UP_PIN, INPUT);
|
||||
pinMode(DOWN_PIN, INPUT);
|
||||
pinMode(LEFT_PIN, INPUT);
|
||||
pinMode(RIGHT_PIN, INPUT);
|
||||
pinMode(RESET_PIN, INPUT);
|
||||
lastAnalog = (analogRead(VOLUME_PIN) / 5);
|
||||
}
|
||||
|
||||
void checkForInput()
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
const int BUTTON_COOLDOWN = 500;
|
||||
extern const bool DEBUG_MODE;
|
||||
|
||||
if(digitalRead(UP_PIN) == HIGH && lastPressed.up + BUTTON_COOLDOWN <= ms)
|
||||
handleInput(UP_PIN);
|
||||
else if(digitalRead(DOWN_PIN) == HIGH && lastPressed.down + BUTTON_COOLDOWN <= ms)
|
||||
handleInput(DOWN_PIN);
|
||||
else if(digitalRead(LEFT_PIN) == HIGH && lastPressed.left + BUTTON_COOLDOWN <= ms)
|
||||
handleInput(LEFT_PIN);
|
||||
else if(digitalRead(RIGHT_PIN) == HIGH && lastPressed.right + BUTTON_COOLDOWN <= ms)
|
||||
handleInput(RIGHT_PIN);
|
||||
else if(digitalRead(RESET_PIN) == HIGH && lastPressed.reset1 + BUTTON_COOLDOWN <= ms)
|
||||
handleInput(RESET_PIN);
|
||||
else
|
||||
{
|
||||
const int ANALOG_CHANGE_RATE = 5;
|
||||
const int ANALOG_WITHIN_RANGE = 20;
|
||||
const int ANALOG_CHANGE_TIME = BUTTON_COOLDOWN * 2;
|
||||
int analogValue = analogRead(VOLUME_PIN) / 5; //conform analog value from 0-200
|
||||
if(DEBUG_MODE) Serial.println(analogValue);
|
||||
if((analogValue >= (lastAnalog + ANALOG_CHANGE_RATE) || analogValue <= (lastAnalog - ANALOG_CHANGE_RATE)) && //if volume has changed enough
|
||||
(analogValue < (lastAnalog + ANALOG_WITHIN_RANGE) && analogValue >(lastAnalog - ANALOG_WITHIN_RANGE)) && //if volume hasn't changed too much for it to be a glitch
|
||||
lastPressedOverall + ANALOG_CHANGE_TIME <= ms) //if another button hasn't been pressed within a certain period for it to affect the reading
|
||||
{
|
||||
//conform analog to certain values if they are within range
|
||||
if(analogValue <= 105 && analogValue >= 95)
|
||||
analogValue = 100;
|
||||
else if(analogValue > 195)
|
||||
analogValue = 200;
|
||||
else if(analogValue < 5)
|
||||
analogValue = 0;
|
||||
lastAnalog = analogValue;
|
||||
handleInput(VOLUME_PIN); //implied that lastAnalog has also been changed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleInput(int inputPin)
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
switch(inputPin)
|
||||
{
|
||||
case UP_PIN:
|
||||
if(menuState == MenuStates::SETTINGS) //prevent settings from changing while not showing
|
||||
{
|
||||
lastPressed.up = ms;
|
||||
lastPressedOverall = ms;
|
||||
changeSetting(1);
|
||||
sendSerialToMain(SerialConstants::SETTING_HEADER, currentMenu, EEPROM.read(currentMenu));
|
||||
updateDisplay();
|
||||
}
|
||||
break;
|
||||
case DOWN_PIN:
|
||||
if(menuState == MenuStates::SETTINGS) //prevent settings from changing while not showing
|
||||
{
|
||||
lastPressed.down = ms;
|
||||
lastPressedOverall = ms;
|
||||
changeSetting(-1);
|
||||
sendSerialToMain(SerialConstants::SETTING_HEADER, currentMenu, EEPROM.read(currentMenu));
|
||||
updateDisplay();
|
||||
}
|
||||
break;
|
||||
case LEFT_PIN:
|
||||
lastPressed.left = ms;
|
||||
lastPressedOverall = ms;
|
||||
currentMenu--;
|
||||
if(currentMenu < 0)
|
||||
currentMenu = NUM_OF_MENUS - 1;
|
||||
menuState = MenuStates::SETTINGS;
|
||||
updateDisplay();
|
||||
break;
|
||||
case RIGHT_PIN:
|
||||
lastPressed.right = ms;
|
||||
lastPressedOverall = ms;
|
||||
currentMenu++;
|
||||
if(currentMenu >= NUM_OF_MENUS)
|
||||
currentMenu == 0;
|
||||
menuState = MenuStates::SETTINGS;
|
||||
updateDisplay();
|
||||
break;
|
||||
case RESET_PIN:
|
||||
lastPressed.reset1 = ms;
|
||||
exitScreen = ms + SPECIAL_MENU_TIMEOUT;
|
||||
sendSerialToMain(SerialConstants::RESET_HEADER, 127, 127);
|
||||
menuState = MenuStates::RESET;
|
||||
updateDisplay();
|
||||
break;
|
||||
case VOLUME_PIN:
|
||||
exitScreen = ms + SPECIAL_MENU_TIMEOUT;
|
||||
menuState = MenuStates::VOLUME;
|
||||
sendSerialToMain(SerialConstants::VOLUME_HEADER, lastAnalog, lastAnalog);
|
||||
updateDisplay();
|
||||
break;
|
||||
}
|
||||
}
|
||||
13
ControlBox/input.h
Normal file
13
ControlBox/input.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
extern unsigned long lastPressedOverall;
|
||||
|
||||
extern int lastAnalog;
|
||||
|
||||
void initializeInputs();
|
||||
void checkForInput();
|
||||
|
||||
#endif
|
||||
113
ControlBox/lcd.cpp
Normal file
113
ControlBox/lcd.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
#include <EEPROM.h>
|
||||
#include "lcd.h"
|
||||
#include "input.h"
|
||||
#include "setting.h"
|
||||
|
||||
extern LiquidCrystal_I2C lcd;
|
||||
|
||||
const String MENU_NAMES[] ={
|
||||
"Handle Notes",
|
||||
"Schedule Notes",
|
||||
"Min Accepted Vel",
|
||||
"Solenoid PWM %",
|
||||
"Min Startup Ms",
|
||||
"Max Startup Ms",
|
||||
"Velocity Var",
|
||||
"Min Off Ms",
|
||||
"Max Off Ms",
|
||||
"Fast Off Ms",
|
||||
"Pedal On Ms",
|
||||
"Pedal Off Ms",
|
||||
"Note Timeout",
|
||||
"Sustain Timeout",
|
||||
"Auto Reset Mins",
|
||||
"Max Left Keys",
|
||||
"Max Right Keys"
|
||||
};
|
||||
|
||||
MenuStates menuState = MenuStates::WELCOME;
|
||||
int currentMenu = -1; //initialize current menu as invalid
|
||||
const int NUM_OF_MENUS = sizeof(MENU_NAMES) / sizeof(MENU_NAMES[0]);
|
||||
const int SETTING_MENU_TIMEOUT = 20000;
|
||||
extern const int SPECIAL_MENU_TIMEOUT = 3000;
|
||||
unsigned long exitScreen;
|
||||
|
||||
void initializeLCD()
|
||||
{
|
||||
lcd.begin();
|
||||
lcd.backlight();
|
||||
}
|
||||
|
||||
void printHomeScreen()
|
||||
{
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Welcome to");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("Player Piano");
|
||||
}
|
||||
|
||||
void updateDisplay()
|
||||
{
|
||||
lcd.clear();
|
||||
switch(menuState)
|
||||
{
|
||||
case MenuStates::WELCOME:
|
||||
printHomeScreen();
|
||||
break;
|
||||
case MenuStates::SETTINGS:
|
||||
lcd.print(MENU_NAMES[currentMenu]);
|
||||
lcd.setCursor(0, 1);
|
||||
//see if the current menu is true or false or just a number
|
||||
if(currentMenu != static_cast<int>(SettingID::SCHEDULE_NOTES) &&
|
||||
currentMenu != static_cast<int>(SettingID::HANDLE_NOTES)) //if current menu is not a bool menu
|
||||
lcd.print(EEPROM.read(currentMenu));
|
||||
else
|
||||
{
|
||||
if(EEPROM.read(currentMenu))
|
||||
lcd.print("True");
|
||||
else
|
||||
lcd.print("False");
|
||||
}
|
||||
break;
|
||||
case MenuStates::VOLUME:
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Volume");
|
||||
lcd.print(" ");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print(lastAnalog);
|
||||
lcd.print("% ");
|
||||
break;
|
||||
case MenuStates::RESET:
|
||||
lcd.clear();
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Resetting Keys");
|
||||
lcd.setCursor(0, 1);
|
||||
lcd.print("...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void checkSchedule()
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
if(ms >= lastPressedOverall + SETTING_MENU_TIMEOUT && lastPressedOverall > 0)
|
||||
{
|
||||
lastPressedOverall = 0;
|
||||
if(menuState == MenuStates::SETTINGS) //if another screen isn't scheduled
|
||||
{
|
||||
menuState = MenuStates::WELCOME;
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
if(ms >= exitScreen && exitScreen > 0)
|
||||
{
|
||||
exitScreen = 0;
|
||||
if(lastPressedOverall == 0) //if setting menu is timed out
|
||||
menuState = MenuStates::WELCOME;
|
||||
else
|
||||
menuState = MenuStates::SETTINGS;
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
25
ControlBox/lcd.h
Normal file
25
ControlBox/lcd.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef LCD_H
|
||||
#define LCD_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
enum class MenuStates
|
||||
{
|
||||
WELCOME,
|
||||
SETTINGS,
|
||||
VOLUME,
|
||||
RESET
|
||||
}; extern MenuStates menuState;
|
||||
|
||||
extern const String MENU_NAMES[];
|
||||
extern const int SPECIAL_MENU_TIMEOUT;
|
||||
extern const int NUM_OF_MENUS;
|
||||
extern int currentMenu;
|
||||
extern unsigned long exitScreen;
|
||||
|
||||
void initializeLCD();
|
||||
void printHomeScreen();
|
||||
void updateDisplay();
|
||||
void checkSchedule();
|
||||
|
||||
#endif
|
||||
41
ControlBox/midi.cpp
Normal file
41
ControlBox/midi.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <MIDIUSB.h>
|
||||
#include "midi.h"
|
||||
#include "serial.h"
|
||||
|
||||
void decodeMidi(uint8_t header, uint8_t byte1, uint8_t byte2, uint8_t byte3);
|
||||
|
||||
void checkForMidiUSB()
|
||||
{
|
||||
midiEventPacket_t rx; //midi data struct from midiUSB libray
|
||||
do
|
||||
{
|
||||
rx = MidiUSB.read(); //get queued info from USB
|
||||
if(rx.header != 0)
|
||||
{
|
||||
decodeMidi(rx.header, rx.byte1, rx.byte2, rx.byte3);
|
||||
}
|
||||
} while(rx.header != 0);
|
||||
}
|
||||
|
||||
void decodeMidi(uint8_t header, uint8_t byte1, uint8_t byte2, uint8_t byte3)
|
||||
{
|
||||
const uint8_t NOTE_ON_HEADER = 9;
|
||||
const uint8_t NOTE_OFF_HEADER = 8;
|
||||
const uint8_t CONTROL_CHANGE_HEADER = 8;
|
||||
const uint8_t SUSTAIN_STATUS_BYTE = 176;
|
||||
const uint8_t MIN_NOTE_PITCH = 21;
|
||||
//note that ESP32 does bounds checking
|
||||
switch(header)
|
||||
{
|
||||
case NOTE_ON_HEADER:
|
||||
sendSerialToMain(SerialConstants::NOTE_HEADER, byte2 - MIN_NOTE_PITCH, byte3);
|
||||
break;
|
||||
case NOTE_OFF_HEADER:
|
||||
sendSerialToMain(SerialConstants::NOTE_HEADER, byte2 - MIN_NOTE_PITCH, 0);
|
||||
break;
|
||||
case SUSTAIN_STATUS_BYTE:
|
||||
if(byte1 == SUSTAIN_STATUS_BYTE)
|
||||
sendSerialToMain(SerialConstants::SUSTAIN_HEADER, byte3, byte3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
8
ControlBox/midi.h
Normal file
8
ControlBox/midi.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef MIDI_H
|
||||
#define MIDI_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void checkForMidiUSB();
|
||||
|
||||
#endif
|
||||
17
ControlBox/serial.cpp
Normal file
17
ControlBox/serial.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "lcd.h"
|
||||
|
||||
namespace SerialConstants
|
||||
{
|
||||
extern const byte NOTE_HEADER = 201;
|
||||
extern const byte SUSTAIN_HEADER = 202;
|
||||
extern const byte SETTING_HEADER = 203;
|
||||
extern const byte RESET_HEADER = 204;
|
||||
extern const byte VOLUME_HEADER = 205;
|
||||
}
|
||||
|
||||
void sendSerialToMain(byte header, byte setting, byte value)
|
||||
{
|
||||
Serial1.write(header);
|
||||
Serial1.write(setting);
|
||||
Serial1.write(value);
|
||||
}
|
||||
18
ControlBox/serial.h
Normal file
18
ControlBox/serial.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace SerialConstants
|
||||
{
|
||||
extern const uint8_t NOTE_HEADER;
|
||||
extern const uint8_t SUSTAIN_HEADER;
|
||||
extern const uint8_t SETTING_HEADER;
|
||||
extern const uint8_t RESET_HEADER;
|
||||
extern const uint8_t VOLUME_HEADER;
|
||||
}
|
||||
|
||||
void sendSerialToMain(uint8_t header, uint8_t setting, uint8_t value);
|
||||
void sendAllSettings();
|
||||
|
||||
#endif
|
||||
24
ControlBox/setting.cpp
Normal file
24
ControlBox/setting.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <EEPROM.h>
|
||||
#include "setting.h"
|
||||
#include "lcd.h"
|
||||
#include "serial.h"
|
||||
#include "input.h"
|
||||
|
||||
void changeSetting(int changeBy)
|
||||
{
|
||||
if(currentMenu != static_cast<int>(SettingID::SCHEDULE_NOTES) &&
|
||||
currentMenu != static_cast<int>(SettingID::HANDLE_NOTES)) //if current menu is not a bool menu
|
||||
EEPROM.write(currentMenu, EEPROM.read(currentMenu) + changeBy);
|
||||
else
|
||||
EEPROM.write(currentMenu, !(static_cast<bool>(EEPROM.read(currentMenu))));
|
||||
}
|
||||
|
||||
void sendAllSettings()
|
||||
{
|
||||
for(int index = 0; index < NUM_OF_MENUS; index++)
|
||||
{
|
||||
sendSerialToMain(SerialConstants::SETTING_HEADER, index, EEPROM.read(index));
|
||||
delay(50);
|
||||
}
|
||||
sendSerialToMain(SerialConstants::VOLUME_HEADER, lastAnalog, lastAnalog);
|
||||
}
|
||||
29
ControlBox/setting.h
Normal file
29
ControlBox/setting.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef SETTING_H
|
||||
#define SETTING_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
enum class SettingID //same order as menus
|
||||
{
|
||||
HANDLE_NOTES,
|
||||
SCHEDULE_NOTES,
|
||||
MIN_ACCEPTED_VEL,
|
||||
PWM_PERCENT,
|
||||
MIN_STARTUP_MS,
|
||||
MAX_STARTUP_MS,
|
||||
VELOCITY_VAR,
|
||||
MIN_OFF_MS,
|
||||
MAX_OFF_MS,
|
||||
FAST_OFF_MS,
|
||||
PEDAL_ON_MS,
|
||||
PEDAL_OFF_MS,
|
||||
NOTE_TIMEOUT,
|
||||
SUSTAIN_TIMEOUT,
|
||||
AUTO_RESET_MINS,
|
||||
MAX_LEFT_KEYS,
|
||||
MAX_RIGHT_KEYS
|
||||
};
|
||||
|
||||
void changeSetting(int changeBy);
|
||||
|
||||
#endif
|
||||
80
ESP32/ESP32.ino
Normal file
80
ESP32/ESP32.ino
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic warning "-fpermissive"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include "sustain.h"
|
||||
#include "settings.h"
|
||||
#include "serial.h"
|
||||
#include "note.h"
|
||||
#include "midi.h"
|
||||
#include "bluetooth.h"
|
||||
#include "main.h"
|
||||
#pragma GCC diagnostic pop
|
||||
const bool DEBUG_MODE = false;
|
||||
|
||||
void resetAll()
|
||||
{
|
||||
acceptMidi = false; //turn midi off during reset to prevent errors
|
||||
for(int noteIndex = 0; noteIndex < 88; noteIndex++)
|
||||
{
|
||||
notes[noteIndex].resetSchedule();
|
||||
}
|
||||
Note::resetInstances();
|
||||
sustain.resetSchedule();
|
||||
msReset();
|
||||
acceptMidi = true;
|
||||
}
|
||||
|
||||
void flashLED()
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(100);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
delay(100);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
const int SHIFT_REGISTER_POWER_PIN = 12;
|
||||
const int SUSTAIN_PIN = 13;
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
Serial.begin(38400);
|
||||
initializeBluetooth();
|
||||
|
||||
//create sustain PWM output. this can't be done by the Pro Micro because the shift registers are filled up
|
||||
ledcSetup(0, 100, 8);
|
||||
ledcAttachPin(SUSTAIN_PIN, 0);
|
||||
|
||||
delay(500); //give pro micro time to inititalize before giving power to shift registers
|
||||
pinMode(SHIFT_REGISTER_POWER_PIN, OUTPUT);
|
||||
digitalWrite(SHIFT_REGISTER_POWER_PIN, HIGH);
|
||||
|
||||
std::vector<unsigned long> testy[6];
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
checkForSerial();
|
||||
|
||||
for(int noteIndex = 0; noteIndex < 88; noteIndex++)
|
||||
{
|
||||
notes[noteIndex].checkSchedule();
|
||||
notes[noteIndex].checkForErrors();
|
||||
}
|
||||
sustain.checkSchedule();
|
||||
sustain.checkForErrors();
|
||||
|
||||
if(millis() >= nextReset)
|
||||
{
|
||||
//first reset will happen immediately and midi will begin to accept after this
|
||||
resetAll();
|
||||
nextReset = millis() + Setting::autoResetMs;
|
||||
}
|
||||
}
|
||||
140
ESP32/ESP32.vcxproj
Normal file
140
ESP32/ESP32.vcxproj
Normal file
File diff suppressed because one or more lines are too long
66
ESP32/ESP32.vcxproj.filters
Normal file
66
ESP32/ESP32.vcxproj.filters
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="__vm\.ESP32.vsarduino.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bluetooth.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="midi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="note.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="serial.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sustain.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bluetooth.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="midi.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="note.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="serial.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="settings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sustain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ESP32.ino" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
109
ESP32/__vm/.ESP32.vsarduino.h
Normal file
109
ESP32/__vm/.ESP32.vsarduino.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Editor: https://www.visualmicro.com/
|
||||
This file is for intellisense purpose only.
|
||||
Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten
|
||||
The contents of the _vm sub folder can be deleted prior to publishing a project
|
||||
All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!).
|
||||
Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again
|
||||
|
||||
Hardware: Node32s, Platform=esp32, Package=espressif
|
||||
*/
|
||||
|
||||
#if defined(_VMICRO_INTELLISENSE)
|
||||
|
||||
#ifndef _VSARDUINO_H_
|
||||
#define _VSARDUINO_H_
|
||||
#define __ESP32_esp32__
|
||||
#define __ESP32_ESP32__
|
||||
#define ESP_PLATFORM
|
||||
#define HAVE_CONFIG_H
|
||||
#define F_CPU 240000000L
|
||||
#define ARDUINO 10809
|
||||
#define ARDUINO_Node32s
|
||||
#define ARDUINO_ARCH_ESP32
|
||||
#define ESP32
|
||||
#define CORE_DEBUG_LEVEL 0
|
||||
#define __cplusplus 201103L
|
||||
|
||||
#define _Pragma(x)
|
||||
#undef __cplusplus
|
||||
#define __cplusplus 201103L
|
||||
|
||||
#define __STDC__
|
||||
#define __ARM__
|
||||
#define __arm__
|
||||
#define __inline__
|
||||
#define __asm__(...)
|
||||
#define __extension__
|
||||
#define __ATTR_PURE__
|
||||
#define __ATTR_CONST__
|
||||
#define __volatile__
|
||||
|
||||
#define __ASM
|
||||
#define __INLINE
|
||||
#define __attribute__(noinline)
|
||||
|
||||
//#define _STD_BEGIN
|
||||
//#define EMIT
|
||||
#define WARNING
|
||||
#define _Lockit
|
||||
#define __CLR_OR_THIS_CALL
|
||||
#define C4005
|
||||
#define _NEW
|
||||
|
||||
typedef bool _Bool;
|
||||
typedef int _read;
|
||||
typedef int _seek;
|
||||
typedef int _write;
|
||||
typedef int _close;
|
||||
typedef int __cleanup;
|
||||
|
||||
//#define inline
|
||||
|
||||
#define __builtin_clz
|
||||
#define __builtin_clzl
|
||||
#define __builtin_clzll
|
||||
#define __builtin_labs
|
||||
#define __builtin_va_list
|
||||
typedef int __gnuc_va_list;
|
||||
|
||||
#define __ATOMIC_ACQ_REL
|
||||
|
||||
#define __CHAR_BIT__
|
||||
#define _EXFUN()
|
||||
|
||||
typedef unsigned char byte;
|
||||
extern "C" void __cxa_pure_virtual() {;}
|
||||
|
||||
typedef long __INTPTR_TYPE__ ;
|
||||
typedef long __UINTPTR_TYPE__ ;
|
||||
typedef long __SIZE_TYPE__ ;
|
||||
typedef long __PTRDIFF_TYPE__;
|
||||
|
||||
typedef long pthread_t;
|
||||
typedef long pthread_key_t;
|
||||
typedef long pthread_once_t;
|
||||
typedef long pthread_mutex_t;
|
||||
typedef long pthread_mutex_t;
|
||||
typedef long pthread_cond_t;
|
||||
|
||||
|
||||
|
||||
#include "arduino.h"
|
||||
#include <pins_arduino.h>
|
||||
|
||||
//#include "..\generic\Common.h"
|
||||
//#include "..\generic\pins_arduino.h"
|
||||
|
||||
//#undef F
|
||||
//#define F(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
//#undef PSTR
|
||||
//#define PSTR(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
//current vc++ does not understand this syntax so use older arduino example for intellisense
|
||||
//todo:move to the new clang/gcc project types.
|
||||
#define interrupts() sei()
|
||||
#define noInterrupts() cli()
|
||||
|
||||
#include "ESP32.ino"
|
||||
#endif
|
||||
#endif
|
||||
12
ESP32/__vm/Compile.vmps.xml
Normal file
12
ESP32/__vm/Compile.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
9
ESP32/__vm/Configuration.Debug.vmps.xml
Normal file
9
ESP32/__vm/Configuration.Debug.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
12
ESP32/__vm/Upload.vmps.xml
Normal file
12
ESP32/__vm/Upload.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
135
ESP32/bluetooth.cpp
Normal file
135
ESP32/bluetooth.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <esp_gatt_defs.h>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_bt_main.h>
|
||||
#include <esp_gatts_api.h>
|
||||
#include <string.h>
|
||||
#include "bluetooth.h"
|
||||
#include "midi.h"
|
||||
#include "main.h"
|
||||
|
||||
uint8_t attrStr[] ={ 0x00 }; //value range of a characteristic
|
||||
esp_attr_value_t gattsAttrVal =
|
||||
{
|
||||
.attr_max_len = 0xFF, //max value of a characteristic
|
||||
.attr_len = sizeof(attrStr),
|
||||
.attr_value = attrStr,
|
||||
};
|
||||
uint8_t serviceUUID128[32] =
|
||||
{
|
||||
0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51, 0xA7, 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03,
|
||||
0xF3, 0x6B, 0x10, 0x9D, 0x66, 0xF2, 0xA9, 0xA1, 0x12, 0x41, 0x68, 0x38, 0xDB, 0xE5, 0x72, 0x77
|
||||
};
|
||||
uint8_t rawAdvData[] =
|
||||
{
|
||||
0x02, 0x01, 0x06, 0x11, 0x07, 0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51, 0xA7, 0x33, 0x4B,
|
||||
0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03,
|
||||
};
|
||||
uint8_t rawScanRspData[] ={ 0x0c, 0x09, 'P','l','a','y','e','r',' ','P','i','a','n','o' };
|
||||
|
||||
|
||||
esp_bt_uuid_t characteristicUUID;
|
||||
esp_gatt_perm_t permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
|
||||
esp_gatt_char_prop_t property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
|
||||
esp_ble_adv_params_t BLEAdvParams;
|
||||
|
||||
static void gattsEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gattsInterface, esp_ble_gatts_cb_param_t* params);
|
||||
void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* params);
|
||||
|
||||
void initializeBluetooth()
|
||||
{
|
||||
//writing the parameters for the bluetooth on BLEAdvParams, which a struct
|
||||
BLEAdvParams.adv_int_min = 0x20;
|
||||
BLEAdvParams.adv_int_max = 0x30;
|
||||
BLEAdvParams.adv_type = ADV_TYPE_IND;
|
||||
BLEAdvParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
|
||||
BLEAdvParams.channel_map = ADV_CHNL_ALL;
|
||||
BLEAdvParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
|
||||
characteristicUUID.len = ESP_UUID_LEN_128;
|
||||
for(int i=0; i < 16; i++)
|
||||
characteristicUUID.uuid.uuid128[i] = serviceUUID128[i + 16];
|
||||
|
||||
btStart();
|
||||
esp_bluedroid_init();
|
||||
esp_bluedroid_enable();
|
||||
esp_ble_gatts_register_callback(gattsEventHandler); //log the callback function for gatts even handling
|
||||
esp_ble_gap_register_callback(gapEventHandler); //log the callback function for gap event handling
|
||||
esp_ble_gatts_app_register(0);
|
||||
}
|
||||
|
||||
static void gattsEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gattsInterface, esp_ble_gatts_cb_param_t* params)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case ESP_GATTS_REG_EVT: //create service
|
||||
if(params->reg.status != ESP_GATT_OK)
|
||||
return;
|
||||
|
||||
esp_gatt_srvc_id_t serviceID;
|
||||
serviceID.is_primary = true;
|
||||
serviceID.id.inst_id = 0x00;
|
||||
serviceID.id.uuid.len = ESP_UUID_LEN_128;
|
||||
for(int i=0; i < 16; i++)
|
||||
serviceID.id.uuid.uuid.uuid128[i] = serviceUUID128[i];
|
||||
|
||||
esp_ble_gap_set_device_name("Player Piano");
|
||||
esp_ble_gap_config_adv_data_raw(rawAdvData, sizeof(rawAdvData));
|
||||
esp_ble_gap_config_scan_rsp_data_raw(rawScanRspData, sizeof(rawScanRspData));
|
||||
esp_ble_gatts_create_service(gattsInterface, &serviceID, 4);
|
||||
break;
|
||||
|
||||
case ESP_GATTS_READ_EVT: //when main device requests data
|
||||
esp_gatt_rsp_t response;
|
||||
memset(&response, 0, sizeof(esp_gatt_rsp_t));
|
||||
response.attr_value.handle = params->read.handle;
|
||||
response.attr_value.len = 1;
|
||||
response.attr_value.value[0] = 0;
|
||||
esp_ble_gatts_send_response(gattsInterface, params->read.conn_id, params->read.trans_id, ESP_GATT_OK, &response);
|
||||
break;
|
||||
|
||||
case ESP_GATTS_WRITE_EVT: //when main device sends data
|
||||
extern bool acceptMidi;
|
||||
if(acceptMidi)
|
||||
decodeBluetooth(params->write.len, params->write.value);
|
||||
break;
|
||||
|
||||
case ESP_GATTS_CREATE_EVT: //start service and characteristic
|
||||
esp_ble_gatts_add_char(params->create.service_handle, &characteristicUUID, permissions, property, &gattsAttrVal, NULL);
|
||||
esp_ble_gatts_start_service(params->create.service_handle);
|
||||
break;
|
||||
|
||||
case ESP_GATTS_DISCONNECT_EVT: //advertise again on disconnect
|
||||
esp_ble_gap_start_advertising(&BLEAdvParams);
|
||||
break;
|
||||
|
||||
case ESP_GATTS_CONNECT_EVT: //connection
|
||||
esp_ble_conn_update_params_t conn_params;
|
||||
memcpy(conn_params.bda, params->connect.remote_bda, sizeof(esp_bd_addr_t));
|
||||
conn_params.latency = 0;
|
||||
conn_params.max_int = 0x30;
|
||||
conn_params.min_int = 0x20;
|
||||
conn_params.timeout = 500;
|
||||
esp_ble_gap_update_conn_params(&conn_params);
|
||||
//if(DEBUG_MODE) flashLED();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* params)
|
||||
{
|
||||
switch(event) {
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
esp_ble_gap_start_advertising(&BLEAdvParams);
|
||||
break;
|
||||
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
||||
esp_ble_gap_start_advertising(&BLEAdvParams);
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
|
||||
esp_ble_gap_start_advertising(&BLEAdvParams);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
8
ESP32/bluetooth.h
Normal file
8
ESP32/bluetooth.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef BLUETOOTH_H
|
||||
#define BLUETOOTH_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void initializeBluetooth();
|
||||
|
||||
#endif
|
||||
8
ESP32/main.h
Normal file
8
ESP32/main.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
extern const bool DEBUG_MODE;
|
||||
void resetAll();
|
||||
void flashLED();
|
||||
|
||||
#endif
|
||||
70
ESP32/midi.cpp
Normal file
70
ESP32/midi.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "midi.h"
|
||||
#include "note.h"
|
||||
#include "sustain.h"
|
||||
#include "settings.h"
|
||||
#include "main.h"
|
||||
|
||||
void sendBluetoothToSerial(int lengthM, uint8_t* message);
|
||||
|
||||
void decodeBluetooth(int lengthM, uint8_t* message)
|
||||
{
|
||||
const uint8_t MIN_NOTE_STATUS_BYTE = 128;
|
||||
const uint8_t MAX_NOTE_STATUS_BYTE = 159;
|
||||
const uint8_t BEGIN_NOTE_OFF_BYTE = 143;
|
||||
const uint8_t CONTROL_CHANGE_BYTE = 176;
|
||||
const uint8_t SUSTAIN_DATA_BYTE = 64;
|
||||
const uint8_t MIN_NOTE_PITCH = 21;
|
||||
const uint8_t MAX_NOTE_PITCH = 108;
|
||||
const uint8_t MIN_NOTE_VELOCITY = Setting::minNoteVelocity;
|
||||
const uint8_t MAX_NOTE_VELOCITY = 127;
|
||||
const uint8_t RESET_LENGTH = 152;
|
||||
const uint8_t RESET_LENGTH2 = 154;
|
||||
|
||||
if(DEBUG_MODE) sendBluetoothToSerial(lengthM, message);
|
||||
|
||||
for(int index = 1; index < lengthM; index++)
|
||||
{
|
||||
if(message[index] >= MIN_NOTE_STATUS_BYTE && message[index] <= MAX_NOTE_STATUS_BYTE)
|
||||
{
|
||||
for(int noteIndex = index + 1; noteIndex < lengthM; noteIndex+=2)
|
||||
{
|
||||
int velocityIndex = noteIndex + 1;
|
||||
//go through message to find notes
|
||||
if(message[noteIndex] >= MIN_NOTE_PITCH && message[noteIndex] <= MAX_NOTE_PITCH &&
|
||||
message[velocityIndex] <= MAX_NOTE_VELOCITY)
|
||||
{
|
||||
int note = message[noteIndex] - MIN_NOTE_PITCH;
|
||||
uint8_t velocity = message[velocityIndex];
|
||||
if(DEBUG_MODE) Serial.print("Detected note: ");
|
||||
if(DEBUG_MODE) Serial.print(note);
|
||||
if(DEBUG_MODE) Serial.print("with velocity: ");
|
||||
if(DEBUG_MODE) Serial.println(velocity);
|
||||
//check if the status byte is off -- anything less than 144 means off -- or if the velocity is 0
|
||||
if(message[index] <= BEGIN_NOTE_OFF_BYTE || velocity == 0)
|
||||
notes[note].prepareToSchedule(0);
|
||||
else if(velocity >= MIN_NOTE_VELOCITY)
|
||||
notes[note].prepareToSchedule(velocity);
|
||||
} else
|
||||
break;
|
||||
//break if note not detected (new status byte is found)
|
||||
}
|
||||
} else if(message[index] == CONTROL_CHANGE_BYTE && message[index + 1] == SUSTAIN_DATA_BYTE)
|
||||
{
|
||||
uint8_t sustainVelocityIndex = index + 2;
|
||||
//sustain.prepareToSchedule(message[sustainVelocityIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
//sytnhesia sends specific lengths of data when it stops playing a midi
|
||||
if(lengthM == RESET_LENGTH || lengthM == RESET_LENGTH2)
|
||||
resetAll();
|
||||
}
|
||||
|
||||
void sendBluetoothToSerial(int lengthM, uint8_t* message)
|
||||
{
|
||||
Serial.println("-----------------------");
|
||||
for(int index = 1; index < lengthM; index++)
|
||||
{
|
||||
Serial.println(message[index], DEC);
|
||||
}
|
||||
}
|
||||
8
ESP32/midi.h
Normal file
8
ESP32/midi.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef MIDI_H
|
||||
#define MIDI_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void decodeBluetooth(int lengthM, uint8_t* message);
|
||||
|
||||
#endif
|
||||
284
ESP32/note.cpp
Normal file
284
ESP32/note.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "note.h"
|
||||
#include "serial.h"
|
||||
#include "main.h"
|
||||
|
||||
Note notes[88];
|
||||
|
||||
int Note::scheduledLeftNotes = 0;
|
||||
int Note::scheduledRightNotes = 0;
|
||||
int Note::idGenerator = 0;
|
||||
int Note::noteVelocityMs[127];
|
||||
|
||||
Note::Note()
|
||||
{
|
||||
id = idGenerator;
|
||||
idGenerator++;
|
||||
//initialize one row of vectors so program won't crash while comparing
|
||||
//initialize note as off by default
|
||||
for(int index = 0; index < 6; index++)
|
||||
{
|
||||
schedule[index].reserve(8);
|
||||
schedule[index].resize(1);
|
||||
}
|
||||
schedule[OFF].push_back(millis());
|
||||
}
|
||||
|
||||
void Note::prepareToSchedule(uint8_t velocity)
|
||||
{
|
||||
if(Setting::handleNotes)
|
||||
{
|
||||
calculateVolume(velocity);
|
||||
if(Setting::scheduleNotes)
|
||||
{
|
||||
if(velocity == 0)
|
||||
scheduleNote(velocity);
|
||||
else if(canBeScheduled())
|
||||
scheduleNote(velocity);
|
||||
} else
|
||||
sendMidiToProMicro(id, velocity);
|
||||
} else
|
||||
sendMidiToProMicro(id, velocity); //without modifying the volume
|
||||
}
|
||||
|
||||
void Note::checkSchedule()
|
||||
{
|
||||
//reverse stack behavior: erases stack from the bottom
|
||||
unsigned long ms = millis();
|
||||
|
||||
if(schedule[OFF].size() > 1 && schedule[DEACTIVATION].size() > 1 &&
|
||||
ms >= schedule[OFF].at(1) && schedule[OFF].at(1) >= schedule[DEACTIVATION].at(1)) //first because sometimes off and on times are the same
|
||||
{
|
||||
schedule[DEACTIVATION].erase(++schedule[DEACTIVATION].begin());
|
||||
}
|
||||
if(schedule[STARTUP].size() > 1 && schedule[OFF].size() > 1 && ms >= schedule[STARTUP].at(1) &&
|
||||
schedule[STARTUP].at(1) >= schedule[OFF].at(1))
|
||||
{
|
||||
schedule[OFF].erase(++schedule[OFF].begin());
|
||||
sendMidiToProMicro(id, 127);
|
||||
}
|
||||
if(schedule[ACTIVATION].size() > 1 && schedule[STARTUP].size() > 1 && schedule[VELOCITY].size() > 1 &&
|
||||
ms >= schedule[ACTIVATION].at(1) && schedule[ACTIVATION].at(1) >= schedule[STARTUP].at(1))
|
||||
{
|
||||
schedule[STARTUP].erase(++schedule[STARTUP].begin());
|
||||
sendMidiToProMicro(id, schedule[VELOCITY].at(1));
|
||||
}
|
||||
if(schedule[ON].size() > 1 && schedule[ACTIVATION].size() > 1 &&
|
||||
ms >= schedule[ON].at(1) && schedule[ON].at(1) >= schedule[ACTIVATION].at(1))
|
||||
{
|
||||
schedule[ACTIVATION].erase(++schedule[ACTIVATION].begin());
|
||||
schedule[VELOCITY].erase(++schedule[VELOCITY].begin());
|
||||
sendMidiToProMicro(id, 127);
|
||||
}
|
||||
if(schedule[DEACTIVATION].size() > 1 && schedule[ON].size() > 1 &&
|
||||
ms >= schedule[DEACTIVATION].at(1) && schedule[DEACTIVATION].at(1) >= schedule[ON].at(1))
|
||||
{
|
||||
schedule[ON].erase(++schedule[ON].begin());
|
||||
sendMidiToProMicro(id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Note::checkForErrors()
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
if(ms >= timeSinceActivation + Setting::noteTimeoutMs && timeSinceActivation > 0) resetSchedule();
|
||||
if(schedule[ON].size() > 1 && ms >= schedule[ON].at(1) + Setting::noteTimeoutMs) resetSchedule();
|
||||
}
|
||||
|
||||
void Note::resetSchedule()
|
||||
{
|
||||
if(DEBUG_MODE) Serial.print("Resetting schedule for note: ");
|
||||
if(DEBUG_MODE) Serial.println(id);
|
||||
for(int index = 0; index < 6; index++)
|
||||
{
|
||||
schedule[index].resize(1);
|
||||
schedule[index].at(0) = 0;
|
||||
}
|
||||
schedule[OFF].push_back(millis());
|
||||
if(timeSinceActivation > 0)
|
||||
updateInstance(false);
|
||||
timeSinceActivation = 0;
|
||||
instances = 0;
|
||||
sendMidiToProMicro(id, 0);
|
||||
}
|
||||
|
||||
void Note::resetInstances()
|
||||
{
|
||||
//warning: only call this function in conjunction with resetSchedule()!
|
||||
scheduledLeftNotes = 0;
|
||||
scheduledRightNotes = 0;
|
||||
}
|
||||
|
||||
void Note::scheduleNote(uint8_t velocity)
|
||||
{
|
||||
if(DEBUG_MODE) sendScheduleToSerial();
|
||||
unsigned long ms = millis();
|
||||
unsigned long msAndDelay = ms + fullDelay;
|
||||
using namespace Setting;
|
||||
|
||||
if(velocity > 0) //if note on command
|
||||
{
|
||||
int velocityMs = noteVelocityMs[velocity - 1];
|
||||
instances++;
|
||||
if(instances == 1) //if note is scheduled to deactivate (was 0 before instances++)
|
||||
{
|
||||
if(msAndDelay - velocityMs - startupMs >= schedule[OFF].back()) //if new note can be scheduled with current scheduling
|
||||
{
|
||||
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[ACTIVATION].push_back(msAndDelay - velocityMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
schedule[VELOCITY]. push_back(velocity);
|
||||
timeSinceActivation == ms;
|
||||
updateInstance(true);
|
||||
} else if(msAndDelay - deactivateMs - velocityMs - startupMs >= schedule[ON].back()) //if current scheduling can be modified to still schedule the new note
|
||||
{
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - deactivateMs);
|
||||
schedule[DEACTIVATION].erase(----schedule[DEACTIVATION].end());
|
||||
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[OFF]. erase(----schedule[OFF].end());
|
||||
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
schedule[VELOCITY]. push_back(velocity);
|
||||
timeSinceActivation == ms;
|
||||
updateInstance(true);
|
||||
} else if(msAndDelay - fastDeactivateMs - velocityMs - startupMs >= schedule[ACTIVATION].back()) //if current scheduling can be modified with fast deactivation to schedule the new note
|
||||
{
|
||||
schedule[ON]. push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
||||
schedule[ON]. erase(----schedule[ON].end());
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
||||
schedule[DEACTIVATION].erase(----schedule[DEACTIVATION].end());
|
||||
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[OFF]. erase(----schedule[OFF].end());
|
||||
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
schedule[VELOCITY]. push_back(velocity);
|
||||
timeSinceActivation == ms;
|
||||
updateInstance(true);
|
||||
}
|
||||
} else //note is scheduled to activate and not deactivate
|
||||
{
|
||||
if(msAndDelay - deactivateMs - velocityMs - startupMs >= schedule[ON].back()) //if current scheduling can be modified to still schedule the new note
|
||||
{
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - deactivateMs);
|
||||
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
schedule[VELOCITY]. push_back(velocity);
|
||||
} else if(msAndDelay - fastDeactivateMs - velocityMs - startupMs >= schedule[ACTIVATION].back() && schedule[ACTIVATION].back() > 0) //if current scheduling can be modified with fast deactivation to still schedule the new note
|
||||
{
|
||||
schedule[ON]. push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
||||
schedule[ON]. erase(----schedule[ON].end());
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - velocityMs - startupMs - fastDeactivateMs);
|
||||
schedule[OFF]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[STARTUP]. push_back(msAndDelay - velocityMs - startupMs);
|
||||
schedule[ACTIVATION]. push_back(msAndDelay - velocityMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
schedule[VELOCITY]. push_back(velocity);
|
||||
}
|
||||
}
|
||||
} else if(instances > 0 /*&& velocity == 0*/) //if note off command and note is not already off
|
||||
{
|
||||
if(instances > 1) //if this isn't the last instance
|
||||
{
|
||||
//remove instance and exit
|
||||
instances--;
|
||||
} else //this is the last instance of the note and it should be scheduled
|
||||
{
|
||||
instances = 0;
|
||||
timeSinceActivation == 0;
|
||||
updateInstance(false);
|
||||
|
||||
if(msAndDelay - fastDeactivateMs >= schedule[ACTIVATION].back() && msAndDelay - fastDeactivateMs <= schedule[ON].back() && schedule[ACTIVATION].back() > 0) //if it's efficient to use fast deactivation
|
||||
{
|
||||
schedule[ON]. push_back(msAndDelay - fastDeactivateMs);
|
||||
schedule[ON]. erase(----schedule[ON].end());
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - fastDeactivateMs);
|
||||
schedule[OFF]. push_back(msAndDelay);
|
||||
} else if(msAndDelay - deactivateMs >= schedule[ON].back()) //if regular deactivation works
|
||||
{
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - deactivateMs);
|
||||
schedule[OFF]. push_back(msAndDelay);
|
||||
} else //if all else fails the key shouldn't stay stuck on
|
||||
{
|
||||
if(schedule[ACTIVATION].back() > 0)
|
||||
{
|
||||
//immediately deactivate the key as soon as it makes sound
|
||||
schedule[ON]. push_back(schedule[ACTIVATION].back());
|
||||
schedule[ON]. erase(----schedule[ON].end());
|
||||
schedule[DEACTIVATION].push_back(schedule[ACTIVATION].back());
|
||||
schedule[OFF]. push_back(schedule[ACTIVATION].back() + fastDeactivateMs);
|
||||
} else //this should never happen
|
||||
{
|
||||
schedule[DEACTIVATION].push_back(msAndDelay);
|
||||
schedule[OFF]. push_back(msAndDelay + deactivateMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(DEBUG_MODE) sendScheduleToSerial();
|
||||
}
|
||||
|
||||
void Note::calculateVolume(uint8_t& velocity)
|
||||
{
|
||||
if(velocity > 0)
|
||||
{
|
||||
velocity = round((velocity * Setting::volume) / (double)100);
|
||||
if(velocity > 127)
|
||||
velocity = 127;
|
||||
else if(velocity < 1)
|
||||
velocity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Note::updateInstance(bool state)
|
||||
{
|
||||
if(id < 44)
|
||||
{
|
||||
if(state)
|
||||
scheduledLeftNotes++;
|
||||
else
|
||||
scheduledLeftNotes--;
|
||||
} else
|
||||
{
|
||||
if(state)
|
||||
scheduledRightNotes++;
|
||||
else
|
||||
scheduledRightNotes--;
|
||||
}
|
||||
}
|
||||
|
||||
void Note::sendScheduleToSerial()
|
||||
{
|
||||
Serial.println("-----------------------");
|
||||
Serial.print("Schedule for note: ");
|
||||
Serial.print(id);
|
||||
Serial.print(" At the time: ");
|
||||
Serial.println(millis());
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
for(int j = 0; j < schedule[i].size(); j++)
|
||||
{
|
||||
Serial.print(schedule[i].at(j), DEC);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println(" ");
|
||||
Serial.print(" ");
|
||||
}
|
||||
}
|
||||
|
||||
bool Note::canBeScheduled()
|
||||
{
|
||||
if(id < 44)
|
||||
{
|
||||
if(scheduledLeftNotes < Setting::maxLeftNotes)
|
||||
return true;
|
||||
} else
|
||||
if(id >= 44)
|
||||
{
|
||||
if(scheduledRightNotes < Setting::maxRightNotes)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
52
ESP32/note.h
Normal file
52
ESP32/note.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef NOTE_H
|
||||
#define NOTE_H
|
||||
|
||||
#include <vector>
|
||||
#include <Arduino.h>
|
||||
#include "settings.h"
|
||||
|
||||
class Note
|
||||
{
|
||||
private:
|
||||
typedef std::vector<unsigned long> scheduleV_t;
|
||||
enum ScheduleID
|
||||
{
|
||||
STARTUP,
|
||||
ACTIVATION,
|
||||
VELOCITY,
|
||||
ON,
|
||||
DEACTIVATION,
|
||||
OFF
|
||||
};
|
||||
scheduleV_t schedule[6];
|
||||
int id = 0;
|
||||
int startupMs = Setting::maxStartupMs;
|
||||
int deactivateMs = Setting::maxDeactivateMs;
|
||||
int instances = 0;
|
||||
static int scheduledLeftNotes;
|
||||
static int scheduledRightNotes;
|
||||
static int idGenerator;
|
||||
static int noteVelocityMs[127];
|
||||
unsigned long timeSinceActivation = 0;
|
||||
|
||||
void scheduleNote(uint8_t velocity);
|
||||
void calculateVolume(uint8_t& volume);
|
||||
void updateInstance(bool state);
|
||||
void sendScheduleToSerial();
|
||||
bool canBeScheduled();
|
||||
public:
|
||||
Note();
|
||||
void setStartupMs(int ms) { startupMs = ms; }
|
||||
void setDeactivateMs(int ms) { deactivateMs = ms; }
|
||||
void prepareToSchedule(uint8_t velocity);
|
||||
void checkSchedule();
|
||||
void checkForErrors();
|
||||
void resetSchedule();
|
||||
static void resetInstances();
|
||||
static void setNoteVelocityMs(int velocity, int ms) { noteVelocityMs[velocity] = ms; }
|
||||
static int getNoteVelocityMs(int velocity) { return noteVelocityMs[velocity]; }
|
||||
};
|
||||
|
||||
extern Note notes[88];
|
||||
|
||||
#endif
|
||||
67
ESP32/serial.cpp
Normal file
67
ESP32/serial.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "serial.h"
|
||||
#include "note.h"
|
||||
#include "sustain.h"
|
||||
#include "settings.h"
|
||||
#include "main.h"
|
||||
|
||||
void sendMidiToProMicro(byte note, byte velocity);
|
||||
|
||||
//serial is always handled in bytes, not uint8_t because serial is dumb
|
||||
extern const byte NOTE_HEADER = 201;
|
||||
extern const byte SUSTAIN_HEADER = 202;
|
||||
extern const byte SETTING_HEADER = 203;
|
||||
extern const byte RESET_HEADER = 204;
|
||||
extern const byte VOLUME_HEADER = 205;
|
||||
extern const byte END_HEADER = 206;
|
||||
|
||||
void checkForSerial()
|
||||
{
|
||||
while(Serial.available() > 2)
|
||||
{
|
||||
uint8_t header = Serial.read();
|
||||
if(header >= NOTE_HEADER && header <= VOLUME_HEADER) //make sure the first byte is a header
|
||||
{
|
||||
uint8_t byte1 = Serial.read(); //only declare these if the first byte is a header
|
||||
uint8_t byte2 = Serial.read(); //otherwise the program will keep looping looking for one
|
||||
switch(header)
|
||||
{
|
||||
case NOTE_HEADER:
|
||||
if(byte1 >= 0 && byte1 <= 87 &&
|
||||
byte2 >= Setting::minNoteVelocity && byte2 <= 127)
|
||||
notes[byte1].prepareToSchedule(byte2);
|
||||
break;
|
||||
case SUSTAIN_HEADER:
|
||||
sustain.prepareToSchedule(byte2);
|
||||
break;
|
||||
case SETTING_HEADER:
|
||||
updateSetting(static_cast<SettingID::SettingID>(byte1), byte2);
|
||||
break;
|
||||
case RESET_HEADER:
|
||||
resetAll();
|
||||
break;
|
||||
case VOLUME_HEADER:
|
||||
setVolume(byte2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendMidiToProMicro(byte note, byte velocity)
|
||||
{
|
||||
//invert notes to be compatible with shift registers
|
||||
byte compatibleNote = (note * -1) + 87;
|
||||
byte message[3] ={ NOTE_HEADER, compatibleNote, velocity };
|
||||
//note that velocity is conformed on Pro Micro
|
||||
Serial.write(NOTE_HEADER);
|
||||
Serial.write(compatibleNote);
|
||||
Serial.write(velocity);
|
||||
//Serial.write(END_HEADER);
|
||||
}
|
||||
|
||||
void customSerialToProMicro(byte header, byte byte1, byte byte2)
|
||||
{
|
||||
Serial.write(header);
|
||||
Serial.write(byte1);
|
||||
Serial.write(byte2);
|
||||
}
|
||||
10
ESP32/serial.h
Normal file
10
ESP32/serial.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void checkForSerial();
|
||||
void sendMidiToProMicro(byte note, byte velocity);
|
||||
void customSerialToProMicro(byte header, byte note, byte velocity);
|
||||
|
||||
#endif
|
||||
121
ESP32/settings.cpp
Normal file
121
ESP32/settings.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "settings.h"
|
||||
#include "settings.h"
|
||||
#include "note.h"
|
||||
#include "serial.h"
|
||||
|
||||
//settings within the program
|
||||
int fullDelay = 0;
|
||||
bool acceptMidi = false;
|
||||
unsigned long nextReset = 0; //first reset will happen immediately
|
||||
|
||||
//settings changed by the control box
|
||||
namespace Setting
|
||||
{
|
||||
bool handleNotes = true;
|
||||
bool scheduleNotes = true;
|
||||
int minNoteVelocity = 4;
|
||||
int minStartupMs = 18;
|
||||
int maxStartupMs = 18;
|
||||
int velocityVar = 35;
|
||||
int minDeactivateMs = 75;
|
||||
int maxDeactivateMs = 80;
|
||||
int fastDeactivateMs = 52;
|
||||
int sustainOnMs = 91;
|
||||
int sustainOffMs = 50;
|
||||
int noteTimeoutMs = 10000;
|
||||
int sustainTimeoutMs = 30000;
|
||||
int autoResetMs = 360000;
|
||||
int maxLeftNotes = 26;
|
||||
int maxRightNotes = 22;
|
||||
int volume = 100;
|
||||
}
|
||||
|
||||
void setVolume(int newVolume) { Setting::volume = newVolume; }
|
||||
|
||||
void updateSetting(SettingID::SettingID setting, int value)
|
||||
{
|
||||
using namespace SettingID;
|
||||
using namespace Setting;
|
||||
switch(setting)
|
||||
{
|
||||
case HANDLE_NOTES:
|
||||
scheduleNotes = value;
|
||||
break;
|
||||
case SCHEDULE_NOTES:
|
||||
scheduleNotes = value;
|
||||
break;
|
||||
case MIN_ACCEPTED_VEL:
|
||||
minNoteVelocity = value;
|
||||
break;
|
||||
case PWM_PERCENT:
|
||||
//only setting passed to pro micro
|
||||
extern const byte SETTING_HEADER;
|
||||
customSerialToProMicro(SETTING_HEADER, value, value);
|
||||
break;
|
||||
case MIN_STARTUP_MS:
|
||||
minStartupMs = value;
|
||||
msReset();
|
||||
break;
|
||||
case MAX_STARTUP_MS:
|
||||
maxStartupMs = value;
|
||||
msReset();
|
||||
break;
|
||||
case VELOCITY_VAR:
|
||||
velocityVar = value;
|
||||
msReset();
|
||||
break;
|
||||
case MIN_DEACTIVATE_MS:
|
||||
minDeactivateMs = value;
|
||||
msReset();
|
||||
break;
|
||||
case MAX_DEACTIVATE_MS:
|
||||
maxDeactivateMs = value;
|
||||
msReset();
|
||||
break;
|
||||
case FAST_DEACTIVATE_MS:
|
||||
fastDeactivateMs = value;
|
||||
break;
|
||||
case SUSTAIN_ON_MS:
|
||||
sustainOnMs = value;
|
||||
break;
|
||||
case SUSTAIN_OFF_MS:
|
||||
sustainOffMs = value;
|
||||
break;
|
||||
case NOTE_TIMEOUT_MS:
|
||||
noteTimeoutMs = value * 1000;
|
||||
break;
|
||||
case SUSTAIN_TIMEOUT_MS:
|
||||
sustainTimeoutMs = value * 1000;
|
||||
break;
|
||||
case AUTO_RESET_MS:
|
||||
autoResetMs = value * 60000;
|
||||
break;
|
||||
case MAX_LEFT_NOTES:
|
||||
maxLeftNotes = value;
|
||||
break;
|
||||
case MAX_RIGHT_NOTES:
|
||||
maxRightNotes = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void msReset()
|
||||
{
|
||||
using namespace Setting;
|
||||
for(int noteIndex = 0; noteIndex < 88; noteIndex++)
|
||||
{
|
||||
//calculate the ms for each key linearly. Calculate this now to process less later
|
||||
notes[noteIndex].setStartupMs((noteIndex * (minStartupMs - maxStartupMs) / 88) + maxStartupMs);
|
||||
notes[noteIndex].setDeactivateMs((noteIndex * (maxDeactivateMs - minDeactivateMs) / 88) + minDeactivateMs);
|
||||
}
|
||||
for(int velocityIndex = 0; velocityIndex < 127; velocityIndex++)
|
||||
{
|
||||
//function created through graphing velocity times and creating a function that best fit
|
||||
Note::setNoteVelocityMs(velocityIndex, round(((-25 * velocityIndex) / (double)127) + velocityVar));
|
||||
}
|
||||
|
||||
//calculate the total maximum time for a note cycle as a reference when scheduling keys
|
||||
fullDelay = maxStartupMs + Note::getNoteVelocityMs(0) + maxDeactivateMs;
|
||||
}
|
||||
|
||||
|
||||
60
ESP32/settings.h
Normal file
60
ESP32/settings.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace SettingID //so a using statement can be used in the switch statement
|
||||
{
|
||||
enum SettingID
|
||||
{
|
||||
HANDLE_NOTES,
|
||||
SCHEDULE_NOTES,
|
||||
MIN_ACCEPTED_VEL,
|
||||
PWM_PERCENT,
|
||||
MIN_STARTUP_MS,
|
||||
MAX_STARTUP_MS,
|
||||
VELOCITY_VAR,
|
||||
MIN_DEACTIVATE_MS,
|
||||
MAX_DEACTIVATE_MS,
|
||||
FAST_DEACTIVATE_MS,
|
||||
SUSTAIN_ON_MS,
|
||||
SUSTAIN_OFF_MS,
|
||||
NOTE_TIMEOUT_MS,
|
||||
SUSTAIN_TIMEOUT_MS,
|
||||
AUTO_RESET_MS,
|
||||
MAX_LEFT_NOTES,
|
||||
MAX_RIGHT_NOTES
|
||||
};
|
||||
}
|
||||
|
||||
extern int fullDelay;
|
||||
extern bool acceptMidi;
|
||||
extern unsigned long nextReset;
|
||||
|
||||
namespace Setting
|
||||
{
|
||||
extern bool handleNotes;
|
||||
extern bool scheduleNotes;
|
||||
extern int minNoteVelocity;
|
||||
extern int minSolenoidPWM;
|
||||
extern int minStartupMs;
|
||||
extern int maxStartupMs;
|
||||
extern int velocityVar;
|
||||
extern int minDeactivateMs;
|
||||
extern int maxDeactivateMs;
|
||||
extern int fastDeactivateMs;
|
||||
extern int sustainOnMs;
|
||||
extern int sustainOffMs;
|
||||
extern int noteTimeoutMs;
|
||||
extern int sustainTimeoutMs;
|
||||
extern int autoResetMs;
|
||||
extern int maxLeftNotes;
|
||||
extern int maxRightNotes;
|
||||
extern int volume;
|
||||
}
|
||||
|
||||
void setVolume(int newVolume);
|
||||
void updateSetting(SettingID::SettingID setting, int value);
|
||||
void msReset();
|
||||
|
||||
#endif
|
||||
122
ESP32/sustain.cpp
Normal file
122
ESP32/sustain.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "sustain.h"
|
||||
#include "sustain.h"
|
||||
#include "serial.h"
|
||||
#include "settings.h"
|
||||
|
||||
Sustain sustain;
|
||||
|
||||
Sustain::Sustain()
|
||||
{
|
||||
//initialize one row of vectors so program won't crash while comparing
|
||||
//initialize sustain as off by default
|
||||
for(int index = 0; index < 4; index++)
|
||||
{
|
||||
schedule[index].reserve(6);
|
||||
schedule[index].resize(1);
|
||||
}
|
||||
schedule[OFF].push_back(millis());
|
||||
}
|
||||
|
||||
void Sustain::prepareToSchedule(uint8_t velocity)
|
||||
{
|
||||
const uint8_t SUSTAIN_MIN_VELOCITY = 64;
|
||||
if(Setting::handleNotes && Setting::scheduleNotes)
|
||||
{
|
||||
if(velocity < SUSTAIN_MIN_VELOCITY)
|
||||
{
|
||||
scheduleSustain(false);
|
||||
} else if(instances == 0) //only schedule if sustain is off
|
||||
scheduleSustain(true);
|
||||
} else
|
||||
{
|
||||
if(velocity < SUSTAIN_MIN_VELOCITY)
|
||||
ledcWrite(0, 0);
|
||||
else
|
||||
ledcWrite(0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
void Sustain::checkSchedule()
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
//less checks for sustain because it's slower and less important
|
||||
if(schedule[OFF].size() > 1 && schedule[DEACTIVATION].size() > 1 &&
|
||||
ms >= schedule[OFF].at(1) && schedule[OFF].at(1) >= schedule[DEACTIVATION].at(1))
|
||||
{
|
||||
schedule[DEACTIVATION].erase(schedule[DEACTIVATION].begin()++);
|
||||
ledcWrite(0, 0);
|
||||
}
|
||||
if(schedule[ACTIVATION].size() > 1 && schedule[OFF].size() > 1 &&
|
||||
ms >= schedule[ACTIVATION].at(1) && schedule[ACTIVATION].at(1) >= schedule[OFF].at(1))
|
||||
{
|
||||
schedule[OFF].erase(schedule[OFF].begin()++);
|
||||
ledcWrite(0, 255);
|
||||
}
|
||||
if(schedule[ON].size() > 1 && schedule[ACTIVATION].size() > 1 &&
|
||||
ms >= schedule[ON].at(1) && schedule[ON].at(1) >= schedule[ACTIVATION].at(1))
|
||||
{
|
||||
schedule[ACTIVATION].erase(schedule[ACTIVATION].begin()++);
|
||||
}
|
||||
if(schedule[DEACTIVATION].size() > 1 && schedule[ON].size() > 1 &&
|
||||
ms >= schedule[DEACTIVATION].at(1) && schedule[DEACTIVATION].at(1) >= schedule[ON].at(1))
|
||||
{
|
||||
schedule[ON].erase(schedule[ON].begin()++);
|
||||
ledcWrite(0, 30);
|
||||
}
|
||||
}
|
||||
|
||||
void Sustain::checkForErrors()
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
if(ms >= timeSinceActivation + Setting::sustainTimeoutMs && timeSinceActivation > 0) resetSchedule();
|
||||
if(schedule[ON].size() > 1) if(ms >= schedule[ON].at(1) + Setting::sustainTimeoutMs) resetSchedule();
|
||||
}
|
||||
|
||||
void Sustain::resetSchedule()
|
||||
{
|
||||
for(int index = 0; index < 4; index++)
|
||||
schedule[index].resize(1);
|
||||
schedule[OFF].push_back(millis());
|
||||
timeSinceActivation = 0;
|
||||
instances = 0;
|
||||
ledcWrite(0, 0);
|
||||
}
|
||||
|
||||
void Sustain::scheduleSustain(bool state)
|
||||
{
|
||||
unsigned long ms = millis();
|
||||
unsigned long msAndDelay = ms + fullDelay;
|
||||
using namespace Setting;
|
||||
|
||||
if(state)
|
||||
{
|
||||
instances++;
|
||||
if(msAndDelay - sustainOnMs >= schedule[OFF].back()) //if sustain can be scheduled with current scheduling
|
||||
{
|
||||
schedule[ACTIVATION].push_back(msAndDelay - sustainOnMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
timeSinceActivation = ms;
|
||||
} else if(msAndDelay - sustainOffMs - sustainOnMs >= schedule[ON].back()) //if current scheduling can be modified to still schedule the sustain
|
||||
{
|
||||
schedule[ON]. push_back(msAndDelay - sustainOnMs - sustainOffMs);
|
||||
schedule[ON]. erase(----schedule[ON].end());
|
||||
schedule[OFF]. push_back(msAndDelay - sustainOnMs);
|
||||
schedule[OFF]. erase(----schedule[OFF].end());
|
||||
schedule[ACTIVATION].push_back(msAndDelay - sustainOnMs);
|
||||
schedule[ON]. push_back(msAndDelay);
|
||||
timeSinceActivation = ms;
|
||||
}
|
||||
} else if(instances > 0) //if sustain off command and sustain is not already off
|
||||
{
|
||||
instances == 0;
|
||||
if(msAndDelay - sustainOffMs >= schedule[ON].back()) //if sustain can be ideally deactivated
|
||||
{
|
||||
schedule[DEACTIVATION].push_back(msAndDelay - sustainOffMs);
|
||||
schedule[OFF]. push_back(msAndDelay);
|
||||
} else //deactivate sustain anyways so it's not stuck on
|
||||
{
|
||||
schedule[DEACTIVATION].push_back(msAndDelay);
|
||||
schedule[OFF]. push_back(msAndDelay + sustainOffMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
ESP32/sustain.h
Normal file
33
ESP32/sustain.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef SUSTAIN_H
|
||||
#define SUSTAIN_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
|
||||
class Sustain
|
||||
{
|
||||
private:
|
||||
typedef std::vector<unsigned long> scheduleV_t;
|
||||
enum ScheduleID
|
||||
{
|
||||
ACTIVATION,
|
||||
ON,
|
||||
DEACTIVATION,
|
||||
OFF
|
||||
};
|
||||
scheduleV_t schedule[4];
|
||||
int instances = 0;
|
||||
unsigned long timeSinceActivation = 0;
|
||||
|
||||
void scheduleSustain(bool state);
|
||||
public:
|
||||
Sustain();
|
||||
void prepareToSchedule(uint8_t velocity);
|
||||
void checkSchedule();
|
||||
void checkForErrors();
|
||||
void resetSchedule();
|
||||
};
|
||||
|
||||
extern Sustain sustain;
|
||||
|
||||
#endif
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Brandon Switzer
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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.
|
||||
37
Piano Project.sln
Normal file
37
Piano Project.sln
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29025.244
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ESP32", "ESP32\ESP32.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProMicro", "ProMicro\ProMicro.vcxproj", "{62539448-1BC7-47A2-A21E-53DE25ACD53F}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlBox", "ControlBox\ControlBox.vcxproj", "{F084E901-4464-411A-9291-371A63C4AA95}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32
|
||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32
|
||||
{62539448-1BC7-47A2-A21E-53DE25ACD53F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{62539448-1BC7-47A2-A21E-53DE25ACD53F}.Debug|x86.Build.0 = Debug|Win32
|
||||
{62539448-1BC7-47A2-A21E-53DE25ACD53F}.Release|x86.ActiveCfg = Release|Win32
|
||||
{62539448-1BC7-47A2-A21E-53DE25ACD53F}.Release|x86.Build.0 = Release|Win32
|
||||
{F084E901-4464-411A-9291-371A63C4AA95}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F084E901-4464-411A-9291-371A63C4AA95}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F084E901-4464-411A-9291-371A63C4AA95}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F084E901-4464-411A-9291-371A63C4AA95}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DEECECCC-4575-4EF1-9891-F9580C4AD9FE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
30
ProMicro/ProMicro.ino
Normal file
30
ProMicro/ProMicro.ino
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <MIDIUSB.h>
|
||||
#include "shiftRegister.h"
|
||||
#include "serial.h"
|
||||
#include "midi.h"
|
||||
|
||||
extern const bool DEBUG_MODE = false;
|
||||
|
||||
void setup()
|
||||
{
|
||||
intitializeRegisters();
|
||||
delay(2000);
|
||||
|
||||
Serial.begin(38400);
|
||||
Serial1.begin(38400);
|
||||
|
||||
pinMode(19, OUTPUT);
|
||||
pinMode(20, OUTPUT);
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(19, HIGH);
|
||||
digitalWrite(20, HIGH);
|
||||
|
||||
if(DEBUG_MODE)
|
||||
testRegisters();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
checkForMidiUSB();
|
||||
checkForSerial();
|
||||
}
|
||||
133
ProMicro/ProMicro.vcxproj
Normal file
133
ProMicro/ProMicro.vcxproj
Normal file
File diff suppressed because one or more lines are too long
45
ProMicro/ProMicro.vcxproj.filters
Normal file
45
ProMicro/ProMicro.vcxproj.filters
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="ProMicro.ino" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="__vm\.ProMicro.vsarduino.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="midi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="serial.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shiftRegister.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="midi.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="serial.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shiftRegister.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
85
ProMicro/__vm/.ProMicro.vsarduino.h
Normal file
85
ProMicro/__vm/.ProMicro.vsarduino.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Editor: https://www.visualmicro.com/
|
||||
This file is for intellisense purpose only.
|
||||
Visual micro (and the arduino ide) ignore this code during compilation. This code is automatically maintained by visualmicro, manual changes to this file will be overwritten
|
||||
The contents of the _vm sub folder can be deleted prior to publishing a project
|
||||
All non-arduino files created by visual micro and all visual studio project or solution files can be freely deleted and are not required to compile a sketch (do not delete your own code!).
|
||||
Note: debugger breakpoints are stored in '.sln' or '.asln' files, knowledge of last uploaded breakpoints is stored in the upload.vmps.xml file. Both files are required to continue a previous debug session without needing to compile and upload again
|
||||
|
||||
Hardware: Arduino Leonardo, Platform=avr, Package=arduino
|
||||
*/
|
||||
|
||||
#if defined(_VMICRO_INTELLISENSE)
|
||||
|
||||
#ifndef _VSARDUINO_H_
|
||||
#define _VSARDUINO_H_
|
||||
#define __AVR_atmega32u4__
|
||||
#define __AVR_ATmega32U4__
|
||||
#define __AVR_ATmega32u4__
|
||||
#define F_CPU 16000000L
|
||||
#define ARDUINO 10809
|
||||
#define ARDUINO_AVR_LEONARDO
|
||||
#define ARDUINO_ARCH_AVR
|
||||
#define USB_VID 0x2341
|
||||
#define USB_PID 0x8036
|
||||
#define __cplusplus 201103L
|
||||
#define _Pragma(x)
|
||||
#define __AVR__
|
||||
#define __inline__
|
||||
#define __asm__(...)
|
||||
#define __extension__
|
||||
#define __inline__
|
||||
#define __volatile__
|
||||
#define GCC_VERSION 40902
|
||||
#define __cplusplus 201103L
|
||||
|
||||
#define volatile(va_arg)
|
||||
#define _CONST
|
||||
#define __builtin_va_start
|
||||
#define __builtin_va_end
|
||||
#define __attribute__(...)
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#define prog_void
|
||||
#define PGM_VOID_P int
|
||||
|
||||
|
||||
#ifndef __builtin_constant_p
|
||||
#define __builtin_constant_p __attribute__((__const__))
|
||||
#endif
|
||||
#ifndef __builtin_strlen
|
||||
#define __builtin_strlen __attribute__((__const__))
|
||||
#endif
|
||||
|
||||
|
||||
#define NEW_H
|
||||
typedef void *__builtin_va_list;
|
||||
//extern "C" void __cxa_pure_virtual() {;}
|
||||
|
||||
typedef int div_t;
|
||||
typedef int ldiv_t;
|
||||
|
||||
|
||||
typedef void *__builtin_va_list;
|
||||
//extern "C" void __cxa_pure_virtual() {;}
|
||||
|
||||
|
||||
|
||||
#include "arduino.h"
|
||||
#include <pins_arduino.h>
|
||||
//#undef F
|
||||
//#define F(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
#undef PSTR
|
||||
#define PSTR(string_literal) ((const PROGMEM char *)(string_literal))
|
||||
|
||||
//typedef unsigned char uint8_t;
|
||||
//typedef unsigned int uint8_t;
|
||||
|
||||
#define pgm_read_byte(address_short) uint8_t()
|
||||
#define pgm_read_word(address_short) uint16_t()
|
||||
#define pgm_read_dword(address_short) uint32_t()
|
||||
#define pgm_read_float(address_short) float()
|
||||
#define pgm_read_ptr(address_short) short()
|
||||
|
||||
#include "ProMicro.ino"
|
||||
#endif
|
||||
#endif
|
||||
12
ProMicro/__vm/Compile.vmps.xml
Normal file
12
ProMicro/__vm/Compile.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
9
ProMicro/__vm/Configuration.Debug.vmps.xml
Normal file
9
ProMicro/__vm/Configuration.Debug.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
12
ProMicro/__vm/Upload.vmps.xml
Normal file
12
ProMicro/__vm/Upload.vmps.xml
Normal file
File diff suppressed because one or more lines are too long
70
ProMicro/midi.cpp
Normal file
70
ProMicro/midi.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <MIDIUSB.h>
|
||||
#include "midi.h"
|
||||
#include "shiftRegister.h"
|
||||
#include "serial.h"
|
||||
|
||||
void decodeMidi(uint8_t header, uint8_t byte1, uint8_t byte2, uint8_t byte3);
|
||||
|
||||
extern const bool DEBUG_MODE;
|
||||
|
||||
void checkForMidiUSB()
|
||||
{
|
||||
midiEventPacket_t rx; //midi data struct from midiUSB libray
|
||||
do
|
||||
{
|
||||
rx = MidiUSB.read(); //get queued info from USB
|
||||
if(rx.header != 0)
|
||||
{
|
||||
decodeMidi(rx.header, rx.byte1, rx.byte2, rx.byte3);
|
||||
}
|
||||
} while(rx.header != 0);
|
||||
}
|
||||
|
||||
void decodeMidi(uint8_t header, uint8_t byte1, uint8_t byte2, uint8_t byte3)
|
||||
{
|
||||
if(DEBUG_MODE)
|
||||
{
|
||||
String message[] =
|
||||
{
|
||||
"-----------",
|
||||
static_cast<String>(header),
|
||||
static_cast<String>(byte1),
|
||||
static_cast<String>(byte2),
|
||||
static_cast<String>(byte3),
|
||||
"-----------"
|
||||
};
|
||||
sendSerialToUSB(message, 6);
|
||||
}
|
||||
|
||||
const uint8_t NOTE_ON_HEADER = 9;
|
||||
const uint8_t NOTE_OFF_HEADER = 8;
|
||||
const uint8_t CONTROL_CHANGE_HEADER = 8;
|
||||
const uint8_t SUSTAIN_STATUS_BYTE = 176;
|
||||
const uint8_t MIN_NOTE_PITCH = 21;
|
||||
const uint8_t MAX_NOTE_PITCH = 108;
|
||||
switch(header)
|
||||
{
|
||||
case NOTE_ON_HEADER:
|
||||
if(byte2 >= MIN_NOTE_PITCH && byte2 <= MAX_NOTE_PITCH &&
|
||||
byte3 >= 0 && byte3 <= 127)
|
||||
{
|
||||
uint8_t note = (byte2 - MIN_NOTE_PITCH) * -1 + 87;
|
||||
activateNote(note, byte3);
|
||||
}
|
||||
break;
|
||||
case NOTE_OFF_HEADER:
|
||||
if(byte2 >= MIN_NOTE_PITCH && byte2 <= MAX_NOTE_PITCH)
|
||||
{
|
||||
uint8_t note = (byte2 - MIN_NOTE_PITCH) * -1 + 87;
|
||||
activateNote(note, 0);
|
||||
}
|
||||
break;
|
||||
case SUSTAIN_STATUS_BYTE:
|
||||
if(byte1 == SUSTAIN_STATUS_BYTE)
|
||||
{
|
||||
extern const uint8_t SUSTAIN_HEADER;
|
||||
sendSerialToMain(SUSTAIN_HEADER, byte3, byte3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
8
ProMicro/midi.h
Normal file
8
ProMicro/midi.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef MIDI_H
|
||||
#define MIDI_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void checkForMidiUSB();
|
||||
|
||||
#endif
|
||||
48
ProMicro/serial.cpp
Normal file
48
ProMicro/serial.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "serial.h"
|
||||
#include "shiftRegister.h"
|
||||
|
||||
extern const bool DEBUG_MODE;
|
||||
extern const uint8_t SUSTAIN_HEADER = 202;
|
||||
|
||||
void checkForSerial()
|
||||
{
|
||||
const byte NOTE_HEADER = 201;
|
||||
const byte SETTING_HEADER = 203;
|
||||
while(Serial1.available() > 2)
|
||||
{
|
||||
uint8_t header = Serial1.read();
|
||||
if(header == NOTE_HEADER)
|
||||
{
|
||||
uint8_t note = Serial1.read();
|
||||
uint8_t velocity = Serial1.read();
|
||||
activateNote(note, velocity);
|
||||
if(DEBUG_MODE)
|
||||
{
|
||||
Serial.println(note);
|
||||
Serial.println(velocity);
|
||||
Serial.println("----------------");
|
||||
}
|
||||
} else
|
||||
if(header == SETTING_HEADER)
|
||||
{
|
||||
uint8_t value1 = Serial1.read();
|
||||
uint8_t value2 = Serial1.read();
|
||||
//Pro Micro knows that only one setting is being sent
|
||||
pwmPercent = value2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendSerialToMain(byte header, byte setting, byte value)
|
||||
{
|
||||
Serial1.print(header);
|
||||
Serial1.print(setting);
|
||||
Serial1.print(value);
|
||||
}
|
||||
|
||||
void sendSerialToUSB(String* message, int lengthOfMessage)
|
||||
{
|
||||
for(int index = 0; index < lengthOfMessage; index++)
|
||||
Serial.println(message[index]);
|
||||
}
|
||||
|
||||
10
ProMicro/serial.h
Normal file
10
ProMicro/serial.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
void checkForSerial();
|
||||
void sendSerialToMain(byte header, byte setting, byte value);
|
||||
void sendSerialToUSB(String* message, int lengthOfMessage);
|
||||
|
||||
#endif
|
||||
48
ProMicro/shiftRegister.cpp
Normal file
48
ProMicro/shiftRegister.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
//Latchpin: Pin 10 goes to second register input
|
||||
//Datapin: Pin 16 goes to first register input
|
||||
//Clockpin: Pin 15 goes to third register input
|
||||
const int ShiftPWM_latchPin=18; //values assigned before includes
|
||||
const bool ShiftPWM_invertOutputs = false;
|
||||
const bool ShiftPWM_balanceLoad = false;
|
||||
#include <ShiftPWM.h>
|
||||
#include <CShiftPWM.h>
|
||||
#include "shiftRegister.h"
|
||||
|
||||
void conformVelocity(uint8_t& velocity);
|
||||
|
||||
extern uint8_t pwmPercent = 45;
|
||||
const char MAX_PWM = 235;
|
||||
|
||||
void intitializeRegisters()
|
||||
{
|
||||
const char PWM_FREQUENCY = 75;
|
||||
const int NUM_REGISTERS = 11;
|
||||
ShiftPWM.SetAmountOfRegisters(NUM_REGISTERS);
|
||||
ShiftPWM.Start(PWM_FREQUENCY, MAX_PWM);
|
||||
}
|
||||
|
||||
void activateNote(uint8_t note, uint8_t velocity)
|
||||
{
|
||||
if(velocity > 0)
|
||||
conformVelocity(velocity);
|
||||
ShiftPWM.SetOne(note, velocity);
|
||||
}
|
||||
|
||||
void conformVelocity(uint8_t& velocity)
|
||||
{
|
||||
//conforms velocity from 0-127 to 0-255 while taking into account the minimum possible solenoid PWM
|
||||
const double MIN_PWM = round(MAX_PWM * pwmPercent / static_cast<double>(100));
|
||||
velocity = round(velocity * (MAX_PWM - MIN_PWM) / static_cast<double>(127) + MIN_PWM);
|
||||
}
|
||||
|
||||
void testRegisters()
|
||||
{
|
||||
for(int led = 0; led < 88; led++)
|
||||
{
|
||||
ShiftPWM.SetOne(led, MAX_PWM);
|
||||
delay(50);
|
||||
ShiftPWM.SetOne(led, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
ProMicro/shiftRegister.h
Normal file
12
ProMicro/shiftRegister.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef SHIFT_REGISTER_H
|
||||
#define SHIFT_REGISTER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
extern uint8_t pwmPercent;
|
||||
|
||||
void intitializeRegisters();
|
||||
void activateNote(uint8_t note, uint8_t velocity);
|
||||
void testRegisters();
|
||||
|
||||
#endif
|
||||
35
README.md
Normal file
35
README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Piano Project
|
||||
Arduino Code for a DIY Player Piano.
|
||||
|
||||
See more information about it here: brandonswitzer.squarespace.com/player-piano
|
||||
|
||||
Role of each of the Arduinos:
|
||||
|
||||
ESP32:
|
||||
|
||||
- Receives and decodes bluetooth message
|
||||
|
||||
- Schedules notes
|
||||
|
||||
- Handles sustain
|
||||
|
||||
- Handles setting changes
|
||||
|
||||
Pro Micro:
|
||||
|
||||
- Receives data from ESP32 and/or USB
|
||||
|
||||
- Activates notes using shift registers
|
||||
|
||||
Control Box (Pro Micro):
|
||||
|
||||
- Interface for changing settings
|
||||
|
||||
- Receives data from USB and sends it to ESP32
|
||||
|
||||
- Reset button
|
||||
|
||||
Required Arduino Libraries:
|
||||
- ShiftPWM (Pro Micro)
|
||||
- MIDIUSB (Pro Micro & Control Box)
|
||||
- LiquidCrystal_I2C (Control Box)
|
||||
Reference in New Issue
Block a user