Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Diagrams / Code (Reference)

Servo Buddy [Beta] Code:

C++:
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneButton.h>
#include <Servo.h>
#include <RunningMedian.h>

// Define the PolarizationStatus enum
enum class PolarizationStatus {
  BOTH,
  PIN1,
  PIN3,
  NONE
};

// Define the DeviceStatus enum
enum class DeviceStatus {
  ESC,
  SERVO,
  NO_CONNECTION
};

// Pin numbers
const int mosfetPin3 = 2;
const int mosfetPin1 = 3;
const int buttonPin = 14;
const int PWM_analog_Read_Pin3 = 26;
const int PWM_analog_Read_Pin1 = 27;
const int ESC_voltage_Check = 28;
const int auto_Power_off = 7;
const int Connect_Center_Pin_to_5V = 15;

// Define the LED pins in order from left to right
const int ledPins[] = {18, 17, 16, 21, 20, 19}; // {White Left, Blue Left, White Center, Blue Center, Blue Right, White Right}
const int numLeds = sizeof(ledPins) / sizeof(ledPins[0]);

// Brightness levels
const int fullBrightness = 0;  // 255 brightness
const int dimBrightness = 0;    // 50 brightness

// Indices for LEDs
const int whiteLeft = 0;
const int blueLeft = 1;
const int whiteCenter = 2;
const int blueCenter = 3;
const int blueRight = 4;
const int whiteRight = 5;

unsigned long previousMillis = 0;
const long scanningInterval = 1000;
const long servoScanningInterval = 3000;

const unsigned int buttonShutoffTime = 2000;   // Time to hold the button for a shutdown
const unsigned int escRuntime = 750;           // Duration for the ESC test
const unsigned long powerOffInterval = 60000;  // 60 seconds
unsigned long lastButtonPressTime = 0;
unsigned long escTestStartTime = 0;

unsigned long lastBatteryCheck = 0;
const unsigned int batteryCheckInterval = 10;
RunningMedian batteryMedian = RunningMedian(20);

bool escTestActive = false;
bool manualMode = false;
bool deviceDetected = false;
DeviceStatus deviceStatus = DeviceStatus::NO_CONNECTION;
PolarizationStatus lastServoDirection = PolarizationStatus::NONE;
PolarizationStatus escDirection = PolarizationStatus::NONE;

unsigned long pressStartTime = 0;

int buttonPressCount = 0;
int servoOutputState = 0;
const unsigned long doublePressTime = 500;

// Define the OLED display object
const uint8_t screenWidth = 128;
const uint8_t screenHeight = 64;
Adafruit_SSD1306 OLED(screenWidth, screenHeight, &Wire, -1);

const int batteryHigh = 660;
const int batteryMed = 650;
const int batteryLow = 640;
const int batteryDead = 600;

int lastBatteryLevel = 0;

bool confirmPowerOff = false;

// Initialize the OneButton object
OneButton button;

// Declare a Servo object
Servo myServo;

struct TextDimensions {
  uint16_t w;
  uint16_t h;

  TextDimensions(const char s[]) {
    int16_t uX, uY;
    OLED.getTextBounds(s, 0, 0, &uX, &uY, &w, &h);
  }
};

// Functions with default parameters must be pre-declared
void drawCenteredText(const char str[], int16_t x, int16_t y, uint8_t textSize, uint16_t c, uint16_t bg = 255);
void drawText(const char str[], int16_t x, int16_t y, uint8_t textSize, uint16_t c, uint16_t bg = 255);

void setup() {
  Serial.begin(115200);

  // Initialize each LED pin as OUTPUT
  for (int i = 0; i < numLeds; i++) {
    pinMode(ledPins[i], OUTPUT);
    analogWrite(ledPins[i], 0); // Set all LED pins to OUTPUT and LOW
  }
 
  allDimWhite(); // Set all LEDs to dim brightness initially

  pinMode(mosfetPin1, OUTPUT);
  pinMode(mosfetPin3, OUTPUT);
  pinMode(ESC_voltage_Check, INPUT);
  pinMode(auto_Power_off, OUTPUT);
  pinMode(Connect_Center_Pin_to_5V, OUTPUT);

  digitalWrite(mosfetPin1, LOW);
  digitalWrite(mosfetPin3, LOW);
  digitalWrite(auto_Power_off, HIGH);
  digitalWrite(Connect_Center_Pin_to_5V, LOW);

  button.setup(buttonPin, INPUT_PULLUP, true);

  if (!OLED.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }
  OLED.clearDisplay();
  OLED.display();
  bootlogo();

  // Wait a bit after logo is displayed
  delay(500);

  button.setPressMs(0);
  button.setDebounceMs(20);
  button.attachLongPressStart(pressStart);
  button.attachDuringLongPress(pressDuration);
  button.attachLongPressStop(pressEnd);
}

void loop() {
  button.tick();

  // Check if the device should power off
  bool shouldPowerOff = millis() - lastButtonPressTime >= powerOffInterval;


  if (deviceStatus != DeviceStatus::ESC && !manualMode) {
    if (!shouldPowerOff) {
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= (deviceStatus == DeviceStatus::SERVO ? servoScanningInterval : scanningInterval)) {
        previousMillis = currentMillis;
        checkDeviceStatus();
      }
    } else
      powerOff();
  }

  if (escTestActive) {
    // Test is finished
    if (millis() - escTestStartTime >= escRuntime) {
      escTestActive = false;
      escOutputOFF();
    }
  }

  if (deviceStatus == DeviceStatus::SERVO || deviceStatus == DeviceStatus::NO_CONNECTION) {
    if (millis() - lastBatteryCheck >= batteryCheckInterval) {
      lastBatteryCheck = millis();
      lastBatteryLevel = analogRead(ESC_voltage_Check);
      batteryMedian.add(lastBatteryLevel);
    }
  }

  if (!confirmPowerOff)
    updateDisplay();
}

void swingServo() {
  buttonPressCount++;
  lastButtonPressTime = millis();  // Reset the timer on button press

  switch (servoOutputState) {
    case 0:
      myServo.writeMicroseconds(1000);
      servoOutputState = 1;
      break;
    case 1:
      myServo.writeMicroseconds(1500);
      servoOutputState = 2;
      break;
    case 2:
      myServo.writeMicroseconds(2000);
      servoOutputState = 3;
      break;
    case 3:
    case -1:
      myServo.writeMicroseconds(1500);
      servoOutputState = 0;
      break;
  }
}

void escOutputON() {
  if (deviceStatus == DeviceStatus::ESC)
    myServo.writeMicroseconds(1300);
}

void escOutputOFF() {
  if (deviceStatus == DeviceStatus::ESC)
    myServo.writeMicroseconds(1000);
}

void pressStart() {
  if (manualMode && millis() - pressStartTime >= doublePressTime) {
    digitalWrite(Connect_Center_Pin_to_5V, HIGH);

    deviceStatus = DeviceStatus::SERVO;

    lastServoDirection = PolarizationStatus::PIN1;
    myServo.writeMicroseconds(1500);

    digitalWrite(mosfetPin1, HIGH);
    digitalWrite(mosfetPin3, LOW);
    myServo.attach(PWM_analog_Read_Pin3);

    servoOutputState = 1;
    swingServo();
  }

  if (millis() - pressStartTime < doublePressTime)
    doublePress();

  pressStartTime = millis();

  if (deviceStatus == DeviceStatus::SERVO) {
    swingServo();
  } else if (deviceStatus == DeviceStatus::ESC) {
    if (!escTestActive) {
      escOutputON();
      escTestStartTime = millis();
      escTestActive = true;
    }
  } else {
    if (!manualMode)
      checkDeviceStatus();
  }
}

void pressDuration() {
  long pressTime = millis() - pressStartTime;

  if (pressTime >= buttonShutoffTime && deviceStatus != DeviceStatus::ESC) {
    powerOff();
  }
}

void pressEnd() {
}

void doublePress() {
  if (deviceStatus == DeviceStatus::NO_CONNECTION) {
    /*  if (!manualMode)
      manualMode = true;
    else {
      deviceStatus = DeviceStatus::ESC;

      digitalWrite(Connect_Center_Pin_to_5V, LOW);

      digitalWrite(mosfetPin1, HIGH);
      digitalWrite(mosfetPin3, LOW);
      myServo.attach(PWM_analog_Read_Pin3);

      myServo.writeMicroseconds(1200);

      escDirection = PolarizationStatus::PIN3;

      escOutputOFF();
    }*/
  }
}

void checkDeviceStatus() {
  int read1 = 0;
  int read3 = 0;
  int escVoltage = 0;

  checkESCVoltage(escVoltage);

  if (escVoltage > 650) {
    deviceStatus = DeviceStatus::ESC;
    deviceDetected = true;

    digitalWrite(Connect_Center_Pin_to_5V, LOW);
    PolarizationStatus polarizationStatus = polarizationCheck(DeviceStatus::ESC, read1, read3);

    if (polarizationStatus == PolarizationStatus::PIN1) {
      digitalWrite(mosfetPin1, LOW);
      digitalWrite(mosfetPin3, HIGH);
      myServo.attach(PWM_analog_Read_Pin1);
    } else if (polarizationStatus == PolarizationStatus::PIN3) {
      digitalWrite(mosfetPin1, HIGH);
      digitalWrite(mosfetPin3, LOW);
      myServo.attach(PWM_analog_Read_Pin3);
    }

    digitalWrite(auto_Power_off, LOW);  // Turn off the auto power off feature when ESC is detected so that the board powers down as soon as the esc is disconnected

    myServo.writeMicroseconds(1200);

    escDirection = polarizationStatus;

    switch (polarizationStatus) {
      case PolarizationStatus::PIN1:
      case PolarizationStatus::PIN3:
        escOutputOFF();
        break;
    }
    return;
  } else {
    digitalWrite(Connect_Center_Pin_to_5V, HIGH);

    PolarizationStatus polarizationStatus = polarizationCheck(DeviceStatus::SERVO, read1, read3);

    if (polarizationStatus == PolarizationStatus::NONE || polarizationStatus == PolarizationStatus::BOTH) {
      deviceStatus = DeviceStatus::NO_CONNECTION;
    } else {
      deviceStatus = DeviceStatus::SERVO;
      deviceDetected = true;

      if (polarizationStatus == PolarizationStatus::PIN3) {
        digitalWrite(mosfetPin1, LOW);
        digitalWrite(mosfetPin3, HIGH);
        myServo.attach(PWM_analog_Read_Pin1);
      } else if (polarizationStatus == PolarizationStatus::PIN1) {
        digitalWrite(mosfetPin1, HIGH);
        digitalWrite(mosfetPin3, LOW);
        myServo.attach(PWM_analog_Read_Pin3);
      }

      if (polarizationStatus == lastServoDirection) {
        // Servo direction hasn't changed since last check. Do nothing
        return;
      } else {
        lastServoDirection = polarizationStatus;
        myServo.writeMicroseconds(1500);
      }
    }
  }

  switch (deviceStatus) {
    case DeviceStatus::SERVO:
      servoOutputState = 1;
      swingServo();
      break;
    case DeviceStatus::NO_CONNECTION:
      myServo.detach();
      deviceDetected = false;
      servoOutputState = 0;
      allDimWhite();
      break;
  }
}

PolarizationStatus polarizationCheck(DeviceStatus statusToCheck, int &read1, int &read3) {
  int saveMicros = myServo.readMicroseconds();

  myServo.detach();

  // myServo.detach resets the servo to center for some reason so in servo mode you have to preserve the timings
  if (deviceStatus == DeviceStatus::SERVO)
    myServo.writeMicroseconds(saveMicros);

  pinMode(PWM_analog_Read_Pin1, INPUT);
  pinMode(PWM_analog_Read_Pin3, INPUT);
  bool pin1HasVoltage = false;
  bool pin3HasVoltage = false;

  // Check pin 1
  digitalWrite(mosfetPin1, LOW);
  digitalWrite(mosfetPin3, HIGH);
  delay(10);
  read1 = analogRead(PWM_analog_Read_Pin1);
  if (read1 > 250) {
    pin1HasVoltage = true;
  }

  // Check pin 3
  digitalWrite(mosfetPin1, HIGH);
  digitalWrite(mosfetPin3, LOW);
  delay(10);
  read3 = analogRead(PWM_analog_Read_Pin3);
  if (read3 > 250) {
    pin3HasVoltage = true;
  }

  if (statusToCheck == DeviceStatus::SERVO) {
    // Special Edge cases

    if (pin1HasVoltage && pin3HasVoltage) {
      if (read1 > read3) {
        pin1HasVoltage = false;
      } else if (read3 > read1) {
        pin3HasVoltage = false;
      }
    }
    if (read1 < 950 && read3 > 950) {
      pin1HasVoltage = true;
      pin3HasVoltage = false;
    }
  }

  if (pin1HasVoltage && pin3HasVoltage) {
    return PolarizationStatus::BOTH;
  } else if (pin1HasVoltage) {
    return PolarizationStatus::PIN1;
  } else if (pin3HasVoltage) {
    return PolarizationStatus::PIN3;
  } else {
    return PolarizationStatus::NONE;
  }
}

void checkESCVoltage(int &escVoltage) {
  digitalWrite(Connect_Center_Pin_to_5V, LOW);
  digitalWrite(mosfetPin1, HIGH);
  digitalWrite(mosfetPin3, HIGH);
  unsigned long startTime = millis();
  RunningMedian median = RunningMedian(200);

  while (millis() - startTime < 20) {
    median.add(analogRead(ESC_voltage_Check));
  }

  escVoltage = median.getMedian();
}

void powerOff() {
  // Make sure the ESC is turned off before the device is
  if (escTestActive)
    escOutputOFF();
  OLED.clearDisplay();
  OLED.display();
  confirmPowerOff = true;
  digitalWrite(auto_Power_off, LOW);
}

void updateDisplay() {
  OLED.clearDisplay();

  if (deviceStatus == DeviceStatus::SERVO)
    updateServoDisplay();
  else if (deviceStatus == DeviceStatus::NO_CONNECTION) {
    if (!manualMode) {
      drawCenteredText("SCANNING", 64, 9, 2, WHITE);
      drawCenteredText("Nothing found", 64, 30, 1, WHITE);
      //drawCenteredText("Manual -> Click Twice", 64, 40, 1, WHITE);
      drawCenteredText("Power off -> Hold", 64, 50, 1, WHITE);
      allDimWhite();
    } else {
      drawCenteredText("Manual Selection", 64, 9, 1, WHITE);
      drawCenteredText("Servo -> Click Once", 64, 40, 1, WHITE);
      drawCenteredText("ESC -> Click Twice", 64, 50, 1, WHITE);
      allDimWhite();
    }

    /* OLED.setCursor(30, 44);
    OLED.print("Read Pin1: ");
    OLED.print(read1);
    OLED.setCursor(30, 54);
    OLED.print("Read Pin3: ");
    OLED.print(read3);*/
  } else if (deviceStatus == DeviceStatus::ESC) {
    updateESCDisplay();
  }

  OLED.display();
}

void updateServoDisplay() {
  if (manualMode) {
    drawCenteredText("+", 64, 3, 2, WHITE);
    drawCenteredText("-", 78, 3, 2, WHITE);
    drawCenteredText("S", 50, 3, 2, WHITE);
  }

  drawCenteredText("SERVO", 64, 3, 3, WHITE);

  if (millis() % ((unsigned long)1000) < 500 && batteryMedian.getMedian() <= 550) {
    OLED.setTextSize(1);
    OLED.setCursor(0, 0);
    OLED.println("LOW");
    blinkDimWhite();
  }

  switch (servoOutputState) {
    case 0:
    case 2:
      OLED.drawRect(2, 31, 40, 31, WHITE);
      drawText("45", 5, 36, 3, WHITE);
      OLED.fillRect(45, 31, 40, 31, WHITE);
      drawText("0", 58, 36, 3, BLACK, WHITE);
      OLED.drawRect(88, 31, 40, 31, WHITE);
      drawText("45", 91, 36, 3, WHITE);
      brightBlueCenter();
      break;
    case 1:
      OLED.drawRect(2, 31, 40, 31, WHITE);
      drawText("45", 5, 36, 3, WHITE);
      OLED.drawRect(45, 31, 40, 31, WHITE);
      drawText("0", 58, 36, 3, WHITE);
      OLED.fillRect(88, 31, 40, 31, WHITE);
      drawText("45", 91, 36, 3, BLACK, WHITE);
      brightBlueRight();
      break;
    case 3:
      OLED.fillRect(2, 31, 40, 31, WHITE);
      drawText("45", 5, 36, 3, BLACK, WHITE);
      OLED.drawRect(45, 31, 40, 31, WHITE);
      drawText("0", 58, 36, 3, WHITE);
      OLED.drawRect(88, 31, 40, 31, WHITE);
      drawText("45", 91, 36, 3, WHITE);
      brightBlueLeft();
      break;
  }
}

void updateESCDisplay() {
  if (manualMode) {
    drawCenteredText("+", 64, 3, 2, WHITE);
    drawCenteredText("-", 78, 3, 2, WHITE);
    drawCenteredText("S", 50, 3, 2, WHITE);
    drawCenteredText("Click to run motor for 2 seconds", 50, 36, 1, WHITE);
  }

  switch (escDirection) {
    case PolarizationStatus::PIN1:
    case PolarizationStatus::PIN3:
      drawCenteredText("ESC", 64, 3, 3, WHITE);
      brightBlueRightAndLeft();
      if (escTestActive) {
        OLED.fillRect(30, 31, 70, 31, WHITE);
        drawText("ON", 48, 36, 3, BLACK, WHITE);
        allBrightBlue();
      } else {
        OLED.drawRect(30, 31, 70, 31, WHITE);
        drawText("OFF", 40, 36, 3, WHITE);
        brightBlueRightAndLeft();
      }
      break;
    case PolarizationStatus::BOTH:
      drawText("ESC - BOTH", 5, 3, 3, WHITE);
      break;
    case PolarizationStatus::NONE:
      drawText("ESC - NONE", 5, 3, 3, WHITE);
      break;
  }
}

void drawCenteredText(const char str[], int16_t x, int16_t y, uint8_t textSize, uint16_t c, uint16_t bg) {
  OLED.setTextSize(textSize);
  TextDimensions dims = TextDimensions(str);
  drawText(str, x - dims.w * 0.5, y, textSize, c, bg);
}

void drawText(const char str[], int16_t x, int16_t y, uint8_t textSize, uint16_t c, uint16_t bg) {
  if (bg == 255)
    bg = c;

  OLED.setTextSize(textSize);
  OLED.setTextColor(c, bg);
  OLED.setCursor(x, y);
  OLED.print(str);
}

// Function: All dim white
void allDimWhite() {
  analogWrite(ledPins[whiteLeft], dimBrightness);
  analogWrite(ledPins[whiteCenter], dimBrightness);
  analogWrite(ledPins[whiteRight], dimBrightness);

  analogWrite(ledPins[blueLeft], 0);
  analogWrite(ledPins[blueCenter], 0);
  analogWrite(ledPins[blueRight], 0);
}

// Function: Blink dim white
void blinkDimWhite() {
  for (int i = 0; i < 3; i++) { // Repeat 3 times
    // Turn on all white LEDs to dim
    analogWrite(ledPins[whiteLeft], dimBrightness);
    analogWrite(ledPins[whiteCenter], dimBrightness);
    analogWrite(ledPins[whiteRight], dimBrightness);

    // Turn off all blue LEDs
    analogWrite(ledPins[blueLeft], 0);
    analogWrite(ledPins[blueCenter], 0);
    analogWrite(ledPins[blueRight], 0);

    delay(100); // Wait for tenth of a second (LEDs on)

    // Turn off all LEDs
    analogWrite(ledPins[whiteLeft], 0);
    analogWrite(ledPins[whiteCenter], 0);
    analogWrite(ledPins[whiteRight], 0);
    analogWrite(ledPins[blueLeft], 0);
    analogWrite(ledPins[blueCenter], 0);
    analogWrite(ledPins[blueRight], 0);

    delay(100); // Wait for half a second (LEDs off)
  }
}

// Function: Bright blue center, rest dim white
void brightBlueCenter() {
    for (int i = 0; i < numLeds; i++) {
    analogWrite(ledPins[i], 0); // Set all LED pins to LOW
  }
  analogWrite(ledPins[whiteLeft], dimBrightness);
  analogWrite(ledPins[whiteCenter], 0);
  analogWrite(ledPins[whiteRight], dimBrightness);

  analogWrite(ledPins[blueLeft], 0);
  analogWrite(ledPins[blueCenter], fullBrightness);
  analogWrite(ledPins[blueRight], 0);
}

// Function: Bright blue left, rest dim white
void brightBlueLeft() {
  for (int i = 0; i < numLeds; i++) {
  analogWrite(ledPins[i], 0); // Set all LED pins to LOW
  }
  analogWrite(ledPins[whiteLeft], 0);
  analogWrite(ledPins[whiteCenter], dimBrightness);
  analogWrite(ledPins[whiteRight], dimBrightness);

  analogWrite(ledPins[blueLeft], fullBrightness);
  analogWrite(ledPins[blueCenter], 0);
  analogWrite(ledPins[blueRight], 0);
}

// Function: Bright blue right, rest dim white
void brightBlueRight() {
  for (int i = 0; i < numLeds; i++) {
  analogWrite(ledPins[i], 0); // Set all LED pins to LOW
  }
  analogWrite(ledPins[whiteLeft], dimBrightness);
  analogWrite(ledPins[whiteCenter], dimBrightness);
  analogWrite(ledPins[whiteRight], 0);

  analogWrite(ledPins[blueLeft], 0);
  analogWrite(ledPins[blueCenter], 0);
  analogWrite(ledPins[blueRight], fullBrightness);
}

// Function: Bright blue right and left, center dim white
void brightBlueRightAndLeft() {
  for (int i = 0; i < numLeds; i++) {
  analogWrite(ledPins[i], 0); // Set all LED pins to LOW
  }
  analogWrite(ledPins[whiteLeft], 0);
  analogWrite(ledPins[whiteCenter], dimBrightness);
  analogWrite(ledPins[whiteRight], 0);

  analogWrite(ledPins[blueLeft], fullBrightness);
  analogWrite(ledPins[blueCenter], 0);
  analogWrite(ledPins[blueRight], fullBrightness);
}

// Function: All bright blue
void allBrightBlue() {
  for (int i = 0; i < numLeds; i++) {
  analogWrite(ledPins[i], 0); // Set all LED pins to LOW
  }
  analogWrite(ledPins[whiteLeft], 0);
  analogWrite(ledPins[whiteCenter], 0);
  analogWrite(ledPins[whiteRight], 0);

  analogWrite(ledPins[blueLeft], fullBrightness);
  analogWrite(ledPins[blueCenter], fullBrightness);
  analogWrite(ledPins[blueRight], fullBrightness);
}

void bootlogo() {
  OLED.clearDisplay();
  // 'HackMakeMod_Logo_v3_Final_Wide_951de7d1-3c1d-41f5-9777-9fc06c8ea31d-141754819', 128x64px
  const uint8_t epd_bitmap_HackMakeMod_Logo_v3_Final_Wide_951de7d1_3c1d_41f5_9777_9fc06c8ea31d_141754819[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0x7f, 0xf0, 0xff, 0xcf, 0x9f, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0xff, 0xf9, 0xff, 0xcf, 0x9f, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0xff, 0xf9, 0xff, 0xcf, 0x9e, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0xff, 0xf9, 0xff, 0xcf, 0xbe, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0xfc, 0xf9, 0xf0, 0x0f, 0xbe, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0xf8, 0xf9, 0xf0, 0x0f, 0xbc, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7e, 0xf8, 0xf9, 0xf0, 0x0f, 0xfc, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0xf8, 0xf9, 0xf0, 0x0f, 0xfc, 0x00,
    0x00, 0x00, 0x00, 0x01, 0x0e, 0x1c, 0x00, 0x00, 0x7f, 0xfe, 0xf8, 0xf9, 0xf0, 0x0f, 0xfc, 0x00,
    0x00, 0x20, 0x00, 0x03, 0x1e, 0x3c, 0x00, 0x00, 0x7f, 0xfe, 0xf8, 0xf9, 0xf0, 0x0f, 0xff, 0x00,
    0x00, 0x30, 0x00, 0x07, 0xbf, 0xfc, 0x00, 0x00, 0x7f, 0xfe, 0xff, 0xf9, 0xf0, 0x0f, 0xff, 0x00,
    0x00, 0x60, 0x00, 0x07, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xfe, 0xff, 0xf9, 0xf0, 0x0f, 0xff, 0x80,
    0x00, 0x60, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00, 0x7c, 0x7e, 0xff, 0xf9, 0xf0, 0x0f, 0x9f, 0x80,
    0x00, 0x61, 0x01, 0x8f, 0xff, 0xff, 0xf0, 0x00, 0x7c, 0x7e, 0xff, 0xf9, 0xf0, 0x0f, 0x9f, 0x80,
    0x00, 0x41, 0x81, 0xff, 0xc0, 0x3f, 0xe0, 0x00, 0x7c, 0x7e, 0xf8, 0xf9, 0xf0, 0x0f, 0x9f, 0x80,
    0x00, 0x40, 0xc3, 0xff, 0x00, 0x0f, 0xe0, 0x00, 0x7c, 0x7e, 0xf8, 0xf9, 0xff, 0xcf, 0x9f, 0x80,
    0x00, 0x06, 0x03, 0xfe, 0x00, 0x07, 0xe0, 0x00, 0x7c, 0x7e, 0xf8, 0xf9, 0xff, 0xcf, 0x9f, 0x80,
    0x00, 0x02, 0x03, 0xfe, 0x00, 0x03, 0xfc, 0x00, 0x7c, 0x7e, 0xf8, 0xf9, 0xff, 0xcf, 0x9f, 0x80,
    0x00, 0x06, 0x03, 0xff, 0x07, 0xe1, 0xfe, 0x00, 0x7c, 0x7e, 0xf8, 0xf8, 0xff, 0xcf, 0x9f, 0x80,
    0x00, 0x06, 0x67, 0xcf, 0xff, 0xe0, 0xff, 0x00, 0x7c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x26, 0x7f, 0x87, 0xff, 0xe0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x3e, 0x7f, 0x03, 0xff, 0xe0, 0x7c, 0x00, 0x78, 0x03, 0xc7, 0xfc, 0x7c, 0xf9, 0xff, 0x80,
    0x00, 0x3c, 0x7e, 0x00, 0x00, 0xf0, 0x38, 0x00, 0x78, 0x03, 0xcf, 0xfe, 0x7c, 0xfb, 0xff, 0x80,
    0x00, 0x3c, 0x3c, 0x00, 0x00, 0xf0, 0x3c, 0x00, 0x7c, 0x07, 0xcf, 0xff, 0x7c, 0xf3, 0xff, 0x80,
    0x00, 0x3e, 0x7c, 0x0c, 0x00, 0xf0, 0x3e, 0x00, 0x7c, 0x0f, 0xcf, 0xff, 0x7d, 0xf3, 0xff, 0x80,
    0x00, 0x3f, 0xf8, 0x08, 0x00, 0x78, 0x3f, 0x80, 0x7e, 0x0f, 0xcf, 0x1f, 0x7d, 0xe3, 0xe0, 0x00,
    0x00, 0x3f, 0xf8, 0x78, 0x00, 0x78, 0x1f, 0x80, 0x7e, 0x1f, 0xcf, 0x1f, 0x7d, 0xe3, 0xe0, 0x00,
    0x00, 0x1f, 0xf8, 0x38, 0x60, 0x78, 0x1f, 0x80, 0x7f, 0x1f, 0xcf, 0x1f, 0x7f, 0xe3, 0xe0, 0x00,
    0x00, 0x4f, 0xf0, 0x30, 0xf0, 0x7c, 0x1e, 0x00, 0x7f, 0xbf, 0xcf, 0x1f, 0x7f, 0xc3, 0xff, 0x80,
    0x00, 0x67, 0xf0, 0x10, 0xf0, 0x3c, 0x1c, 0x00, 0x7f, 0xff, 0xcf, 0x1f, 0x7f, 0xf3, 0xff, 0x80,
    0x00, 0x63, 0xf0, 0x30, 0xe0, 0x3c, 0x1c, 0x00, 0x7f, 0xff, 0xcf, 0xff, 0x7f, 0xfb, 0xff, 0x80,
    0x00, 0x71, 0xe0, 0xc0, 0xe0, 0x7e, 0x3c, 0x00, 0x7f, 0xff, 0xcf, 0xff, 0x7f, 0xfb, 0xff, 0x80,
    0x00, 0x73, 0xe0, 0x70, 0xe0, 0xf3, 0x3e, 0x00, 0x7b, 0xff, 0xcf, 0xff, 0x7c, 0xfb, 0xe0, 0x00,
    0x00, 0x3f, 0xe0, 0x30, 0xe0, 0xe3, 0xbf, 0x00, 0x79, 0xf7, 0xcf, 0xff, 0x7c, 0xfb, 0xe0, 0x00,
    0x00, 0x3f, 0xe0, 0x70, 0xe0, 0xe3, 0xbf, 0x00, 0x79, 0xe7, 0xcf, 0x1f, 0x7c, 0xfb, 0xe0, 0x00,
    0x00, 0x3f, 0xe0, 0xc0, 0x60, 0xe3, 0xbe, 0x00, 0x78, 0xe7, 0xcf, 0x1f, 0x7c, 0xfb, 0xff, 0x80,
    0x00, 0x1f, 0xe0, 0x7c, 0x60, 0xf1, 0xb8, 0x00, 0x78, 0x47, 0xcf, 0x1f, 0x7c, 0xfb, 0xff, 0x80,
    0x00, 0x0f, 0xe0, 0x0c, 0x30, 0x7f, 0x30, 0x00, 0x78, 0x07, 0xcf, 0x1f, 0x7c, 0xfb, 0xff, 0x80,
    0x00, 0x07, 0xe0, 0x18, 0x38, 0x3e, 0x30, 0x00, 0x78, 0x03, 0xcf, 0x1e, 0x7c, 0x79, 0xff, 0x80,
    0x00, 0x07, 0xf0, 0x3a, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x07, 0xfc, 0x3e, 0x07, 0x80, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x07, 0xfc, 0x04, 0x03, 0xff, 0xf0, 0x00, 0x78, 0x00, 0x3e, 0x1f, 0xf8, 0x3f, 0xfc, 0x00,
    0x00, 0x07, 0xfc, 0x07, 0x00, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x3e, 0x7f, 0xfe, 0x7f, 0xff, 0x00,
    0x00, 0x07, 0xfc, 0x01, 0x38, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x7e, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x07, 0xfc, 0x03, 0x78, 0x03, 0x30, 0x00, 0x7e, 0x00, 0x7e, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x07, 0xfe, 0x03, 0xc8, 0x87, 0x70, 0x00, 0x7f, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x07, 0xfe, 0x09, 0x1f, 0xcf, 0x60, 0x00, 0x7f, 0x01, 0xfe, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x07, 0xbf, 0x18, 0x0f, 0xdb, 0x60, 0x00, 0x7f, 0x81, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x06, 0x1f, 0x1c, 0x00, 0xf3, 0x40, 0x00, 0x7f, 0x83, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x1f, 0xbe, 0x00, 0xe3, 0xc0, 0x00, 0x7f, 0xc3, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x0f, 0xfe, 0x00, 0xc3, 0x80, 0x00, 0x7f, 0xe7, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x0f, 0xff, 0x00, 0x03, 0x00, 0x00, 0x7f, 0xe7, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x07, 0xff, 0xc0, 0x02, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x03, 0xf7, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x01, 0xf1, 0xfc, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x00, 0xe0, 0x3f, 0xf8, 0x00, 0x00, 0x7e, 0xff, 0x7e, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0x7e, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0xfc, 0x3f, 0x7e, 0x1f, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0xfc, 0x3f, 0x7e, 0x3f, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x3c, 0x7e, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x7e, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x7e, 0xff, 0xff, 0x7f, 0xff, 0x80,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x7f, 0xfe, 0x7f, 0xff, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x3f, 0xfc, 0x7f, 0xfe, 0x00
  };

  // Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 1040)
  const int epd_bitmap_allArray_LEN = 1;
  const uint8_t *epd_bitmap_allArray[1] = {
    epd_bitmap_HackMakeMod_Logo_v3_Final_Wide_951de7d1_3c1d_41f5_9777_9fc06c8ea31d_141754819
  };

  // Draw the bitmap on the display
  OLED.drawBitmap(0, 0, epd_bitmap_HackMakeMod_Logo_v3_Final_Wide_951de7d1_3c1d_41f5_9777_9fc06c8ea31d_141754819, 128, 64, SSD1306_WHITE);
  OLED.display();
}
 
Servo Buddy Beta
BOM (Bill of Materials)

IDNameDesignatorFootprintQuantityManufacturer PartManufacturerSupplierSupplier PartPrice
1Servo connectorH1HDR-TH_03P-P2.54-H-M-W10.4-N1PZ254-1-03-W-8.5HCTL(华灿天禄)LCSCC2894945$0.03
2N096-2864KSWEG01-H30OLED1OLED_0.96-30P-P0.70-12864-NO-HOLES1N096-2864KSWEG01-H30Newvision(新智景)LCSCC2890596$2.14
3TS-1088-AR02016PROGRAM,SW1SW-SMD_L3.9-W3.0-P4.452TS-1088-AR02016XUNPU(讯普)LCSCC720477$0.04
4BH-AAA-B5AA005U2BAT-SMD_BH-AAA-B5AA0051BH-AAA-B5AA005MYOUNG(美阳)LCSCC2979170$1.38
5TLP2301(GB-TPL,E(TU3OPTO-SMD-4_L3.7-W4.6-P2.54-LS7.0-BR-11TLP2301(GB-TPL,E(TTOSHIBA(东芝)LCSCC2924740$0.32
6XL-1608UBC-04LED4,LED5,LED6LED0603-RD_BLUE3XL-1608UBC-04XINGLIGHT(成兴光)LCSCC965807$0.00
751ΩR16,R31,R32,R33,R34,R35R060360603WAF510JT5EUNI-ROYAL(厚声)LCSCC23197$0.00
80603White light_C2290LED1,LED2,LED3LED0603-R-RD_WHITE3KT-0603WKENTOLCSCC2290$0.01
9100kΩR5,R3,R10,R21,R22R060350603WAF1003T5EUNI-ROYAL(厚声)LCSCC25803$0.00
101N4148WS T4D5,D1SOD-323_L1.8-W1.3-LS2.5-RD21N4148WSCJ(江苏长电/长晶)LCSCC2128$0.01
11100nFC2,C8,C10,C11,C14,C15,C16,C17,C18,C19C060310CC0603KRX7R9BB104YAGEO(国巨)LCSCC14663$0.00
122.2uFC4,C9,C12,C13,C20C06035CL10A225KO8NNNCSAMSUNG(三星)LCSCC23630$0.01
131uFC5,C22,C23,C24,C25C06035CL10A105KB8NNNCSAMSUNG(三星)LCSCC15849$0.00
144.7uFC21C06031CL10A475KO8NNNCSAMSUNG(三星)LCSCC19666$0.01
15SS54_C22452D2,D3,D4SMA_L4.4-W2.8-LS5.4-R-RD3SS54MDD(辰达半导体)LCSCC22452$0.04
16AO3400AQ1,Q5,Q6SOT-23-3_L2.9-W1.3-P1.90-LS2.4-BR3AO3400AAOSLCSCC20917$0.07
17MMBT3904-C20526Q2,Q3SOT-23-3_L2.9-W1.3-P1.90-LS2.4-BR2MMBT3904CJ(江苏长电/长晶)LCSCC20526$0.01
18AO3401AQ4SOT-23_L2.9-W1.3-P1.90-LS2.4-BR1AO3401AAOSLCSCC15127$0.05
191kΩR1,R2,R4,R17,R18,R19,R20,R23,R24,R9,R12R0603110603WAF1001T5EUNI-ROYAL(厚声)LCSCC21190$0.00
2010kΩR25,R26,R13,R14R060340603WAF1002T5EUNI-ROYAL(厚声)LCSCC25804$0.00
21560kΩR27R060310603WAF5603T5EUNI-ROYAL(厚声)LCSCC23203$0.00
224.7kΩR28,R29,R30R060330603WAF4701T5EUNI-ROYAL(厚声)LCSCC23162$0.00
23LargeSwitchSW3KEY-SMD_4P-L12.0-W12.0-P5.00-LS15.21TS-1003S-07026XUNPU(讯普)LCSCC455223$0.07
2410uFC3,C1C06032CL10A106MA8NRNCSAMSUNG(三星)LCSCC96446$0.02
2520pFC6,C7C06032CL10C200JB8NNNCSAMSUNG(三星)LCSCC1648$0.00
265.1kΩR6,R11R060320603WAF5101T5EUNI-ROYAL(厚声)LCSCC23186$0.00
27270ΩR7,R8R060320603WAF2700T5EUNI-ROYAL(厚声)LCSCC22966$0.00
28200ΩR15R060310603WAF2000T5EUNI-ROYAL(厚声)LCSCC8218$0.00
29RP2040U1LQFN-56_L7.0-W7.0-P0.4-EP1RP2040Raspberry Pi(树莓派)LCSCC2040$1.04
30HT7533-1_C14289U5SOT-89-3_L4.5-W2.5-P1.50-LS4.2-BR1HT7533-1HOLTEK(合泰/盛群)LCSCC14289$0.11
31W25Q16JVUXIQU6USON-8_L3.0-W2.0-P0.50-BL-EP1W25Q16JVUXIQWINBOND(华邦)LCSCC2843335$0.34
32TYPE-C 16PIN 2MD(073)USB1USB-C-SMD_TYPE-C-6PIN-2MD-0731TYPE-C 16PIN 2MD(073)SHOU HAN(首韩)LCSCC2765186$0.05
3312MHzX1CRYSTAL-SMD_4P-L3.2-W2.5-BL1X322512MSB4SIYXC(扬兴晶振)LCSCC9002$0.06
 
Hey Chad! How do I go about re flashing the servo buddy? I created a PIO project with the rpi pico since it uses the same chip but couldnt get my computer to recognize it. It keeps saying this device malfunctioned.
 
I'm seeing the same thing. Sometimes it will say the USB device has malfunctioned and other times it will show up as COM port 5. Holding boot while connecting USB doesn't make a difference. Thanks for any help
 
I'm seeing the same thing. Sometimes it will say the USB device has malfunctioned and other times it will show up as COM port 5. Holding boot while connecting USB doesn't make a difference. Thanks for any help
Glad i’m not the only one. Looking forwards to a response from chad.
 
Back
Top