stepper motor project for blind people

Hello everyone, I’m pretty new in Arduino coding and this year at high school me and friends of me had to do a project for finals exams.
We choose to do a simple device, which will display braille caracters for blind people from a computer-entered text.
Basically, there is a wheel directly putted in the stepper motor (200 steps, unipolar) with 40 faces, and on every face there is a braille caracter.
My really big problem is I wish to put a detector (magnetic detector) to be sure that when I power on the device the stepper motor turn until it is on the “e” letter (reference letter, so the motor display good caracters). Then it turns to selected letters depend on what words you enter in serial console.
The motor turns, but don’t stop when it pass in front of detector. I’m in pain, I give you the code :

#include <Stepper.h>
#define STEPS 200
int oldR1 = 1; // variable de l'ancienne position de la roue 1
int nxtR1 = 0; // variable de la future position de la roue 1
int pos = 0;   // variable de position
int motIN = 0; // Sauvegarde l'octet entrant par le port série
int debut = 0;

int pin1 = 2;
int pin2 = 3;
int pin3 = 4;
int pin4 = 5;
int ledpin = 10;
int capt = 13;

 Stepper step1(200 , pin1, pin2, pin3, pin4);

void setup() // fonction setup - début de l'exécution du programme
{
  Serial.begin(9600);
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
  pinMode(pin3, OUTPUT);
  pinMode(pin4, OUTPUT);
  pinMode(capt, INPUT);
  step1.setSpeed(100); 
  pinMode(ledpin, OUTPUT);
  digitalWrite(ledpin, HIGH);
  
  
}



void loop() // fonction loop - est exécutée en boucle 
               // une fois que la fonction setup a été exécutée
{
  if (digitalRead(capt == LOW))  
 {delay(100);
   while (digitalRead(capt == LOW))
 { step1.step(1);
 delay(100);
 }
 }
 
 if (digitalRead(capt == HIGH))
 { 
 
   debut == 1;
 }
 
   
     while (debut == 1)
  {
    if (Serial.available() > 0)
    {
      motIN == Serial.read();

    if (motIN == 101) // Si le charactere entrant est la lettre e 
  {
    pos = 1;
    nxtR1 = (oldR1 - pos)*5;
    
    // dit ce que vous obtenez
		Serial.print("J'ai recu : ");
	        Serial.println(motIN, DEC);
 
    Serial.print("nbr de pas a effectuer : ");
    Serial.println(nxtR1, DEC);
    step1.step(nxtR1);
  
    
    oldR1 = pos; // sauvegarde la position du moteur 1 après avoir tourné
      
    
  }
    else if (motIN == 102) // Si le charactere entrant est la lettre f
    {
    pos = 10;
    nxtR1 = (oldR1 - pos)*5;
    
    // dit ce que vous obtenez
		Serial.print("J'ai recu : ");
	        Serial.println(motIN, DEC);
    
    Serial.print("nbr de pas a effectuer : ");
    Serial.println(nxtR1, DEC);
    step1.step(nxtR1);
 
    
    oldR1 = pos;
    
  }
   
// HERE THERE IS ALL OTHER LETTERS

 else if (motIN == 48)// caractère 0
 {
       pos = 21;
    nxtR1 = (oldR1 - pos)*5;
    
    Serial.print("nbr de pas a effectuer : ");
    Serial.println(nxtR1, DEC);
    step1.step(nxtR1);
 
    
    oldR1 = pos;
    
 }
 delay(1000);
  
          
}
  }
  
}

I’m french, so please be indulgent with my poor english :wink:

Thank you by advance for any help !!

What kind of magnetic detector are you using?

The usual method is to use a photo-interrupter for this. A small tab on the back of the wheel breaks an infra-red beam, which indicates when it's in the "home" position. For many many years printers have used this method to position the print head, and it's worked so well and so simply that there has been no need to replace it with anything more complex over the decades that it's been used.

Photo-interrupters are cheap, common, and only take a couple of resistors to wire them up to an Arduino.

Thanks for fast anwer !

Well I'm using an ILS detector (looks like a rice grain) :

http://www.google.fr/imgres?imgurl=http://www.teaser.fr/~mzirnheld/tc/ils_prin.gif&imgrefurl=http://www.teaser.fr/~mzirnheld/tc/compteu2.htm&h=160&w=354&sz=2&tbnid=I4698yujUivm9M:&tbnh=52&tbnw=115&zoom=1&usg=__O7oSa_QsYduc0SRMQlUYSSPRqdw=&docid=h0hSjJz7r8Hn_M&sa=X&ei=jWO0UZmgIIu0hAf97oHgBQ&ved=0CD0Q9QEwAw&dur=2249

And your solution seems to be the best at all, but here in my poor high school they don't have such materials (except if we order it but it is too late for now... unfortunately)

Maybe I need to put resistor with my ILS to make a current, but it is necessary if I use the "digitalWrite(capt, HIGH)" intruction in setup ? (capt is my ILS variable)

Ah yes - that "sensor" is what we call a "reed switch".

It works just like a push-button with the magnet being the button.

You will need a pull-up (or pull-down depending on how it's wired) resistor just like you would with a button.

Also, you have to ensure that it's oriented right so the magnet pulls the mobile portion of the switch in the right direction.

Okay so it's a reed switch thanks !

Well, admitting connections are well done (the project isn't with me, so I can't take any pictures), do you personally think there is a problem in my code ??

And I'm sorry but, what do you mean by "pull-down/up resistor ? I wired the sensor in numeric inputs, with a 100 ohms resistor... And I guess the sensor is well placed (will have to check for sure)

The reed switch operates like a button.

When it's "pressed" (i.e., when the magnet is close by) it provides whichever signal level it is connected to on the digital pin (HIGH, if it is connected between the digital pin and +5V, or LOW if it's between the digital pin and GND). When it's "not pressed" (i.e., when the magnet is not near) it doesn't provide any signal to the digital pin. This means that the digital pin is in an unknown state (it is known as "floating") and it could be read as either HIGH or LOW depending on the weather, the phase of the moon, or the type of wine you had with dinner, etc. In order to be able to distinguish between the signal from the button / reed switch and "no signal" it is necessary to force the digital pin to read a "default" state - one that is the opposite to that which the reed switch provides. This is usually done by connecting a resistor between the digital pin and the opposite power rail to the reed switch. If the reed switch is connected between the digital pin and GND, then you connect a resistor between the digital pin and +5V, so when the reed switch is open the digital pin is "pulled up" to 5V. The opposite is if the reed switch is connected between the digital pin and +5V, in which case you connect the resistor between the digital pin and GND, and the pin is "pulled down" to GND when the reed switch is open. 10K is a usual value to use. Alternatively, if you're not in a noise (EMI noise, that is), and you're not running wires long distances you can use the internal pull-up resistors inside the Atmel chip (use pinMode(capt, INPUT_PULLUP); to enable), but this only works if the reed switch is connected between the IO pin and GND.

As for your code - the messy formatting makes it very hard to follow. Please re-format it using the IDE's auto-format facility (see the Tools menu IIRC) and re-post your code so it's readable.

If you can't imagine what should happen if a condition is not true, such as the switch not being pressed, it is perfectly acceptable to do nothing. Doing something, like delay(), is often worse than doing nothing.

In your case, doing nothing is a far better idea. When the stepper gets to the correct letter, it will stop immediately. It won't take forever to get there.

The Stepper::step() function is a blocking function. That is, it does not return until the stepper has stepped. It is not necessary to delay() before telling the motor to step again.

Alright, not any of my teachers could ever help me that way !!

I think you just solved my problem, and sorry about the messy in my program here is cleaner version (I translated coms also) :

#include <Stepper.h>
#define STEPS 200
int oldR1 = 1; // old wheel position
int nxtR1 = 0; // future wheel position
int pos = 0;   // position var
int motIN = 0; // income bytes on serial
int debut = 0; // var for starting program

int pin1 = 2;  // All these var defines the pins used for motor card (easy step 1000)
int pin2 = 3;
int pin3 = 4;
int pin4 = 5;
int ledpin = 10;
int capt = 13;

Stepper step1(200 , pin1, pin2, pin3, pin4);

void setup() // fonction setup - début de l'exécution du programme
{
  Serial.begin(9600);
  pinMode(pin1, OUTPUT);
  pinMode(pin2, OUTPUT);
  pinMode(pin3, OUTPUT);
  pinMode(pin4, OUTPUT);
  pinMode(capt, INPUT_PULLUP);
  step1.setSpeed(100); 
  pinMode(ledpin, OUTPUT);
  digitalWrite(ledpin, HIGH);


}



void loop() // fonction loop - est exécutée en boucle 
// une fois que la fonction setup a été exécutée
{
  if (digitalRead(capt == LOW))  // if sensor isn't in front of wheel
  {
    delay(100);
    while (digitalRead(capt == LOW))
    { 
      step1.step(1);           // turn the stepper until the sensor is "HIGH"
      delay(100);
    }
  }

  if (digitalRead(capt == HIGH)) // if sensor is already HIGH, then put the starting var to 1
  { 

    debut == 1;
  }


  while (debut == 1)               // Here is the true program
  {
    if (Serial.available() > 0)     // Read incoming letters
    {
      motIN == Serial.read();

      if (motIN == 101) // Si le charactere entrant est la lettre e 
      {
        pos = 1;                          //define position of letter "e" (referential letter)
        nxtR1 = (oldR1 - pos)*5;           // there is 40 letters on the wheel and 200 steps so 5 steps per letter, define the number of steps the stepper had to do
  
        // dit ce que vous obtenez
        Serial.print("J'ai recu : ");
        Serial.println(motIN, DEC);

        Serial.print("nbr de pas a effectuer : ");
        Serial.println(nxtR1, DEC);
        step1.step(nxtR1);


        oldR1 = pos; // save the position for future letter


      }
      else if (motIN == 102) // Si le charactere entrant est la lettre f
      {
        pos = 10;
        nxtR1 = (oldR1 - pos)*5;

        // dit ce que vous obtenez
        Serial.print("J'ai recu : ");
        Serial.println(motIN, DEC);

        Serial.print("nbr de pas a effectuer : ");
        Serial.println(nxtR1, DEC);
        step1.step(nxtR1);


        oldR1 = pos;

      }

                //HERE THERE IS AN ELSE IF FOR EVERY CARACTERS 

      else if (motIN == 48)// caractère 0
      {
        pos = 21;
        nxtR1 = (oldR1 - pos)*5;

        Serial.print("nbr de pas a effectuer : ");
        Serial.println(nxtR1, DEC);
        step1.step(nxtR1);


        oldR1 = pos;

      }
      delay(1000);


    }
  }

}

Hope this could help understanding it

Ok PaulS, I putted this delay because I was wondering why my sensor wasn't working, so I thought it was like the pressure button, our teachers told us it was usefull to put delay with pressure buttons to be sure the microchip is recording only one state... I'll delete it thank you

if (digitalRead(capt == HIGH)) // if sensor is already HIGH, then put the starting var to 1
  { 

    debut == 1;
  }

The statement in the if block body is comparing the value of debut to 1, and discarding the result. Seems a useless thing to do.

= is for assignment. == is for equality comparison.

Get rid of the delays, without delay.

Get rid of some of the empty lines. A blank line between functions is good. More than one isn't. Blank lines before the closing } of a function are annoying.

      if (motIN == 101) // Si le charactere entrant est la lettre e

Why not make the code reflect the comment?

      if (motIN == 'e')

Then, you don't need the comment? (If I guessed correctly at it's meaning.)

        nxtR1 = (oldR1 - pos)*5;           // there is 40 letters on the wheel and 200 steps so 5 steps per letter, define the number of steps the stepper had to do

The comment is inappropriate here. There should be a # define statement at the top of the program that looks like

// there are 40 letters on the wheel
// and 200 steps per revolution so 5
// steps per letter
#define SPL 5

Then, this statement should be:

        nxtR1 = (oldR1 - pos) * SPL;  // define the number of steps the stepper has to do

That whole series of if statements should be replaced by a switch/case construct, with 90% of the code put in a function:

switch(motIN)
{
   case 'e':
      moveToPostion(1);
      break;
   case 'f':
      moveToPosition(10);
      break;
}

There is no need to replicate the common code 40 times. Put in a function, and call that function 40 times.

GOD, I knew I could use switch/case but didn’t know how to in this configuration… You just get the monkey off my back !

Thanks for this great help, now I finally know what I have to do.

To resume all I need to do is making a function called “movetoPosition” :

void movetoposition(int pos)
{
  nxtR1 = (oldR1 - pos) * SPL;
  step1.step(nxtR1);
}

And make a switch/case structure instead of all those else if… And of course deleting delays, and blank lines.

To resume all I need to do is making a function called “movetoPosition” :

I prefer moveToPostion, where each word, except the first, is capitolized.

void movetoposition(int pos)

That’s not movetoPostion…

All of this:

        nxtR1 = (oldR1 - pos)*SPL; // define the number of steps the stepper had to do
  
        // dit ce que vous obtenez
        Serial.print("J'ai recu : ");
        Serial.println(motIN, DEC);

        Serial.print("nbr de pas a effectuer : ");
        Serial.println(nxtR1, DEC);
        step1.step(nxtR1);

        oldR1 = pos; // save the position for future letter

should be in the function. Updating the value in oldR1 is essential.

But, yes, you’ve got the hang of it now, I think.

Of course, of course, my excitation made me write bad things we'll say :grin:

Last question : Is the "detection" part of the program is good ? I mean this part :

if (digitalRead(capt == LOW))  // if sensor isn't in front of wheel
  {
    while (digitalRead(capt == LOW))
    { 
      step1.step(1);           // turn the stepper until the sensor is "HIGH"
    }
  }

If it is, I think you guys just solved my problem and saved me from an unworking device to present this week...

Thanks a lot !

Last question : Is the “detection” part of the program is good ? I mean this part :

If it is, I think you guys just solved my problem and saved me from an unworking device to present this week…

It looks OK. The proof comes when you actually run the code, though.

Sometimes, there is nothing wrong with going to a review with a non-working device, if the purpose of the review is to get help on the parts that don’t work, as well as a review of the overall design.

Of course, a working device is better. Good luck.

if (digitalRead(capt == LOW))

Are you sure about that?

Absolutely not, you’re right it is supposed to be

if (digitalRead(capt == HIGH))

because of the

pinMode (capt, INPUT_UP);

Thanks to notify me I could forgot…

I will try tomorrow then

SiZiX: Absolutely not, you're right it is supposed to be

if (digitalRead(capt == HIGH))

because of the

pinMode (capt, INPUT_UP);

Thanks to notify me I could forgot...

I will try tomorrow then

Almost...

It should be

if (digitalRead(capt) == HIGH)

and

pinMode (capt, INPUT_PULLUP);

And because of the INPUT_PULLUP, HIGH is "not at home position", and LOW is "at home position", so check your usage of LOW / HIGH.

if (digitalRead(capt == HIGH))

Are you really sure ????

digitalRead(X) reads the value of pin X and returns a HIGH/LOW value. What pin does your code read ?

There is a 'capt' variable, defined to be pin 13, don't worry I made it thanks :)

capt == HIGH (where capt is 13, and HIGH is 1) will always be false, and false is 0. So, what you have there is digitalRead(0).

Make sure you have the capt and only the capt in the brackets for the digitalRead function, and compare the results of that function with HIGH.