Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <ADS1220_WE.h>
- #include <BleGamepad.h>
- #include <SPI.h>
- #include <Wire.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_SSD1306.h>
- #include <EEPROM.h>
- // Pin definitions for ESP32-S3 Super Mini
- #define ADS1220_CS_PIN 10
- #define ADS1220_DRDY_PIN 9
- #define SPI_SCK 13
- #define SPI_MISO 12
- #define SPI_MOSI 11
- #define OLED_SDA 4
- #define OLED_SCL 5
- #define MENU_BUTTON 6
- #define SELECT_BUTTON 7
- // SSD1306 display (128x64, I2C)
- #define SCREEN_WIDTH 128
- #define SCREEN_HEIGHT 64
- Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
- // ADS1220 and BLE objects
- ADS1220_WE ads(ADS1220_CS_PIN, ADS1220_DRDY_PIN);
- BleGamepad bleGamepad("ESP32S3 Joystick", "xAI", 100);
- // Axis definitions
- enum Axis { AXIS_CLUTCH, AXIS_BRAKE, AXIS_GAS, AXIS_HANDBRAKE };
- const char* axisNames[] = {"Clutch (X)", "Brake (Y)", "Gas (Z)", "Handbrake (Rz)"};
- const ads1220Mux axisMux[] = {ADS1220_MUX_0_AVSS, ADS1220_MUX_1_AVSS, ADS1220_MUX_2_AVSS, ADS1220_MUX_3_AVSS};
- // ADC raw range
- const uint32_t ADC_MAX_RAW = 16777215; // 2^24 - 1 for 4.805V
- const float VREF = 4.805; // Reference voltage
- // Calibration struct
- struct Calibration {
- uint32_t minRaw;
- uint32_t maxRaw;
- };
- // Default calibration values (raw ADC counts)
- const Calibration defaultCal[4] = {
- {0, ADC_MAX_RAW}, // Clutch: 0–4.805V
- {1745305, 15707747}, // Brake: 0.5V–4.5V
- {0, ADC_MAX_RAW}, // Gas: 0–4.805V
- {0, ADC_MAX_RAW} // Handbrake: 0–4.805V
- };
- const float DEFAULT_BRAKE_PSI = 0.0;
- const uint32_t PSI_MIN_RAW = 1745305; // 0.5V
- const uint32_t PSI_MAX_RAW = 15707747; // 4.5V
- // SPS reporting
- unsigned long lastSpsReport = 0;
- unsigned long readingCount = 0;
- // EEPROM for calibration
- #define EEPROM_SIZE 68
- #define EEPROM_VERSION 1
- Calibration cal[4];
- // Moving average filter
- #define FILTER_SAMPLES_CLUTCH 4
- #define FILTER_SAMPLES_BRAKE 4
- #define FILTER_SAMPLES_GAS 6 // Reduced from 8 for speed
- #define FILTER_SAMPLES_HANDBRAKE 4
- float rawBuffer[4][FILTER_SAMPLES_GAS];
- int bufferIndex[4] = {0, 0, 0, 0};
- uint32_t lastFilteredRaw[4] = {0, 0, 0, 0}; // Integer for speed
- const uint32_t RAW_THRESHOLD = 6677; // ~2mV
- const int16_t BLE_THRESHOLD = 33; // ~0.1% of 0–32767
- // Screen and button state
- static int currentScreen = 0;
- static bool calibrating = false;
- static bool calibrationMin = true;
- static unsigned long calibrationStart = 0;
- static int calibrationTimer = 4;
- static unsigned long buttonPressStart = 0;
- static bool selectPressed = false;
- static bool menuPressed = false;
- static bool longPressTriggered = false;
- static unsigned long lastDisplayUpdate = 0;
- static const unsigned long displayRefreshInterval = 60; // 60ms for smoother rendering
- static unsigned long lastBleCheck = 0;
- static const unsigned long bleCheckInterval = 2000; // 2000ms
- static bool showCalibrationDone = false;
- static unsigned long calibrationDoneStart = 0;
- static const unsigned long calibrationDoneDuration = 2000;
- static unsigned long lastButtonDebounce = 0;
- static const unsigned long debounceDelay = 50;
- // Display state
- static float lastPercentages[4] = {-1, -1, -1, -1};
- static float lastPsi = -1;
- static float lastKg = -1;
- static bool lastBtConnected = false;
- static int16_t lastAxisValues[4] = {0, 0, 0, 0}; // Track BLE changes
- static char displayBuffer[64]; // Increased to 64 bytes
- static char calMinStr[16]; // Precomputed calibration strings
- static char calMaxStr[16];
- static char calRawStr[16];
- static int lastCalTimer = -1; // Track timer changes
- void setup() {
- Serial.begin(115200);
- pinMode(MENU_BUTTON, INPUT_PULLUP);
- pinMode(SELECT_BUTTON, INPUT_PULLUP);
- // Initialize I2C and SSD1306
- Wire.begin(OLED_SDA, OLED_SCL);
- Wire.setClock(400000);
- if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
- Serial.println("SSD1306 init failed");
- while (1);
- }
- display.setTextSize(1);
- display.setTextColor(SSD1306_WHITE);
- display.setCursor(0, 0);
- display.println("Initializing...");
- display.display();
- // Initialize SPI
- SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI, ADS1220_CS_PIN);
- // Initialize ADS1220
- if (!ads.init()) {
- Serial.println("ADS1220 init failed");
- while (1);
- }
- ads.setSPIClockSpeed(10000000); // 10 MHz SPI
- ads.setVRefSource(ADS1220_VREF_REFP0_REFN0);
- ads.setVRefValue_V(4.805);
- ads.setGain(ADS1220_GAIN_1);
- ads.bypassPGA(true);
- ads.setDataRate(ADS1220_DR_LVL_6); // 1200 SPS
- ads.setOperatingMode(ADS1220_TURBO_MODE);
- ads.setConversionMode(ADS1220_CONTINUOUS);
- ads.setFIRFilter(ADS1220_50HZ_60HZ);
- // Initialize raw buffers
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < FILTER_SAMPLES_GAS; j++) {
- rawBuffer[i][j] = 0.0;
- }
- }
- // Initialize EEPROM
- EEPROM.begin(EEPROM_SIZE);
- uint32_t version;
- EEPROM.get(0, version);
- if (version != EEPROM_VERSION) {
- for (int i = 0; i < 4; i++) {
- cal[i] = defaultCal[i];
- EEPROM.put(4 + i * 8, cal[i]);
- }
- version = EEPROM_VERSION;
- EEPROM.put(0, version);
- } else {
- for (int i = 0; i < 4; i++) {
- EEPROM.get(4 + i * 8, cal[i]);
- if (cal[i].minRaw >= cal[i].maxRaw || cal[i].maxRaw > ADC_MAX_RAW) {
- cal[i] = defaultCal[i];
- EEPROM.put(4 + i * 8, cal[i]);
- }
- }
- }
- EEPROM.commit();
- // Start BLE
- bleGamepad.begin();
- // Note: setConnInterval(6, 12) not supported by ESP32-BLE-Gamepad; update library or use NimBLE for 7.5–15ms interval
- }
- uint32_t getFilteredRaw(int axis) {
- ads.setCompareChannels(axisMux[axis]);
- while (digitalRead(ADS1220_DRDY_PIN) == HIGH);
- // Note: If ADS1220_WE has readRawData(), use: uint32_t raw = ads.readRawData();
- float voltage = ads.getVoltage_mV() / 1000.0;
- if (voltage < 0) voltage = 0.0;
- if (voltage > VREF) voltage = VREF;
- uint32_t raw = (uint32_t)((voltage / VREF) * ADC_MAX_RAW);
- if (raw > ADC_MAX_RAW) raw = ADC_MAX_RAW;
- // Integer-based moving average
- int samples = (axis == AXIS_GAS) ? FILTER_SAMPLES_GAS :
- (axis == AXIS_BRAKE) ? FILTER_SAMPLES_BRAKE :
- (axis == AXIS_CLUTCH) ? FILTER_SAMPLES_CLUTCH :
- FILTER_SAMPLES_HANDBRAKE;
- rawBuffer[axis][bufferIndex[axis]] = raw;
- bufferIndex[axis] = (bufferIndex[axis] + 1) % samples;
- uint32_t sum = 0;
- for (int i = 0; i < samples; i++) {
- sum += (uint32_t)rawBuffer[axis][i];
- }
- uint32_t filteredRaw = sum / samples;
- // Apply noise threshold
- if (abs((int32_t)(filteredRaw - lastFilteredRaw[axis])) < RAW_THRESHOLD) {
- filteredRaw = lastFilteredRaw[axis];
- }
- lastFilteredRaw[axis] = filteredRaw;
- return filteredRaw;
- }
- void updateDisplay(unsigned long currentTime) {
- if (showCalibrationDone) {
- if (currentTime - calibrationDoneStart < calibrationDoneDuration) {
- display.clearDisplay();
- display.setCursor(0, 16);
- display.println("Done");
- display.display();
- } else {
- showCalibrationDone = false;
- display.clearDisplay();
- display.display();
- }
- return;
- }
- if (currentScreen == 0) {
- // Screen 0: PSI, KG, BT status
- uint32_t brakeRaw = getFilteredRaw(AXIS_BRAKE);
- float psi = DEFAULT_BRAKE_PSI;
- if (brakeRaw >= PSI_MIN_RAW && brakeRaw <= PSI_MAX_RAW) {
- psi = ((brakeRaw - PSI_MIN_RAW) / (float)(PSI_MAX_RAW - PSI_MIN_RAW)) * 100;
- }
- float kg = psi * 1.379;
- bool btConnected = bleGamepad.isConnected();
- if (abs(psi - lastPsi) >= 0.1) {
- display.fillRect(0, 0, 128, 8, SSD1306_BLACK);
- display.setCursor(0, 0);
- snprintf(displayBuffer, sizeof(displayBuffer), "Brake PSI: %.1f", psi);
- display.print(displayBuffer);
- lastPsi = psi;
- }
- if (abs(kg - lastKg) >= 0.1) {
- display.fillRect(0, 16, 128, 8, SSD1306_BLACK);
- display.setCursor(0, 16);
- snprintf(displayBuffer, sizeof(displayBuffer), "Brake KG: %.1f", kg);
- display.print(displayBuffer);
- lastKg = kg;
- }
- if (btConnected != lastBtConnected) {
- display.fillRect(0, 32, 128, 8, SSD1306_BLACK);
- if (btConnected) {
- display.setCursor(0, 32);
- display.print("BT");
- }
- lastBtConnected = btConnected;
- }
- display.display();
- } else if (currentScreen == 5) {
- // Screen 5: Percentage for each axis
- float percentages[4];
- for (int i = 0; i < 4; i++) {
- uint32_t raw = getFilteredRaw(i);
- uint32_t minRaw = cal[i].minRaw;
- uint32_t maxRaw = cal[i].maxRaw;
- if (minRaw >= maxRaw || maxRaw > ADC_MAX_RAW) {
- minRaw = defaultCal[i].minRaw;
- maxRaw = defaultCal[i].maxRaw;
- }
- raw = constrain(raw, minRaw, maxRaw);
- percentages[i] = (raw - minRaw) / (float)(maxRaw - minRaw) * 100.0;
- if (abs(percentages[i] - lastPercentages[i]) >= 0.1) {
- display.fillRect(0, i * 12, 128, 8, SSD1306_BLACK);
- display.setCursor(0, i * 12);
- snprintf(displayBuffer, sizeof(displayBuffer), "%s: %.1f%%", axisNames[i], percentages[i]);
- display.print(displayBuffer);
- lastPercentages[i] = percentages[i];
- }
- }
- display.display();
- } else if (!calibrating) {
- // Screens 1–4: Calibration (non-calibrating mode)
- int axisIdx = currentScreen - 1;
- // Precompute min/max strings only on screen change
- if (lastPercentages[0] == -1) { // Reset flag from screen change
- snprintf(calMinStr, sizeof(calMinStr), "Min: %lu", cal[axisIdx].minRaw);
- snprintf(calMaxStr, sizeof(calMaxStr), "Max: %lu", cal[axisIdx].maxRaw);
- Serial.printf("Calibration Screen %d: %s, %s\n", currentScreen, calMinStr, calMaxStr);
- }
- display.clearDisplay();
- display.setCursor(0, 0);
- display.println(axisNames[axisIdx]);
- display.println(calMinStr);
- display.println(calMaxStr);
- display.println("SP=Calibrate");
- display.println("LP=Reset");
- display.display();
- } else {
- // Calibration mode
- int axisIdx = currentScreen - 1;
- uint32_t raw = getFilteredRaw(axisIdx);
- bool updateNeeded = false;
- // Update raw value string
- snprintf(calRawStr, sizeof(calRawStr), "%s: %lu", calibrationMin ? "MIN" : "MAX", raw);
- Serial.printf("Calibrating %s: %s\n", axisNames[axisIdx], calRawStr);
- // Update timer string only if changed
- if (currentTime - calibrationStart >= 1000) {
- calibrationTimer--;
- calibrationStart = currentTime;
- updateNeeded = true;
- if (calibrationTimer < 0) {
- if (calibrationMin) {
- cal[axisIdx].minRaw = raw;
- snprintf(calMinStr, sizeof(calMinStr), "Min: %lu", cal[axisIdx].minRaw);
- calibrationMin = false;
- calibrationTimer = 4;
- } else {
- cal[axisIdx].maxRaw = raw;
- if (cal[axisIdx].minRaw >= cal[axisIdx].maxRaw || cal[axisIdx].maxRaw > ADC_MAX_RAW) {
- cal[axisIdx] = defaultCal[axisIdx];
- }
- snprintf(calMaxStr, sizeof(calMaxStr), "Max: %lu", cal[axisIdx].maxRaw);
- EEPROM.put(4 + axisIdx * 8, cal[axisIdx]);
- EEPROM.commit();
- calibrating = false;
- calibrationStart = currentTime;
- showCalibrationDone = true;
- calibrationDoneStart = currentTime;
- }
- }
- }
- if (calibrationTimer != lastCalTimer) {
- updateNeeded = true;
- lastCalTimer = calibrationTimer;
- }
- // Render only if needed
- if (updateNeeded) {
- display.clearDisplay();
- display.setCursor(0, 0);
- display.println(axisNames[axisIdx]);
- display.println(calRawStr);
- snprintf(displayBuffer, sizeof(displayBuffer), "Time: %d s", calibrationTimer);
- display.println(displayBuffer);
- display.display();
- }
- }
- }
- void loop() {
- unsigned long currentTime = millis();
- // Prioritize BLE updates
- if (bleGamepad.isConnected()) {
- int16_t axisValues[4] = {0, 0, 0, 0};
- bool axisChanged = false;
- for (int i = 0; i < 4; i++) {
- uint32_t raw = getFilteredRaw(i);
- readingCount++;
- uint32_t minRaw = cal[i].minRaw;
- uint32_t maxRaw = cal[i].maxRaw;
- if (minRaw >= maxRaw || maxRaw > ADC_MAX_RAW) {
- minRaw = defaultCal[i].minRaw;
- maxRaw = defaultCal[i].maxRaw;
- }
- raw = constrain(raw, minRaw, maxRaw);
- long mappedValue = (raw - minRaw) / (float)(maxRaw - minRaw) * 32767;
- axisValues[i] = (int16_t)mappedValue;
- if (abs(axisValues[i] - lastAxisValues[i]) >= BLE_THRESHOLD) {
- axisChanged = true;
- lastAxisValues[i] = axisValues[i];
- }
- }
- if (axisChanged) {
- bleGamepad.setAxes(
- axisValues[AXIS_CLUTCH],
- axisValues[AXIS_BRAKE],
- axisValues[AXIS_GAS],
- axisValues[AXIS_HANDBRAKE],
- 0, 0, 0, 0
- );
- }
- if (currentTime - lastSpsReport >= 1000) {
- Serial.printf("SPS: %lu readings/sec\n", readingCount);
- readingCount = 0;
- lastSpsReport = currentTime;
- }
- } else if (currentTime - lastBleCheck >= bleCheckInterval) {
- lastBleCheck = currentTime;
- }
- // Handle buttons (combined debounce)
- int menuReading = digitalRead(MENU_BUTTON);
- int selectReading = digitalRead(SELECT_BUTTON);
- if ((menuReading == LOW || selectReading == LOW) && currentTime - lastButtonDebounce > debounceDelay) {
- if (menuReading == LOW && !menuPressed) {
- if (!calibrating) {
- currentScreen = (currentScreen + 1) % 6;
- display.clearDisplay();
- display.display(); // Ensure clear before new content
- lastPercentages[0] = lastPercentages[1] = lastPercentages[2] = lastPercentages[3] = -1;
- lastPsi = lastKg = -1;
- lastBtConnected = false;
- lastCalTimer = -1; // Reset calibration timer tracking
- }
- menuPressed = true;
- }
- if (selectReading == LOW && !selectPressed) {
- buttonPressStart = currentTime;
- selectPressed = true;
- longPressTriggered = false;
- }
- lastButtonDebounce = currentTime;
- }
- if (menuReading == HIGH && menuPressed) {
- menuPressed = false;
- lastButtonDebounce = currentTime;
- }
- if (selectReading == HIGH && selectPressed) {
- unsigned long pressDuration = currentTime - buttonPressStart;
- if (!calibrating && currentScreen >= 1 && currentScreen <= 4 && pressDuration < 3000 && !longPressTriggered) {
- calibrating = true;
- calibrationMin = true;
- calibrationStart = currentTime;
- calibrationTimer = 4;
- lastCalTimer = 4;
- display.clearDisplay();
- display.display(); // Ensure clear before calibration
- showCalibrationDone = false;
- }
- selectPressed = false;
- lastButtonDebounce = currentTime;
- }
- if (selectPressed && currentTime - buttonPressStart >= 3000 && currentScreen >= 1 && currentScreen <= 4 && !longPressTriggered) {
- int axisIdx = currentScreen - 1;
- cal[axisIdx] = defaultCal[axisIdx];
- snprintf(calMinStr, sizeof(calMinStr), "Min: %lu", cal[axisIdx].minRaw);
- snprintf(calMaxStr, sizeof(calMaxStr), "Max: %lu", cal[axisIdx].maxRaw);
- EEPROM.put(4 + axisIdx * 8, cal[axisIdx]);
- EEPROM.commit();
- longPressTriggered = true;
- lastButtonDebounce = currentTime;
- }
- // Update display (every 60ms)
- if (currentTime - lastDisplayUpdate >= displayRefreshInterval) {
- updateDisplay(currentTime);
- lastDisplayUpdate = currentTime;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement