Advertisement
LandoRo

ESP32-S3 SSD1306 ADS1220 2BTN v1.7

May 26th, 2025
466
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.29 KB | Gaming | 0 0
  1. #include <ADS1220_WE.h>
  2. #include <BleGamepad.h>
  3. #include <SPI.h>
  4. #include <Wire.h>
  5. #include <Adafruit_GFX.h>
  6. #include <Adafruit_SSD1306.h>
  7. #include <EEPROM.h>
  8.  
  9. // Pin definitions for ESP32-S3 Super Mini
  10. #define ADS1220_CS_PIN    10
  11. #define ADS1220_DRDY_PIN  9
  12. #define SPI_SCK           13
  13. #define SPI_MISO          12
  14. #define SPI_MOSI          11
  15. #define OLED_SDA          4
  16. #define OLED_SCL          5
  17. #define MENU_BUTTON       6
  18. #define SELECT_BUTTON     7
  19.  
  20. // SSD1306 display (128x64, I2C)
  21. #define SCREEN_WIDTH 128
  22. #define SCREEN_HEIGHT 64
  23. Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
  24.  
  25. // ADS1220 and BLE objects
  26. ADS1220_WE ads(ADS1220_CS_PIN, ADS1220_DRDY_PIN);
  27. BleGamepad bleGamepad("ESP32S3 Joystick", "xAI", 100);
  28.  
  29. // Axis definitions
  30. enum Axis { AXIS_CLUTCH, AXIS_BRAKE, AXIS_GAS, AXIS_HANDBRAKE };
  31. const char* axisNames[] = {"Clutch (X)", "Brake (Y)", "Gas (Z)", "Handbrake (Rz)"};
  32. const ads1220Mux axisMux[] = {ADS1220_MUX_0_AVSS, ADS1220_MUX_1_AVSS, ADS1220_MUX_2_AVSS, ADS1220_MUX_3_AVSS};
  33.  
  34. // ADC raw range
  35. const uint32_t ADC_MAX_RAW = 16777215; // 2^24 - 1 for 4.805V
  36. const float VREF = 4.805; // Reference voltage
  37.  
  38. // Calibration struct
  39. struct Calibration {
  40.   uint32_t minRaw;
  41.   uint32_t maxRaw;
  42. };
  43.  
  44. // Default calibration values (raw ADC counts)
  45. const Calibration defaultCal[4] = {
  46.   {0, ADC_MAX_RAW},        // Clutch: 0–4.805V
  47.   {1745305, 15707747},     // Brake: 0.5V–4.5V
  48.   {0, ADC_MAX_RAW},        // Gas: 0–4.805V
  49.   {0, ADC_MAX_RAW}         // Handbrake: 0–4.805V
  50. };
  51. const float DEFAULT_BRAKE_PSI = 0.0;
  52. const uint32_t PSI_MIN_RAW = 1745305; // 0.5V
  53. const uint32_t PSI_MAX_RAW = 15707747; // 4.5V
  54.  
  55. // SPS reporting
  56. unsigned long lastSpsReport = 0;
  57. unsigned long readingCount = 0;
  58.  
  59. // EEPROM for calibration
  60. #define EEPROM_SIZE 68
  61. #define EEPROM_VERSION 1
  62. Calibration cal[4];
  63.  
  64. // Moving average filter
  65. #define FILTER_SAMPLES_CLUTCH 4
  66. #define FILTER_SAMPLES_BRAKE 4
  67. #define FILTER_SAMPLES_GAS 6    // Reduced from 8 for speed
  68. #define FILTER_SAMPLES_HANDBRAKE 4
  69. float rawBuffer[4][FILTER_SAMPLES_GAS];
  70. int bufferIndex[4] = {0, 0, 0, 0};
  71. uint32_t lastFilteredRaw[4] = {0, 0, 0, 0}; // Integer for speed
  72. const uint32_t RAW_THRESHOLD = 6677; // ~2mV
  73. const int16_t BLE_THRESHOLD = 33; // ~0.1% of 0–32767
  74.  
  75. // Screen and button state
  76. static int currentScreen = 0;
  77. static bool calibrating = false;
  78. static bool calibrationMin = true;
  79. static unsigned long calibrationStart = 0;
  80. static int calibrationTimer = 4;
  81. static unsigned long buttonPressStart = 0;
  82. static bool selectPressed = false;
  83. static bool menuPressed = false;
  84. static bool longPressTriggered = false;
  85. static unsigned long lastDisplayUpdate = 0;
  86. static const unsigned long displayRefreshInterval = 60; // 60ms for smoother rendering
  87. static unsigned long lastBleCheck = 0;
  88. static const unsigned long bleCheckInterval = 2000; // 2000ms
  89. static bool showCalibrationDone = false;
  90. static unsigned long calibrationDoneStart = 0;
  91. static const unsigned long calibrationDoneDuration = 2000;
  92. static unsigned long lastButtonDebounce = 0;
  93. static const unsigned long debounceDelay = 50;
  94.  
  95. // Display state
  96. static float lastPercentages[4] = {-1, -1, -1, -1};
  97. static float lastPsi = -1;
  98. static float lastKg = -1;
  99. static bool lastBtConnected = false;
  100. static int16_t lastAxisValues[4] = {0, 0, 0, 0}; // Track BLE changes
  101. static char displayBuffer[64]; // Increased to 64 bytes
  102. static char calMinStr[16]; // Precomputed calibration strings
  103. static char calMaxStr[16];
  104. static char calRawStr[16];
  105. static int lastCalTimer = -1; // Track timer changes
  106.  
  107. void setup() {
  108.   Serial.begin(115200);
  109.   pinMode(MENU_BUTTON, INPUT_PULLUP);
  110.   pinMode(SELECT_BUTTON, INPUT_PULLUP);
  111.  
  112.   // Initialize I2C and SSD1306
  113.   Wire.begin(OLED_SDA, OLED_SCL);
  114.   Wire.setClock(400000);
  115.   if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  116.     Serial.println("SSD1306 init failed");
  117.     while (1);
  118.   }
  119.   display.setTextSize(1);
  120.   display.setTextColor(SSD1306_WHITE);
  121.   display.setCursor(0, 0);
  122.   display.println("Initializing...");
  123.   display.display();
  124.  
  125.   // Initialize SPI
  126.   SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI, ADS1220_CS_PIN);
  127.  
  128.   // Initialize ADS1220
  129.   if (!ads.init()) {
  130.     Serial.println("ADS1220 init failed");
  131.     while (1);
  132.   }
  133.   ads.setSPIClockSpeed(10000000);               // 10 MHz SPI
  134.   ads.setVRefSource(ADS1220_VREF_REFP0_REFN0);
  135.   ads.setVRefValue_V(4.805);
  136.   ads.setGain(ADS1220_GAIN_1);
  137.   ads.bypassPGA(true);
  138.   ads.setDataRate(ADS1220_DR_LVL_6);           // 1200 SPS
  139.   ads.setOperatingMode(ADS1220_TURBO_MODE);
  140.   ads.setConversionMode(ADS1220_CONTINUOUS);
  141.   ads.setFIRFilter(ADS1220_50HZ_60HZ);
  142.  
  143.   // Initialize raw buffers
  144.   for (int i = 0; i < 4; i++) {
  145.     for (int j = 0; j < FILTER_SAMPLES_GAS; j++) {
  146.       rawBuffer[i][j] = 0.0;
  147.     }
  148.   }
  149.  
  150.   // Initialize EEPROM
  151.   EEPROM.begin(EEPROM_SIZE);
  152.   uint32_t version;
  153.   EEPROM.get(0, version);
  154.   if (version != EEPROM_VERSION) {
  155.     for (int i = 0; i < 4; i++) {
  156.       cal[i] = defaultCal[i];
  157.       EEPROM.put(4 + i * 8, cal[i]);
  158.     }
  159.     version = EEPROM_VERSION;
  160.     EEPROM.put(0, version);
  161.   } else {
  162.     for (int i = 0; i < 4; i++) {
  163.       EEPROM.get(4 + i * 8, cal[i]);
  164.       if (cal[i].minRaw >= cal[i].maxRaw || cal[i].maxRaw > ADC_MAX_RAW) {
  165.         cal[i] = defaultCal[i];
  166.         EEPROM.put(4 + i * 8, cal[i]);
  167.       }
  168.     }
  169.   }
  170.   EEPROM.commit();
  171.  
  172.   // Start BLE
  173.   bleGamepad.begin();
  174.   // Note: setConnInterval(6, 12) not supported by ESP32-BLE-Gamepad; update library or use NimBLE for 7.5–15ms interval
  175. }
  176.  
  177. uint32_t getFilteredRaw(int axis) {
  178.   ads.setCompareChannels(axisMux[axis]);
  179.   while (digitalRead(ADS1220_DRDY_PIN) == HIGH);
  180.   // Note: If ADS1220_WE has readRawData(), use: uint32_t raw = ads.readRawData();
  181.   float voltage = ads.getVoltage_mV() / 1000.0;
  182.   if (voltage < 0) voltage = 0.0;
  183.   if (voltage > VREF) voltage = VREF;
  184.   uint32_t raw = (uint32_t)((voltage / VREF) * ADC_MAX_RAW);
  185.   if (raw > ADC_MAX_RAW) raw = ADC_MAX_RAW;
  186.  
  187.   // Integer-based moving average
  188.   int samples = (axis == AXIS_GAS) ? FILTER_SAMPLES_GAS :
  189.                 (axis == AXIS_BRAKE) ? FILTER_SAMPLES_BRAKE :
  190.                 (axis == AXIS_CLUTCH) ? FILTER_SAMPLES_CLUTCH :
  191.                 FILTER_SAMPLES_HANDBRAKE;
  192.   rawBuffer[axis][bufferIndex[axis]] = raw;
  193.   bufferIndex[axis] = (bufferIndex[axis] + 1) % samples;
  194.   uint32_t sum = 0;
  195.   for (int i = 0; i < samples; i++) {
  196.     sum += (uint32_t)rawBuffer[axis][i];
  197.   }
  198.   uint32_t filteredRaw = sum / samples;
  199.  
  200.   // Apply noise threshold
  201.   if (abs((int32_t)(filteredRaw - lastFilteredRaw[axis])) < RAW_THRESHOLD) {
  202.     filteredRaw = lastFilteredRaw[axis];
  203.   }
  204.   lastFilteredRaw[axis] = filteredRaw;
  205.   return filteredRaw;
  206. }
  207.  
  208. void updateDisplay(unsigned long currentTime) {
  209.   if (showCalibrationDone) {
  210.     if (currentTime - calibrationDoneStart < calibrationDoneDuration) {
  211.       display.clearDisplay();
  212.       display.setCursor(0, 16);
  213.       display.println("Done");
  214.       display.display();
  215.     } else {
  216.       showCalibrationDone = false;
  217.       display.clearDisplay();
  218.       display.display();
  219.     }
  220.     return;
  221.   }
  222.  
  223.   if (currentScreen == 0) {
  224.     // Screen 0: PSI, KG, BT status
  225.     uint32_t brakeRaw = getFilteredRaw(AXIS_BRAKE);
  226.     float psi = DEFAULT_BRAKE_PSI;
  227.     if (brakeRaw >= PSI_MIN_RAW && brakeRaw <= PSI_MAX_RAW) {
  228.       psi = ((brakeRaw - PSI_MIN_RAW) / (float)(PSI_MAX_RAW - PSI_MIN_RAW)) * 100;
  229.     }
  230.     float kg = psi * 1.379;
  231.     bool btConnected = bleGamepad.isConnected();
  232.  
  233.     if (abs(psi - lastPsi) >= 0.1) {
  234.       display.fillRect(0, 0, 128, 8, SSD1306_BLACK);
  235.       display.setCursor(0, 0);
  236.       snprintf(displayBuffer, sizeof(displayBuffer), "Brake PSI: %.1f", psi);
  237.       display.print(displayBuffer);
  238.       lastPsi = psi;
  239.     }
  240.     if (abs(kg - lastKg) >= 0.1) {
  241.       display.fillRect(0, 16, 128, 8, SSD1306_BLACK);
  242.       display.setCursor(0, 16);
  243.       snprintf(displayBuffer, sizeof(displayBuffer), "Brake KG: %.1f", kg);
  244.       display.print(displayBuffer);
  245.       lastKg = kg;
  246.     }
  247.     if (btConnected != lastBtConnected) {
  248.       display.fillRect(0, 32, 128, 8, SSD1306_BLACK);
  249.       if (btConnected) {
  250.         display.setCursor(0, 32);
  251.         display.print("BT");
  252.       }
  253.       lastBtConnected = btConnected;
  254.     }
  255.     display.display();
  256.   } else if (currentScreen == 5) {
  257.     // Screen 5: Percentage for each axis
  258.     float percentages[4];
  259.     for (int i = 0; i < 4; i++) {
  260.       uint32_t raw = getFilteredRaw(i);
  261.       uint32_t minRaw = cal[i].minRaw;
  262.       uint32_t maxRaw = cal[i].maxRaw;
  263.       if (minRaw >= maxRaw || maxRaw > ADC_MAX_RAW) {
  264.         minRaw = defaultCal[i].minRaw;
  265.         maxRaw = defaultCal[i].maxRaw;
  266.       }
  267.       raw = constrain(raw, minRaw, maxRaw);
  268.       percentages[i] = (raw - minRaw) / (float)(maxRaw - minRaw) * 100.0;
  269.       if (abs(percentages[i] - lastPercentages[i]) >= 0.1) {
  270.         display.fillRect(0, i * 12, 128, 8, SSD1306_BLACK);
  271.         display.setCursor(0, i * 12);
  272.         snprintf(displayBuffer, sizeof(displayBuffer), "%s: %.1f%%", axisNames[i], percentages[i]);
  273.         display.print(displayBuffer);
  274.         lastPercentages[i] = percentages[i];
  275.       }
  276.     }
  277.     display.display();
  278.   } else if (!calibrating) {
  279.     // Screens 1–4: Calibration (non-calibrating mode)
  280.     int axisIdx = currentScreen - 1;
  281.     // Precompute min/max strings only on screen change
  282.     if (lastPercentages[0] == -1) { // Reset flag from screen change
  283.       snprintf(calMinStr, sizeof(calMinStr), "Min: %lu", cal[axisIdx].minRaw);
  284.       snprintf(calMaxStr, sizeof(calMaxStr), "Max: %lu", cal[axisIdx].maxRaw);
  285.       Serial.printf("Calibration Screen %d: %s, %s\n", currentScreen, calMinStr, calMaxStr);
  286.     }
  287.     display.clearDisplay();
  288.     display.setCursor(0, 0);
  289.     display.println(axisNames[axisIdx]);
  290.     display.println(calMinStr);
  291.     display.println(calMaxStr);
  292.     display.println("SP=Calibrate");
  293.     display.println("LP=Reset");
  294.     display.display();
  295.   } else {
  296.     // Calibration mode
  297.     int axisIdx = currentScreen - 1;
  298.     uint32_t raw = getFilteredRaw(axisIdx);
  299.     bool updateNeeded = false;
  300.  
  301.     // Update raw value string
  302.     snprintf(calRawStr, sizeof(calRawStr), "%s: %lu", calibrationMin ? "MIN" : "MAX", raw);
  303.     Serial.printf("Calibrating %s: %s\n", axisNames[axisIdx], calRawStr);
  304.  
  305.     // Update timer string only if changed
  306.     if (currentTime - calibrationStart >= 1000) {
  307.       calibrationTimer--;
  308.       calibrationStart = currentTime;
  309.       updateNeeded = true;
  310.       if (calibrationTimer < 0) {
  311.         if (calibrationMin) {
  312.           cal[axisIdx].minRaw = raw;
  313.           snprintf(calMinStr, sizeof(calMinStr), "Min: %lu", cal[axisIdx].minRaw);
  314.           calibrationMin = false;
  315.           calibrationTimer = 4;
  316.         } else {
  317.           cal[axisIdx].maxRaw = raw;
  318.           if (cal[axisIdx].minRaw >= cal[axisIdx].maxRaw || cal[axisIdx].maxRaw > ADC_MAX_RAW) {
  319.             cal[axisIdx] = defaultCal[axisIdx];
  320.           }
  321.           snprintf(calMaxStr, sizeof(calMaxStr), "Max: %lu", cal[axisIdx].maxRaw);
  322.           EEPROM.put(4 + axisIdx * 8, cal[axisIdx]);
  323.           EEPROM.commit();
  324.           calibrating = false;
  325.           calibrationStart = currentTime;
  326.           showCalibrationDone = true;
  327.           calibrationDoneStart = currentTime;
  328.         }
  329.       }
  330.     }
  331.     if (calibrationTimer != lastCalTimer) {
  332.       updateNeeded = true;
  333.       lastCalTimer = calibrationTimer;
  334.     }
  335.  
  336.     // Render only if needed
  337.     if (updateNeeded) {
  338.       display.clearDisplay();
  339.       display.setCursor(0, 0);
  340.       display.println(axisNames[axisIdx]);
  341.       display.println(calRawStr);
  342.       snprintf(displayBuffer, sizeof(displayBuffer), "Time: %d s", calibrationTimer);
  343.       display.println(displayBuffer);
  344.       display.display();
  345.     }
  346.   }
  347. }
  348.  
  349. void loop() {
  350.   unsigned long currentTime = millis();
  351.  
  352.   // Prioritize BLE updates
  353.   if (bleGamepad.isConnected()) {
  354.     int16_t axisValues[4] = {0, 0, 0, 0};
  355.     bool axisChanged = false;
  356.     for (int i = 0; i < 4; i++) {
  357.       uint32_t raw = getFilteredRaw(i);
  358.       readingCount++;
  359.       uint32_t minRaw = cal[i].minRaw;
  360.       uint32_t maxRaw = cal[i].maxRaw;
  361.       if (minRaw >= maxRaw || maxRaw > ADC_MAX_RAW) {
  362.         minRaw = defaultCal[i].minRaw;
  363.         maxRaw = defaultCal[i].maxRaw;
  364.       }
  365.       raw = constrain(raw, minRaw, maxRaw);
  366.       long mappedValue = (raw - minRaw) / (float)(maxRaw - minRaw) * 32767;
  367.       axisValues[i] = (int16_t)mappedValue;
  368.       if (abs(axisValues[i] - lastAxisValues[i]) >= BLE_THRESHOLD) {
  369.         axisChanged = true;
  370.         lastAxisValues[i] = axisValues[i];
  371.       }
  372.     }
  373.     if (axisChanged) {
  374.       bleGamepad.setAxes(
  375.         axisValues[AXIS_CLUTCH],
  376.         axisValues[AXIS_BRAKE],
  377.         axisValues[AXIS_GAS],
  378.         axisValues[AXIS_HANDBRAKE],
  379.         0, 0, 0, 0
  380.       );
  381.     }
  382.     if (currentTime - lastSpsReport >= 1000) {
  383.       Serial.printf("SPS: %lu readings/sec\n", readingCount);
  384.       readingCount = 0;
  385.       lastSpsReport = currentTime;
  386.     }
  387.   } else if (currentTime - lastBleCheck >= bleCheckInterval) {
  388.     lastBleCheck = currentTime;
  389.   }
  390.  
  391.   // Handle buttons (combined debounce)
  392.   int menuReading = digitalRead(MENU_BUTTON);
  393.   int selectReading = digitalRead(SELECT_BUTTON);
  394.   if ((menuReading == LOW || selectReading == LOW) && currentTime - lastButtonDebounce > debounceDelay) {
  395.     if (menuReading == LOW && !menuPressed) {
  396.       if (!calibrating) {
  397.         currentScreen = (currentScreen + 1) % 6;
  398.         display.clearDisplay();
  399.         display.display(); // Ensure clear before new content
  400.         lastPercentages[0] = lastPercentages[1] = lastPercentages[2] = lastPercentages[3] = -1;
  401.         lastPsi = lastKg = -1;
  402.         lastBtConnected = false;
  403.         lastCalTimer = -1; // Reset calibration timer tracking
  404.       }
  405.       menuPressed = true;
  406.     }
  407.     if (selectReading == LOW && !selectPressed) {
  408.       buttonPressStart = currentTime;
  409.       selectPressed = true;
  410.       longPressTriggered = false;
  411.     }
  412.     lastButtonDebounce = currentTime;
  413.   }
  414.   if (menuReading == HIGH && menuPressed) {
  415.     menuPressed = false;
  416.     lastButtonDebounce = currentTime;
  417.   }
  418.   if (selectReading == HIGH && selectPressed) {
  419.     unsigned long pressDuration = currentTime - buttonPressStart;
  420.     if (!calibrating && currentScreen >= 1 && currentScreen <= 4 && pressDuration < 3000 && !longPressTriggered) {
  421.       calibrating = true;
  422.       calibrationMin = true;
  423.       calibrationStart = currentTime;
  424.       calibrationTimer = 4;
  425.       lastCalTimer = 4;
  426.       display.clearDisplay();
  427.       display.display(); // Ensure clear before calibration
  428.       showCalibrationDone = false;
  429.     }
  430.     selectPressed = false;
  431.     lastButtonDebounce = currentTime;
  432.   }
  433.   if (selectPressed && currentTime - buttonPressStart >= 3000 && currentScreen >= 1 && currentScreen <= 4 && !longPressTriggered) {
  434.     int axisIdx = currentScreen - 1;
  435.     cal[axisIdx] = defaultCal[axisIdx];
  436.     snprintf(calMinStr, sizeof(calMinStr), "Min: %lu", cal[axisIdx].minRaw);
  437.     snprintf(calMaxStr, sizeof(calMaxStr), "Max: %lu", cal[axisIdx].maxRaw);
  438.     EEPROM.put(4 + axisIdx * 8, cal[axisIdx]);
  439.     EEPROM.commit();
  440.     longPressTriggered = true;
  441.     lastButtonDebounce = currentTime;
  442.   }
  443.  
  444.   // Update display (every 60ms)
  445.   if (currentTime - lastDisplayUpdate >= displayRefreshInterval) {
  446.     updateDisplay(currentTime);
  447.     lastDisplayUpdate = currentTime;
  448.   }
  449. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement