Bläddra i källkod

Initial commit

Seb 5 månader sedan
incheckning
b9feb08362
4 ändrade filer med 588 tillägg och 0 borttagningar
  1. 101 0
      README.md
  2. 18 0
      platformio.ini
  3. 409 0
      src/main.cpp
  4. 60 0
      test_ssd1306.cpp

+ 101 - 0
README.md

@@ -0,0 +1,101 @@
+# Mirror HUD Display - ESP32 (SPI Interface)
+
+ESP32-based mirror display for EUC battery and speed data via BLE.
+
+## Hardware Configuration
+
+### Waveshare 1.51" Transparent OLED (SSD1309)
+- **Interface**: SPI (4-wire)
+- **Resolution**: 128x64
+- **Controller**: SSD1309
+
+### Wiring for Standard ESP32 DevKit (SPI Interface)
+
+| OLED Pin | ESP32 Pin | Description |
+|----------|-----------|-------------|
+| VCC      | 3.3V      | Power Supply |
+| GND      | GND       | Ground |
+| CLK      | GPIO 18   | SPI Clock (SCK) |
+| DIN      | GPIO 23   | SPI Data (MOSI) |
+| RES      | GPIO 16   | Reset |
+| DC       | GPIO 17   | Data/Command |
+| CS       | GPIO 5    | Chip Select |
+
+### Pin Configuration
+
+```cpp
+#define OLED_CLK  18   // SPI Clock (SCL/SCK)
+#define OLED_MOSI 23   // SPI Data (SDA/MOSI) 
+#define OLED_RES  16   // Reset
+#define OLED_DC   17   // Data/Command
+#define OLED_CS   5    // Chip Select
+```
+
+## BLE Configuration
+
+- **Device Name**: `mirror-hud`
+- **Service UUID**: `12345678-1234-1234-1234-123456789abc`
+- **Battery Characteristic**: `87654321-4321-4321-4321-cba987654321`
+- **Speed Characteristic**: `11111111-2222-3333-4444-555555555555`
+
+## Build & Upload
+
+```bash
+# Navigate to project
+cd /Users/seb/euc/mirror-display
+
+# Build for ESP32
+pio run
+
+# Upload and monitor
+pio run --target upload --target monitor
+```
+
+## Display Behavior
+
+### Boot Sequence
+1. **Power on**: Shows "tangerine" in large text with border
+2. **BLE Ready**: Shows "tangerine" with connection status
+3. **Data Received**: Switches to EUC HUD display
+
+### Data Display Layout
+```
+┌─────────────────────────────┐
+│              CONN           │
+│         EUC HUD             │
+│                             │
+│       25.4  km/h            │
+│                             │
+│ Batt: 85.2% [████████░░]    │
+└─────────────────────────────┘
+```
+
+## Troubleshooting
+
+### SPI Connection Issues
+- Verify all 7 wire connections (VCC, GND, CLK, DIN, RES, DC, CS)
+- Check 3.3V power supply (not 5V!)
+- Ensure ESP32 SPI pins are correct (18, 23, 5, 16, 17)
+- Try different ESP32 board if issues persist
+
+### Display Issues
+- Serial monitor shows SPI initialization steps
+- Look for "✓ U8g2 SPI initialization successful!"
+- Check for "SPI OLED should now show 'tangerine'!" message
+
+### Common SPI Wiring Mistakes
+- **CLK/DIN swapped**: Double-check GPIO 18 (CLK) and GPIO 23 (DIN)
+- **Missing DC/CS**: Data/Command and Chip Select are required
+- **Power issues**: Must use 3.3V, not 5V
+- **Loose connections**: SPI requires all connections to be solid
+
+## Alternative Pin Configuration
+
+If default pins don't work, try:
+```cpp
+#define OLED_CLK  14   // Alternative SPI Clock
+#define OLED_MOSI 13   // Alternative SPI Data
+#define OLED_CS   15   // Alternative Chip Select
+#define OLED_DC   2    // Alternative Data/Command
+#define OLED_RES  4    // Alternative Reset
+```

+ 18 - 0
platformio.ini

@@ -0,0 +1,18 @@
+; PlatformIO Project Configuration File for Mirror HUD Display
+;
+; ESP32 with 1.51" Transparent OLED (SSD1309)
+; BLE Server receiving battery and speed data from glove
+
+[platformio]
+default_envs = esp32dev
+
+[env:esp32dev]
+platform = espressif32
+board = esp32dev
+framework = arduino
+monitor_speed = 115200
+lib_deps = 
+    olikraus/U8g2@^2.34.22
+build_flags = 
+    -DCORE_DEBUG_LEVEL=3
+    -DCONFIG_ESP_COEX_SW_COEXIST_ENABLE

+ 409 - 0
src/main.cpp

@@ -0,0 +1,409 @@
+#include <Arduino.h>
+#include <BLEDevice.h>
+#include <BLEServer.h>
+#include <BLEUtils.h>
+#include <BLE2902.h>
+#include <SPI.h>
+#include <U8g2lib.h>
+
+// OLED Display Configuration for Waveshare 1.51" Transparent OLED
+// SSD1309 128x64 SPI Interface
+#define OLED_CLK  18   // SPI Clock (SCL/SCK)
+#define OLED_MOSI 23   // SPI Data (SDA/MOSI) 
+#define OLED_RES  16   // Reset
+#define OLED_DC   17   // Data/Command
+#define OLED_CS   5    // Chip Select
+
+// BLE Configuration
+#define SERVICE_UUID        "12345678-1234-1234-1234-123456789abc"
+#define BATTERY_CHAR_UUID   "87654321-4321-4321-4321-cba987654321"
+#define SPEED_CHAR_UUID     "11111111-2222-3333-4444-555555555555"
+#define POWER_CHAR_UUID     "22222222-3333-4444-5555-666666666666"
+#define LIGHT_CHAR_UUID     "33333333-4444-5555-6666-777777777777"
+#define DEVICE_NAME         "mirror-hud"
+
+// Display object for SSD1309 - Hardware SPI
+U8G2_SSD1309_128X64_NONAME0_F_4W_HW_SPI u8g2(U8G2_R0, OLED_CS, OLED_DC, OLED_RES);
+
+// Alternative display objects to try if the above doesn't work:
+// U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2_alt1(U8G2_R0, OLED_CS, OLED_DC, OLED_RES);
+// U8G2_SSD1309_128X64_NONAME0_F_4W_SW_SPI u8g2_alt2(U8G2_R0, OLED_CLK, OLED_MOSI, OLED_CS, OLED_DC, OLED_RES);
+
+// BLE Variables
+BLEServer* pServer = nullptr;
+BLEService* pService = nullptr;
+BLECharacteristic* pBatteryCharacteristic = nullptr;
+BLECharacteristic* pSpeedCharacteristic = nullptr;
+BLECharacteristic* pPowerCharacteristic = nullptr;
+BLECharacteristic* pLightCharacteristic = nullptr;
+bool deviceConnected = false;
+bool oldDeviceConnected = false;
+
+// Data variables
+float batteryVoltage = 0.0; // Voltage instead of percentage
+float speed = 0.0;          // Speed in km/h
+float power = 0.0;          // Power in watts
+bool lightOn = false;       // Headlight status
+unsigned long lastUpdate = 0;
+const unsigned long UPDATE_INTERVAL = 250; // Update display every 250ms (reduce load)
+bool dataReceived = false;  // Flag to track if we've received data from glove
+// BLE Server Callbacks
+class MyServerCallbacks: public BLEServerCallbacks {
+    void onConnect(BLEServer* pServer) {
+        deviceConnected = true;
+        Serial.println("BLE Client connected to mirror-hud");
+    }
+
+    void onDisconnect(BLEServer* pServer) {
+        deviceConnected = false;
+        Serial.println("BLE Client disconnected from mirror-hud");
+    }
+};
+
+// Battery Level Characteristic Callback
+class BatteryCallbacks: public BLECharacteristicCallbacks {
+    void onWrite(BLECharacteristic* pCharacteristic) {
+        std::string value = pCharacteristic->getValue();
+        
+        if (value.length() == 4) { // Expecting 4 bytes for float
+            memcpy(&batteryVoltage, value.data(), sizeof(float));
+            dataReceived = true;  // Mark that we've received data
+            Serial.print("Received battery voltage: ");
+            Serial.print(batteryVoltage);
+            Serial.println("V");
+        }
+    }
+};
+
+// Speed Characteristic Callback
+class SpeedCallbacks: public BLECharacteristicCallbacks {
+    void onWrite(BLECharacteristic* pCharacteristic) {
+        std::string value = pCharacteristic->getValue();
+        
+        if (value.length() == 4) { // Expecting 4 bytes for float
+            memcpy(&speed, value.data(), sizeof(float));
+            dataReceived = true;  // Mark that we've received data
+            Serial.print("Received speed: ");
+            Serial.print(speed);
+            Serial.println(" km/h");
+        }
+    }
+};
+
+// Power Characteristic Callback
+class PowerCallbacks: public BLECharacteristicCallbacks {
+    void onWrite(BLECharacteristic* pCharacteristic) {
+        std::string value = pCharacteristic->getValue();
+        
+        if (value.length() == 4) { // Expecting 4 bytes for float
+            memcpy(&power, value.data(), sizeof(float));
+            dataReceived = true;  // Mark that we've received data
+            Serial.print("Received power: ");
+            Serial.print(power);
+            Serial.println(" W");
+        }
+    }
+};
+
+// Light Characteristic Callback
+class LightCallbacks: public BLECharacteristicCallbacks {
+    void onWrite(BLECharacteristic* pCharacteristic) {
+        std::string value = pCharacteristic->getValue();
+        
+        if (value.length() == 1) { // Expecting 1 byte for boolean
+            lightOn = (value[0] != 0);
+            dataReceived = true;  // Mark that we've received data
+            Serial.print("Received light status: ");
+            Serial.println(lightOn ? "ON" : "OFF");
+        }
+    }
+};
+void initBLE() {
+    Serial.println("Initializing BLE...");
+    
+    // Initialize BLE
+    BLEDevice::init(DEVICE_NAME);
+    
+    // Create BLE Server
+    pServer = BLEDevice::createServer();
+    pServer->setCallbacks(new MyServerCallbacks());
+    
+    // Create BLE Service
+    pService = pServer->createService(SERVICE_UUID);
+    
+    // Create Battery Characteristic
+    pBatteryCharacteristic = pService->createCharacteristic(
+        BATTERY_CHAR_UUID,
+        BLECharacteristic::PROPERTY_READ |
+        BLECharacteristic::PROPERTY_WRITE |
+        BLECharacteristic::PROPERTY_NOTIFY
+    );
+    pBatteryCharacteristic->setCallbacks(new BatteryCallbacks());
+    pBatteryCharacteristic->addDescriptor(new BLE2902());
+    
+    // Create Speed Characteristic
+    pSpeedCharacteristic = pService->createCharacteristic(
+        SPEED_CHAR_UUID,
+        BLECharacteristic::PROPERTY_READ |
+        BLECharacteristic::PROPERTY_WRITE |
+        BLECharacteristic::PROPERTY_NOTIFY
+    );
+    pSpeedCharacteristic->setCallbacks(new SpeedCallbacks());
+    pSpeedCharacteristic->addDescriptor(new BLE2902());
+    
+    // Create Power Characteristic
+    pPowerCharacteristic = pService->createCharacteristic(
+        POWER_CHAR_UUID,
+        BLECharacteristic::PROPERTY_READ |
+        BLECharacteristic::PROPERTY_WRITE |
+        BLECharacteristic::PROPERTY_NOTIFY
+    );
+    pPowerCharacteristic->setCallbacks(new PowerCallbacks());
+    pPowerCharacteristic->addDescriptor(new BLE2902());
+    
+    // Create Light Characteristic
+    pLightCharacteristic = pService->createCharacteristic(
+        LIGHT_CHAR_UUID,
+        BLECharacteristic::PROPERTY_READ |
+        BLECharacteristic::PROPERTY_WRITE |
+        BLECharacteristic::PROPERTY_NOTIFY
+    );
+    pLightCharacteristic->setCallbacks(new LightCallbacks());
+    pLightCharacteristic->addDescriptor(new BLE2902());
+    
+    // Start the service
+    pService->start();
+    
+    // Start advertising
+    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
+    pAdvertising->addServiceUUID(SERVICE_UUID);
+    pAdvertising->setScanResponse(false);
+    pAdvertising->setMinPreferred(0x0);
+    BLEDevice::startAdvertising();
+    
+    Serial.println("BLE Server started, advertising as 'mirror-hud'");
+}
+void initDisplay() {
+    Serial.println("=== OLED Display Initialization (SPI) ===");
+    
+    // Configure SPI pins
+    Serial.println("Configuring SPI pins...");
+    pinMode(OLED_CS, OUTPUT);
+    pinMode(OLED_DC, OUTPUT);
+    pinMode(OLED_RES, OUTPUT);
+    
+    // Manual reset sequence
+    Serial.println("Performing manual reset...");
+    digitalWrite(OLED_RES, LOW);
+    delay(100);
+    digitalWrite(OLED_RES, HIGH);
+    delay(100);
+    
+    // Initialize SPI
+    Serial.println("Initializing SPI...");
+    SPI.begin(OLED_CLK, -1, OLED_MOSI, OLED_CS); // CLK, MISO(-1), MOSI, CS
+    
+    Serial.print("SPI pins - CLK:");
+    Serial.print(OLED_CLK);
+    Serial.print(" MOSI:");
+    Serial.print(OLED_MOSI);
+    Serial.print(" CS:");
+    Serial.print(OLED_CS);
+    Serial.print(" DC:");
+    Serial.print(OLED_DC);
+    Serial.print(" RES:");
+    Serial.println(OLED_RES);
+    
+    // Initialize U8g2 with SPI
+    Serial.println("Initializing U8g2 library for SPI...");
+    
+    bool displayInit = u8g2.begin();
+    if (displayInit) {
+        Serial.println("✓ U8g2 SPI initialization successful!");
+    } else {
+        Serial.println("✗ U8g2 SPI initialization failed, but continuing...");
+    }
+    
+    // Force display setup
+    u8g2.clearBuffer();
+    u8g2.setDisplayRotation(U8G2_R0);
+    u8g2.setPowerSave(0);  // Wake up display
+    
+    // Test with simple elements
+    Serial.println("Testing SPI display...");
+    u8g2.clearBuffer();
+    u8g2.drawPixel(64, 32);  // Center pixel
+    u8g2.sendBuffer();
+    delay(500);
+    
+    // Test with rectangle
+    u8g2.clearBuffer();
+    u8g2.drawBox(10, 10, 20, 20);
+    u8g2.sendBuffer();
+    delay(500);
+    
+    // Show "tangerine" boot screen
+    Serial.println("Displaying tangerine boot screen via SPI...");
+    u8g2.clearBuffer();
+    u8g2.setFont(u8g2_font_ncenB18_tr);
+    u8g2.drawStr(15, 35, "tangerine");
+    
+    // Add border to confirm display is working
+    u8g2.drawFrame(0, 0, 128, 64);
+    u8g2.sendBuffer();
+    
+    Serial.println("SPI OLED should now show 'tangerine'!");
+    delay(3000);  // Show "tangerine" for 3 seconds
+    
+    Serial.println("=== SPI OLED Display Initialization Complete ===");
+}
+void updateDisplay() {
+    u8g2.clearBuffer();
+    
+    if (!dataReceived) {
+        // Show "tangerine" screen when no data received
+        u8g2.setFont(u8g2_font_ncenB18_tr);
+        u8g2.drawStr(15, 35, "tangerine");
+        
+        // Show connection status
+        u8g2.setFont(u8g2_font_6x10_tr);
+        if (deviceConnected) {
+            u8g2.drawStr(30, 55, "Connected");
+        } else {
+            u8g2.drawStr(25, 55, "Waiting...");
+        }
+    } else {
+        // Show EUC data when received from glove
+        
+        // Connection status indicator
+        if (deviceConnected) {
+            u8g2.setFont(u8g2_font_4x6_tr);
+            u8g2.drawStr(100, 8, "CONN");
+        } else {
+            u8g2.setFont(u8g2_font_4x6_tr);
+            u8g2.drawStr(95, 8, "DISC");
+        }
+        
+        // Speed Display - Large and prominent
+        u8g2.setFont(u8g2_font_ncenB24_tn);  // Increased from 18 to 24
+        char speedStr[10];
+        dtostrf(speed, 4, 1, speedStr);
+        u8g2.drawStr(5, 35, speedStr);  // Moved left to make room for larger font
+        
+        // "K" instead of "km/h"
+        u8g2.setFont(u8g2_font_ncenB12_tr);  // Larger K
+        u8g2.drawStr(85, 35, "K");
+        
+        // Power Display where km/h used to be
+        u8g2.setFont(u8g2_font_ncenB08_tr);
+        char powerStr[15];
+        sprintf(powerStr, "%.0fW", power);
+        u8g2.drawStr(100, 35, powerStr);
+        
+        // Battery Voltage Display
+        u8g2.setFont(u8g2_font_ncenB08_tr);
+        char voltStr[15];
+        sprintf(voltStr, "%.1fV", batteryVoltage);
+        u8g2.drawStr(10, 55, voltStr);
+        
+        // 5-Bar Battery Display based on actual voltage range
+        // 134.4V = 100%, 117.7V = 44% 
+        float minVoltage = 104.58;   // Calculated 0% voltage
+        float maxVoltage = 134.4;    // 100% voltage
+        
+        // Calculate number of bars to display (0-5)
+        int numBars = 0;
+        if (batteryVoltage >= 134.40) numBars = 5;      // 100%+
+        else if (batteryVoltage >= 128.44) numBars = 4; // 80%+
+        else if (batteryVoltage >= 122.47) numBars = 3; // 60%+
+        else if (batteryVoltage >= 116.51) numBars = 2; // 40%+
+        else if (batteryVoltage >= 110.54) numBars = 1; // 20%+
+        else numBars = 0;                               // <20%
+        
+        // Draw 5 individual battery bars (each 8px wide, 2px spacing)
+        int barX = 70;  // Starting X position
+        int barY = 48;  // Y position
+        int barWidth = 8;
+        int barHeight = 8;
+        int barSpacing = 2;
+        
+        for (int i = 0; i < 5; i++) {
+            int x = barX + i * (barWidth + barSpacing);
+            
+            // Draw bar outline
+            u8g2.drawFrame(x, barY, barWidth, barHeight);
+            
+            // Fill bar if it should be active
+            if (i < numBars) {
+                u8g2.drawBox(x + 1, barY + 1, barWidth - 2, barHeight - 2);
+            }
+        }
+        
+        // Show percentage for debugging
+        float percentage = ((batteryVoltage - minVoltage) / (maxVoltage - minVoltage)) * 100.0;
+        if (percentage < 0) percentage = 0;
+        if (percentage > 100) percentage = 100;
+        
+        char percentStr[8];
+        sprintf(percentStr, "%.0f%%", percentage);
+        u8g2.setFont(u8g2_font_4x6_tr);
+        u8g2.drawStr(70, 63, percentStr);
+        
+        // Headlight icon at bottom left
+        if (lightOn) {
+            // Draw filled headlight icon when on
+            u8g2.drawCircle(8, 58, 4);      // Outer circle
+            u8g2.drawDisc(8, 58, 2);        // Inner filled circle
+            u8g2.drawLine(8, 54, 8, 50);    // Light beam line
+            u8g2.drawLine(6, 55, 4, 53);    // Left beam
+            u8g2.drawLine(10, 55, 12, 53);  // Right beam
+        } else {
+            // Draw outline headlight icon when off
+            u8g2.drawCircle(8, 58, 4);      // Outer circle only
+            u8g2.drawCircle(8, 58, 2);      // Inner circle outline
+        }
+    }
+    
+    // Send buffer to display
+    u8g2.sendBuffer();
+}
+void setup() {
+    Serial.begin(115200);
+    delay(1000);  // Give time for serial to initialize
+    Serial.println();
+    Serial.println("=== Mirror HUD Display Starting ===");
+    Serial.println("ESP32 Standard Board");
+    Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
+    
+    // Initialize OLED display first
+    initDisplay();
+    
+    // Initialize BLE
+    initBLE();
+    
+    Serial.println("=== Mirror HUD Ready! ===");
+    Serial.printf("Free heap after init: %d bytes\n", ESP.getFreeHeap());
+}
+
+void loop() {
+    // Handle BLE disconnection
+    if (!deviceConnected && oldDeviceConnected) {
+        delay(500); // Give time for BLE stack
+        pServer->startAdvertising(); // Restart advertising
+        Serial.println("Restarting BLE advertising...");
+        oldDeviceConnected = deviceConnected;
+    }
+    
+    // Handle BLE connection
+    if (deviceConnected && !oldDeviceConnected) {
+        oldDeviceConnected = deviceConnected;
+    }
+    
+    // Update display at regular intervals
+    if (millis() - lastUpdate > UPDATE_INTERVAL) {
+        updateDisplay();
+        lastUpdate = millis();
+    }
+    
+    delay(10);
+}

+ 60 - 0
test_ssd1306.cpp

@@ -0,0 +1,60 @@
+#include <Arduino.h>
+#include <BLEDevice.h>
+#include <BLEServer.h>
+#include <BLEUtils.h>
+#include <BLE2902.h>
+#include <Wire.h>
+#include <U8g2lib.h>
+
+// OLED Display Configuration - Alternative SSD1306 mode
+#define OLED_SDA 21
+#define OLED_SCL 22
+#define OLED_RST 16
+#define OLED_ADDR 0x3C
+
+// BLE Configuration
+#define SERVICE_UUID        "12345678-1234-1234-1234-123456789abc"
+#define BATTERY_CHAR_UUID   "87654321-4321-4321-4321-cba987654321"
+#define SPEED_CHAR_UUID     "11111111-2222-3333-4444-555555555555"
+#define DEVICE_NAME         "mirror-hud"
+
+// Try SSD1306 compatibility mode (comment out SSD1309 line above and use this)
+U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, OLED_RST, OLED_SCL, OLED_SDA);
+
+// Other variables and classes remain the same...
+bool dataReceived = false;
+float batteryLevel = 0.0;
+float speed = 0.0;
+
+void initDisplay() {
+    Serial.println("=== TRYING SSD1306 MODE ===");
+    
+    Wire.begin(OLED_SDA, OLED_SCL);
+    
+    // Manual reset
+    pinMode(OLED_RST, OUTPUT);
+    digitalWrite(OLED_RST, LOW);
+    delay(100);
+    digitalWrite(OLED_RST, HIGH);
+    delay(100);
+    
+    u8g2.begin();
+    u8g2.clearBuffer();
+    u8g2.setFont(u8g2_font_ncenB18_tr);
+    u8g2.drawStr(15, 35, "tangerine");
+    u8g2.sendBuffer();
+    
+    Serial.println("SSD1306 mode display initialized");
+}
+
+// Minimal main functions for testing
+void setup() {
+    Serial.begin(115200);
+    delay(1000);
+    Serial.println("Starting OLED test...");
+    initDisplay();
+}
+
+void loop() {
+    delay(1000);
+}