0

I am having some trouble with controlling a stepper motor using a IOIO board. I started with the HelloIOIOPower example and added my code. The program will run and turn on/off the LED when the toggle button is pressed but if a button is pressed that changes the value of motor_command in the UI thread the motor control signals do not get set and the program appears to hang. It does not crash but the LED control is lost.

I tried using synchronized on the switch statement in the run method to have the entire switch statement evaluated before allowing a change to motor_control in the UI thread.

I believe the correct way to implement this is to bind to the IOIO thread but I have been unsuccessful in getting that to work correctly.

I would like to be able to change the value of "motor_control" to create a control command to the IOIO thread to move the stepper motor. Once the command is executed the IOIO thread sets "motor_control" to zero which is a do noting or hold state.

Running the IOIO in a service would be preferred but is not required.

I plan to have this app running 24/7 with intermittent commands sent to the stepper motor. what is the correct method for controlling a stepper motor via a IOIO when only intermittent commands will be sent to the IOIO?

this is my first attempt at Java/ Android/ IOIO please feel free to point out all errors, I would love to learn where I can improve....

Thanks...

` package com.medo.testmotor4;

import ioio.lib.api.DigitalOutput;
import ioio.lib.api.IOIO;
import ioio.lib.api.IOIOFactory;
import ioio.lib.api.exception.ConnectionLostException;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;


public class TestMotor4 extends Activity {
/** The text displayed at the top of the page. */
private TextView title_;
/** The toggle button used to control the LED. */
private ToggleButton button_;

Button full;
Button half;
Button step16th;
Button cup;
Button dish;
TextView cup_count;
TextView random_num;
int traycount = 0;  // try cup position
int motor_command = 0;    //motor command index value for switch statement




/** The thread that interacts with the IOIO. */
private IOIOThread ioio_thread_;

/**
 * Called when the activity is first created. Here we normally initialize
 * our GUI.
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test_motor4);
    title_ = (TextView) findViewById(R.id.title);
    button_ = (ToggleButton) findViewById(R.id.button);

// Assign value to Text Views:
    cup_count = (TextView)findViewById(R.id.tv_cup_count);
    cup_count.setText(Integer.toString(traycount));

    random_num = (TextView)findViewById(R.id.tv_random_number);
    random_num.setText("no number");


    // Set up buttons
    dish = (Button) findViewById(R.id.btn_dish);
    dish.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View arg0) {
            // call method that moves motor one full step
            motor_command = 4;      
            Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
        }
    });

    full = (Button) findViewById(R.id.btn_fullstep);
    full.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View arg0) {
            // call method that moves motor one full step
            motor_command = 1;  
            Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
        }
    });

    cup = (Button) findViewById(R.id.btn_cup);
    cup.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View arg0) {
            // call method that moves motor one full step
            traycount ++;
            if (traycount % 3 == 0 )    
                motor_command = 3;      // every 3rd tray cup move 54 1/8 steps 
                                        // all others move 53 1/8 steps
            else motor_command = 2; 
            Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
            cup_count.setText(Integer.toString(traycount));         
        }
    });

    half = (Button) findViewById(R.id.btnhalfstep);
    half.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View arg0) {
            // call method that moves motor one half step
            motor_command = 3;
                Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();

        }
    });


}  //onCreate

/**
 * Called when the application is resumed (also when first started). Here is
 * where we'll create our IOIO thread.
 */
@Override
protected void onResume() {
    super.onResume();
    ioio_thread_ = new IOIOThread();
    ioio_thread_.start();
}

/**
 * Called when the application is paused. We want to disconnect with the
 * IOIO at this point, as the user is no longer interacting with our
 * application.
 */
@Override
protected void onPause() {
    super.onPause();
    ioio_thread_.abort();
    try {
        ioio_thread_.join();
    } catch (InterruptedException e) {
    }
}

/**
 * This is the thread that does the IOIO interaction.
 * 
 * It first creates a IOIO instance and wait for a connection to be
 * established. Then it starts doing the main work of opening the LED pin
 * and constantly updating it to match the toggle button's state.
 * 
 * Whenever a connection drops, it tries to reconnect, unless this is a
 * result of abort().
 */
class IOIOThread extends Thread {
    private IOIO ioio_;
    private boolean abort_ = false;

    DigitalOutput led;

    private DigitalOutput dir;
    private DigitalOutput stp;
    private DigitalOutput slp_,rst_, ms3, ms2, ms1,en_;
    private DigitalOutput led_;
    private DigitalOutput dish;
    private static final int STEP_WAIT = 5;


    /** Thread body. */
    @Override
    public void run() {
        super.run();
        while (true) {
            synchronized (this) {
                if (abort_) {
                    break;
                }
                ioio_ = IOIOFactory.create();
            }
            try {

                setText(R.string. wait_ioio);
                ioio_.waitForConnect();
                setText(R.string.ioio_connected);

                //IOIO pin setup:
                //DigitalOutput led = ioio_.openDigitalOutput(0, true);
                rst_ = ioio_.openDigitalOutput(13, true);   // /RESET   
                ms3  = ioio_.openDigitalOutput(14, true);   //  ms3  set step size     L L L full step
                ms2  = ioio_.openDigitalOutput(15, true);   //  ms2
                ms1  = ioio_.openDigitalOutput(16, true);   //  ms1                    H H H 1/16 step
                en_  = ioio_.openDigitalOutput(17, false);  // /enable 
                dir  = ioio_.openDigitalOutput(18, false);  //  DIRECTION -  set initial direction of motor rotation
                stp  = ioio_.openDigitalOutput(19, false);  //  STEP
                slp_ = ioio_.openDigitalOutput(20, true);       // /SLEEP  - 
                led  = ioio_.openDigitalOutput(IOIO.LED_PIN);
                dish = ioio_.openDigitalOutput(9, false);   // Dish - linear actuator activate signal   
                Log.d ("IOIOLooper setup", "Complete");
                int test = 0;


                Toast.makeText(TestMotor4.this, "Starting while loop",Toast.LENGTH_SHORT).show();
                while (true) {

                    led.write(!button_.isChecked());


                    switch(motor_command){

                        case 0:  // hold - do nothing for 5 sec.
                            this.timeout(100);

                            break;

                        case 1:   //full step on motor
                            full();
                            Log.d("STEPPER-FULL","Complete");
                            motor_command = 0;                  
                            break;

                        case 2:  // move motor 1 cup - (53) 1/8 steps
                            cup(53);
                            motor_command = 0; 
                            Log.d("STEPPER-CUP - 53","Complete" );
                            break;

                        case 3: // move motor 1 cup (54) 1/8 steps
                            cup(54);
                            motor_command = 0; 
                            Log.d("STEPPER-CUP - 54","Complete" );
                            break;

                        case 4: // dump scoop dish
                            dish.write(true);
                            //Toast.makeText(TestMotor4.this, "Dish set HIGH: ",Toast.LENGTH_SHORT).show();
                            this.timeout(5000);
                            dish.write(false);
                            //Toast.makeText(TestMotor4.this, "Dish set LOW " + motor_command,Toast.LENGTH_SHORT).show();
                            motor_command = 0;
                            break;

                    } // switch
                    sleep(10);

                }
            } catch (ConnectionLostException e) {
                setText(R.string.connectionLost);

            } catch (Exception e) {
                Log.e("TestMotor4", "Unexpected exception caught", e);
                ioio_.disconnect();
                break;
            } finally {
                    if (ioio_ != null) {
                        try {
                            ioio_.waitForDisconnect();
                        } catch (InterruptedException e) {
                        }
                    }
                    synchronized (this) {
                        ioio_ = null;
                    }
            }
        } // while(true)
    }// Run 


    /**
     * Abort the connection.
     * 
     * This is a little tricky synchronization-wise: we need to be handle
     * the case of abortion happening before the IOIO instance is created or
     * during its creation.
     */
    synchronized public void abort() {
        abort_ = true;
        if (ioio_ != null) {
            ioio_.disconnect();
        }
    }// abort

    /**
     * Set the text line on top of the screen.
     * 
     * @param id
     *            The string ID of the message to present.
     */
    private void setText(final int id) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                title_.setText(getString(id));
            }
        });
    } //setText

    public void full ()throws ConnectionLostException{
        // move motor 1 full step  (1.8 degrees)
        // requires stp to be LOW before entering method  - leaves stp LOW on exit of method
        try{
            en_.write(false);  // enable MC outputs
        // configure for full step  ( L L L)
            ms1.write(false);
            ms2.write(false);               
            ms3.write(false);
            Log.d("Set FULL", "L L L");

        //send step signal with LOW To HIGH transition
            stp.write(true);
            timeout(STEP_WAIT);
            stp.write(false);
            this.timeout(STEP_WAIT); 
        }catch(ConnectionLostException e) {
            //enableUi(false);
            Toast.makeText(TestMotor4.this, "IOIO Connection Lost ",Toast.LENGTH_LONG).show();
            throw e;
        }
    } // full

    public void cup(int num) throws ConnectionLostException{
        try{                    
            // set step size to 1/8 step
            ms1.write(true);
            ms2.write(true);
            ms3.write(false);
            Log.d("Set 1/8 step size ", "H H L");
            // set up wait for signals to stabilize
            this.timeout(STEP_WAIT); 


            //move motor 53 steps
            for (int i = 0; i<num; i++){
                stp.write(true);
                this.timeout(STEP_WAIT); 
                stp.write(false);
                this.timeout(STEP_WAIT); 
            }

        }catch (ConnectionLostException e){
            Toast.makeText(TestMotor4.this, "IOIO Connection Lost ",Toast.LENGTH_LONG).show();
            throw e;
        }
    }   //cup   
    private void timeout(int ms) {
        try {
        sleep(ms);
        } catch (InterruptedException e) {
        // Do nothing...
        }
        }

}  //IOIOThread

}`

  • Hey, I've been working with a IOIO for a few months now so I hope I can help you out a bit. I'm guessing that you're having sort of the same trouble I had with getting values from the UI and passing them to the IOIO thread. If your program appears to hang and you lose LED control it sounds like you're getting forcefully disconnected from the IOIO, but I'm not sure. Is anything interesting popping up in the LogCat when you do this? – Tyler Mar 07 '13 at 07:20
  • @Tyler I have progressed further by rewriting this code using HelloIOIO as a template instead of HelloIOIOpower. I am currently having the trouble you described of passing values into the IOIOthread from other active activities. Unfortunately I have the V1 board and no Bluetooth capability on the Android device I am currently working on so I do not get LogCat data when connected to the IOIO board. I can send you the current code if interested in taking a look as I feel it is to much to post here. – user2076649 Mar 12 '13 at 06:08
  • Okay cool, I can help you with that then. Give me a bit more background on how this is structured. You have an activity running the UI, and then another activity based on HelloIOIO? Or you just turned HelloIOIO into a runnable and are running it as a thread off of your main activity? In the mean time, if you haven't checked out the IOIO Google group yet you should definitely do that. Most of the time the creator of IOIO himself, Ytai, will help people out. He's really helpful and is somehow on there all the time. – Tyler Mar 12 '13 at 22:32

0 Answers0