Do I need an interrupt?-2

Yes, thank you, very true. I was planning a directional change STOP after the basic stuff was done. Probably 100-250ms to start since the motor will need its ramp-down time before changing directions.

This what I have been working with, trying to integrate your ideas into.

/*
* Turn a bi-directional AC motor (cc) clockwise, (cw) counter-
* clockwise, and off with special characters sent over the
* serial connection with only one direction allowed on at a time. If 
* "cw" is commanded on, "cc" will be turned off prior to "cw" being enabled.
* Also, a timer function to allow either direction to be enabled for
* the time allotted and then be disabled as well as a current timer being reset and re-enabled if a direction change should be commanded prior to the first timer sequence ending. 
* timer being reset and re-enabled if a direction change should be 
* commanded prior to the first timer sequence ending. k

*/

int cw = 13;      // led is hooked up to this pin
int cc = 12;      // led is hooked up to this pin

char red = '1';  // ascii LED 1 on
char off = 'o';  // ascii led off
char yellow = '2';  // ascii LED 2 on

int cwState;
int ccState;
char nextChar;

void setup()
{
   Serial.begin(9600);
   
   pinMode(cw, OUTPUT);
   digitalWrite(cw, LOW);
   cwState = LOW;
   
   pinMode(cc, OUTPUT);
   digitalWrite(cc, LOW);
   ccState = LOW;
}

void loop()
{
  if (Serial.available())
    {   
       nextChar = Serial.read();  // read just one character
       if (nextChar == red)  // check for 'r'
       {
         cwState = HIGH;
         ccState = LOW;
         Serial.println("ledcw 1 On");
       }
       else if (nextChar == yellow)  // check for 'y'
       { 
         cwState = LOW;
         ccState = HIGH;
         Serial.println("ledcc 2 On");
       }
       else if (nextChar == off)  // check for 'o'
       {                 
         cwState = LOW;
         ccState = LOW;
         Serial.println("leds Off");
       }
       digitalWrite(cw, cwState);
       digitalWrite(cc, ccState);
    }
   
}

It’s been a slow day so I’ve been playing with the code I sent you yesterday. Here’s a modified version.

#define STOP     0
#define CCW          1
#define CW           2

#define N_MOTORS 2

// there are the values sent by the PC
#define MOTOR_A_STOP     '1'
#define MOTOR_A_CW          '2'
#define MOTOR_A_CCW          '3'
#define MOTOR_B_STOP     '4'
#define MOTOR_B_CW           '5'
#define MOTOR_B_CCW          '6'

char *action_strings [] = {"STOP", "CCW", "CW"};
char *command_strings [] = {
  "MOTOR_A_STOP", "MOTOR_A_CW", "MOTOR_A_CCW", "MOTOR_B_STOP", "MOTOR_B_CW", "MOTOR_B_CCW"};
 
void ControlMotor (byte motor, byte action);

struct motor {

      long     count;
      byte     pin1;
      byte     pin2;
      byte     action;

} ;

struct motor     motors[2];

void scanCounters (void) {
      // this gets called every 100mS
     // it decrements the motor's counter and if it reaches -1 it will call
     // ControlMotor with the motor's number and action (which at present is fixed at STOP)
     for (int i = 0; i < N_MOTORS; i++) {
          if (motors[i].count != -1) {
               motors[i].count--;
               if (motors[i].count == -1)
                    // when counter times out we call
                    // ControlMotor() with whatever action
                    // is in the motor's struct
                    ControlMotor (i, motors[i].action);
          }
     }
}

void ControlMotor (byte motor, byte action) {
        if (motor < 2 && action <= CW) {
          Serial.print ("Motor :");
          Serial.print (motor, HEX);
          Serial.print ("  Action:");
          Serial.println (action_strings [action]);
        }
          
     switch (motor) {

          case 0:
               switch (action) {
                    case STOP:
                         digitalWrite (motors[0].pin1, LOW);
                         digitalWrite (motors[0].pin2, LOW);
                         break;

                    case CW:
                         digitalWrite (motors[0].pin2, LOW);
                         digitalWrite (motors[0].pin1, HIGH);
                         break;

                    case CCW:
                         digitalWrite (motors[0].pin1, LOW);      // note we turn OFF before ON
                         digitalWrite (motors[0].pin2, HIGH);
                         break;

                    default:
                         digitalWrite (motors[0].pin1, LOW);     // stop motor if bad value
                         digitalWrite (motors[0].pin2, LOW);
               }
               break;

          case 1:
               switch (action) {
                    case STOP:
                         digitalWrite (motors[1].pin1, LOW);
                         digitalWrite (motors[1].pin2, LOW);
                         break;

                    case CW:
                         digitalWrite (motors[1].pin2, LOW);
                         digitalWrite (motors[1].pin1, HIGH);
                         break;

                    case CCW:
                         digitalWrite (motors[1].pin1, LOW);
                         digitalWrite (motors[1].pin2, HIGH);
                         break;

                    default:
                         digitalWrite (motors[1].pin1, LOW);
                         digitalWrite (motors[1].pin2, LOW);               }
               break;
     }
}

void setup () {

     // map logical motor pins to physical pins
     motors[0].pin1 = 14;  // use your pin numbers here
     motors[0].pin2 = 15;
     motors[1].pin1 = 16;
     motors[1].pin2 = 17;

     Serial.begin (115200);
        Serial.println ("\n\nEnter numbers 1-6");

     // init counters and IO pins
     for (int i = 0; i < N_MOTORS; i++) {
          motors[i].count = -1;
          motors[i].action = STOP;
          pinMode(motors[i].pin1, OUTPUT);
          pinMode(motors[i].pin2, OUTPUT);
          digitalWrite(motors[i].pin1, LOW);
          digitalWrite(motors[i].pin2, LOW);
     }
}

void loop () {
      if ((millis() % 100) == 0) scanCounters(); // scan counters 10x a second (100mS)
                                                    // potentially will skip a beat every now and then
        delay (1);  // so we don't do scanCounters() multiple times within the same mS 
        
     if (Serial.available () > 0) {
                   byte command = Serial.read ();
 
              if (command >= MOTOR_A_STOP && command <= MOTOR_B_CCW) {
                 Serial.print ("\nCommand=");
                 Serial.println (command_strings[command - '1']);
               }

                switch (command) {
                     case MOTOR_A_STOP:
                           motors[0].count = -1;
                                  ControlMotor (0, STOP);
                          break;

                    case MOTOR_A_CW:
                         motors[0].count = 200;  // 200 * 100mS = 20 secs
                         ControlMotor (0, CW);
                         break;

                    case MOTOR_A_CCW:
                         motors[0].count = 200;
                         ControlMotor (0, CCW);
                         break;

                    case MOTOR_B_STOP:
                         motors[1].count = -1;
                         ControlMotor (1, STOP);
                         break;

                    case MOTOR_B_CW:
                         motors[1].count = 100;
                         ControlMotor (1, CW);
                         break;

                    case MOTOR_B_CCW:
                         motors[1].count = 100;
                         ControlMotor (1, CCW);
                         break;

                     default:
                           ControlMotor (0, STOP);
                          ControlMotor (1, STOP);
                          Serial.println ("Bad command from PC");
            }
       }
}

I’ve tested it on my clone with four LEDs and it works as follows.

Enter number from 1-6 to stop motors A and B or turn them CW or CCW.

MOTOR_A_STOP ‘1’
MOTOR_A_CW ‘2’
MOTOR_A_CCW ‘3’
MOTOR_B_STOP ‘4’
MOTOR_B_CW ‘5’
MOTOR_B_CCW ‘6’

I’ve set the times for A at 20s and B at 10s.

Once started a motor will run for it’s alloted time then turn off.

So if you say press 2 (A turn CW) follwed immediately by 6 (B turn CCW) 10 seconds later you will see a message saying that B has stopped and after a further 10 seconds another message saying that A has stopped.

This isn’t all you wanted I think but it must be getting close enough for you to play with and is better than looking at a blank screen.


Rob

Rob

How did you know about the blank screen? Too true.

Many thanks for the code. I am trying to understand how it works. What I put together is sooo different from yours but still in the same language. I am a terrible newbie here.

I did modify the timer counts. I got tha much.

But, how you started is so different from how the books and tutorials say, I feel I am in way over my head.

No problems.

Note that this works for LEDs but as mentioned there will be issues when controlling a motor such as not slamming it from CW to CCW without a STOP and a delay etc. Testing for dumb commands could (should) be added at some point.

But, how you started is so different from how the books and tutorials say,

I prefer this sort of approach, it’s not as obvious to look at but way better IMO to a stack of “if this state and time < 20 then else that state” constructs.


Rob

Your way seems more streamlined and efficient to me.

Only, it is like seeing cursive while I am just learning to print. I do appreciate it though. I will now try ,in your code, how to add in a timer for motor ramp-down, whether changing direction or just stopping, before a new direction can happen.

BTW, what is IMO?

IMO, in my opinion.

Here’s a modified version, this one takes 3 seconds to stop a motor (try typing 2 then 1 (or 5 then 4) and wait for a few seconds). Still needs auto wind down and restart.

I’ve removed some code to to simplify the ControlMotor() func.

#define STOP    0
#define CCW         1
#define CW          2
#define STOPPED    3

#define STOPPING_TIME    30

#define N_MOTORS 2

// there are the values sent by the PC
#define MOTOR_A_STOP    '1'
#define MOTOR_A_CW         '2'
#define MOTOR_A_CCW         '3'
#define MOTOR_B_STOP    '4'
#define MOTOR_B_CW          '5'
#define MOTOR_B_CCW         '6'

char *action_strings [] = {"STOP", "CCW", "CW", "STOPPED"};
char *command_strings [] = {
  "MOTOR_A_STOP", "MOTOR_A_CW", "MOTOR_A_CCW", "MOTOR_B_STOP", "MOTOR_B_CW", "MOTOR_B_CCW"};

void ControlMotor (byte motor, byte action);

struct motor {

     long    count;
     byte    pin1;
     byte    pin2;
     byte    action;
     byte    state;

} ;

struct motor    motors[2];

void scanCounters (void) {
     // this gets called every 100mS
    // it decrements the motor's counter and if it reaches -1 it will call
    // ControlMotor with the motor's number and action (which at present is fixed at STOP)
    for (int i = 0; i < N_MOTORS; i++) {
         if (motors[i].count != -1) {
              motors[i].count--;
              if (motors[i].count == -1)
                    // when counter times out we call
                    // ControlMotor() with whatever action
                    // is in the motor's struct
                    ControlMotor (i, motors[i].action);
         }
    }
}

void ControlMotor (byte motor, byte action) {
         if (motor < 2 && action <= STOPPED) {
            Serial.print ("Motor :");
            Serial.print (motor, HEX);
            Serial.print ("  Action:");
            Serial.println (action_strings [action]);
        }
        motors[motor].state = action;
        switch (action) {
            case STOPPED:
                motors[motor].state = STOPPED;
                 break;

            case STOP:
                digitalWrite (motors[motor].pin1, LOW);
                digitalWrite (motors[motor].pin2, LOW);
                motors[motor].count = STOPPING_TIME;
                motors[motor].action = STOPPED;
                 break;

            case CW:
                digitalWrite (motors[motor].pin2, LOW);
                digitalWrite (motors[motor].pin1, HIGH);
                break;

            case CCW:
                digitalWrite (motors[motor].pin1, LOW);     // note we turn OFF before ON
                digitalWrite (motors[motor].pin2, HIGH);
                break;

            default:
                digitalWrite (motors[motor].pin1, LOW);    // stop motor if bad value
                digitalWrite (motors[motor].pin2, LOW);
          }
}

void setup () {

    // map logical motor pins to physical pins
    motors[0].pin1 = 14;  // use your pin numbers here
    motors[0].pin2 = 15;
    motors[1].pin1 = 16;
    motors[1].pin2 = 17;

    Serial.begin (115200);
        Serial.println ("\n\nEnter numbers 1-6");

    // init counters and IO pins
    for (int i = 0; i < N_MOTORS; i++) {
         motors[i].count = -1;
         motors[i].action = STOP;
         pinMode(motors[i].pin1, OUTPUT);
         pinMode(motors[i].pin2, OUTPUT);
         digitalWrite(motors[i].pin1, LOW);
         digitalWrite(motors[i].pin2, LOW);
    }
}

void loop () {
     if ((millis() % 100) == 0) scanCounters(); // scan counters 10x a second (100mS)
        delay (1);  // so we don't do scanCounters() multiple times within the same mS

    if (Serial.available () > 0) {
                  byte command = Serial.read ();

             if (command >= MOTOR_A_STOP && command <= MOTOR_B_CCW) {
                Serial.print ("\nCommand=");
                Serial.println (command_strings[command - '1']);
              }

                switch (command) {
                    case MOTOR_A_STOP:
                        ControlMotor (0, STOP);
                         break;

                    case MOTOR_A_CW:
                        motors[0].count = 20;  // 200 * 100mS = 20 secs
                        ControlMotor (0, CW);
                        break;

                    case MOTOR_A_CCW:
                        motors[0].count = 20;
                        ControlMotor (0, CCW);
                        break;

                    case MOTOR_B_STOP:
                        ControlMotor (1, STOP);
                        break;

                    case MOTOR_B_CW:
                        motors[1].count = 10;
                        ControlMotor (1, CW);
                        break;

                    case MOTOR_B_CCW:
                        motors[1].count = 102;
                        ControlMotor (1, CCW);
                        break;

                    default:
                          ControlMotor (0, STOP);
                         ControlMotor (1, STOP);
                         Serial.println ("Bad command from PC");
            }
      }
}

Rob

Ciao Rob,

I am playing with the code. I reset all timers to 10 and set the pins for my 13, 12, 11, and 10. Saved and uploaded.

I notice that when I send “2”, LED 1 comes on then stops then after ramp-down timer, is stopped. Then I send “3”, LED2 comes on then is stopped. I am not sure if the ramp-down timer is working, but, LED 2 is still on. In the serial monitor, I get no STOP issued. I try it with motor 1 as well and get an initial correct reaction, but, a bad one thereafter. I have been trying to understand how the code works to find the inconsistency, but, to no avail. I am unable to even ID the STOPPING timer amount. Or perhaps it is the same time as the directional timers.

This is monitor output with different commands. Any ideas?

Thanks,

Enter numbers 1-6


Command=MOTOR_A_CW

Motor :0  Action:CW

Motor :0  Action:STOP

Motor :0  Action:STOPPED


Command=MOTOR_A_CCW

Motor :0  Action:CCW

Motor :0  Action:STOPPED


Command=MOTOR_A_CW

Motor :0  Action:CW

Motor :0  Action:STOPPED


Command=MOTOR_A_STOP

Motor :0  Action:STOP


Command=MOTOR_B_CW

Motor :1  Action:CW

Motor :0  Action:STOPPED

Motor :1  Action:STOP


Command=MOTOR_B_CCW

Motor :1  Action:CCW

Motor :1  Action:STOPPED


Command=MOTOR_B_CW

Motor :1  Action:CW

Motor :1  Action:STOPPED


Command=MOTOR_B_STOP

Motor :1  Action:STOP

Here’s a modified version that should fix that problem

#define STOP    0
#define CCW         1
#define CW          2
#define STOPPED    3

#define STOPPING_TIME    30
#define DISABLE          -1

#define N_MOTORS 2

// there are the values sent by the PC
#define MOTOR_A_STOP    '1'
#define MOTOR_A_CW        '2'
#define MOTOR_A_CCW        '3'
#define MOTOR_B_STOP    '4'
#define MOTOR_B_CW        '5'
#define MOTOR_B_CCW        '6'

char *action_strings [] = {"STOP", "CCW", "CW", "STOPPED"};
char *command_strings [] = {
  "MOTOR_A_STOP", "MOTOR_A_CW", "MOTOR_A_CCW", "MOTOR_B_STOP", "MOTOR_B_CW", "MOTOR_B_CCW"};

void ControlMotor (byte motor, byte action);

// this is a data structure that holds information about a "motor"
struct motor {

     long    count;            // current timer value
     byte    pin1;
     byte    pin2;
     byte    next_action;    // the action that will happen when counter reaches -1
     byte    state;            // current state of the motor

} ;

struct motor    motors[2];

void scanCounters (void) {
    // this gets called every 100mS
    // it decrements the motor's counter and if it reaches -1 it will call
    // ControlMotor with the motor's number and action (which at present is fixed at STOP)
    for (int i = 0; i < N_MOTORS; i++) {
         if (motors[i].count != -1) {
              motors[i].count--;
              if (motors[i].count == -1)
                    // when counter times out we call
                    // ControlMotor() with whatever action
                    // is in the motor's struct
                    ControlMotor (i, motors[i].next_action);
         }
    }
}

void ControlMotor (byte motor, byte action) {
         if (motor < 2 && action <= STOPPED) {
            Serial.print ("Motor :");
            Serial.print (motor, HEX);
            Serial.print ("  Action:");
            Serial.println (action_strings [action]);
        }
        motors[motor].state = action;    // set the motor's current state

        switch (action) {
            case STOPPED:
               break;

            case STOP:
                digitalWrite (motors[motor].pin1, LOW);
                digitalWrite (motors[motor].pin2, LOW);
                motors[motor].count = STOPPING_TIME;
                motors[motor].next_action = STOPPED;
                break;

            case CW:
                  motors[motor].next_action = STOP;
                digitalWrite (motors[motor].pin2, LOW);
                digitalWrite (motors[motor].pin1, HIGH);
                break;

            case CCW:
                motors[motor].next_action = STOP;
                digitalWrite (motors[motor].pin1, LOW);     // note we turn OFF before ON
                digitalWrite (motors[motor].pin2, HIGH);
                break;

            default:
                digitalWrite (motors[motor].pin1, LOW);    // stop motor if bad value
                digitalWrite (motors[motor].pin2, LOW);
          }
}

void setup () {

    // map logical motor pins to physical pins
    motors[0].pin1 = 14;  // use your pin numbers here
    motors[0].pin2 = 15;
    motors[1].pin1 = 16;
    motors[1].pin2 = 17;

    Serial.begin (115200);
        Serial.println ("\n\nEnter numbers 1-6");

    // init counters and IO pins
    for (int i = 0; i < N_MOTORS; i++) {
         motors[i].count = -1;
         motors[i].next_action = STOP;
         pinMode(motors[i].pin1, OUTPUT);
         pinMode(motors[i].pin2, OUTPUT);
         digitalWrite(motors[i].pin1, LOW);
         digitalWrite(motors[i].pin2, LOW);
    }
}

void loop () {
     if ((millis() % 100) == 0) scanCounters(); // scan counters 10x a second (100mS)
        delay (1);  // so we don't do scanCounters() multiple times within the same mS

    if (Serial.available () > 0) {
        byte command = Serial.read ();

        if (command >= MOTOR_A_STOP && command <= MOTOR_B_CCW) {
            Serial.print ("\nCommand=");
            Serial.println (command_strings[command - '1']);
        }

        switch (command) {
            case MOTOR_A_STOP:
                ControlMotor (0, STOP);
                 break;

            case MOTOR_A_CW:
                motors[0].count = 40;
                ControlMotor (0, CW);
                break;

            case MOTOR_A_CCW:
                motors[0].count = 40;
                ControlMotor (0, CCW);
                break;

            case MOTOR_B_STOP:
                ControlMotor (1, STOP);
                break;

            case MOTOR_B_CW:
                motors[1].count = 10;
                ControlMotor (1, CW);
                break;

            case MOTOR_B_CCW:
                motors[1].count = 102;
                ControlMotor (1, CCW);
                break;

            default:
                ControlMotor (0, STOP);
                ControlMotor (1, STOP);
                Serial.println ("Bad command from PC");
        }
    }
}

I’ll have a think about the CW-to-CCW issue and how to best implement that.

I’ve also added a couple of comments that might help understand the code.


Rob

Rob

Yes! It works very nice. I am trying to dissect the code by pieces while comparing to Arduinos’ commands pages to place things appropriately.

I wonder, though, if it is a matter of reading current state of both motor pins and upon finding the opposite than commanded pin HIGH, then engaging the ramp-down timer.

ie. if motor 0 is sent a HIGH command, check for state of pins. If opposite pin from which is commanded HIGH is currently HIGH pin, then STOP, and STOPPING timer, then send commanded pin HIGH.

Actually, the ramp-down timer would only be needed in this case but wouldn’t hurt to have every time.

You are quite good at this. I am grateful for your help. The extra notes are helping as well.

Ciao

You are quite good at this.

Not that good, I'm sure there are plenty of real experts here who will probably cringe at my bad code :)


Rob

Here’s the latest version (attached), this does just about everything I think. There is a bug in that a STOP command waits until the motor times out which isn’t that clever because it will stop then anyway.

Lot more comments this time, but also I’ve had to add a “queue” construct which has made it a bit more complicated.

Remove the // from the first #define to see debug messages.

Sorry if it’s got too complicated, there may be a much simpler method but that’s what I came up with. If you need any lines explained as always just ask.


Rob

motor_sketch.c (7.27 KB)

It seems to work fine. I send 2 and 0,1 goes HIGH, then STOP, then times out STOPPING. Then if I send 3 during 2, it STOPs 2, times out STOPPING 2, then runs 3 high, then STOP, then STOPPING. I added the STOP down on the motor 1 area in the code and it seems to work the same.

I think it is time to mock up the relays and motors for complete testing. I'll be using a TI amplifier for the relays.

It'll probably take me a few days to get it set up (still have family and work).

BTW, yes, it does seem a bit more complicated than what I had thought it to be. But, then again, what basis do I have to work from? :P

Thank you once again. I'll send feedback upon full testing.

ciao