commit 4de531147e6535fa4f92f388e7f7521842564324 Author: cpu Date: Thu Oct 30 14:45:28 2025 +0100 Initial commit diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..7d920e2 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,28 @@ +# STM32 Bluepill Quickstart + +## Temporary files +Create files and directories within the project temp directory e.g.: `~/.gemini/tmp/d9929d1c794fb414052498bc12a08fef640ff8b7c30d9190d0699962781facbf` + +## Compilation + +To compile the sketch, run the following command from the project root: + +```bash +arduino-cli compile --fqbn STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103CB,upload_method=OpenOCDDapLink --build-path /tmp/$(basename $(pwd)) . +``` + +## Flashing + +To flash the sketch use `openocd` to flash the sketch. + +```bash +openocd -d2 -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c "program {/tmp/$(basename $(pwd))/$(basename $(pwd)).ino.elf} verify reset exit" +``` + +## Viewing Logs + +The best way for me to read the logs is to capture the output from the serial port to a file, and then read that file. + +1. First, I'd configure the serial port settings without starting an interactive session using `stty`. +2. Then, I'd use a command like `head` to read a specific number of lines from the serial port and save them to a temporary file. This command will exit automatically after reading the lines. +3. Finally, I would use `read_file` to read the contents of that temporary file. diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..c2aa480 --- /dev/null +++ b/Readme.md @@ -0,0 +1,205 @@ +# SSD1306 OLED Servo Control with STM32 Blue Pill + +This project demonstrates how to control a servo motor using an IBT_2 (BTS7960) motor driver, an STM32 Blue Pill, and a 128x64 SSD1306 OLED display. The user can control the motor's speed and direction using four push buttons, with real-time feedback on the OLED screen. + +## Features + +* **Motor Control:** Adjust speed and direction of a servo motor. +* **OLED Display:** A 128x64 SSD1306 OLED display shows the current speed, direction, and motor state (ON/OFF). +* **User Interface:** A simple and intuitive interface with four buttons for control (Up, Down, OK, Cancel). +* **Two-Color Display Optimization:** The UI is designed for two-color (Yellow and Blue) OLED displays, with a static header in the yellow section and dynamic values in the blue section. + +## Hardware Required + +* STM32F103C8T6 "Blue Pill" development board +* 128x64 SSD1306 OLED display (I2C) with 4 push buttons (up, down, OK, Back) +* IBT_2 (dual BTS7960) motor driver +* +* Servo motor e.g.: RS-550 or R-775 +* External power supply for the motor 12-24V/5A +* Breadboard and jumper wires + +## Wiring + +Connect the components to the Blue Pill as described below. + +### OLED Display (I2C) + +| OLED Display Pin | Blue Pill Pin | +| :--------------- | :------------ | +| VCC | 3.3V | +| GND | GND | +| SCL | PB6 | +| SDA | PB7 | + +### Control Buttons + +The buttons are connected to GPIO pins using the internal pull-up resistors. The other terminal of each button should be connected to GND. + +| Button Function | Blue Pill Pin | +| :-------------- | :------------ | +| Up | PA0 | +| Down | PA1 | +| OK | PA2 | +| Cancel | PA3 | + +### IBT_2 Motor Driver + +The IBT_2 module requires its own power supply for the motor. + +| IBT_2 Pin | Blue Pill Pin | Description | +| :-------- | :------------ | :--------------------------- | +| VCC | 5V | Logic Power | +| GND | GND | Logic Ground | +| R_EN | 3.3V | Right Enable (connect to 3.3V) | +| L_EN | 3.3V | Left Enable (connect to 3.3V) | +| RPWM | PB0 | Right PWM Signal (Forward) | +| LPWM | PB1 | Left PWM Signal (Reverse) | + +### DAPLink UART + +For debugging and logging, you can connect the DAPLink's virtual COM port to the Blue Pill's UART1. + +| DAPLink Pin | Blue Pill Pin | Description | +| :---------- | :------------ | :------------------ | +| RXD | PA9 (TX1) | Connect to TX | +| TXD | PA10 (RX1) | Connect to RX | +| GND | GND | Common Ground | + +**IBT_2 Power Connections:** + +| IBT_2 Terminal | Connection | +| :------------- | :----------------------------- | +| B+ | Positive of Motor Power Supply | +| B- | Negative of Motor Power Supply | +| M+ | Positive of Servo Motor | +| M- | Negative of Servo Motor | + +## Dependencies + +This sketch requires the following Arduino libraries: +* `Wire` +* `Adafruit_GFX` +* `Adafruit_SSD1306` +* `Adafruit_BusIO` (A dependency for the Adafruit libraries) + +## Getting Started + +### Prerequisites + +* [Arduino CLI](https://arduino.github.io/arduino-cli/installation/) +* [STMicroelectronics STM32 core](https://github.com/stm32duino/Arduino_Core_STM32) + +### Installation + +1. **Install arduino-cli:** Follow the official instructions at [arduino.github.io/arduino-cli/installation/](https://arduino.github.io/arduino-cli/installation/). + +2. **Initialize and Configure arduino-cli:** + * Create a default configuration file: + ```bash + arduino-cli config init + ``` + * Add the STMicroelectronics board manager URL: + ```bash + arduino-cli config add board_manager.additional_urls https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json + ``` + * Update the local core index: + ```bash + arduino-cli core update-index + ``` + * Install the STM32 core: + ```bash + arduino-cli core install STMicroelectronics:stm32 + ``` + +3. **Install Required Libraries:** + ```bash + arduino-cli lib install "Adafruit SSD1306" "Adafruit GFX Library" "Adafruit BusIO" + ``` + +## Board Setup + +In Arduino IDE → Tools, set: + + Board → STM32 MCU based boards → Generic STM32F1 series + + Board part number → BluePill F103CB (or C8 with 128K) + + Upload Method → OpenOCD DAPLink (SWD) + +## Compilation + +1. **Prepare Your Sketch Directory:** Ensure your sketch file is named `SSD1306_sketch.ino` and is located in a folder with the same name. For example: + `/path/to/your/project/SSD1306_sketch/SSD1306_sketch.ino` + +2. **Compile the Sketch:** Compile the sketch for the Blue Pill (STM32F103CB or C8 with 128k), specifying the OpenOCD upload method. + ```bash + arduino-cli compile --fqbn STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103CB,upload_method=OpenOCDDapLink /path/to/your/project/SSD1306_sketch + ``` + The binary files will be generated in a temporary build directory. To find the path to the binary, you can run the compile command with the `--verbose` flag. + +## Upload to the Board + +The compiled sketch can be uploaded to the Blue Pill using `openocd`. The exact command may vary depending on your setup. + +First, you need to find the path to your compiled `.elf` file. You can find this in the output of the `arduino-cli compile` command. It will be in a temporary directory `~/.cache/arduino/sketches`. + +Then, find the `openocd` e.g.: `~/.arduino15/packages/STMicroelectronics/tools/xpack-openocd/0.12.0-6/bin/openocd` or install a distro specific version e.g.: `sudo apt install openocd`. Example of the path to the compiled sketch: `/home/martin/.cache/arduino/sketches/96DBB1C909C240C2F96DCDD5DDFB9A12/SSD1306_sketch.ino.elf` + +Here is an example command to upload the sketch using DAPLink: + +```bash +/path/to/your/openocd -d2 -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c "program {/path/to/your/sketch.ino.elf} verify reset exit" +``` + +## 🐞 Hardware Debugging with DAPLink (CMSIS-DAP) on Blue Pill + +This section describes how to enable **hardware debugging** for the STM32F103 "Blue Pill" board using a **DAPLink / CMSIS-DAP** debugger in **Arduino IDE 2**. +By default, the STM32 Arduino core loads the wrong OpenOCD configuration (`dapdirect_swd`, meant for ST-Link). + +Until the (bug)[https://github.com/stm32duino/Arduino_Core_STM32/issues/2807] is not fixed, follow these steps to fix it: + +### 🧭 1. Locate STM32 Core Folder (Linux) + +Open a terminal and navigate to your STM32 Arduino core installation: + +```bash +cd ~/.arduino15/packages/STMicroelectronics/hardware/stm32/2.11.0/ +``` + Adjust the version number if different (check in Arduino IDE → Tools → Board → Boards Manager). + +🧰 2. Create or Edit platform.local.txt + +Create (or edit) a local override file: + +```bash +nano platform.local.txt +``` + +Paste the following lines: + +```bash +debug.server.openocd.scripts.0=interface/cmsis-dap.cfg +debug.server.openocd.scripts.1={runtime.platform.path}/debugger/select_swd.cfg +``` + +Save and close (Ctrl + O, Enter, Ctrl + X). + +These lines tell OpenOCD to: + + Use the CMSIS-DAP interface (interface/cmsis-dap.cfg) + + Use standard SWD transport (select_swd.cfg) instead of dapdirect_swd + +🔁 3. Restart Arduino IDE + +Completely close and reopen Arduino IDE 2 to apply the new settings. +⚙️ 4. Configure the IDE + +In Arduino IDE → Tools, set: + + Debug symbols and core logs → Core Logs and Symbols Enabled (-g) + + Optimize → Debug (-Og) + +Then click the 🐞 Debug icon and press Start Debugging. \ No newline at end of file diff --git a/SSD1306_sketch.ino b/SSD1306_sketch.ino new file mode 100644 index 0000000..47cbfe9 --- /dev/null +++ b/SSD1306_sketch.ino @@ -0,0 +1,139 @@ +#include +#include +#include + +// Screen dimensions +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +// OLED display setup +#define OLED_RESET -1 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +// Button pins +#define UP_BUTTON_PIN PA0 +#define DOWN_BUTTON_PIN PA1 +#define OK_BUTTON_PIN PA2 +#define CANCEL_BUTTON_PIN PA3 + +// IBT_2 Motor Driver pins +#define RPWM_PIN PB0 +#define LPWM_PIN PB1 + +// Motor control variables +int motorSpeed = 0; +bool motorDirection = true; // true for forward, false for reverse +bool motorEnabled = false; + +void setup() { + Serial.begin(115200); + + // Initialize OLED display + if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + Serial.println(F("SSD1306 allocation failed")); + for(;;); + } + + // Button pin setup with internal pull-up resistors + pinMode(UP_BUTTON_PIN, INPUT_PULLUP); + pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP); + pinMode(OK_BUTTON_PIN, INPUT_PULLUP); + pinMode(CANCEL_BUTTON_PIN, INPUT_PULLUP); + + // Motor driver pin setup + pinMode(RPWM_PIN, OUTPUT); + pinMode(LPWM_PIN, OUTPUT); + + updateDisplay(); +} + +void loop() { + bool needsUpdate = false; + + // Button handling + if (digitalRead(UP_BUTTON_PIN) == LOW) { + Serial.println("Up button pressed"); + motorSpeed += 5; + if (motorSpeed > 255) motorSpeed = 255; + needsUpdate = true; + delay(100); // Simple debounce + } + + if (digitalRead(DOWN_BUTTON_PIN) == LOW) { + Serial.println("Down button pressed"); + motorSpeed -= 5; + if (motorSpeed < 0) motorSpeed = 0; + needsUpdate = true; + delay(100); // Simple debounce + } + + if (digitalRead(OK_BUTTON_PIN) == LOW) { + Serial.println("OK button pressed"); + motorEnabled = !motorEnabled; + needsUpdate = true; + delay(200); // Simple debounce + } + + if (digitalRead(CANCEL_BUTTON_PIN) == LOW) { + Serial.println("Cancel button pressed"); + motorDirection = !motorDirection; + needsUpdate = true; + delay(200); // Simple debounce + } + + // Only update the display if a button was pressed + if (needsUpdate) { + updateDisplay(); + } + + // Update motor driver + if (motorEnabled) { + if (motorDirection) { + analogWrite(RPWM_PIN, motorSpeed); + analogWrite(LPWM_PIN, 0); + } else { + analogWrite(RPWM_PIN, 0); + analogWrite(LPWM_PIN, motorSpeed); + } + } else { + analogWrite(RPWM_PIN, 0); + analogWrite(LPWM_PIN, 0); + } +} + +void updateDisplay() { + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + + // --- TOP YELLOW SECTION (Labels) --- + display.setTextSize(1); + display.setCursor(5, 4); + display.print("SPEED"); + display.setCursor(50, 4); + display.print("DIR"); + display.setCursor(95, 4); + display.print("STATE"); + display.drawLine(0, 15, 127, 15, SSD1306_WHITE); // Separator line + + // --- BOTTOM BLUE SECTION (Values) --- + display.setTextSize(2); + + // Display Speed Value with padding for alignment + display.setCursor(0, 25); + if (motorSpeed < 10) { + display.print(" "); + } else if (motorSpeed < 100) { + display.print(" "); + } + display.print(motorSpeed); + + // Display Direction Value + display.setCursor(45, 25); + display.print(motorDirection ? "FWD" : "REV"); + + // Display State Value + display.setCursor(95, 25); + display.print(motorEnabled ? "ON" : "OFF"); + + display.display(); +} \ No newline at end of file