Stepper_motor_controller firmware for LSM303DLHC sensor

Hi all. I’m making a homebrew rotator based on the next hardware elements:

  • Arduino Uno
  • CNC Shield
  • DRV8825
  • Stepper motors 17HD48002H-22B
  • LSM303DLHC sensor

The CNC Shield is configured with all jumpers to work with 1/32 microsteps. Next a picture with the LSM303DLHC sensor sticked in the arm (still in development):

I’ve modified the original stepper_motor_controller Arduino firmware to fit into my needs. I’m into trouble because the steppers don’t run to the desired position according to sensor readings. Next my modified code (not yet much debugged, sorry):
/*!
* @file stepper_motor_controller.ino
*
* This is the documentation for satnogs rotator controller firmware
* for stepper motors configuration. The board (PCB) is placed in
*
* satnogs-rotator-controller
and is for releases:
* v2.0
* v2.1
* v2.2
* wiki page
*
* @section dependencies Dependencies
*
* This firmware depends on
* AccelStepper library
being present on your system. Please make sure you
* have installed the latest version before using this firmware.
*
* @section license License
*
* Licensed under the GPLv3.
*
*/

#define SAMPLE_TIME        0.1      ///< Control loop in s
#define RATIO              3.4375   ///< Gear ratio of rotator gear box                                 default 54
#define MICROSTEP          32       ///< Set Microstep
#define MIN_PULSE_WIDTH    20       ///< In microsecond for AccelStepper
#define MAX_SPEED          500      ///< In steps/s, consider the microstep
#define MAX_ACCELERATION   800      ///< In steps/s^2, consider the microstep
#define SPR                6400     ///< Step Per Revolution, consider the microstep
#define MIN_M1_ANGLE       0        ///< Minimum angle of azimuth
#define MAX_M1_ANGLE       360      ///< Maximum angle of azimuth
#define MIN_M2_ANGLE       0        ///< Minimum angle of elevation
#define MAX_M2_ANGLE       90       ///< Maximum angle of elevation
#define DEFAULT_HOME_STATE LOW      ///< Change to LOW according to Home sensor
#define HOME_DELAY         12000    ///< Time for homing Deceleration in millisecond

#include <AccelStepper.h>
#include <Wire.h>
#include <globals.h>
#include <easycomm.h>
#include <rotator_pins.h>
#include <endstop.h>
#include <PID_v1.h>
#include <MultiStepper.h>

// Includes for sensor libraries
#include <Adafruit_Sensor.h>
#include <Adafruit_LSM303_U.h>
#include <Adafruit_L3GD20_U.h>
#include <Adafruit_9DOF.h>

uint32_t t_run = 0; // run time of uC
easycomm comm;
AccelStepper stepper_az(AccelStepper::DRIVER, M1IN1, M1IN2);
AccelStepper stepper_el(AccelStepper::DRIVER, M2IN1, M2IN2);

enum _rotator_error homing();
int32_t deg2step(float deg);
float step2deg(int32_t step);

// Initial assignmets to sensors
/* Assign a unique ID to the sensors */
Adafruit_9DOF                dof   = Adafruit_9DOF();
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
Adafruit_LSM303_Mag_Unified   mag   = Adafruit_LSM303_Mag_Unified(30302);
/* Update this with the correct SLP for accurate altitude measurements */
float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;

// Routine declarations for sensors (at the end)
void initSensors();
float getPitch(void);
float getHeading(void);

void setup() {
    
    // Serial Communication
    comm.easycomm_init();
    Serial.begin(115200);

    // Stepper Motor setup
    stepper_az.setEnablePin(MOTOR_EN);
    stepper_az.setPinsInverted(false, false, true);
    stepper_az.enableOutputs();      
    stepper_az.setMaxSpeed(MAX_SPEED);
    stepper_az.setAcceleration(MAX_ACCELERATION);
    stepper_az.setMinPulseWidth(MIN_PULSE_WIDTH);

    stepper_el.setEnablePin(MOTOR_EN);
    stepper_el.setPinsInverted(false, false, true);
    stepper_el.enableOutputs();    
    stepper_el.setMaxSpeed(MAX_SPEED);
    stepper_el.setAcceleration(MAX_ACCELERATION);
    stepper_el.setMinPulseWidth(MIN_PULSE_WIDTH);

    /* Initialise the sensors */
    initSensors();   
   
}

void loop() {
    
    // Run easycomm implementation
    comm.easycomm_proc();

    // Get position of both axis
    control_az.input = step2deg(stepper_az.currentPosition());
    control_el.input = step2deg(stepper_el.currentPosition());    

    // Check rotator status
    if (rotator.rotator_status != error) {
        if (rotator.homing_flag == false) {            
            // Check home flag
            rotator.control_mode = position;
            // Homing
            // rotator.rotator_error = homing(deg2step(-MAX_M1_ANGLE), deg2step(-MAX_M2_ANGLE));
            //rotator.rotator_error = homing(deg2step(getHeading()), deg2step(getPitch()));
            //rotator.rotator_error = homing(deg2step(MIN_M1_ANGLE - 1), deg2step(MIN_M2_ANGLE - 1));
            rotator.rotator_error = homing();
            
            if (rotator.rotator_error == no_error) {
                // No error
                rotator.rotator_status = idle;
                rotator.homing_flag = true;
            } else {
                // Error
                rotator.rotator_status = error;
                rotator.rotator_error = homing_error;
            }
        } else {             
            // Control Loop
            stepper_az.moveTo(deg2step(control_az.setpoint));
            stepper_el.moveTo(deg2step(control_el.setpoint));
            rotator.rotator_status = pointing;
            // Move azimuth and elevation motors            
            stepper_az.run();
            stepper_el.run();            
            // Idle rotator
            if (stepper_az.distanceToGo() == 0 && stepper_el.distanceToGo() == 0) {
                rotator.rotator_status = idle;
            }
        }
    } else {
        // Error handler, stop motors and disable the motor driver
        stepper_az.stop();
        stepper_az.disableOutputs();
        stepper_el.stop();
        stepper_el.disableOutputs();
        if (rotator.rotator_error != homing_error) {
            // Reset error according to error value
            rotator.rotator_error = no_error;
            rotator.rotator_status = idle;
        }
    }
}

/**************************************************************************/
/*!
    @brief    Move both axis with one direction in order to find home position,
              end-stop switches
    @param    seek_az
              Steps to find home position for azimuth axis
    @param    seek_el
              Steps to find home position for elevation axis
    @return   _rotator_error
*/
/**************************************************************************/

// NEW HOMING ROUTINE TAKE INTO ACCOUNT THE SENSORS
enum _rotator_error homing() {
  
    float az_actual = getHeading();
    float el_actual = getPitch();
    
    // Set the steppers position with sensor reading
    stepper_az.setCurrentPosition(deg2step(az_actual));
    stepper_el.setCurrentPosition(deg2step(el_actual));
    
    // Moving steppers to 0
    stepper_az.setSpeed(MAX_SPEED); stepper_el.setSpeed(MAX_SPEED);
    stepper_az.runToNewPosition(0); stepper_el.runToNewPosition(0);
    
    // Delay to Deccelerate and homing, to complete the movements
    uint32_t time = millis();
    while (millis() - time < HOME_DELAY) {
       // wdt.watchdog_reset();        
        stepper_az.run();
        stepper_el.run();        
    }
    
    // Set the home position and reset all critical control variables
    stepper_az.setCurrentPosition(0);
    stepper_el.setCurrentPosition(0);
    control_az.setpoint = 0;
    control_el.setpoint = 0;

    return no_error;  
}


/**************************************************************************/
/*!
    @brief    Convert degrees to steps according to step/revolution, rotator
              gear box ratio and microstep
    @param    deg
              Degrees in float format
    @return   Steps for stepper motor driver, int32_t
*/
/**************************************************************************/
int32_t deg2step(float deg) {
    return (RATIO * SPR * deg / 360);
}

/**************************************************************************/
/*!
    @brief    Convert steps to degrees according to step/revolution, rotator
              gear box ratio and microstep
    @param    step
              Steps in int32_t format
    @return   Degrees in float format
*/
/**************************************************************************/
float step2deg(int32_t step) {
    return (360.00 * step / (SPR * RATIO));
}

/*
 * -- Sensors routines
 */

// -- Init sensor routine
void initSensors()
{
  if(!accel.begin())
  {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println(F("Ooops, no LSM303 detected ... Check your wiring!"));
    while(1);
  }
  if(!mag.begin())
  {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
    while(1);
  }
}

// -- Routines for getting orientation.
// -- Pitch (X) is between -180º and +180º and heading (Z) between 0º and +359º
float getPitch(void)
{
  sensors_event_t accel_event; 
  // sensors_event_t mag_event; 
  sensors_vec_t   orientation;

  /* Calculate pitch and roll from the raw accelerometer data */
  accel.getEvent(&accel_event);
  if (dof.accelGetOrientation(&accel_event, &orientation))
  {
    /* 'orientation' should have valid .roll and .pitch fields */
    // Serial.print(F("Roll: "));
    // Serial.print(orientation.roll);
    // Serial.print(F("; "));
    /*Serial.print(F("Pitch (elevación): "));
    Serial.print(orientation.pitch);
    Serial.println(F("; "));*/
  }

  return orientation.pitch;  
}

float getHeading(void)
{
  // sensors_event_t accel_event;
  sensors_event_t mag_event;
  sensors_vec_t   orientation;
  
  /* Calculate the heading using the magnetometer */
  mag.getEvent(&mag_event);
  if (dof.magGetOrientation(SENSOR_AXIS_Z, &mag_event, &orientation))
  {
    /* 'orientation' should have valid .heading data now */
    /*Serial.print(F("Heading (azimut): "));
    Serial.print(orientation.heading);
    Serial.print(F("; "));*/
  } 
    
  return orientation.heading;
}

Has anyone experience with the same hardware than me?

Thanks in advance.

A diff might be easier to review.

Hi again and sorry by the delay. Here is the diff file:

--- stepper_motor_controller.ino	2020-10-10 16:33:04.486458773 +0200
+++ stepper_motor_controller_with_LSM303.ino	2020-10-10 16:30:51.033087575 +0200
@@ -23,17 +23,17 @@
  */
 
 #define SAMPLE_TIME        0.1   ///< Control loop in s
-#define RATIO              54    ///< Gear ratio of rotator gear box
-#define MICROSTEP          8     ///< Set Microstep
-#define MIN_PULSE_WIDTH    20    ///< In microsecond for AccelStepper
-#define MAX_SPEED          3200  ///< In steps/s, consider the microstep
-#define MAX_ACCELERATION   1600  ///< In steps/s^2, consider the microstep
-#define SPR                1600L ///< Step Per Revolution, consider the microstep
+#define RATIO              3.4375    ///< Gear ratio of rotator gear box
+#define MICROSTEP          32     ///< Set Microstep
+#define MIN_PULSE_WIDTH    1.9    ///< In microsecond for AccelStepper
+#define MAX_SPEED          5000  ///< In steps/s, consider the microstep
+#define MAX_ACCELERATION   800  ///< In steps/s^2, consider the microstep
+#define SPR                6400 ///< Step Per Revolution, consider the microstep
 #define MIN_M1_ANGLE       0     ///< Minimum angle of azimuth
 #define MAX_M1_ANGLE       360   ///< Maximum angle of azimuth
 #define MIN_M2_ANGLE       0     ///< Minimum angle of elevation
-#define MAX_M2_ANGLE       180   ///< Maximum angle of elevation
-#define DEFAULT_HOME_STATE HIGH  ///< Change to LOW according to Home sensor
+#define MAX_M2_ANGLE       90   ///< Maximum angle of elevation
+#define DEFAULT_HOME_STATE LOW  ///< Change to LOW according to Home sensor
 #define HOME_DELAY         12000 ///< Time for homing Deceleration in millisecond
 
 #include <AccelStepper.h>
@@ -41,21 +41,40 @@
 #include "../libraries/globals.h"
 #include "../libraries/easycomm.h"
 #include "../libraries/rotator_pins.h"
-#include "../libraries/rs485.h"
+//#include "../libraries/rs485.h"
 #include "../libraries/endstop.h"
-#include "../libraries/watchdog.h"
+//#include "../libraries/watchdog.h"
+#include <PID_v1.h>
+#include <MultiStepper.h>
+// -- Include sensor libraries
+#include <Adafruit_Sensor.h>
+#include <Adafruit_LSM303_U.h>
+#include <Adafruit_L3GD20_U.h>
+#include <Adafruit_9DOF.h>
 
 uint32_t t_run = 0; // run time of uC
 easycomm comm;
-AccelStepper stepper_az(1, M1IN1, M1IN2);
-AccelStepper stepper_el(1, M2IN1, M2IN2);
+AccelStepper stepper_az(AccelStepper::DRIVER, M1IN1, M1IN2);
+AccelStepper stepper_el(AccelStepper::DRIVER, M2IN1, M2IN2);
 endstop switch_az(SW1, DEFAULT_HOME_STATE), switch_el(SW2, DEFAULT_HOME_STATE);
-wdt_timer wdt;
+//wdt_timer wdt;
 
 enum _rotator_error homing(int32_t seek_az, int32_t seek_el);
 int32_t deg2step(float deg);
 float step2deg(int32_t step);
 
+/* Assign a unique ID to the sensors */
+Adafruit_9DOF                dof   = Adafruit_9DOF();
+Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
+Adafruit_LSM303_Mag_Unified   mag   = Adafruit_LSM303_Mag_Unified(30302);
+/* Update this with the correct SLP for accurate altitude measurements */
+float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
+
+// -- Routine declarations for sensor (at the end)
+void initSensors();
+float getPitch(void);
+float getHeading(void);
+
 void setup() {
     // Homing switch
     switch_az.init();
@@ -63,6 +82,7 @@
 
     // Serial Communication
     comm.easycomm_init();
+    Serial.begin(115200);
 
     // Stepper Motor setup
     stepper_az.setEnablePin(MOTOR_EN);
@@ -71,6 +91,7 @@
     stepper_az.setMaxSpeed(MAX_SPEED);
     stepper_az.setAcceleration(MAX_ACCELERATION);
     stepper_az.setMinPulseWidth(MIN_PULSE_WIDTH);
+    stepper_el.setEnablePin(MOTOR_EN);
     stepper_el.setPinsInverted(false, false, true);
     stepper_el.enableOutputs();
     stepper_el.setMaxSpeed(MAX_SPEED);
@@ -78,12 +99,15 @@
     stepper_el.setMinPulseWidth(MIN_PULSE_WIDTH);
 
     // Initialize WDT
-    wdt.watchdog_init();
+    //wdt.watchdog_init();
+    
+    /* Initialise the sensors */
+   initSensors();  
 }
 
 void loop() {
     // Update WDT
-    wdt.watchdog_reset();
+    //wdt.watchdog_reset();
 
     // Get end stop status
     rotator.switch_az = switch_az.get_state();
@@ -154,6 +178,10 @@
 enum _rotator_error homing(int32_t seek_az, int32_t seek_el) {
     bool isHome_az = false;
     bool isHome_el = false;
+    
+    // Get the actual position from sensor
+    stepper_az.setCurrentPosition(deg2step(getHeading()));
+    stepper_el.setCurrentPosition(deg2step(getPitch()));
 
     // Move motors to "seek" position
     stepper_az.moveTo(seek_az);
@@ -162,13 +190,13 @@
     // Homing loop
     while (isHome_az == false || isHome_el == false) {
         // Update WDT
-        wdt.watchdog_reset();
-        if (switch_az.get_state() == true && !isHome_az) {
+        //wdt.watchdog_reset();
+        if ((switch_az.get_state() == true || stepper_az.currentPosition() == 0) && !isHome_az) {
             // Find azimuth home
             stepper_az.moveTo(stepper_az.currentPosition());
             isHome_az = true;
         }
-        if (switch_el.get_state() == true && !isHome_el) {
+        if ((switch_el.get_state() == true || stepper_el.currentPosition() == 0) && !isHome_el) {
             // Find elevation home
             stepper_el.moveTo(stepper_el.currentPosition());
             isHome_el = true;
@@ -224,3 +252,66 @@
 float step2deg(int32_t step) {
     return (360.00 * step / (SPR * RATIO));
 }
+
+/*
+ * -- Sensor routines
+ */
+
+// -- Init sensor routine
+void initSensors()
+{
+  if(!accel.begin())
+  {
+    /* There was a problem detecting the LSM303 ... check your connections */
+    Serial.println(F("Ooops, no LSM303 detected ... Check your wiring!"));
+    while(1);
+  }
+  if(!mag.begin())
+  {
+    /* There was a problem detecting the LSM303 ... check your connections */
+    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
+    while(1);
+  }
+}
+
+// -- Routines to get orientation.
+// -- pitch (X) betwenn -180º and +180º, and heading (Z) between 0º y +359º
+float getPitch(void)
+{
+  sensors_event_t accel_event; 
+  // sensors_event_t mag_event; 
+  sensors_vec_t   orientation;
+
+  /* Calculate pitch and roll from the raw accelerometer data */
+  accel.getEvent(&accel_event);
+  if (dof.accelGetOrientation(&accel_event, &orientation))
+  {
+    /* 'orientation' should have valid .roll and .pitch fields */
+    // Serial.print(F("Roll: "));
+    // Serial.print(orientation.roll);
+    // Serial.print(F("; "));
+    //Serial.print(orientation.pitch);
+    //Serial.println(F("; "));*/
+  }
+
+  return orientation.pitch;  
+}
+
+float getHeading(void)
+{
+  // sensors_event_t accel_event;
+  sensors_event_t mag_event;
+  sensors_vec_t   orientation;
+  
+  /* Calculate the heading using the magnetometer */
+  mag.getEvent(&mag_event);
+  if (dof.magGetOrientation(SENSOR_AXIS_Z, &mag_event, &orientation))
+  {
+    /* 'orientation' should have valid .heading data now */
+    /*Serial.print(F("Heading (azimut): "));
+    Serial.print(orientation.heading);
+    Serial.print(F("; "));*/
+  } 
+    
+  return orientation.heading;
+}

I have issues with movements because the angle and direction I set and the real movement don’t be the same. I’ve tried several values in RATIO 'cos I think it’s fine but I suspect that this is the problem. This is my design:

Thanks in advance.

Hi vjosesan,
Sorry to bring up old topic.
Did you have any luck solving your problems.
I am trying to incorporate an LSM303DHLC with my rotator, and am interested in your outcome.

Thanks in advance
Steve
VK4SMC

Hi Steve,

I’ve had little time to go on with my rotator. I’ve developed a modified Arduino sketch to include some steps and instructions to calibrate LSM303 but I have no much success (now I’m getting a NETROTCTL_RET -5 error again and I don’t know why). If you want it I can send you by e-mail (the code should works but it’s need to clean a bit).
Meanwhile I’m trying with K3NG software too: I’ve had a hard work to identify correctly the output pins in the configuration files but now it appears to move, however the movement is erratic (I’m still working on it …)

Regards.

Hi again vjosesan,
Thank you for your speedy reply.
Sorry for slow reply.
I am having internet service “Upgrades” at the moment so no internet most of the days.
Thanks for offer of latest code, but will say no at the moment.
I have been working on the stepper_motor_controller.ino that you have posted above and modified it to use the Pololu LSM303 sensor libraries, Which has some sensor calibration, from Adafruit libraries , with some success.
I have a little more testing to do.
When finished I will post code here.
Should only be a week or so.

Regards,
Steve VK4SMC

Thank you for your offer of your code. I have no much time in the latest months and I only can go on with my rotator a few moments in a month. Now I’m getting more success with LSM303 with the K3NG code but it also a bit erratic sometimes.

I have a little more testing to do too next Christmas … I hope.

Thank you very much again for your help.

Regards, Vic.