Millis() help, turning a stepper motor

Hello, to start this is my first project. I have a Planetary LEGO set 42179. I am using an esp32 and a stepper motor to turn the model in semi-realtime. I also am using a TM1637 to display the time.

I am using 2 different codes spliced together. I was originally using the delay() function but I have since learned that that will not work. Currently everything is running in order but I want everything timed independently of each other.

I want 3 things to happen

  • update the time every 1 second(from the original code I am using for the clock/time functions)
  • turn the motor 113 steps every 4 hours
  • turn the motor 4 steps every 3 days. This is to calibrate the system due to decimals, it gets it close enough at the end if the year, if not it is off bay about 0.2 days. If this part is too much and it is much easier I can delete this if there is a better way to make the first two work.

what is the best way to setup the millis() function? I am having trouble with the setup. Currently I have the times set in shorter interval test mode, my final numbers are in the notes above the commands.

Thank you in advance. Here is the code:

#include <WiFi.h>
#include "time.h"
#include <Stepper.h>

const char* ntpServer1 = "time.google.com";
const char* time_zone = "CST 6";

const uint8_t CLK_CYCLE_DELAY = 100;// us
const uint8_t CLK_PIN = D2;
const uint8_t DIO_PIN = D3;
const uint8_t NUM_DIGITS = 4;


// # steps for full 360-degree rotation, change to fit your motor
int stepsPerRevolution = 2048;

// set a speed for the stepper motor
int rpm = 10;

// initialize stepper library on pins 8 - 11
// pin order IN1, IN3, IN2, IN4
Stepper myStepper (stepsPerRevolution, D11, D9, D10, D8);

const char* ssid = "ATT5up9t9G";
const char* password = "2v#yfd3?4qg7";

// LED segment patterns.
const uint8_t NUM_PATTERNS = 10;
const uint8_t PATTERNS[NUM_PATTERNS] = {
  0x3F, // 0
  0x06, // 1
  0x5B, // 2
  0x4F, // 3
  0x66, // 4
  0x6D, // 5
  0x7D, // 6
  0x07, // 7
  0x7F, // 8
  0x6F, // 9
};

void setDataPin(uint8_t _bit){
  digitalWrite(DIO_PIN,_bit);
}
void setClkPint(uint8_t _bit){
  digitalWrite(CLK_PIN,_bit);
}

void sleep(int sleep){
  delayMicroseconds(sleep);
}

void start() {
    setDataPin(0);
    sleep(CLK_CYCLE_DELAY);
    setClkPint(0);
    sleep(CLK_CYCLE_DELAY);
}

void stop() {
    setDataPin(0);
    sleep(CLK_CYCLE_DELAY);
    setClkPint(1);
    sleep(CLK_CYCLE_DELAY);
    setDataPin(1);
}
    

void writeByte(uint8_t _byte){
    for(uint8_t i=0;i<8;i++){
      setDataPin(_byte >> i & 0x01) ;
      sleep(CLK_CYCLE_DELAY);
      setClkPint(1);
      sleep(CLK_CYCLE_DELAY);
      setClkPint(0);
      sleep(CLK_CYCLE_DELAY);
    }
      
    setClkPint(0);
    sleep(CLK_CYCLE_DELAY);
    setClkPint(1);
    sleep(CLK_CYCLE_DELAY);
    setClkPint(0);
    sleep(CLK_CYCLE_DELAY);
}

void writeDataCmd(uint8_t cmd){
    start();
    writeByte(cmd);
    stop();
}

void sendByteStream(uint8_t *bytes,uint8_t len) {
    writeDataCmd(0x40);
    start();
    for(uint8_t i=0;i<len;i++){
        writeByte(bytes[i]);
    }
    stop();
}

void updateTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("No time available (yet)");
    return;
  }
  char printBuffer[10];
  int strLen = sprintf(printBuffer, "%02d%02d",
                        timeinfo.tm_hour, timeinfo.tm_min);
  uint8_t tokenBuffer[NUM_DIGITS+1] = {0};
  tokenBuffer[0]= {0xC0}; // first byte will be the register address
  uint8_t showColon = timeinfo.tm_sec % 2;
  for(int i=0;i<strLen && i<NUM_DIGITS ;i++){
      uint8_t digit = ((uint8_t)printBuffer[i])-48;
      tokenBuffer[i+1] = PATTERNS[digit];
      if(showColon == 1){
        tokenBuffer[i+1] = 0x80 | tokenBuffer[i+1];
      }
  }
  sendByteStream(tokenBuffer,NUM_DIGITS+1);
  sprintf(printBuffer, "%02d:%02d:%02d",
                        timeinfo.tm_hour, timeinfo.tm_min,timeinfo.tm_sec);
  Serial.println(printBuffer);
}

void setup() {
  Serial.begin(115200);
  configTzTime(time_zone, ntpServer1);
  pinMode(CLK_PIN,OUTPUT);
  pinMode(DIO_PIN,OUTPUT);
  stop();
  // switch on the display
  uint8_t displayControlCmd[1]={0x8F};// display on with full brightness
  sendByteStream(displayControlCmd,1);
  //connect to WiFi
  Serial.printf("Connecting to access point");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  Serial.println(" CONNECTED");

myStepper.setSpeed(rpm);


}
       

void loop() {
  delay(1000);
  updateTime();

// 113 steps every 4 hours(14400000)
  myStepper.step(113);
  delay(5000);
// calibration steps. 4 steps every 3.01 days(260064000)
  myStepper.step(4);
  delay(42500);

}

Show these two in separate code blocks.

unsigned long timerONESECOND, timeoutONESECOND = 1000; // milliseconds
unsigned long timerFOURHOURS, timeoutFOURHOURS = 4 * 60 * 60 * 1000UL; // hours * min/hr * sec/min * ms/sec
unsigned long timerTHREEDAYS, timeoutTHREEDAYS = 3 * 24 * 60 * 60 * 1000UL; // days * hr/day * min/hr * sec/min * ms/sec

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

void loop() {
  if (millis() - timerONESECOND > timeoutONESECOND) {
    timerONESECOND = millis();
    Serial.println("One second");
    // the code that updates the time
  }

  if (millis() - timerFOURHOURS > timeoutFOURHOURS) {
    timerFOURHOURS = millis();
    Serial.println("Four hors");
    // the code that move the motor 113 steps
  }

  if (millis() - timerTHREEDAYS > timeoutTHREEDAYS) {
    timerTHREEDAYS = millis();
    Serial.println("Three days");
    // the code that moves the motor 4 steps
  }
}

Why not average the 113 steps over four hours? 240/113 steps per minute (about 2.1)
Same with 4 steps over three days.

Thank you!

As for your comment, no specific reason but I would prefer it to just move every little bit instead of constant movement. Also assuming that periodic movement might be a little easier on the motor.

A stepper motor holding its place is working just as much as a moving stepper motor.

With an esp32 with wifi and an NTP connection, you should use the NTP time to track seconds accurately rather than the unsynchonized and needlessly precise millis().

Maybe something like this less-blocking and completely untested snippet:

void loop() {
  static time_t lastAdvanceTime = 0, lastCorrectionTime=0;
  time_t nowtime;
  time(&nowtime);
  if(nowtime - lastAdvanceTime >= 14400 ){
    lastAdvanceTime = nowtime;
    myStepper.step(113);
  }
  if(nowtime - lastCorrectionTime >= 260064 ){
    lastCorrectionTime = nowtime;
    myStepper.step(4);
  }
  delay(1000);
  updateTime();
}

There is a fundamental difference between delaying by using he function delay and how delaying is done using millis()

It is NOT a simple replacement.

delay does just block everything.

Using non-blocking timing based on function millis() works very very different.
You do a very fast repeated comparising "how much time has passed by since a reference-point in time.

Here is a tutorial that explains how non-blocking timing based on millis() works

For the display of course you can use your own functions that drive the display.
But there are libraries for TM1637-driven displays which make life easier

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.