InlineCurrentSense constructor gotcha

Hi all! First post, love the community and project.

I’m working on getting inline current sense working on a esp32 (specifically esp32-s3-devkitc-1, this one here).

I’m seeing some odd behavior with the InlineCurrentSense constructor.

The following code fails (using just defines for the pins):

#define CURRENT_SENSE_A 6  // GPIO6 - ADC1_5
#define CURRENT_SENSE_B 7  // GPIO7 - ADC1_6

InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50, CURRENT_SENSE_A, CURRENT_SENSE_B);

results in:

Starting...
ESP32-DRV: Configuring 3PWM in group: 0 on timer: 0
ESP32-DRV: Configuring 2 operators.
ESP32-DRV: Configuring 3 comparators.
ESP32-DRV: Configuring 3 generators.
ESP32-DRV: Configuring center-aligned pwm.
ESP32-DRV: Enabling timer: 0
ESP32-DRV: MCPWM configured!
MOT: Monitor enabled!
[  1012][E][esp32-hal-gpio.c:113] __pinMode(): Invalid IO 50 selected
ESP32-CS: ERROR: Not ADC pin: 50
ESP32-CS: ERROR: Failed to initialise ADC pin: 50, maybe not an ADC pin?
Current sense init failed!

However, specifying pins directly in the constructor works:

InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50.0f, 6, 7);

results in:

Starting...
ESP32-DRV: Configuring 3PWM in group: 0 on timer: 0
ESP32-DRV: Configuring 2 operators.
ESP32-DRV: Configuring 3 comparators.
ESP32-DRV: Configuring 3 generators.
ESP32-DRV: Configuring center-aligned pwm.
ESP32-DRV: Enabling timer: 0
ESP32-DRV: MCPWM configured!
MOT: Monitor enabled!
[  1012][E][esp32-hal-adc.c:172] __analogChannelConfig(): Pin is not configured as analog channel
[  1021][E][esp32-hal-adc.c:172] __analogChannelConfig(): Pin is not configured as analog channel
Current sense init success!

casting to int also works:

InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50.0f, (int)CURRENT_SENSE_A, (int)CURRENT_SENSE_B);  

I feel like there’s a cpp thing going on that I don’t understand or I missed something in the docs.

Full code is here:

#include <Arduino.h>
#include <SimpleFOC.h>
#include <Wire.h>

#define I2C_SDA 4 // SDA on encoder (encoder yellow)
#define I2C_SCL 5 // SCL on encoder (encoder orange)
#define CURRENT_SENSE_A 6  // GPIO6 - ADC1_5
#define CURRENT_SENSE_B 7  // GPIO7 - ADC1_6
#define PWM_A 15 // 9 on simplefoc shield v2 (red)
#define PWM_B 16  // 5 on simplefoc shield v2 (orange)
#define PWM_C 17 // 6 on simplefoc shield v2 (yellow)
#define ENABLE 18 // 8 on simplefoc shield v2 (green)


BLDCMotor motor = BLDCMotor(7);
BLDCDriver3PWM driver = BLDCDriver3PWM(PWM_A, PWM_B, PWM_C, ENABLE);
MagneticSensorI2C sensor = MagneticSensorI2C(0x36, 12, 0x0E, 4);

// InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50.0f, CURRENT_SENSE_A, CURRENT_SENSE_B); // fails 
// InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50.0f, 6, 7);  // Direct pin numbers - works
InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50.0f, (int)CURRENT_SENSE_A, (int)CURRENT_SENSE_B);  

float target_velocity = 0.0f;

Commander command = Commander(Serial);

void doMotor(char* cmd) { command.motor(&motor, cmd); }
void doTarget(char* cmd) { 
  command.scalar(&target_velocity, cmd); 
  // Serial.print("Target velocity set to: ");
  // Serial.println(target_velocity);
}

void setup() {
  
  Serial.begin(115200);
  _delay(1000);
  
  Serial.println("Starting...");

  SimpleFOCDebug::enable(&Serial);

  Wire.begin(I2C_SDA, I2C_SCL);
  Wire.setClock(400000);

  sensor.init(&Wire);

  motor.linkSensor(&sensor);
  
  driver.voltage_power_supply = 12;
  if(!driver.init()){
    Serial.println("Driver init failed!");
    return;
  }

  current_sense.linkDriver(&driver);

  motor.linkDriver(&driver);
  motor.controller = MotionControlType::velocity;
  motor.PID_velocity.P = 0.04f;
  motor.PID_velocity.I = 0.7f;
  motor.PID_velocity.D = 0.0f;
  motor.LPF_velocity.Tf = 0.01f;
  motor.voltage_limit = 12;   // [V]
  motor.velocity_limit = 1000; // [rad/s]
  motor.voltage_sensor_align = 1; // [V]
  motor.velocity_index_search = 3;
  motor.useMonitoring(Serial);
  

  // init current sense
  if (current_sense.init())  Serial.println("Current sense init success!");
  else{
    Serial.println("Current sense init failed!");
    return;
  }

  // driver.pwm_frequency = 20000;
  Serial.println(driver.pwm_frequency);

  Serial.print("Current sense pins: ");
  Serial.print(CURRENT_SENSE_A);
  Serial.print(", ");
  Serial.println(CURRENT_SENSE_B);

  if(!motor.init()){
    Serial.println("Motor init failed!");
    return;
  }
  motor.initFOC();

  command.add('T', doTarget, "target velocity");
  command.add('M', doMotor,"my motor");
  
  _delay(1000);
}

void loop() {
  motor.loopFOC();
  //motor.monitor();
  motor.move(target_velocity);  // use the target velocity here

  command.run();
  
}

// columtarget, voltage.q, velocity, angle

PlatformIO config:

[env:esp32-s3_i2c_simplefoc]
build_src_filter = +<esp32-s3_i2c_simplefoc.cpp> 
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_build.mcu = esp32s3
board_build.f_cpu = 240000000L
board_build.partitions = default_8MB.csv
board_build.arduino.memory_type = qio_qspi
build_flags = 
    -DARDUINO_USB_MODE=1
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DBOARD_HAS_PSRAM
upload_protocol = esptool
monitor_speed = 115200
lib_deps = 
	askuric/Simple FOC@^2.3.4
	Wire

How strange. That should compile to the exact same thing either way. Even if those #defines already existed with different values, it should give an error for trying to re-define them. And there are two constructors, but the second argument being float type should ensure the correct one is called.

If you want to get to the bottom of it, put print commands in both constructors to see which one is being called, and print the pin arguments from inside the constructor to see what values it’s getting.

EDIT: Another thing you could try is making the first argument explicitly float type rather than double (0.01f instead of just 0.01), since that could conceivably throw off the constructor selection.

You called it. Good catch btw. I’m using Cursor, an AI-powered fork of VSCode. PlatformIO works perfectly, it’s a dream. I don’t analyze the commits as closely as I should. It changed the gain value to a float between my tests in my original post.

Specifying both as floats worked:

InlineCurrentSense current_sense = InlineCurrentSense(0.01f, 50.0f, CURRENT_SENSE_A, CURRENT_SENSE_B);

Here’s the constructor code (shunt_resistor and gain are floats as mentioned):

class InlineCurrentSense: public CurrentSense{
  public:
    /**
      InlineCurrentSense class constructor
      @param shunt_resistor shunt resistor value
      @param gain current-sense op-amp gain
      @param phA A phase adc pin
      @param phB B phase adc pin
      @param phC C phase adc pin (optional)
    */
    InlineCurrentSense(float shunt_resistor, float gain, int pinA, int pinB, int pinC = NOT_SET);
    /**
      InlineCurrentSense class constructor
      @param mVpA mV per Amp ratio
      @param phA A phase adc pin
      @param phB B phase adc pin
      @param phC C phase adc pin (optional)
    */
    InlineCurrentSense(float mVpA, int pinA, int pinB, int pinC = NOT_SET);

    // CurrentSense interface implementing functions 
    int init() override;
    PhaseCurrent_s getPhaseCurrents() override;

  private:
  
    // gain variables
    float shunt_resistor; //!< Shunt resistor value
    float amp_gain; //!< amp gain value
    float volts_to_amps_ratio; //!< Volts to amps ratio
    
    /**
     *  Function finding zero offsets of the ADC
     */
    void calibrateOffsets();

};