AS5600 Angle sensor support?

Are you using SimpleFOC 1.6 or my recent pull request? The latter assumes you’ll do it outside SimpleFOC e.g by calling Wire.setClock(1000000);. The former has it as a property of MagneticSensorI2CConfig_s.

Setting the clock worked on my esp32 but caused a infinite loop on my disco_stm32f407 board (problem with stm32_core not SimpleFOC) so will be interested how you get on.

I tried both the 1.6 and your code. I hooked up a tacho today to test the speeds and the most I am getting out of the as5600 with i2c at even 5khz update rate is 380rpm. @Owen_Williams is this a typical speed you would expect? Everything is running on an esp32 and fix is in angle mode. I made a little video showing our progress also.
https://streamable.com/z3xdt2

5000 * 60 = 30,000 rpm. So I guess some of our settings needs a bit of a tune up.

@Adam_Donovan - I don’t think your slow motor speed is anything to do with the AS5600 performance. If you were having sensor problems in closed loop then it would stall or spin erratically. Your motion was smooth, it was just slower than you wanted! To go faster you need more volts not a better sensor!

The motor you are using looks a bit like my 2804 gimbal motor. I can get that spinning up to about 180 rad/s (~1800rpm) whilst openloop (I’d expect closed loop to be similar) but this is when I’ve got a high motor.voltage_limit = 7

At motor.voltage_limit = 2, I get a similar max speed to you. What do you have you voltage_limit and voltage_power_supply set?

I have two types of AS5600 a white square like yours and a green circle one. The green circle one I can get analog from ‘out’ pin but with white I cannot get it to work. Looking at the spec it is possible to configure output stage as analog or pwm - perhaps they are configured differently?

Thanks for that Owen. Re tested again today against our open loop control which reaches about the same speed limit for our little motors as yours which is 1800RPM. Playing with velocity limits and voltage limits in angle mode we had it up to around 900 rpm with settings of 8.4volts and velocity limit of 200. Interesting is the relationship between current in open vs closed loop. In open loop the current consumption goes down with speed and with closed loop it goes up(inverse). At 1800RPM in openloop the current draw of the motor is around 10mA and in closed loop its closer to 1 and as high as 1.4amps(for 3 motors). Lower speeds and a voltage limit of 4.1 produce some good low power control modes with current of all 3 motors being around 40mA. Ive attached a terrible picture of our whiteboard to show some of the values. One other challenge we are facing is now how to make the FOC run quieter than it is right now. We changed with the pre scalar for esp32 and now get PWM of around 40Khz but we still have audible noise(oscilloscope says 39.1Khz). I am guessing its coming though some timing with the code execution time. I am thinking to break out the high frequency microphone to see what measurements I get on the audible and if it relates to any timing we can see. If oyu have any insights into that might be cool.

Oh god, thanks, I ordered the same one and I’ve already wasted a few hours and thanks to you I know that it’s the stupid :magnet: :rage:.

Carelsbergh Stijn

Hey Adam, can you please share with us the changes you did to the code for the ESP32? Thanks.

@Adam_Donovan - david is our expert on all things esp32 so I’d take up his offer!

Back to your current observations. Openloop will start with high currents at low speed and drop as you get faster. At higher speeds the back emf of the motor will almost cancel out the source voltage hence the reducing current. In closed loop you sort of see the opposite - instead of using a fixed voltage (like we do in openloop) the closed loop system uses the least amount of voltage that will give you your desired speed essentially it is increasing the voltage to overcome the increasing back emf. If the sensor says you are going a bit slow, the PID will increase the voltage (and vice versus).

I’d like to see the two current graphs (openloop and closed loop) overlaid on each other. Ideally the closed loop would be much more efficient at low speeds and similar efficiency at higher speeds (i.e. they converge at higher speeds). It sounds like this is not the case with your setup and closed loop can’t get anywhere near what openloop can achieve at higher speeds. Odd.

Stepping back a minute - are these for your 3 wheeled robots? Doing a quick bit off maths - lets say your wheel diameter is 15cm and your target speed is 50cm/s then you’ll be wanting 3 or 4 rotations a second. or ~200 rpm. Perhaps you are over optimising!

Hi @David_Gonzalez
I am Katrin and collaborating with Adam_Donovan.
So I will share our code with you.

1.)
As we are using a multiplexer to address 3 motors at once, I modified the"MagneticSensorI2C.cpp" and “MagneticSensorI2C.h” . We are using the current FOC library (master v1.6.0 Releases 10) . Here I will just write what I added / the functions I modified, the complete code is here: https://drive.google.com/drive/folders/18HJprLy6dZAAi1Yfn-eJ00Omie4MwIr6?usp=sharing

----
My changes / additions to “MagneticSensorI2C.cpp”
----

#define MULTIPLEXER

void TCA9548A(uint8_t bus)
{
  Wire.beginTransmission(0x70);  // TCA9548A address is 0x70 
  Wire.write(1 << bus);          // send byte to select bus
  Wire.endTransmission();
}

MagneticSensorI2C::MagneticSensorI2C()
{};


void MagneticSensorI2C::init(int _id) {

  id = _id;

... code like the normal  init function ... 

}


int MagneticSensorI2C::read(uint8_t angle_reg_msb) {
  // read the angle register first MSB then LSB
	byte readArray[2];
	uint16_t readValue = 0;
	
	#ifdef MULTIPLEXER
	 TCA9548A (id);
	#endif
	
  // notify the device that is aboout to be read
	Wire.beginTransmission(chip_address);
	Wire.write(angle_reg_msb);
  Wire.endTransmission(false);
  
  // read the data msb and lsb
	Wire.requestFrom(chip_address, (uint8_t)2);
	for (byte i=0; i < 2; i++) {
		readArray[i] = Wire.read();
	}

  // depending on the sensor architecture there are different combinations of 
  // LSB and MSB register used bits
  // AS5600 uses 0..7 LSB and 8..11 MSB
  // AS5048 uses 0..5 LSB and 6..13 MSB
	readValue = ( readArray[1] &  lsb_mask );
	readValue += ( ( readArray[0] & msb_mask ) << lsb_used );
	return readValue;
}

----
My changes / additions to “MagneticSensorI2C.h”
----

class MagneticSensorI2C: public Sensor{
 public:
    MagneticSensorI2C();
    void init(int _id) ;

  private:

    int id;
}

2.)
Trying to change the audible frequency, it did not seem that reasonable values like in the original code of “hardware_utils.cpp”, made any difference, neither on what we could hear, nor on what the oscilloscope was outputing. So I tried a value of 4000000 Hz instead of 40000 Hz

----
My changes / additions to “hardware_utils.cpp”
----
in
void _setPwmFrequency(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {

...
#elif defined(ESP_H) // if esp32 boards
  if(pwm_frequency == NOT_SET) pwm_frequency = 4000000; // KKK, org: 40000 default frequency 20khz - centered pwm has twice lower frequency
  else pwm_frequency = constrain(2*pwm_frequency, 0, 8000000); //KKK, org:  60000 constrain to 30kHz max - centered pwm has twice lower frequency
...

That alone did not have an influence, so I experimented with the prescales
in
void _configureTimerFrequency(long pwm_frequency, mcpwm_dev_t* mcpwm_num, mcpwm_unit_t mcpwm_unit){

...
  mcpwm_num->clk_cfg.prescale = 0; //KKK , org 0

  mcpwm_num->timer[0].period.prescale = 0; //KKK , org 4
  mcpwm_num->timer[1].period.prescale = 0; //KKK , org 4
  mcpwm_num->timer[2].period.prescale = 0; //KKK , org 4   
...

and now we have as Adam discribed 39.1Khz on the oscilloscope, but can still hear noise…

3.)
The Arduino Test code for our 3 motors is now structured with a Wheel class, which has an instance of MagneticSensorI2C and an instance of BLDCMotor that are maching pairs in terms of sensor id and motor pins.

----
Main program: 201020_FOC1_6_esp32_position_control_3Motors_Multiplexer.ino
----

/**
   ESP32 position motion control example with magnetic sensor
*/

#include "globals.h"

#define ESP_H
#define MULTIPLEXER

#include <SimpleFOC.h>



#include "config.h"

#include "Wheel.h"

Wheel wheelA;
Wheel wheelB;
Wheel wheelC;



int lastTime = 0;
int count = 0;
float rpmCount = 0;

void setup() {

  // use monitoring with serial
  Serial.begin(115200);

  // ENABLE PIN FOR MOTORDRIVER POWER AND I2C PIN 0 and 2 (used for Bootup) //
  pinMode(MOTOR_ENABLE_PIN, OUTPUT);
  digitalWrite(MOTOR_ENABLE_PIN, HIGH);

  //Wheel(int phAPin, int phBPin, int phCPin ,int polepairNb, int id)
  wheelA = Wheel (MA0_PIN, MA1_PIN, MA2_PIN, 7, 2);//1
  wheelB = Wheel (MB0_PIN, MB1_PIN, MB2_PIN, 7, 1);//2
  wheelC = Wheel (MC0_PIN, MC1_PIN, MC2_PIN, 7, 0);//0

  wheelA.init();
  wheelB.init();
  wheelC.init();


  //-----------------------------------------


  lastTime = micros();
}




void loop() {

  count++;
  rpmCount += wheelA.getVelocity();
  int currentTime = micros();
  if (currentTime - lastTime >= 1000000) //=1 s = 1000 ms = 1000000 us
  {
    // Serial.println ("vel: " + (String) degrees (sensor.getVelocity()/360.) *60 + " rpm "); // degrees/s / 360°
    Serial.println ("vel: " + (String) ( degrees (rpmCount) / float(count) / 360. *60) + " rpm "); // degrees/s / 360°
    Serial.println((String)count + " loop udates/s");
    count = 0;
    rpmCount = 0;
    lastTime = currentTime;
  }

  wheelA.loopFOC();
  wheelB.loopFOC();
  wheelC.loopFOC();

  wheelA.moveTo(target_angle);
  wheelB.moveTo(target_angle);
  wheelC.moveTo(target_angle);


}

----
Wheel.h
----

#ifndef _WHEEL_
#define _WHEEL_

#include "Arduino.h"
#include"SimpleFOC.h"


extern float target_angle;


// Wheel class, combination of BLDC Motor, and I2C Sensor //

class Wheel
{
  public:

    Wheel();
    Wheel(int phAPin, int phBPin, int phCPin , int polepairNb, int _id);

    void init();
    void loopFOC();
    void move();
    void moveTo(float target_angle);

    float getVelocity();
    float getAngle();


  private:

    int id;

    // SENSOR INSTANCE //
    MagneticSensorI2C sensor;

    // MOTOR INSTANCE //
    BLDCMotor motor;


};


#endif

----
Wheel.cpp, here is also where the motor parameters are set
----

#include "Wheel.h"

Wheel::Wheel(int phAPin, int phBPin, int phCPin , int polepairNb, int _id)
{

  //MagneticSensorI2C::MagneticSensorI2C(uint8_t _chip_address, int _bit_resolution, uint8_t _angle_register_msb, int _bits_used_msb)
  //  @param _chip_address  I2C chip address
  //  @param _bit_resolution  bit resolution of the sensor
  //  @param _angle_register_msb  angle read register
  //  @param _bits_used_msb number of used bits in msb
  sensor = MagneticSensorI2C(0x36, 12, 0x0E, 4);

  // MOTOR INSTANCE //
  // BLDCMotor( int phA, int phB, int phC, int pp, int cpr, int en)
  // - phA, phB, phC - motor A,B,C phase pwm pins
  // - pp            - pole pair number
  // - cpr           - counts per rotation number (cpm=ppm*4)
  // - enable pin    - (optional input)
  motor = BLDCMotor(phAPin, phBPin, phCPin, polepairNb);

  id = _id;
}

Wheel::Wheel()
{}

void Wheel::init()
{
  // initialise magnetic sensor hardware
  sensor.init( id);
  // link the motor to the sensor
  motor.linkSensor(&sensor);

  // MOTOR SETTINGS --------------------------------------------------------------------------


  // power supply voltage
  // default 12V
  motor.voltage_power_supply = 8.4;

  // choose FOC modulation (optional)
  motor.foc_modulation = FOCModulationType::SinePWM;

  // set motion control loop to be used
  motor.controller = ControlType::angle ; //angle;

  // contoller configuration
  // default parameters in defaults.h

  // velocity PI controller parameters
  motor.PID_velocity.P = 0.01; // 0.01;
  motor.PID_velocity.I = 5;//5;
  // maximal voltage to be set to the motor
  motor.voltage_limit = 4.1; //2.1// 4.1;

  // velocity low pass filtering time constant
  // the lower the less filtered
  motor.LPF_velocity.Tf = 0.0001;

  // angle P controller
  motor.P_angle.P = 20; //20; //80; //16;
  // maximal velocity of the position control
  motor.velocity_limit = 60;//60 //100; //40;


  // comment out if not needed
  motor.useMonitoring(Serial);


  // initialize motor
  motor.init(); //frequency of pins to make it silent //KKK changed code in library 
  // align sensor and start FOC
  motor.initFOC();



  Serial.print("Motor "), Serial.print (id), Serial.println( " ready.");

}

void Wheel:: loopFOC ()
{
  // main FOC algorithm function
  // the faster you run this function the better
  motor.loopFOC();
  // Arduino UNO loop  ~1kHz
  // Bluepill loop ~10kHz
}

void Wheel:: move()

{
  // Motion control function
  // velocity, position or voltage (defined in motor.controller)
  // this function can be run at much lower frequency than loopFOC() function
  // You can also use motor.move() and set the motor.target in the code
  motor.move();


  // function intended to be used with serial plotter to monitor motor variables
  // significantly slowing the execution down!!!!
  // motor.monitor();

}

void Wheel::moveTo(float target_angle)
{

  // Motion control function
  // velocity, position or voltage (defined in motor.controller)
  // this function can be run at much lower frequency than loopFOC() function
  // You can also use motor.move() and set the motor.target in the code
  motor.move(target_angle);

  // function intended to be used with serial plotter to monitor motor variables
  // significantly slowing the execution down!!!!
  // motor.monitor();
}

float Wheel::getVelocity()
{
  return motor.shaftVelocity();
}

float Wheel::getAngle()
{
  return motor.shaftAngle();
}

----
config.h - Pin definitions
----

#ifndef config_h
#define config_h


//------------------------------------------------------------------------------
// PIN DEFINITIONS
//------------------------------------------------------------------------------
// MOTOR PINS //

// MOTOR B = M2
#define MA0_PIN          27
#define MA1_PIN          14
#define MA2_PIN          12

// MOTOR C = M1
#define MB0_PIN          5
#define MB1_PIN          18
#define MB2_PIN          19

// MOTOR A = M3
#define MC0_PIN          32
#define MC1_PIN          33
#define MC2_PIN          25

// ENABLE PIN FOR MOTORDRIVER POWER //
#define MOTOR_ENABLE_PIN  4


// I2C PINS FOR MUlTIPLEXER + ADDRESS OF MULTIPLEXER 0x70

#define SDA_PIN 21
#define SCL_PIN 22


#endif

----
globals.h - for future angle transmission from kinematic model
----

#ifndef Globals_h
#define Globals_h

//------------------------------------------------------------------------------
// INCLUDES
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// GLOBAL VARIABLES
//------------------------------------------------------------------------------

extern float target_angle = 90000000;


#endif

Hi,

I just received mine from aliexpress : https://www.aliexpress.com/item/4000757749339.html?spm=a2g0s.9042311.0.0.5d3b4c4dOQrKVh

And with a bad magnet…

so don’t buy in this store : https://greatzt.aliexpress.com/store/1916536/search?origin=y&SearchText=

JP

Thanks a lot for sharing!

I’ll experiment with the frequency and noise and see if I can find something that is quieter. I’ve found that some motors are noisier than others, I’ve had the exact same code running two different motors with the ESP32 and have one being almost dead silent and the other audible. I’ll also try and see if the controller has something to do with it and report back.

Hey Owen, The current does do what you describe. Running at 4.1 volts for a voltage limit gets me some nice values until we get over I guess 400rpm then its going up a bit too much for my liking. There is 2 huge advantages in running FOC with the robots and thats no missed movements or slipping as we call it and also the huge power savings for general swarm movement. I think the power savings at lower speeds could end up making our batteries go for days rather than hours. This being said I am trying to think about how we can also still end up with the faster speeds sometimes without the high current. I have questions going in my head like is it possible to predict the movements so that that the update of the sensor doesn’t mean its trying to drive the motor extra hard at the higher speeds or perhaps there are other approaches. BTW I did order a bunch of AS5600Ls from mouser and will solder these up early next week…so then we get to really test what funky stuff goes on with the white boards vs original known chips.

Id expect its some function of the speed of main loop adding in intermittent noise/or the fighting of the the angle control(my intuition says its that push and pull). The open loop is dead silent on these motors. Thats why I think the microphone could be interesting as it can pick up what we hear and see if any of the main loop times correspond with those values. I have tried picking it up on the oscilloscope but the main frequency of the motors is always stronger than anything else

Hey Guys,
this is very very interesting thread :smiley:

I just wanted to add some thoughts that might have been left unsaid. The current lowering for open loop and growing for closed loop is a topic we have discussed few times so far. It is a bit counter-intuitive but it makes sense at the end electrically. Here is a link to one of the older threads where this was discussed.
https://community.simplefoc.com/t/using-simplefoc-as-an-ebike-foc-controler/121/11?u=antun_skuric

Now in terms of huge current rise towards end of the range. The reason why this happens is that the FOC needs very precise alignment to work and since you are pushing the velocity very high you have two effects:

  1. les serious: the time in between reading the position value and setting the appropriate FOC voltages becomes relevant and the position interpolation is no longer a good estimation where the motor is. This can be solved with using:
position = position + velocity*dt
  1. a bit more serious: the time of execution of your loop function is starting to be considerable. For example:
    for velocity of 400rpm roughly 40rad/s or 7 rotations per second if your motor has 11 pole pairs then you need to do at least N loopFOC() calls per full rotor rotation
N = 11*6 = 66 times 

Which means that you need:

loop execution time < 1 / (66 * 7) ~ 2ms

This doesn’t seem a lot but if you combine 3 motors loop time grows rapidly.
Also for nice foc control you will need much more than 6 steps per rotation, at least double

So what you can do in this sense is to test your loop execution time. You can also do a trick of not running the motor.move() function in each loop cycle and down-sample it few times even more than 10 times should not be a problem.

Last thing I wanted to say is, if closed loop less silent than open loop, than it can be the filtering problem. Is the voltage mode silent?
I2C sensors are noisy and you should adapt the low pass filter value to your application. Noise in velocity calculation can be the source of noise. :slight_smile:

Yeah its not cool but any of those white boards are coming from the same place and all have fridge magnets instead of the real thing. You can get good magents from aliexpress in bulk for very very cheap. Also next week I will test out real as5600s vs those that come on the white boards, I am suspecting that they are not worth the headache to get in the first place. I will throw up a design for some boards in the next weeks that we can all use with the slightly newer as5600l and while it wont be $1.50 a board it will save everyone the headache of going through the same problem…in fact once I do that It think we should make link to it as much as possible so that other people can avoid buying things that simply are not what they say they are.

Atun thanks, those a really good points and lets us know where to dig deeper. Katrin and I will have a look at the voltage mode and report back.

You are right Adam !

BTW I will try with the magnet mounted vertically. I am not sure that it fully respects the datasheet specification, but it should work …

JP

Almost off topic but not totally…

3 years ago I made a simple windspeed meter using magnet in front of the hall sensor of my Samsung S5 phone.
See it here : Windmeter for Android by f2knpw - Thingiverse.
It worked but only on Samsung S5…

After reading the datasheet of AS56000 I discovered that it can be used in “autonomous” mode with just power, and it outputs a voltage proportionnal to the angle. (well a digital pot !)
I wonder if I could use this chip to input “saw tooth” signal provided by my windmeter into the microphone input of the phone (after voltage divider of course).

Do you know from your experiments what is the max RPM that this sensor supports ? (not found this in the datasheet).

My ultimate idea would be to make a hole into one of the blades to try to determine also wind direction as they did here : Anémomètre en stock | Flysurf.com

As I said it was almost off topic … sorry for this

JP

Well we got the new sensors today and nothing much changed. Voltage has the same amount of noise. Velocity mode without sensor input at least gives us less current but still never manage to get up to 1800rpm. In voltage mode the motor still seems to need the sensor attached which I find odd seeing as I would have thought that this is open loop. Anyhow more testing in progress:)

Voltage mode depends on the rotor position and this is why the encoder is needed. A great way to see the difference between Voltage mode and Velocity-open-loop mode is that if you hold the rotor on Voltage mode you will just feel the motor wanting to spin (fighting you); in contrast if you hold the rotor in velocity-open-loop mode you will feel the motor vibrating as the controller doesn’t know where the rotor is and just “hopes” the rotor is following along. This is essentially why you can’t have Voltage-open-loop mode.

Voltage mode is essentially closed loop control. Field Oriented Control | Arduino-FOC