Stepper motor controlled by temperature sensor

Hi,

I have DS18B20 temperature sensor hooked up like this

and a bipolar stepper motor and A4988 , 1.8deg/step, 200 steps. Size Nema 17 hooked up like this. dir and step outputs are in pins 12 and 13.

I have downloaded onewire and Accelstepper stepper libraries and placed them in the arduino libraries folder.

The code I’m using to get the temperature reading from the DS18b20 sensor is working and I’m getting correct ambient temperature to show up in the serial monitor window.

Code looks like this:

#include <OneWire.h> 

int DS18S20_Pin = 2; //DS18S20 Signal pin on digital 2

//Temperature chip i/o
OneWire ds(DS18S20_Pin); // on digital pin 2

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

void loop(void) {
 float temperature = getTemp();
 Serial.println(temperature);
 
 delay(100); //just here to slow down the output so it is easier to read
 
}


float getTemp(){
 //returns the temperature from one DS18S20 in DEG Celsius

 byte data[12];
 byte addr[8];

 if ( !ds.search(addr)) {
   //no more sensors on chain, reset search
   ds.reset_search();
   return -1000;
 }

 if ( OneWire::crc8( addr, 7) != addr[7]) {
   Serial.println("CRC is not valid!");
   return -1000;
 }

 if ( addr[0] != 0x10 && addr[0] != 0x28) {
   Serial.print("Device is not recognized");
   return -1000;
 }

 ds.reset();
 ds.select(addr);
 ds.write(0x44,1); // start conversion, with parasite power on at the end

 byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE); // Read Scratchpad

 
 for (int i = 0; i < 9; i++) { // we need 9 bytes
  data[i] = ds.read();
 }
 
 ds.reset_search();
 
 byte MSB = data[1];
 byte LSB = data[0];

 float tempRead = ((MSB << 8) | LSB); //using two's compliment
 float TemperatureSum = tempRead / 16;
 
 return TemperatureSum;

I have also been able to get the stepper motor to spin in both directions with varying speed and accleration. The codes that I have tried are as follows:

//simple A4988 connection
//jumper reset and sleep together
//connect  VDD to Arduino 3.3v or 5v
//connect  GND to Arduino GND (GND near VDD)
//connect  1A and 1B to stepper coil 1
//connect 2A and 2B to stepper coil 2
//connect VMOT to power source (9v battery + term)
//connect GRD to power source (9v battery - term)


int stp = 13;  //connect pin 13 to step
int dir = 12;  // connect pin 12 to dir
int a = 0;     //  gen counter

void setup() 
{                
  pinMode(stp, OUTPUT);
  pinMode(dir, OUTPUT);       
}


void loop() 
{
  if (a <  200)  //sweep 200 step in dir 1
   {
    a++;
    digitalWrite(stp, HIGH);   
    delay(10);               
    digitalWrite(stp, LOW);  
    delay(10);              
   }
  else 
   {
    digitalWrite(dir, HIGH);
    a++;
    digitalWrite(stp, HIGH);  
    delay(10);               
    digitalWrite(stp, LOW);  
    delay(10);
    
    if (a>400)    //sweep 200 in dir 2
     {
      a = 0;
      digitalWrite(dir, LOW);
     }
    }
}

The other code looks like this:

#include <AccelStepper.h>

AccelStepper Stepper1(1,13,12); //use pin 12 and 13 for dir and step, 1 is the "external driver" mode (A4988)
int dir = 1; //used to switch direction

void setup() {
  Stepper1.setMaxSpeed(3000); //set max speed the motor will turn (steps/second)
  Stepper1.setAcceleration(13000); //set acceleration (steps/second^2)
}

void loop() {
  if(Stepper1.distanceToGo()==0){ //check if motor has already finished his last move
    Stepper1.move(1600*dir); //set next movement to 1600 steps (if dir is -1 it will move -1600 -> opposite direction)
    dir = dir*(-1); //negate dir to make the next movement go in opposite direction
    delay(1000); //wait 1 second
  }
  
  Stepper1.run(); //run the stepper. this has to be done over and over again to continously move the stepper
}

Now the fun part is that with my next to none experience in coding I’ve been trying to get the stepper to rotate one full cycle in a given direction when temperature reaches certain threshold temperature value. Then it should stop, wait for a new temperature and repeat the process.

I have been trying through trial and error to compile a working code by taking pieces of code from the example codes above. I have set certain threshold values where the motor should change direction and it’s working, but not quite how I expected. The motor keeps on stepping in one direction till it reaches the given temperature and never stops and it totally ignores the 200 pulses and speed value. I also realized that the motor is stepping as fast as the Ds18B20 is putting out temperature data. So if I set delay(1000) after serial.printIn(temperature) the motor steps 1step/1 sec. There is some sort of conflict here that I can’t get my head around. So can someone push me in the right direction so I can quit banging my head on the wall continuously? :slight_smile:

Here’s the code that I have compiled so far. It’s ugly by all standards for sure :roll_eyes:

#include <OneWire.h> 

int DS18S20_Pin = 2; //DS18S20 Signal pin on digital 2
int stepPin = 13; 
int dirPin = 12; 
int temp;
int tempMin = 25;
int tempMax = 25;


//Temperature chip i/o
OneWire ds(DS18S20_Pin);  // on digital pin 2


void setup(void) {
  Serial.begin(9600);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(DS18S20_Pin, INPUT);
  }

void loop(void) {
  float temperature = getTemp();
  Serial.println(temperature);
  delay(1000);

float temp = getTemp();    // get the temperature
   if(temp <  tempMin)    // if temp is lower than minimum temp
       digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
      // Makes 200 pulses for making one full cycle rotation
      for(int x = 0; x < 200; x++) 
        digitalWrite(stepPin,HIGH); 
        delayMicroseconds(2000); 
        digitalWrite(stepPin,LOW); 
        delayMicroseconds(2000);  
      
  if(temp > tempMax) 
    digitalWrite(dirPin,LOW); // Enables the motor to move in a particular direction
      // Makes 200 pulses for making one full cycle rotation
      for(int x = 0; x < 200; x++) 
        digitalWrite(stepPin,HIGH); 
        delayMicroseconds(2000); 
        digitalWrite(stepPin,LOW); 
        delayMicroseconds(2000); 

  }
 
float getTemp(){
  //returns the temperature from one DS18S20 in DEG Celsius

  byte data[12];
  byte addr[8];

  if ( !ds.search(addr)) {
      //no more sensors on chain, reset search
      ds.reset_search();
      return -1000;
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return -1000;
  }

  if ( addr[0] != 0x10 && addr[0] != 0x28) {
      Serial.print("Device is not recognized");
      return -1000;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1); // start conversion, with parasite power on at the end

  byte present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE); // Read Scratchpad

  
  for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = ds.read();
  }
  
  ds.reset_search();
  
  byte MSB = data[1];
  byte LSB = data[0];

  float tempRead = ((MSB << 8) | LSB); //using two's compliment
  float TemperatureSum = tempRead / 16;
  
  return TemperatureSum;
  
}

Thanks,
JP

If I understand what you are doing, you need to have a delay between times that you tell the motor what to do. That delay is going to depend on exactly what you are doing and how fast the temperature of the environment changes. One of those “real time” things where you need to determine what the response time is of your environment. If you are controlling a mixing valve on hot/cold water, that will be fairly fast while if you are looking for the temperature to change in a greenhouse as you open the vent, you may need a 10 or 20 minute time constant. You need to separate the two parts of what you are doing - the first is the temperature measurement and the second is controlling something that will affect that temperature. What I would consider doing for the temperature measurement is using some sort of averaging say the last 10 readings together (say a reading every 5 seconds or whatever is appropriate for your environment) and average them. That will give you a better reference to control against if there are things that can cause the temperature to fluctuate (say breezes in the case of a greenhouse vent for example). Check out “olympic average” for more information on how to do that part. Without knowing more about the environment you are trying to control, it is difficult to come up with better information - it all depends on the response time of the environment how fast you need to be doing things - real time for the rudder control of a super tanker is not the same as rudder control on a supersonic fighter as an example - the time constants are very different. You may even want to as you get into this, do some looking into what the call a PID controller (stands for Proportional, Integrating, Derivitive) - basically it looks at where you are currently, where you want to go, how fast things are changing and adjusts the controller appropriately.

Thanks for replying!

At this point I'm trying to keep this as simple as possible and add the averaging maybe later as I learn more about this stuff.

I'll write a rough rundown of what I'm trying to achieve. (the code I posted previously is merely a test so the values aren't final in it)

Stepper motor controls a valve. In the beginning the valve is half open. From this state the valve needs two full cycles(400 steps) counter-clockwise to be fully open and two full cycles clockwise to be closed. So 4 full cycles from fully closed to fully open and vice versa.

Temperature sensor measures temperature every 10 minutes. When the temperature is over +24C the stepper motor rotates one full cycle (200 steps) clockwise and stop. If the new temperature value is still over 24C the stepper motor rotates another full cycle clockwise and stop. After two full cycles the motor should now ignore the temperature value. This is because two full cycles is enough to close the valve. If it still tires to rotate it would only strain the valve and the motor itself.

If temperature is below +20C the stepper does the same as above but clockwise.

If temperature stays between +20C and +24C. Motor stays idle.

Any comments on my code? What changed should I make so that the motor rotates one full cycle, stops and waits for a new temperature info? Now it just ticks away one step at a time every 1sec and never stops. This is imho my codes biggest problem at the moment.

Thanks, JP

I suggest you make a function that rotates the motor N steps in the specified direction. Putting code into functions allows you to test the function separately and when it works just use it.

You already have a function to get the temperature.

You also need something - probably a time component - to prevent the motor moving more times than you want. The code in loop() should be like this pseudo code

void loop
   currentMillis = millis()
   getTemp 
   if currentMillis - lastMotorMoveMillis >= motorMoveInterval
       if temp is low
            moveMotor(200)
            lastMotorMoveMillis += motorMoveInterval
       if temp is high
            moveMotor(-200)
            lastMotorMoveMillis += motorMoveInterval

You should take ALL the delay()s out of your code and use millis() to manage the timing without blocking. See Several Things at a Time

...R

Thank you for your guidance. I'm off to youtube then for some coding lessons :) I'll chime in if I get stuck again.

JP

Jarsalla: Thank you for your guidance. I'm off to youtube then for some coding lessons :) I'll chime in if I get stuck again.

JP

Oh, please don't go there to learn! Learn to teach yourself from online research instead. You'll never regret it, once you get used to it.

Well I usually do both. Read bunch of forums and articles and then watch some youtube video lessons if I can find them. I did this with the temperature sensor and stepper motor.

Ok, I have made some progress. The motor now takes 200 steps and stops =>> delay(30000) and if the temperature is still above 21c or less than 17 c after 30s, it will take another 200 steps and stop. This is ok and what I want. I don’t need temperature info during this 30s delay.

I’m still working on with the maximum rounds the motor should make (400steps in one direction) regardless of the temperature value. millis() might be the key as Robin2 pointed out, but I’m still learning how to use it…

My current WIP code looks like this:

#include <OneWire.h> 
#include <AccelStepper.h>

int DS18S20_Pin = 2; //DS18S20 Signal pin on digital 2
double tempMin = 17;
double tempMax = 22;

AccelStepper Stepper1(1,13,12); //use pin 12 and 13 for dir and step, 1 is the "external driver" mode (A4988)
int dir = 1; //used to switch direction

//Temperature chip i/o
OneWire ds(DS18S20_Pin);  // on digital pin 2


void setup(void) {
  Serial.begin(9600);
  pinMode(DS18S20_Pin, INPUT);
  }

void Motor1()
{

  if(Stepper1.distanceToGo()==0){ //check if motor has already finished his last move
  Stepper1.setMaxSpeed(3000); //set max speed the motor will turn (steps/second),  doesn't work for some reason???
  Stepper1.setAcceleration(13000); //set acceleration (steps/second^2), doesn't work for some reason???
    Stepper1.move(200*dir); //set next movement to 200 steps (if dir is -1 it will move 200 -> opposite direction)
    
    delay(30000); //wait x seconds
  }
  
  Stepper1.run(); //run the stepper. this has to be done over and over again to continously move the stepper
}


void Motor2()
{

  if(Stepper1.distanceToGo()==0){ //check if motor has already finished his last move
     Stepper1.setMaxSpeed(3000); //set max speed the motor will turn (steps/second),  doesn't work for some reason???
  Stepper1.setAcceleration(13000); //set acceleration (steps/second^2), doesn't work for some reason???
    Stepper1.move(200*dir*-1); //set next movement to 200 steps (if dir is -1 it will move 200 -> opposite direction)
    
    delay(30000); //wait x seconds
  }
  
  Stepper1.run(); //run the stepper. this has to be done over and over again to continously move the stepper
}


void loop(void) {
   

  float temperature = getTemp(); // reads temperature, 
  Serial.println(temperature); // prints tempereture to serial monitor, it seems that this affects motor speed?? motor steps at the same space as the temperature is printed out 

 { if ( temperature <= tempMin )
{
  Motor1();
  }}


{if ( temperature >= tempMax )
{
  Motor2();
  }}
} 

float getTemp(){
  //returns the temperature from one DS18S20 in DEG Celsius

  byte data[12];
  byte addr[8];

  if ( !ds.search(addr)) {
      //no more sensors on chain, reset search
      ds.reset_search();
      return -1000;
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return -1000;
  }

  if ( addr[0] != 0x10 && addr[0] != 0x28) {
      Serial.print("Device is not recognized");
      return -1000;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1); // start conversion, with parasite power on at the end

  byte present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE); // Read Scratchpad

   
  for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = ds.read();
  }
  
  ds.reset_search();
  
  byte MSB = data[1];
  byte LSB = data[0];

  float tempRead = ((MSB << 8) | LSB); //using two's compliment
  float TemperatureSum = tempRead / 16;
  
  return TemperatureSum;
 

}