├── .DS_Store ├── mcp6004.png ├── sweet_pcb.jpg ├── 20191105_181811.jpg ├── SweetSixteen_.jpg ├── firmware ├── .DS_Store ├── Sweet_3 │ ├── .DS_Store │ ├── usb_name.c │ ├── TxHelper.h │ ├── utils.ino │ ├── LICENSE │ ├── SYSEX_SPEC.md │ ├── config.h │ ├── TxHelper.cpp │ ├── README.md │ ├── configuration.ino │ ├── sysex.ino │ └── Sweet_3.ino ├── Old_versions │ ├── 2.0.1 │ │ └── _16n_faderbank_firmware │ │ │ ├── usb_name.c │ │ │ ├── TxHelper.h │ │ │ ├── utils.ino │ │ │ ├── LICENSE │ │ │ ├── config.h │ │ │ ├── SYSEX_SPEC.md │ │ │ ├── TxHelper.cpp │ │ │ ├── README.md │ │ │ ├── configuration.ino │ │ │ ├── sysex.ino │ │ │ └── _16n_faderbank_firmware.ino │ ├── First_version │ │ └── _16n_faderbank_firmware_Sweet │ │ │ ├── usb_name.c │ │ │ ├── TxHelper.h │ │ │ ├── TxHelper.cpp │ │ │ ├── config.h │ │ │ └── _16n_faderbank_firmware_Sweet.ino │ ├── Sweet_mkII │ │ └── _16n_faderbank_firmware_Sweet_mkII │ │ │ ├── usb_name.c │ │ │ ├── TxHelper.h │ │ │ ├── TxHelper.cpp │ │ │ ├── config.h │ │ │ └── _16n_faderbank_firmware_Sweet_mkII.ino │ └── README.md └── README.md ├── hardware ├── README.md └── Sweet Sixteen BOM.csv ├── LICENSE.md └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/.DS_Store -------------------------------------------------------------------------------- /mcp6004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/mcp6004.png -------------------------------------------------------------------------------- /sweet_pcb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/sweet_pcb.jpg -------------------------------------------------------------------------------- /20191105_181811.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/20191105_181811.jpg -------------------------------------------------------------------------------- /SweetSixteen_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/SweetSixteen_.jpg -------------------------------------------------------------------------------- /firmware/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/firmware/.DS_Store -------------------------------------------------------------------------------- /firmware/Sweet_3/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M4ngu/Sweet-Sixteen/HEAD/firmware/Sweet_3/.DS_Store -------------------------------------------------------------------------------- /firmware/Sweet_3/usb_name.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PRODUCT_NAME \ 4 | { \ 5 | '1', '6', 'n' \ 6 | } 7 | #define PRODUCT_NAME_LEN 3 8 | 9 | struct usb_string_descriptor_struct usb_string_product_name = { 10 | 2 + PRODUCT_NAME_LEN * 2, 11 | 3, 12 | PRODUCT_NAME}; 13 | -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/usb_name.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PRODUCT_NAME \ 4 | { \ 5 | '1', '6', 'n' \ 6 | } 7 | #define PRODUCT_NAME_LEN 3 8 | 9 | struct usb_string_descriptor_struct usb_string_product_name = { 10 | 2 + PRODUCT_NAME_LEN * 2, 11 | 3, 12 | PRODUCT_NAME}; 13 | -------------------------------------------------------------------------------- /firmware/Old_versions/First_version/_16n_faderbank_firmware_Sweet/usb_name.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PRODUCT_NAME \ 4 | { \ 5 | '1', '6', 'n' \ 6 | } 7 | #define PRODUCT_NAME_LEN 3 8 | 9 | struct usb_string_descriptor_struct usb_string_product_name = { 10 | 2 + PRODUCT_NAME_LEN * 2, 11 | 3, 12 | PRODUCT_NAME}; 13 | -------------------------------------------------------------------------------- /firmware/Old_versions/Sweet_mkII/_16n_faderbank_firmware_Sweet_mkII/usb_name.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define PRODUCT_NAME \ 4 | { \ 5 | '1', '6', 'n' \ 6 | } 7 | #define PRODUCT_NAME_LEN 3 8 | 9 | struct usb_string_descriptor_struct usb_string_product_name = { 10 | 2 + PRODUCT_NAME_LEN * 2, 11 | 3, 12 | PRODUCT_NAME}; 13 | -------------------------------------------------------------------------------- /firmware/Sweet_3/TxHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #ifndef TxHelper_h 8 | #define TxHelper_h 9 | 10 | // i2c 11 | #include 12 | 13 | #include "Arduino.h" 14 | 15 | struct TxResponse 16 | { 17 | byte Command; 18 | byte Output; 19 | int Value; 20 | }; 21 | 22 | struct TxIO 23 | { 24 | short Port; 25 | short Mode; 26 | }; 27 | 28 | class TxHelper 29 | { 30 | public: 31 | static TxResponse Parse(size_t len); 32 | static TxIO DecodeIO(int io); 33 | 34 | static void SetPorts(int ports); 35 | static void SetModes(int modes); 36 | static void UseWire1(bool use); 37 | 38 | protected: 39 | static int Ports; 40 | static int Modes; 41 | static bool W0; 42 | 43 | private: 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/TxHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #ifndef TxHelper_h 8 | #define TxHelper_h 9 | 10 | // i2c 11 | #include 12 | 13 | #include "Arduino.h" 14 | 15 | struct TxResponse 16 | { 17 | byte Command; 18 | byte Output; 19 | int Value; 20 | }; 21 | 22 | struct TxIO 23 | { 24 | short Port; 25 | short Mode; 26 | }; 27 | 28 | class TxHelper 29 | { 30 | public: 31 | static TxResponse Parse(size_t len); 32 | static TxIO DecodeIO(int io); 33 | 34 | static void SetPorts(int ports); 35 | static void SetModes(int modes); 36 | static void UseWire1(bool use); 37 | 38 | protected: 39 | static int Ports; 40 | static int Modes; 41 | static bool W0; 42 | 43 | private: 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /firmware/Old_versions/First_version/_16n_faderbank_firmware_Sweet/TxHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #ifndef TxHelper_h 8 | #define TxHelper_h 9 | 10 | // i2c 11 | #include 12 | 13 | #include "Arduino.h" 14 | 15 | struct TxResponse 16 | { 17 | byte Command; 18 | byte Output; 19 | int Value; 20 | }; 21 | 22 | struct TxIO 23 | { 24 | short Port; 25 | short Mode; 26 | }; 27 | 28 | class TxHelper 29 | { 30 | public: 31 | static TxResponse Parse(size_t len); 32 | static TxIO DecodeIO(int io); 33 | 34 | static void SetPorts(int ports); 35 | static void SetModes(int modes); 36 | static void UseWire1(bool use); 37 | 38 | protected: 39 | static int Ports; 40 | static int Modes; 41 | static bool W0; 42 | 43 | private: 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /firmware/Old_versions/Sweet_mkII/_16n_faderbank_firmware_Sweet_mkII/TxHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #ifndef TxHelper_h 8 | #define TxHelper_h 9 | 10 | // i2c 11 | #include 12 | 13 | #include "Arduino.h" 14 | 15 | struct TxResponse 16 | { 17 | byte Command; 18 | byte Output; 19 | int Value; 20 | }; 21 | 22 | struct TxIO 23 | { 24 | short Port; 25 | short Mode; 26 | }; 27 | 28 | class TxHelper 29 | { 30 | public: 31 | static TxResponse Parse(size_t len); 32 | static TxIO DecodeIO(int io); 33 | 34 | static void SetPorts(int ports); 35 | static void SetModes(int modes); 36 | static void UseWire1(bool use); 37 | 38 | protected: 39 | static int Ports; 40 | static int Modes; 41 | static bool W0; 42 | 43 | private: 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /hardware/README.md: -------------------------------------------------------------------------------- 1 | If you want to order pcbs yourself make sure you have a valid replacement for the faders, the ones I use may have a weird pinout. You can also edit the board in order to use your desired fader model. 2 | 3 | The voltage of the faders (when there's nothing plugged in) could be easy modified by replacing the IC4 for another LDO and changing the value of the summing resistors in the MCP6004 circuits, in order to get 3,3v (or close) from the LDO once passes throught the op-amp. 4 | 5 | The formula is pretty simple: 6 | 7 | Vin * (RFB / RIN) = 3,3 8 | 9 | In this case the IC4 LDO is an 8V regulator, so: 10 | 11 | 8v * (33k / 80,6k) = 3,27v 12 | 13 | ![MCP6004 circuit](/mcp6004.png) 14 | 15 | Keep in mind that if you change the 33k feedback resistor for other value, the summing resistors for the voltage reference must be also adapted with the same criteria: 16 | 17 | 5v * (33k / 49,9k) = 3,3v 18 | -------------------------------------------------------------------------------- /firmware/Sweet_3/utils.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Utility Functions 3 | * (c) 2020 by Tom Armitage 4 | * MIT License 5 | */ 6 | 7 | void readEEPROMArray(int start, byte buffer[], int length) { 8 | for (int i = 0; i < length; i++) { 9 | buffer[i] = EEPROM.read(start+i); 10 | } 11 | } 12 | 13 | void writeEEPROMArray(int start, byte buffer[], int length) { 14 | for (int i = 0; i < length; i++) { 15 | EEPROM.write(start+i, buffer[i]); 16 | } 17 | } 18 | 19 | void printHex(uint8_t num) { 20 | char hexCar[2]; 21 | 22 | sprintf(hexCar, "%02X", num); 23 | Serial.print(hexCar); 24 | } 25 | 26 | void printHexArray(byte* array, int size) { 27 | for(int i=0; i 12 | 13 | // initialize the basic values for the TXi 14 | int TxHelper::Ports = 8; 15 | int TxHelper::Modes = 3; 16 | bool TxHelper::W0 = true; 17 | 18 | /** 19 | * Set the number of ports the device has (TXi is 8; FADER is 16) 20 | */ 21 | void TxHelper::SetPorts(int ports) 22 | { 23 | TxHelper::Ports = ports; 24 | } 25 | 26 | /** 27 | * Determine how the modes shift (TXi shifts 3; FADER shifts 4) 28 | */ 29 | void TxHelper::SetModes(int modes) 30 | { 31 | TxHelper::Modes = modes; 32 | } 33 | 34 | /** 35 | * Determine if the Wire or Wire1 interface should be used 36 | */ 37 | void TxHelper::UseWire1(bool use) 38 | { 39 | TxHelper::W0 = !use; 40 | } 41 | 42 | /** 43 | * Parse the response coming down the wire 44 | */ 45 | TxResponse TxHelper::Parse(size_t len) 46 | { 47 | 48 | TxResponse response; 49 | 50 | int buffer[4] = {0, 0, 0, 0}; 51 | 52 | // zero out the read buffer 53 | int counterPal = 0; 54 | memset(buffer, 0, sizeof(buffer)); 55 | 56 | // read the data 57 | while (1 < (W0 ? Wire.available() : Wire1.available())) 58 | { 59 | if (counterPal < 4) 60 | { 61 | buffer[counterPal++] = (W0 ? Wire.read() : Wire1.read()); 62 | } 63 | } 64 | // get the last byte 65 | buffer[counterPal] = (W0 ? Wire.read() : Wire1.read()); 66 | 67 | // Serial.printf("Buffers: %d, %d, %d, %d\n", buffer[0], buffer[1], buffer[2], buffer[3]); 68 | 69 | uint16_t temp = (uint16_t)((buffer[2] << 8) + (buffer[3])); 70 | int16_t temp2 = (int16_t)temp; 71 | 72 | response.Command = buffer[0]; 73 | response.Output = buffer[1]; 74 | response.Value = (int)temp2; 75 | 76 | // Serial.printf("temp: %d; temp2: %d; helper: %d\n", temp, temp2, response.Value); 77 | 78 | return response; 79 | } 80 | 81 | /** 82 | * Decode the IO from the value coming down the wire 83 | */ 84 | TxIO TxHelper::DecodeIO(int io) 85 | { 86 | 87 | TxIO decoded; 88 | 89 | // turn it into 0-7 for the individual device's port (TXi) 90 | decoded.Port = io % Ports; 91 | 92 | // output mode (0-7 = normal; 8-15 = Quantized; 16-23 = Note Number) (TXi) 93 | decoded.Mode = io >> Modes; 94 | 95 | return decoded; 96 | } 97 | -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/TxHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #include "TxHelper.h" 8 | #include "Arduino.h" 9 | 10 | // i2c 11 | #include 12 | 13 | // initialize the basic values for the TXi 14 | int TxHelper::Ports = 8; 15 | int TxHelper::Modes = 3; 16 | bool TxHelper::W0 = true; 17 | 18 | /** 19 | * Set the number of ports the device has (TXi is 8; FADER is 16) 20 | */ 21 | void TxHelper::SetPorts(int ports) 22 | { 23 | TxHelper::Ports = ports; 24 | } 25 | 26 | /** 27 | * Determine how the modes shift (TXi shifts 3; FADER shifts 4) 28 | */ 29 | void TxHelper::SetModes(int modes) 30 | { 31 | TxHelper::Modes = modes; 32 | } 33 | 34 | /** 35 | * Determine if the Wire or Wire1 interface should be used 36 | */ 37 | void TxHelper::UseWire1(bool use) 38 | { 39 | TxHelper::W0 = !use; 40 | } 41 | 42 | /** 43 | * Parse the response coming down the wire 44 | */ 45 | TxResponse TxHelper::Parse(size_t len) 46 | { 47 | 48 | TxResponse response; 49 | 50 | int buffer[4] = {0, 0, 0, 0}; 51 | 52 | // zero out the read buffer 53 | int counterPal = 0; 54 | memset(buffer, 0, sizeof(buffer)); 55 | 56 | // read the data 57 | while (1 < (W0 ? Wire.available() : Wire1.available())) 58 | { 59 | if (counterPal < 4) 60 | { 61 | buffer[counterPal++] = (W0 ? Wire.read() : Wire1.read()); 62 | } 63 | } 64 | // get the last byte 65 | buffer[counterPal] = (W0 ? Wire.read() : Wire1.read()); 66 | 67 | // Serial.printf("Buffers: %d, %d, %d, %d\n", buffer[0], buffer[1], buffer[2], buffer[3]); 68 | 69 | uint16_t temp = (uint16_t)((buffer[2] << 8) + (buffer[3])); 70 | int16_t temp2 = (int16_t)temp; 71 | 72 | response.Command = buffer[0]; 73 | response.Output = buffer[1]; 74 | response.Value = (int)temp2; 75 | 76 | // Serial.printf("temp: %d; temp2: %d; helper: %d\n", temp, temp2, response.Value); 77 | 78 | return response; 79 | } 80 | 81 | /** 82 | * Decode the IO from the value coming down the wire 83 | */ 84 | TxIO TxHelper::DecodeIO(int io) 85 | { 86 | 87 | TxIO decoded; 88 | 89 | // turn it into 0-7 for the individual device's port (TXi) 90 | decoded.Port = io % Ports; 91 | 92 | // output mode (0-7 = normal; 8-15 = Quantized; 16-23 = Note Number) (TXi) 93 | decoded.Mode = io >> Modes; 94 | 95 | return decoded; 96 | } 97 | -------------------------------------------------------------------------------- /firmware/Old_versions/First_version/_16n_faderbank_firmware_Sweet/TxHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #include "TxHelper.h" 8 | #include "Arduino.h" 9 | 10 | // i2c 11 | #include 12 | 13 | // initialize the basic values for the TXi 14 | int TxHelper::Ports = 8; 15 | int TxHelper::Modes = 3; 16 | bool TxHelper::W0 = true; 17 | 18 | /** 19 | * Set the number of ports the device has (TXi is 8; FADER is 16) 20 | */ 21 | void TxHelper::SetPorts(int ports) 22 | { 23 | TxHelper::Ports = ports; 24 | } 25 | 26 | /** 27 | * Determine how the modes shift (TXi shifts 3; FADER shifts 4) 28 | */ 29 | void TxHelper::SetModes(int modes) 30 | { 31 | TxHelper::Modes = modes; 32 | } 33 | 34 | /** 35 | * Determine if the Wire or Wire1 interface should be used 36 | */ 37 | void TxHelper::UseWire1(bool use) 38 | { 39 | TxHelper::W0 = !use; 40 | } 41 | 42 | /** 43 | * Parse the response coming down the wire 44 | */ 45 | TxResponse TxHelper::Parse(size_t len) 46 | { 47 | 48 | TxResponse response; 49 | 50 | int buffer[4] = {0, 0, 0, 0}; 51 | 52 | // zero out the read buffer 53 | int counterPal = 0; 54 | memset(buffer, 0, sizeof(buffer)); 55 | 56 | // read the data 57 | while (1 < (W0 ? Wire.available() : Wire1.available())) 58 | { 59 | if (counterPal < 4) 60 | { 61 | buffer[counterPal++] = (W0 ? Wire.read() : Wire1.read()); 62 | } 63 | } 64 | // get the last byte 65 | buffer[counterPal] = (W0 ? Wire.read() : Wire1.read()); 66 | 67 | // Serial.printf("Buffers: %d, %d, %d, %d\n", buffer[0], buffer[1], buffer[2], buffer[3]); 68 | 69 | uint16_t temp = (uint16_t)((buffer[2] << 8) + (buffer[3])); 70 | int16_t temp2 = (int16_t)temp; 71 | 72 | response.Command = buffer[0]; 73 | response.Output = buffer[1]; 74 | response.Value = (int)temp2; 75 | 76 | // Serial.printf("temp: %d; temp2: %d; helper: %d\n", temp, temp2, response.Value); 77 | 78 | return response; 79 | } 80 | 81 | /** 82 | * Decode the IO from the value coming down the wire 83 | */ 84 | TxIO TxHelper::DecodeIO(int io) 85 | { 86 | 87 | TxIO decoded; 88 | 89 | // turn it into 0-7 for the individual device's port (TXi) 90 | decoded.Port = io % Ports; 91 | 92 | // output mode (0-7 = normal; 8-15 = Quantized; 16-23 = Note Number) (TXi) 93 | decoded.Mode = io >> Modes; 94 | 95 | return decoded; 96 | } 97 | -------------------------------------------------------------------------------- /firmware/Old_versions/Sweet_mkII/_16n_faderbank_firmware_Sweet_mkII/TxHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TELEX Eurorack Modules 3 | * (c) 2016,2017 Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | #include "TxHelper.h" 8 | #include "Arduino.h" 9 | 10 | // i2c 11 | #include 12 | 13 | // initialize the basic values for the TXi 14 | int TxHelper::Ports = 8; 15 | int TxHelper::Modes = 3; 16 | bool TxHelper::W0 = true; 17 | 18 | /** 19 | * Set the number of ports the device has (TXi is 8; FADER is 16) 20 | */ 21 | void TxHelper::SetPorts(int ports) 22 | { 23 | TxHelper::Ports = ports; 24 | } 25 | 26 | /** 27 | * Determine how the modes shift (TXi shifts 3; FADER shifts 4) 28 | */ 29 | void TxHelper::SetModes(int modes) 30 | { 31 | TxHelper::Modes = modes; 32 | } 33 | 34 | /** 35 | * Determine if the Wire or Wire1 interface should be used 36 | */ 37 | void TxHelper::UseWire1(bool use) 38 | { 39 | TxHelper::W0 = !use; 40 | } 41 | 42 | /** 43 | * Parse the response coming down the wire 44 | */ 45 | TxResponse TxHelper::Parse(size_t len) 46 | { 47 | 48 | TxResponse response; 49 | 50 | int buffer[4] = {0, 0, 0, 0}; 51 | 52 | // zero out the read buffer 53 | int counterPal = 0; 54 | memset(buffer, 0, sizeof(buffer)); 55 | 56 | // read the data 57 | while (1 < (W0 ? Wire.available() : Wire1.available())) 58 | { 59 | if (counterPal < 4) 60 | { 61 | buffer[counterPal++] = (W0 ? Wire.read() : Wire1.read()); 62 | } 63 | } 64 | // get the last byte 65 | buffer[counterPal] = (W0 ? Wire.read() : Wire1.read()); 66 | 67 | // Serial.printf("Buffers: %d, %d, %d, %d\n", buffer[0], buffer[1], buffer[2], buffer[3]); 68 | 69 | uint16_t temp = (uint16_t)((buffer[2] << 8) + (buffer[3])); 70 | int16_t temp2 = (int16_t)temp; 71 | 72 | response.Command = buffer[0]; 73 | response.Output = buffer[1]; 74 | response.Value = (int)temp2; 75 | 76 | // Serial.printf("temp: %d; temp2: %d; helper: %d\n", temp, temp2, response.Value); 77 | 78 | return response; 79 | } 80 | 81 | /** 82 | * Decode the IO from the value coming down the wire 83 | */ 84 | TxIO TxHelper::DecodeIO(int io) 85 | { 86 | 87 | TxIO decoded; 88 | 89 | // turn it into 0-7 for the individual device's port (TXi) 90 | decoded.Port = io % Ports; 91 | 92 | // output mode (0-7 = normal; 8-15 = Quantized; 16-23 = Note Number) (TXi) 93 | decoded.Mode = io >> Modes; 94 | 95 | return decoded; 96 | } 97 | -------------------------------------------------------------------------------- /hardware/Sweet Sixteen BOM.csv: -------------------------------------------------------------------------------- 1 | Qty,DESIGNATOR,FOOTPRINT,COMMENT,Description,MF 2 | 2,"R3, R4",0805-R,47,"RESISTOR, European symbol", 3 | 2,"R5, R6",0603-R,270,"RESISTOR, American symbol", 4 | 16,"R1, R2, R17, R18, R21, R22, R43, R44, R45, R46, R57, R58, R61, R62, R76, R77",0603-R,1K,"RESISTOR, European symbol", 5 | 16,"R7, R8, R9, R10, R11, R12, R13, R14, R47, R48, R49, R50, R51, R52, R53, R54",0603-R,10k,"RESISTOR, European symbol", 6 | 16,"R15, R16, R29, R30, R31, R32, R41, R42, R55, R56, R69, R70, R71, R72, R83, R84",0603-R,33k,"RESISTOR, European symbol", 7 | 16,"R19, R20, R27, R28, R33, R34, R39, R40, R59, R60, R67, R68, R73, R74, R81, R82",0603-R,49.9k,"RESISTOR, European symbol", 8 | 1,R26,0603-R,50K,"RESISTOR, European symbol", 9 | 15,"R23, R24, R25, R35, R36, R37, R38, R63, R64, R65, R66, R75, R78, R79, R80",0603-R,80.6k,"RESISTOR, European symbol", 10 | 11,"C5, C10, C11, C12, C13, C14, C15, C16, C18, C19, C20, C8, C17, C9, C22",0603-C,100n,"CAPACITOR, European symbol", 11 | 1,C23,0603-C,330n,"CAPACITOR, American symbol", 12 | 2,"C1, C2",0603-C,470n,"CAPACITOR, American symbol", 13 | 1,C24,PANASONIC_C,10u,"POLARIZED CAPACITOR, American symbol", 14 | 3,"C6, C7, C21",PANASONIC_D,22u,"POLARIZED CAPACITOR, American symbol", 15 | 2,"C3, C4",PANASONIC_D,47u,"POLARIZED CAPACITOR, American symbol", 16 | 4,"D1, D2, D3, D6",SOD-123,1N4148,, 17 | 2,"D4, D5",SOD123,1N5819,DIODE, 18 | 1,IC3,SOT223,LM1117-5,800mA and 1A Low Dropout (LDO) Positive Regulator, 19 | 1,IC1,DBZ_R-PDSO-G3,LM4040C25,PRECISION MICROPOWER SHUNT VOLTAGE REFERENCE, 20 | 1,IC2,DBZ_R-PDSO-G3,LM4040c50,PRECISION MICROPOWER SHUNT VOLTAGE REFERENCE, 21 | 1,IC4,SOT223,UA78M08DCY,8V POSITIVE-VOLTAGE REGULATORS, 22 | 1,U5,SOIC127P1030X265-24,CD74HC4067SM96,16-channel analog MUX, 23 | 4,"U1, U2, U7, U8",SO14,MCP6004_SMD,Generic Quad Op Amp, 24 | 4,"U3, U4, U6, U9",SO14,TL074_SMD,Generic Quad Op Amp, 25 | ,,,,, 26 | 1,TENSY3.2,TEENSY_3.0-3.2&LC_DIL,TEENSY_3.1-3.2_DIL,Teensy 3.1 or 3.2 in a DIL Layout., 27 | 34,"J1, J2, J3, J4, J5, J6, J7, J8, J9, J10, J11, J12, J13, J14, J15, J16, J17, J18, J19, J20, J21, J22, J23, J24, J25, J26, J27, J28, J29, J30, J31, J32, J33, J34",WQP_PJ_301M6,PJ301_THONKICONN6,, 28 | 1,X1,KUSBVX-BS1N,KUSBVX-BS1N,"USB B-type Receptacle Vertical, 4 Position, Through Hole", 29 | 2,"+5V, JP3",1X03,,PIN HEADER, 30 | 1,JP1,2X03,,PIN HEADER, 31 | 16,"TM1, TM2, TM3, TM4, TM5, TM6, TM7, TM8, TM9, TM10, TM11, TM12, TM13, TM14, TM15, TM16",SLIDEPOT-45,,Sliding potentiometers, 32 | 4,"S1, S2, S3, S4",,,SLIDING SWITCH, 33 | 16,"LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8, LED9, LED10, LED11, LED12, LED13, LED14, LED15, LED16",LED3MM,,BI-COLOR LED, 34 | 1,SV1,ML16,,HARTING, -------------------------------------------------------------------------------- /firmware/Old_versions/First_version/_16n_faderbank_firmware_Sweet/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Configuration 3 | * (c) 2017,2018 by Brian Crabtree, Sean Hellfritsch, Tom Armitage, and Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | // restricts output to only channel 1 for development purposes 8 | // #define DEV 1 9 | 10 | // reverses faders left-to-right 11 | // #define REV 1 12 | 13 | // flips faders up-down. You almost certainly want #REV enabled as well for this. 14 | 15 | #define FLIP 1 // necesary for the Tesseract eurorack version 'Sweet Sixteen' 16 | 17 | // activates printing of debug messages 18 | // #define DEBUG 1 19 | 20 | // enables legacy compatibility with non-multiplexer boards 21 | // #define V125 22 | 23 | // turn on power LED 24 | // #define LED 1 25 | 26 | // MASTER MODE allows you to broadcast values from the 16n 27 | // this supports up to 4 TXo modules and/or up to 4 Ansible devices and/or 1 ER-301 28 | // uncomment this #define and compile the firmware 29 | // 30 | // NOTE: in MASTER MODE the 16n will not respond to the Teletype 31 | // 32 | //#define MASTER 1 33 | 34 | // minimum and maximum values for faders (to deal with tolerances) 35 | #define MINFADER 15 36 | #define MAXFADER 8135 37 | 38 | // I2C Address for Faderbank. 0x34 unless you ABSOLUTELY know what 39 | #define I2C_ADDRESS 0x34 40 | 41 | // this adds some delay before the boot-up, necesary for the ER-301, which needs to be ON before the Sweet Sixteen, 42 | // time is represented in milliseconds, leave uncomment to enable this option 43 | //#define bootDelay 10000 44 | 45 | #ifdef DEV 46 | 47 | const int channelCount = 1; 48 | const int ports[] = {A0}; 49 | const int usb_ccs[] = {32}; 50 | const int trs_ccs[] = {32}; 51 | 52 | #else 53 | 54 | const int channelCount = 16; 55 | 56 | #ifdef V125 57 | // analog ports on the Teensy for the 1.25 board. 58 | #ifdef REV 59 | const int ports[] = {A15, A14, A13, A12, A11, A10, A9, A8, A7, A6, A5, A4, A3, A2, A1, A0}; 60 | #else 61 | const int ports[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15}; 62 | #endif 63 | 64 | #endif 65 | 66 | // set up CCs. 67 | // if you wish to have different CCs for TRS and USB, specify them here. 68 | // FOR PITCH BEND USE "128" 69 | const int usb_ccs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; 70 | const int trs_ccs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 128}; 71 | 72 | // set up MIDI channels for each fader 73 | // if you wish to have different channels for TRS and USB - or for each channel - specify them here. 74 | 75 | const int usb_channels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 76 | const int trs_channels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /firmware/Old_versions/Sweet_mkII/_16n_faderbank_firmware_Sweet_mkII/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Configuration 3 | * (c) 2017,2018 by Brian Crabtree, Sean Hellfritsch, Tom Armitage, and Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | // restricts output to only channel 1 for development purposes 8 | // #define DEV 1 9 | 10 | // reverses faders left-to-right 11 | // #define REV 1 12 | 13 | // flips faders up-down. You almost certainly want #REV enabled as well for this. 14 | 15 | #define FLIP 1 // necesary for the Tesseract eurorack version 'Sweet Sixteen' 16 | 17 | // activates printing of debug messages 18 | // #define DEBUG 1 19 | 20 | // enables legacy compatibility with non-multiplexer boards 21 | // #define V125 22 | 23 | // turn on power LED 24 | // #define LED 1 25 | 26 | // MASTER MODE allows you to broadcast values from the 16n 27 | // this supports up to 4 TXo modules and/or up to 4 Ansible devices and/or 1 ER-301 28 | // uncomment this #define and compile the firmware 29 | // 30 | // NOTE: in MASTER MODE the 16n will not respond to the Teletype 31 | //#define MASTER 1 32 | 33 | // minimum and maximum values for faders (to deal with tolerances) 34 | #define MINFADER 15 35 | #define MAXFADER 8135 36 | 37 | // I2C Address for Faderbank. 0x34 unless you ABSOLUTELY know what 38 | #define I2C_ADDRESS 0x34 39 | 40 | // this adds some delay before the boot-up, necesary for the ER-301, which needs to be ON before the Sweet Sixteen, 41 | // time is represented in milliseconds, leave uncomment to enable this option 42 | //#define bootDelay 10000 43 | 44 | // allow the Tsesseract Modular GESS & midi note experimental implementation: 45 | #define GESS 1 46 | 47 | /* type of functions to do with the gate inputs in future firmware tweaks: 48 | * note 49 | * fixed pitch 50 | * program change 51 | * CC value 52 | */ 53 | 54 | #ifdef DEV 55 | 56 | const int channelCount = 1; 57 | const int ports[] = {A0}; 58 | const int usb_ccs[] = {32}; 59 | const int trs_ccs[] = {32}; 60 | 61 | #else 62 | 63 | const int channelCount = 16; 64 | 65 | #ifdef V125 66 | // analog ports on the Teensy for the 1.25 board. 67 | #ifdef REV 68 | const int ports[] = {A15, A14, A13, A12, A11, A10, A9, A8, A7, A6, A5, A4, A3, A2, A1, A0}; 69 | #else 70 | const int ports[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15}; 71 | #endif 72 | 73 | #endif 74 | 75 | // set up CCs. 76 | // if you wish to have different CCs for TRS and USB, specify them here. 77 | // FOR PITCH BEND USE "128" 78 | const int usb_ccs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; 79 | const int trs_ccs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 128}; 80 | 81 | // set up MIDI channels for each fader 82 | // if you wish to have different channels for TRS and USB - or for each channel - specify them here. 83 | 84 | const int usb_channels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 85 | const int trs_channels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/README.md: -------------------------------------------------------------------------------- 1 | # 16n Firmware 2 | 3 | 16n Firmware is designed to run on a Teensy 3.2; it is also compatible with a Teensy LC, although the official BOM recommends a 3.2. 4 | 5 | This README serves as a guide for **developing** and **compiling** your own versions of the firwmare. Since firmware v2.0.0, the recommended method for putting firmware onto a 16n is to use Teensy Loader directly. [Find out more on the wiki][load-firmware] 6 | 7 | If you are interested in compiling your own firmware, or hacking on it, read on! 8 | 9 | ## Requirements 10 | 11 | - Latest Teensyduino install. 12 | - `ResponsiveAnalogRead` library 13 | - `CD74HC4067` library 14 | 15 | ## Compilation 16 | 17 | - You **must** compile this with Tools->USB type set to **MIDI**. (**Serial+MIDI** may have issues with certain devices you connect to). 18 | - Be sure that the board speed is set to 120mhz (overclock) for maximum repsonsiveness. 19 | - If you're having issues compiling related to MIDI libraries or code: make sure your Arduino `libraries` folder doesn't have any old versions of weird MIDI libraries in. The MIDI library should be installed by default via Teensyduino; otherwise, if you have the latest version of the 47effects MIDI library in your Libraries Manager, that'll also behave. It turns out that older versions in the legacy `libraries` folder sometimes lead to conflicts. 20 | 21 | ## Customisation and configuration 22 | 23 | As of 16n firmware 2.0.0, you no longer should do ANY configuration through the Arduino IDE. All configuration is conducted from a web browser, using the [16n editor][editor] 24 | 25 | When you upgrade to 2.0.0 you will LOSE ANY CONFIGURATION YOU HAVE from v1.3x. This is a one-time thing; apologies. You will not lose future configurations (and also, they are easier to back up). 26 | 27 | The 16n will be initialised to a set of default settings (All outputs for TRS and USB set to MIDI channel 1, CCs 32-47, I2C set to follower). Once this is done, connect over USB, and go to the [editor][editor] in Google Chrome; you will be able to see the current configuration, edit the configuration, and transmit the new config to your device. You will likely need to customise the maximum fader value calibrations. 28 | 29 | Note that if you do change any config related to I2C, you should power-cycle the 16n before it will be picked up. 30 | 31 | Some options _do_ remain in `config.h`; they are for developers to specify options that are likely to need setting once, or adjusting during the development process: 32 | 33 | In `config.h` 34 | 35 | ```C 36 | #define DEBUG 1 37 | ``` 38 | 39 | will log debug messages to the serial port. 40 | 41 | ```C 42 | #define DEV 1 43 | ``` 44 | 45 | will restrict the faderbank to its first channel. Designed for breadboard development; almost certainly not of interest. 46 | 47 | ## Memory Map 48 | 49 | Configuration is stored in the first 80 bytes of the on-board EEPROM. It looks like this: 50 | 51 | Addresses 0-15 are reserved for configuration flags/data. 52 | 53 | FADERMAX and FADERMIN are 14-bit numbers; as such, they are stored in two bytes as MSB and LSB; the actual number is calculated by `(MSB << 8) + LSB` 54 | 55 | +---------+--------+------------------------------------+ 56 | | Address | Format | Description | 57 | +---------+--------+------------------------------------+ 58 | | 0 | 0/1 | LED on when powered | 59 | | 1 | 0/1 | LED blink on MIDI data | 60 | | 2 | 0/1 | Rotate controller outputs via 180º | 61 | | 3 | 0/1 | I2C Master/Follower | 62 | | 4,5 | 0-127 | FADERMIN lsb/msb | 63 | | 6,7 | 0-127 | FADERMAX lsb/msb | 64 | | 8-15 | | Currently vacant | 65 | +---------+--------+------------------------------------+ 66 | | 16-31 | 0-15 | Channel for each control (USB) | 67 | | 32-47 | 0-15 | Channel for each control (TRS) | 68 | | 48-63 | 0-127 | CC for each control (USB) | 69 | | 64-79 | 0-127 | CC for each control (TRS) | 70 | +---------+--------+------------------------------------+ 71 | 72 | ## LICENSING 73 | 74 | see `LICENSE` 75 | 76 | [load-firmware]: https://github.com/16n-faderbank/16n/wiki/Firmware:-installation-instructions 77 | [editor]: https://16n-faderbank.github.io/editor 78 | -------------------------------------------------------------------------------- /firmware/Sweet_3/README.md: -------------------------------------------------------------------------------- 1 | # Sweet_3 Firmware 2 | 3 | Designed to run on a Teensy 3.2; 4 | 5 | This README serves as a guide for **developing** and **compiling** your own versions of the firwmare. The recommended method for putting firmware onto a Sweet Sixteen is to use Teensy Loader directly. 6 | 7 | If you are interested in compiling your own firmware, or hacking on it, read on! 8 | 9 | ## Requirements 10 | 11 | - Latest Teensyduino install. 12 | - `ResponsiveAnalogRead` library 13 | - `CD74HC4067` library 14 | 15 | ## Compilation 16 | 17 | - You **must** compile this with Tools->USB type set to **MIDI**. (**Serial+MIDI** may have issues with certain devices you connect to). 18 | - Be sure that the board speed is set to 120mhz (overclock) for maximum repsonsiveness. 19 | - If you're having issues compiling related to MIDI libraries or code: make sure your Arduino `libraries` folder doesn't have any old versions of weird MIDI libraries in. The MIDI library should be installed by default via Teensyduino; otherwise, if you have the latest version of the 47effects MIDI library in your Libraries Manager, that'll also behave. It turns out that older versions in the legacy `libraries` folder sometimes lead to conflicts. 20 | 21 | ## Customisation and configuration 22 | 23 | As of 16n firmware 2.0.0, you no longer should do ANY configuration through the Arduino IDE. Most configurations are conducted from a web browser, using the [16n editor][editor] 24 | 25 | The Sweet Sixteen will be initialised to a set of default settings (All outputs for TRS and USB set to MIDI channel 1, CCs 32-47, I2C set to Leader). Once this is done, connect over USB, and go to the [editor][editor] in Google Chrome; you will be able to see the current configuration, edit the configuration, and transmit the new config to your device. You will likely need to customise the maximum fader value calibrations. 26 | 27 | Note that if you do change any config related to I2C, you should power-cycle the 16n before it will be picked up. 28 | 29 | Some options _do_ remain in `config.h`; they are for developers to specify options that are likely to need setting once, or adjusting during the development process: 30 | 31 | In `config.h` 32 | 33 | // define startup delay in milliseconds 34 | 35 | // for ER-301, which needs to be ON before Sweet Sixteen to connect via i2c 36 | 37 | #define BOOTDELAY 10000 38 | 39 | // uncomment this to allow PITCHBEND for controller 127: 40 | 41 | #define PITCHBEND 1 42 | 43 | // allow the Tsesseract Modular GESS & midi note implementation: 44 | 45 | #define GESS 1 46 | 47 | // default GESS settings (midi note, velocity and channel): 48 | 49 | byte _nNote[8] = { 30, 40, 50, 60, 70, 80, 90, 100 }; 50 | 51 | byte _nVelocity[8] = { 120, 120, 120, 120, 120, 120, 120, 120 }; 52 | 53 | byte _nChannel[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 54 | 55 | ```C 56 | #define DEBUG 1 57 | ``` 58 | 59 | will log debug messages to the serial port. 60 | 61 | ```C 62 | #define DEV 1 63 | ``` 64 | 65 | will restrict the faderbank to its first channel. Designed for breadboard development; almost certainly not of interest. 66 | 67 | ## Memory Map 68 | 69 | Configuration is stored in the first 80 bytes of the on-board EEPROM. It looks like this: 70 | 71 | Addresses 0-15 are reserved for configuration flags/data. 72 | 73 | FADERMAX and FADERMIN are 14-bit numbers; as such, they are stored in two bytes as MSB and LSB; the actual number is calculated by `(MSB << 8) + LSB` 74 | 75 | +---------+--------+------------------------------------+ 76 | 77 | | Address | Format | Description | 78 | 79 | +---------+--------+------------------------------------+ 80 | 81 | | 0 | 0/1 | LED on when powered | 82 | 83 | | 1 | 0/1 | LED blink on MIDI data | 84 | 85 | | 2 | 0/1 | Rotate controller outputs via 180º | 86 | 87 | | 3 | 0/1 | I2C Master/Follower | 88 | 89 | | 4,5 | 0-127 | FADERMIN lsb/msb | 90 | 91 | | 6,7 | 0-127 | FADERMAX lsb/msb | 92 | 93 | | 8-15 | | Currently vacant | 94 | 95 | +---------+--------+------------------------------------+ 96 | 97 | | 16-31 | 0-15 | Channel for each control (USB) | 98 | 99 | | 32-47 | 0-15 | Channel for each control (TRS) | 100 | 101 | | 48-63 | 0-127 | CC for each control (USB) | 102 | 103 | | 64-79 | 0-127 | CC for each control (TRS) | 104 | 105 | +---------+--------+------------------------------------+ 106 | 107 | EEPROM address 100-115 is used for GESS presets 108 | 109 | ## LICENSING 110 | 111 | see `LICENSE` 112 | 113 | [load-firmware]: https://github.com/16n-faderbank/16n/wiki/Firmware:-installation-instructions 114 | [editor]: https://16n-faderbank.github.io/editor 115 | -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/configuration.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank EEPROM-based configuration 3 | * (c) 2020 by Tom Armitage 4 | * MIT License 5 | */ 6 | 7 | const int defaultUSBCCs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; 8 | const int defaultTRSCCs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; 9 | 10 | const int defaultUSBChannels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 11 | const int defaultTRSChannels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 12 | 13 | void checkDefaultSettings() { 14 | // if byte1 of EEPROM is FF for whatever reason, let's assume the machine needs initializing 15 | int firstByte = EEPROM.read(0x00); 16 | 17 | if(firstByte > 0x01) { 18 | D(Serial.println("First Byte is > 0x01, probably needs initialising")); 19 | initializeFactorySettings(); 20 | } else { 21 | D(Serial.print("First Byte is set to: ")); 22 | D(printHex(firstByte)); 23 | D(Serial.println()); 24 | byte buffer[80]; 25 | readEEPROMArray(0, buffer, 80); 26 | D(Serial.println("Config found:")); 27 | D(printHexArray(buffer, 80)); 28 | } 29 | } 30 | 31 | void initializeFactorySettings() { 32 | // set default config flags (LED ON, LED DATA, ROTATE, etc) 33 | // fadermin/max are based on "works for me" for twra2. Your mileage may vary. 34 | EEPROM.write(0,1); // LED ON 35 | EEPROM.write(1,1); // LED DATA 36 | EEPROM.write(2,1); // ROTATE EEPROM.write(2,0) 37 | EEPROM.write(3,1); // I2C follower by default EEPROM.write(3,0) 38 | EEPROM.write(4,15); // fadermin LSB 39 | EEPROM.write(5,0); // fadermin MSB 40 | EEPROM.write(6,71); // fadermax LSB 41 | EEPROM.write(7,63); // fadermax MSB 42 | 43 | // set default USB channels 44 | for(int i = 0; i < channelCount; i++) { 45 | int baseAddress = 16; 46 | int writeAddress = baseAddress + i; 47 | EEPROM.write(writeAddress, defaultUSBChannels[i]); 48 | } 49 | 50 | // set default TRS channels 51 | for(int i = 0; i < channelCount; i++) { 52 | int baseAddress = 32; 53 | int writeAddress = baseAddress + i; 54 | EEPROM.write(writeAddress, defaultTRSChannels[i]); 55 | } 56 | 57 | // set default USB channel ccs 58 | for(int i = 0; i < channelCount; i++) { 59 | int baseAddress = 48; 60 | int writeAddress = baseAddress + i; 61 | EEPROM.write(writeAddress, defaultUSBCCs[i]); 62 | } 63 | 64 | // set default TRS channel ccs 65 | for(int i = 0; i < channelCount; i++) { 66 | int baseAddress = 64; 67 | int writeAddress = baseAddress + i; 68 | EEPROM.write(writeAddress, defaultTRSCCs[i]); 69 | } 70 | 71 | // serial dump that config. 72 | byte buffer[80]; 73 | readEEPROMArray(0,buffer,80); 74 | D(Serial.println("Config Instantiated.")); 75 | D(printHexArray(buffer, 80)); 76 | } 77 | 78 | void loadSettingsFromEEPROM() { 79 | // load usb channels 80 | for(int i = 0; i < channelCount; i++) { 81 | int baseAddress = 16; 82 | int readAddress = baseAddress + i; 83 | usbChannels[i] = EEPROM.read(readAddress); 84 | } 85 | 86 | D(Serial.println("USB Channels loaded:")); 87 | D(printIntArray(usbChannels,channelCount)); 88 | 89 | // load TRS channels 90 | for(int i = 0; i < channelCount; i++) { 91 | int baseAddress = 32; 92 | int readAddress = baseAddress + i; 93 | trsChannels[i] = EEPROM.read(readAddress); 94 | } 95 | 96 | D(Serial.println("TRS Channels loaded:")); 97 | D(printIntArray(trsChannels,channelCount)); 98 | 99 | // load USB ccs 100 | for(int i = 0; i < channelCount; i++) { 101 | int baseAddress = 48; 102 | int readAddress = baseAddress + i; 103 | usbCCs[i] = EEPROM.read(readAddress); 104 | } 105 | 106 | D(Serial.println("USB CCs loaded:")); 107 | D(printIntArray(usbCCs,channelCount)); 108 | 109 | 110 | // load TRS ccs 111 | for(int i = 0; i < channelCount; i++) { 112 | int baseAddress = 64; 113 | int readAddress = baseAddress + i; 114 | trsCCs[i] = EEPROM.read(readAddress); 115 | } 116 | 117 | D(Serial.println("TRS CCs loaded:")); 118 | D(printIntArray(trsCCs,channelCount)); 119 | 120 | // load other config 121 | ledOn = EEPROM.read(0); 122 | ledFlash = EEPROM.read(1); 123 | flip = EEPROM.read(2); 124 | 125 | // i2cMaster only read at startup 126 | 127 | int faderminLSB = EEPROM.read(4); 128 | int faderminMSB = EEPROM.read(5); 129 | 130 | D(Serial.print ("Setting fadermin to ")); 131 | D(Serial.println((faderminMSB << 7) + faderminLSB)); 132 | faderMin = (faderminMSB << 7) + faderminLSB; 133 | 134 | int fadermaxLSB = EEPROM.read(6); 135 | int fadermaxMSB = EEPROM.read(7); 136 | 137 | D(Serial.print ("Setting fadermax to ")); 138 | D(Serial.println((fadermaxMSB << 7) + fadermaxLSB)); 139 | faderMax = (fadermaxMSB << 7) + fadermaxLSB; 140 | } 141 | -------------------------------------------------------------------------------- /firmware/Sweet_3/configuration.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank EEPROM-based configuration 3 | * (c) 2020 by Tom Armitage 4 | * MIT License 5 | */ 6 | 7 | 8 | // for pitch bend use CC value 127 9 | const int defaultUSBCCs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; 10 | const int defaultTRSCCs[] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; 11 | 12 | const int defaultUSBChannels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 13 | const int defaultTRSChannels[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 14 | 15 | void checkDefaultSettings() { 16 | // if byte1 of EEPROM is FF for whatever reason, let's assume the machine needs initializing 17 | int firstByte = EEPROM.read(0x00); 18 | 19 | if(firstByte > 0x01) { 20 | D(Serial.println("First Byte is > 0x01, probably needs initialising")); 21 | initializeFactorySettings(); 22 | } else { 23 | D(Serial.print("First Byte is set to: ")); 24 | D(printHex(firstByte)); 25 | D(Serial.println()); 26 | byte buffer[80]; 27 | readEEPROMArray(0, buffer, 80); 28 | D(Serial.println("Config found:")); 29 | D(printHexArray(buffer, 80)); 30 | } 31 | } 32 | 33 | void initializeFactorySettings() { 34 | // set default config flags (LED ON, LED DATA, ROTATE, etc) 35 | // fadermin/max are based on "works for me" for twra2. Your mileage may vary. 36 | EEPROM.write(0,0); // LED ON 37 | EEPROM.write(1,0); // LED DATA 38 | EEPROM.write(2,1); // ROTATE EEPROM.write(2,0) 39 | EEPROM.write(3,1); // I2C follower by default EEPROM.write(3,0) 40 | EEPROM.write(4,20); // fadermin LSB 41 | EEPROM.write(5,0); // fadermin MSB 42 | EEPROM.write(6,36); // fadermax LSB 43 | EEPROM.write(7,63); // fadermax MSB 44 | 45 | // set default USB channels 46 | for(int i = 0; i < channelCount; i++) { 47 | int baseAddress = 16; 48 | int writeAddress = baseAddress + i; 49 | EEPROM.write(writeAddress, defaultUSBChannels[i]); 50 | } 51 | 52 | // set default TRS channels 53 | for(int i = 0; i < channelCount; i++) { 54 | int baseAddress = 32; 55 | int writeAddress = baseAddress + i; 56 | EEPROM.write(writeAddress, defaultTRSChannels[i]); 57 | } 58 | 59 | // set default USB channel ccs 60 | for(int i = 0; i < channelCount; i++) { 61 | int baseAddress = 48; 62 | int writeAddress = baseAddress + i; 63 | EEPROM.write(writeAddress, defaultUSBCCs[i]); 64 | } 65 | 66 | // set default TRS channel ccs 67 | for(int i = 0; i < channelCount; i++) { 68 | int baseAddress = 64; 69 | int writeAddress = baseAddress + i; 70 | EEPROM.write(writeAddress, defaultTRSCCs[i]); 71 | } 72 | 73 | // serial dump that config. 74 | byte buffer[80]; 75 | readEEPROMArray(0,buffer,80); 76 | D(Serial.println("Config Instantiated.")); 77 | D(printHexArray(buffer, 80)); 78 | } 79 | 80 | void loadSettingsFromEEPROM() { 81 | // load usb channels 82 | for(int i = 0; i < channelCount; i++) { 83 | int baseAddress = 16; 84 | int readAddress = baseAddress + i; 85 | usbChannels[i] = EEPROM.read(readAddress); 86 | } 87 | 88 | D(Serial.println("USB Channels loaded:")); 89 | D(printIntArray(usbChannels,channelCount)); 90 | 91 | // load TRS channels 92 | for(int i = 0; i < channelCount; i++) { 93 | int baseAddress = 32; 94 | int readAddress = baseAddress + i; 95 | trsChannels[i] = EEPROM.read(readAddress); 96 | } 97 | 98 | D(Serial.println("TRS Channels loaded:")); 99 | D(printIntArray(trsChannels,channelCount)); 100 | 101 | // load USB ccs 102 | for(int i = 0; i < channelCount; i++) { 103 | int baseAddress = 48; 104 | int readAddress = baseAddress + i; 105 | usbCCs[i] = EEPROM.read(readAddress); 106 | } 107 | 108 | D(Serial.println("USB CCs loaded:")); 109 | D(printIntArray(usbCCs,channelCount)); 110 | 111 | 112 | // load TRS ccs 113 | for(int i = 0; i < channelCount; i++) { 114 | int baseAddress = 64; 115 | int readAddress = baseAddress + i; 116 | trsCCs[i] = EEPROM.read(readAddress); 117 | } 118 | 119 | D(Serial.println("TRS CCs loaded:")); 120 | D(printIntArray(trsCCs,channelCount)); 121 | 122 | // load other config 123 | ledOn = EEPROM.read(0); 124 | ledFlash = EEPROM.read(1); 125 | flip = EEPROM.read(2); 126 | 127 | // i2cMaster only read at startup 128 | 129 | int faderminLSB = EEPROM.read(4); 130 | int faderminMSB = EEPROM.read(5); 131 | 132 | D(Serial.print ("Setting fadermin to ")); 133 | D(Serial.println((faderminMSB << 7) + faderminLSB)); 134 | faderMin = (faderminMSB << 7) + faderminLSB; 135 | 136 | int fadermaxLSB = EEPROM.read(6); 137 | int fadermaxMSB = EEPROM.read(7); 138 | 139 | D(Serial.print ("Setting fadermax to ")); 140 | D(Serial.println((fadermaxMSB << 7) + fadermaxLSB)); 141 | faderMax = (fadermaxMSB << 7) + fadermaxLSB; 142 | } 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sweet-Sixteen 2 | Eurorack module based in the [16n faderbank][16n-faderbank/16n]. 3 | 4 | ![Sweet Sixteen](SweetSixteen_.jpg) 5 | 6 | ## The following changes were made to the original hardware: 7 | 8 | -The PCB layout has been made from scratch, so it's totally different, with 0603 passives instead of 0805. 9 | 10 | -A power section has been added to work within the eurorack modular synth format, with diode protection and the option to power the Teensy with an internal 5V LDO or the eurorack 5V rail (selectable with a jumper on the back of the module). The voltage to feed the faders now comes from a dedicated voltage regulator, more stable and precise than most USB voltage sources. 11 | 12 | -16 Inputs has been added. A "default voltage" is normalled to the input jacks, so when something is plugged in, that normalization is broken, being the incomming CV attenuated by the fader. This adition makes the module to work also as 16 attenuators, Cv to Midi, Cv to i2C... 13 | 14 | -The op-amps circuit has been modiffied, being TL074s for the CV outs and MCP6004s for the circuit that prepares the voltages to be read by the Teensy. In this version the MCP6004 are powered with 3,3V & GND (instead of the original 5V), the rail-to-rail characteristic of those op-amps provides voltage protection to the Teensy. That is specially handy for the CV inputs, as any voltage plugged in, will be limited to 3,3v by the MCP6004s. Another important modification in this part of the circuit is the addition of negative voltage references and that now is a summing inverting gain circuit (for that reason the FLIP option in the firmware is necessary for this version of the hardware, as in the minimum position of the fader the voltage readed by the Teensy will be 3,3V and in the max position it will be 0V). The TL074s are powered by +12V & -12V. 15 | 16 | -Four slide switches has been added to swap the voltage reference (1 for every 4 faders, there was no room for 16 switches). The reason is to allow negative voltages to be coorectly converted to midi CC (or i2C). In the normal position a 0V input is traduced as minimum CC value, with the switch turned up the voltage reference is -2,5V (instead of -5V) that means that 0V input correspond to the center position of the MidiCC, positive voltages will increase the value while negative ones will decrease it. 17 | 18 | -The output voltage of the faders (that "default voltage" normalled to the jacks) has been increassed to 8v, the original 5v seems to be not enough to work in eurorack, like to wide open-close the cutoff of a filter etc. I decided to go 8v instead of 10v because of the CV inputs. If the circuit that prepares the voltages comming from the faders to be readed by the Teensy is expecting 10v then it will be impossible to cover all the range of a MidiCC with an LFO which is ±4v (or 8v pp). For the contrary, if that circuit is expecting 8V it can not only cover the whole Midi CC range but also allows saturating the waveform if the voltage source has over 8V pp (like a 10V envelope or a ±5V LFO). 19 | 20 | -16 bicolor leds has been added in parallel with the CV out, allowing visual feedback of what's going on each track, specially handy when using the CV inputs. 21 | 22 | -USB type B connector pcb mounted has been also added. Those connectors are much stronger than mini or micro ones, the USB 5V pin is not connected, this way you can update the firmware of the Teensy without powering off the module. 23 | 24 | ![PCB](sweet_pcb.jpg) 25 | 26 | Various versions of the firmware are available, all them derived from the 16n by Tom Armitage. 27 | 28 | Manual and build guide: 29 | 30 | https://www.tesseractmodular.com/manuals/sweet-sixteen-manual 31 | 32 | ## [firmware](firmware/) 33 | 34 | ## [hardware](hardware/) 35 | 36 | ### There are 16 faders/channels, which can be used as: 37 | 38 | -Midi controller. 39 | -Manual CV generator (0 to 8v range). 40 | -Attenuator. 41 | -CV to Midi CC. 42 | -i2C controller. 43 | -CV to i2C data. 44 | 45 | ### Features: 46 | 47 | -16 inputs. 48 | -16 outputs. 49 | -16 faders. 50 | -16 bi-color leds to show what's going on in every channel. 51 | -Midi trs out. 52 | -Jumpers on the back to swap between 'Arturia/Novation' and 'Korg/Makenoise' Midi trs standards. 53 | -USB Midi out (connector type B, usb midi class compliant device). 54 | -4 switches to enable bi-polar CV to Midi/i2C conversion (one for every 4 channels). 55 | -i2C via mini jack on front panel and pin header on the back of the module. 56 | 57 | ## Credits 58 | This is a derivative work of the [16n faderbank][16n-faderbank/16n]. 59 | Based on original work by [Brian Crabtree][tehn] and Sean Hellfritsch. 60 | Minijack MIDI, I2C circuitry and CV outputs by [Tom Armitage][infovore]. 61 | Firmware by [Brian Crabtree][tehn], [Tom Armitage][infovore], and [Brendon Cassidy][bpcmusic]. 62 | Eurorack conversion, hardware & firmware mods by Mangu Díaz 2019. 63 | 64 | ## Licensing 65 | 66 | Panels and electronic schematics/layouts/gerber files are licensed under 67 | [Creative Commons Attribution Share-Alike 4.0][ccbysa]. 68 | 69 | Firmware is licensed under the [MIT License][mitlicense]. 70 | 71 | [linespost]: https://llllllll.co/t/sixteen-n-faderbank/3643 72 | [tehn]: https://github.com/tehn 73 | [bpcmusic]: https://github.com/bpcmusic 74 | [infovore]: https://github.com/infovore 75 | [octobom]: https://octopart.com/bom-tool/unJxkzvR 76 | [ccbysa]: https://creativecommons.org/licenses/by-sa/4.0/ 77 | [mitlicense]: https://opensource.org/licenses/MIT 78 | [16n-faderbank/16n]: https://github.com/16n-faderbank/16n 79 | -------------------------------------------------------------------------------- /firmware/Sweet_3/sysex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Configuration Sysex Processing 3 | * (c) 2020 by Tom Armitage 4 | * MIT License 5 | */ 6 | 7 | void processIncomingSysex(byte* sysexData, unsigned size) { 8 | D(Serial.println("Ooh, sysex")); 9 | D(printHexArray(sysexData, size)); 10 | D(Serial.println()); 11 | 12 | if(size < 3) { 13 | D(Serial.println("That's an empty sysex, bored now")); 14 | return; 15 | } 16 | 17 | // if(!(sysexData[1] == 0x00 && sysexData[2] == 0x58 && sysexData[3] == 0x49)) { 18 | if(!(sysexData[1] == 0x7d && sysexData[2] == 0x00 && sysexData[3] == 0x00)) { 19 | D(Serial.println("That's not a sysex message for us")); 20 | return; 21 | } 22 | 23 | switch(sysexData[4]) { 24 | case 0x1f: 25 | // 1F = "1nFo" - please send me your current config 26 | D(Serial.println("Got an 1nFo request")); 27 | sendCurrentState(); 28 | break; 29 | case 0x0e: 30 | // 0E - c0nfig Edit - here is a new config 31 | D(Serial.println("Incoming c0nfig Edit")); 32 | updateAllSettingsAndStoreInEEPROM(sysexData, size); 33 | break; 34 | case 0x0d: 35 | // 0D - c0nfig Device edit - new config just for device opts 36 | D(Serial.println("Incoming c0nfig Device edit")); 37 | updateDeviceSettingsAndStoreInEEPROM(sysexData, size); 38 | break; 39 | case 0x0c: 40 | // 0C - c0nfig usb edit - here is a new config just for usb 41 | D(Serial.println("Incoming c0nfig usb edit")); 42 | updateUSBSettingsAndStoreInEEPROM(sysexData, size); 43 | break; 44 | case 0x0b: 45 | // 0B - c0nfig trs edit - here is a new config just for trs 46 | D(Serial.println("Incoming c0nfig trs edit")); 47 | updateTRSSettingsAndStoreInEEPROM(sysexData, size); 48 | break; 49 | } 50 | } 51 | 52 | void updateAllSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 53 | // store the settings from sysex in flash 54 | // also update all our settings. 55 | D(Serial.print("Received a new config with size ")); 56 | D(Serial.println(size)); 57 | // D(printHexArray(newConfig,size)); 58 | 59 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,9,80,0); 60 | } 61 | 62 | void updateDeviceSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 63 | // store the settings from sysex in flash 64 | // also update all our settings. 65 | D(Serial.print("Received a new device config with size ")); 66 | D(Serial.println(size)); 67 | // D(printHexArray(newConfig,size)); 68 | 69 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,5,16,0); 70 | } 71 | 72 | void updateUSBSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 73 | // store channels 74 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,5,16,16); 75 | // store CCs 76 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,21,16,48); 77 | } 78 | 79 | void updateTRSSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 80 | // store channels 81 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,5,16,32); 82 | // store CCs 83 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,21,16,64); 84 | } 85 | 86 | void updateSettingsBlockAndStoreInEEPROM(byte* configFromSysex, unsigned sysexSize, int configStartIndex, int configDataLength, int EEPROMStartIndex) { 87 | D(Serial.print("Storing data of size ")); 88 | D(Serial.print(configDataLength)); 89 | D(Serial.print(" at location ")); 90 | D(Serial.print(EEPROMStartIndex)); 91 | D(Serial.print(" from data of length ")); 92 | D(Serial.print(sysexSize)); 93 | D(Serial.print(" beginning at byte ")); 94 | D(Serial.println(configStartIndex)); 95 | D(printHexArray(configFromSysex, sysexSize)); 96 | 97 | // walk the config, ignoring the top, tail, and firmware version 98 | byte dataToWrite[configDataLength]; 99 | 100 | for(int i = 0; i < (configDataLength); i++) { 101 | int configIndex = i + configStartIndex; 102 | dataToWrite[i] = configFromSysex[configIndex]; 103 | } 104 | 105 | // write new Data 106 | writeEEPROMArray(EEPROMStartIndex, dataToWrite, configDataLength); 107 | 108 | // now load that. 109 | loadSettingsFromEEPROM(); 110 | } 111 | void sendCurrentState() { 112 | // 0F - "c0nFig" - outputs its config: 113 | byte sysexData[88]; 114 | 115 | sysexData[0] = 0x7d; // manufacturer 116 | sysexData[1] = 0x00; 117 | sysexData[2] = 0x00; 118 | 119 | sysexData[3] = 0x0F; // ConFig; 120 | 121 | sysexData[4] = DEVICE_ID; // Device 01, ie, dev board 122 | sysexData[5] = MAJOR_VERSION; // major version 123 | sysexData[6] = MINOR_VERSION; // minor version 124 | sysexData[7] = POINT_VERSION; // point version 125 | 126 | 127 | // 16 bytes of config flags, notably: 128 | // LED PERMANENT 129 | // LED DATA XFER 130 | // ROTATE (flip+reverse) 131 | // i2c MASTER/FOLLOEWR 132 | // fadermin LSB 133 | // fadermin MSB 134 | // fadermax LSB 135 | // fadermax MSB 136 | 137 | // 16x USBccs 138 | // 16x TRSccs 139 | // 16x USBchannel 140 | // 16x TRS channel 141 | 142 | // So that's 3 for the mfg + 1 for the message + 80 bytes 143 | // can be done with a simple "read eighty bytes and send them." 144 | 145 | byte buffer[80]; 146 | readEEPROMArray(0, buffer, 80); 147 | 148 | int offset = 8; 149 | for(int i = 0; i < 80; i++) { 150 | byte data = buffer[i]; 151 | if(data == 0xff) { 152 | data = 0x7f; 153 | } 154 | sysexData[i+offset] = data; 155 | } 156 | 157 | D(Serial.println("Sending this data")); 158 | D(printHexArray(sysexData,88)); 159 | 160 | usbMIDI.sendSysEx(88, sysexData, false); 161 | forceMidiWrite = true; 162 | } -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/sysex.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Configuration Sysex Processing 3 | * (c) 2020 by Tom Armitage 4 | * MIT License 5 | */ 6 | 7 | void processIncomingSysex(byte* sysexData, unsigned size) { 8 | D(Serial.println("Ooh, sysex")); 9 | D(printHexArray(sysexData, size)); 10 | D(Serial.println()); 11 | 12 | if(size < 3) { 13 | D(Serial.println("That's an empty sysex, bored now")); 14 | return; 15 | } 16 | 17 | // if(!(sysexData[1] == 0x00 && sysexData[2] == 0x58 && sysexData[3] == 0x49)) { 18 | if(!(sysexData[1] == 0x7d && sysexData[2] == 0x00 && sysexData[3] == 0x00)) { 19 | D(Serial.println("That's not a sysex message for us")); 20 | return; 21 | } 22 | 23 | switch(sysexData[4]) { 24 | case 0x1f: 25 | // 1F = "1nFo" - please send me your current config 26 | D(Serial.println("Got an 1nFo request")); 27 | sendCurrentState(); 28 | break; 29 | case 0x0e: 30 | // 0E - c0nfig Edit - here is a new config 31 | D(Serial.println("Incoming c0nfig Edit")); 32 | updateAllSettingsAndStoreInEEPROM(sysexData, size); 33 | break; 34 | case 0x0d: 35 | // 0D - c0nfig Device edit - new config just for device opts 36 | D(Serial.println("Incoming c0nfig Device edit")); 37 | updateDeviceSettingsAndStoreInEEPROM(sysexData, size); 38 | break; 39 | case 0x0c: 40 | // 0C - c0nfig usb edit - here is a new config just for usb 41 | D(Serial.println("Incoming c0nfig usb edit")); 42 | updateUSBSettingsAndStoreInEEPROM(sysexData, size); 43 | break; 44 | case 0x0b: 45 | // 0B - c0nfig trs edit - here is a new config just for trs 46 | D(Serial.println("Incoming c0nfig trs edit")); 47 | updateTRSSettingsAndStoreInEEPROM(sysexData, size); 48 | break; 49 | } 50 | } 51 | 52 | void updateAllSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 53 | // store the settings from sysex in flash 54 | // also update all our settings. 55 | D(Serial.print("Received a new config with size ")); 56 | D(Serial.println(size)); 57 | // D(printHexArray(newConfig,size)); 58 | 59 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,9,80,0); 60 | } 61 | 62 | void updateDeviceSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 63 | // store the settings from sysex in flash 64 | // also update all our settings. 65 | D(Serial.print("Received a new device config with size ")); 66 | D(Serial.println(size)); 67 | // D(printHexArray(newConfig,size)); 68 | 69 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,5,16,0); 70 | } 71 | 72 | void updateUSBSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 73 | // store channels 74 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,5,16,16); 75 | // store CCs 76 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,21,16,48); 77 | } 78 | 79 | void updateTRSSettingsAndStoreInEEPROM(byte* newConfig, unsigned size) { 80 | // store channels 81 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,5,16,32); 82 | // store CCs 83 | updateSettingsBlockAndStoreInEEPROM(newConfig,size,21,16,64); 84 | } 85 | 86 | void updateSettingsBlockAndStoreInEEPROM(byte* configFromSysex, unsigned sysexSize, int configStartIndex, int configDataLength, int EEPROMStartIndex) { 87 | D(Serial.print("Storing data of size ")); 88 | D(Serial.print(configDataLength)); 89 | D(Serial.print(" at location ")); 90 | D(Serial.print(EEPROMStartIndex)); 91 | D(Serial.print(" from data of length ")); 92 | D(Serial.print(sysexSize)); 93 | D(Serial.print(" beginning at byte ")); 94 | D(Serial.println(configStartIndex)); 95 | D(printHexArray(configFromSysex, sysexSize)); 96 | 97 | // walk the config, ignoring the top, tail, and firmware version 98 | byte dataToWrite[configDataLength]; 99 | 100 | for(int i = 0; i < (configDataLength); i++) { 101 | int configIndex = i + configStartIndex; 102 | dataToWrite[i] = configFromSysex[configIndex]; 103 | } 104 | 105 | // write new Data 106 | writeEEPROMArray(EEPROMStartIndex, dataToWrite, configDataLength); 107 | 108 | // now load that. 109 | loadSettingsFromEEPROM(); 110 | } 111 | void sendCurrentState() { 112 | // 0F - "c0nFig" - outputs its config: 113 | byte sysexData[88]; 114 | 115 | sysexData[0] = 0x7d; // manufacturer 116 | sysexData[1] = 0x00; 117 | sysexData[2] = 0x00; 118 | 119 | sysexData[3] = 0x0F; // ConFig; 120 | 121 | sysexData[4] = DEVICE_ID; // Device 01, ie, dev board 122 | sysexData[5] = MAJOR_VERSION; // major version 123 | sysexData[6] = MINOR_VERSION; // minor version 124 | sysexData[7] = POINT_VERSION; // point version 125 | 126 | 127 | // 16 bytes of config flags, notably: 128 | // LED PERMANENT 129 | // LED DATA XFER 130 | // ROTATE (flip+reverse) 131 | // i2c MASTER/FOLLOEWR 132 | // fadermin LSB 133 | // fadermin MSB 134 | // fadermax LSB 135 | // fadermax MSB 136 | 137 | // 16x USBccs 138 | // 16x TRSccs 139 | // 16x USBchannel 140 | // 16x TRS channel 141 | 142 | // So that's 3 for the mfg + 1 for the message + 80 bytes 143 | // can be done with a simple "read eighty bytes and send them." 144 | 145 | byte buffer[80]; 146 | readEEPROMArray(0, buffer, 80); 147 | 148 | int offset = 8; 149 | for(int i = 0; i < 80; i++) { 150 | byte data = buffer[i]; 151 | if(data == 0xff) { 152 | data = 0x7f; 153 | } 154 | sysexData[i+offset] = data; 155 | } 156 | 157 | D(Serial.println("Sending this data")); 158 | D(printHexArray(sysexData,88)); 159 | 160 | usbMIDI.sendSysEx(88, sysexData, false); 161 | forceMidiWrite = true; 162 | } -------------------------------------------------------------------------------- /firmware/Old_versions/First_version/_16n_faderbank_firmware_Sweet/_16n_faderbank_firmware_Sweet.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 16n Faderbank Firmware 3 | (c) 2017,2018 by Brian Crabtree, Sean Hellfritsch, Tom Armitage, and Brendon Cassidy 4 | MIT License 5 | */ 6 | 7 | /* 8 | NOTES: 9 | - Hardware MIDI is on pin 1 10 | - You **must** also compile this with Tools->USB type set to MIDI or MIDI/Serial (for debugging) 11 | - You also should overclock to 120MHz to make it as snappy as possible 12 | */ 13 | 14 | /* 15 | ALL configuration should take place in config.h. 16 | You can disable/enable flags, and configure MIDI channels in there. 17 | */ 18 | // mod for pitchbend & boot-up delay by Mangu Díaz 19 | 20 | #include "config.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "TxHelper.h" 27 | 28 | MIDI_CREATE_DEFAULT_INSTANCE(); 29 | 30 | // loop helpers 31 | int i, temp; 32 | 33 | // midi write helpers 34 | int q, shiftyTemp, notShiftyTemp; 35 | 36 | // the storage of the values; current is in the main loop; last value is for midi output 37 | int volatile currentValue[channelCount]; 38 | int lastMidiValue[channelCount]; 39 | 40 | // memory of the last unshifted value 41 | int lastValue[channelCount]; 42 | 43 | #ifdef MASTER 44 | 45 | // the i2c message buffer we are sending 46 | uint8_t messageBuffer[4]; 47 | 48 | // temporary values 49 | uint16_t valueTemp; 50 | uint8_t device = 0; 51 | uint8_t port = 0; 52 | 53 | #endif 54 | 55 | // the thing that smartly smooths the input 56 | ResponsiveAnalogRead *analog[channelCount]; 57 | 58 | // mux config 59 | CD74HC4067 mux(8, 7, 6, 5); 60 | #ifdef REV 61 | const int muxMapping[16] = {8, 9, 10, 11, 12, 13, 14, 15, 7, 6, 5, 4, 3, 2, 1, 0}; 62 | #else 63 | const int muxMapping[16] = {0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8}; 64 | #endif 65 | 66 | // MIDI timers 67 | IntervalTimer midiWriteTimer; 68 | IntervalTimer midiReadTimer; 69 | int midiInterval = 1000; // 1ms 70 | bool shouldDoMidiRead = false; 71 | bool shouldDoMidiWrite = false; 72 | 73 | // helper values for i2c reading and future expansion 74 | int activeInput = 0; 75 | int activeMode = 0; 76 | 77 | /* 78 | The function that sets up the application 79 | */ 80 | void setup() 81 | { 82 | 83 | #ifdef bootDelay // whait some time before boot-up 84 | delay(bootDelay); 85 | #endif 86 | 87 | #ifdef DEBUG 88 | while (!Serial) 89 | ; 90 | Serial.print("16n Firmware Debug Mode\n"); 91 | #endif 92 | 93 | // initialize the TX Helper 94 | #ifdef V125 95 | TxHelper::UseWire1(true); 96 | #else 97 | TxHelper::UseWire1(false); 98 | #endif 99 | TxHelper::SetPorts(16); 100 | TxHelper::SetModes(4); 101 | 102 | // set read resolution to teensy's 13 usable bits 103 | analogReadResolution(13); 104 | 105 | // initialize the value storage 106 | for (i = 0; i < channelCount; i++) 107 | { 108 | // analog[i] = new ResponsiveAnalogRead(0, false); 109 | 110 | analog[i] = new ResponsiveAnalogRead(0, true, .0001); 111 | analog[i]->setAnalogResolution(1 << 13); 112 | 113 | // ResponsiveAnalogRead is designed for 10-bit ADCs 114 | // meanining its threshold defaults to 4. Let's bump that for 115 | // our 13-bit adc by setting it to 4 << (13-10) 116 | analog[i]->setActivityThreshold(32); 117 | 118 | currentValue[i] = 0; 119 | lastMidiValue[i] = 0; 120 | //#ifdef MASTER 121 | lastValue[i] = 0; 122 | //#endif 123 | } 124 | 125 | // i2c using the default I2C pins on a Teensy 3.2 126 | #ifdef MASTER 127 | 128 | #ifdef DEBUG 129 | Serial.println("Enabling i2c in MASTER mode"); 130 | #endif 131 | 132 | #ifdef V125 133 | Wire1.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 134 | #else 135 | Wire.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 136 | #endif 137 | 138 | #else 139 | // non-master mode 140 | 141 | #ifdef DEBUG 142 | Serial.println("Enabling i2c enabled in SLAVE mode"); 143 | #endif 144 | 145 | #ifdef V125 146 | Wire1.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 147 | Wire1.onReceive(i2cWrite); 148 | Wire1.onRequest(i2cReadRequest); 149 | #else 150 | Wire.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 151 | Wire.onReceive(i2cWrite); 152 | Wire.onRequest(i2cReadRequest); 153 | #endif 154 | 155 | #endif 156 | 157 | // turn on the MIDI party 158 | MIDI.begin(); 159 | midiWriteTimer.begin(writeMidi, midiInterval); 160 | midiReadTimer.begin(readMidi, midiInterval); 161 | 162 | #ifdef LED 163 | pinMode(13, OUTPUT); 164 | digitalWrite(13, HIGH); 165 | #endif 166 | } 167 | 168 | /* 169 | The main read loop that goes through all of the sliders 170 | */ 171 | void loop() 172 | { 173 | // read loop using the i counter 174 | for (i = 0; i < channelCount; i++) 175 | { 176 | #ifdef V125 177 | temp = analogRead(ports[i]); // mux goes into A0 178 | #else 179 | // set mux to appropriate channel 180 | mux.channel(muxMapping[i]); 181 | 182 | // read the value 183 | temp = analogRead(0); // mux goes into A0 184 | #endif 185 | 186 | // put the value into the smoother 187 | analog[i]->update(temp); 188 | 189 | // read from the smoother, constrain (to account for tolerances), and map it 190 | temp = analog[i]->getValue(); 191 | 192 | #ifdef FLIP 193 | temp = MAXFADER - temp; 194 | #endif 195 | 196 | temp = constrain(temp, MINFADER, MAXFADER); 197 | 198 | temp = map(temp, MINFADER, MAXFADER, 0, 16383); 199 | 200 | // map and update the value 201 | currentValue[i] = temp; 202 | } 203 | 204 | if (shouldDoMidiRead) 205 | { 206 | doMidiRead(); 207 | noInterrupts(); 208 | shouldDoMidiRead = false; 209 | interrupts(); 210 | } 211 | 212 | if (shouldDoMidiWrite) 213 | { 214 | doMidiWrite(); 215 | noInterrupts(); 216 | shouldDoMidiWrite = false; 217 | interrupts(); 218 | } 219 | } 220 | 221 | /* 222 | Tiny function called via interrupt 223 | (it's important to catch inbound MIDI messages even if we do nothing with 224 | them.) 225 | */ 226 | void readMidi() 227 | { 228 | shouldDoMidiRead = true; 229 | } 230 | 231 | /* 232 | Function called when shouldDoMidiRead flag is HIGH 233 | */ 234 | 235 | void doMidiRead() 236 | { 237 | MIDI.read(); 238 | usbMIDI.read(); 239 | } 240 | 241 | /* 242 | Tiny function called via interrupt 243 | */ 244 | void writeMidi() 245 | { 246 | shouldDoMidiWrite = true; 247 | } 248 | 249 | /* 250 | The function that writes changes in slider positions out the midi ports 251 | Called when shouldDoMidiWrite flag is HIGH 252 | */ 253 | void doMidiWrite() 254 | { 255 | // write loop using the q counter ( 256 | // (can't use i or temp cuz this might interrupt the reads) 257 | for (q = 0; q < channelCount; q++) 258 | { 259 | notShiftyTemp = currentValue[q]; 260 | 261 | // shift for MIDI precision (0-127) 262 | shiftyTemp = notShiftyTemp >> 7; 263 | 264 | // if there was a change in the midi value 265 | if (( shiftyTemp != lastMidiValue[q]) || ((( usb_ccs[q] == 128 ) || ( trs_ccs[q] == 128 )) && ( notShiftyTemp != lastValue[q] ))) 266 | { 267 | if ( usb_ccs[q] < 128 ) 268 | { // send the message over USB 269 | usbMIDI.sendControlChange(usb_ccs[q], shiftyTemp, usb_channels[q]); 270 | } 271 | else 272 | { 273 | usbMIDI.sendPitchBend((notShiftyTemp - 8192), usb_channels[q]); 274 | } 275 | 276 | if ( trs_ccs[q] < 128 ) 277 | { // send the message over physical MIDI 278 | MIDI.sendControlChange(trs_ccs[q], shiftyTemp, trs_channels[q]); 279 | } 280 | else 281 | { 282 | MIDI.sendPitchBend((notShiftyTemp - 8192), trs_channels[q]); 283 | } 284 | 285 | // store the shifted value for future comparison 286 | lastMidiValue[q] = shiftyTemp; 287 | 288 | #ifdef DEBUG 289 | Serial.printf("MIDI[%d]: %d\n", q, shiftyTemp); 290 | #endif 291 | } 292 | 293 | // we send out to all three supported i2c slave devices 294 | // keeps the firmware simple :) 295 | 296 | if (notShiftyTemp != lastValue[q]) 297 | { 298 | #ifdef MASTER 299 | #ifdef DEBUG 300 | Serial.printf("i2c Master[%d]: %d\n", q, notShiftyTemp); 301 | #endif 302 | 303 | // for 4 output devices 304 | port = q % 4; 305 | device = q / 4; 306 | 307 | // TXo 308 | sendi2c(0x60, device, 0x11, port, notShiftyTemp); 309 | 310 | // ER-301 311 | sendi2c(0x31, 0, 0x11, q, notShiftyTemp); 312 | 313 | // ANSIBLE 314 | sendi2c(0x20, device << 1, 0x06, port, notShiftyTemp); 315 | #endif 316 | lastValue[q] = notShiftyTemp; 317 | } 318 | } 319 | } 320 | 321 | #ifdef MASTER 322 | 323 | /* 324 | Sends an i2c command out to a slave when running in master mode 325 | */ 326 | void sendi2c(uint8_t model, uint8_t deviceIndex, uint8_t cmd, uint8_t devicePort, int value) 327 | { 328 | 329 | valueTemp = (uint16_t)value; 330 | messageBuffer[2] = valueTemp >> 8; 331 | messageBuffer[3] = valueTemp & 0xff; 332 | 333 | #ifdef V125 334 | Wire1.beginTransmission(model + deviceIndex); 335 | messageBuffer[0] = cmd; 336 | messageBuffer[1] = (uint8_t)devicePort; 337 | Wire1.write(messageBuffer, 4); 338 | Wire1.endTransmission(); 339 | #else 340 | Wire.beginTransmission(model + deviceIndex); 341 | messageBuffer[0] = cmd; 342 | messageBuffer[1] = (uint8_t)devicePort; 343 | Wire.write(messageBuffer, 4); 344 | Wire.endTransmission(); 345 | #endif 346 | } 347 | 348 | #else 349 | 350 | /* 351 | The function that responds to a command from i2c. 352 | In the first version, this simply sets the port to be read from. 353 | */ 354 | void i2cWrite(size_t len) 355 | { 356 | 357 | #ifdef DEBUG 358 | Serial.printf("i2c Write (%d)\n", len); 359 | #endif 360 | 361 | // parse the response 362 | TxResponse response = TxHelper::Parse(len); 363 | 364 | // true command our setting of the input for a read? 365 | if (len == 1) 366 | { 367 | 368 | // use a helper to decode the command 369 | TxIO io = TxHelper::DecodeIO(response.Command); 370 | 371 | #ifdef DEBUG 372 | Serial.printf("Port: %d; Mode: %d [%d]\n", io.Port, io.Mode, response.Command); 373 | #endif 374 | 375 | // this is the single byte that sets the active input 376 | activeInput = io.Port; 377 | activeMode = io.Mode; 378 | } 379 | else 380 | { 381 | // act on the command 382 | actOnCommand(response.Command, response.Output, response.Value); 383 | } 384 | } 385 | 386 | /* 387 | The function that responds to read requests over i2c. 388 | This uses the port from the write request to determine which slider to send. 389 | */ 390 | void i2cReadRequest() 391 | { 392 | 393 | #ifdef DEBUG 394 | Serial.print("i2c Read\n"); 395 | #endif 396 | 397 | // get and cast the value 398 | uint16_t shiftReady = 0; 399 | switch (activeMode) 400 | { 401 | case 1: 402 | shiftReady = (uint16_t)currentValue[activeInput]; 403 | break; 404 | case 2: 405 | shiftReady = (uint16_t)currentValue[activeInput]; 406 | break; 407 | default: 408 | shiftReady = (uint16_t)currentValue[activeInput]; 409 | break; 410 | } 411 | 412 | #ifdef DEBUG 413 | Serial.printf("delivering: %d; value: %d [%d]\n", activeInput, currentValue[activeInput], shiftReady); 414 | #endif 415 | 416 | // send the puppy as a pair of bytes 417 | #ifdef V125 418 | Wire1.write(shiftReady >> 8); 419 | Wire1.write(shiftReady & 255); 420 | #else 421 | Wire.write(shiftReady >> 8); 422 | Wire.write(shiftReady & 255); 423 | #endif 424 | } 425 | 426 | /* 427 | Future function if we add more i2c capabilities beyond reading values. 428 | */ 429 | void actOnCommand(byte cmd, byte out, int value) {} 430 | 431 | #endif 432 | -------------------------------------------------------------------------------- /firmware/Old_versions/2.0.1/_16n_faderbank_firmware/_16n_faderbank_firmware.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Firmware 3 | * (c) 2017,2018,2020 by Brian Crabtree, Sean Hellfritsch, Tom Armitage, and Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | /* 8 | * NOTES: 9 | * - Hardware MIDI is on pin 1 10 | * - You **must** also compile this with Tools->USB type set to MIDI or MIDI/Serial (for debugging) 11 | * - You also should overclock to 120MHz to make it as snappy as possible 12 | */ 13 | 14 | /* 15 | * Most configuration now hpapens via online editor. 16 | * config.h is mainly for developer configuration. 17 | */ 18 | // slightly modified version for the Tesseract Modular Sweet Sixteen by Mangu Díaz 19 | // changes from the original 16n 2.0.1 firmware from Tom Armitage: 20 | // - 'rotate' and i2c 'leader' set ON by default 21 | // - calibration of the max fader value is now possible when 'rotated' is active 22 | // - channel numbers are not inverted when 'rotate' is active 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "config.h" 31 | #include "TxHelper.h" 32 | 33 | // wrap code to be executed only under DEBUG conditions in D() 34 | #ifdef DEBUG 35 | #define D(x) x 36 | #else 37 | #define D(x) 38 | #endif 39 | 40 | MIDI_CREATE_DEFAULT_INSTANCE(); 41 | 42 | // loop helpers 43 | int i, temp; 44 | 45 | // midi write helpers 46 | int q, shiftyTemp, notShiftyTemp, lastMidiActivityAt; 47 | int midiDirty = 0; 48 | const int midiFlashDuration = 50; 49 | int ledPin = 13; 50 | 51 | // the storage of the values; current is in the main loop; last value is for midi output 52 | int volatile currentValue[channelCount]; 53 | int lastMidiValue[channelCount]; 54 | 55 | // variables to hold configuration 56 | int usbChannels[channelCount]; 57 | int trsChannels[channelCount]; 58 | int usbCCs[channelCount]; 59 | int trsCCs[channelCount]; 60 | int legacyPorts[channelCount]; // for V125 only 61 | int flip; 62 | int ledOn; 63 | int ledFlash; 64 | int i2cMaster; 65 | 66 | int faderMin; 67 | int faderMax; 68 | 69 | // variables for i2c master mode 70 | // memory of the last unshifted value 71 | int lastValue[channelCount]; 72 | 73 | // the i2c message buffer we are sending 74 | uint8_t messageBuffer[4]; 75 | 76 | // temporary values 77 | uint16_t valueTemp; 78 | uint8_t device = 0; 79 | uint8_t port = 0; 80 | 81 | // master i2c specific stuff 82 | const int ansibleI2Caddress = 0x20; 83 | const int er301I2Caddress = 0x31; 84 | const int txoI2Caddress = 0x60; 85 | bool er301Present = false; 86 | bool ansiblePresent = false; 87 | bool txoPresent = false; 88 | 89 | 90 | // the thing that smartly smooths the input 91 | ResponsiveAnalogRead *analog[channelCount]; 92 | 93 | // mux config 94 | CD74HC4067 mux(8, 7, 6, 5); 95 | const int muxMapping[16] = {0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8}; 96 | 97 | // MIDI timers 98 | IntervalTimer midiWriteTimer; 99 | IntervalTimer midiReadTimer; 100 | int midiInterval = 1000; // 1ms 101 | bool shouldDoMidiRead = false; 102 | bool shouldDoMidiWrite = false; 103 | bool forceMidiWrite = false; 104 | 105 | // helper values for i2c reading and future expansion 106 | int activeInput = 0; 107 | int activeMode = 0; 108 | 109 | /* 110 | * The function that sets up the application 111 | */ 112 | void setup() 113 | { 114 | 115 | D(Serial.println("16n Firmware Debug Mode")); 116 | 117 | #ifdef BOOTDELAY // wait some time before continuing boot-up (default is none) 118 | delay(BOOTDELAY); 119 | #endif 120 | 121 | checkDefaultSettings(); 122 | 123 | loadSettingsFromEEPROM(); 124 | i2cMaster = EEPROM.read(3) == 1; 125 | 126 | usbMIDI.setHandleSystemExclusive(processIncomingSysex); 127 | 128 | #ifdef V125 129 | // analog ports on the Teensy for the 1.25 board. 130 | if(flip) { 131 | int portsToAssign[] = {A15, A14, A13, A12, A11, A10, A9, A8, A7, A6, A5, A4, A3, A2, A1, A0}; 132 | for(int i=0; i < channelCount; i++) { 133 | legacyPorts[i] = portsToAssign[i]; 134 | } 135 | } else { 136 | int portsToAssign[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15}; 137 | for(int i=0; i < channelCount; i++) { 138 | legacyPorts[i] = portsToAssign[i]; 139 | } 140 | } 141 | #endif 142 | 143 | // initialize the TX Helper 144 | #ifdef V125 145 | TxHelper::UseWire1(true); 146 | #else 147 | TxHelper::UseWire1(false); 148 | #endif 149 | TxHelper::SetPorts(16); 150 | TxHelper::SetModes(4); 151 | 152 | // set read resolution to teensy's 13 usable bits 153 | analogReadResolution(13); 154 | 155 | // initialize the value storage 156 | for (i = 0; i < channelCount; i++) 157 | { 158 | // analog[i] = new ResponsiveAnalogRead(0, false); 159 | 160 | analog[i] = new ResponsiveAnalogRead(0, true, .0001); 161 | analog[i]->setAnalogResolution(1 << 13); 162 | 163 | // ResponsiveAnalogRead is designed for 10-bit ADCs 164 | // meanining its threshold defaults to 4. Let's bump that for 165 | // our 13-bit adc by setting it to 4 << (13-10) 166 | analog[i]->setActivityThreshold(32); 167 | 168 | currentValue[i] = 0; 169 | lastMidiValue[i] = 0; 170 | 171 | if(i2cMaster) { 172 | lastValue[i] = 0; 173 | } 174 | } 175 | 176 | // i2c using the default I2C pins on a Teensy 3.2 177 | if(i2cMaster) { 178 | 179 | D(Serial.println("Enabling i2c in MASTER mode")); 180 | 181 | #ifdef V125 182 | Wire1.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 183 | Wire1.setDefaultTimeout(10000); // 10ms 184 | 185 | D(Serial.println ("Scanning I2C bus")); 186 | 187 | Wire1.begin(); 188 | 189 | for (byte i = 8; i < 120; i++) 190 | { 191 | Wire1.beginTransmission (i); 192 | if (Wire1.endTransmission () == 0) 193 | { 194 | if(i == ansibleI2Caddress) { 195 | ansiblePresent = true; 196 | D(Serial.println ("Found ansible")); 197 | } 198 | 199 | if(i == txoI2Caddress) { 200 | txoPresent = true; 201 | D(Serial.println ("Found TXO")); 202 | } 203 | 204 | if(i == er301I2Caddress) { 205 | er301Present = true; 206 | D(Serial.println ("Found ER301")); 207 | } 208 | delay (1); // maybe unneeded? 209 | } // end of good response 210 | } // end of for loop 211 | 212 | D(Serial.println ("I2C scan complete.")); 213 | 214 | #else 215 | Wire.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 216 | Wire.setDefaultTimeout(10000); // 10ms 217 | 218 | D(Serial.println ("Scanning I2C bus")); 219 | 220 | Wire.begin(); 221 | 222 | for (byte i = 8; i < 120; i++) 223 | { 224 | Wire.beginTransmission (i); 225 | if (Wire.endTransmission () == 0) 226 | { 227 | if(i == ansibleI2Caddress) { 228 | ansiblePresent = true; 229 | D(Serial.println ("Found ansible")); 230 | } 231 | 232 | if(i == txoI2Caddress) { 233 | txoPresent = true; 234 | D(Serial.println ("Found TXO")); 235 | } 236 | 237 | if(i == er301I2Caddress) { 238 | er301Present = true; 239 | D(Serial.println ("Found ER301")); 240 | } 241 | delay (1); // maybe unneeded? 242 | } // end of good response 243 | } // end of for loop 244 | 245 | Serial.println ("I2C scan complete."); 246 | 247 | #endif 248 | 249 | } else { 250 | // non-master mode 251 | 252 | D(Serial.println("Enabling i2c enabled in SLAVE mode")); 253 | 254 | #ifdef V125 255 | Wire1.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 256 | Wire1.onReceive(i2cWrite); 257 | Wire1.onRequest(i2cReadRequest); 258 | #else 259 | Wire.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 260 | Wire.onReceive(i2cWrite); 261 | Wire.onRequest(i2cReadRequest); 262 | #endif 263 | 264 | } 265 | 266 | // turn on the MIDI party 267 | MIDI.begin(); 268 | midiWriteTimer.begin(writeMidi, midiInterval); 269 | midiReadTimer.begin(readMidi, midiInterval); 270 | 271 | pinMode(ledPin, OUTPUT); 272 | 273 | if(ledOn) { 274 | digitalWrite(ledPin, HIGH); 275 | } 276 | } 277 | 278 | /* 279 | * The main read loop that goes through all of the sliders 280 | */ 281 | void loop() 282 | { 283 | // this whole chunk makes the LED flicker on MIDI activity - 284 | // and inverts that flicker if the power light is on. 285 | if (ledFlash) { 286 | if (millis() > (lastMidiActivityAt + midiFlashDuration)) { 287 | if(ledOn) { 288 | digitalWrite(ledPin, HIGH); 289 | } else { 290 | digitalWrite(ledPin, LOW); 291 | } 292 | midiDirty = 0; 293 | } else { 294 | if(ledOn) { 295 | digitalWrite(ledPin, LOW); 296 | } else { 297 | digitalWrite(ledPin, HIGH); 298 | } 299 | } 300 | } else { 301 | if(ledOn) { 302 | digitalWrite(ledPin, HIGH); 303 | } else { 304 | digitalWrite(ledPin, LOW); 305 | } 306 | } 307 | 308 | // read loop using the i counter 309 | for (i = 0; i < channelCount; i++) 310 | { 311 | #ifdef V125 312 | temp = analogRead(legacyPorts[i]); // mux goes into A0 313 | #else 314 | // set mux to appropriate channel 315 | // if(flip) { 316 | // mux.channel(muxMapping[channelCount - i - 1]); 317 | // } else { 318 | mux.channel(muxMapping[i]); 319 | // } 320 | 321 | // read the value 322 | temp = analogRead(0); // mux goes into A0 323 | #endif 324 | 325 | // put the value into the smoother 326 | analog[i]->update(temp); 327 | 328 | // read from the smoother, constrain (to account for tolerances), and map it 329 | temp = analog[i]->getValue(); 330 | 331 | if(flip){ 332 | temp = 8191 - temp; // temp = faderMax - temp; 333 | } 334 | 335 | temp = constrain(temp, faderMin, faderMax); 336 | 337 | temp = map(temp, faderMin, faderMax, 0, 16383); 338 | 339 | // map and update the value 340 | currentValue[i] = temp; 341 | } 342 | 343 | if (shouldDoMidiRead) 344 | { 345 | doMidiRead(); 346 | noInterrupts(); 347 | shouldDoMidiRead = false; 348 | interrupts(); 349 | } 350 | 351 | if (shouldDoMidiWrite) 352 | { 353 | doMidiWrite(); 354 | noInterrupts(); 355 | shouldDoMidiWrite = false; 356 | interrupts(); 357 | } 358 | } 359 | 360 | /* 361 | * Tiny function called via interrupt 362 | * (it's important to catch inbound MIDI messages even if we do nothing with 363 | * them.) 364 | */ 365 | void readMidi() 366 | { 367 | shouldDoMidiRead = true; 368 | } 369 | 370 | /* 371 | * Function called when shouldDoMidiRead flag is HIGH 372 | */ 373 | 374 | void doMidiRead() 375 | { 376 | MIDI.read(); 377 | usbMIDI.read(); 378 | } 379 | 380 | /* 381 | * Tiny function called via interrupt 382 | */ 383 | void writeMidi() 384 | { 385 | shouldDoMidiWrite = true; 386 | } 387 | 388 | /* 389 | * The function that writes changes in slider positions out the midi ports 390 | * Called when shouldDoMidiWrite flag is HIGH 391 | */ 392 | void doMidiWrite() 393 | { 394 | // write loop using the q counter ( 395 | // (can't use i or temp cuz this might interrupt the reads) 396 | for (q = 0; q < channelCount; q++) 397 | { 398 | notShiftyTemp = currentValue[q]; 399 | 400 | // shift for MIDI precision (0-127) 401 | shiftyTemp = notShiftyTemp >> 7; 402 | 403 | // if there was a change in the midi value 404 | if ((shiftyTemp != lastMidiValue[q]) || forceMidiWrite) 405 | { 406 | if(ledFlash && !midiDirty) { 407 | lastMidiActivityAt = millis(); 408 | midiDirty = 1; 409 | } 410 | // send the message over USB and physical MIDI 411 | usbMIDI.sendControlChange(usbCCs[q], shiftyTemp, usbChannels[q]); 412 | MIDI.sendControlChange(trsCCs[q], shiftyTemp, trsChannels[q]); 413 | 414 | // store the shifted value for future comparison 415 | lastMidiValue[q] = shiftyTemp; 416 | 417 | // D(Serial.printf("MIDI[%d]: %d\n", q, shiftyTemp)); 418 | } 419 | 420 | if(i2cMaster) { 421 | 422 | // we send out to all three supported i2c slave devices 423 | // keeps the firmware simple :) 424 | 425 | if (notShiftyTemp != lastValue[q]) 426 | { 427 | D(Serial.printf("i2c Master[%d]: %d\n", q, notShiftyTemp)); 428 | 429 | // for 4 output devices 430 | port = q % 4; 431 | device = q / 4; 432 | 433 | // TXo 434 | if(txoPresent) { 435 | sendi2c(txoI2Caddress, device, 0x11, port, notShiftyTemp); 436 | } 437 | 438 | // ER-301 439 | if(er301Present) { 440 | sendi2c(er301I2Caddress, 0, 0x11, q, notShiftyTemp); 441 | } 442 | 443 | // ANSIBLE 444 | if(ansiblePresent) { 445 | sendi2c(0x20, device << 1, 0x06, port, notShiftyTemp); 446 | } 447 | 448 | lastValue[q] = notShiftyTemp; 449 | } 450 | } 451 | } 452 | forceMidiWrite = false; 453 | } 454 | 455 | /* 456 | * Sends an i2c command out to a slave when running in master mode 457 | */ 458 | void sendi2c(uint8_t model, uint8_t deviceIndex, uint8_t cmd, uint8_t devicePort, int value) 459 | { 460 | 461 | valueTemp = (uint16_t)value; 462 | messageBuffer[2] = valueTemp >> 8; 463 | messageBuffer[3] = valueTemp & 0xff; 464 | 465 | #ifdef V125 466 | Wire1.beginTransmission(model + deviceIndex); 467 | messageBuffer[0] = cmd; 468 | messageBuffer[1] = (uint8_t)devicePort; 469 | Wire1.write(messageBuffer, 4); 470 | Wire1.endTransmission(); 471 | #else 472 | Wire.beginTransmission(model + deviceIndex); 473 | messageBuffer[0] = cmd; 474 | messageBuffer[1] = (uint8_t)devicePort; 475 | Wire.write(messageBuffer, 4); 476 | Wire.endTransmission(); 477 | #endif 478 | } 479 | 480 | /* 481 | * The function that responds to a command from i2c. 482 | * In the first version, this simply sets the port to be read from. 483 | */ 484 | void i2cWrite(size_t len) 485 | { 486 | 487 | D(Serial.printf("i2c Write (%d)\n", len)); 488 | 489 | // parse the response 490 | TxResponse response = TxHelper::Parse(len); 491 | 492 | // true command our setting of the input for a read? 493 | if (len == 1) 494 | { 495 | 496 | // use a helper to decode the command 497 | TxIO io = TxHelper::DecodeIO(response.Command); 498 | 499 | D(Serial.printf("Port: %d; Mode: %d [%d]\n", io.Port, io.Mode, response.Command)); 500 | 501 | // this is the single byte that sets the active input 502 | activeInput = io.Port; 503 | activeMode = io.Mode; 504 | } 505 | else 506 | { 507 | // act on the command 508 | actOnCommand(response.Command, response.Output, response.Value); 509 | } 510 | } 511 | 512 | /* 513 | * The function that responds to read requests over i2c. 514 | * This uses the port from the write request to determine which slider to send. 515 | */ 516 | void i2cReadRequest() 517 | { 518 | 519 | D(Serial.print("i2c Read\n")); 520 | 521 | // get and cast the value 522 | uint16_t shiftReady = 0; 523 | switch (activeMode) 524 | { 525 | case 1: 526 | shiftReady = (uint16_t)currentValue[activeInput]; 527 | break; 528 | case 2: 529 | shiftReady = (uint16_t)currentValue[activeInput]; 530 | break; 531 | default: 532 | shiftReady = (uint16_t)currentValue[activeInput]; 533 | break; 534 | } 535 | 536 | D(Serial.printf("delivering: %d; value: %d [%d]\n", activeInput, currentValue[activeInput], shiftReady)); 537 | 538 | // send the puppy as a pair of bytes 539 | #ifdef V125 540 | Wire1.write(shiftReady >> 8); 541 | Wire1.write(shiftReady & 255); 542 | #else 543 | Wire.write(shiftReady >> 8); 544 | Wire.write(shiftReady & 255); 545 | #endif 546 | } 547 | 548 | /* 549 | * Future function if we add more i2c capabilities beyond reading values. 550 | */ 551 | void actOnCommand(byte cmd, byte out, int value) {} 552 | -------------------------------------------------------------------------------- /firmware/Old_versions/Sweet_mkII/_16n_faderbank_firmware_Sweet_mkII/_16n_faderbank_firmware_Sweet_mkII.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 16n Faderbank Firmware 3 | (c) 2017,2018 by Brian Crabtree, Sean Hellfritsch, Tom Armitage, and Brendon Cassidy 4 | MIT License 5 | */ 6 | 7 | /* 8 | NOTES: 9 | - Hardware MIDI is on pin 1 10 | - You **must** also compile this with Tools->USB type set to MIDI or MIDI/Serial (for debugging) 11 | - You also should overclock to 120MHz to make it as snappy as possible 12 | */ 13 | 14 | /* 15 | ALL configuration should take place in config.h. 16 | You can disable/enable flags, and configure MIDI channels in there. 17 | */ 18 | // mod for pitchbend & boot-up delay by Mangu Díaz 19 | // Tesseract Modular 20 | // www.tesseractmodular.com 21 | // experimental version for Sweet Sixteen mkII and 'GESS' (Gate Expander for Sweet Sixteen) 22 | 23 | #include "config.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "TxHelper.h" 29 | 30 | #ifdef GESS // only necessary for the GESS & midi note thing 31 | 32 | #include 33 | // gate in, panel led and function button pins: 34 | const int G[8] = { 3, 0, 22, 20, 21, 23, 4, 2 }; 35 | const int Led_ = 12; 36 | const int Butt = 15; 37 | // variables for the gate inputs 38 | bool volatile _G[8] ; // state of the gate input 39 | bool old_G[8]; 40 | bool nNote[8]; 41 | bool nVelocity[8]; 42 | byte _nNote[8]; 43 | byte _nVelocity[8]; 44 | byte _nChannel[8]; 45 | // variables for the functon button 46 | bool _Butt ; 47 | bool old_Butt = HIGH; 48 | // loop helpers 49 | int ii, k; 50 | // preset management 51 | int nAddress = 0; // for the memory address 52 | int nPreset; 53 | bool loadActive = false; 54 | bool saveActive = false; 55 | #endif 56 | 57 | MIDI_CREATE_DEFAULT_INSTANCE(); 58 | 59 | // loop helpers 60 | int i, temp; 61 | 62 | // midi write helpers 63 | int q, shiftyTemp, notShiftyTemp; 64 | 65 | // the storage of the values; current is in the main loop; last value is for midi output 66 | int volatile currentValue[channelCount]; 67 | int lastMidiValue[channelCount]; 68 | 69 | // memory of the last unshifted value 70 | int lastValue[channelCount]; 71 | 72 | #ifdef MASTER 73 | 74 | // the i2c message buffer we are sending 75 | uint8_t messageBuffer[4]; 76 | 77 | // temporary values 78 | uint16_t valueTemp; 79 | uint8_t device = 0; 80 | uint8_t port = 0; 81 | 82 | #endif 83 | 84 | // the thing that smartly smooths the input 85 | ResponsiveAnalogRead *analog[channelCount]; 86 | 87 | // mux config 88 | CD74HC4067 mux(8, 7, 6, 5); 89 | #ifdef REV 90 | const int muxMapping[16] = {8, 9, 10, 11, 12, 13, 14, 15, 7, 6, 5, 4, 3, 2, 1, 0}; 91 | #else 92 | const int muxMapping[16] = {0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8}; 93 | #endif 94 | 95 | // MIDI timers 96 | IntervalTimer midiWriteTimer; 97 | IntervalTimer midiReadTimer; 98 | int midiInterval = 1000; // 1ms 99 | bool shouldDoMidiRead = false; 100 | bool shouldDoMidiWrite = false; 101 | 102 | // helper values for i2c reading and future expansion 103 | int activeInput = 0; 104 | int activeMode = 0; 105 | 106 | /* 107 | The function that sets up the application 108 | */ 109 | void setup() 110 | { 111 | 112 | #ifdef bootDelay // whait some time before boot-up 113 | delay(bootDelay); 114 | #endif 115 | 116 | #ifdef GESS 117 | for (i = 0; i < 8; i++) 118 | { 119 | pinMode (G[i], INPUT_PULLUP); 120 | _nChannel[i] = i + 1; 121 | old_G[i] = HIGH; 122 | nNote[i] = true; 123 | nVelocity[i] = true; 124 | } 125 | pinMode (Butt, INPUT_PULLUP); 126 | pinMode (Led_, OUTPUT); 127 | // boot up pattern 128 | for ( i = 0; i < 3; i++) 129 | { 130 | digitalWrite(Led_, HIGH); 131 | delay(100); 132 | digitalWrite(Led_, LOW); 133 | delay(100); 134 | } 135 | #endif 136 | 137 | #ifdef DEBUG 138 | while (!Serial) 139 | ; 140 | Serial.print("16n Firmware Debug Mode\n"); 141 | #endif 142 | 143 | // initialize the TX Helper 144 | #ifdef V125 145 | TxHelper::UseWire1(true); 146 | #else 147 | TxHelper::UseWire1(false); 148 | #endif 149 | TxHelper::SetPorts(16); 150 | TxHelper::SetModes(4); 151 | 152 | // set read resolution to teensy's 13 usable bits 153 | analogReadResolution(13); 154 | 155 | // initialize the value storage 156 | for (i = 0; i < channelCount; i++) 157 | { 158 | // analog[i] = new ResponsiveAnalogRead(0, false); 159 | 160 | analog[i] = new ResponsiveAnalogRead(0, true, .0001); 161 | analog[i]->setAnalogResolution(1 << 13); 162 | 163 | // ResponsiveAnalogRead is designed for 10-bit ADCs 164 | // meanining its threshold defaults to 4. Let's bump that for 165 | // our 13-bit adc by setting it to 4 << (13-10) 166 | analog[i]->setActivityThreshold(32); 167 | 168 | currentValue[i] = 0; 169 | lastMidiValue[i] = 0; 170 | //#ifdef MASTER 171 | lastValue[i] = 0; 172 | //#endif 173 | } 174 | 175 | // i2c using the default I2C pins on a Teensy 3.2 176 | #ifdef MASTER 177 | 178 | #ifdef DEBUG 179 | Serial.println("Enabling i2c in MASTER mode"); 180 | #endif 181 | 182 | #ifdef V125 183 | Wire1.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 184 | #else 185 | Wire.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 186 | #endif 187 | 188 | #else 189 | // non-master mode 190 | 191 | #ifdef DEBUG 192 | Serial.println("Enabling i2c enabled in SLAVE mode"); 193 | #endif 194 | 195 | #ifdef V125 196 | Wire1.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 197 | Wire1.onReceive(i2cWrite); 198 | Wire1.onRequest(i2cReadRequest); 199 | #else 200 | Wire.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 201 | Wire.onReceive(i2cWrite); 202 | Wire.onRequest(i2cReadRequest); 203 | #endif 204 | 205 | #endif 206 | 207 | // turn on the MIDI party 208 | MIDI.begin(); 209 | midiWriteTimer.begin(writeMidi, midiInterval); 210 | midiReadTimer.begin(readMidi, midiInterval); 211 | 212 | #ifdef LED 213 | pinMode(13, OUTPUT); 214 | digitalWrite(13, HIGH); 215 | #endif 216 | } 217 | 218 | /* 219 | The main read loop that goes through all of the sliders 220 | */ 221 | void loop() 222 | { 223 | // read loop using the i counter 224 | for (i = 0; i < channelCount; i++) 225 | { 226 | #ifdef V125 227 | temp = analogRead(ports[i]); // mux goes into A0 228 | #else 229 | // set mux to appropriate channel 230 | mux.channel(muxMapping[i]); 231 | 232 | // read the value 233 | temp = analogRead(0); // mux goes into A0 234 | #endif 235 | 236 | // put the value into the smoother 237 | analog[i]->update(temp); 238 | 239 | // read from the smoother, constrain (to account for tolerances), and map it 240 | temp = analog[i]->getValue(); 241 | 242 | #ifdef FLIP 243 | temp = 8191 - temp; // MAXFADER - temp; 244 | #endif 245 | 246 | temp = constrain(temp, MINFADER, MAXFADER); 247 | 248 | temp = map(temp, MINFADER, MAXFADER, 0, 16383); 249 | 250 | // map and update the value 251 | currentValue[i] = temp; 252 | 253 | #ifdef GESS 254 | // read the gate inputs 255 | if (i < 8) 256 | { 257 | _G[i] = digitalRead(G[i]); 258 | } 259 | #endif 260 | 261 | } 262 | 263 | if (shouldDoMidiRead) 264 | { 265 | doMidiRead(); 266 | noInterrupts(); 267 | shouldDoMidiRead = false; 268 | interrupts(); 269 | } 270 | 271 | if (shouldDoMidiWrite) 272 | { 273 | doMidiWrite(); 274 | noInterrupts(); 275 | shouldDoMidiWrite = false; 276 | interrupts(); 277 | } 278 | } 279 | 280 | /* 281 | Tiny function called via interrupt 282 | (it's important to catch inbound MIDI messages even if we do nothing with 283 | them.) 284 | */ 285 | void readMidi() 286 | { 287 | shouldDoMidiRead = true; 288 | } 289 | 290 | /* 291 | Function called when shouldDoMidiRead flag is HIGH 292 | */ 293 | 294 | void doMidiRead() 295 | { 296 | MIDI.read(); 297 | usbMIDI.read(); 298 | } 299 | 300 | /* 301 | Tiny function called via interrupt 302 | */ 303 | void writeMidi() 304 | { 305 | shouldDoMidiWrite = true; 306 | } 307 | 308 | /* 309 | The function that writes changes in slider positions out the midi ports 310 | Called when shouldDoMidiWrite flag is HIGH 311 | */ 312 | void doMidiWrite() 313 | { 314 | // write loop using the q counter ( 315 | // (can't use i or temp cuz this might interrupt the reads) 316 | for (q = 0; q < channelCount; q++) 317 | { 318 | notShiftyTemp = currentValue[q]; 319 | 320 | // shift for MIDI precision (0-127) 321 | shiftyTemp = notShiftyTemp >> 7; 322 | 323 | // if there was a change in the midi value 324 | if (( shiftyTemp != lastMidiValue[q]) || ((( usb_ccs[q] == 128 ) || ( trs_ccs[q] == 128 )) && ( notShiftyTemp != lastValue[q] ))) 325 | { 326 | if ( usb_ccs[q] < 128 ) 327 | { // send the message over USB 328 | usbMIDI.sendControlChange(usb_ccs[q], shiftyTemp, usb_channels[q]); 329 | } 330 | else 331 | { 332 | usbMIDI.sendPitchBend((notShiftyTemp - 8192), usb_channels[q]); 333 | } 334 | 335 | #ifdef GESS 336 | if ((( q < 8 ) && (nNote[q] == false)) || (( q > 7 ) && (nVelocity[q - 8] == false))) 337 | { 338 | #endif 339 | 340 | if ( trs_ccs[q] < 128 ) 341 | { // send the message over physical MIDI 342 | MIDI.sendControlChange(trs_ccs[q], shiftyTemp, trs_channels[q]); 343 | } 344 | else 345 | { 346 | MIDI.sendPitchBend((notShiftyTemp - 8192), trs_channels[q]); 347 | } 348 | 349 | #ifdef GESS 350 | } 351 | #endif 352 | 353 | // store the shifted value for future comparison 354 | lastMidiValue[q] = shiftyTemp; 355 | 356 | #ifdef DEBUG 357 | Serial.printf("MIDI[%d]: %d\n", q, shiftyTemp); 358 | #endif 359 | } 360 | 361 | #ifdef GESS 362 | // Midi Note 363 | if ((q > 7) && (_G[q - 8] != old_G[q - 8])) // only if the gate input has changed we do this: 364 | { 365 | k = ( q - 8) ; 366 | if ( _G[k] == LOW ) //note ON 367 | { 368 | if (nNote[k] == true) // taking the note value from the upper row of faders instead of a fixed value 369 | { 370 | _nNote[k] = lastMidiValue[k]; 371 | } 372 | if (nVelocity[k] == true) // taking the midi velocity from the lower row instead instead of a fixed value 373 | { 374 | _nVelocity[k] = lastMidiValue[q]; 375 | } 376 | MIDI.sendNoteOn(_nNote[k], _nVelocity[k], _nChannel[k] ); // at the moment channels are 1 to 8 preassigned 377 | } 378 | else //Note Off 379 | { 380 | MIDI.sendNoteOff(_nNote[k], _nVelocity[k], _nChannel[k] ); 381 | } 382 | old_G[k] = _G[k]; 383 | } //end of midi note code 384 | #endif 385 | 386 | // we send out to all three supported i2c slave devices 387 | // keeps the firmware simple :) 388 | 389 | if (notShiftyTemp != lastValue[q]) 390 | { 391 | #ifdef MASTER 392 | #ifdef DEBUG 393 | Serial.printf("i2c Master[%d]: %d\n", q, notShiftyTemp); 394 | #endif 395 | 396 | // for 4 output devices 397 | port = q % 4; 398 | device = q / 4; 399 | 400 | // TXo 401 | sendi2c(0x60, device, 0x11, port, notShiftyTemp); 402 | 403 | // ER-301 404 | sendi2c(0x31, 0, 0x11, q, notShiftyTemp); 405 | 406 | // ANSIBLE 407 | sendi2c(0x20, device << 1, 0x06, port, notShiftyTemp); 408 | #endif 409 | lastValue[q] = notShiftyTemp; 410 | } 411 | } // end of channels loop 412 | 413 | #ifdef GESS 414 | // button read to load & save presets etc: 415 | _Butt = digitalRead(Butt); 416 | 417 | if (_Butt != old_Butt) // change in the button 418 | { 419 | if (_Butt == true ) 420 | { 421 | // button depressed, switch the load preset mode 422 | saveActive = false; 423 | loadActive = !loadActive; 424 | digitalWrite(Led_, loadActive); 425 | } 426 | else 427 | { 428 | saveActive = true; 429 | } 430 | old_Butt = _Butt; 431 | } 432 | 433 | if ( saveActive == true ) // save preset if any of the GESS buttons/triggers is active 434 | { 435 | for (ii = 0; ii < 8; ii++) 436 | { 437 | if (_G[ii] == LOW) 438 | { 439 | savePreset(ii); 440 | saveActive = false; 441 | loadActive = true; // when the button is released it will become false ;-) 442 | digitalWrite(Led_, HIGH); 443 | } 444 | } 445 | } 446 | 447 | if (( loadActive == true ) && ( _Butt == HIGH )) 448 | { 449 | for (ii = 0; ii < 8; ii++) 450 | { 451 | if (_G[ii] == LOW) 452 | { 453 | loadPreset(ii); 454 | loadActive = false; 455 | digitalWrite(Led_, LOW); 456 | } 457 | } 458 | } 459 | // end of the preset management 460 | #endif 461 | 462 | 463 | } // end of doMidiWrite 464 | 465 | #ifdef MASTER 466 | 467 | /* 468 | Sends an i2c command out to a slave when running in master mode 469 | */ 470 | void sendi2c(uint8_t model, uint8_t deviceIndex, uint8_t cmd, uint8_t devicePort, int value) 471 | { 472 | 473 | valueTemp = (uint16_t)value; 474 | messageBuffer[2] = valueTemp >> 8; 475 | messageBuffer[3] = valueTemp & 0xff; 476 | 477 | #ifdef V125 478 | Wire1.beginTransmission(model + deviceIndex); 479 | messageBuffer[0] = cmd; 480 | messageBuffer[1] = (uint8_t)devicePort; 481 | Wire1.write(messageBuffer, 4); 482 | Wire1.endTransmission(); 483 | #else 484 | Wire.beginTransmission(model + deviceIndex); 485 | messageBuffer[0] = cmd; 486 | messageBuffer[1] = (uint8_t)devicePort; 487 | Wire.write(messageBuffer, 4); 488 | Wire.endTransmission(); 489 | #endif 490 | } 491 | 492 | #else 493 | 494 | /* 495 | The function that responds to a command from i2c. 496 | In the first version, this simply sets the port to be read from. 497 | */ 498 | void i2cWrite(size_t len) 499 | { 500 | 501 | #ifdef DEBUG 502 | Serial.printf("i2c Write (%d)\n", len); 503 | #endif 504 | 505 | // parse the response 506 | TxResponse response = TxHelper::Parse(len); 507 | 508 | // true command our setting of the input for a read? 509 | if (len == 1) 510 | { 511 | 512 | // use a helper to decode the command 513 | TxIO io = TxHelper::DecodeIO(response.Command); 514 | 515 | #ifdef DEBUG 516 | Serial.printf("Port: %d; Mode: %d [%d]\n", io.Port, io.Mode, response.Command); 517 | #endif 518 | 519 | // this is the single byte that sets the active input 520 | activeInput = io.Port; 521 | activeMode = io.Mode; 522 | } 523 | else 524 | { 525 | // act on the command 526 | actOnCommand(response.Command, response.Output, response.Value); 527 | } 528 | } 529 | 530 | /* 531 | The function that responds to read requests over i2c. 532 | This uses the port from the write request to determine which slider to send. 533 | */ 534 | void i2cReadRequest() 535 | { 536 | 537 | #ifdef DEBUG 538 | Serial.print("i2c Read\n"); 539 | #endif 540 | 541 | // get and cast the value 542 | uint16_t shiftReady = 0; 543 | switch (activeMode) 544 | { 545 | case 1: 546 | shiftReady = (uint16_t)currentValue[activeInput]; 547 | break; 548 | case 2: 549 | shiftReady = (uint16_t)currentValue[activeInput]; 550 | break; 551 | default: 552 | shiftReady = (uint16_t)currentValue[activeInput]; 553 | break; 554 | } 555 | 556 | #ifdef DEBUG 557 | Serial.printf("delivering: %d; value: %d [%d]\n", activeInput, currentValue[activeInput], shiftReady); 558 | #endif 559 | 560 | // send the puppy as a pair of bytes 561 | #ifdef V125 562 | Wire1.write(shiftReady >> 8); 563 | Wire1.write(shiftReady & 255); 564 | #else 565 | Wire.write(shiftReady >> 8); 566 | Wire.write(shiftReady & 255); 567 | #endif 568 | } 569 | 570 | /* 571 | Future function if we add more i2c capabilities beyond reading values. 572 | */ 573 | void actOnCommand(byte cmd, byte out, int value) {} 574 | 575 | #endif 576 | 577 | #ifdef GESS 578 | void loadPreset(int nPreset) //read eeprom 579 | { 580 | nAddress = ( nPreset * 16 ); 581 | for ( ii = 0; ii < 8; ii++ ) //gates 582 | { 583 | _nNote[ii] = EEPROM.read(nAddress); 584 | if (_nNote[ii] == 0) 585 | { 586 | nNote[ii] = true; 587 | } 588 | else 589 | { 590 | nNote[ii] = false; 591 | } 592 | nAddress++ ; 593 | _nVelocity[ii] = EEPROM.read(nAddress); 594 | if (_nVelocity[ii] == 0) 595 | { 596 | nVelocity[ii] = true; 597 | } 598 | else 599 | { 600 | nVelocity[ii] = false; 601 | } 602 | nAddress++ ; 603 | // _nChannel[ii] = EEPROM.read(nAddress); 604 | // nAddress++ ; 605 | } 606 | } 607 | void savePreset(int nPreset) 608 | { 609 | nAddress = ( nPreset * 16 ); 610 | for ( ii = 0; ii < 8; ii++ ) //gates 611 | { 612 | 613 | if (lastMidiValue[ii] == 0) 614 | { 615 | nNote[ii] = true; 616 | _nNote[ii] = 0; 617 | } 618 | else 619 | { 620 | nNote[ii] = false; 621 | } 622 | 623 | EEPROM.write(nAddress, _nNote[ii]); 624 | nAddress++; 625 | 626 | if (lastMidiValue[ii + 8] == 0) 627 | { 628 | nVelocity[ii] = true; 629 | _nVelocity[ii] = 0; 630 | } 631 | else 632 | { 633 | nVelocity[ii] = false; 634 | } 635 | 636 | EEPROM.write(nAddress, _nVelocity[ii]); 637 | nAddress++; 638 | // midi channel setting are not saved because they're preassigned 639 | // EEPROM.write(nAddress, _nChannel[ii]); 640 | // nAddress++; 641 | } 642 | } 643 | #endif 644 | -------------------------------------------------------------------------------- /firmware/Sweet_3/Sweet_3.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 16n Faderbank Firmware 3 | * (c) 2017,2018,2020 by Brian Crabtree, Sean Hellfritsch, Tom Armitage, and Brendon Cassidy 4 | * MIT License 5 | */ 6 | 7 | /* 8 | * NOTES: 9 | * - Hardware MIDI is on pin 1 10 | * - You **must** also compile this with Tools->USB type set to MIDI or MIDI/Serial (for debugging) 11 | * - You also should overclock to 120MHz to make it as snappy as possible 12 | */ 13 | 14 | /* 15 | * Most configuration now hpapens via online editor. 16 | * config.h is mainly for developer configuration. 17 | */ 18 | // modified version for the Tesseract Modular Sweet Sixteen by Mangu Díaz 19 | // changes from the original 16n 2.0.1 firmware from Tom Armitage: 20 | // - calibration of the max fader value is now possible when 'rotated' is active 21 | // - channel numbers are not inverted when 'rotate' is active 22 | // - added support for GESS (it makes midi notes with gates) 23 | // - added pitch bend option (when midiCC 127 is selected) 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "config.h" 32 | #include "TxHelper.h" 33 | 34 | // wrap code to be executed only under DEBUG conditions in D() 35 | #ifdef DEBUG 36 | #define D(x) x 37 | #else 38 | #define D(x) 39 | #endif 40 | 41 | #define TO_TR 0x00 42 | #define TO_TR_TOG 0x01 43 | #define TO_TR_TIME 0x02 44 | #define TO_TR_TIME_S 0x03 45 | #define TO_TR_TIME_M 0x04 46 | #define TO_TR_PULSE 0x05 47 | #define TO_TR_POL 0x06 48 | 49 | #define TO_CV 0x10 50 | #define TO_CV_SET 0x11 51 | #define TO_CV_SLEW 0x12 52 | #define TO_CV_SLEW_S 0x13 53 | #define TO_CV_SLEW_M 0x14 54 | #define TO_CV_OFF 0x15 55 | 56 | #ifdef GESS // only necessary for GESS & midi note thing 57 | // panel led and function button pins: 58 | const int Led_ = 12; 59 | const int Butt = 15; 60 | // variables for the functon button 61 | bool _Butt ; 62 | bool old_Butt = HIGH; 63 | 64 | // gate in 65 | const int G[8] = { 3, 0, 22, 20, 21, 23, 4, 2 }; 66 | // variables for the gate inputs 67 | bool volatile _G[8] ; // state of the gate input 68 | bool old_G[8]; 69 | bool nNote[8]; 70 | bool nVelocity[8]; 71 | // loop helpers 72 | int ii, k; 73 | // preset management 74 | int nAddress = 100; // memory address for GESS presets 75 | int nPreset; 76 | bool loadActive = false; 77 | bool saveActive = false; 78 | #endif 79 | 80 | MIDI_CREATE_DEFAULT_INSTANCE(); 81 | 82 | // loop helpers 83 | int i, temp; 84 | 85 | // midi write helpers 86 | int q, shiftyTemp, notShiftyTemp, lastMidiActivityAt; 87 | int midiDirty = 0; 88 | const int midiFlashDuration = 50; 89 | int ledPin = 13; 90 | 91 | // the storage of the values; current is in the main loop; last value is for midi output 92 | int volatile currentValue[channelCount]; 93 | int lastMidiValue[channelCount]; 94 | 95 | // variables to hold configuration 96 | int usbChannels[channelCount]; 97 | int trsChannels[channelCount]; 98 | int usbCCs[channelCount]; 99 | int trsCCs[channelCount]; 100 | int legacyPorts[channelCount]; // for V125 only 101 | int flip; 102 | int ledOn; 103 | int ledFlash; 104 | int i2cMaster; 105 | 106 | int faderMin; 107 | int faderMax; 108 | 109 | // variables for i2c master mode 110 | // memory of the last unshifted value 111 | int lastValue[channelCount]; 112 | 113 | // the i2c message buffer we are sending 114 | uint8_t messageBuffer[4]; 115 | 116 | // temporary values 117 | uint16_t valueTemp; 118 | uint8_t device = 0; 119 | uint8_t port = 0; 120 | 121 | // master i2c specific stuff 122 | const int ansibleI2Caddress = 0x20; 123 | const int er301I2Caddress = 0x31; 124 | const int txoI2Caddress = 0x60; 125 | bool er301Present = false; 126 | bool ansiblePresent = false; 127 | bool txoPresent = false; 128 | 129 | 130 | // the thing that smartly smooths the input 131 | ResponsiveAnalogRead *analog[channelCount]; 132 | 133 | // mux config 134 | CD74HC4067 mux(8, 7, 6, 5); 135 | const int muxMapping[16] = {0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8}; 136 | 137 | // MIDI timers 138 | IntervalTimer midiWriteTimer; 139 | IntervalTimer midiReadTimer; 140 | int midiInterval = 1000; // 1ms 141 | bool shouldDoMidiRead = false; 142 | bool shouldDoMidiWrite = false; 143 | bool forceMidiWrite = false; 144 | 145 | // helper values for i2c reading and future expansion 146 | int activeInput = 0; 147 | int activeMode = 0; 148 | 149 | /* 150 | * The function that sets up the application 151 | */ 152 | void setup() 153 | { 154 | 155 | D(Serial.println("16n Firmware Debug Mode")); 156 | 157 | #ifdef BOOTDELAY // wait some time before continuing boot-up (default is none) 158 | delay(BOOTDELAY); 159 | #endif 160 | 161 | #ifdef GESS 162 | for (i = 0; i < 8; i++) 163 | { 164 | pinMode (G[i], INPUT_PULLUP); 165 | old_G[i] = HIGH; 166 | // default GESS settings does not read velocity and note from faders: 167 | nNote[i] = false; 168 | nVelocity[i] = false; 169 | } 170 | pinMode (Butt, INPUT_PULLUP); 171 | pinMode (Led_, OUTPUT); 172 | // boot up pattern 173 | for ( i = 0; i < 3; i++) 174 | { 175 | digitalWrite(Led_, HIGH); 176 | delay(100); 177 | digitalWrite(Led_, LOW); 178 | delay(100); 179 | } 180 | #endif 181 | 182 | checkDefaultSettings(); 183 | 184 | loadSettingsFromEEPROM(); 185 | i2cMaster = EEPROM.read(3) == 1; 186 | 187 | usbMIDI.setHandleSystemExclusive(processIncomingSysex); 188 | 189 | #ifdef V125 190 | // analog ports on the Teensy for the 1.25 board. 191 | if(flip) { 192 | int portsToAssign[] = {A15, A14, A13, A12, A11, A10, A9, A8, A7, A6, A5, A4, A3, A2, A1, A0}; 193 | for(int i=0; i < channelCount; i++) { 194 | legacyPorts[i] = portsToAssign[i]; 195 | } 196 | } else { 197 | int portsToAssign[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15}; 198 | for(int i=0; i < channelCount; i++) { 199 | legacyPorts[i] = portsToAssign[i]; 200 | } 201 | } 202 | #endif 203 | 204 | // initialize the TX Helper 205 | #ifdef V125 206 | TxHelper::UseWire1(true); 207 | #else 208 | TxHelper::UseWire1(false); 209 | #endif 210 | TxHelper::SetPorts(16); 211 | TxHelper::SetModes(4); 212 | 213 | // set read resolution to teensy's 13 usable bits 214 | analogReadResolution(13); 215 | 216 | // initialize the value storage 217 | for (i = 0; i < channelCount; i++) 218 | { 219 | // analog[i] = new ResponsiveAnalogRead(0, false); 220 | 221 | analog[i] = new ResponsiveAnalogRead(0, true, .0001); 222 | analog[i]->setAnalogResolution(1 << 13); 223 | 224 | // ResponsiveAnalogRead is designed for 10-bit ADCs 225 | // meanining its threshold defaults to 4. Let's bump that for 226 | // our 13-bit adc by setting it to 4 << (13-10) 227 | analog[i]->setActivityThreshold(32); 228 | 229 | currentValue[i] = 0; 230 | lastMidiValue[i] = 0; 231 | 232 | if(i2cMaster) { 233 | lastValue[i] = 0; 234 | } 235 | } 236 | 237 | // i2c using the default I2C pins on a Teensy 3.2 238 | if(i2cMaster) { 239 | 240 | D(Serial.println("Enabling i2c in MASTER mode")); 241 | 242 | #ifdef V125 243 | Wire1.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 244 | Wire1.setDefaultTimeout(10000); // 10ms 245 | 246 | D(Serial.println ("Scanning I2C bus")); 247 | 248 | Wire1.begin(); 249 | 250 | for (byte i = 8; i < 120; i++) 251 | { 252 | Wire1.beginTransmission (i); 253 | if (Wire1.endTransmission () == 0) 254 | { 255 | if(i == ansibleI2Caddress) { 256 | ansiblePresent = true; 257 | D(Serial.println ("Found ansible")); 258 | } 259 | 260 | if(i == txoI2Caddress) { 261 | txoPresent = true; 262 | D(Serial.println ("Found TXO")); 263 | } 264 | 265 | if(i == er301I2Caddress) { 266 | er301Present = true; 267 | D(Serial.println ("Found ER301")); 268 | } 269 | delay (1); // maybe unneeded? 270 | } // end of good response 271 | } // end of for loop 272 | 273 | D(Serial.println ("I2C scan complete.")); 274 | 275 | #else 276 | Wire.begin(I2C_MASTER, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 277 | Wire.setDefaultTimeout(10000); // 10ms 278 | 279 | D(Serial.println ("Scanning I2C bus")); 280 | 281 | Wire.begin(); 282 | 283 | for (byte i = 8; i < 120; i++) 284 | { 285 | Wire.beginTransmission (i); 286 | if (Wire.endTransmission () == 0) 287 | { 288 | if(i == ansibleI2Caddress) { 289 | ansiblePresent = true; 290 | D(Serial.println ("Found ansible")); 291 | } 292 | 293 | if(i == txoI2Caddress) { 294 | txoPresent = true; 295 | D(Serial.println ("Found TXO")); 296 | } 297 | 298 | if(i == er301I2Caddress) { 299 | er301Present = true; 300 | D(Serial.println ("Found ER301")); 301 | } 302 | delay (1); // maybe unneeded? 303 | } // end of good response 304 | } // end of for loop 305 | 306 | Serial.println ("I2C scan complete."); 307 | 308 | #endif 309 | 310 | } else { 311 | // non-master mode 312 | 313 | D(Serial.println("Enabling i2c enabled in SLAVE mode")); 314 | 315 | #ifdef V125 316 | Wire1.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_29_30, I2C_PULLUP_EXT, 400000); 317 | Wire1.onReceive(i2cWrite); 318 | Wire1.onRequest(i2cReadRequest); 319 | #else 320 | Wire.begin(I2C_SLAVE, I2C_ADDRESS, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000); 321 | Wire.onReceive(i2cWrite); 322 | Wire.onRequest(i2cReadRequest); 323 | #endif 324 | 325 | } 326 | 327 | // turn on the MIDI party 328 | MIDI.begin(); 329 | midiWriteTimer.begin(writeMidi, midiInterval); 330 | midiReadTimer.begin(readMidi, midiInterval); 331 | 332 | pinMode(ledPin, OUTPUT); 333 | 334 | if(ledOn) { 335 | digitalWrite(ledPin, HIGH); 336 | } 337 | } 338 | 339 | /* 340 | * The main read loop that goes through all of the sliders 341 | */ 342 | void loop() 343 | { 344 | // this whole chunk makes the LED flicker on MIDI activity - 345 | // and inverts that flicker if the power light is on. 346 | if (ledFlash) { 347 | if (millis() > (lastMidiActivityAt + midiFlashDuration)) { 348 | if(ledOn) { 349 | digitalWrite(ledPin, HIGH); 350 | } else { 351 | digitalWrite(ledPin, LOW); 352 | } 353 | midiDirty = 0; 354 | } else { 355 | if(ledOn) { 356 | digitalWrite(ledPin, LOW); 357 | } else { 358 | digitalWrite(ledPin, HIGH); 359 | } 360 | } 361 | } else { 362 | if(ledOn) { 363 | digitalWrite(ledPin, HIGH); 364 | } else { 365 | digitalWrite(ledPin, LOW); 366 | } 367 | } 368 | 369 | // read loop using the i counter 370 | for (i = 0; i < channelCount; i++) 371 | { 372 | #ifdef V125 373 | temp = analogRead(legacyPorts[i]); // mux goes into A0 374 | #else 375 | // set mux to appropriate channel 376 | // if(flip) { 377 | // mux.channel(muxMapping[channelCount - i - 1]); 378 | // } else { 379 | mux.channel(muxMapping[i]); 380 | // } 381 | 382 | // read the value 383 | temp = analogRead(0); // mux goes into A0 384 | #endif 385 | 386 | // put the value into the smoother 387 | analog[i]->update(temp); 388 | 389 | // read from the smoother, constrain (to account for tolerances), and map it 390 | temp = analog[i]->getValue(); 391 | 392 | if(flip){ 393 | temp = 8191 - temp; // temp = faderMax - temp; 394 | } 395 | 396 | temp = constrain(temp, faderMin, faderMax); 397 | 398 | temp = map(temp, faderMin, faderMax, 0, 16383); 399 | 400 | // map and update the value 401 | currentValue[i] = temp; 402 | 403 | #ifdef GESS 404 | // read the gate inputs 405 | if (i < 8) 406 | { 407 | switch(i) 408 | { 409 | case 0: 410 | _G[i] = digitalReadFast(3); 411 | break; 412 | case 1: 413 | _G[i] = digitalReadFast(0); 414 | break; 415 | case 2: 416 | _G[i] = digitalReadFast(22); 417 | break; 418 | case 3: 419 | _G[i] = digitalReadFast(20); 420 | break; 421 | case 4: 422 | _G[i] = digitalReadFast(21); 423 | break; 424 | case 5: 425 | _G[i] = digitalReadFast(23); 426 | break; 427 | case 6: 428 | _G[i] = digitalReadFast(4); 429 | break; 430 | case 7: 431 | _G[i] = digitalReadFast(2); 432 | break; 433 | } 434 | //_G[i] = digitalRead(G[i]); 435 | //const int G[8] = { 3, 0, 22, 20, 21, 23, 4, 2 }; 436 | } 437 | #endif 438 | } 439 | 440 | if (shouldDoMidiRead) 441 | { 442 | doMidiRead(); 443 | noInterrupts(); 444 | shouldDoMidiRead = false; 445 | interrupts(); 446 | } 447 | 448 | if (shouldDoMidiWrite) 449 | { 450 | doMidiWrite(); 451 | noInterrupts(); 452 | shouldDoMidiWrite = false; 453 | interrupts(); 454 | } 455 | } 456 | 457 | /* 458 | * Tiny function called via interrupt 459 | * (it's important to catch inbound MIDI messages even if we do nothing with 460 | * them.) 461 | */ 462 | void readMidi() 463 | { 464 | shouldDoMidiRead = true; 465 | } 466 | 467 | /* 468 | * Function called when shouldDoMidiRead flag is HIGH 469 | */ 470 | 471 | void doMidiRead() 472 | { 473 | MIDI.read(); 474 | usbMIDI.read(); 475 | } 476 | 477 | /* 478 | * Tiny function called via interrupt 479 | */ 480 | void writeMidi() 481 | { 482 | shouldDoMidiWrite = true; 483 | } 484 | 485 | /* 486 | * The function that writes changes in slider positions out the midi ports 487 | * Called when shouldDoMidiWrite flag is HIGH 488 | */ 489 | void doMidiWrite() 490 | { 491 | // write loop using the q counter ( 492 | // (can't use i or temp cuz this might interrupt the reads) 493 | for (q = 0; q < channelCount; q++) 494 | { 495 | notShiftyTemp = currentValue[q]; 496 | 497 | // shift for MIDI precision (0-127) 498 | shiftyTemp = notShiftyTemp >> 7; 499 | 500 | // if there was a change in the midi value 501 | // if ((shiftyTemp != lastMidiValue[q]) || forceMidiWrite) 502 | if (( shiftyTemp != lastMidiValue[q]) || ((( usbCCs[q] == 127 ) || ( trsCCs[q] == 127 )) && ( notShiftyTemp != lastValue[q] )) || forceMidiWrite ) 503 | { 504 | if(ledFlash && !midiDirty) { 505 | lastMidiActivityAt = millis(); 506 | midiDirty = 1; 507 | } 508 | 509 | // only if GESS is not using the faders we send midiCC etc... 510 | #ifdef GESS 511 | if ((( q < 8 ) && (nNote[q] == false)) || (( q > 7 ) && (nVelocity[q - 8] == false))) 512 | { 513 | #endif 514 | 515 | #ifdef PITCHBEND 516 | if ( usbCCs[q] == 127 ) 517 | { 518 | usbMIDI.sendPitchBend((notShiftyTemp - 8192), usbChannels[q]); 519 | } 520 | else 521 | #endif 522 | { 523 | // send the message over USB and physical MIDI 524 | usbMIDI.sendControlChange(usbCCs[q], shiftyTemp, usbChannels[q]); 525 | } 526 | #ifdef PITCHBEND 527 | if ( trsCCs[q] == 127 ) 528 | { 529 | MIDI.sendPitchBend((notShiftyTemp - 8192), trsChannels[q]); 530 | } 531 | else 532 | #endif 533 | { 534 | // send the message over physical MIDI 535 | MIDI.sendControlChange(trsCCs[q], shiftyTemp, trsChannels[q]); 536 | } 537 | 538 | #ifdef GESS 539 | } 540 | #endif 541 | 542 | // store the shifted value for future comparison 543 | lastMidiValue[q] = shiftyTemp; 544 | 545 | // D(Serial.printf("MIDI[%d]: %d\n", q, shiftyTemp)); 546 | } 547 | 548 | #ifdef GESS 549 | // Midi Note 550 | if ((q > 7) && (_G[q - 8] != old_G[q - 8])) // only if the gate input has changed we do this: 551 | { 552 | k = ( q - 8) ; 553 | if ( _G[k] == LOW ) //note ON 554 | { 555 | if (nNote[k] == true) // taking the note value from the upper row of faders instead of a fixed value 556 | { 557 | _nNote[k] = lastMidiValue[k]; 558 | } 559 | if (nVelocity[k] == true) // taking the midi velocity from the lower row instead instead of a fixed value 560 | { 561 | _nVelocity[k] = lastMidiValue[q]; 562 | } 563 | MIDI.sendNoteOn(_nNote[k], _nVelocity[k], _nChannel[k] ); // at the moment channels are 1 to 8 preassigned 564 | usbMIDI.sendNoteOn(_nNote[k], _nVelocity[k], _nChannel[k] ); 565 | // ER-301 566 | if(er301Present) { 567 | // https://github.com/odevices/er-301/blob/develop/mods/teletype/Dispatcher.cpp 568 | // SC.TR 1-n α --> Set TR value to α (0/1) 569 | // commandTRSet(delay, getPort(msg), getValue(msg)); 570 | sendi2c(er301I2Caddress, 0, TO_TR, q-8, 1); 571 | } 572 | 573 | } 574 | else //Note Off 575 | { 576 | MIDI.sendNoteOff(_nNote[k], _nVelocity[k], _nChannel[k] ); 577 | usbMIDI.sendNoteOff(_nNote[k], _nVelocity[k], _nChannel[k] ); 578 | if(er301Present) { 579 | sendi2c(er301I2Caddress, 0, TO_TR, q-8, 0); 580 | } 581 | 582 | } 583 | old_G[k] = _G[k]; 584 | } //end of midi note code 585 | #endif 586 | 587 | if(i2cMaster) { 588 | 589 | // we send out to all three supported i2c slave devices 590 | // keeps the firmware simple :) 591 | 592 | if (notShiftyTemp != lastValue[q]) 593 | { 594 | D(Serial.printf("i2c Master[%d]: %d\n", q, notShiftyTemp)); 595 | 596 | // for 4 output devices 597 | port = q % 4; 598 | device = q / 4; 599 | 600 | // TXo 601 | if(txoPresent) { 602 | sendi2c(txoI2Caddress, device, 0x11, port, notShiftyTemp); 603 | } 604 | 605 | // ER-301 606 | if(er301Present) { 607 | sendi2c(er301I2Caddress, 0, TO_CV_SET, q, notShiftyTemp); 608 | } 609 | 610 | // ANSIBLE 611 | if(ansiblePresent) { 612 | sendi2c(0x20, device << 1, 0x06, port, notShiftyTemp); 613 | } 614 | 615 | lastValue[q] = notShiftyTemp; 616 | } 617 | } 618 | } 619 | forceMidiWrite = false; 620 | 621 | #ifdef GESS 622 | // button read to load & save presets etc: 623 | _Butt = digitalReadFast(Butt); 624 | 625 | if (_Butt != old_Butt) // change in the button 626 | { 627 | if (_Butt == true ) 628 | { 629 | // button depressed, switch the load preset mode 630 | saveActive = false; 631 | loadActive = !loadActive; 632 | digitalWrite(Led_, loadActive); 633 | } 634 | else 635 | { 636 | saveActive = true; 637 | } 638 | old_Butt = _Butt; 639 | } 640 | 641 | if ( saveActive == true ) // save preset if any of the GESS buttons/triggers is active 642 | { 643 | for (ii = 0; ii < 8; ii++) 644 | { 645 | if (_G[ii] == LOW) 646 | { 647 | savePreset(ii); 648 | saveActive = false; 649 | loadActive = true; // when the button is released it will become false ;-) 650 | digitalWrite(Led_, HIGH); 651 | } 652 | } 653 | } 654 | 655 | if (( loadActive == true ) && ( _Butt == HIGH )) 656 | { 657 | for (ii = 0; ii < 8; ii++) 658 | { 659 | if (_G[ii] == LOW) 660 | { 661 | loadPreset(ii); 662 | loadActive = false; 663 | digitalWrite(Led_, LOW); 664 | } 665 | } 666 | }// end of the preset management 667 | 668 | #endif 669 | } // end of doMidiWrite 670 | 671 | /* 672 | * Sends an i2c command out to a slave when running in master mode 673 | */ 674 | void sendi2c(uint8_t model, uint8_t deviceIndex, uint8_t cmd, uint8_t devicePort, int value) 675 | { 676 | 677 | valueTemp = (uint16_t)value; 678 | messageBuffer[2] = valueTemp >> 8; 679 | messageBuffer[3] = valueTemp & 0xff; 680 | 681 | #ifdef V125 682 | Wire1.beginTransmission(model + deviceIndex); 683 | messageBuffer[0] = cmd; 684 | messageBuffer[1] = (uint8_t)devicePort; 685 | Wire1.write(messageBuffer, 4); 686 | Wire1.endTransmission(); 687 | #else 688 | Wire.beginTransmission(model + deviceIndex); 689 | messageBuffer[0] = cmd; 690 | messageBuffer[1] = (uint8_t)devicePort; 691 | Wire.write(messageBuffer, 4); 692 | Wire.endTransmission(); 693 | #endif 694 | } 695 | 696 | /* 697 | * The function that responds to a command from i2c. 698 | * In the first version, this simply sets the port to be read from. 699 | */ 700 | void i2cWrite(size_t len) 701 | { 702 | 703 | D(Serial.printf("i2c Write (%d)\n", len)); 704 | 705 | // parse the response 706 | TxResponse response = TxHelper::Parse(len); 707 | 708 | // true command our setting of the input for a read? 709 | if (len == 1) 710 | { 711 | 712 | // use a helper to decode the command 713 | TxIO io = TxHelper::DecodeIO(response.Command); 714 | 715 | D(Serial.printf("Port: %d; Mode: %d [%d]\n", io.Port, io.Mode, response.Command)); 716 | 717 | // this is the single byte that sets the active input 718 | activeInput = io.Port; 719 | activeMode = io.Mode; 720 | } 721 | else 722 | { 723 | // act on the command 724 | actOnCommand(response.Command, response.Output, response.Value); 725 | } 726 | } 727 | 728 | /* 729 | * The function that responds to read requests over i2c. 730 | * This uses the port from the write request to determine which slider to send. 731 | */ 732 | void i2cReadRequest() 733 | { 734 | 735 | D(Serial.print("i2c Read\n")); 736 | 737 | // get and cast the value 738 | uint16_t shiftReady = 0; 739 | switch (activeMode) 740 | { 741 | case 1: 742 | shiftReady = (uint16_t)currentValue[activeInput]; 743 | break; 744 | case 2: 745 | shiftReady = (uint16_t)currentValue[activeInput]; 746 | break; 747 | default: 748 | shiftReady = (uint16_t)currentValue[activeInput]; 749 | break; 750 | } 751 | 752 | D(Serial.printf("delivering: %d; value: %d [%d]\n", activeInput, currentValue[activeInput], shiftReady)); 753 | 754 | // send the puppy as a pair of bytes 755 | #ifdef V125 756 | Wire1.write(shiftReady >> 8); 757 | Wire1.write(shiftReady & 255); 758 | #else 759 | Wire.write(shiftReady >> 8); 760 | Wire.write(shiftReady & 255); 761 | #endif 762 | } 763 | 764 | /* 765 | * Future function if we add more i2c capabilities beyond reading values. 766 | */ 767 | void actOnCommand(byte cmd, byte out, int value) {} 768 | 769 | #ifdef GESS 770 | void loadPreset(int nPreset) //read eeprom 771 | { 772 | nAddress = 100 + ( nPreset * 16 ); 773 | for ( ii = 0; ii < 8; ii++ ) //gates 774 | { 775 | _nNote[ii] = EEPROM.read(nAddress); 776 | if (_nNote[ii] == 0) 777 | { 778 | nNote[ii] = true; 779 | } 780 | else 781 | { 782 | nNote[ii] = false; 783 | } 784 | nAddress++ ; 785 | _nVelocity[ii] = EEPROM.read(nAddress); 786 | if (_nVelocity[ii] == 0) 787 | { 788 | nVelocity[ii] = true; 789 | } 790 | else 791 | { 792 | nVelocity[ii] = false; 793 | } 794 | nAddress++ ; 795 | // _nChannel[ii] = EEPROM.read(nAddress); 796 | // nAddress++ ; 797 | } 798 | } 799 | void savePreset(int nPreset) 800 | { 801 | nAddress = 100 + ( nPreset * 16 ); 802 | for ( ii = 0; ii < 8; ii++ ) //gates 803 | { 804 | 805 | if (lastMidiValue[ii] == 0) 806 | { 807 | nNote[ii] = true; 808 | _nNote[ii] = 0; 809 | } 810 | else 811 | { 812 | nNote[ii] = false; 813 | } 814 | 815 | EEPROM.write(nAddress, _nNote[ii]); 816 | nAddress++; 817 | 818 | if (lastMidiValue[ii + 8] == 0) 819 | { 820 | nVelocity[ii] = true; 821 | _nVelocity[ii] = 0; 822 | } 823 | else 824 | { 825 | nVelocity[ii] = false; 826 | } 827 | 828 | EEPROM.write(nAddress, _nVelocity[ii]); 829 | nAddress++; 830 | // midi channel setting are not saved because they're preassigned 831 | // EEPROM.write(nAddress, _nChannel[ii]); 832 | // nAddress++; 833 | } 834 | } 835 | #endif 836 | --------------------------------------------------------------------------------