stepper motor thermometer

Sorry in advance for any silly questions here. This is my first project. I am trying to use a stepper motor to move a pointer for a thermometer.

The stepper motor is a 28BYJ-48 and is being driven by a ULN2003 driver board. There is a small pointer attached to the motor shaft.

I think the project will be broken out into 2 main parts. Setting up the temp sensor, and using that data to move the motor to the correct position. I'm waiting on parts for my temp sensor, so I'm trying to work out the motor part while manually entering temperature readings into the serial monitor. Eventually I plan on having the temperature read every 5 min or so and the serial monitor part will go away.

I have a switch to home the motor on startup. That part of the sketch seems to work OK. Power on and the motor turns back to zero and closes the switch, then backs off slowly until the switch opens again. That works OK.

From there, I want to read a temp (say 70 deg) and have the motor move the pointer to that position. That parts seems to work as well. But I need a way so that when the temp is read again, it doesn't just add another 70 deg (or whatever the temp is) to the existing position. So I created some variables called "current_temp", "new_temp", and "temp_change". My thinking was:
new_temp-current_temp=temp_change
then use that temp-change value to calculate how many steps to move the motor. Then I would need to take the "new_temp" value and store it as the "current_temp" value before doing the whole loop again. But when I tried to set that up in the sketch...it doesn't work. I think I'm doing something very basically wrong.

//Libraries:
  #include <Stepper.h>

//Hardware Declarations:
  #define home_switch 2

    
//Stepper Declarations:  
  const int revsteps = 2051;  // change this to fit the number of steps per revolution
  const int RPM = 10;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
  Stepper stepper1(revsteps, 8, 10, 9, 11);
  int steps;
  

//Temp Declarations:
  int current_temp;
  int new_temp;
  int temp_change;



void setup() {
  pinMode(home_switch, INPUT_PULLUP);
  stepper1.setSpeed(5);

   Serial.begin(9600);
   Serial.println("-----HOMING-----");

//-----HOME STEPPER-----
  while(digitalRead(home_switch)){
    stepper1.step(-1);
    delay(10);
  }
  while(!digitalRead(home_switch)){
    stepper1.step(1);
    delay(10);
  }
  current_temp=5;
  stepper1.setSpeed(RPM);

  Serial.println("<<Waiting for Temp>>");
 }

void loop() {  

//later replace this function with actual temp sensor input
  if (Serial.available())   
  {
    new_temp=Serial.parseInt();
    temp_change=new_temp-current_temp;
    steps=map(temp_change,0,100, 0, 630);  //Converting temp to number of steps
    stepper1.step(steps); //Move stepper.
   }

  stepperpoweroff();
  
  new_temp=current_temp;
  
}

void stepperpoweroff() {
  digitalWrite(8, LOW);
  digitalWrite(10, LOW);
  digitalWrite(9, LOW);
  digitalWrite(11, LOW);
}

I noticed that the stepper would get very hot when sitting holding position. The pointer I'm using is very light and the motor wouldn't move while waiting for the next cylce, so I have a function to power off the motor after each movement.

Another odd thing...my thermometer doesn't home to 0 degrees...when homed it goes to 5 deg. It's an odd thing...but you can see that's what I'm trying to do after the homing sequence with "current_temp=5;" That also doesn't seem to work.

I think I'm probably making some silly beginner mistake(s) here. Anyone have any ideas?

I noticed that the stepper would get very hot when sitting holding position.

The stepper motor will heat up any time it is powered on and most are designed to run hot. "Holding" is irrelevant. Please post a wiring diagram and state the motor and power supply voltage.

Since you know where zero is after startup, it is straightforward to count steps and keep track of the current motor position. All moves should be computed relative to the current position and after the move, the current position should be updated.

Post a link to where you downloaded the stepper library. It is possible that the library will keep track of the current position for you, and allow moves to absolute positions.

jshepard77:
But I need a way so that when the temp is read again, it doesn't just add another 70 deg (or whatever the temp is) to the existing position.

You need a variable that holds the value of the temperature that the motor displays. Then the motor code will only refer to that.

I noticed that the stepper would get very hot when sitting holding position.

My 28BYJ-48 motor never gets hot - only warm - when powered with 5v.

...R

Thanks for the replies!

I'll post a crude diagram of how it is hooked up. I am using an external power supply board. The power board came with a kit from Amazon...I think it's a MB-102 power supply board. That is run with a 9V battery.

When I say the stepper was getting hot...it may not be what someone would consider very hot. It was pretty warm to the touch. I did go through a new 9V battery pretty quickly though. I just added the function to turn off the pins to the motor to save power. In the final product there isn't any force that would cause the motor to turn, so it seemed worthwhile to do.

The homing switch is just connected to ground and pin 2 on the arduino. So the wiring is pretty simple I think.

So for the variable that holds the temp until the next read cycle...that's what I was trying to do. But in my sketch it's not working. I was calling that stored temp "current_temp". I'm trying to read a new temp (called "new_temp"), then subtract the new temp from the current temp to get a variable called "temp_change". Then I am trying to convert that temp change to steps, and have the motor turn that amount. After all that, it store the "new_temp" value to "current_temp" and wait for the next temp reading.

It makes sense in my head...but it's not working in reality. Any ideas on where the issue is? I figure I'm making a very basic mistake in how I'm handling the temp values in the sketch. This is my first project, and I don't have any coding experience...so it's like a new language to me.

We STRONGLY recommend that beginners work their way through some of the introductory tutorials that come with Arduino -- learn to read a button, blink an LED without using delay, read a sensor or a voltage. Do that to also learn the language and special features of the Arduino.

Otherwise, expect endless frustration.

Another word of advice: if you are asked for information on this forum, it is to your advantage to supply that information. For example, I requested a link to the web site where you downloaded or learned about the library Stepper.h. There are several and you are probably not using the one you have correctly.

Image from Reply #3 so we don't have to download it. See this Simple Image Posting Guide

...R

Sorry, but Fritzing diagrams are too easy to misunderstand and we can't read the pin labels. Just make a simple pencil drawing and post a photo of that.

From Reply #3 ...

I'm trying to read a new temp (called "new_temp"), then subtract the new temp from the current temp to get a variable called "temp_change". Then I am trying to convert that temp change to steps, and have the motor turn that amount.

That's broadly the way to go but I would approach it slightly differently which may make the problem easier to handle.

Rather than calculate the steps as part of the temperature calculations I would have code in my stepper motor function which reads the current temperature, figures out how many steps that represents (perhaps using the map() function) and then compare that step value to the current step value.

...R

jremington:
Sorry, forgot to mention that I don't recall downloading the stepper.h library. I think it was already there when I downloaded the IDE onto my computer. If I go to the Library Manager, the info it shows is:
"Stepper
Built-in by Arduino Version 1.1.3 INSTALLED..." Is that the info you are looking for?

So, I bought a arduino kit on Amazon, and worked my way through the tutorials. Everything seemed OK with those, LED's, switches, LCD display, etc. I think what I'm missing is a tutorial on the basics of the language. I haven't found much that is useful for that.

The language for Arduino is standard C/C++, for which there are countless tutorials on line.

The Arduino also has many built in functions that are individually documented on the Arduino Reference pages.

The default Stepper.h does not have absolute moves, so you will have to keep track of the current stepper motor shaft position in the code, and work out the relative moves from that.

Robin,

Thanks for fixing that image. Sorry about that. I'll write out a pencil schematic tonight.

So, I thought I was doing what you are suggesting already in the code:

if (Serial.available())   
  {
    new_temp=Serial.parseInt();
    temp_change=new_temp-current_temp;
    steps=map(temp_change,0,100, 0, 630);  //Converting temp to number of steps
    stepper1.step(steps); //Move stepper.
   }

  stepperpoweroff();
 
  new_temp=current_temp;

I thought that this would take in the new temp from the serial monitor (will replace with actual temp sensor input later), calculate the temp change, map that to calculate the number of steps, then move the stepper. Then the last line I thought was taking that new temp and storing it as the old temp.

The stepper moves fine, and points to the temp to I want on the first input. The problem is that then it adds the next temp input, instead of calculating the change and using that.

FIRST, convert the temperature to absolute shaft angle/position in steps.

THEN calculate the difference between the current motor shaft position and the desired motor shaft position, and move that many steps. Otherwise position errors will creep in.

jremington:
FIRST, convert the temperature to absolute shaft angle/position in steps.

THEN calculate the difference between the current motor shaft position and the desired motor shaft position, and move that many steps. Otherwise position errors will creep in.

That's what I was trying to say in Reply #6

Also (from Reply #9) I don't like this style of code

if (Serial.available())   
  {
    new_temp=Serial.parseInt();
    temp_change=new_temp-current_temp;
    steps=map(temp_change,0,100, 0, 630);  //Converting temp to number of steps
    stepper1.step(steps); //Move stepper.
   }

where reading serial data and moving the stepper motor are mixed up.

I suggest that you separate the two tasks as then you can test them separately and you don't run the risk of strange things happening if some unexpected serial data arrives. Something like this pseudo code

void loop() {
   readSerial();
   updatePointer();
}

void readSerial() {
   new_temp=Serial.parseInt();
}

void updatePointer() {
   newPointerPosition = convert new_temp to absolute step position
   pointerChangeSteps = newPointerPosition - currentPointerPosition;
   move motor by pointerChangeSteps
   currentPointerPosition = newPointerPosition
}

...R

And BTW in the original sketch you had:

  new_temp=current_temp;

which is both assigning in the wrong direction, and in the wrong place (you should only update the current_temp
after a new temperature comes in).

When doing the difference calculation in terms of steps the same principle applies.

You should have called the variable "previous_temp" not "current_temp" really, less confusing.

I would have used the AccelStepper library and its moveTo() method, completely avoiding this whole issue:

  stepper.moveTo (map (new_temp,0,100, 0, 630)) ;

Much more readable I think.

Thanks for the feedback and help everyone. I'll jump on these suggestions tonight.

I had started off using accelstepper, but since I didn't need any acceleration really I thought maybe it wasn't a great solution. I had read some other posts where if the motor was only going to move a small amount then accelstepper wasn't a good idea. Since what I think I'm doing is so simple, it seemed just a simple "move X steps" each time was better.

Thanks again everyone!

OK, so I tried to implement some of the suggestions... Here's what I did.

  1. I renamed some of the variables to be more clear.
  2. I used Robin's suggestion of breaking the loop portion out into individual functions.
  3. I ditched the map function to calculate steps. It's a pretty easy conversion...6.4 steps per degree of temp on the gauge. So now I just take the temp*6.4-newPointerPosition.
  4. Take the newPointerPosition and subtract the prevPointerPosition to get change in steps
  5. Move the stepper that number of steps
  6. Store the new position as the prev position.

What happens when I run this is that after homing (which works fine), I enter a temp in the serial. The pointer moves to the correct place on the thermometer, but then moves back to the starting point. It doesn't stay there.

So if I enter "100" into the serial monitor, the pointer goes to 100 on the dial...then moves back to 0. Then if I enter "50", it moves to 50, then back to 0.

I don't see why it would do this. I don't see where it's getting the instruction to go back to zero.

Here's the updated code:

//Libraries:
  #include <Stepper.h>

//Hardware Declarations:
  #define home_switch 2

//Stepper Declarations:  
  const int revsteps = 2051;  // change this to fit the number of steps per revolution
  const int RPM = 10;         // Adjustable range of 28BYJ-48 stepper is 0~17 rpm
  Stepper stepper1(revsteps, 8, 10, 9, 11);

//Temp Declarations:
  int temp;  //new temp reading
  int newPointerPosition;  //position that the pointer needs to move to
  int prevPointerPosition;  //existing temp pointer location
  int pointerChangeSteps;  //steps to move the stepper



void setup() {
  pinMode(home_switch, INPUT_PULLUP);
  stepper1.setSpeed(5);

   Serial.begin(9600);
   Serial.println("-----HOMING-----");

//-----HOME STEPPER-----
  while(digitalRead(home_switch)){
    stepper1.step(-1);
    delay(10);
  }
  while(!digitalRead(home_switch)){
    stepper1.step(1);
    delay(10);
  }
  //prevPointerPosition=5*6.4;
  stepper1.setSpeed(RPM);

  Serial.println("<<Waiting for Temp>>");
 }

void loop() {  
  readSerial(); 
  updatePointer();
  stepperPowerOff();
}

void readSerial() {
   temp=Serial.parseInt();
}

void updatePointer() {
   newPointerPosition = temp*6.4;
   pointerChangeSteps = newPointerPosition - prevPointerPosition;
   stepper1.step(pointerChangeSteps);
   prevPointerPosition = newPointerPosition;
}

void stepperPowerOff() {
  digitalWrite(8, LOW);
  digitalWrite(10, LOW);
  digitalWrite(9, LOW);
  digitalWrite(11, LOW);
}

Now you should begin to see the value of the separate functions.

I suspect the problem is that the variable temp is reverting to 0 when there is nothing sent to Serial. You can test this by temporarily adding the line temp = 75; like this (so it ignores the value from parseInt()

void readSerial() {
   temp=Serial.parseInt();
   temp = 75;
}

Assuming my guess is correct you need code in your readSerial() function to deal with invalid values. Something like this may work

void readSerial() {
   int newVal = Serial.parseInt();
   if (newVal > 0) {
      temp = newVal;
   }
}

If that does not work have a look at have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

Separately ...
This won't work as you expect

newPointerPosition = temp * 6.4;

because all the variables are integers which means that the effective multiplier will be 6, not 6.4

If you need decimal precision then the variables need to be defined as float rather than int but I suspect it would be close enough using integers with this code

newPointerPosition = temp * 64 / 100;

...R

:slight_smile: Yes, that was it. No input from the serial monitor must have been interpreted as a 0.

Thanks for the tip on that int variable type. It's probably close enough for what I'm doing...but I'll change it to a float variable anyway to be more correct.

Thanks again to everyone. Sorry for the very beginner question.

jshepard77:
Sorry for the very beginner question.

Nothing to apologise for. Beginners are very welcome. Now you are less of a beginner.

...R