diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0f0e53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +.DS_Store diff --git a/examples/wvr_basic/wvr_basic.ino b/examples/wvr_basic/wvr_basic.ino new file mode 100644 index 0000000..bf180d4 --- /dev/null +++ b/examples/wvr_basic/wvr_basic.ino @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +WVR wvr; +Button btn1(D2, FALLING, 300); + +bool serverOn = true; + +void on1(void) +{ + // wvr.play(0,40,127); + if(serverOn) + { + wvr.serverPause(); + } + else + { + wvr.serverResume(); + } + serverOn = !serverOn; +} + +void setup() { + // put your setup code here, to run once: + wvr.begin(); + pinMode(D2, INPUT_PULLUP); + btn1.onPress(on1); +} + +void loop() { + // put your main code here, to run repeatedly: + vTaskDelay(portMAX_DELAY); +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..116ac70 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,32 @@ +# Syntax Coloring Map For ExampleLibrary + +# Datatypes (KEYWORD1) + +# Methods and Functions (KEYWORD2) +onPress KEYWORD2 +onRelease KEYWORD2 +play KEYWORD2 +serverPause KEYWORD2 +serverResume KEYWORD2 + +# Instances (KEYWORD2) +WVR KEYWORD2 +Button KEYWORD2 + +# Structures KEYWORD3 +wav_player_event_t KEYWORD3 + +# Constants (LITERAL1) +D0 LITERAL1 +D1 LITERAL1 +D2 LITERAL1 +D3 LITERAL1 +D4 LITERAL1 +D5 LITERAL1 +D6 LITERAL1 +D7 LITERAL1 +D8 LITERAL1 +D9 LITERAL1 +D10 LITERAL1 +D11 LITERAL1 +D12 LITERAL1 \ No newline at end of file diff --git a/library.json b/library.json new file mode 100644 index 0000000..6b5a18f --- /dev/null +++ b/library.json @@ -0,0 +1,33 @@ +{ + "name":"WVR", + "description":"Firmware for WVR, the ESP32 Audio Board", + "keywords":"wvr,audio,esp32", + "authors": + { + "name": "Andrew March", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/marchingband/WVR.git" + }, + "version": "0.0.1", + "license": "LGPL-3.0", + "frameworks": "arduino", + "platforms": ["espressif32"], + "dependencies": [ + { + "name": "AsyncTCP", + "platforms": "espressif32" + }, + { + "name": "ESPAsyncWebServer", + "platforms": "espressif32" + }, + { + "name": "Adafruit_NeoPixel", + "platforms": "espressif32" + } + ] +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..dd12311 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=WVR +version=0.1.1 +author=Andrew March +maintainer=Andrew March +sentence=Firmware for WVR, the ESP32 Audio Board +paragraph=Firmware for WVR, the ESP32 Audio Board +category=Other +url=https://github.com/marchingband/WVR +architectures=* +depends=AsyncTCP,ESPAsyncWebServer,Adafruit_NeoPixel diff --git a/src/WVR.cpp b/src/WVR.cpp new file mode 100644 index 0000000..18659cc --- /dev/null +++ b/src/WVR.cpp @@ -0,0 +1,26 @@ +#include "Arduino.h" +#include "WVR.h" +#include "wvr_0.3.h" +#include "wav_player.h" +#include "server.h" + +WVR::WVR() +{ +} + +void WVR::begin() +{ + wvr_init(); +} +void WVR::play(uint8_t voice, uint8_t note, uint8_t velocity) +{ + play_wav(voice,note,velocity); +} +void WVR::serverPause() +{ + server_pause(); +} +void WVR::serverResume() +{ + server_resume(); +} \ No newline at end of file diff --git a/src/WVR.h b/src/WVR.h new file mode 100644 index 0000000..fcea8d3 --- /dev/null +++ b/src/WVR.h @@ -0,0 +1,20 @@ +/* + WVR.h - Library for building WVR firmware. + Created by Andrew March, May 20, 2021. + Released into the public domain. +*/ +#ifndef WVR_H +#define WVR_H + +#include "Arduino.h" + +class WVR { + public: + WVR(); + void begin(); + void play(uint8_t voice, uint8_t note, uint8_t velocity); + void serverPause(); + void serverResume(); +}; + +#endif \ No newline at end of file diff --git a/src/boot.cpp b/src/boot.cpp new file mode 100644 index 0000000..673b11d --- /dev/null +++ b/src/boot.cpp @@ -0,0 +1,138 @@ +#include +#include "esp_ota_ops.h" +#include "esp_image_format.h" +#include "driver/sdmmc_host.h" +#include "esp_system.h" +#include "file_system.h" +#include "wvr_pins.h" + +extern "C" esp_err_t emmc_read(void *dst, size_t start_sector, size_t sector_count); +extern "C" struct metadata_t *get_metadata(void); +extern "C" void write_metadata(struct metadata_t m); + +void bootFromEmmc(int index) +{ + firmware_t *firmware = get_firmware_slot(index); + log_i("booting firmware index %d, length %d, start_block %d", index, firmware->length, firmware->start_block); + bool ret = Update.begin(firmware->length); + if(!ret){ + log_e("not enough space in flash to boot this firmware"); + return; + } + size_t num_sectors = firmware->length / SECTOR_SIZE; + size_t remaining = firmware->length % SECTOR_SIZE; + size_t sector = firmware->start_block; + uint8_t *buf = (uint8_t *)ps_malloc(SECTOR_SIZE); + + log_i("starting update"); + for(int i=0;icurrent_firmware_index = index; + new_metadata->current_website_index = index; + write_metadata(*new_metadata); + sdmmc_host_deinit(); + feedLoopWDT(); + // delay(1000); + ESP.restart(); + } else { + log_e("Update.isFinished() : false"); + } + } else { + log_e("Update.end() : false"); + } + free(buf); +} + +void bootIntoRecoveryMode(void) +{ + int index = -1; + firmware_t *firmware = get_firmware_slot(index); + log_i("booting firmware index %d, length %d, start_block %d", index, firmware->length, firmware->start_block); + bool ret = Update.begin(firmware->length); + if(!ret){ + log_e("not enough space in flash to boot this firmware"); + return; + } + size_t num_sectors = firmware->length / SECTOR_SIZE; + size_t remaining = firmware->length % SECTOR_SIZE; + size_t sector = firmware->start_block; + uint8_t *buf = (uint8_t *)ps_malloc(SECTOR_SIZE); + + log_i("starting update"); + for(int i=0;icurrent_firmware_index = index; + write_metadata(*new_metadata); + // sdmmc_host_deinit(); + // feedLoopWDT(); + // delay(1000); + ESP.restart(); + } else { + log_e("Update.isFinished() : false"); + } + } else { + log_e("Update.end() : false"); + } + free(buf); +} + +int check_for_recovery_mode(void) +{ + metadata_t *new_metadata = get_metadata(); + gpio_reset_pin(gpio_pins[new_metadata->recovery_mode_straping_pin]); + pinMode(wvr_pins[new_metadata->recovery_mode_straping_pin], INPUT_PULLUP); + int res = digitalRead(wvr_pins[new_metadata->recovery_mode_straping_pin]); + log_i("strapping pin %d reads %d",new_metadata->recovery_mode_straping_pin,res); + // if(res == 0) // boot from recovery slot + // { + // log_i("booting in recovery mode!"); + // bootFromEmmc(-1); + // } + return res; +} \ No newline at end of file diff --git a/src/boot.h b/src/boot.h new file mode 100644 index 0000000..7392f51 --- /dev/null +++ b/src/boot.h @@ -0,0 +1,8 @@ +#ifndef BOOT_H +#define BOOT_H + +void bootFromEmmc(int index); +int check_for_recovery_mode(void); +void bootIntoRecoveryMode(void); + +#endif \ No newline at end of file diff --git a/src/button.cpp b/src/button.cpp new file mode 100644 index 0000000..6259646 --- /dev/null +++ b/src/button.cpp @@ -0,0 +1,75 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "Arduino.h" +#include "button.h" +#include "wvr_pins.h" +#include "ws_log.h" + +xQueueHandle gpio_queue_handle; +xTaskHandle gpio_task_handle; + +static void gpioTask(void* x) { + button_event_t *event; + for(;;) { + if(xQueueReceive(gpio_queue_handle, &event, portMAX_DELAY)) { + event->button->handleChange(event->val); + } + }; +} + +void IRAM_ATTR isr(void *e){ + button_event_t *event = (button_event_t*)e; + event->val = digitalRead(event->pin); + xQueueSendFromISR(gpio_queue_handle, &event, NULL); +} + +Button::Button(int pin, int mode, int dbnc){ + this->pin = pin; + this->mode = mode; + this->dbnc = dbnc; + this->last = 0; + event.pin = pin; + event.button = this; +} + +Button::~Button(){ + log_i("destructor button on %u",pin); + detachInterrupt(pin); +} + +void Button::onPress(void(*handlePress)()){ + this->handlePress = handlePress; + attachInterruptArg(pin, isr, (void*)&event, CHANGE); +} + +void Button::onRelease(void(*handleRelease)()){ + this->handleRelease=handleRelease; + attachInterruptArg(pin, isr, (void*)&event, CHANGE); +} + +void Button::handleChange(int val){ + // if EDGE_NONE then ignore + if(mode != RISING && mode != FALLING) return; + int now = millis(); + if((now - last) > dbnc){ + last = now; + if( + (val==0 && mode == FALLING) || + (val==1 && mode == RISING) + ){ + if(handlePress){ + handlePress(); + } + } else { + if(handleRelease){ + handleRelease(); + } + } + } +} + +void button_init(){ + gpio_queue_handle = xQueueCreate(10, sizeof(button_event_t)); + xTaskCreate(gpioTask, "gpio_task", 2048, NULL, 3, &gpio_task_handle); +} diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..7980d7c --- /dev/null +++ b/src/button.h @@ -0,0 +1,24 @@ +#ifndef BUTTON_H +#define BUTTON_H + +#include "button_struct.h" + +class Button { + public: + int pin; + int dbnc; + int last; + int mode; + void (*handlePress)(); + void (*handleRelease)(); + button_event_t event; + Button(int pin, int mode, int dbnc); + ~Button(); + void onPress(void(*handlePress)()); + void onRelease(void(*handlePress)()); + void handleChange(int val); +}; + +void button_init(); + +#endif \ No newline at end of file diff --git a/src/button_struct.h b/src/button_struct.h new file mode 100644 index 0000000..e25516c --- /dev/null +++ b/src/button_struct.h @@ -0,0 +1,16 @@ +// this exists because of circular deps in Button class + +#ifndef BUTTON_STRUCT_H +#define BUTTON_STRUCT_H + +// #include "button.h" + +class Button; + +typedef struct { + Button *button; + int val; + int pin; +} button_event_t; + +#endif \ No newline at end of file diff --git a/src/dac.c b/src/dac.c new file mode 100644 index 0000000..54f64ae --- /dev/null +++ b/src/dac.c @@ -0,0 +1,103 @@ +#include "esp_err.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp32-hal-log.h" +#include "driver/sdspi_host.h" +#include "soc/rtc_cntl_reg.h" +#include "freertos/FreeRTOS.h" +#include "driver/i2s.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +static const char* TAG = "dac"; + +static const int i2s_num = 0; +QueueHandle_t dac_queue_handle; +esp_err_t ret; + +esp_err_t dac_write(const void *src, size_t size, size_t *bytes_written) +{ + // ret = i2s_write((i2s_port_t)i2s_num, (const void *)src,(size_t)size, (size_t *)bytes_written, 0); + ret = i2s_write((i2s_port_t)i2s_num, (const void *)src,(size_t)size, (size_t *)bytes_written, portMAX_DELAY); + return(ret); +} + + +void dac_queue_handler_task(void* pvParameters) +{ + i2s_event_t *dac_queue=(i2s_event_t *)malloc(100*sizeof(i2s_event_t)); + for(;;) + { + if(xQueueReceive(dac_queue_handle, dac_queue, portMAX_DELAY)) + { + log_i("i2s event"); + if(dac_queue[0].type == I2S_EVENT_TX_DONE) + { + log_i("dma done"); + } + } + } + vTaskDelete(NULL); +} + +void dac_pause(void) +{ + i2s_stop(i2s_num); +} +void dac_resume(void) +{ + i2s_start(i2s_num); +} + + +void dac_init(void) +{ + static const i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = 44100, + .bits_per_sample = 16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + // .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL3, // high interrupt priority + // .dma_buf_count = 2, + .dma_buf_count = 2, + // 15! cnt:32 len:128 chunk:(4096 or 2048) + + // .dma_buf_len = 1024, + // .dma_buf_len = 512, + .dma_buf_len = 256, + // .dma_buf_len = 192, + // .dma_buf_len = 128, + .tx_desc_auto_clear = true, + .use_apll = false + }; + + static const i2s_pin_config_t pin_config = { + .bck_io_num = 26, + .ws_io_num = 25, + .data_out_num = 22, + .data_in_num = I2S_PIN_NO_CHANGE + }; + + if((dac_queue_handle = xQueueCreate(100, sizeof(i2s_event_t)))==pdFAIL) + { + log_e("failed to creat dac_queue"); + } + + // i2s_driver_install(i2s_num, &i2s_config, 100, &dac_queue_handle); //install and start i2s driver + i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + i2s_set_pin(i2s_num, &pin_config); + i2s_set_sample_rates(i2s_num, 44100); //set sample rates + + // if( xTaskCreate(&dac_queue_handler_task, "DAC_event_handler_task", 1024 * 4, NULL, 10, NULL) != pdPASS) + // { + // log_e("failed to create DAC queue handler task"); + // } + +} diff --git a/src/dev_board.cpp b/src/dev_board.cpp new file mode 100644 index 0000000..8a2ab81 --- /dev/null +++ b/src/dev_board.cpp @@ -0,0 +1,77 @@ +#include "Arduino.h" +#include "button.h" +#include "wvr_pins.h" +#include "ws_log.h" + +extern "C" void play_sound_ext(int i); +extern "C" void encoder_init(void); +void rgb_init(void); +extern "C" void pot_init(void); + +Button b1(D5,FALLING, 50); +Button b2(D6,FALLING, 50); +Button b3(D7,FALLING, 50); +Button s1(D3,FALLING, 50); +Button s2(D4,FALLING, 50); + +void logB1(){ + wlog_i("1"); + play_sound_ext(40); +} +void logB2(){ + wlog_i("2"); + play_sound_ext(41); +} +void logB3(){ + wlog_i("3"); +} +void logSw1Up(){ + wlog_i("1up"); + digitalWrite(D0,HIGH); + digitalWrite(D1,HIGH); + digitalWrite(D2,HIGH); +} +void logSw1Down(){ + wlog_i("1down"); + digitalWrite(D0,LOW); + digitalWrite(D1,LOW); + digitalWrite(D2,LOW); +} +void logSw2Up(){ + wlog_i("2up"); +} +void logSw2Down(){ + wlog_i("2down"); +} + +void dev_board_init(){ + + button_init(); + encoder_init(); + rgb_init(); + pot_init(); + // gpio_reset_pin(GPIO_NUM_3); + // gpio_reset_pin(GPIO_NUM_1); + + // pinMode(D0,OUTPUT); + // pinMode(D1,OUTPUT); + pinMode(D2,OUTPUT); + + // digitalWrite(D0,LOW); + // digitalWrite(D1,LOW); + digitalWrite(D2,LOW); + + pinMode(D3,INPUT_PULLUP); + pinMode(D4,INPUT_PULLUP); + pinMode(D5,INPUT_PULLUP); + pinMode(D6,INPUT_PULLUP); + pinMode(D7,INPUT_PULLUP); + + // b1.onPress(logB1); + // b2.onPress(logB2); + // b3.onPress(logB3); + // s1.onPress(logSw1Up); + // s1.onRelease(logSw1Down); + // s2.onPress(logSw2Up); + // s2.onRelease(logSw2Down); +} diff --git a/src/emmc.c b/src/emmc.c new file mode 100644 index 0000000..4f84797 --- /dev/null +++ b/src/emmc.c @@ -0,0 +1,151 @@ +#include "esp_err.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include +#include +#include +#include +#include "esp_err.h" +#include "esp32-hal-log.h" +#include "driver/sdmmc_host.h" +#include "driver/sdspi_host.h" +#include "sdmmc_cmd.h" +#include "soc/rtc_cntl_reg.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "ws_log.h" + +static const char* TAG = "emmc"; + +esp_err_t ret; +sdmmc_card_t card; +char file_buf[512]; +int position = 0; +char *p = file_buf; +size_t current_block = 0; + +// utility functions for other files to import +esp_err_t emmc_write(const void *source, size_t block, size_t size) +{ +// log_i("%u",block); + ret = sdmmc_write_sectors(&card, (const void *)source, (size_t)block, (size_t)size); + // ret = sdmmc_write_sectors(&card, (char *)source, (size_t)block, (size_t)size); + return(ret); +} + +esp_err_t emmc_read(void *dst, size_t start_sector, size_t sector_count) +{ + ret = sdmmc_read_sectors(&card, (void *)dst, (size_t)start_sector, (size_t)sector_count); + return(ret); +} + + +void emmc_init(void) +{ + wlog_i("********************"); + wlog_i("hello from emmc"); + wlog_i("*********************"); + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + // slot_config.width = 4; +// slot_config.width = 1; + host.flags = SDMMC_HOST_FLAG_4BIT; +// host.flags = SDMMC_HOST_FLAG_1BIT; + + host.max_freq_khz = SDMMC_FREQ_52M; + // host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + // host.max_freq_khz = 80000; + // host.max_freq_khz = SDMMC_FREQ_DEFAULT; + // host.max_freq_khz = SDMMC_FREQ_PROBING; + + ret = sdmmc_host_set_bus_ddr_mode(SDMMC_HOST_SLOT_1, true); + if(ret != ESP_OK){ + log_i( "sdmmc_host_init : %s", esp_err_to_name(ret)); + } + gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes + gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes + gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only + gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only + gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes + gpio_pullup_en(GPIO_NUM_12); + + ESP_ERROR_CHECK(sdmmc_host_init()); + + ret = sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + if(ret != ESP_OK){ + log_i( "sdmmc_host_init_slot : %s", esp_err_to_name(ret)); + } + + ret = sdmmc_card_init(&host, &card); + if(ret != ESP_OK){ + log_i( "sdmmc_card_init : %s", esp_err_to_name(ret)); + } + sdmmc_card_print_info(stdout, &card); + size_t width = sdmmc_host_get_slot_width(SDMMC_HOST_SLOT_1); + log_i( "bus width is %d", width); +} + +esp_err_t write_wav_to_emmc(char* source, size_t block, size_t size) +{ + if(current_block==0) + { + // this is the first chunk from the client + current_block = block; + } + char *src = source; + for(int i=0; i 0){ + for(int k=0;kempty = 0; + wav_entry->start_block = start_block; + wav_entry->length = size; + wav_entry->isRack = -2; // is a rack member + memcpy(wav_entry->name,name,24); + + // set all the config data for the rack even if its redundant + struct rack_file_t rack = buf[rack_index]; + rack.free = 0; + const cJSON *rack_name = cJSON_GetObjectItemCaseSensitive(json, "name"); + memcpy(&rack.name,rack_name->valuestring,24); + const cJSON *break_points = cJSON_GetObjectItemCaseSensitive(json, "breakPoints"); + const cJSON *point = NULL; + int layers = 0; + cJSON_ArrayForEach(point,break_points) + { + // log_i("point:%u",point->valueint); + rack.break_points[layers] = point->valueint; + layers++; + } + // there is one more num break-points then num layers + rack.num_layers = layers - 1; + // log_i("layers : %u",layers - 1); + buf[rack_index] = rack; + ESP_ERROR_CHECK(emmc_write(buf,RACK_DIRECTORY_START_BLOCK,RACK_DIRECTORY_BLOCKS)); + free(buf); + // write_rack_lut_to_disk(); + // read_rack_lut_from_disk(); +} + +size_t find_gap_in_file_system(size_t size){ + size_t num_wavs; + struct wav_lu_t *data = get_all_wav_files(&num_wavs); + size_t address = search_directory( + data, + num_wavs, + FILE_STORAGE_START_BLOCK, + FILE_STORAGE_END_BLOCK, + size + ); + return(address); +} + +struct wav_lu_t *get_all_wav_files(size_t *len){ + // count the total wavs on file looking at the racks and the normal wav_lut + size_t num_wavs = 0; + for(int i=0;i 0){ + data = (struct wav_lu_t *)ps_malloc(num_wavs * sizeof(struct wav_lu_t)); + if(data == NULL){ + log_e("couldnt malloc data buffer"); + } + } + + // put in all the data + size_t index = 0; + for(int i=0;istart_block - ((struct wav_lu_t*)b)->start_block ); +} + +size_t search_directory(struct wav_lu_t *data, size_t num_used_entries, size_t start, size_t end, size_t file_size){ + size_t i; + if(num_used_entries == 0){ + // log_i("file system is empty"); + return(FILE_STORAGE_START_BLOCK); + } + + // sort the files by position + qsort(data,num_used_entries,sizeof(struct wav_lu_t),sort_lut); + + // make an array to hold the gaps (there is always one more gap then wav) and an index to keep track + size_t num_gap_entries = num_used_entries +1; + struct wav_lu_t *gaps = (struct wav_lu_t*)ps_malloc(num_gap_entries * sizeof(struct wav_lu_t)); + size_t num_gaps = 0; + + // initialize the array,add one for the end gap + for(i=0;i start){ + // log_i("there is a gap at the start"); + struct wav_lu_t gap; + gap.start_block = start; + size_t gap_num_blocks = data[0].start_block - start; + gap.length = gap_num_blocks * SECTOR_SIZE; + gap.empty = 0; + gaps[0] = gap; + num_gaps++; + } + + // find all the gaps and place them into the gap array + for(i=0; i file_size){ + // if this is the first fitting gap, start with that + if(smallest_fitting_gap == 0){ + smallest_fitting_gap = entry.length; + best_slot = entry; + // log_i("first best gap: start %u, size %u",entry.start_block,entry.length); + } + if(entry.length < smallest_fitting_gap){ + smallest_fitting_gap = entry.length; + best_slot = entry; + // log_i("new best gap : start %u, size %u",entry.start_block,entry.length); + } + } + } + + free(gaps); + + if(smallest_fitting_gap == 0){ + log_e("couldn't find a gap big enough for that wav"); + return(0); + } + return(best_slot.start_block); +} + +char* print_fs_json(){ + cJSON *j_RESPONSE_ROOT = cJSON_CreateObject(); + cJSON *j_metadata = cJSON_AddObjectToObject(j_RESPONSE_ROOT,"metadata"); + cJSON *j_voices = cJSON_AddArrayToObject(j_RESPONSE_ROOT,"voices"); + cJSON *j_firmwares = cJSON_AddArrayToObject(j_RESPONSE_ROOT,"firmwares"); + cJSON *j_websites = cJSON_AddArrayToObject(j_RESPONSE_ROOT,"websites"); + cJSON *j_pin_config = cJSON_AddArrayToObject(j_RESPONSE_ROOT,"pinConfig"); + for(int j=0;jvalueint; + voice_data[num_note].retrigger_mode = cJSON_GetObjectItemCaseSensitive(note, "retrigger")->valueint; + voice_data[num_note].note_off_meaning = cJSON_GetObjectItemCaseSensitive(note, "noteOff")->valueint; + voice_data[num_note].response_curve = cJSON_GetObjectItemCaseSensitive(note, "responseCurve")->valueint; + voice_data[num_note].priority = cJSON_GetObjectItemCaseSensitive(note, "priority")->valueint; + // is a rack, and not a new rack (would be -2) + if(cJSON_GetObjectItemCaseSensitive(note, "isRack")->valueint >= 0) + { + updateRackConfig(note); + } + num_note++; + } + ESP_ERROR_CHECK(emmc_write(voice_data,voice_start_block,BLOCKS_PER_VOICE)); + num_voice++; + } + wlog_i("done voice config update"); + read_wav_lut_from_disk(); + //cleanup + cJSON_Delete(vc_json); + free(voice_data); +} + +void updateRackConfig(cJSON *note){ + int rack_num = cJSON_GetObjectItemCaseSensitive(note, "isRack")->valueint; + cJSON *rack = cJSON_GetObjectItemCaseSensitive(note, "rack"); + cJSON *break_points = cJSON_GetObjectItemCaseSensitive(rack, "break_points"); + cJSON *point = NULL; + // load up the rack file + struct rack_file_t *buf = (struct rack_file_t *)ps_malloc(RACK_DIRECTORY_BLOCKS * SECTOR_SIZE); + if(buf == NULL){log_i("malloc rack_file_t buf failed");} + ESP_ERROR_CHECK(emmc_read(buf,RACK_DIRECTORY_START_BLOCK,RACK_DIRECTORY_BLOCKS)); + struct rack_file_t rack_file = buf[rack_num]; + // set the data in the rack file + rack_file.free = 0; + memcpy(&rack_file.name,cJSON_GetObjectItemCaseSensitive(rack, "name")->valuestring,24); + // update the breakpoints and layer count + int layer = 0; + cJSON_ArrayForEach(point,break_points) + { + rack_file.break_points[layer] = point->valueint; + layer++; + } + rack_file.num_layers = layer - 1; + //write the rack file to disk + buf[rack_num] = rack_file; + ESP_ERROR_CHECK(emmc_write(buf,RACK_DIRECTORY_START_BLOCK,RACK_DIRECTORY_BLOCKS)); + free(buf); +} + +void updatePinConfig(cJSON *config){ + cJSON *json = cJSON_Parse(config); + cJSON *pin = NULL; + int num_pin = 0; + cJSON_ArrayForEach(pin,json) + { + pin_config_lut[num_pin].action = cJSON_GetObjectItemCaseSensitive(pin, "action")->valueint; + pin_config_lut[num_pin].edge = cJSON_GetObjectItemCaseSensitive(pin, "edge")->valueint; + pin_config_lut[num_pin].gpio_num = cJSON_GetObjectItemCaseSensitive(pin, "gpioNum")->valueint; + pin_config_lut[num_pin].note = cJSON_GetObjectItemCaseSensitive(pin, "note")->valueint; + pin_config_lut[num_pin].touch = cJSON_GetObjectItemCaseSensitive(pin, "touch")->valueint; + pin_config_lut[num_pin].velocity = cJSON_GetObjectItemCaseSensitive(pin, "velocity")->valueint; + pin_config_lut[num_pin].debounce = cJSON_GetObjectItemCaseSensitive(pin, "debounce")->valueint; + num_pin++; + } + ESP_ERROR_CHECK(emmc_write(pin_config_lut,PIN_CONFIG_START_BLOCK,PIN_CONFIG_BLOCKS)); + wlog_i("wrote pin config to disk"); + cJSON_Delete(json); +} + +/** + * file system starts empty. + * when browser starts up, it receives JSON of the file system. + * browser sorts the system into its order on disk. + * when a file is added, browser takes its info and finds a spot for the file + * (trying not to overwrite files slated for deletion in this pass) + * and modifys the file system accordingly. + * when user hits UPLOAD, the browser sends the files one at a time, passing the start block as a header. + * Waver writes the files to disk + * the browser receives comfirmation that each file was uploaded successfully. + * If all the files are successfully uploaded, then the browser sends the new filesystem as JSON. + * WAVER receives, and decodes the JSON into its array of array of wav_file_t structs. + * It writes this new fileSystem into memory at the location given by the system_metadata, + * then it changes a flag in the metadata to indicate the current file_system location, + * which will be one of 2 possile locations, + * which are fixed, dedicated memory blocks. It alternates back and forth each time. + * then it send an OK response to the browser, and the browser reports that the upload was completed. + * Now the browser marks the new file_system as the defacto one, and erases the old record. +*/ + +/** + * FOR MIDI START SIGS + * the midi task receives uart data, and parses the midi. + * it then sends the midi_event_t(plus bank) struct via queue to the filesystem task. + * the filesystem task finds the file to play, and sends the file info and the midi_event_t(plus bank) struct + * to the wav_player task, indicating that it should start playing that file. + * the wav_player task finds a free buffer and starts the playback, taking note of the midi_event_t (plus bank) struct, + * and keeping a record that assiciates that file, buffer, and struct. + * + * FOR MIDI STOP SIGS + * the midi task receives uart data, and parses the midi. + * it then sends the midi_event_t(plus bank) struct via queue directly to the wav_player task. ??? + * the wav_player task looks up the buffer that is running that file, and flags it to be cleared at the next pass. +*/ \ No newline at end of file diff --git a/src/file_system.h b/src/file_system.h new file mode 100644 index 0000000..4614274 --- /dev/null +++ b/src/file_system.h @@ -0,0 +1,345 @@ +#ifndef FILE_SYSTEM_H +#define FILE_SYSTEM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define SECTOR_SIZE 512 + +#define MAX_FIRMWARE_SIZE 2097152 //2MB +#define MAX_FIRMWARE_SIZE_IN_BLOCKS (MAX_FIRMWARE_SIZE / SECTOR_SIZE + (MAX_FIRMWARE_SIZE % SECTOR_SIZE !=1)) +#define MAX_WEBSITE_SIZE 2097152 //2MB +#define MAX_WEBSITE_SIZE_IN_BLOCKS (MAX_WEBSITE_SIZE / SECTOR_SIZE + (MAX_WEBSITE_SIZE % SECTOR_SIZE !=1)) +#define MAX_RACK_LAYERS 32 +#define NUM_PIN_CONFIGS 14 +#define DEFAULT_DEBOUNCE_MS 60 +#define DEFAULT_VELOCITY 127 + +#define NUM_VOICES 16 +#define NUM_NOTES 128 + +#include "midi_in.h" +#include "cJSON.h" +#include "wav_player.h" +#include "wvr_pins.h" + +#define METADATA_TAG_LENGTH 12 + +enum play_back_mode { + ONE_SHOT, + LOOP, + PING_PONG +}; + +enum retrigger_mode { + RETRIGGER, + RESTART, + NONE, + NOTE_OFF +}; + +enum note_off_meaning { + HALT, + IGNORE +}; + +enum edge { + EDGE_NONE, + EDGE_FALLING, + EDGE_RISING +}; + +enum response_curve { + RESPONSE_LINEAR, + RESPONSE_ROOT_SQUARE, + RESPONSE_FIXED +}; + +enum action { + NOTE_ON, + BANK_UP, + BANK_DOWN, + WVR_WIFI_ON, + WVR_WIFI_OFF, + TOGGLE_WIFI, + VOLUME_UP, + VOLUME_DOWN, + MUTE_ON, + MUTE_OFF, + TOGGLE_MUTE, + SOFT // reduce velocity +}; + +struct pin_config_t { + enum action action; + enum edge edge; + uint8_t gpio_num; + uint8_t note; + int8_t touch; + uint8_t velocity; + int16_t debounce; +}; + +struct metadata_t { + char tag[METADATA_TAG_LENGTH]; + size_t num_voices; + size_t file_system_start; + size_t file_system_size; + size_t file_storage_start_block; + size_t num_firmwares; + size_t num_websites; + int current_firmware_index; + int current_website_index; + // struct pin_config_t pin_config[14]; + ///new + int recovery_mode_straping_pin; + size_t recovery_firmware_size; + uint8_t global_volume; + uint8_t wlog_verbosity; + uint8_t wifi_starts_on; +}; + +struct wav_lu_t { + size_t length; + size_t start_block; + int isRack; + enum play_back_mode play_back_mode; + enum retrigger_mode retrigger_mode; + enum note_off_meaning note_off_meaning; + enum response_curve response_curve; + int8_t buffer_index; + uint8_t priority; // 0 to 15 + uint8_t empty :1; + uint8_t shouldHalt :1; + uint8_t playing :1; +}; + +struct wav_file_t { + char name[24]; + size_t length; + size_t start_block; + int isRack; + enum play_back_mode play_back_mode; + enum retrigger_mode retrigger_mode; + enum note_off_meaning note_off_meaning; + enum response_curve response_curve; + uint8_t priority; // 0 to 15 + uint8_t empty; +}; + +struct firmware_t { + char name[24]; + size_t length; + size_t start_block; + size_t index; + // size_t website_slot; + uint8_t free; + uint8_t corrupt; +}; + +struct website_t { + char name[24]; + size_t length; + size_t start_block; + size_t index; + uint8_t free; + uint8_t corrupt; +}; + +struct rack_lu_t { + uint8_t num_layers; + struct wav_lu_t layers[MAX_RACK_LAYERS]; + uint8_t break_points[MAX_RACK_LAYERS + 1]; + uint8_t free; +}; + +struct rack_file_t { + char name[24]; + uint8_t num_layers; + struct wav_file_t layers[MAX_RACK_LAYERS]; + uint8_t break_points[MAX_RACK_LAYERS + 1]; + uint8_t free; +}; + +// static struct metadata_t metadata; +// static struct wav_lu_t **wav_lut; +// static struct firmware_t *firmware_lut; +// static struct website_t *website_lut; +// static struct rack_lu_t *rack_lut; +// static struct pin_config_t *pin_config_lut; + +static struct pin_config_t default_pin_config_array[14] = { + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D0, + .note = 40, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D1, + .note = 41, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D2, + .note = 42, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D3, + .note = 43, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D4, + .note = 44, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D5, + .note = 45, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D6, + .note = 46, + .touch = 0, + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_NONE, + .gpio_num = D7, + .note = 47, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_NONE, + .gpio_num = D8, + .note = 48, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_NONE, + .gpio_num = D9, + .note = 49, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_NONE, + .gpio_num = D10, + .note = 50, + .touch = -1, //no touch on this pin + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D11, + .note = 51, + .touch = 0, + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D12, + .note = 52, + .touch = 0, + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + }, + { + .action = NOTE_ON, + .edge = EDGE_FALLING, + .gpio_num = D13, + .note = 53, + .touch = 0, + .velocity = DEFAULT_VELOCITY, + .debounce = DEFAULT_DEBOUNCE_MS + } +}; + +void file_system_init(void); +void read_wav_lut_from_disk(void); +// struct wav_lu_t get_file_t_from_lookup_table(struct midi_event_t midi_event, int current_bank); +struct wav_lu_t get_file_t_from_lookup_table(uint8_t voice, uint8_t note, uint8_t velocity); +int try_read_metadata(void); +void write_metadata(struct metadata_t m); +void init_metadata(void); +void init_wav_lut(void); +void init_firmware_lut(void); +void write_firmware_lut_to_disk(void); +void init_website_lut(void); +void write_website_lut_to_disk(void); +void read_wav_lut_from_disk(void); +void read_firmware_lut_from_disk(void); +void read_website_lut_from_disk(void); +struct firmware_t *get_firmware_slot(int index); +struct website_t *get_website_slot(char index); +int write_firmware_to_emmc(char slot, uint8_t *source, size_t size); +void close_firmware_to_emmc(char index); +int write_website_to_emmc(char slot, uint8_t *source, size_t size); +void close_website_to_emmc(char index); +cJSON* add_voice_json(uint8_t voice_num); +void updateVoiceConfig(char *json); +void add_metadata_json(cJSON * root); +void add_firmware_json(cJSON * root); +void add_website_json(cJSON * root); +size_t get_website_chunk(size_t start_block, size_t toWrite, uint8_t *buffer, size_t total); +void init_rack_lut(void); +void write_frack_lut_to_disk(void); +void read_rack_lut_from_disk(void); +void add_rack_to_file_system(char *name, int voice, int note, size_t start_block, size_t size, int layer, cJSON *json); +int get_empty_rack(void); +void add_wav_to_rack(char* name, int rack_index, size_t start_block, size_t size, int layer, cJSON *json); +struct wav_lu_t *get_all_wav_files(size_t *len); +int sort_lut(const void * a, const void * b); +size_t search_directory(struct wav_lu_t *_data, size_t num_data_entries, size_t start, size_t end, size_t file_size); +void current_bank_up(void); +void current_bank_down(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/gpio.cpp b/src/gpio.cpp new file mode 100644 index 0000000..35f158e --- /dev/null +++ b/src/gpio.cpp @@ -0,0 +1,199 @@ +#include "Arduino.h" +#include "button.h" +#include "wvr_pins.h" +#include "ws_log.h" +#include "file_system.h" +// extern "C" { +// #include "wav_player.h" +// } + +extern "C" void rpc_out(int procedure, int arg0, int arg1, int arg2); +extern "C" void server_pause(void); +extern "C" void server_resume(void); +// extern "C" void current_bank_up(void); +// extern "C" void current_bank_down(void); + +struct pin_config_t *pin_config_lut; +extern QueueHandle_t midi_queue; +struct midi_event_t gpio_midi_event; +bool wifi_on = true; +bool wvr_pin_override[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +Button pinD0(D0,FALLING,300); +Button pinD1(D1,FALLING,300); +Button pinD2(D2,FALLING,300); +Button pinD3(D3,FALLING,300); +Button pinD4(D4,FALLING,300); +Button pinD5(D5,FALLING,300); +Button pinD6(D6,FALLING,300); +Button pinD7(D7,FALLING,300); +Button pinD8(D8,FALLING,300); +Button pinD9(D9,FALLING,300); +Button pinD10(D10,FALLING,300); +Button pinD11(D11,FALLING,300); +Button pinD12(D12,FALLING,300); +Button pinD13(D13,FALLING,300); + +Button *pin_buttons[14] = {&pinD0,&pinD1,&pinD2,&pinD3,&pinD4,&pinD5,&pinD6,&pinD7,&pinD8,&pinD9,&pinD10,&pinD11,&pinD12,&pinD13,}; + +void note_on(uint8_t pin) +{ + gpio_midi_event.channel = 0; + gpio_midi_event.code = 9; + gpio_midi_event.note = pin_config_lut[pin].note; + gpio_midi_event.velocity = pin_config_lut[pin].velocity; + xQueueSendToBack(midi_queue,(void *) &gpio_midi_event, portMAX_DELAY); +} + +void on_press(uint8_t i) +{ + wlog_i("onPress %d",i); + pin_config_t pin = pin_config_lut[i]; + switch (pin.action) + { + case NOTE_ON: + note_on(i); + break; + case BANK_UP: + current_bank_up(); + break; + case BANK_DOWN: + current_bank_down(); + break; + case TOGGLE_WIFI: + wifi_on ? server_pause() : server_resume(); + wifi_on = !wifi_on; + break; + default: + break; + } +} + +void on_release(uint8_t pin) +{ + wlog_i("onRelease %d",pin); + gpio_midi_event.channel = 0; + gpio_midi_event.code = 8; + gpio_midi_event.note = pin_config_lut[pin].note; + gpio_midi_event.velocity = 0; + xQueueSendToBack(midi_queue,(void *) &gpio_midi_event, portMAX_DELAY); +} + +void gpio_update_config(void) +{ + for(int i=0;i<14;i++) + { + pin_buttons[i]->dbnc = pin_config_lut[i].debounce; + pin_buttons[i]->mode = + pin_config_lut[i].edge == EDGE_RISING ? RISING : + pin_config_lut[i].edge == EDGE_FALLING ? FALLING : + EDGE_NONE; + wlog_i("pin%d debounce:%d",i,pin_config_lut[i].debounce); + wlog_i("pin%d edge:%d",i,pin_config_lut[i].edge); + wlog_i("pin%d action:%d",i,pin_config_lut[i].action); + wlog_i("pin%d velocity:%d",i,pin_config_lut[i].velocity); + } +} + +void gpio_init(void) +{ + if (!wvr_pin_override[0]) + { + gpio_reset_pin(GPIO_NUM_3); + pinMode(D0, INPUT_PULLUP); + pinD0.onPress([](){on_press(0);}); + pinD0.onRelease([](){on_release(0);}); + } + if (!wvr_pin_override[1]) + { + gpio_reset_pin(GPIO_NUM_1); + pinMode(D1, INPUT_PULLUP); + pinD1.onPress([](){on_press(1);}); + pinD1.onRelease([](){on_release(1);}); + } + if (!wvr_pin_override[2]) + { + gpio_reset_pin(GPIO_NUM_21); + pinMode(D2, INPUT_PULLUP); + pinD2.onPress([](){on_press(2);}); + pinD2.onRelease([](){on_release(2);}); + } + if (!wvr_pin_override[3]) + { + gpio_reset_pin(GPIO_NUM_19); + pinMode(D3, INPUT_PULLUP); + pinD3.onPress([](){on_press(3);}); + pinD3.onRelease([](){on_release(3);}); + } + if (!wvr_pin_override[4]) + { + gpio_reset_pin(GPIO_NUM_18); + pinMode(D4, INPUT_PULLUP); + pinD4.onPress([](){on_press(4);}); + pinD4.onRelease([](){on_release(4);}); + } + if (!wvr_pin_override[5]) + { + gpio_reset_pin(GPIO_NUM_5); + pinMode(D5, INPUT_PULLUP); + pinD5.onPress([](){on_press(5);}); + pinD5.onRelease([](){on_release(5);}); + } + if (!wvr_pin_override[6]) + { + gpio_reset_pin(GPIO_NUM_0); + pinMode(D6, INPUT_PULLUP); + pinD6.onPress([](){on_press(6);}); + pinD6.onRelease([](){on_release(6);}); + } + if (!wvr_pin_override[7]) + { + gpio_reset_pin(GPIO_NUM_36); + pinMode(D7, INPUT_PULLUP); + pinD7.onPress([](){on_press(7);}); + pinD7.onRelease([](){on_release(7);}); + } + if (!wvr_pin_override[8]) + { + gpio_reset_pin(GPIO_NUM_39); + pinMode(D8, INPUT_PULLUP); + pinD8.onPress([](){on_press(8);}); + pinD8.onRelease([](){on_release(8);}); + } + if (!wvr_pin_override[9]) + { + gpio_reset_pin(GPIO_NUM_34); + pinMode(D9, INPUT_PULLUP); + pinD9.onPress([](){on_press(9);}); + pinD9.onRelease([](){on_release(9);}); + } + if (!wvr_pin_override[10]) + { + gpio_reset_pin(GPIO_NUM_35); + pinMode(D10, INPUT_PULLUP); + pinD10.onPress([](){on_press(10);}); + pinD10.onRelease([](){on_release(10);}); + } + if (!wvr_pin_override[11]) + { + gpio_reset_pin(GPIO_NUM_32); + pinMode(D11, INPUT_PULLUP); + pinD11.onPress([](){on_press(11);}); + pinD11.onRelease([](){on_release(11);}); + } + if (!wvr_pin_override[12]) + { + gpio_reset_pin(GPIO_NUM_33); + pinMode(D12, INPUT_PULLUP); + pinD12.onPress([](){on_press(12);}); + pinD12.onRelease([](){on_release(12);}); + } + if (!wvr_pin_override[13]) + { + gpio_reset_pin(GPIO_NUM_27); + pinMode(D13, INPUT_PULLUP); + pinD13.onPress([](){on_press(13);}); + pinD13.onRelease([](){on_release(13);}); + } + gpio_update_config(); +} \ No newline at end of file diff --git a/src/midi.cpp b/src/midi.cpp new file mode 100644 index 0000000..21c2601 --- /dev/null +++ b/src/midi.cpp @@ -0,0 +1,38 @@ +#include "midiXparser.h" +#include "esp_log.h" +#include "esp32-hal-log.h" +#include "midi_in.h" +#include "ws_log.h" + +midiXparser midiParser; +uint8_t *msg; + +void midi_parser_init(void) +{ + midiParser.setMidiMsgFilter( midiXparser::channelVoiceMsgTypeMsk ); +} + +extern "C" uint8_t* midi_parse(uint8_t in) +{ + if ( midiParser.parse( in ) ) // Do we received a channel voice msg ? + { + if ( midiParser.isMidiStatus(midiXparser::noteOnStatus) || midiParser.isMidiStatus(midiXparser::noteOffStatus) || midiParser.isMidiStatus(midiXparser::programChangeStatus) ) + { + // if ( midiParser.isMidiStatus(midiXparser::noteOnStatus)){ + // log_i("on"); + // } + // if ( midiParser.isMidiStatus(midiXparser::noteOffStatus)){ + // log_i("off"); + // } + msg = midiParser.getMidiMsg(); + return msg; + } + // if ( midiParser.isMidiStatus(midiXparser::noteOffStatus) || midiParser.isMidiStatus(midiXparser::noteOnStatus) ) { + // log_i("got message"); + // delay(200); + // Serial.write(midiParser1.getMidiMsg(),midiParser1.getMidiMsgLen()); + // Serial.write(midiParser1.getMidiMsg(),midiParser1.getMidiMsgLen()); + } + return NULL; +} + diff --git a/src/midiXparser.cpp b/src/midiXparser.cpp new file mode 100644 index 0000000..a5f7546 --- /dev/null +++ b/src/midiXparser.cpp @@ -0,0 +1,288 @@ +/* + midXparser + A small footprint midi parser. + Copyright (C) 2017/2018 by The KikGen labs. + HEADER CLASS FILE - METHODS + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + Licence : MIT. +*/ + +#include "midiXparser.h" +#include +#include + +const uint8_t midiXparser::m_systemCommonMsglen[]={ + // SYSTEM COMMON + 0, // soxStatus = 0XF0, + 2, // midiTimeCodeStatus = 0XF1, + 3, // songPosPointerStatus = 0XF2, + 2, // songSelectStatus = 0XF3, + 0, // reserved1Status = 0XF4, + 0, // reserved2Status = 0XF5, + 1, // tuneRequestStatus = 0XF6, + 0 // eoxStatus = 0XF7, +}; + +const uint8_t midiXparser::m_channelVoiceMsgMsglen[]={ + 3, // noteOffStatus = 0X80, + 3, // noteOnStatus = 0X90, + 3, // polyKeyPressureStatus = 0XA0, + 3, // controlChangeStatus = 0XB0, + 2, // programChangeStatus = 0XC0, + 2, // channelPressureStatus = 0XD0, + 3, // pitchBendStatus = 0XE0, +}; + +// Constructors +midiXparser::midiXparser() { + +}; + +// Give the current sysex state, +bool midiXparser::isSysExMode() { return m_sysExMode ;} + +bool midiXparser::wasSysExMode() { + return ( ( m_readByte == eoxStatus && m_isByteCaptured ) || m_sysExError ); +} + +// Give the status of the last SYSEX transmission. +bool midiXparser::isSysExError() { return m_sysExError ;} + +// Used to check if the last byte parsed was captured +bool midiXparser::isByteCaptured() { return m_isByteCaptured; } + +// Check the midi status of a non sysex parsed midi msg +bool midiXparser::isMidiStatus(midiStatusValue midiStatus) { + + if ( m_midiParsedMsgType == noneMsgTypeMsk ) return false; // Only if a parsed msg exists + + // Channel voice msg + if ( m_midiParsedMsgType == channelVoiceMsgTypeMsk ) return ( ( m_midiMsg[0] & 0xF0 ) == (uint8_t)midiStatus ) ; + // System Common msg + if ( m_midiParsedMsgType == systemCommonMsgTypeMsk ) return ( m_midiMsg[0] == (uint8_t)midiStatus ) ; + // realtime msg + if ( m_midiParsedMsgType == realTimeMsgTypeMsk ) return ( m_midiMsgRealTime == (uint8_t)midiStatus) ; + + return false; +} + +// Return the type of the last parsed midi message +uint8_t midiXparser::getMidiMsgType() { return m_midiParsedMsgType; } + +// Return the type of the currently parsed midi message +uint8_t midiXparser::getMidiCurrentMsgType() { return m_midiCurrentMsgType; } + +// Return the type of a midi status (cf enum) +uint8_t midiXparser::getMidiStatusMsgTypeMsk(uint8_t midiStatus) { + + if (midiStatus >= 0xF8 ) return realTimeMsgTypeMsk; + if (midiStatus == 0XF7 || midiStatus == 0xF0 ) return sysExMsgTypeMsk; + if (midiStatus >= 0xF0 ) return systemCommonMsgTypeMsk; + if (midiStatus >= 0x80 ) return channelVoiceMsgTypeMsk; + + return noneMsgTypeMsk ; +} + +// Return the len of the last parsed midi message, including sysex +uint8_t midiXparser::getMidiMsgLen() { + if (m_midiParsedMsgType == sysExMsgTypeMsk ) return getSysExMsgLen() ; + if (m_midiParsedMsgType == realTimeMsgTypeMsk ) return 1 ; + if (m_midiParsedMsgType == channelVoiceMsgTypeMsk ) return m_channelVoiceMsgMsglen[ (getMidiMsg()[0] >> 4) - 8 ] ; + if (m_midiParsedMsgType == systemCommonMsgTypeMsk ) return m_systemCommonMsglen[getMidiMsg()[0] & 0x0F] ; + + return 0; +} + +// Return the len of a midistatus message (cf enum) +// Nb: SOX or EOX return always 0. + +uint8_t midiXparser::getMidiStatusMsgLen(uint8_t midiStatus) { + if (midiStatus >= 0xF8 ) return 1; + if (midiStatus >= 0xF0 ) return m_systemCommonMsglen[midiStatus & 0x0F]; + if (midiStatus >= 0x80 ) return m_channelVoiceMsgMsglen[ (midiStatus >> 4) - 8 ]; + return 0 ; +} + +// Return the len of the last Sysex msg. +// This persists until the next sysex. +unsigned midiXparser::getSysExMsgLen() { return m_sysExMsgLen ;} + +// Return the parsed message buffered +uint8_t * midiXparser::getMidiMsg() { + + switch (m_midiParsedMsgType) { + case realTimeMsgTypeMsk: + return &m_midiMsgRealTime; + break; + case sysExMsgTypeMsk: + return (uint8_t*) NULL; + break; + } + + return m_midiMsg ; +} + +// Get the last byte parsed +byte midiXparser::getByte() { return m_readByte ;} + +// Set filter mask all/none for all midi Msg including Sysex +// For Sysex, the "on the fly" mode is activated by default +// To change that, you must call explicitly setSysExFilter again. +void midiXparser::setMidiMsgFilter(uint8_t midiMsgTypeFilterMsk) { + m_midiMsgTypeFilterMsk = midiMsgTypeFilterMsk; +} + +////////////////////////////////////////////////////////////////////////////// +// midiXParser MIDI PARSER +//---------------------------------------------------------------------------- +// The main method. +// It parses midi byte per byte and return true if a message is matching filters. +// Set also the byte capture flag if a byte belong to a filtered message. +////////////////////////////////////////////////////////////////////////////// +bool midiXparser::parse(byte readByte) { + + // Store the passed byte so it can be sent back to serial + // if not captured + m_readByte = readByte; + m_isByteCaptured = false; + m_sysExError = false; // Clear any previous sysex error + + // MIDI Message status are starting at 0x80 + if ( readByte >= 0x80 ) + { + + // Check filters. + m_isByteCaptured = (m_midiMsgTypeFilterMsk & getMidiStatusMsgTypeMsk(readByte) ); + + // SysEx can be terminated abnormally with a midi status. + // We must check that before applying filters. + if ( m_sysExMode && m_readByte < 0xF7 ) { + m_sysExError = true; + m_sysExMode = false; + m_sysExMsgLen = m_sysExindexMsgLen; + } + + // Real time messages must be processed as transparent for all other status + if ( readByte >= 0xF8 ) { + m_midiParsedMsgType = realTimeMsgTypeMsk; + // NB : m_midiCurrentMsgType can't be used as real time can be mixed with + // channel voice msg. + m_midiMsgRealTime = readByte; + return m_isByteCaptured; + } + + // Running status not possible at this point + m_runningStatusPossible=false; + + // Reset current msg type and msg len + m_midiCurrentMsgType = noneMsgTypeMsk; + m_indexMsgLen = m_expectedMsgLen = 0; + + // Apply filter + if (!m_isByteCaptured) return false; + + // Start SYSEX --------------------------------------------------------- + + // END OF Sysex. + if ( readByte == eoxStatus ) { + m_sysExMsgLen = m_sysExindexMsgLen; + if (m_sysExMode ) { + m_midiParsedMsgType = sysExMsgTypeMsk; + m_sysExMode = false; + return true; + } // Isolated EOX without SOX. + m_sysExMsgLen = 0; + m_sysExError = true; + m_midiCurrentMsgType = sysExMsgTypeMsk; + return false; + } + + // Start SYSEX + if ( readByte == soxStatus ) { + m_sysExMode = true; + m_sysExindexMsgLen = 0; + m_midiCurrentMsgType = sysExMsgTypeMsk; + return false; + } + // Start midi msg ------------------------------------------------------ + + // get the len of the midi msg to parse minus status + m_midiMsg[0] = readByte; + m_expectedMsgLen = getMidiStatusMsgLen(readByte) ; + m_indexMsgLen = m_expectedMsgLen - 1; + + // Channel messages between 0x80 and 0xEF ------------------------------- + if ( readByte <= 0xEF ) { + m_midiCurrentMsgType = channelVoiceMsgTypeMsk; + } + + // System common messages between 0xF0 and 0xF7 ------------------------- + // but SOX / EOX + else { + m_midiCurrentMsgType = systemCommonMsgTypeMsk; + // Case of 1 byte len midi msg (Tune request) + if ( m_indexMsgLen == 0 ) { + m_midiParsedMsgType = m_midiCurrentMsgType; + return true; + } + } + } + + // Midi Data from 00 to 0X7F ---------------------------------------------- + else { + + // Capture the SYSEX message if filter is set + // If m_sysExBufferSize is 0, do not store + if (m_sysExMode ) { + m_sysExindexMsgLen++; + m_isByteCaptured = true; + return false; + } + + // "Pure" midi message data + // check if Running status set and if so, generate a true midi channel msg with + // the previous one. Possible only if filters matchs. + if (m_runningStatusPossible) { + m_indexMsgLen = m_expectedMsgLen-1; + m_runningStatusPossible = false; + } + + // Len was set only if filters matched before + if ( m_indexMsgLen ) { + + m_midiMsg[m_expectedMsgLen-m_indexMsgLen] = readByte; + m_isByteCaptured = true; + m_indexMsgLen -- ; + // Message complete ? + // Enable running status if it is a message channel. + if (m_indexMsgLen == 0) { + m_midiParsedMsgType = m_midiCurrentMsgType; + if (m_midiParsedMsgType == channelVoiceMsgTypeMsk) { + m_runningStatusPossible = true; + } + return true; + } + } + + } // Midi data from 00 to 0X7F + + // All other data here are purely ignored. + // In respect of the MIDI specifications. + + return false; +} \ No newline at end of file diff --git a/src/midiXparser.h b/src/midiXparser.h new file mode 100644 index 0000000..10c34ae --- /dev/null +++ b/src/midiXparser.h @@ -0,0 +1,122 @@ +/* + midXparser + A small footprint midi parser. + Copyright (C) 2017/2018 by The KikGen labs. + HEADER CLASS FILE + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. + Licence : MIT. +*/ +// #pragma once + +#ifndef midiXparser_h +#define midiXparser_h + +#if ARDUINO +#include +#else +#include +typedef uint8_t byte; +#endif +#include + +class midiXparser { + uint8_t m_midiMsg[3]; + uint8_t m_midiMsgRealTime; // Used for real time only + uint8_t m_indexMsgLen = 0; + uint8_t m_expectedMsgLen = 0; + bool m_sysExMode = false; + bool m_sysExError = false; + unsigned m_sysExMsgLen = 0; + unsigned m_sysExindexMsgLen = 0; + bool m_isByteCaptured=false; + byte m_readByte = 0; + bool m_runningStatusPossible=false; + uint8_t m_midiMsgTypeFilterMsk = noneMsgTypeMsk; + uint8_t m_midiParsedMsgType = noneMsgTypeMsk; + uint8_t m_midiCurrentMsgType = noneMsgTypeMsk; + + static const uint8_t m_systemCommonMsglen[8]; + static const uint8_t m_channelVoiceMsgMsglen[7]; + + + + public: + // Midi messages type + enum midiMsgTypeMaskValue { + noneMsgTypeMsk = 0B0000, + channelVoiceMsgTypeMsk = 0B0001, + systemCommonMsgTypeMsk = 0B0010, + realTimeMsgTypeMsk = 0B0100, + sysExMsgTypeMsk = 0B1000, + allMsgTypeMsk = 0B1111 + }; + + enum midiStatusValue { + // CHANNEL VOICE + noteOffStatus = 0X80, + noteOnStatus = 0X90, + polyKeyPressureStatus = 0XA0, + controlChangeStatus = 0XB0, + programChangeStatus = 0XC0, + channelPressureStatus = 0XD0, + pitchBendStatus = 0XE0, + // SYSTEM COMMON + soxStatus = 0XF0, + midiTimeCodeStatus = 0XF1, + songPosPointerStatus = 0XF2, + songSelectStatus = 0XF3, + reserved1Status = 0XF4, + reserved2Status = 0XF5, + tuneRequestStatus = 0XF6, + eoxStatus = 0XF7, + // REAL TIME + timingClockStatus = 0XF8, + reserved3Status = 0XF9, + startStatus = 0XFA, + continueStatus = 0XFB, + stopStatus = 0XFC, + reserved4Status = 0XFD, + activeSensingStatus = 0XFE, + systemResetStatus = 0XFF + }; + + // Constructor + midiXparser(); + + // Methods + bool isSysExMode() ; + bool wasSysExMode() ; + bool isSysExError(); + bool isByteCaptured() ; + bool isMidiStatus(midiStatusValue ); + uint8_t getMidiMsgType() ; + uint8_t getMidiCurrentMsgType() ; + uint8_t getMidiMsgLen(); + uint8_t * getMidiMsg(); + byte getByte() ; + unsigned getSysExMsgLen() ; + void setMidiMsgFilter(uint8_t ); + bool parse(byte ); + static uint8_t getMidiStatusMsgTypeMsk(uint8_t ) ; + static uint8_t getMidiStatusMsgLen(uint8_t ); + +}; + + + +#endif \ No newline at end of file diff --git a/src/midi_in.c b/src/midi_in.c new file mode 100644 index 0000000..9cc8cac --- /dev/null +++ b/src/midi_in.c @@ -0,0 +1,154 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/uart.h" +#include "esp_log.h" +#include "esp32-hal-log.h" +#include "midi_in.h" +#include "ws_log.h" +#include "wav_player.h" +#include "file_system.h" + +// #define MIDI_UART_NUM UART_NUM_1 +#define MIDI_UART_NUM UART_NUM_2 +#define BUF_SIZE (1024) +#define RD_BUF_SIZE (BUF_SIZE) + +// #define RX_PIN 18 // opto test +#define RX_PIN 23 // wrv midi in + +static const char *TAG = "midi"; + +// from server.cpp +void sendWSMsg(char* msg); +// from midiXparser.cpp +uint8_t *midi_parse(uint8_t in); + +esp_err_t ret; + +QueueHandle_t wav_player_queue; +QueueHandle_t uart_queue; // uart Events queue + +struct wav_player_event_t wav_player_event; +int bytes_read; +uint8_t *msg; + +void init_gpio(void) +{ + gpio_config_t io_conf; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pin_bit_mask = (1 << GPIO_NUM_23); // WVR MIDI IN +// io_conf.pin_bit_mask = (1 << GPIO_NUM_18); // opto tests +// io_conf.pin_bit_mask = (1 << GPIO_NUM_5); // USB MIDI +// io_conf.pin_bit_mask = (1 << GPIO_NUM_16); // ?? + io_conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&io_conf); +} + +#define MIDI_BUFFER_SIZE 256 + +uint8_t channel_lut[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +static void read_uart_task() +{ + uart_event_t event; + uint8_t* tmp = (uint8_t*)malloc(MIDI_BUFFER_SIZE); + + uart_config_t uart_config = { + .baud_rate = 31250, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + ESP_ERROR_CHECK(uart_param_config(MIDI_UART_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(MIDI_UART_NUM, UART_PIN_NO_CHANGE, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_driver_install(MIDI_UART_NUM, 256, 0, 256, &uart_queue, 0)); + ESP_ERROR_CHECK(uart_set_rx_timeout(MIDI_UART_NUM, 1)); + + if(tmp == NULL) + { + log_e("failed to malloc tmp"); + } + + log_i("midi task running on core %u",xPortGetCoreID()); + + for(;;) { + if(xQueueReceive(uart_queue, (void *)&event, (portTickType)portMAX_DELAY)) { + bzero(tmp, MIDI_BUFFER_SIZE); + if(event.type==UART_DATA) + { + bytes_read = uart_read_bytes(MIDI_UART_NUM, tmp, event.size, portMAX_DELAY); + for(int i=0;i> 4) & 0b00001111; + switch (code) + { + case MIDI_NOTE_ON: + case MIDI_NOTE_OFF: + { + struct wav_player_event_t wav_player_event; + wav_player_event.code = (msg[0] >> 4) & 0b00001111; + wav_player_event.voice = channel_lut[channel]; + wav_player_event.note = msg[1] & 0b01111111; + wav_player_event.velocity = msg[2] & 0b01111111; + xQueueSendToBack(wav_player_queue,(void *) &wav_player_event, portMAX_DELAY); + // log_e("%d %d",i,wav_player_event.note); + } + break; + case MIDI_PROGRAM_CHANGE: + { + uint8_t voice = msg[1] & 0b01111111; + // log_e("prog chng %d on channel %d",voice, channel); + // if the program change is out of range set it to the highest voice + voice = voice < NUM_VOICES ? voice : NUM_VOICES - 1; + channel_lut[channel] = voice; + break; + } + default: + break; + } + } + } + } + else if(event.type==UART_FIFO_OVF) + { + log_e("UART_FIFO_OVF"); + } + else if(event.type==UART_BUFFER_FULL) + { + log_e("UART_BUFFER_FULL"); + } + else if(event.type==UART_BREAK) + { + log_e("UART_BREAK"); + } + else if(event.type==UART_PARITY_ERR) + { + log_e("UART_PARITY_ERR"); + } + else if(event.type==UART_FRAME_ERR) + { + log_e("UART_FRAME_ERR"); + } + else + { + log_e("other uart event %d", event.type); + } + } + } +} + +void midi_init(void) +{ + init_gpio(); + xTaskCreatePinnedToCore(read_uart_task, "read_uart_task", 4096, NULL, 9, NULL, 1); + // xTaskCreatePinnedToCore(read_uart_task, "read_uart_task", 4096, NULL, 3, NULL, 1); +} \ No newline at end of file diff --git a/src/midi_in.h b/src/midi_in.h new file mode 100644 index 0000000..5bef8e0 --- /dev/null +++ b/src/midi_in.h @@ -0,0 +1,22 @@ +#ifndef MIDI_IN_H +#define MIDI_IN_H +#ifdef __cplusplus + extern "C" + { +#endif + +#define MIDI_NOTE_OFF 8 +#define MIDI_NOTE_ON 9 +#define MIDI_PROGRAM_CHANGE 12 + +struct midi_event_t { + uint8_t code; + uint8_t note; + uint8_t velocity; + uint8_t channel; +}; + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/mkr_board.cpp b/src/mkr_board.cpp new file mode 100644 index 0000000..29a75a2 --- /dev/null +++ b/src/mkr_board.cpp @@ -0,0 +1,67 @@ +#include "Arduino.h" +#include "button.h" +#include "wvr_pins.h" +#include "ws_log.h" + +extern "C" void play_sound_ext(int i); + +Button p1(D13,FALLING, 50); +Button p2(D12,FALLING, 50); +Button p3(D11,FALLING, 50); +Button p4(D10,FALLING, 50); +Button p5(D9,FALLING, 50); +Button p6(D8,FALLING, 50); +Button p7(D7,FALLING, 50); +Button p8(D6,FALLING, 50); +Button p9(D5,FALLING, 50); +Button p10(D4,FALLING, 50); +Button p11(D3,FALLING, 50); +Button p12(D2,FALLING, 50); +Button p13(D1,FALLING, 50); +Button p14(D0,FALLING, 50); + +void logPress(){ + play_sound_ext(40); + wlog_i("*"); +} +void logPress2(){ + wlog_i("*"); + play_sound_ext(41); +} + +void mkr_init(){ + + button_init(); + + gpio_reset_pin(GPIO_NUM_1); + + pinMode(D13,INPUT_PULLUP); + pinMode(D12,INPUT_PULLUP); + pinMode(D11,INPUT_PULLUP); + pinMode(D10,INPUT_PULLUP); + pinMode(D9,INPUT_PULLUP); + pinMode(D8,INPUT_PULLUP); + pinMode(D7,INPUT_PULLUP); + pinMode(D6,INPUT_PULLUP); + pinMode(D5,INPUT_PULLUP); + pinMode(D4,INPUT_PULLUP); + pinMode(D3,INPUT_PULLUP); + pinMode(D2,INPUT_PULLUP); + pinMode(D1,INPUT_PULLUP); + pinMode(D0,INPUT_PULLUP); + + p1.onPress([](void){wlog_i("1");}); + p2.onPress([](void){wlog_i("2");}); + p3.onPress([](void){wlog_i("3");}); + p4.onPress([](void){wlog_i("4");}); + p5.onPress([](void){wlog_i("5");}); + p6.onPress([](void){wlog_i("6");}); + p7.onPress([](void){wlog_i("7");}); + p8.onPress([](void){wlog_i("8");}); + p9.onPress([](void){wlog_i("9");}); + p10.onPress([](void){wlog_i("10");}); + p11.onPress([](void){wlog_i("11");}); + p12.onPress([](void){wlog_i("12");}); + p13.onPress([](void){wlog_i("13");}); + p14.onPress([](void){wlog_i("14");}); +} diff --git a/src/pot.c b/src/pot.c new file mode 100644 index 0000000..82805b4 --- /dev/null +++ b/src/pot.c @@ -0,0 +1,50 @@ +#include "Arduino.h" + +int sort(const void * a, const void * b) { + return ( *(int*)a - *(int*)b ); +} + +static void adc_task(void* arg) +{ + int percent = 0; + for(;;) { + // uint32_t samples[30]; + // uint32_t acc = 0; + // for(int i=0;i<30;i++){ + // samples[i] = analogRead(39) >> 4; + // } + // // sort + // qsort(samples,30,sizeof(uint32_t),sort); + // // log_i("sorted:"); + // // for(int i=0;i<30;i++){ + // // log_i("%u",samples[i]); + // // } + // acc = samples[10]; + // for(int i=11;i<20;i++){ + // acc += samples[i]; + // acc = acc/2; + // } + // log_i("average : %u",acc); + + uint32_t adc_reading = analogRead(39); + log_i("%u",adc_reading); + pinMode(39,OUTPUT); + digitalWrite(39,HIGH); + pinMode(39, INPUT); + // log_i("%u",adc_reading >> 5); + // int new_percent = adc_reading / (0b111111111111 / 100); + // if(new_percent != percent) + // { + // percent = new_percent; + // printf("%d percent\n", new_percent); + // } + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +void pot_init(void){ + // analogSetWidth(8); + analogSetCycles(8); + // analogSetSamples(4); + xTaskCreate(&adc_task, "adc_task", 1024 * 4, NULL, 3, NULL); +} diff --git a/src/rgb.cpp b/src/rgb.cpp new file mode 100644 index 0000000..690484b --- /dev/null +++ b/src/rgb.cpp @@ -0,0 +1,20 @@ +#include +#include "Arduino.h" + +#define NUMPIXELS 1 +#define PIN 21 + +void rgb_init(){ + Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); + pixels.begin(); + pixels.clear(); + pixels.setPixelColor(0, pixels.Color(0, 150, 0)); + pixels.show(); + delay(1000); + pixels.setPixelColor(0, pixels.Color(150, 0, 0)); + pixels.show(); + delay(1000); + pixels.setPixelColor(0, pixels.Color(0, 0, 150)); + pixels.show(); + delay(1000); +} \ No newline at end of file diff --git a/src/rpc.c b/src/rpc.c new file mode 100644 index 0000000..54b8069 --- /dev/null +++ b/src/rpc.c @@ -0,0 +1,111 @@ +// #include "esp32-hal-log.h" +#include "esp_err.h" +#include "cJSON.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "esp32-hal-log.h" + +#include "ws_log.h" +#include "midi_in.h" +#include "rpc.h" +#include "wav_player.h" + +// QueueHandle_t rpc_in_queue; +QueueHandle_t rpc_out_queue; +// TaskHandle_t rpc_in_task_handle; +TaskHandle_t rpc_out_task_handle; +BaseType_t task_return; + +#define NO_RPC_OUT 1 + +// wav_player.c +// int play_wav(uint8_t voice, uint8_t note, uint8_t velocity); +// int stop_wav(uint8_t voice, uint8_t note); +// server.cpp +// void server_pause(void); +// void server_resume(void); + +void sendWSMsg(char* msg); + +void rpc_out(int procedure, int arg0, int arg1, int arg2) +{ + if(ARDUHAL_LOG_LEVEL == ARDUHAL_LOG_LEVEL_NONE) return; + if(NO_RPC_OUT) return; + wlog_i("rpc_out called"); + struct rpc_event_t rpc_event_out; + rpc_event_out.procedure = procedure; + rpc_event_out.arg0 = arg0; + rpc_event_out.arg1 = arg1; + rpc_event_out.arg2 = arg2; + xQueueSendToBack(rpc_out_queue, (void *)&rpc_event_out, 0); +} + +char* on_rpc_in(cJSON *json) +{ + enum rpc_procedure procedure = cJSON_GetObjectItemCaseSensitive(json, "procedure")->valueint; + switch (procedure) + { + case RPC_NOTE_ON: + { + uint8_t voice = cJSON_GetObjectItemCaseSensitive(json, "voice")->valueint; + uint8_t note = cJSON_GetObjectItemCaseSensitive(json, "note")->valueint; + uint8_t velocity = cJSON_GetObjectItemCaseSensitive(json, "velocity")->valueint; + play_wav(voice, note, velocity); + char * res = "started wav"; + return(res); + break; + } + case RPC_NOTE_OFF: + { + uint8_t voice = cJSON_GetObjectItemCaseSensitive(json, "voice")->valueint; + uint8_t note = cJSON_GetObjectItemCaseSensitive(json, "note")->valueint; + stop_wav(voice, note); + char * res = "started wav"; + return(res); + break; + } + default: + break; + } +} + +void rpc_out_task(void* pvParameters) +{ + struct rpc_event_t event; + for(;;){ + if(xQueueReceive(rpc_out_queue, (void *)&event, (portTickType)portMAX_DELAY)) + { + + wlog_i("rpc_out_task received event"); + cJSON *rpc_root = cJSON_CreateObject(); + switch(event.procedure){ + case RPC_NOTE_ON: + wlog_i("rpc note on"); + cJSON_AddNumberToObject(rpc_root, "procedure", RPC_NOTE_ON); + cJSON_AddNumberToObject(rpc_root, "voice", event.arg0); + cJSON_AddNumberToObject(rpc_root, "note", event.arg1); + cJSON_AddNumberToObject(rpc_root, "velocity", event.arg2); + break; + case RPC_NOTE_OFF: + cJSON_AddNumberToObject(rpc_root, "procedure", RPC_NOTE_OFF); + cJSON_AddNumberToObject(rpc_root, "voice", event.arg0); + cJSON_AddNumberToObject(rpc_root, "note", event.arg1); + break; + default: break; + } + char *json = cJSON_PrintUnformatted(rpc_root); + // wlog_i("json: %s",json); + sendWSMsg(json); + cJSON_Delete(rpc_root); + } + } +} + +void rpc_init(void) +{ + // rpc_in_queue = xQueueCreate(20, sizeof(struct rpc_event_t)); + rpc_out_queue = xQueueCreate(20, sizeof(struct rpc_event_t)); + // task_return = xTaskCreate(rpc_in_task,"rpc_in_task", 1024, NULL, 1, rpc_in_task_handle); + task_return = xTaskCreate(rpc_out_task,"rpc_out_task", 1024 * 4, NULL, 1, rpc_out_task_handle); +} \ No newline at end of file diff --git a/src/rpc.h b/src/rpc.h new file mode 100644 index 0000000..4d4a140 --- /dev/null +++ b/src/rpc.h @@ -0,0 +1,30 @@ +#ifndef RPC_H +#define RPC_H +#ifdef __cplusplus +extern "C" +{ +#endif + + +struct rpc_event_t { + int procedure; + int arg0; + int arg1; + int arg2; +}; + +enum rpc_procedure { + RPC_NOTE_ON, + RPC_NOTE_OFF, + RPC_VOICE_UP, + RPC_VOICE_DOWN, + RPC_WIFI_TOGGLE, +}; + +void rpc_out(int procedure, int arg0, int arg1, int arg2); +void rpc_init(void); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 0000000..34eddf0 --- /dev/null +++ b/src/server.cpp @@ -0,0 +1,624 @@ +#include "Arduino.h" + +#include +#include +#include +#include +#include "esp_ota_ops.h" +#include "esp_image_format.h" +#include "driver/sdmmc_host.h" +#include "wvr_ui.h" +#include +#include "soc/rtc_wdt.h" +#include "cJSON.h" +#include "ws_log.h" +#include "wvr_0.3.h" +#include "server.h" +#include "file_system.h" + +extern "C" char* print_fs_json(); +extern "C" size_t find_gap_in_file_system(size_t size); +extern "C" esp_err_t write_wav_to_emmc(uint8_t* source, size_t block, size_t size); +extern "C" esp_err_t close_wav_to_emmc(void); +extern "C" void add_wav_to_file_system(char *name,int voice,int note,size_t start_block,size_t size); +extern "C" size_t place_wav(struct lut_t *_data, size_t num_data_entries, size_t start, size_t end, size_t file_size); +extern "C" void updateVoiceConfig(char* json); +extern "C" void updatePinConfig(char* json); +extern "C" char* on_rpc_in(cJSON* json); +extern "C" char* write_recovery_firmware_to_emmc(uint8_t* source, size_t size); +extern "C" char* close_recovery_firmware_to_emmc(size_t recovery_firmware_size); +// extern "C" size_t add_firmware_and_gui_to_file_system(char *gui_name,char *firmware_name,size_t gui_start_block,size_t firmware_start_block,size_t gui_size,size_t firmware_size); + +struct metadata_t metadata; +struct wav_lu_t **lut; +struct firmware_t *firmware_lut; +struct website_t *website_lut; + +void bootFromEmmc(int index); +void wifi_log_boot_stage(void); + +const char *ssid = "yourAP"; +const char *password = "yourPassword"; + +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); + +extern "C" void sendWSLog(char* msg){ + cJSON *root = cJSON_CreateObject(); + cJSON_AddStringToObject(root,"log",msg); + char* out = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + ws.textAll(out); + free(out); +} + +extern "C" void sendWSMsg(char* msg){ + ws.textAll(msg); +} + +cJSON *ws_root; + +void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ + if(type == WS_EVT_CONNECT){ + Serial.printf("ws[%s][%u] connect\n", server->url(), client->id()); + client->printf("Hello Client %u :) firmware v%s", client->id(), VERSION_CODE); + wifi_log_boot_stage(); + client->ping(); + } else if(type == WS_EVT_DISCONNECT){ + Serial.printf("ws[%s][%u] disconnect\n", server->url(), client->id()); + } else if(type == WS_EVT_ERROR){ + Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data); + } else if(type == WS_EVT_PONG){ + Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:""); + } else if(type == WS_EVT_DATA){ + AwsFrameInfo * info = (AwsFrameInfo*)arg; + String msg = ""; + if(info->final && info->index == 0 && info->len == len){ + //the whole message is in a single frame and we got all of it's data + + ///////////////// + // receive RPC // + ///////////////// + + ws_root = cJSON_Parse((char *)data); + // int procedure = cJSON_GetObjectItem(ws_root,"procedure")->valueint; + // wlog_i("ws procedure: %d",procedure); + on_rpc_in(ws_root); + // switch(procedure){ + // case(NOTE_ON): + // // play note + // break; + // default: + // wlog_i("RPC received unknown procedure code: %d", procedure); + // break; + // } + + ///////////////// + ////// end ////// + ///////////////// + + // Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len); + // if(info->opcode == WS_TEXT){ + // for(size_t i=0; i < info->len; i++) { + // msg += (char) data[i]; + // } + // } else { + // char buff[3]; + // for(size_t i=0; i < info->len; i++) { + // sprintf(buff, "%02x ", (uint8_t) data[i]); + // msg += buff ; + // } + // } + // Serial.printf("%s\n",msg.c_str()); + + // if(info->opcode == WS_TEXT) + // client->text("I got your text message"); + // else + // client->binary("I got your binary message"); + // } else { + // //message is comprised of multiple frames or the frame is split into multiple packets + // if(info->index == 0){ + // if(info->num == 0) + // Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + // Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len); + // } + + // Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); + + // if(info->opcode == WS_TEXT){ + // for(size_t i=0; i < len; i++) { + // msg += (char) data[i]; + // } + // } else { + // char buff[3]; + // for(size_t i=0; i < len; i++) { + // sprintf(buff, "%02x ", (uint8_t) data[i]); + // msg += buff ; + // } + // } + // Serial.printf("%s\n",msg.c_str()); + // if((info->index + len) == info->len){ + // Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len); + // if(info->final){ + // Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + // if(info->message_opcode == WS_TEXT) + // client->text("I got your text message"); + // else + // client->binary("I got your binary message"); + // } + // } + } + } +} + +void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + Serial.println("UploadStart: " + filename); + } + for(size_t i=0; isend(500); + Update.abort(); + return; + } + Serial.print("."); + if(index + len == total){ + Serial.println("."); + if(Update.end()){ + if(Update.isFinished()){ + Serial.println("success\n\n"); + request->send(204); + sdmmc_host_deinit(); + delay(1000); + ESP.restart(); + } else { + request->send(500); + Serial.println("not finished"); + } + } else { + request->send(500); + Serial.println("failed"); + } + request->send(204); + } +} + +uint8_t *voice_config_json = NULL; + +void handleUpdateVoiceConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + if(index==0){ + //start + voice_config_json = (uint8_t*)ps_malloc(total + 1); + if(!voice_config_json){ + wlog_i("failed to malloc for json"); + } + } + //always + for(int i=0;isend(200, "text/plain", "all done voice config update"); + } +} + +uint8_t *pin_config_json = NULL; + +void handleUpdatePinConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + if(index==0){ + //start + pin_config_json = (uint8_t*)ps_malloc(total); + if(!pin_config_json){ + wlog_i("failed to malloc for json"); + } + } + //always + for(int i=0;isend(200, "text/plain", "all done pin config update"); + } +} + +int w_bytes_read = 0; +char w_name[21]; +int w_voice; +int w_note; +int w_size; +size_t w_start_block; + +void handleWav(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + if(index==0){ + //start + AsyncWebHeader* size_string = request->getHeader("size"); + sscanf(size_string->value().c_str(), "%d", &w_size); + AsyncWebHeader* name = request->getHeader("name"); + strcpy(&w_name[0], name->value().c_str()); + AsyncWebHeader* voice_string = request->getHeader("voice"); + sscanf(voice_string->value().c_str(), "%d", &w_voice); + AsyncWebHeader* note_string = request->getHeader("note"); + sscanf(note_string->value().c_str(), "%d", &w_note); + w_start_block = find_gap_in_file_system(w_size); + } + //always + write_wav_to_emmc(data, w_start_block, len); + w_bytes_read += len; + if(index + len == total){ + //done + close_wav_to_emmc(); + add_wav_to_file_system(&w_name[0],w_voice,w_note,w_start_block,total); + request->send(200, "text/plain", "done upload wav"); + } +} + +char r_name[24]; +int r_voice; +int r_note; +int r_layer; +cJSON *r_rack_json; +size_t r_start_block; + +void handleRack(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + if(index==0){ + //start + strcpy(&r_name[0], request->getHeader("name")->value().c_str()); + sscanf(request->getHeader("voice")->value().c_str(), "%d", &r_voice); + sscanf(request->getHeader("note")->value().c_str(), "%d", &r_note); + sscanf(request->getHeader("layer")->value().c_str(), "%d", &r_layer); + // log_i("\nNEW REQUEST %u %ubytes\n",r_layer, total); + r_rack_json = cJSON_Parse(request->getHeader("rack-json")->value().c_str()); + r_start_block = find_gap_in_file_system(total); + } + //always + write_wav_to_emmc(data, r_start_block, len); + w_bytes_read += len; + if(index + len == total){ + //done + close_wav_to_emmc(); + add_rack_to_file_system(&r_name[0],r_voice,r_note,r_start_block,total,r_layer,r_rack_json); + free(r_rack_json); + } +} + +int f_bytes_read = 0; + +char f_firmware_slot; +size_t f_gui_size; +size_t f_firmware_size; +char f_gui_name[24]; +char f_firmware_name[24]; + +void handleNewFirmware(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + //once + if(index==0){ + AsyncWebHeader* firmware_slot_string = request->getHeader("slot-index"); + sscanf(firmware_slot_string->value().c_str(), "%d", &f_firmware_slot); + AsyncWebHeader* firmware_size_string = request->getHeader("firmware-size"); + sscanf(firmware_size_string->value().c_str(), "%d", &f_firmware_size); + AsyncWebHeader* firmware_name_string = request->getHeader("firmware-name"); + strcpy(&f_firmware_name[0], firmware_name_string->value().c_str()); + log_i("firmware size %u, firmware name %s",f_firmware_size,f_firmware_name); + + struct firmware_t *f = get_firmware_slot(f_firmware_slot); + f->length = f_firmware_size; + strcpy(f->name, firmware_name_string->value().c_str()); + } + //always + if((f_firmware_size > MAX_FIRMWARE_SIZE) || (f_gui_size > MAX_WEBSITE_SIZE)){ + request->send(400, "text/plain", "FILES TOO LARGE"); + return; + } + + Serial.print("."); + write_firmware_to_emmc(f_firmware_slot, data, len); + f_bytes_read += len; + + //done + if(index + len == total){ + close_firmware_to_emmc(f_firmware_slot); + log_i("done"); + log_i("wrote %u bytes",f_bytes_read); + f_bytes_read = 0; + request->send(200, "text/plain", "all done firmware"); + } +} + +int rf_bytes_read = 0; +// char rf_firmware_slot; +// size_t rf_gui_size; +size_t rf_firmware_size; +// char rf_gui_name[24]; +// char rf_firmware_name[24]; + + +void handleNewRecoveryFirmware(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + //once + if(index==0){ + // AsyncWebHeader* firmware_slot_string = request->getHeader("slot-index"); + // sscanf(firmware_slot_string->value().c_str(), "%d", &f_firmware_slot); + AsyncWebHeader* firmware_size_string = request->getHeader("firmware-size"); + sscanf(firmware_size_string->value().c_str(), "%d", &rf_firmware_size); + // AsyncWebHeader* firmware_name_string = request->getHeader("firmware-name"); + // strcpy(&f_firmware_name[0], firmware_name_string->value().c_str()); + // log_i("firmware size %u, firmware name %s",f_firmware_size,f_firmware_name); + + // struct firmware_t *f = get_firmware_slot(-1); + // f->length = rf_firmware_size; + // strcpy(f->name, firmware_name_string->value().c_str()); + } + //always + if(rf_firmware_size > MAX_FIRMWARE_SIZE){ + request->send(400, "text/plain", "FILES TOO LARGE"); + return; + } + + Serial.print("."); + write_recovery_firmware_to_emmc(data, len); + rf_bytes_read += len; + + //done + if(index + len == total){ + close_recovery_firmware_to_emmc(total); + log_i("done"); + log_i("wrote %u bytes",rf_bytes_read); + rf_bytes_read = 0; + request->send(200, "text/plain", "all done recovery firmware"); + } +} + +void handleNewGUI(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + //once + if(index==0){ + log_i("handle new GUI"); + AsyncWebHeader* firmware_slot_string = request->getHeader("slot-index"); + sscanf(firmware_slot_string->value().c_str(), "%d", &f_firmware_slot); + AsyncWebHeader* gui_size_string = request->getHeader("gui-size"); + sscanf(gui_size_string->value().c_str(), "%d", &f_gui_size); + AsyncWebHeader* gui_name_string = request->getHeader("gui-name"); + log_i("strcpy : %s",gui_name_string->value().c_str()); + strcpy(&f_gui_name[0], gui_name_string->value().c_str()); + log_i("gui %s is %u bytes",f_gui_name,f_gui_size); + + struct website_t *w = get_website_slot(f_firmware_slot); + w->length = f_gui_size; + strcpy(w->name, gui_name_string->value().c_str()); + } + //always + if((f_firmware_size > MAX_FIRMWARE_SIZE) || (f_gui_size > MAX_WEBSITE_SIZE)){ + request->send(400, "text/plain", "FILES TOO LARGE"); + return; + } + + Serial.print("."); + write_website_to_emmc(f_firmware_slot, data, len); + f_bytes_read += len; + + //done + if(index + len == total){ + close_website_to_emmc(f_firmware_slot); + log_i("done"); + log_i("wrote %u bytes",f_bytes_read); + f_bytes_read = 0; + request->send(200, "text/plain", "all done"); + } +} + +void handleFsjson(AsyncWebServerRequest *request){ + char *json = print_fs_json(); + size_t size = strlen(json); + request->send("text/html", size, [size,json](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + size_t toWrite = min(size - index, maxLen); + memcpy(buffer, json + index, toWrite); + if(index + toWrite == size){ + free(json); + } + return toWrite; + }); +} + +void handleEmmcGUI(AsyncWebServerRequest *request){ + size_t size = website_lut[metadata.current_website_index].length; + size_t start_block = website_lut[metadata.current_website_index].start_block; + request->send("text/html", size, [size,start_block](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + size_t toWrite = min(size - index, maxLen); + size_t written = get_website_chunk(start_block, toWrite, buffer, size); + // memcpy(buffer, buf, toWrite); + return written; + }); +} + +void handleMain(AsyncWebServerRequest *request){ + size_t size = sizeof(MAIN_page) / sizeof(char); + request->send("text/html", size, [size](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { + size_t toWrite = min(size - index, maxLen); + memcpy(buffer, MAIN_page + index, toWrite); + return toWrite; + }); +} + +void handleRPC(AsyncWebServerRequest *request){ + cJSON* json = cJSON_Parse(request->getHeader("json")->value().c_str()); + on_rpc_in(json); + // char *res = on_rpc_in(json); + request->send(200, "text/html", "done"); +} + +void handleBootFromEmmc(AsyncWebServerRequest *request){ + char index = 0; + AsyncWebHeader* firmware_slot_string = request->getHeader("index"); + sscanf(firmware_slot_string->value().c_str(), "%d", &index); + request->send(204); + bootFromEmmc(index); +} + +void handleMulti(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + log_i("UploadStart: %s",filename.c_str()); + } + log_i("%s",filename.c_str()); + if(final){ + request->send(204); + log_i("done %s",filename.c_str()); + } +} + +void _server_pause(){ + ws.closeAll(); + server.end(); + WiFi.mode(WIFI_OFF); +} + +void server_begin() { + Serial.println("Configuring access point..."); + + WiFi.mode(WIFI_AP); //Access Point mode + WiFi.softAP("ESPWebServer", "12345678"); + + IPAddress myIP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(myIP); + + server.on( + "/", + HTTP_GET, + [](AsyncWebServerRequest *request){ + request->send(200, "text/html", MAIN_page); + } + ); + + server.on( + "/main", + HTTP_GET, + handleMain + ); + + server.on( + "/emmc", + HTTP_GET, + handleEmmcGUI + ); + + server.on( + "/fsjson", + HTTP_GET, + handleFsjson + ); + + server.on( + "/update", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleUpdate + ); + + server.on( + "/addwav", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleWav + ); + + server.on( + "/updateVoiceConfig", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleUpdateVoiceConfig + ); + + server.on( + "/updatePinConfig", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleUpdatePinConfig + ); + + server.on( + "/addfirmware", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleNewFirmware + ); + + server.on( + "/updaterecoveryfirmware", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleNewRecoveryFirmware + ); + + server.on( + "/addgui", + HTTP_POST, + [](AsyncWebServerRequest * request){request->send(204);}, + NULL, + handleNewGUI + ); + + server.on( + "/bootFromEmmc", + HTTP_GET, + handleBootFromEmmc + ); + + server.on( + "/multi", + HTTP_POST, + [](AsyncWebServerRequest *request){request->send(204);}, + handleMulti + ); + + server.on( + "/addrack", + HTTP_POST, + [](AsyncWebServerRequest *request){request->send(204);}, + NULL, + handleRack + ); + + server.on( + "/rpc", + HTTP_GET, + handleRPC + ); + + ws.onEvent(onWsEvent); + server.addHandler(&ws); + + server.begin(); + Serial.println("Server started"); +} + +void server_pause(void){ + _server_pause(); +} + +void server_resume(void){ + server_begin(); +} diff --git a/src/server.h b/src/server.h new file mode 100644 index 0000000..530193c --- /dev/null +++ b/src/server.h @@ -0,0 +1,7 @@ +#ifndef SERVER_H +#define SERVER_H + +void server_pause(); +void server_resume(); + +#endif \ No newline at end of file diff --git a/src/touch_test.c b/src/touch_test.c new file mode 100644 index 0000000..dfa9206 --- /dev/null +++ b/src/touch_test.c @@ -0,0 +1,181 @@ +/* Touch Pad Interrupt Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" + +#include "driver/touch_pad.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +#include "freertos/queue.h" + +#include "esp32-hal-log.h" + +QueueHandle_t midi_queue; + +static const char* TAG = "Touch pad"; +#define TOUCH_THRESH_NO_USE (0) +#define TOUCH_THRESH_PERCENT (80) +#define TOUCHPAD_FILTER_TOUCH_PERIOD (10) + +static bool s_pad_activated[TOUCH_PAD_MAX]; +static uint32_t s_pad_init_val[TOUCH_PAD_MAX]; + +/* + Read values sensed at all available touch pads. + Use 2 / 3 of read value as the threshold + to trigger interrupt when the pad is touched. + Note: this routine demonstrates a simple way + to configure activation threshold for the touch pads. + Do not touch any pads when this routine + is running (on application start). + */ + +int pads[4] = {1,7,8,9}; + +static void tp_example_set_thresholds(void) +{ + uint16_t touch_value; + for (int i = 0; i<4; i++) { + //read filtered value + touch_pad_read_filtered(pads[i], &touch_value); + s_pad_init_val[i] = touch_value; + ESP_LOGI(TAG, "test init: touch pad [%d] val is %d", pads[i], touch_value); + //set interrupt threshold. + ESP_ERROR_CHECK(touch_pad_set_thresh(pads[i], touch_value * 2 / 3)); + + } +} + +struct midi_event_t { + uint8_t code; + uint8_t note; + uint8_t velocity; + uint8_t channel; +}; + +struct midi_event_t midi_event; + +void play_sound(int i) +{ + midi_event.code = 8; + midi_event.channel = 0; + midi_event.velocity = 127; + midi_event.note = i==1 ? 40 : i==7 ? 41 : i==8 ? 42 : i==9 ? 43 : 0; + xQueueSendToBack(midi_queue,(void *) &midi_event, portMAX_DELAY); +} + +void play_sound_ext(int i){ + midi_event.code = 8; + midi_event.channel = 0; + midi_event.velocity = 127; + midi_event.note = i; + xQueueSendToBack(midi_queue,(void *) &midi_event, portMAX_DELAY); +} + +/* + Check if any of touch pads has been activated + by reading a table updated by rtc_intr() + If so, then print it out on a serial monitor. + Clear related entry in the table afterwards + + In interrupt mode, the table is updated in touch ISR. + + In filter mode, we will compare the current filtered value with the initial one. + If the current filtered value is less than 80% of the initial value, we can + regard it as a 'touched' event. + When calling touch_pad_init, a timer will be started to run the filter. + This mode is designed for the situation that the pad is covered + by a 2-or-3-mm-thick medium, usually glass or plastic. + The difference caused by a 'touch' action could be very small, but we can still use + filter mode to detect a 'touch' event. + */ + +int DEBOUNCE = 100000; +int last_hit_time[4] = {0,0,0,0}; +bool in_play[4] = {false,false,false,false}; +static void tp_example_read_task(void *pvParameter) +{ + while (1) { + touch_pad_intr_enable(); + for (int i = 0; i < 4; i++) { + int now = esp_timer_get_time(); + if( in_play[i] && ( ( last_hit_time[i] + DEBOUNCE ) < now ) ) + { + in_play[i]=false; + s_pad_activated[i] = false; + } + if(s_pad_activated[i] && (in_play[i] == false) ) + { + last_hit_time[i] = now; + in_play[i]=true; + play_sound(pads[i]); + ESP_LOGI(TAG, "T%d activated!", pads[i]); + s_pad_activated[i] = false; + } + } + + vTaskDelay(2 / portTICK_PERIOD_MS); + } +} + +/* + Handle an interrupt triggered when a pad is touched. + Recognize what pad has been touched and save it in a table. + */ +static void tp_example_rtc_intr(void * arg) +{ + uint32_t pad_intr = touch_pad_get_status(); + //clear interrupt + touch_pad_clear_status(); + for (int i = 0; i < 4; i++) { + if ((pad_intr >> pads[i]) & 0x01) { + isr_log_i("%u",pad_intr); + s_pad_activated[i] = true; + } + } +} + +/* + * Before reading touch pad, we need to initialize the RTC IO. + */ +static void tp_example_touch_pad_init() +{ + // for (int i = 0;i< TOUCH_PAD_MAX;i++) { + for (int i = 0;i< 4;i++) { + //init RTC IO and mode for touch pad. + touch_pad_config(pads[i], TOUCH_THRESH_NO_USE); + } +} + +void touch_test() +{ + // Initialize touch pad peripheral, it will start a timer to run a filter + ESP_LOGI(TAG, "Initializing touch pad"); + touch_pad_init(); + // If use interrupt trigger mode, should set touch sensor FSM mode at 'TOUCH_FSM_MODE_TIMER'. + touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); + // Set reference voltage for charging/discharging + // For most usage scenarios, we recommend using the following combination: + // the high reference valtage will be 2.7V - 1V = 1.7V, The low reference voltage will be 0.5V. + touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_0V5); + // touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V); + // Init touch pad IO + tp_example_touch_pad_init(); + // Initialize and start a software filter to detect slight change of capacitance. + touch_pad_filter_start(TOUCHPAD_FILTER_TOUCH_PERIOD); + // Set thresh hold + tp_example_set_thresholds(); + // Register touch interrupt ISR + touch_pad_isr_register(tp_example_rtc_intr, NULL); + // Start a task to show what pads have been touched + xTaskCreate(&tp_example_read_task, "touch_pad_read_task", 2048, NULL, 5, NULL); +} diff --git a/src/wav_player.c b/src/wav_player.c new file mode 100644 index 0000000..3457f97 --- /dev/null +++ b/src/wav_player.c @@ -0,0 +1,680 @@ +#include +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "driver/i2s.h" +#include "esp32-hal-log.h" +#include "esp32-hal-cpu.h" +#include "file_system.h" +#include "midi_in.h" +#include "file_system.h" +#include "ws_log.h" +#include "rpc.h" +#include "wav_player.h" + +static const char* TAG = "wav_player"; + +#define wav_player_queue_SIZE 80 +#define BLOCK_SIZE 512 + +#define BLOCKS_PER_READ 6 +#define BYTES_PER_READ (BLOCKS_PER_READ * BLOCK_SIZE) +#define SAMPLES_PER_READ (BYTES_PER_READ / sizeof(int16_t)) + +#define DAC_BUFFER_SIZE_IN_SAMPLES 256 +#define DAC_BUFFER_SIZE_IN_BYTES ( DAC_BUFFER_SIZE_IN_SAMPLES * sizeof(int16_t) ) +#define LOOPS_PER_BUFFER ( BYTES_PER_READ / DAC_BUFFER_SIZE_IN_BYTES ) + +#define NUM_BUFFERS 18 +// #define NUM_BUFFERS 20 +// #define DAMPEN_BITS 2 +#define DAMPEN_BITS 1 +#define USE_SQRT_SCALE 0 +#define USE_VOLUME 0 +#define USE_VOLUME_FP 1 +#define USE_VOLUME_FP_SQRT 0 + +// #define FADE_OUT_NUM_LOOPS 30 +// #define DAMPEN_PER_FADE_OUT_LOOP 5 +// #define FADE_OUT_NUM_LOOPS 20 +// #define DAMPEN_PER_FADE_OUT_LOOP 7 +#define FADE_OUT_NUM_LOOPS 15 +#define DAMPEN_PER_FADE_OUT_LOOP 8 +// #define FADE_OUT_NUM_LOOPS 10 +// #define DAMPEN_PER_FADE_OUT_LOOP 13 +#define LOG_PERFORMANCE 0 + +#define MAX_READS_PER_LOOP 4 +// #define MAX_READS_PER_LOOP 3 + +struct buf_t { + struct wav_lu_t wav_data; + struct wav_player_event_t wav_player_event; + int16_t *buffer_a; + int16_t *buffer_b; + size_t read_block; + size_t wav_position; + size_t size; + uint8_t volume; + uint8_t voice; + uint8_t head_position; + uint8_t fade :1; + uint8_t free :1; + uint8_t done :1; + uint8_t full :1; + uint8_t current_buf :1; +}; + +esp_err_t ret; +QueueHandle_t wav_player_queue; +QueueHandle_t dac_queue_handle; +i2s_event_t dac_event; + +// declare local function prototypes +void wav_player_pause(void); + +// declare function prototypes from emmc.c +esp_err_t emmc_read(void *dst, size_t start_sector, size_t sector_count); + +// declare function prototypes from dac.c +esp_err_t dac_write(const void *src, size_t size, size_t *bytes_written); +void dac_pause(void); +void dac_resume(void); + +int16_t sqrtLUT[128]; + +struct buf_t bufs[NUM_BUFFERS]; +int16_t *output_buf; +struct wav_player_event_t wav_player_event; +int new_midi = 0; +int new_midi_buf = -1; + +float float_lut[128]; + +int current_bank = 0; +uint32_t tmp32; +int16_t tmp16; + +void current_bank_up(void) +{ + // current_bank += (current_bank < 15); + if(current_bank < 15) + current_bank++; +} + +void current_bank_down(void) +{ + // current_bank -= (current_bank > 0); + if(current_bank > 0) + current_bank--; +} + +void init_buffs(void) +{ + output_buf = (int16_t *)malloc( DAC_BUFFER_SIZE_IN_BYTES ); + if(output_buf==NULL) + { + log_e("failed to alloc output buf"); + } + for(uint8_t i=0; i bufs[candidate].wav_position) + ) + { + // wlog_i("checked wav position"); + candidate = i; + } + } + } + // wlog_i("pruned buf %d",candidate); + return candidate; +} + +void wav_player_task(void* pvParameters) +{ + static size_t bytes_to_dma = 0; + size_t base_index; + int16_t *buf_pointer; + int num_reads = 0; + // int new_midi = 0; + // int new_midi_buf = -1; + bool abort_note = false; + + if((wav_player_queue = xQueueCreate(wav_player_queue_SIZE, sizeof(struct wav_player_event_t)))==pdFAIL) + { + log_e("failed to creat wav_player_queue"); + } + + log_i("wav player running on core %u",xPortGetCoreID()); + + for(;;) + { + // check for midi events and add try to place them into the buffers + if(xQueueReceive(wav_player_queue, &wav_player_event, 0)) + { + abort_note = false; + // fetch the file + struct wav_lu_t new_wav = get_file_t_from_lookup_table(wav_player_event.voice, wav_player_event.note, wav_player_event.velocity); + // check that there is a wav file there + if(new_wav.empty == 1) abort_note = 1; + // secret midi note to trigger pause + if(wav_player_event.voice == 0xFF) wav_player_pause(); + // check if it is a disguised note-off + if(wav_player_event.code == MIDI_NOTE_ON && wav_player_event.velocity == 0) wav_player_event.code = MIDI_NOTE_OFF; + // check if this is a retrigger + if(wav_player_event.code == MIDI_NOTE_ON && !abort_note && wav_player_event.velocity != 0) + { + for(int b=0;b> (15 + DAMPEN_BITS)); + if(bufs[i].fade) + { + if(bufs[i].volume > 0) + { + bufs[i].volume--; + } + else + { + bufs[i].done = true; + } + } + } + } + break; + case RESPONSE_FIXED: + case RESPONSE_LINEAR: + { + for(int s=0; s> DAMPEN_BITS ) ; + if(bufs[i].fade) + { + if(bufs[i].volume > 0) + { + bufs[i].volume--; + } + else + { + bufs[i].done = true; + } + } + } + } + } + // if(USE_SQRT_SCALE) + // { + // for(int s=0; s> (15 + DAMPEN_BITS)); + // if(bufs[i].fade) + // bufs[i].volume -= ( bufs[i].volume > 0 ); + // } + // } + // else if(USE_VOLUME) + // { + // for(int s=0; s> (7 + DAMPEN_BITS) ) ; + // } + // } + // else if(USE_VOLUME_FP) + // { + // int min = 1000; + // for(int s=0; s> DAMPEN_BITS ) ; + // if(bufs[i].fade) + // { + // if(bufs[i].volume > 0) + // { + // bufs[i].volume--; + // } + // else + // { + // bufs[i].done = 1; + // } + // } + // } + // } + // this one makes no sense does it? + // else if(USE_VOLUME_FP_SQRT) + // { + // for(int s=0; s> (15 + DAMPEN_BITS)); + // } + // } + // else + // { + // for(int s=0; s> DAMPEN_BITS); + // } + // } + + // incriment that buffers position + bufs[i].head_position++; + bufs[i].wav_position += to_write * sizeof(int16_t); + // if the head has reached the end + if(bufs[i].head_position >= LOOPS_PER_BUFFER) + { + if(bufs[i].full == 0) wlog_i("buffer underrun!"); + bufs[i].full = 0; + bufs[i].current_buf = bufs[i].current_buf == 0 ? 1 : 0; + bufs[i].head_position = 0; + } + // if the wav is done + if(bufs[i].wav_position >= bufs[i].size) + { + if(bufs[i].wav_data.play_back_mode == LOOP) + { + new_midi = 1; + new_midi_buf = i; + bufs[i].full = 0; + // bufs[i].current_buf = 0; + bufs[i].head_position = 0; + bufs[i].read_block = bufs[i].wav_data.start_block; + bufs[i].wav_position = 0; + } + else + { + bufs[i].done = 1; + } + } + } + } + ret = dac_write((int16_t *)output_buf, DAC_BUFFER_SIZE_IN_BYTES, &bytes_to_dma); + if(ret != ESP_OK){ + log_i("i2s write error %s", esp_err_to_name(ret)); + } + // clear the output buffer + for(int i=0;i + +int log_printf(const char *fmt, ...); +void sendWSLog(char* msg); + +int w_log_printf(const char *format, ...) +{ + static char loc_buf[64]; + char * temp = loc_buf; + int len; + va_list arg; + va_list copy; + va_start(arg, format); + va_copy(copy, arg); + len = vsnprintf(NULL, 0, format, arg); + va_end(copy); + if(len >= sizeof(loc_buf)){ + temp = (char*)malloc(len+1); + if(temp == NULL) { + return 0; + } + } + vsnprintf(temp, len+1, format, arg); + + log_printf(temp); + sendWSLog(temp); +// #if !CONFIG_DISABLE_HAL_LOCKS +// if(_uart_bus_array[s_uart_debug_nr].lock){ +// xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY); +// ets_printf("%s", temp); +// xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock); +// } else { +// ets_printf("%s", temp); +// } +// #else +// ets_printf("%s", temp); +// #endif + va_end(arg); + if(len >= sizeof(loc_buf)){ + free(temp); + } + return len; +} diff --git a/src/ws_log.h b/src/ws_log.h new file mode 100644 index 0000000..8414bad --- /dev/null +++ b/src/ws_log.h @@ -0,0 +1,69 @@ +#ifndef WS_LOG_H +#define WS_LOG_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "esp32-hal-log.h" + + +int w_log_printf(const char *fmt, ...); + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE +#define wlog_v(format, ...) w_log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#define isr_wlog_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#else +#define wlog_v(format, ...) +#define isr_wlog_v(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG +#define wlog_d(format, ...) w_log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#define isr_wlog_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#else +#define wlog_d(format, ...) +#define isr_wlog_d(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +#define wlog_i(format, ...) w_log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#define isr_wlog_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#else +#define wlog_i(format, ...) +#define isr_wlog_i(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN +#define wlog_w(format, ...) w_log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#define isr_wlog_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#else +#define wlog_w(format, ...) +#define isr_wlog_w(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#define wlog_e(format, ...) w_log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_wlog_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#else +#define wlog_e(format, ...) +#define isr_wlog_e(format, ...) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE +#define wlog_n(format, ...) w_log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_wlog_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#else +#define wlog_n(format, ...) +#define isr_wlog_n(format, ...) +#endif + +#undef log_i +#define log_i wlog_i + + +#ifdef __cplusplus +} +#endif +#endif /* __WS_LOG_H__ */ diff --git a/src/wvr_0.3.cpp b/src/wvr_0.3.cpp new file mode 100644 index 0000000..18871dc --- /dev/null +++ b/src/wvr_0.3.cpp @@ -0,0 +1,113 @@ +#include "cJSON.h" +#include "esp32-hal-log.h" +#include "esp32-hal-cpu.h" +#include "esp32-hal-gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include +#include "Arduino.h" +#include "wvr_pins.h" +#include "button.h" +#include "midi_in.h" +#include "ws_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "wvr_0.3.h" +#include "file_system.h" + +struct wav_lu_t **wav_lut; + +void server_begin(void); +extern "C" void emmc_init(void); +extern "C" void dac_init(void); +extern "C" void midi_init(void); +extern "C" void wav_player_start(void); +extern "C" void encoder_init(void); +extern "C" void touch_test(void); +extern "C" void pot_init(void); +extern "C" void rpc_init(void); +void bootFromEmmc(int index); +void bootIntoRecoveryMode(void); +int check_for_recovery_mode(void); +void dev_board_init(); +void rgb_init(void); +void neopixel_test(void); +void mkr_init(void); +void gpio_init(void); +void midi_parser_init(void); + +size_t heap_remaining = esp_get_free_heap_size(); + +void wifi_log_boot_stage(void){ + static int done = 0; + if(!done){ + done = 1; + wlog_i("wlog_i"); + log_i("log_i"); + wlog_i("firmware version %s", VERSION_CODE); + } +} + +void logSize(char* name){ + size_t free = esp_get_free_heap_size(); + Serial.printf("after %s, remaining: %u, used:%u\n", name, free, heap_remaining - free); + heap_remaining = free; +} + +void logRam(){ + Serial.printf("Total heap: %d\n", ESP.getHeapSize()); + Serial.printf("Free heap: %d\n", ESP.getFreeHeap()); + Serial.printf("Total PSRAM: %d\n", ESP.getPsramSize()); + Serial.printf("Free PSRAM: %d\n", ESP.getFreePsram()); +} + +void wvr_init() { + Serial.begin(115200); + logRam(); + log_i("arduino setup running on core %u",xPortGetCoreID()); + log_i("\nwvr starting up \n\n*** VERSION %s ***\n\n",VERSION_CODE); + + cJSON_Hooks memoryHook; + memoryHook.malloc_fn = ps_malloc; + memoryHook.free_fn = free; + cJSON_InitHooks(&memoryHook); + logSize("begin"); + + emmc_init(); + logSize("emmc"); + + file_system_init(); + logSize("file system"); + + int ret = check_for_recovery_mode(); + if(!ret) + { + bootIntoRecoveryMode(); + return; + } + + dac_init(); + logSize("dac"); + + midi_init(); + logSize("midi"); + + midi_parser_init(); + logSize("midi parser"); + + wav_player_start(); + logSize("wav player"); + + server_begin(); + logSize("server"); + + button_init(); + logSize("button"); + + rpc_init(); + logSize("rpc"); + + logRam(); +} \ No newline at end of file diff --git a/src/wvr_0.3.h b/src/wvr_0.3.h new file mode 100644 index 0000000..f69f8df --- /dev/null +++ b/src/wvr_0.3.h @@ -0,0 +1,15 @@ +#ifndef WVR_0_3_H +#define WVR_0_3_H +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define VERSION_CODE "0.2.18" +void wvr_init(); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/wvr_pins.h b/src/wvr_pins.h new file mode 100644 index 0000000..8594ae1 --- /dev/null +++ b/src/wvr_pins.h @@ -0,0 +1,162 @@ +#ifndef WVR_PINS_H +#define WVR_PINS_H +#ifdef __cplusplus +extern "C" +{ +#endif + +#define D0_GPIO GPIO_NUM_3 +#define D1_GPIO GPIO_NUM_1 +#define D2_GPIO GPIO_NUM_21 +#define D3_GPIO GPIO_NUM_19 +#define D4_GPIO GPIO_NUM_18 +#define D5_GPIO GPIO_NUM_5 +#define D6_GPIO GPIO_NUM_0 +#define D7_GPIO GPIO_NUM_36 // no pullup +#define D8_GPIO GPIO_NUM_39 // no pullup +#define D9_GPIO GPIO_NUM_34 // no pullup +#define D10_GPIO GPIO_NUM_35 // no pullup +#define D11_GPIO GPIO_NUM_32 +#define D12_GPIO GPIO_NUM_33 +#define D13_GPIO GPIO_NUM_27 + +static const gpio_num_t gpio_pins[14] = { + D0_GPIO, + D1_GPIO, + D2_GPIO, + D3_GPIO, + D4_GPIO, + D5_GPIO, + D6_GPIO, + D7_GPIO, // no pullup + D8_GPIO, // no pullup + D9_GPIO, // no pullup + D10_GPIO, // no pullup + D11_GPIO, + D12_GPIO, + D13_GPIO +}; + +#define D0 3 +#define D1 1 +#define D2 21 +#define D3 19 +#define D4 18 +#define D5 5 +#define D6 0 +#define D7 36 // no pullup +#define D8 39 // no pullup +#define D9 34 // no pullup +#define D10 35 // no pullup +#define D11 32 +#define D12 33 +#define D13 27 + +static const uint8_t wvr_pins[14] = { + D0, + D1, + D2, + D3, + D4, + D5, + D6, + D7, // no pullup + D8, // no pullup + D9, // no pullup + D10, // no pullup + D11, + D12, + D13 +}; + + +#define A0 D6 +#define A1 D7 +#define A2 D8 +#define A3 D9 +#define A4 D10 +#define A5 D11 +#define A6 D12 +#define A7 D13 + +#define T0 D6 +#define T1 D11 +#define T2 D12 +#define T3 D13 + +#define PAD1 D13 +#define PAD2 D12 +#define PAD3 D11 +#define PAD4 D10 +#define PAD5 D9 +#define PAD6 D8 +#define PAD7 D7 +#define PAD8 D6 +#define PAD9 D5 +#define PAD10 D4 +#define PAD11 D3 +#define PAD12 D2 +#define PAD13 D1 +#define PAD14 D0 + +// 34 +// 35 +// 36 +// 39 + +// int wvr_get_pin_name(int pin){ +// switch (pin) +// { +// case D0: +// return 0; +// break; +// case D1: +// return 1; +// break; +// case D2: +// return 2; +// break; +// case D3: +// return 3; +// break; +// case D4: +// return 4; +// break; +// case D5: +// return 5; +// break; +// case D6: +// return 6; +// break; +// case D7: +// return 7; +// break; +// case D8: +// return 8; +// break; +// case D9: +// return 9; +// break; +// case D10: +// return 10; +// break; +// case D11: +// return 11; +// break; +// case D12: +// return 12; +// break; +// case D13: +// return 13; +// break; +// default: +// return -1; +// break; +// } +// } +// D7,D8,D9,D10 (34,35,36,39) INPUT ONLY, No PULLUPS + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/src/wvr_ui.h b/src/wvr_ui.h new file mode 100644 index 0000000..76e5af4 --- /dev/null +++ b/src/wvr_ui.h @@ -0,0 +1,3 @@ +const char MAIN_page[] PROGMEM = R"=====( +Waver UI
+)====="; \ No newline at end of file diff --git a/wvr.sh b/wvr.sh new file mode 100755 index 0000000..dac8b82 --- /dev/null +++ b/wvr.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +if [ "$1" == "site" ]; then + cd react && \ + npm run build && \ + cd ../.. && \ + arduino-cli compile -e --build-property build.code_debug=5 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation, uploading" && \ + curl --data-binary "@/Users/temporary/Documents/Arduino/wvr_0.3/build/esp32.esp32.esp32wrover/wvr_0.3.ino.bin" http://192.168.4.1/update --header "content-type:text/plain" ; \ + echo "done" +elif [ "$1" == "code" ]; then + echo "compiling" + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=5 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation, uploading" && \ + curl --data-binary "@/Users/temporary/Documents/Arduino/wvr_0.3/build/esp32.esp32.esp32wrover/wvr_0.3.ino.bin" http://192.168.4.1/update --header "content-type:text/plain" ; \ + echo "done" +elif [ "$1" == "codenodev" ]; then + echo "compiling" + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=1 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation, uploading" && \ + curl --data-binary "@/Users/temporary/Documents/Arduino/wvr_0.3/build/esp32.esp32.esp32wrover/wvr_0.3.ino.bin" http://192.168.4.1/update --header "content-type:text/plain" ; \ + echo "done" +elif [ "$1" == "compile" ]; then + echo "compiling" + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=5 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation" +elif [ "$1" == "compilenodev" ]; then + echo "compiling" + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=0 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation" +elif [ "$1" == "compilewithsite" ]; then + echo "compiling website" + cd react && \ + npm run build && \ + cd ../.. && \ + echo "compiling binary" + arduino-cli compile -e --build-property build.code_debug=5 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation" +elif [ "$1" == "compilewithsitenodev" ]; then + echo "compiling website" + cd react && \ + npm run build && \ + cd ../.. && \ + echo "compiling binary" + arduino-cli compile -e --build-property build.code_debug=0 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation" +elif [ "$1" == "flash" ]; then + cd .. && \ + echo "uploading" && \ + curl --data-binary "@/Users/temporary/Documents/Arduino/wvr_0.3/build/esp32.esp32.esp32wrover/wvr_0.3.ino.bin" http://192.168.4.1/update --header "content-type:text/plain" ; \ + echo "done" +elif [ "$1" == "ftdi" ]; then + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=5 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation, uploading" && \ + arduino-cli upload -p /dev/cu.usbserial-A50285BI --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done" +elif [ "$1" == "ftdinodev" ]; then + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=1 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation, uploading" && \ + arduino-cli upload -p /dev/cu.usbserial-A50285BI --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done" +elif [ "$1" == "test" ]; then + echo "compiling" + cd .. && \ + arduino-cli compile -e --build-property build.code_debug=5 --fqbn esp32:esp32:esp32wrover wvr_0.3 && \ + echo "done compilation, uploading" && \ + curl --data-binary "@/Users/temporary/Documents/Arduino/wvr_0.3/build/esp32.esp32.esp32wrover/wvr_0.3.ino.bin" http://192.168.4.1/addfirmware --header "content-type:text/plain" --header "name:test_update" --header "size:100000" ; \ + echo "done" +else + echo "please use one of flags : site, code, or flash" +fi \ No newline at end of file