Solar Tracker with Node Red AccelStepper and MQTT

Hello

I’m successfully sending MQTT messages from Node-Red (Altitude and Azimuth from the SunPos node) as a number and reading them in an Arduino sketch where I’d like to move a Stepper Motor to the degrees supplied by the SunPos node, I also have the Steppers moving with the test sketches.

I’m using AysncMQTT Client, could someone please advise on where to correctly place that part in the code?

The code is still a work in progress and not properly commented I’m afraid.

I wish to also later enable and disable the motors after every move to save power.

#include <WiFi.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
}
#include <AsyncMqttClient.h>
#include <AccelStepper.h>

#define WIFI_SSID "xxxxxxx"
#define WIFI_PASSWORD "xxxxxxxxx"

#define MQTT_HOST IPAddress(192, 168, 0, 3)
//#define MQTT_HOST IPAddress(192, 168, 1, 188)
#define MQTT_PORT 1883

// Define two steppers and the pins they will use
AccelStepper stepper1(AccelStepper::DRIVER, 36, 39);
//AccelStepper stepper2(AccelStepper::DRIVER, 14, 12);

int pos1 = 3600;
//int pos2 = 5678;

String temperatureString = "";      // Variable to hold the temperature reading
unsigned long previousMillis0 = 0;   // Stores last time temperature was published
unsigned long previousMillis1 = 0;
const long interval = 5000;         // interval at which to publish sensor readings

AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer;

void connectToWifi() {
  Serial.println("Connecting to Wi-Fi...");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}

void connectToMqtt() {
  Serial.println("Connecting to MQTT...");
  mqttClient.connect();
}

void WiFiEvent(WiFiEvent_t event) {
  Serial.printf("[WiFi-event] event: %d\n", event);
  switch (event) {
    case SYSTEM_EVENT_STA_GOT_IP:
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
      connectToMqtt();
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      Serial.println("WiFi lost connection");
      xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
      xTimerStart(wifiReconnectTimer, 0);
      break;
  }
}

void onMqttConnect(bool sessionPresent) {
  Serial.println("Connected to MQTT.");
  Serial.print("Session present: ");
  Serial.println(sessionPresent);
  uint16_t packetIdSub0 = mqttClient.subscribe("azimuth", 0);
  uint16_t packetIdSub1 = mqttClient.subscribe("altitude", 0);
  Serial.print("Subscribing at QoS 0, packetId: ");
  Serial.println(packetIdSub0);
  Serial.println(packetIdSub1);

}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  Serial.println("Disconnected from MQTT.");

  if (WiFi.isConnected()) {
    xTimerStart(mqttReconnectTimer, 0);
  }
}

void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
  //Serial.println("Subscribe acknowledged.");
  //Serial.print("  packetId: ");
  //Serial.println(packetId);
  //Serial.print("  qos: ");
  //Serial.println(qos);
}

void onMqttUnsubscribe(uint16_t packetId) {
  //Serial.println("Unsubscribe acknowledged.");
  //Serial.print("  packetId: ");
  //Serial.println(packetId);
}

void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
  String messageTemp;
  for (int i = 0; i < len; i++) {
    Serial.print((char)payload[i]);
    messageTemp += (char)payload[i];
  }

  if (strcmp(topic, "azimuth") == 0) {
    stepper1.moveTo(messageTemp);
    stepper1.run();
    delay(5);
    Serial.print("moved to: ");
  }

  Serial.println(" Publish received.");
  Serial.print("  topic: ");
  Serial.println(topic);
  //Serial.print("  qos: ");
  //Serial.println(properties.qos);
  //Serial.print("  dup: ");
  //Serial.println(properties.dup);
  //Serial.print("  retain: ");
  //Serial.println(properties.retain);
  //Serial.print("  len: ");
  //Serial.println(len);
  //Serial.print("  index: ");
  //Serial.println(index);
  //Serial.print("  total: ");
  //Serial.println(total);
}

void onMqttPublish(uint16_t packetId) {
  Serial.println("Publish acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

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

  stepper1.setCurrentPosition(0);
  stepper1.setMaxSpeed(1000);
  stepper1.setAcceleration(1000);
  //stepper2.setMaxSpeed(2000);
  //stepper2.setAcceleration(800);

  stepper1.setEnablePin(34);

  mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
  wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));

  WiFi.onEvent(WiFiEvent);

  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  mqttClient.onSubscribe(onMqttSubscribe);
  mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onMessage(onMqttMessage);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  connectToWifi();
}

void loop() {

}

Are you using a ESP32?

Yes I am

I know this is not your issue but why is there code in the loop() when you are using freeRTOS?

When using freeRTOS the loop should be void loop() {} // runs on core 1. You can find many mentions that once freeRTOS is loaded the void loop() should be left empty as code in the void loop() is NOT guaranteed to run. freeRTOS demotes the void loop() to the lowest priority and its job becomes cleanup.

You will, also, find that using millis for timing is not needed or as efficient as using vTaskDelayUntil( ).

I will post an example of a use of freeRTOS with the Arduino IDE, the below code is a work in progress and is not very streamlined and filled with redundancies.

#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
////
#include "ESP32_SPI_API.h"
////////////////////////////////////////////////////
/* create event group */
EventGroupHandle_t eg;
/* define event bits */
// #define evt ( 1 << 0 ) // 1
#define evtfMLX90393_ReadSensor_0 ( 1 << 2 ) // 100
#define evtfMLX90393_ReadSensor_1 ( 1 << 3 ) // 1000
#define evtTriggerMLX90393_1  ( 1 << 4 ) // 10000
////////////////////////////////////////////////////
#define TaskCore1 1
#define TaskCore0 0
#define SerialDataBits 115200
#define TaskStack10K 10000
#define TaskStack30K 30000
#define Priority4 4
///////////////////////////////////////////////////
TickType_t xSemaphoreTicksToWait = 500;
///////////////////////////////////////////
#define MLX90393_AXIS_ALL       (0x0F)  // X+Y+Z+T axis bits for commands
// registers
#define MLX90393_CONF1          (0x00)  // Gain
#define MLX90393_CONF2          (0x01)  // Burst, comm mode, trigger interrupt, 
#define MLX90393_CONF3          (0x02)  // Oversampling, filter, res.
#define MLX90393_CONF4          (0x03)  // Sensitivty drift.
#define MLX90393_GAIN_SHIFT     (0x04)  // Left-shift for gain bits. 


#define MLX90393_HALL_CONF      (0x0C)  // Hall plate spinning rate adj.
#define MLX90393_STATUS_OK      (0x00)  // OK value for status response. 
#define MLX90393_STATUS_MASK    (0xFC)  // Mask for status OK checks.
// commands
#define MLX90393_REG_SB (0x10) // Start burst mode.
#define MLX90393_REG_SW (0x20) // Start wakeup on change mode.
#define MLX90393_REG_SM (0x30) // Start single-meas mode. 
#define MLX90393_REG_RM (0x40) // Read measurement
#define MLX90393_REG_RR (0x50) // Read register.
#define MLX90393_REG_WR (0x60) // Write Register.
#define MLX90393_REG_EX (0x80) // Exit moode
#define MLX90393_REG_HR (0xD0) // Memory recall
#define MLX90393_REG_HS (0x70) // Memory store
#define MLX90393_REG_RT (0xF0) // Reset
#define MLX90393_REG_NOP (0x00) // NOP
/** Gain settings for CONF1 register. */
uint8_t    MLX90393_GAIN_5X     = (0x00);
uint8_t    MLX90393_GAIN_4X = (0x01);
uint8_t    MLX90393_GAIN_3X = (0x02);
uint8_t    MLX90393_GAIN_2_5X = (0x03);
uint8_t    MLX90393_GAIN_2X = (0x04);
uint8_t    MLX90393_GAIN_1_67X = (0x05);
uint8_t    MLX90393_GAIN_1_33X = (0x06);
uint8_t    MLX90393_GAIN_1X = (0x07);
//
//#define MLX90393_RES_2_15 (0x00) // Resolution (2^15) - 0.
//#define MLX90393_RES_2_16 (0x01) // Resolution (2^16) - 1.
//#define MLX90393_RES_2_17 (0x02) // Resolution (2^17) - 3.
//#define MLX90393_RES_2_18 (0x03) // Resolution (2^18) - 7.
///////////////////////////////////////////
//SemaphoreHandle_t sema_fMLX90393_1;
//SemaphoreHandle_t sema_MLX90393readSensorTrigger;
//SemaphoreHandle_t sema_triggerEvent;
///////////////////////////////////////////
#define csPinMLX90393_0 32
#define csPinMLX90393_1 5
#define spiCLK 25 // 
#define spiMOSI 26 // 
#define spiMISO 27 // 
#define MLX90393int_0 18
#define MLX90393int_1 4
spi_device_handle_t _hMLX90393_0;
spi_device_handle_t _hMLX90393_1;
//////////////////////////////////////////
TickType_t xTicksToWait0 = 0;
//////////////////////////////////////////
/** Lookup table to convert (typical)raw values to uT based on gain and res.
   each gain setting has 4 resoultions
   numbered from left to right
   res=0, res=1, res=2, res=3
   first value is gain for X/Y 2nd value is gain for Z
*/
const float mlx90393_lsb_lookup[8][4][2] = {
  /* 5x gain */
  {{0.805, 1.468}, {1.610, 2.936}, {3.220, 5.872}, {6.440, 11.744}},
  /* 4x gain */
  {{0.644, 1.174}, {1.288, 2.349}, {2.576, 4.698}, {5.152, 9.395}},
  /* 3x gain */
  {{0.483, 0.881}, {0.966, 1.762}, {1.932, 3.523}, {3.864, 7.046}},
  /* 2.5x gain */
  {{0.403, 0.734}, {0.805, 1.468}, {1.610, 2.936}, {3.220, 5.872}},
  /* 2x gain */
  {{0.322, 0.587}, {0.644, 1.174}, {1.288, 2.349}, {2.576, 4.698}},
  /* 1.667x gain */
  {{0.268, 0.489}, {0.537, 0.979}, {1.073, 1.957}, {2.147, 3.915}},
  /* 1.333x gain */
  {{0.215, 0.391}, {0.429, 0.783}, {0.859, 1.566}, {1.717, 3.132}},
  /* 1x gain */
  {{0.161, 0.294}, {0.322, 0.587}, {0.644, 1.174}, {1.288, 2.349}}
};
//////////////////////////////////////////
struct stuMagCompensation
{
  float x = 0.0f;
  float y = 0.0f;
  float z = 0.0f;
} xMag;
//////////////////////////////////////////
//////////////////////////////////////////
//////////////////////////////////////////
//////////////////////////////////////////
//////////////////////////////////////////
//////////////////////////////////////////
void IRAM_ATTR fMLX90393_triggerReadSensor_1( )
{
  BaseType_t xHigherPriorityTaskWoken;
  xEventGroupSetBitsFromISR(eg, evtfMLX90393_ReadSensor_1, &xHigherPriorityTaskWoken);
}
////
void IRAM_ATTR fMLX90393_triggerReadSensor_0( )
{
  BaseType_t xHigherPriorityTaskWoken;
  xEventGroupSetBitsFromISR(eg, evtfMLX90393_ReadSensor_0, &xHigherPriorityTaskWoken);
}
//////////////////////////////////////////
void setup()
{
  //  sema_triggerEvent = xSemaphoreCreateMutex();
  // sema_fMLX90393_1 = xSemaphoreCreateBinary(); // must create this semaphore before the tasks are created
  //  sema_MLX90393readSensorTrigger = xSemaphoreCreateBinary();
  pinMode( MLX90393int_0, INPUT );
  pinMode( MLX90393int_1, INPUT );
  Serial.begin( SerialDataBits );
  ////
  eg = xEventGroupCreate();
  ////
  if ( fInitializeSPI_Channel( spiCLK, spiMOSI, spiMISO, HSPI_HOST, true) != 0 )
  {
    Serial.println ( "fInitializeSPI_Channel error " );
  }
  if ( fInitializeSPI_Devices( _hMLX90393_0, csPinMLX90393_0 ) != 0 )
  {
    Serial.print ( "fInitializeSPI_Device MLX90393 error " );
  }
  ////
  if ( fInitializeSPI_Devices( _hMLX90393_1, csPinMLX90393_1 ) != 0 )
  {
    Serial.print ( "fInitializeSPI_Device MLX90393 error " );
  }
  // if ( fInitializeAG() != true )
  //  {
  //    Serial.print ( "fInitializeSPI_Device LSM9DS1 error " );
  //  }
  //  if ( fInitializeSPI_Devices( _hMLX90393, csPinMLX90393 ) != true )
  //  {
  //    Serial.print ( "fInitializeSPI_Device LSM9DS1_M_0 error " );
  //  }
  //  if ( fInitializeM() != true )
  //  {
  //    Serial.print ( "fInitializeSPI_Device LSM9DS1_M error " );
  //  }
  /////////////////// CORE 0 ////////////////////////////////////////////////////////////////////////////////
  xTaskCreatePinnedToCore ( fMLX90393_0, "fMLX90393_0", TaskStack30K, NULL, Priority4, NULL, TaskCore0 );
  // xTaskCreatePinnedToCore ( fGetIMU, "v_getIMU", TaskStack30K, NULL, Priority4, NULL, TaskCore0 );
  //////////////////// CORE 1 ////////////////////////////////////////////////////////////////////////////////
  xTaskCreatePinnedToCore ( fMLX90393_1, "fMLX90393_1", TaskStack30K, NULL, Priority4, NULL, TaskCore1 );
}
//////////////////////////////////////////////////////////
void loop() {}
//////////////////////////////////////////////////////////
////
// input 0,1,2,3 for resolution settings
void fMLX90393_ChangeResoultion( spi_device_handle_t &hM, int xyz_res )
{
  int rx[3] = { 0 };
  uint16_t _res = (((uint8_t)xyz_res & 0x3) << 4) | (((uint8_t)xyz_res & 0x3) << 2) | ((uint8_t)xyz_res & 0x3);
  // read config3 register
  fReadSPIdataXbits( hM, (MLX90393_CONF3 & 0x3f) << 2, rx, 3 );
  uint16_t workingValue = ( rx[2] << 8 ) | rx[1];
  uint16_t RES_XYZ_MASK = 0x07e0, RES_XYZ_SHIFT = 5;
  // create bits for conversion to values needed
  // 6 bits, 2 bits per plane, Z bits MSB, y the middle bits, x LSB
  workingValue = ( workingValue & ~RES_XYZ_MASK) | ( _res << RES_XYZ_SHIFT ) & RES_XYZ_MASK ;
  // split 16 bit into 2 8 bits
  rx[0] = workingValue & 0xFF; // low byte
  workingValue = workingValue >> 8;
  rx[1] = workingValue & 0xFF;
  fWriteSPIdata32bits( hM, MLX90393_REG_WR, rx[0], rx[1], (MLX90393_CONF3 & 0x3F) << 2 );
}
////
////
void fMLX90393_1( void * pvParameters )
{
  int rx[10] = { 0 };
  int _gain = MLX90393_GAIN_1X;
  int xyResoultion = 0;
  int zResoultion = 1;
  int xRes;
  int yRes;
  int zRes;
  float xAverage = 0.0f;
  esp_err_t intError;
  vTaskDelay( 500 ); // delay to let the other sensor come online 1st
  intError = fWriteSPIdata8bits2( _hMLX90393_1, MLX90393_REG_RT ); // reset
  vTaskDelay( 300 ); // allow reset time to complete
  // set gain
  intError = fWriteSPIdata32bits( _hMLX90393_1, MLX90393_REG_WR, 0x00, (((_gain & 0x7) << MLX90393_GAIN_SHIFT) | MLX90393_HALL_CONF), (MLX90393_CONF1 & 0x3F) << 2 );
  vTaskDelay( 10 );
  // MLX90393_CONF2 for comm mode TRIG_INT_SEL 0x1 + 0x10 use spi + relative measurement 0x1 + temp compensation 0x1 = 0xD0
  intError = fWriteSPIdata32bits( _hMLX90393_1, MLX90393_REG_WR, 0x00, 0xD0, (MLX90393_CONF2 & 0x3f) << 2 ); // using 0x00 for the lower 8 bits of the register
  vTaskDelay( 10 );
  //
  fMLX90393_ChangeResoultion( _hMLX90393_1, 0 );
  vTaskDelay( 10 );
  // read MLX90393_CONF3 for resolution
  intError = fReadSPIdataXbits( _hMLX90393_1, (MLX90393_CONF3 & 0x3F) << 2, rx, 3 );
  // combine response into 16bit
  int temp = ( rx[2] << 8) | rx[1];
  temp = temp >> 5;
  // extract 1st 2 bits, x resolution
  xRes = temp & 3;
  temp = temp >> 2;
  yRes = temp & 3;
  temp = temp >> 2;
  zRes = temp & 3;
  attachInterrupt( MLX90393int_1, fMLX90393_triggerReadSensor_1, RISING );
  while (1)
  {
    xEventGroupWaitBits ( eg, evtTriggerMLX90393_1, pdTRUE, pdTRUE, portMAX_DELAY);
    // Serial.println( " wakie wakie 1 ");
    // request a single data read
    intError = fWriteSPIdata8bits2( _hMLX90393_1, MLX90393_REG_SM | MLX90393_AXIS_ALL ); // single measurement all axis and temprature
    //triggered  from void IRAM_ATTR triggerMLX90393read(), when the unit has data available
    xEventGroupWaitBits ( eg, evtfMLX90393_ReadSensor_1, pdTRUE, pdTRUE, portMAX_DELAY );
    intError = fReadSPIdataXbits( _hMLX90393_1, MLX90393_REG_RM | MLX90393_AXIS_ALL, rx, 9 );
    /* rx[0] = status bit
       rx[1] & rx[2] = temprature
       rx[3] & rx[4] x
       rx[5] & rx[6] y
       rx[7] & rx[8] z
    */
    // Convert data to 16 bit
    int16_t xi, yi, zi;
    xi = (rx[3] << 8) | rx[4]; // shift MSB over to the left by 8 & with LSB
    yi = (rx[5] << 8) | rx[6];
    zi = (rx[7] << 8) | rx[8];
    // determine gain being used
    float xT = 0.0f, yT = 0.0f, zT = 0.0f;
    xT = (float)xi *  mlx90393_lsb_lookup[_gain][xRes][xyResoultion];
    yT = (float)yi * mlx90393_lsb_lookup[_gain][yRes][xyResoultion];
    zT = (float)zi * mlx90393_lsb_lookup[_gain][zRes][zResoultion];
    xMag.x -= xT;
    xMag.y -= yT;
    xMag.z -= zT;

  }
  vTaskDelete(NULL);
}
/////////////////////////////////////////////////////////
void fMLX90393_0( void * pvParameters )
{
  int rx[10] = { 0 };
  int _gain = MLX90393_GAIN_1X;
  int xyResoultion = 0;
  int zResoultion = 1;
  int xRes;
  int yRes;
  int zRes;
  float xAverage = 0.0f;
Serial.println( "CLEARSHEET" );
Serial.println("LABEL,uT_X, uT_Y, uT_Z");

  esp_err_t intError;
  intError = fWriteSPIdata8bits2( _hMLX90393_0, MLX90393_REG_RT ); // reset
  vTaskDelay( 300 ); // allow reset time to complete
  // set gain
  intError = fWriteSPIdata32bits( _hMLX90393_0, MLX90393_REG_WR, 0x00, (((_gain & 0x7) << MLX90393_GAIN_SHIFT) | MLX90393_HALL_CONF), (MLX90393_CONF1 & 0x3F) << 2 );
  vTaskDelay( 10 );
  // MLX90393_CONF2 for comm mode TRIG_INT_SEL 0x1 + 0x10 use spi + relative measurement 0x1 + temp compensation 0x1 = 0xD0
  intError = fWriteSPIdata32bits( _hMLX90393_0, MLX90393_REG_WR, 0x00, 0xD0, (MLX90393_CONF2 & 0x3f) << 2 ); // using 0x00 for the lower 8 bits of the register
  vTaskDelay( 10 );
  fReadSPIdataXbits( _hMLX90393_0, (MLX90393_CONF2 & 0x3f) << 2, rx, 3 );
  fMLX90393_ChangeResoultion( _hMLX90393_0, 0 );
  vTaskDelay( 10 );
  // read MLX90393_CONF3 for resolution
  intError = fReadSPIdataXbits( _hMLX90393_0, (MLX90393_CONF3 & 0x3f) << 2, rx, 3 );
  int temp = ( rx[2] << 8) | rx[1];
  temp = temp >> 5;
  xRes = temp & 3;
  temp = temp >> 2;
  yRes = temp & 3;
  temp = temp >> 2;
  zRes = temp & 3;
  TickType_t xLastWakeTime;
  const TickType_t xFrequency = pdMS_TO_TICKS( 70 );
  // Initialise the xLastWakeTime variable with the current time.
  xLastWakeTime = xTaskGetTickCount();
  //attaching the interrupt to microcontroller pin
  attachInterrupt( MLX90393int_0, fMLX90393_triggerReadSensor_0, RISING );
  while (1)
  {
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    // Serial.println( " wakie wakie 0 ");
    xMag.x = 0.0f; xMag.y = 0.0f; xMag.z = 0.0f;
    // request a single data read
    intError = fWriteSPIdata8bits2( _hMLX90393_0, MLX90393_REG_SM | MLX90393_AXIS_ALL ); // single measurement all axis and temprature
    xEventGroupWaitBits ( eg, evtfMLX90393_ReadSensor_0, pdTRUE, pdTRUE, portMAX_DELAY );
    intError = fReadSPIdataXbits( _hMLX90393_0, MLX90393_REG_RM | MLX90393_AXIS_ALL, rx, 9 );

    /* rx[0] = status bit
       rx[1] & rx[2] = temprature
       rx[3] & rx[4] x
       rx[5] & rx[6] y
       rx[7] & rx[8] z
    */
    // Convert data to 16 bit
    int16_t xi, yi, zi;
    xi = (rx[3] << 8) | rx[4]; // shift MSB over to the left by 8 & with LSB
    yi = (rx[5] << 8) | rx[6];
    zi = (rx[7] << 8) | rx[8];
    // get gain being used and apply
    float xT = 0.0f, yT = 0.0f, zT = 0.0f;
    xT = (float)xi *  mlx90393_lsb_lookup[_gain][xRes][xyResoultion];
    yT = (float)yi * mlx90393_lsb_lookup[_gain][yRes][xyResoultion];
    zT = (float)zi * mlx90393_lsb_lookup[_gain][zRes][zResoultion];
    xMag.x = xT;
    xMag.y = yT;
    xMag.z = zT;
    xEventGroupSetBits( eg, evtTriggerMLX90393_1 ); // trigger the mlx90393-1
    xLastWakeTime = xTaskGetTickCount();
  }
  vTaskDelete(NULL);
}

You can see that the task void fMLX90393_0( void * pvParameters ) operates under a vTaskDelayUntil( &xLastWakeTime, xFrequency ); and, when completed, triggers an event, xEventGroupSetBits( eg, evtTriggerMLX90393_1 ); , to run the task fMLX90393_1( void * pvParameters ).

Not shown is the ESP32 SPI API code.

You will find that vTaskDelay( ); does not stop program execution. When a vTaskDelay( ) is encountered the task is paused and, under FreeRTOS, other tasks will run, based upon their priority settings or position in the task queue.

You will find this link quite useful: https://www.freertos.org/a00106.html

Thanks,

I'm afraid it's been a bit of learning by doing up until now.

I know this is not your issue but why is there code in the loop() when you are using freeRTOS?

But the loop is empty, (apart from the empty line)?

void loop() {

}

But I’ll definitely have a read up on what you’ve suggested, and get back to you if that’s ok

https://create.arduino.cc/projecthub/feilipu/using-freertos-multi-tasking-in-arduino-ebc3cc?f=1

Ians:
Thanks,

I’m afraid it’s been a bit of learning by doing up until now.

But the loop is empty, (apart from the empty line)?

void loop() {

}

My error.

The ESP32 has 4 user hardware timers.

Might I suggest using the ESP32 hardware timer instead of the freeRTOS software timer?

Here I declare a hardware timer:

/* create a hardware timer */
hw_timer_t * timer = NULL;

Here is a ISR for the hardware timer:

// timer ISR callback set at 1000X a second
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iTicCount++;
  xEventGroupSetBitsFromISR(eg, OneMilliSecondGroupBits, &xHigherPriorityTaskWoken);
  if ( (iTicCount % 2) == 0 )
  {
    if ( (xSemaphoreTakeFromISR(sema_ReceiveSerial_LIDAR, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
    {
      xEventGroupSetBitsFromISR(eg, evtReceiveSerial_LIDAR, &xHigherPriorityTaskWoken); // trigger every 2mS, if not already processing
    }
  }
  if ( (iTicCount % 3) == 0 )
  {
    if ( (xSemaphoreTakeFromISR(sema_HexaPodAdjustment, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
    {
      xEventGroupSetBitsFromISR(eg1, evtWalk_Forward, &xHigherPriorityTaskWoken);
    }
  }
  if ( iTicCount == OneK )
  {
    xEventGroupSetBitsFromISR(eg, OneSecondGroupBits, &xHigherPriorityTaskWoken); // trigger every 1X a second
    // reset counter to start again
    iTicCount = 0;
  }
  if ( !bLIDAR_OK )
  {
    xSemaphoreTakeFromISR ( sema_iLIDAR_Power_Reset, &xHigherPriorityTaskWoken );
    ++iLIDAR_Power_Reset;
    xSemaphoreGiveFromISR ( sema_iLIDAR_Power_Reset,  &xHigherPriorityTaskWoken);
    if ( iLIDAR_Power_Reset >= 4000 )
    {
      xEventGroupSetBitsFromISR(eg, evtfLIDAR_Power_On, &xHigherPriorityTaskWoken);
    }
  }
} // void IRAM_ATTR onTimer()

Here is my code from setup to configure and start a hardware timer:

 /* Use 4th timer of 4.
    1 tick 1/(80MHZ/80) = 1us set divider 80 and count up.
    Attach onTimer function to timer
    Set alarm to call timer ISR, every 1000uS and repeat / reset ISR (true) after each alarm
    Start an timer alarm
  */
  timer = timerBegin( TIMER_FOUR, TimerDivider, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, OneK, true);
  timerAlarmEnable(timer);

You can find, in the freeRTOS docs, that the freeRTOS software timers are not going to be as exact as using hardware timers.

You may have to do a bit of digging to find if #include <AccelStepper.h> is using a hardware timer, you, most likely, do not want to use the same timer to run the tasks.

I'm afraid it's a bit above me at the moment, I would like to get this code working as is for now and refine it when I have a working version.

Could you advise me to as what could be causing this error please.

no matching function for call to 'AccelStepper::moveTo(String&)'

Referring to here:

if (strcmp(topic, "azimuth") == 0) {
    stepper1.moveTo(messageTemp);
    stepper1.run();
    delay(5);
    Serial.print("moved to: ");
  }

Ians: I'm afraid it's a bit above me at the moment, I would like to get this code working as is for now and refine it when I have a working version.

Could you advise me to as what could be causing this error please.

no matching function for call to 'AccelStepper::moveTo(String&)'

What have you done to troubleshoot the error?

Idahowalker: What have you done to troubleshoot the error?

Well I first thought that "moveTo(messageTemp)" is being received as a string (String&)' so I created a function node to make sure the MQTT was sent as a number, with no effect.

Then I googled the whole error, this was the best response but a bit long winded with using an array which I didn't need. https://forum.arduino.cc/index.php?topic=334231.0

I then googled Arduino function and came to the conclusion that something else hasn't been declared, but I can't see what I'm afraid.

Have you confirmed that the library you are using has a method called moveTo(String&)?

Idahowalker: Have you confirmed that the library you are using has a method called moveTo(String&)?

Yes, I did in fact check what the function does (not mentioned in my last post). But I could not see whether or not it supported a string.

void AccelStepper::moveTo ( long absolute )

This, in fact, is my stumbling point. I know it shouldn't be read as a string but how can correct this.

To move a stepper to a position it can't be a string.

Ians: Yes, I did in fact check what the function does (not mentioned in my last post). But I could not see whether or not it supported a string.

void AccelStepper::moveTo ( long absolute )

This, in fact, is my stumbling point. I know it shouldn't be read as a string but how can correct this.

To move a stepper to a position it can't be a string.

Correct, you need to send a long to the moveTo ( ) function. If your long int value is in a string format then casting the string as a long int like this moveTo ( long(StringValue) ) should work.

I don't believe you can cast a string to a long. You can however use the toInt method on the string, which, despite its name, returns a long.