User:Mike Barnkob/Projects/Liquid handling robot

From OpenWetWare

< User:Mike Barnkob(Difference between revisions)
Jump to: navigation, search
Current revision (15:48, 30 April 2010) (view source)
(Liquid handling robot)
 
(42 intermediate revisions not shown.)
Line 1: Line 1:
=Liquid handling robot=
=Liquid handling robot=
-
=Introduction=
+
[[Image:LEGO Robot version 4.jpg|300px|thumb|right|The current LEGO liquid handling robot]]
-
=Background=
+
The goal of this project is to create an open-source liquid handling robot by using LEGO Mindstorm.
 +
 
 +
The current project uses two Mindstorm NTX's, a 200μL micropipet, a couple of aluminum beams and a great deal of LEGO bricks. It's programmed using ROBOTC.
 +
 
 +
There are a number of limitations with the current design; the robot has very wobbly up/down movements as can be seen on the videos below; also the area available to work in is very small; the robot does not error check the volumes it pipets. Still it can be used to simple experiments and liquid handling.
 +
 
 +
==Documentation==
 +
 
 +
The finale documentation is not yet ready.
 +
 
 +
==Specifications==
 +
 
 +
The table below outlines the specifications of the machine.
 +
 
 +
{| {{table}}
 +
| align="center" style="background:#f0f0f0;"|''''''
 +
| align="center" style="background:#f0f0f0;"|'''Metric'''
 +
|-
 +
| Model||
 +
|-
 +
| Technology|| LEGO Mindstorm NTX 2.0 and ROBOTC
 +
|-
 +
| Price of all materials||
 +
|-
 +
| Size || 420 mm (Width) x 375 mm (Depth) x 500 mm (Height)
 +
|-
 +
| Weight ||
 +
|-
 +
| Area of movement||85 mm (Width) x 200 mm (Depth) x 62 mm (Height)
 +
|-
 +
| Speed||
 +
|-
 +
| Accuracy||
 +
|-
 +
|
 +
|}
==Ressources==
==Ressources==
 +
 +
LEGO
 +
* [http://www.peeron.com Peeron - LEGO sets part and inventory lists]
Software
Software
Line 22: Line 60:
* [http://carrot.whitman.edu/Robots/PDF/Functions.pdf Creating own functions in RobotC] By Albert W. Schueller.
* [http://carrot.whitman.edu/Robots/PDF/Functions.pdf Creating own functions in RobotC] By Albert W. Schueller.
* [http://www.robotc.net/support/nxt/MindstormsWebHelp/index.htm Robot C Functions help]
* [http://www.robotc.net/support/nxt/MindstormsWebHelp/index.htm Robot C Functions help]
-
* [http://see.stanford.edu/see/lecturelist.aspx?coll=86cc8662-f6e4-43c3-a1be-b30d1d179743 Introduction to robotics] at Stanford Engineering.  
+
* [http://see.stanford.edu/see/lecturelist.aspx?coll=86cc8662-f6e4-43c3-a1be-b30d1d179743 Introduction to robotics] at Stanford Engineering.
=Ideas=
=Ideas=
Line 196: Line 234:
 +
}
 +
</pre>
 +
 +
'''Tuesday''': Horizontal control: did a test at 40% motor power, with 50 movements back and forth. The bay slides a bit to the right, 0,2 cm after 50 turns. Lateral control: gravity is playing games and the movement changes alot over time. Redid the pipet-holding and added larger wheels. The first testing seems good.
 +
 +
'''Wednesday''': Added a touch sensor to the lateral movements. After 50 runs it seem to "slide", but solved this by adding a touch-sensor, that makes sure the movement is reset after each movement (ie. moves to the same position).
 +
 +
<pre>
 +
//MAIN TASK
 +
task main () {
 +
 +
  int i;
 +
  for ( i=0; i<=10; i++) {
 +
 +
    //Down
 +
    nMotorEncoder [motor_B] = 0;
 +
    LateralControl(-55, -100, 140, false);
 +
 +
    //Up
 +
    nMotorEncoder [motor_B] = 0;
 +
    LateralControl(55, 100, 0, true);
 +
 +
    //Reset - go up until the sensor is not pressed anymore
 +
    while( !(SensorValue(sensor_1) == 0) ) {
 +
      motor[motor_B]=55;
 +
      wait1Msec (1);
 +
    } 
 +
   
 +
  }
 +
}
 +
</pre>
 +
 +
 +
<html>
 +
<object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/Dv-0IIxN1yg&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Dv-0IIxN1yg&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>
 +
</html>
 +
 +
 +
'''Thursday''': Worked on the forward and backward movement, which seems to be working fine. No sliding was observed after 10 runs. Used the following code:
 +
 +
<pre>
 +
#pragma config(Motor,  motorA,          WallE_A,      tmotorNormal, PIDControl, encoder)
 +
#pragma config(Motor,  motorB,          WallE_B,      tmotorNormal, PIDControl, encoder)
 +
//*!!Code automatically generated by 'ROBOTC' configuration wizard              !!*//
 +
 +
//Forward and backward control
 +
void ForwardControl (int Fmotor, int Fencode, int Fpause) {
 +
 +
  //Sync'ing motors A and B
 +
  nSyncedMotors = synchAB ;
 +
  nSyncedTurnRatio = +100;
 +
 
 +
  //Power up
 +
  while( !(nMotorEncoder[WallE_A]==Fencode) ) {
 +
      motor[WallE_A]=Fmotor;
 +
      nxtDisplayClearTextLine(5);
 +
      nxtDisplayClearTextLine(6);
 +
 +
      nxtDisplayString (5,"%d", nMotorEncoder[WallE_A]);
 +
      nxtDisplayString (6,"%d, %d", Fencode, Fmotor);
 +
      wait1Msec (1);
 +
 +
  }
 +
 +
  motor [WallE_A] = 0;
 +
 +
  //End pause if any
 +
  wait10Msec(Fpause);
 +
}
 +
</pre>
 +
 +
<html>
 +
<object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/Xhi2GBqtNMM&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Xhi2GBqtNMM&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>
 +
</html>
 +
 +
Naming conventions: I'm current using two NTX's with the names WallE and R2D2. The motors will be called WallE_A, WallE_B, WallE_C, the sensors WallE_1, WallE_2, ect.
 +
 +
Tested bluetooth with the following two small programs:
 +
 +
BT send test 1 (25-03-10).c
 +
<pre>
 +
 +
//MAIN TASK
 +
task main () {
 +
 +
 
 +
  //Sending BT message - WallE is set up as master, R2D2 as slave.
 +
 +
  int i , x2 , x3;
 +
 +
  x2 = 0; // dummy
 +
  x3 = 0; // dummy
 +
 +
  for ( i=0; i<=10; i++) {
 +
      sendMessageWithParm (i ,x2 ,x3 );
 +
      wait1Msec (2000); // wait one -tenth of a second
 +
  }
 +
 
 +
}
 +
</pre>
 +
 +
BT receive test 1 (25-03-10).c
 +
<pre>
 +
 +
 +
//MAIN TASK
 +
task main () {
 +
 
 +
  while ( true ) {
 +
 +
    if ( bQueuedMsgAvailable () ) {
 +
     
 +
      eraseDisplay ();
 +
      nxtDisplayCenteredTextLine (3,"%d",
 +
      messageParm [0]);
 +
      ClearMessage ();
 +
    }
 +
 +
    wait1Msec (500);
 +
  }
 +
 +
 
 +
}
 +
 +
</pre>
 +
 +
'''Saturday''': Worked on improving the bluetooth connection.
 +
 +
==Week 3==
 +
'''Monday''': Continued working on the bluetooth connection, which is now finished. Tweaked the different functions that move the robot, so that the distances fit. Did a simple test with liquid handling, 200 ul.
 +
 +
Tasks: Plan and do a advanced experiment where water from 50 wells are moved to 50 other wells.
 +
 +
<html>
 +
<object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/IOQqZ7yG2VQ&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/IOQqZ7yG2VQ&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>
 +
</html>
 +
 +
'''Thuesday''': Build loading bays and pipet-tip removal bay. Built resetting mechanism but since the cables are not long enough, the robot has no reset for the back-forward movement and the dispensing. These two movements are very stable, but will have to look into making longer cables.
 +
 +
Discussed experiment with Martin. Decided to drop 50 well experiment for now and instead focus on emulating real experiment. 800ul, 5ul and 5ul liquid has to be loaded on small glass plate.
 +
 +
'''Wednesday''': Continued working on experiment. Made improvements to the master-slave code. Made a reset-mechanism for the back-forward movement. The space in which the robot can move is quickly becoming crammed; also the pipets movement up and down, are being pushed to their maximum. For future development it would be nice with a longer holding-arm and bigger up-down possibilities. For now the robot is okay though.
 +
 +
Tasks: Make a map over the plate with encoder distances, for easier programming of different assays. Order another transformer.
 +
 +
==Week 4==
 +
 +
'''Monday''': Finished the platform. The working area is small: 85 mm wide, 58 mm high and 298 mm long. For the first experiment it will be okay, although a bigger width and height is desirable.
 +
 +
Messured motor encoder distances: full width (85 mm) = 1590 rotations (18,71 rotations pr. mm); full hight (58 mm) = 337 rotations (5,81 rotations pr. mm); full depth (298 mm) = 2950 rotations (9,90 rotations pr. mm).
 +
 +
Made a new pipet holder, so that the movement is more stable. Created and tested tip-removal.
 +
 +
'''Tuesday''': Worked on improving the depth movement, which is off. Constructed new feet, but are still having trouble creating a precise movement.
 +
 +
'''Sunday''': Have been working on the depth movement all week. Constructed an aluminum rail on which the NTX's roll and a singel motor attached to a differential drive to drive the robot back and forth (the depth movement). To my delight it seems to be working.
 +
 +
<html>
 +
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/HdvS5W-KMuI&hl=da_DK&fs=1&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/HdvS5W-KMuI&hl=da_DK&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>
 +
</html>
 +
 +
The code now looks like this:
 +
 +
Master (aka Wall_E):
 +
 +
<pre>
 +
#pragma config(Sensor, S1,    WallE_1,            sensorTouch)
 +
#pragma config(Sensor, S4,    WallE_4,            sensorTouch)
 +
#pragma config(Motor,  motorA,          WallE_A,      tmotorNormal, PIDControl, encoder)
 +
#pragma config(Motor,  motorC,          WallE_C,      tmotorNormal, PIDControl, encoder)
 +
//*!!Code automatically generated by 'ROBOTC' configuration wizard              !!*//
 +
 +
//DECLARING FUNCTIONS
 +
 +
//Lateral control, ie. up and down
 +
void LateralControl (int Lmotor, int Lencode, int Lpause) {
 +
  //Reset encoder
 +
  nMotorEncoder [WallE_C] = 0;
 +
 
 +
  //Power up
 +
  while( (nMotorEncoder[WallE_C]!=Lencode) ) {
 +
      motor[WallE_C]=Lmotor;
 +
      nxtDisplayClearTextLine(5);
 +
      nxtDisplayClearTextLine(6);
 +
 +
      nxtDisplayString (5,"%d", nMotorEncoder[WallE_C]);
 +
      nxtDisplayString (6,"%d, %d", Lencode, Lmotor);
 +
      wait1Msec (1);
 +
  }
 +
  //Stop
 +
  motor [WallE_C] = 0;
 +
  //End pause if any
 +
  wait10Msec(Lpause);
 +
 +
  return;
 +
}
 +
 +
 +
//Horizontal control, ie. left and right
 +
void HorizontalControl (int Hmotor, int Hencode, int Hpause) {
 +
 +
  //Reset encoder
 +
  nMotorEncoder [WallE_A] = 0;
 +
 
 +
  //Power up
 +
  while( (nMotorEncoder[WallE_A]!=Hencode) ) {
 +
    motor[WallE_A]=Hmotor;
 +
    nxtDisplayClearTextLine(5);
 +
    nxtDisplayClearTextLine(6);
 +
 +
    nxtDisplayString (5,"%d", nMotorEncoder[WallE_A]);
 +
    nxtDisplayString (6,"%d, %d", Hencode, Hmotor);
 +
    wait1Msec (1);
 +
  }
 +
  //Stop
 +
  motor [WallE_A] = 0;
 +
  //End pause
 +
  wait10Msec(Hpause);
 +
 +
  return;
 +
}
 +
 +
//Simple bluetooth control. BT1 defines which function the slave NTX should preform.
 +
//BT1 = 1 is depth movement, BT1 = 2 is dispens control, BT1 = 3 is reset depth, BT1 = 4 is reset dispens
 +
//BT2 is power, BT3 is encode distance.
 +
void BTcontrol (int BT1, int BT2, int BT3) {
 +
 +
  //Sending command
 +
  sendMessageWithParm (BT1 ,BT2 , BT3);
 +
 +
  wait10Msec(100);
 +
  nxtDisplayClearTextLine(5);
 +
  nxtDisplayString (5,"Contacting slave");
 +
 +
  //Wait till the slave sends back message with confirmation that the task is done
 +
  while (true) {
 +
 +
    if (message == 0) {
 +
      nxtDisplayClearTextLine(5);
 +
      nxtDisplayString (5,"Waiting for slave");
 +
      wait1Msec(5);
 +
    }
 +
    else {
 +
 +
      if (messageParm [0] == 99) {
 +
        ClearMessage ();
 +
 +
        nxtDisplayClearTextLine(5);
 +
        nxtDisplayString (5,"Slave move done");
 +
 +
        //PlaySound(soundBeepBeep); //Debug sound
 +
        wait10Msec(10);
 +
        break;
 +
        return;
 +
        }
 +
    }
 +
 +
    wait10Msec(5);
 +
  }
 +
}
 +
 +
//Resetting function
 +
void ResetRobot(int ResetLat, int ResetHor, int ResetDepth, int ResetDispens) {
 +
 +
  if (ResetLat == 1) {
 +
    nxtDisplayClearTextLine(5);
 +
    nxtDisplayString (5,"Resetting height");
 +
 +
    if (SensorValue(WallE_4) == 1) {
 +
      while( (SensorValue(WallE_4) != 0) ) {
 +
        motor[WallE_C]=30;
 +
        wait1Msec (1);
 +
      }
 +
    motor[WallE_C]=0; //stopping motor again
 +
    }
 +
    wait10Msec(200);
 +
  }
 +
  if (ResetHor == 1) {
 +
    nxtDisplayClearTextLine(5);
 +
    nxtDisplayString (5,"Resetting width");
 +
 +
    if (SensorValue(WallE_1) == 0) {
 +
      while( (SensorValue(WallE_1) != 1) ) {
 +
        motor[WallE_A]=30;
 +
        wait1Msec (1);
 +
      }
 +
      motor[WallE_A]=0; //stopping motor again
 +
    }
 +
    wait10Msec(200);
 +
  }
 +
  if (ResetDepth == 1) {
 +
    nxtDisplayClearTextLine(5);
 +
    nxtDisplayString (5,"Resetting depth");
 +
 +
    BTcontrol (3, 0, 0); //Command 3 starts a resetting code on the slave ntx
 +
//    sendMessageWithParm (3, 0, 0);
 +
 +
    wait10Msec(600);
 +
  }
 +
  if (ResetDispens == 1) {
 +
    nxtDisplayClearTextLine(5);
 +
    nxtDisplayString (5,"Resetting dispens");
 +
 +
    BTcontrol (4, 0, 0); //Command 4 starts a resetting code on the slave ntx
 +
//    sendMessageWithParm (4, 0, 0);
 +
 +
    wait10Msec(300);
 +
  }
 +
 +
  PlaySound(soundBlip);
 +
  wait10Msec(10);
 +
 +
  return;
 +
}
 +
 +
//MAIN TASK
 +
task main () {
 +
 +
  // START-UP PROCEDURE
 +
 +
  //Resetting and display
 +
  PlaySound(soundFastUpwardTones);
 +
  eraseDisplay ();
 +
  ClearMessage ();
 +
  nxtDisplayCenteredTextLine (1, "MASTER");
 +
  nxtDisplayString(3, "Status:");
 +
 +
  //Starting PID
 +
  nMotorPIDSpeedCtrl[motorA] = mtrSpeedReg;
 +
  nMotorPIDSpeedCtrl[motorC] = mtrSpeedReg;
 +
 +
  //Checking bluetooth connection
 +
  if (nBTCurrentStreamIndex >= 0) {
 +
    nxtDisplayString(4, "NTX's connected");
 +
  }
 +
  else {
 +
    btConnect(1, "R2D2");            // Connects to NXT named "R2D2" on port 1.
 +
    nxtDisplayString(4, "Connecting to");
 +
    nxtDisplayString(5, "slave NTX");
 +
    wait10Msec(750); //Delay so instructions don't get lost
 +
  }
 +
 +
  //Code for starting slave program on slave NTX -
 +
  // NOT DONE
 +
 +
  //Resetting robot, by calling function ResetRobot
 +
 +
  ResetRobot(1, 1, 1, 1);
 +
 +
 +
  //PROGRAM
 +
 +
 
 +
  //1st turn
 +
  HorizontalControl(-60, -145, 10); //Right
 +
  BTcontrol(1, 30, 110); //Forward
 +
  LateralControl(-50, -305, 50); //Pipet down
 +
  ResetRobot(1,0,0,0); //Pipet up
 +
  BTcontrol(1, 30, 960); //Forward
 +
  HorizontalControl(60, 70, 10); //Left
 +
  BTcontrol(2, 40, 54); //Air out
 +
  LateralControl(-50, -290, 60); //Pipet down
 +
  ResetRobot(0,0,0,1); //Aspire
 +
  wait10Msec(10);
 +
  ResetRobot(1,0,0,0); //Pipet up
 +
  BTcontrol(1, 30, 565); //Forward
 +
  HorizontalControl(-60, -240, 10); //Right
 +
  LateralControl(-50, -140, 60); //Pipet down
 +
  BTcontrol(2, 40, 65); //Dispensing
 +
  ResetRobot(1,0,0,0); //Pipet up
 +
  ResetRobot(0,0,0,1); //Release dispens
 +
  HorizontalControl(-60, -240, 10); //Right
 +
  BTcontrol(1, 30, 250); //Forward
 +
  LateralControl(-50, -290, 60); //Pipet down
 +
  HorizontalControl(60, 220, 10); //Left
 +
  ResetRobot(1,1,1,1); //Pipet up
 +
 
 +
  //2nd turn
 +
 +
}
 +
</pre>
 +
 +
Slave (aka R2D2) code:
 +
 +
<pre>
 +
#pragma config(Sensor, S3,    R2D2_3,              sensorTouch)
 +
#pragma config(Sensor, S4,    R2D2_4,              sensorTouch)
 +
#pragma config(Motor,  motorA,          R2D2_A,        tmotorNormal, PIDControl, encoder)
 +
#pragma config(Motor,  motorB,          R2D2_B,        tmotorNormal, PIDControl, encoder)
 +
#pragma config(Motor,  motorC,          R2D2_C,        tmotorNormal, PIDControl, encoder)
 +
//*!!Code automatically generated by 'ROBOTC' configuration wizard              !!*//
 +
 +
//DECLARING FUNCTIONS
 +
 +
//Control of the dispenser
 +
void DispensControl (int DCmotor, int DCencode, int DCpause) {
 +
  //Syncronizing motors
 +
  nSyncedMotors = synchAC;
 +
  nSyncedTurnRatio = +100; // Left motor turns 100% of right motor
 +
  //Power up
 +
  while( !(nMotorEncoder[R2D2_A]==DCencode) ) {
 +
    motor[R2D2_A]=DCmotor;
 +
    nxtDisplayClearTextLine(5);
 +
    nxtDisplayClearTextLine(6);
 +
 +
    nxtDisplayString (5,"%d, %d", nMotorEncoder[R2D2_A],nMotorEncoder[R2D2_C]);
 +
    nxtDisplayString (6,"%d, %d", DCencode, DCmotor);
 +
    wait1Msec (1);
 +
  }
 +
  //Stop
 +
  motor [R2D2_A] = 0;
 +
  //End pause if any
 +
  wait10Msec(DCpause);
 +
 +
  return;
 +
}
 +
 +
//Forward and backward control
 +
void DepthControl (int Dmotor, int Dencode, int Dpause) {
 +
  //Power up
 +
  while( (nMotorEncoder[R2D2_B]!=Dencode) ) {
 +
      motor[R2D2_B]=Dmotor;
 +
      nxtDisplayClearTextLine(5);
 +
      nxtDisplayClearTextLine(6);
 +
 +
      nxtDisplayString (5,"%d", nMotorEncoder[R2D2_B]);
 +
      nxtDisplayString (6,"%d, %d", Dencode, Dmotor);
 +
      wait1Msec (1);
 +
  }
 +
  motor [R2D2_B] = 0;
 +
  //End pause if any
 +
  wait10Msec(Dpause);
 +
 +
  return;
 +
}
 +
 +
//MAIN TASK
 +
task main () {
 +
 +
  //Resetting and display
 +
  eraseDisplay ();
 +
  nxtDisplayCenteredTextLine (1, "SLAVE");
 +
  nxtDisplayString(3, "Status:");
 +
 +
  //Starting PID
 +
  nMotorPIDSpeedCtrl[motorA] = mtrSpeedReg;
 +
  nMotorPIDSpeedCtrl[motorB] = mtrSpeedReg;
 +
  nMotorPIDSpeedCtrl[motorC] = mtrSpeedReg;
 +
 +
  // Clearing all old bluetooth messages
 +
  cCmdBTPurgeRcvBuffer();
 +
  while ( message != 0 ) {
 +
    ClearMessage ();
 +
  }
 +
  nxtDisplayString (4,"Old instructions");
 +
  nxtDisplayString(5,"removed");
 +
  wait10Msec(100);
 +
 +
  // Starting slave program
 +
  while (true) {
 +
 +
    //Only do something if a message is available.
 +
    if ( bQueuedMsgAvailable () ) {
 +
 +
      nxtDisplayClearTextLine(4);
 +
      nxtDisplayClearTextLine(5);
 +
      nxtDisplayString (4,"Message recieved");
 +
      wait10Msec(50);
 +
 +
      //Message 1 = depth movement
 +
      if (messageParm [0] == 1) {
 +
 +
          nxtDisplayClearTextLine(4);
 +
          if (messageParm[1] < 0) {
 +
            nxtDisplayString (4,"Pipet forward");
 +
          }
 +
          else {
 +
            nxtDisplayString (4,"Pipet backwards");
 +
          }
 +
 +
          //DepthControl(Power, Encode, Pause)
 +
          nMotorEncoder [R2D2_B] = 0;
 +
          DepthControl(messageParm [1], messageParm [2], 300);
 +
 +
          //sends confirmation back to master, that task is done
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayString (4,"Move done");
 +
          sendMessageWithParm (99, 0, 0);
 +
 +
          //Reset message buffer
 +
          ClearMessage ();
 +
          //PlaySound(soundBeepBeep); //Debug sound
 +
      }
 +
 +
      //Message 2 = dispens control
 +
      if (messageParm [0] == 2) {
 +
 +
          nxtDisplayClearTextLine(4);
 +
          if (messageParm[1] < 0) {
 +
            nxtDisplayString (4,"Pipet down");
 +
          }
 +
          else {
 +
            nxtDisplayString (4,"Pipet up");
 +
          }
 +
 +
          //DispensControl(Power, Encode, Pause)
 +
          nMotorEncoder [R2D2_A] = 0;
 +
          DispensControl(messageParm [1], messageParm [2], 300);
 +
 +
          //sends confirmation back to master, that task is done
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayString (4,"Move done");
 +
          sendMessageWithParm (99, 0, 0);
 +
 +
          //Reset message buffer
 +
          ClearMessage ();
 +
          //PlaySound(soundBeepBeep); //debug sound
 +
 +
      }
 +
 +
      //Message 3 = reset depth movement
 +
      if (messageParm [0] == 3) {
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayString (4,"Resetting depth");
 +
 +
          //HorizontalControl(Power, Encode, Pause)
 +
          nMotorEncoder [R2D2_B] = 0;
 +
          while( (SensorValue(R2D2_3) != 1) ) {
 +
            motor[R2D2_B]=-20;
 +
            wait1Msec (1);
 +
          }
 +
 +
          motor[R2D2_B]=0; //stopping motor again
 +
 +
          //sends confirmation back to master, that task is done
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayString (4,"Move done");
 +
          sendMessageWithParm (99, 0, 0);
 +
 +
          //Reset message buffer
 +
          ClearMessage ();
 +
          //PlaySound(soundBeepBeep); //Debug sound
 +
      }
 +
 +
      //Message 4 = reset dispens
 +
      if (messageParm [0] == 4) {
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayString (4,"Resetting dispens");
 +
 +
            //Syncronizing motors
 +
            nSyncedMotors = synchAC;
 +
            nSyncedTurnRatio = +100; // Left motor turns 100% of right motor
 +
            //Dispens control
 +
            nMotorEncoder [R2D2_A] = 0;
 +
            while( (SensorValue(R2D2_4) != 1) ) {
 +
              motor[R2D2_A]=-20;
 +
              wait1Msec (1);
 +
            }
 +
            motor[R2D2_A]=0; //stopping motor again
 +
 +
          //sends confirmation back to master, that task is done
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayString (4,"Move done");
 +
          sendMessageWithParm (99, 0, 0);
 +
 +
          //Reset message buffer
 +
          ClearMessage ();
 +
          //PlaySound(soundBeepBeep); //Debug sound
 +
      }
 +
 +
    }
 +
    //If no message available then wait
 +
    else {
 +
          nxtDisplayClearTextLine(4);
 +
          nxtDisplayClearTextLine(5);
 +
          nxtDisplayString (4,"Waiting for");
 +
          nxtDisplayString(5,"instructions");
 +
          wait1Msec(5);
 +
    }
 +
 +
  }
}
}
</pre>
</pre>

Current revision

Contents

Liquid handling robot

The current LEGO liquid handling robot
The current LEGO liquid handling robot

The goal of this project is to create an open-source liquid handling robot by using LEGO Mindstorm.

The current project uses two Mindstorm NTX's, a 200μL micropipet, a couple of aluminum beams and a great deal of LEGO bricks. It's programmed using ROBOTC.

There are a number of limitations with the current design; the robot has very wobbly up/down movements as can be seen on the videos below; also the area available to work in is very small; the robot does not error check the volumes it pipets. Still it can be used to simple experiments and liquid handling.

Documentation

The finale documentation is not yet ready.

Specifications

The table below outlines the specifications of the machine.

' Metric
Model
Technology LEGO Mindstorm NTX 2.0 and ROBOTC
Price of all materials
Size 420 mm (Width) x 375 mm (Depth) x 500 mm (Height)
Weight
Area of movement85 mm (Width) x 200 mm (Depth) x 62 mm (Height)
Speed
Accuracy

Ressources

LEGO

Software

Hardware

Knowledge

Ideas

Notebook

Week 1

Monday: Worked on the primary handling operation with a very simpel design and set up the lab space.

The linear actuator works up to aboout 1,6 cm, putting a limit on how much the pipet can be pushed down. Solutions might be: using two actuators, using a rail-design instead or adding more power to the actuator.

The design is becoming quite big. Solution might be to use the power function powers, which are smaller.

The pipet needs to be tested for accuracy.

Tuesday: Checked the pipet for accuracy, and it was way of. Found another one, Eppendorf 200 ul.

Continued working on the dispensing handling. Created a successful design with two motors that press down on the pipet. I'm a bit nervous about the stress on the motor, but for now they seem to do fine.

The battery in one of the NTX bricks already ran out. I've ordered a rechargeable battery - but no transformer, since they are not sold here. Hopefully the inlet will accepts other transformers as well.

Programmed two aspiration and dispensing programs, called 'asp & disp 1 and 2 (16-03-10).c'. The code for asp & disp 2 is:


#pragma config(Motor,  motorA,          motor_A,       tmotorNormal, PIDControl, encoder)
#pragma config(Motor,  motorB,          motor_B,       tmotorNormal, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

//DECLARING FUNCTIONS

//Control of the dispenser
void DispensControl (int DCmotor, int DCencode, int DCpause) {

  //Syncronizing motors
  nSyncedMotors = synchAB;
  nSyncedTurnRatio = +100; // Left motor turns 100% of right motor
  //Power up

  while( !(nMotorEncoder[motor_A]==DCencode) ) {
    motor[motor_A]=DCmotor;
    nxtDisplayClearTextLine(5);
    nxtDisplayClearTextLine(6);

    nxtDisplayString (5,"%d, %d", nMotorEncoder[motorA],nMotorEncoder[motorB]);
    nxtDisplayString (6,"%d, %d", DCencode, DCmotor);
    wait1Msec (1);
  }

  motor [motorA] = 0;

  //End pause if any
  wait10Msec(DCpause);
}


//MAIN TASK
task main () {

  nxtDisplayString (1,"DISPENSING 2");
  nMotorEncoder [motor_A] = 0;

  //Air-out
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Air-out");
  DispensControl(30, 30, 0);
  DispensControl(70, 60, 250);

  //Aspiring
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Aspiring");
  DispensControl(-15, 0, 250);

  //Dispensing
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Dispensing");
  DispensControl(30, 40, 0);
  DispensControl(70, 80, 250);

  //Resetting
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Resetting");
  DispensControl(-15, 0, 250);

}

Wednesday: Started designing the holding bay for the pipet. This will control the downward and upward motion of the pipet, but should also be able to be pulled either left or right. Decided to work with only one motor, since this would make it possible to control the entire robot with two NTX's.

Friday: Continued work on holding bay design.

Week 2

Monday: Worked on holding bay design and horizontal control. The design right now uses one motor for the horizontal control, which seems to be working fine. One motor is also used for the lateral control, which is a bit bumpy: the pipet holding and the motors need to be adjusted.

First code for lateral and horizontal control (Horizontal control (22-03-10).c):

#pragma config(Motor,  motorB,          motor_B,       tmotorNormal, PIDControl, encoder)
#pragma config(Motor,  motorC,          motor_C,       tmotorNormal, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

//DECLARING FUNCTIONS

//Horizontal control, ie. left and right
void HorizontalControl (int Hmotor, int Hencode, int Hpause) {

  //Power up
  while( !(nMotorEncoder[motor_C]==Hencode) ) {
    motor[motor_C]=Hmotor;
    nxtDisplayClearTextLine(5);
    nxtDisplayClearTextLine(6);

    nxtDisplayString (5,"%d", nMotorEncoder[motor_C]);
    nxtDisplayString (6,"%d, %d", Hencode, Hmotor);
    wait1Msec (1);
  }

  motor [motor_C] = 0;

  //End pause if any
  wait10Msec(Hpause);
}

//Lateral control, ie. up and down
void LateralControl (int Lmotor, int Lencode, int Lpause) {

  //Power up
  while( !(nMotorEncoder[motor_B]==Lencode) ) {
    motor[motor_B]=Lmotor;
    nxtDisplayClearTextLine(5);
    nxtDisplayClearTextLine(6);

    nxtDisplayString (5,"%d", nMotorEncoder[motor_B]);
    nxtDisplayString (6,"%d, %d", Lencode, Lmotor);
    wait1Msec (1);
  }

  motor [motor_B] = 0;

  //End pause if any
  wait10Msec(Lpause);
}


//MAIN TASK
task main () {

  nxtDisplayString (1,"HORIZONTAL CONTROL");
  nMotorEncoder [motor_C] = 0;

  //Move to the left-most position
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Going left");
  HorizontalControl(30, 600, 250);

  //Reset
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Going right");
  HorizontalControl(-30, 0, 250);

  //Move to the left-most position
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Going down");
  LateralControl(-20, -250, 80);

  //Up and down
  nxtDisplayClearTextLine(3);
  nxtDisplayString (3,"Going up");
  LateralControl(33, 0, 0);


}

Tuesday: Horizontal control: did a test at 40% motor power, with 50 movements back and forth. The bay slides a bit to the right, 0,2 cm after 50 turns. Lateral control: gravity is playing games and the movement changes alot over time. Redid the pipet-holding and added larger wheels. The first testing seems good.

Wednesday: Added a touch sensor to the lateral movements. After 50 runs it seem to "slide", but solved this by adding a touch-sensor, that makes sure the movement is reset after each movement (ie. moves to the same position).

//MAIN TASK
task main () {

  int i;
  for ( i=0; i<=10; i++) {

    //Down
    nMotorEncoder [motor_B] = 0;
    LateralControl(-55, -100, 140, false);

    //Up
    nMotorEncoder [motor_B] = 0;
    LateralControl(55, 100, 0, true);

    //Reset - go up until the sensor is not pressed anymore
    while( !(SensorValue(sensor_1) == 0) ) {
      motor[motor_B]=55;
      wait1Msec (1);
    }   
    
  }
}



Thursday: Worked on the forward and backward movement, which seems to be working fine. No sliding was observed after 10 runs. Used the following code:

#pragma config(Motor,  motorA,          WallE_A,       tmotorNormal, PIDControl, encoder)
#pragma config(Motor,  motorB,          WallE_B,       tmotorNormal, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

//Forward and backward control
void ForwardControl (int Fmotor, int Fencode, int Fpause) {

  //Sync'ing motors A and B
  nSyncedMotors = synchAB ; 
  nSyncedTurnRatio = +100; 
  
  //Power up
  while( !(nMotorEncoder[WallE_A]==Fencode) ) {
      motor[WallE_A]=Fmotor;
      nxtDisplayClearTextLine(5);
      nxtDisplayClearTextLine(6);

      nxtDisplayString (5,"%d", nMotorEncoder[WallE_A]);
      nxtDisplayString (6,"%d, %d", Fencode, Fmotor);
      wait1Msec (1);

  }

  motor [WallE_A] = 0;

  //End pause if any
  wait10Msec(Fpause);
}

Naming conventions: I'm current using two NTX's with the names WallE and R2D2. The motors will be called WallE_A, WallE_B, WallE_C, the sensors WallE_1, WallE_2, ect.

Tested bluetooth with the following two small programs:

BT send test 1 (25-03-10).c


//MAIN TASK
task main () {

  
  //Sending BT message - WallE is set up as master, R2D2 as slave.

  int i , x2 , x3;

  x2 = 0; // dummy
  x3 = 0; // dummy

  for ( i=0; i<=10; i++) {
      sendMessageWithParm (i ,x2 ,x3 );
      wait1Msec (2000); // wait one -tenth of a second
  }
  
}

BT receive test 1 (25-03-10).c



//MAIN TASK
task main () {
  
  while ( true ) {

    if ( bQueuedMsgAvailable () ) {
      
      eraseDisplay ();
      nxtDisplayCenteredTextLine (3,"%d",
      messageParm [0]);
      ClearMessage ();
    }

    wait1Msec (500);
  }

  
}

Saturday: Worked on improving the bluetooth connection.

Week 3

Monday: Continued working on the bluetooth connection, which is now finished. Tweaked the different functions that move the robot, so that the distances fit. Did a simple test with liquid handling, 200 ul.

Tasks: Plan and do a advanced experiment where water from 50 wells are moved to 50 other wells.

Thuesday: Build loading bays and pipet-tip removal bay. Built resetting mechanism but since the cables are not long enough, the robot has no reset for the back-forward movement and the dispensing. These two movements are very stable, but will have to look into making longer cables.

Discussed experiment with Martin. Decided to drop 50 well experiment for now and instead focus on emulating real experiment. 800ul, 5ul and 5ul liquid has to be loaded on small glass plate.

Wednesday: Continued working on experiment. Made improvements to the master-slave code. Made a reset-mechanism for the back-forward movement. The space in which the robot can move is quickly becoming crammed; also the pipets movement up and down, are being pushed to their maximum. For future development it would be nice with a longer holding-arm and bigger up-down possibilities. For now the robot is okay though.

Tasks: Make a map over the plate with encoder distances, for easier programming of different assays. Order another transformer.

Week 4

Monday: Finished the platform. The working area is small: 85 mm wide, 58 mm high and 298 mm long. For the first experiment it will be okay, although a bigger width and height is desirable.

Messured motor encoder distances: full width (85 mm) = 1590 rotations (18,71 rotations pr. mm); full hight (58 mm) = 337 rotations (5,81 rotations pr. mm); full depth (298 mm) = 2950 rotations (9,90 rotations pr. mm).

Made a new pipet holder, so that the movement is more stable. Created and tested tip-removal.

Tuesday: Worked on improving the depth movement, which is off. Constructed new feet, but are still having trouble creating a precise movement.

Sunday: Have been working on the depth movement all week. Constructed an aluminum rail on which the NTX's roll and a singel motor attached to a differential drive to drive the robot back and forth (the depth movement). To my delight it seems to be working.

The code now looks like this:

Master (aka Wall_E):

#pragma config(Sensor, S1,     WallE_1,             sensorTouch)
#pragma config(Sensor, S4,     WallE_4,             sensorTouch)
#pragma config(Motor,  motorA,          WallE_A,       tmotorNormal, PIDControl, encoder)
#pragma config(Motor,  motorC,          WallE_C,       tmotorNormal, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

//DECLARING FUNCTIONS

//Lateral control, ie. up and down
void LateralControl (int Lmotor, int Lencode, int Lpause) {
  //Reset encoder
  nMotorEncoder [WallE_C] = 0;
  
  //Power up
  while( (nMotorEncoder[WallE_C]!=Lencode) ) {
      motor[WallE_C]=Lmotor;
      nxtDisplayClearTextLine(5);
      nxtDisplayClearTextLine(6);

      nxtDisplayString (5,"%d", nMotorEncoder[WallE_C]);
      nxtDisplayString (6,"%d, %d", Lencode, Lmotor);
      wait1Msec (1);
  }
  //Stop
  motor [WallE_C] = 0;
  //End pause if any
  wait10Msec(Lpause);

  return;
}


//Horizontal control, ie. left and right
void HorizontalControl (int Hmotor, int Hencode, int Hpause) {

  //Reset encoder
  nMotorEncoder [WallE_A] = 0;
  
  //Power up
  while( (nMotorEncoder[WallE_A]!=Hencode) ) {
    motor[WallE_A]=Hmotor;
    nxtDisplayClearTextLine(5);
    nxtDisplayClearTextLine(6);

    nxtDisplayString (5,"%d", nMotorEncoder[WallE_A]);
    nxtDisplayString (6,"%d, %d", Hencode, Hmotor);
    wait1Msec (1);
  }
  //Stop
  motor [WallE_A] = 0;
  //End pause
  wait10Msec(Hpause);

  return;
}

//Simple bluetooth control. BT1 defines which function the slave NTX should preform.
//BT1 = 1 is depth movement, BT1 = 2 is dispens control, BT1 = 3 is reset depth, BT1 = 4 is reset dispens
//BT2 is power, BT3 is encode distance.
void BTcontrol (int BT1, int BT2, int BT3) {

  //Sending command
  sendMessageWithParm (BT1 ,BT2 , BT3);

  wait10Msec(100);
  nxtDisplayClearTextLine(5);
  nxtDisplayString (5,"Contacting slave");

  //Wait till the slave sends back message with confirmation that the task is done
  while (true) {

    if (message == 0) {
      nxtDisplayClearTextLine(5);
      nxtDisplayString (5,"Waiting for slave");
      wait1Msec(5);
    }
    else {

      if (messageParm [0] == 99) {
        ClearMessage ();

        nxtDisplayClearTextLine(5);
        nxtDisplayString (5,"Slave move done");

        //PlaySound(soundBeepBeep); //Debug sound
        wait10Msec(10);
        break;
        return;
        }
    }

    wait10Msec(5);
  }
}

//Resetting function
void ResetRobot(int ResetLat, int ResetHor, int ResetDepth, int ResetDispens) {

  if (ResetLat == 1) {
    nxtDisplayClearTextLine(5);
    nxtDisplayString (5,"Resetting height");

    if (SensorValue(WallE_4) == 1) {
      while( (SensorValue(WallE_4) != 0) ) {
        motor[WallE_C]=30;
        wait1Msec (1);
      }
    motor[WallE_C]=0; //stopping motor again
    }
    wait10Msec(200);
  }
  if (ResetHor == 1) {
    nxtDisplayClearTextLine(5);
    nxtDisplayString (5,"Resetting width");

    if (SensorValue(WallE_1) == 0) {
      while( (SensorValue(WallE_1) != 1) ) {
        motor[WallE_A]=30;
        wait1Msec (1);
      }
      motor[WallE_A]=0; //stopping motor again
    }
    wait10Msec(200);
  }
  if (ResetDepth == 1) {
    nxtDisplayClearTextLine(5);
    nxtDisplayString (5,"Resetting depth");

    BTcontrol (3, 0, 0); //Command 3 starts a resetting code on the slave ntx
//    sendMessageWithParm (3, 0, 0);

    wait10Msec(600);
  }
  if (ResetDispens == 1) {
    nxtDisplayClearTextLine(5);
    nxtDisplayString (5,"Resetting dispens");

    BTcontrol (4, 0, 0); //Command 4 starts a resetting code on the slave ntx
//    sendMessageWithParm (4, 0, 0);

    wait10Msec(300);
  }

  PlaySound(soundBlip);
  wait10Msec(10);

  return;
}

//MAIN TASK
task main () {

  // START-UP PROCEDURE

  //Resetting and display
  PlaySound(soundFastUpwardTones);
  eraseDisplay ();
  ClearMessage ();
  nxtDisplayCenteredTextLine (1, "MASTER");
  nxtDisplayString(3, "Status:");

  //Starting PID
  nMotorPIDSpeedCtrl[motorA] = mtrSpeedReg;
  nMotorPIDSpeedCtrl[motorC] = mtrSpeedReg;

  //Checking bluetooth connection
  if (nBTCurrentStreamIndex >= 0) {
    nxtDisplayString(4, "NTX's connected");
  }
  else {
    btConnect(1, "R2D2");            // Connects to NXT named "R2D2" on port 1.
    nxtDisplayString(4, "Connecting to");
    nxtDisplayString(5, "slave NTX");
    wait10Msec(750); //Delay so instructions don't get lost
  }

  //Code for starting slave program on slave NTX -
  // NOT DONE

  //Resetting robot, by calling function ResetRobot

  ResetRobot(1, 1, 1, 1);


  //PROGRAM

  
  //1st turn
  HorizontalControl(-60, -145, 10); //Right
  BTcontrol(1, 30, 110); //Forward
  LateralControl(-50, -305, 50); //Pipet down
  ResetRobot(1,0,0,0); //Pipet up
  BTcontrol(1, 30, 960); //Forward
  HorizontalControl(60, 70, 10); //Left
  BTcontrol(2, 40, 54); //Air out
  LateralControl(-50, -290, 60); //Pipet down
  ResetRobot(0,0,0,1); //Aspire
  wait10Msec(10);
  ResetRobot(1,0,0,0); //Pipet up 
  BTcontrol(1, 30, 565); //Forward
  HorizontalControl(-60, -240, 10); //Right
  LateralControl(-50, -140, 60); //Pipet down
  BTcontrol(2, 40, 65); //Dispensing
  ResetRobot(1,0,0,0); //Pipet up
  ResetRobot(0,0,0,1); //Release dispens
  HorizontalControl(-60, -240, 10); //Right
  BTcontrol(1, 30, 250); //Forward
  LateralControl(-50, -290, 60); //Pipet down
  HorizontalControl(60, 220, 10); //Left
  ResetRobot(1,1,1,1); //Pipet up
  
  //2nd turn

}

Slave (aka R2D2) code:

#pragma config(Sensor, S3,     R2D2_3,              sensorTouch)
#pragma config(Sensor, S4,     R2D2_4,              sensorTouch)
#pragma config(Motor,  motorA,          R2D2_A,        tmotorNormal, PIDControl, encoder)
#pragma config(Motor,  motorB,          R2D2_B,        tmotorNormal, PIDControl, encoder)
#pragma config(Motor,  motorC,          R2D2_C,        tmotorNormal, PIDControl, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

//DECLARING FUNCTIONS

//Control of the dispenser
void DispensControl (int DCmotor, int DCencode, int DCpause) {
  //Syncronizing motors
  nSyncedMotors = synchAC;
  nSyncedTurnRatio = +100; // Left motor turns 100% of right motor
  //Power up
  while( !(nMotorEncoder[R2D2_A]==DCencode) ) {
    motor[R2D2_A]=DCmotor;
    nxtDisplayClearTextLine(5);
    nxtDisplayClearTextLine(6);

    nxtDisplayString (5,"%d, %d", nMotorEncoder[R2D2_A],nMotorEncoder[R2D2_C]);
    nxtDisplayString (6,"%d, %d", DCencode, DCmotor);
    wait1Msec (1);
  }
  //Stop
  motor [R2D2_A] = 0;
  //End pause if any
  wait10Msec(DCpause);

  return;
}

//Forward and backward control
void DepthControl (int Dmotor, int Dencode, int Dpause) {
  //Power up
  while( (nMotorEncoder[R2D2_B]!=Dencode) ) {
      motor[R2D2_B]=Dmotor;
      nxtDisplayClearTextLine(5);
      nxtDisplayClearTextLine(6);

      nxtDisplayString (5,"%d", nMotorEncoder[R2D2_B]);
      nxtDisplayString (6,"%d, %d", Dencode, Dmotor);
      wait1Msec (1);
  }
  motor [R2D2_B] = 0;
  //End pause if any
  wait10Msec(Dpause);

  return;
}

//MAIN TASK
task main () {

  //Resetting and display
  eraseDisplay ();
  nxtDisplayCenteredTextLine (1, "SLAVE");
  nxtDisplayString(3, "Status:");

  //Starting PID
  nMotorPIDSpeedCtrl[motorA] = mtrSpeedReg;
  nMotorPIDSpeedCtrl[motorB] = mtrSpeedReg;
  nMotorPIDSpeedCtrl[motorC] = mtrSpeedReg;

  // Clearing all old bluetooth messages
  cCmdBTPurgeRcvBuffer();
  while ( message != 0 ) {
    ClearMessage ();
  }
  nxtDisplayString (4,"Old instructions");
  nxtDisplayString(5,"removed");
  wait10Msec(100);

  // Starting slave program
  while (true) {

    //Only do something if a message is available.
    if ( bQueuedMsgAvailable () ) {

      nxtDisplayClearTextLine(4);
      nxtDisplayClearTextLine(5);
      nxtDisplayString (4,"Message recieved");
      wait10Msec(50);

      //Message 1 = depth movement
      if (messageParm [0] == 1) {

          nxtDisplayClearTextLine(4);
          if (messageParm[1] < 0) {
            nxtDisplayString (4,"Pipet forward");
          }
          else {
            nxtDisplayString (4,"Pipet backwards");
          }

          //DepthControl(Power, Encode, Pause)
          nMotorEncoder [R2D2_B] = 0;
          DepthControl(messageParm [1], messageParm [2], 300);

          //sends confirmation back to master, that task is done
          nxtDisplayClearTextLine(4);
          nxtDisplayString (4,"Move done");
          sendMessageWithParm (99, 0, 0);

          //Reset message buffer
          ClearMessage ();
          //PlaySound(soundBeepBeep); //Debug sound
      }

      //Message 2 = dispens control
      if (messageParm [0] == 2) {

          nxtDisplayClearTextLine(4);
          if (messageParm[1] < 0) {
            nxtDisplayString (4,"Pipet down");
          }
          else {
            nxtDisplayString (4,"Pipet up");
          }

          //DispensControl(Power, Encode, Pause)
          nMotorEncoder [R2D2_A] = 0;
          DispensControl(messageParm [1], messageParm [2], 300);

          //sends confirmation back to master, that task is done
          nxtDisplayClearTextLine(4);
          nxtDisplayString (4,"Move done");
          sendMessageWithParm (99, 0, 0);

          //Reset message buffer
          ClearMessage ();
          //PlaySound(soundBeepBeep); //debug sound

      }

      //Message 3 = reset depth movement
      if (messageParm [0] == 3) {
          nxtDisplayClearTextLine(4);
          nxtDisplayString (4,"Resetting depth");

          //HorizontalControl(Power, Encode, Pause)
          nMotorEncoder [R2D2_B] = 0;
          while( (SensorValue(R2D2_3) != 1) ) {
            motor[R2D2_B]=-20;
            wait1Msec (1);
          }

          motor[R2D2_B]=0; //stopping motor again

          //sends confirmation back to master, that task is done
          nxtDisplayClearTextLine(4);
          nxtDisplayString (4,"Move done");
          sendMessageWithParm (99, 0, 0);

          //Reset message buffer
          ClearMessage ();
          //PlaySound(soundBeepBeep); //Debug sound
      }

      //Message 4 = reset dispens
      if (messageParm [0] == 4) {
          nxtDisplayClearTextLine(4);
          nxtDisplayString (4,"Resetting dispens");

            //Syncronizing motors
            nSyncedMotors = synchAC;
            nSyncedTurnRatio = +100; // Left motor turns 100% of right motor
            //Dispens control
            nMotorEncoder [R2D2_A] = 0;
            while( (SensorValue(R2D2_4) != 1) ) {
              motor[R2D2_A]=-20;
              wait1Msec (1);
            }
            motor[R2D2_A]=0; //stopping motor again

          //sends confirmation back to master, that task is done
          nxtDisplayClearTextLine(4);
          nxtDisplayString (4,"Move done");
          sendMessageWithParm (99, 0, 0);

          //Reset message buffer
          ClearMessage ();
          //PlaySound(soundBeepBeep); //Debug sound
      }

    }
    //If no message available then wait
    else {
          nxtDisplayClearTextLine(4);
          nxtDisplayClearTextLine(5);
          nxtDisplayString (4,"Waiting for");
          nxtDisplayString(5,"instructions");
          wait1Msec(5);
    }

  }
}
Personal tools