|
13 | 13 | #define KNOB_ATTEN_ZERO_WIDTH 80 |
14 | 14 | #define DEBOUNCE_FILE_SWITCH 500 |
15 | 15 |
|
| 16 | +// Input detection configuration |
| 17 | +#define SIGNAL_SETTLE_TIME_US 8 // Time for signal to settle |
| 18 | +#define SIGNAL_READ_SAMPLES 1 // Number of samples to average |
| 19 | +#define DETECTION_THRESHOLD 5 // Consecutive matches needed for state change |
| 20 | +#define SIGNAL_THRESHOLD_MARGIN 50 // ADC counts above/below mean for detection |
| 21 | +#define MEAN_ALPHA 0.1f // EMA coefficient for mean signal |
| 22 | +#define DETECTION_INTERVAL_MS 10 // Minimum time between detection runs |
| 23 | +#define MEAN_SIGNAL_INTERVAL_MS 1000 // Time between mean signal recalculations |
| 24 | +#define SIGNAL_ERROR_TOLERANCE 2 // Allow N bit errors in pattern matching |
| 25 | + |
16 | 26 | typedef struct EctocoreFlash { |
17 | 27 | uint16_t center_calibration[8]; |
18 | 28 | } EctocoreFlash; |
@@ -450,16 +460,20 @@ void __not_in_flash_func(input_handling)() { |
450 | 460 | uint8_t debounce_trig = 0; |
451 | 461 | Saturation_setActive(saturation, sf->fx_active[FX_SATURATE]); |
452 | 462 |
|
453 | | - uint16_t debounce_input_detection = 0; |
454 | | - uint16_t debounce_mean_signal = 0; |
455 | | - uint16_t mean_signal = 0; |
456 | | - const uint8_t length_signal = 9; |
457 | | - uint8_t magic_signal[3][10] = { |
458 | | - {0, 1, 1, 0, 1, 1, 0, 1, 0, 0}, |
459 | | - {0, 0, 1, 0, 1, 1, 0, 0, 1, 1}, |
460 | | - {1, 0, 0, 1, 0, 1, 0, 1, 1, 1}, |
| 463 | + uint32_t last_input_detection_time = 0; |
| 464 | + uint32_t last_mean_signal_time = 0; |
| 465 | + float mean_signal_ema = 0; |
| 466 | + const uint8_t length_signal = 16; |
| 467 | + // Improved magic signals with better Hamming distance |
| 468 | + uint8_t magic_signal[3][16] = { |
| 469 | + {0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0}, |
| 470 | + {1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1}, |
| 471 | + {0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1}, |
461 | 472 | }; |
462 | 473 | bool cv_was_unplugged[3] = {false, false, false}; |
| 474 | + uint8_t cv_detection_count[3] = {0, 0, 0}; |
| 475 | + uint16_t detection_errors[3] = {0, 0, 0}; |
| 476 | + uint8_t signal_strength[3] = {0, 0, 0}; |
463 | 477 |
|
464 | 478 | // update the knobs |
465 | 479 | #define KNOB_NUM 5 |
@@ -687,81 +701,124 @@ void __not_in_flash_func(input_handling)() { |
687 | 701 | } |
688 | 702 | } |
689 | 703 |
|
690 | | - if (debounce_mean_signal > 0 && mean_signal > 0) { |
691 | | - debounce_mean_signal--; |
692 | | - } else { |
693 | | - // calculate mean signal |
| 704 | + // Calculate mean signal using exponential moving average |
| 705 | + uint32_t current_time = to_ms_since_boot(get_absolute_time()); |
| 706 | + if (current_time - last_mean_signal_time >= MEAN_SIGNAL_INTERVAL_MS) { |
694 | 707 | int16_t total_mean_signal = 0; |
695 | 708 | uint8_t total_signals_sent = 0; |
696 | 709 | for (uint8_t j = 0; j < 3; j++) { |
697 | 710 | if (!cv_plugged[j]) { |
698 | 711 | total_signals_sent++; |
699 | 712 | for (uint8_t i = 0; i < length_signal; i++) { |
700 | 713 | gpio_put(GPIO_INPUTDETECT, magic_signal[j][i]); |
701 | | - sleep_us(6); |
| 714 | + sleep_us(SIGNAL_SETTLE_TIME_US); |
702 | 715 | total_mean_signal += MCP3208_read(mcp3208, cv_signals[j], false); |
703 | 716 | } |
704 | 717 | } |
705 | 718 | } |
706 | 719 | if (total_signals_sent > 0) { |
707 | | - mean_signal = total_mean_signal / (total_signals_sent * length_signal); |
708 | | - // printf("[ectocore] mean_signal: %d\n", mean_signal); |
| 720 | + float new_sample = |
| 721 | + (float)total_mean_signal / (total_signals_sent * length_signal); |
| 722 | + if (mean_signal_ema == 0) { |
| 723 | + mean_signal_ema = new_sample; // Initialize on first run |
| 724 | + } else { |
| 725 | + mean_signal_ema = |
| 726 | + MEAN_ALPHA * new_sample + (1.0f - MEAN_ALPHA) * mean_signal_ema; |
| 727 | + } |
| 728 | + // printf("[ectocore] mean_signal_ema: %f\n", mean_signal_ema); |
709 | 729 | } |
710 | | - debounce_mean_signal = 10000; |
| 730 | + last_mean_signal_time = current_time; |
711 | 731 | } |
712 | 732 |
|
713 | | - if (debounce_input_detection > 0) { |
714 | | - debounce_input_detection--; |
715 | | - } else if (mean_signal > 0) { |
716 | | - // input detection |
717 | | - bool found_change = false; |
| 733 | + // Input detection with time-based debouncing |
| 734 | + if (mean_signal_ema > 0 && |
| 735 | + current_time - last_input_detection_time >= DETECTION_INTERVAL_MS) { |
718 | 736 | int16_t val_input; |
719 | | - uint8_t response_signal[3][10] = { |
720 | | - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
721 | | - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
722 | | - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| 737 | + uint8_t response_signal[3][16] = { |
| 738 | + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| 739 | + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| 740 | + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
723 | 741 | }; |
724 | 742 |
|
725 | 743 | for (uint8_t j = 0; j < 3; j++) { |
| 744 | + int16_t total_signal_strength = 0; |
726 | 745 | for (uint8_t i = 0; i < length_signal; i++) { |
727 | 746 | gpio_put(GPIO_INPUTDETECT, magic_signal[j][i]); |
728 | | - sleep_us(6); |
| 747 | + sleep_us(SIGNAL_SETTLE_TIME_US); |
729 | 748 | val_input = MCP3208_read(mcp3208, cv_signals[j], false); |
730 | | - if (val_input > mean_signal) { |
| 749 | + |
| 750 | + // Track signal strength for diagnostics |
| 751 | + int16_t signal_diff = val_input - (int16_t)mean_signal_ema; |
| 752 | + total_signal_strength += abs(signal_diff); |
| 753 | + |
| 754 | + // Threshold with margin to reduce noise sensitivity |
| 755 | + if (val_input > (int16_t)mean_signal_ema + SIGNAL_THRESHOLD_MARGIN) { |
731 | 756 | response_signal[j][i] = 1; |
| 757 | + } else if (val_input < |
| 758 | + (int16_t)mean_signal_ema - SIGNAL_THRESHOLD_MARGIN) { |
| 759 | + response_signal[j][i] = 0; |
| 760 | + } else { |
| 761 | + // Ambiguous reading - use previous state or default to 0 |
| 762 | + response_signal[j][i] = 0; |
732 | 763 | } |
733 | | - // if (j == 0) { |
734 | | - // printf("%d ", val_input); |
735 | | - // } |
736 | 764 | } |
737 | | - // if (j == 0) { |
738 | | - // printf("\n"); |
739 | | - // } |
| 765 | + // Store average signal strength |
| 766 | + signal_strength[j] = total_signal_strength / length_signal; |
740 | 767 | } |
| 768 | + |
| 769 | + // Validate signals with error correction |
741 | 770 | bool is_signal[3] = {true, true, true}; |
742 | 771 | for (uint8_t j = 0; j < 3; j++) { |
| 772 | + uint8_t count_matches = 0; |
743 | 773 | for (uint8_t i = 0; i < length_signal; i++) { |
744 | | - if (response_signal[j][i] != magic_signal[j][i]) { |
745 | | - is_signal[j] = false; |
746 | | - break; |
| 774 | + if (response_signal[j][i] == magic_signal[j][i]) { |
| 775 | + count_matches++; |
747 | 776 | } |
748 | 777 | } |
| 778 | + // Allow SIGNAL_ERROR_TOLERANCE bit errors |
| 779 | + is_signal[j] = |
| 780 | + (count_matches >= length_signal - SIGNAL_ERROR_TOLERANCE); |
| 781 | + if (!is_signal[j] && |
| 782 | + count_matches < length_signal - SIGNAL_ERROR_TOLERANCE) { |
| 783 | + detection_errors[j]++; |
| 784 | + } |
749 | 785 | } |
| 786 | + |
| 787 | + // Hysteresis-based state machine |
750 | 788 | for (uint8_t j = 0; j < 3; j++) { |
751 | 789 | if (!is_signal[j] && !cv_plugged[j]) { |
752 | | - printf("[ectocore] cv_%d plugged\n", j); |
753 | | - debounce_mean_signal = 10; |
| 790 | + // Potential plug-in detected |
| 791 | + cv_detection_count[j]++; |
| 792 | + if (cv_detection_count[j] >= DETECTION_THRESHOLD) { |
| 793 | + cv_plugged[j] = true; |
| 794 | + cv_detection_count[j] = 0; |
| 795 | + last_mean_signal_time = 0; // Trigger mean recalculation |
| 796 | + printf("[ectocore] cv_%d plugged\n", j); |
| 797 | + } |
754 | 798 | } else if (is_signal[j] && cv_plugged[j]) { |
755 | | - printf("[ectocore] cv_%d unplugged\n", j); |
756 | | - debounce_mean_signal = 10; |
757 | | - cv_was_unplugged[j] = true; |
| 799 | + // Potential unplug detected |
| 800 | + cv_detection_count[j]++; |
| 801 | + if (cv_detection_count[j] >= DETECTION_THRESHOLD) { |
| 802 | + cv_plugged[j] = false; |
| 803 | + cv_detection_count[j] = 0; |
| 804 | + cv_was_unplugged[j] = true; |
| 805 | + last_mean_signal_time = 0; // Trigger mean recalculation |
| 806 | + printf("[ectocore] cv_%d unplugged\n", j); |
| 807 | + } |
| 808 | + } else { |
| 809 | + // State matches expectation - reset counter |
| 810 | + cv_detection_count[j] = 0; |
758 | 811 | } |
759 | | - cv_plugged[j] = !is_signal[j]; |
760 | 812 | } |
761 | | - debounce_input_detection = 100; |
| 813 | + |
| 814 | + last_input_detection_time = current_time; |
762 | 815 | } |
763 | 816 |
|
764 | 817 | // update the cv for each channel |
| 818 | + // Ensure GPIO_INPUTDETECT is LOW to prevent race condition |
| 819 | + gpio_put(GPIO_INPUTDETECT, 0); |
| 820 | + sleep_us(SIGNAL_SETTLE_TIME_US); |
| 821 | + |
765 | 822 | for (uint8_t i = 0; i < 3; i++) { |
766 | 823 | if (cv_plugged[i]) { |
767 | 824 | // collect out CV values |
@@ -873,7 +930,7 @@ void __not_in_flash_func(input_handling)() { |
873 | 930 | } |
874 | 931 | } |
875 | 932 |
|
876 | | - uint32_t current_time = to_ms_since_boot(get_absolute_time()); |
| 933 | + current_time = to_ms_since_boot(get_absolute_time()); |
877 | 934 |
|
878 | 935 | if (debounce_file_change > 0) { |
879 | 936 | if (debounce_file_switching > 0) { |
|
0 commit comments