Project Overview: 2S LiPo High-Current Power Box with two Sbus inputs

This circuit is a high-performance power distribution system designed for large-scale RC models. It manages power from dual batteries to drive up to 16 High-Voltage (HV) servos while providing redundancy from two Sbus receivers and electronic switching.

Key Technical Features

Schematics

PCB

The PCB can be bought at PCBWay 

The PCB was kindly sponsored by PCBWay and is as usual of excellent quality.

You can order it here :PCBWay shared project. It's cheap, delivered very fast and so professional looking!

and if you are new to PCBWay please use this affiliated link : https://pcbway.com/g/o35z4O

As you can see "big" tracks are exposed to be reinforced either with solder or (better) with copper wire. Doing this will increase drastically the current capcabilities of this board. It will sustain up to 20A with a total of 16 servos powered with 2s lipo packs.

Power considerations

2x 2s lipo packs are used to power the box. The "OR" of their voltage is performed with two ideal diodes

These tiny devices are available for cheap on Aliexpress. They are mostly used as "anti back flow" components on solar panels. The XL 74610 is an ideal diode module built around the brilliant LM 74610 chip from Texas Instruments.

When paired with an N-channel power MOSFET, it behaves like a diode, but with a significantly lower voltage drop.

Switch control

A three stages mosfet combination allows to switch On/Off the power box

The three big P channels mosfets IRF4905 are used to cut the VCC line. 

Each Mosfet can handle 74A

Heat Dissipation per MOSFET

Thermal Analysis

This means that the mosfets should remain totally cold and no heatsink willbe needed. 

Reliability : 3 mosfets are soldered in parallel allowing a very strong reliability in case of one or even two Mosfets failure.

Switching Logic and Fail-safe Mechanism

The switch circuit uses a dual-stage N-FET inverter logic to control the main high-power P-MOSFETs (Q7, Q8, Q9).

Operating States

Fail-safe Logic

This design features a "Fail-On" safety mechanism, which is standard for high-end RC power systems:


Software

ESP32 firmware can be found on my Github pages

firmware is pretty simple and rely mostly on borderflight SbuS library

All the RF of the chip is disabled (no Wifi no Bluetooth) and a watchdog is enabled to reboot in case of emergency...

The toucpad "FAIL" allows to memorize all channels configuration. These values will be used to output "failsafe" values to servos if SBUS AND SBUS2 fail simultaneously.

//SBUS
#include "sbus.h"  //https://github.com/bolderflight/sbus/tree/main

// two SBUS object, which are on hardware serial ports 1 and 2
bfs::SbusData data1;
bfs::SbusRx sbus_rx1(&Serial1, 34, 0, true);  //ESP32  (true = inverted 3.3V from FSIA6B SBUS pin)

bfs::SbusData data2;
bfs::SbusRx sbus_rx2(&Serial2, 35, 0, true);  //ESP32  (true = inverted 3.3V from FSIA6B SBUS pin)


//PWM for servos
#include                                                                                                       //https://github.com/madhephaestus/ESP32Servo
uint16_t pwmPin[16] = { 14, 27, 26, 25, 33, 32, 19, 13, 16, 17, 18, 5, 23, 4, 2, 15 };                                       //contains pins for PWM output for each servo
uint16_t failsafe[16] = { 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500 };  //failsafe servos values for each 16 channels in µs
Servo servos[16];
uint16_t channels[16];

#define LED_PIN 22

//watchdog
#include <esp_task_wdt.h>
#define WDT_TIMEOUT 1  //1 seconds WDT

//touchpad
touch_pad_t touchPin;
int threshold = 35;  //Threshold value for touchpads pins
long lastTouch;

#define FAIL_PIN 12

//wifi/bluetooth (just to switch them Off !)
#include <WiFi.h>
#include <bt.h>)

//Preferences
#include <Preferences.h>
Preferences preferences;

//#define DEBUG //uncomment to get debug messages


void setup() {
  //stop Wifi and Bluetooth
  WiFi.mode(WIFI_OFF);
  btStop();
  
  Serial.begin(115200);
  delay(1000);
  Serial.println("program started");

  //Preferences
  preferences.begin("sbusBox", false);
  size_t size = preferences.getBytesLength("failsafe");  //check if failsafe is already saved
  if (size == sizeof(failsafe)) {
    preferences.getBytes("failsafe", failsafe, sizeof(failsafe));       //get values
  } else preferences.putBytes("failsafe", failsafe, sizeof(failsafe));  // if not already done, save failsafe array
  //preferences.clear();              // Remove all preferences under the opened namespace
  //preferences.remove("counter");   // remove the counter key only


  Serial.println("read preferences :");
  Serial.print("\tstart at ");
  //Serial.println(startThr);


  //preferences.end();  // Close the Preferences


  //SBUS
  sbus_rx1.Begin();
  sbus_rx2.Begin();

  //servos PWM
  for (int i = 0; i < 16; i++) {
    servos[i].attach(pwmPin[i]);  // each servo is attached to a "Ledc channel"
  }

  //start watchdog (will be reseted into the loop)
  esp_task_wdt_init(WDT_TIMEOUT, true);  //enable panic so ESP32 restarts
  esp_task_wdt_add(NULL);                //add current thread to WDT watch

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
}

void loop() {
  esp_task_wdt_reset();  //reset the watchdog

  if (sbus_rx1.Read()) {  // if something on SBUS1
    data1 = sbus_rx1.data();
#ifdef DEBUG
    for (int8_t i = 0; i < data1.NUM_CH; i++) {
      Serial.print(data1.ch[i]);
      Serial.print("\t");
    }
    Serial.print(data1.lost_frame);
    Serial.print("\t");
    Serial.println(data1.failsafe);
#endif
  }

  if (sbus_rx2.Read()) {  //if something on  SBUS2
    data2 = sbus_rx2.data();
#ifdef DEBUG
    for (int8_t i = 0; i < data2.NUM_CH; i++) {
      Serial.print(data2.ch[i]);
      Serial.print("\t");
    }
    /* Display lost frames and failsafe data */
    Serial.print(data2.lost_frame);
    Serial.print("\t");
    Serial.println(data2.failsafe);
#endif
  }

  //map SBUS to servos PWM
  if (!data1.lost_frame && !data1.failsafe) {  // if SBUS1 is "clean" use it !
    for (int i = 0; i < 16; i++) {
      int pulseWidth = map(data1.ch[i], 172, 1811, 1000, 2000);
      servos[i].write(pulseWidth);  //write directly pulsewodth to the library
      channels[i] = pulseWidth;
    }
    digitalWrite(LED_PIN, LOW);                       // RX1 OK : LED on
  } else if (!data2.lost_frame && !data2.failsafe) {  // else if SBUS2 is clean use it
    for (int i = 0; i < 16; i++) {
      int pulseWidth = map(data2.ch[i], 172, 1811, 1000, 2000);
      servos[i].write(pulseWidth);  //write directly pulsewodth to the library
      channels[i] = pulseWidth;
    }
    digitalWrite(LED_PIN, (millis() / 100) % 2 == 0 ? LOW : HIGH);  // RX2 ok : blink LED
  } else                                                            //apply failsafe
  {
    for (int i = 0; i < 16; i++) {
      servos[i].write(failsafe[i]);
    }
    digitalWrite(LED_PIN, HIGH);  // LED off in failsafe
  }

  //check if should save failsafe
  if ((millis() - lastTouch) > 1000) {  //avoid bouncing on touchpad
    if (ftouchRead(FAIL_PIN) < threshold) {
      Serial.println("touchpad - detected : save faisafe");
      lastTouch = millis();
      for (int i = 0; i < 16; i++) {
        failsafe[i] = channels[i];  //save current position into failsafe array
      }
      preferences.putBytes("failsafe", failsafe, sizeof(failsafe));  // if not already done, save failsafe array into preferences
    }
  }
}

int ftouchRead(int gpio)  // this will filter false readings of touchRead() function...
{
  int val = 0;
  int readVal;
  for (int i = 0; i < 10; i++) {  //perform 10 readings and keep the max
    readVal = touchRead(gpio);
    val = max(val, readVal);
  }
  return val;
}

Box for the power box

An enclosure for the PCB can be 3D printed. It will accomodate the PCB soldered with 90° pin headers, so that its form factor should remain "flat" when inserted into the plane.

If you prefer vertical pin headers, you will have to redesign the box (which is not complex !)

Parts are available on thingiverse : https://www.thingiverse.com/thing:7291279