Trossen Desktop RoboTurret Top Banner

Tutorial: Complete Control Of An Arduino Via Serial

  1. ROBOTMAN's Avatar
    ROBOTMAN ROBOTMAN is offline Multi-Core Processor
    Category
    How To, DIY
    Views
    96,566
    Replies
    13
     

    Complete Control Of An Arduino Via Serial

    Difficulty
    Medium
    Estimated Time
    1hr
    Skills Required
    Knowledge Of Arduino Board
    Knowledge Of Arduino Programing
    Parts Required
    Arduino
    Computer

    Tools Required
    Jumpers For Testing
    Controlling An Arduino Via Serial

    This is a in depth tutorial on how control your arduino micro-controller via serial. Unlike other tutorials I will not use the 'char' variable but 'unassigned long's' to store numbers much larger than 255 "If this dose not make sense I will explain later".

    This tutorial will focus on the serial control of the digital/analog ports as well as servos but the concepts will be applicable to anything you want to control. Also I will go over how to read data from the digital and analog ports sending it back to the computer.

    For this tutorial I will just use the arduino Serial Monitor to send and receive data but any language that can send and receive serial data will be interface with the arduino with no modifications to the code. Examples would be python, C, C++, Processing, an so on...

    Before starting I assume you have an arduino board and have install the standard programing environment. If not the boards and the programing environment can be found here. I also assume you are familiar with the arduino, and the basics of the language so I will skip over the setup.

    Also this is my first tutorial so keep that in mind while reading and please post a comment if you notice anything off.




    Serial Protocol


    Before we jump into code we need to think about serial Protocol. How do we tell the arduino what we want it to do?

    A lot of peoples first thoughts for controlling the arduino is to send a command in the form of words. For example "digital pin 4 on". This works OK, but because the arduino isn't human it dose not understand English; so a command like that takes a lot more code to translate into an action. Also that is quite a bit of data to send over the serial port!

    The best way I have found to talk to the arduino is not with words but with numbers. Each number we send will correspond to an action. In this tutorial we will group the actions into three groups a '1' will change the state of a pin "analog/digital", a '2' will read data from a pin "analog/digital", and a '3' will control or read from a servo.

    By using numbers we can make it easy for the arduino to understand what we want, but what about the extra variables the arduino needs to carry out one of the above tasks? How do we tell the arduino to read an analog pin not a digital one? Actually its simple just have another group inside of the first; a '1' will read digital, a '2' will read analog.
    Finally we need to get the variables the arduino needs; which in this case is pin number. All you need to do for that is read a final number from the serial port!

    So for example to "read digital pin 4" we would send '1 1 4'
    To "read analog pin 4" we send '1 2 4'

    The full serial protocol for all the tasks I mentioned would look this:

    1 = Wright:
    1 = digital:
    "pin number"
    "1 for LOW"
    "2 for HIGH"
    2 = analog:
    "pin number"
    "frequency (0-255)"
    2 = Read:
    1 = digital:
    "pin number"
    2 = analog:
    "pin number"
    3 = Servo:
    1 = read:
    "pin number"
    2 = wright:
    "pin number"
    " position"


    You may be wondering why make a group within a group when you could make separate groups for analog read and analog wright? Really its my personal preference once you understand the concepts you can do it any way you want.


    It may look complex but I found it to be the best way to do it. Also I don't have any good flow charts for it yet .

    If you ever want to add a function like motor control or what ever else is not covered by these categories just add a fourth position and so on...

    Like this.

    1 = Wright:
    1 = digital:
    "pin number"
    "1 for LOW"
    "2 for HIGH"
    2 = analog:
    "pin number"
    "frequency (0-255)"

    2 = Read:
    1 = digital:
    "pin number"
    2 = analog:
    "pin number"
    3 = Servo:
    1 = read:
    "pin number"
    2 = wright:
    "pin number"
    " position"
    4 = Your Function:
    1 = Your Sub Function:
    "your variables"
    2 = Your Sub Function:
    "your variables"

    OK now that you have a general idea of the protocol I will be using lets jump into the code!


    Code

    Disclaimer: I will not go into great depth about the assigning of arrays or really any code that dose not pertain to serial "this includes the servo library". So of something like this looks foreign myservos[pinNumber] = Servo s1; you may need to read up on the arduino documentation found here before preceding. Also some of this code deals with variable scope so make sure you know the programing basics. However even if you have limited experience many of the code examples as far as getting the serial data may be extremely useful. Also at the end there is an attached file with the complete and working code and the final example will show the complete program.


    Step 1:

    The first step is to figure out how to receive serial data from the computer. The easiest way to do this is with the Serial.read() statement. Sending data is accomplished a similar way with the Serial.print() or Serial.println() statements. The first statement simply prints the data you want, and the other dose the same thing but with a /r/n newline characters on the end.

    So lets try a simple program.

    Code:
    int serialData = 0;
    
    void setup()
    {
      Serial.begin(9600);  
    }
    
    void loop()
    {
      if (Serial.available() > 0)
      {
        serialData = Serial.read();
        Serial.println(serialData);
      }  
    }
    I'll skip over the basics like the assigning of the variable, but whats happening here is the serial is being started at a "9600" data rate Serial.begin(9600);

    Next we check if there is new data available if (Serial.available() > 0)

    Finally we read the data serialData = Serial.read(); and print it back over the same serial port Serial.println(serialData);

    If you run this program and open the serial monitor you should have what ever you type echoed back but in ASCII . There for 1 = 49, 2 = 50 and so on... An ASCII chart is available here .

    Step 2:

    Now time to make things more complicated. As you may have noticed the arduino only receives 1 piece of data at a time. This works OK if we are only needed to send numbers between 0-9 but we want to be able to tell a servo to go to position 180 right? This is where we need to take multiple numbers one at a time and combine them into one big number.

    This is where some very basic math comes in here is our formula.


    x = number we want
    n = our current number
    i = number we want to add to the end

    x = n * 10 + i

    As you can see this simple attaches the number received to the end of the number we are making. Now we can append each new number to the end of what will be come our final number.

    Lets translate this into code. The variables used through this tutorial will be:

    unassigned long serialdata; Used to store the chain of numbers
    int inbyte; The number we just received from the serial port

    So the formula looks like this to the arduino:

    Code:
    serialdata = serialdata * 10 + inbyte;


    This gives us a number we can use to determine which function the user wants to run 1, 2, or 3. Or we can plug it directly into a varable like pinNumber or servoPose .

    So lets modify the old program to combine the numbers like this.

    Code:
    unsigned long serialdata;
    int inbyte;
    
    void setup()
    {
      Serial.begin(9600);
    }
    
    void loop()
    {
      inbyte = Serial.read();  
        if (inbyte > 0)
        { 
          serialdata = serialdata * 10 + inbyte;
          Serial.println(serialdata);
        }
    }
    This should output the combined value of numbers you input via serial. If strange numbers come up things may be being printed in ASCII. I'm not 100% sure why the arduino sometimes nor am I sure how to fix it but the variable its self is OK and everything but the Serial.println() statement will see it correctly.

    You have probably noticed by now that the program just jumbles all the values you enter into one giant variable? But if it takes three numbers to read a analog pin '1' '2' '7' wouldn't the function combine that all in to '127' ? If it did that our program would fail. We need a way to tell the program when one number begins and the next ends. I like to do that with the '/' character. Because our program only receives numbers the '/' character will never be sent so we can use it as a special character. In this case it will signify the end of a number.

    Code to implement this '/' number terminator is below:

    Code:
    unsigned long serialdata;
    int inbyte;
    
    void setup()
    {
      Serial.begin(9600);
    }
    
    void loop()
    {
      getSerial();
    }
    
    long getSerial()
    {
      serialdata = 0;
      while (inbyte != '/')
      {
        inbyte = Serial.read();  
        if (inbyte > 0 && inbyte != '/')
        { 
          serialdata = serialdata * 10 + inbyte - '0';
          Serial.println(serialdata);
        }
      }
      
      return serialdata;
      inbyte = 0;
    }


    As you can see I migrated the all the code to a separate function. This will come in handy later when we are writing the final program. This will print the current number every time you add a number then clear the data when you send a '/' . Also it will return the variable serialdata so code in the main loop can use it.

    Congrats! The serial data gathering part is over! For beginners and experts alike the above function can be very useful for getting numbers from the serial port. If you want to see how the protocol I went over way back at the beginning works and create the program for controlling the arduino keep reading.

    Step 3:

    Now we need to build the program that requests and handles the serial data. Remember our end goal is to control the analog/digital ports, servos, and read the analog/digital ports. So lets begin.

    Now we see the serial protocol way back at the beginning come into full play. I will be using "switch/case" statements but "if" statements work just as well. Info on those can be found here and here . The reason I chose to use a switch/case statement is because it is easier to expand on.

    So lets start by adding data to control analog/digital pins. I'll use the program I just made as a base to build the code on.

    Code:
    unsigned long serialdata;
    int inbyte;
    int digitalState;
    int pinNumber;
    int analogRate;
    
    void setup()
    {
      Serial.begin(9600);
    }
    
    void loop()
    {
      getSerial();
      switch(serialdata)
      {
      case 1:
        {
          //analog digital write
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              analogRate = serialdata;
              pinMode(pinNumber, OUTPUT);
              analogWrite(pinNumber, analogRate);
              pinNumber = 0;
              break;
            }
          case 2:
            {
              //digital write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              digitalState = serialdata;
              pinMode(pinNumber, OUTPUT);
              if (digitalState == 0)
              {
                digitalWrite(pinNumber, LOW);
              }
              if (digitalState == 1)
              {
                digitalWrite(pinNumber, HIGH);
              }
              pinNumber = 0;
              break;
              
            }
         }
         break; 
       }
      }
    }
    
    long getSerial()
    {
      serialdata = 0;
      while (inbyte != '/')
      {
        inbyte = Serial.read(); 
        if (inbyte > 0 && inbyte != '/')
        {
         
          serialdata = serialdata * 10 + inbyte - '0';
        }
      }
      inbyte = 0;
      return serialdata;
    }


    Here is the new code minus new variables:

    Code:
    getSerial();
      switch(serialdata)
      {
      case 1:
        {
          //analog digital write
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              analogRate = serialdata;
              pinMode(pinNumber, OUTPUT);
              analogWrite(pinNumber, analogRate);
              pinNumber = 0;
              break;
            }
          case 2:
            {
              //digital write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              digitalState = serialdata;
              pinMode(pinNumber, OUTPUT);
              if (digitalState == 0)
              {
                digitalWrite(pinNumber, LOW);
              }
              if (digitalState == 1)
              {
                digitalWrite(pinNumber, HIGH);
              }
              pinNumber = 0;
              break;
              
            }
         }
         break; 
       }
      }


    This may look very complex but its function is simple turn analog/digital ports on and off. As you can see every time the getSerial(); command is called the value stored in serialdata is updated. That allows us to check if it is a known command or assign it to a variable depending on what part of the loop we are at.

    With this code added sending a command like '1/2/7/1/' will turn pin 7 on HIGH
    Sending '1/2/7/0/' would turn it off
    Sending '1/1/7/255/' would turn pin 7 on at a analog rate of 255 or full power
    And so on...

    Each case statement has a getSerial(); statement before it so that when it determines which case the user wants to run the data us up to date.
    The first two numbers are fed into switch statements each switch branches of to the correct commands for the numbers received.
    The third number received is stored in pinNumber and that is used to initialize the pin and write to the pin. The fourth number received is the state between 1-2 for digital and 0-255 for analog.

    That covers the important points of that function now lets move on to reading values. As I said I will not really go over the code line by line but instead brush over the important parts concerning serial.

    Now lets add our analog/digital read code.

    Code:
    unsigned long serialdata;
    int inbyte;
    int digitalState;
    int pinNumber;
    int analogRate;
    int sensorVal;
    
    void setup()
    {
      Serial.begin(9600);
    }
    
    void loop()
    {
      getSerial();
      switch(serialdata)
      {
      case 1:
        {
          //analog digital write
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              analogRate = serialdata;
              pinMode(pinNumber, OUTPUT);
              analogWrite(pinNumber, analogRate);
              pinNumber = 0;
              break;
            }
          case 2:
            {
              //digital write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              digitalState = serialdata;
              pinMode(pinNumber, OUTPUT);
              if (digitalState == 1)
              {
                digitalWrite(pinNumber, LOW);
              }
              if (digitalState == 2)
              {
                digitalWrite(pinNumber, HIGH);
              }
              pinNumber = 0;
              break;
             
            }
         }
         break; 
        }
        case 2:
        {
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog read
              getSerial();
              pinNumber = serialdata;
              pinMode(pinNumber, INPUT);
              sensorVal = analogRead(pinNumber);
              Serial.println(sensorVal);
              sensorVal = 0;
              pinNumber = 0;
              break;
            } 
          case 2:
            {
              //digital read
              getSerial();
              pinNumber = serialdata;
              pinMode(pinNumber, INPUT);
              sensorVal = digitalRead(pinNumber);
              Serial.println(sensorVal);
              sensorVal = 0;
              pinNumber = 0;
              break;
            }
          }
          break;
        }
      }
    }
    
    long getSerial()
    {
      serialdata = 0;
      while (inbyte != '/')
      {
        inbyte = Serial.read(); 
        if (inbyte > 0 && inbyte != '/')
        {
         
          serialdata = serialdata * 10 + inbyte - '0';
        }
      }
      inbyte = 0;
      return serialdata;
    }

    And here is what was added minus new variables:

    Code:
    case 2:
        {
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog read
              getSerial();
              pinNumber = serialdata;
              pinMode(pinNumber, INPUT);
              sensorVal = analogRead(pinNumber);
              Serial.println(sensorVal);
              sensorVal = 0;
              pinNumber = 0;
              break;
            } 
          case 2:
            {
              //digital read
              getSerial();
              pinNumber = serialdata;
              pinMode(pinNumber, INPUT);
              sensorVal = digitalRead(pinNumber);
              Serial.println(sensorVal);
              sensorVal = 0;
              pinNumber = 0;
              break;
            }
          }
          break;
        }
    As you can see the same principles used in the first one apply to this.
    First two numbers determine which case we want.
    Third number is stored in pinNumber and used to determine which pin to read.

    However a statement I introduced back at the beginning is added Serial.println(); this is used to print the sensors value back to the computer via serial. If you are using serial monitor when running a command like '2/1/7/' you should get a value.

    That sums up the analog/digital read one more step to go!

    The servo code is the most complex in my opinion and I may have done things poorly/strangely so feel free to leave a comment asking WTF and recommending a better way to do things.

    Code:
    #include <Servo.h>
    
    unsigned long serialdata;
    int inbyte;
    int servoPose;
    int servoPoses[80] = {};
    int attachedServos[80] = {};
    int servoPin;
    int pinNumber;
    int sensorVal;
    int analogRate;
    int digitalState;
    Servo myservo[] = {};
    
    void setup()
    {
      Serial.begin(9600);
    }
    
    void loop()
    {
      getSerial();
      switch(serialdata)
      {
      case 1:
        {
          //analog digital write
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              analogRate = serialdata;
              pinMode(pinNumber, OUTPUT);
              analogWrite(pinNumber, analogRate);
              pinNumber = 0;
              break;
            }
          case 2:
            {
              //digital write
              getSerial();
              pinNumber = serialdata;
              getSerial();
              digitalState = serialdata;
              pinMode(pinNumber, OUTPUT);
              if (digitalState == 1)
              {
                digitalWrite(pinNumber, LOW);
              }
              if (digitalState == 2)
              {
                digitalWrite(pinNumber, HIGH);
              }
              pinNumber = 0;
              break;
             
            }
         }
         break; 
        }
        case 2:
        {
          getSerial();
          switch (serialdata)
          {
          case 1:
            {
              //analog read
              getSerial();
              pinNumber = serialdata;
              pinMode(pinNumber, INPUT);
              sensorVal = analogRead(pinNumber);
              Serial.println(sensorVal);
              sensorVal = 0;
              pinNumber = 0;
              break;
            } 
          case 2:
            {
              //digital read
              getSerial();
              pinNumber = serialdata;
              pinMode(pinNumber, INPUT);
              sensorVal = digitalRead(pinNumber);
              Serial.println(sensorVal);
              sensorVal = 0;
              pinNumber = 0;
              break;
            }
          }
          break;
        }
        case 3:
        {
          getSerial();
          switch (serialdata)
          {
            case 1:
            {
               //servo read
               getSerial();
               servoPin = serialdata;
               Serial.println(servoPoses[servoPin]);
               break;
            }
            case 2:
            {
               //servo write
               getSerial();
               servoPin = serialdata;
               getSerial();
               servoPose = serialdata;
               if (attachedServos[servoPin] == 1)
               {
                 myservo[servoPin].write(servoPose);
               }
               if (attachedServos[servoPin] == 0)
               {
                 Servo s1;
                 myservo[servoPin] = s1;
                 myservo[servoPin].attach(servoPin);
                 myservo[servoPin].write(servoPose);
                 attachedServos[servoPin] = 1;
               }
               servoPoses[servoPin] = servoPose;
               break;
            }
            case 3:
            {
              //detach
              getSerial();
              servoPin = serialdata;
              if (attachedServos[servoPin] == 1)
              {
                myservo[servoPin].detach();
                attachedServos[servoPin] = 0;  
              }
            }
          }
        break;
        }
      }
    }
    
    long getSerial()
    {
      serialdata = 0;
      while (inbyte != '/')
      {
        inbyte = Serial.read(); 
        if (inbyte > 0 && inbyte != '/')
        {
         
          serialdata = serialdata * 10 + inbyte - '0';
        }
      }
      inbyte = 0;
      return serialdata;
    }

    Here is the added code minus the variables:


    Code:
     case 3:
        {
          getSerial();
          switch (serialdata)
          {
            case 1:
            {
               //servo read
               getSerial();
               servoPin = serialdata;
               Serial.println(servoPoses[servoPin]);
               break;
            }
            case 2:
            {
               //servo write
               getSerial();
               servoPin = serialdata;
               getSerial();
               servoPose = serialdata;
               if (attachedServos[servoPin] == 1)
               {
                 myservo[servoPin].write(servoPose);
               }
               if (attachedServos[servoPin] == 0)
               {
                 Servo s1;
                 myservo[servoPin] = s1;
                 myservo[servoPin].attach(servoPin);
                 myservo[servoPin].write(servoPose);
                 attachedServos[servoPin] = 1;
               }
               servoPoses[servoPin] = servoPose;
               break;
            }
            case 3:
            {
              //detach
              getSerial();
              servoPin = serialdata;
              if (attachedServos[servoPin] == 1)
              {
                myservo[servoPin].detach();
                attachedServos[servoPin] = 0;  
              }
            }
          }
        break;
        }

    There it is! Full serial control of analog/digital, servos, and analog/digital read. You will see in the servo code there is three cases, that is because there is an attach/write function, detach function, and a read function that returns the current position of a servo or a '0' if its not set.

    I will go over the variables for the final piece of servo code just because there are so many.

    int servoPoses[80] = {};
    Stores the current position of all the servos

    int attachedServos[80] = {};
    If a servo is attached the value corresponding to the pin number is set to one; this is important so you don't attach the same servo twice.

    Servo myservo[] = {};
    Stores all the servo object in an array so they are easy to access

    int servoPin;
    Pin of the servo the user wants

    int servoPose;
    Position the user wants the specified servo to go to

    That sums up the coding part of this tutorial.


    Tips & Tricks


    Finally I would like to go over some key concepts/extra information you may need.

    Most importantly is the serial bus length. The arduino has a serial bus length of I believe 128bits but for the life of me I could not find it on google. If you send to many numbers at once and the arduino dose not read them fast enough it will overflow and any data you pile on it will be lost! A good method to prevent this is called handshaking where the arduino asks the computer for data then the computer sends sends it, and the process repeats... I may go over that in a new tutorial but this one is getting to long.

    You may have noticed that I never really went over the computer side of the equation besides mentioning the serial monitor on the arduino IDE. The main reason is there are tons of program languages and they are all different. Making a example for each one would be very difficult.

    However I will include two programs in the attached file with the arduino code made in python. On lights up lights sequentially on pins 1-13 in the "knight rider" style. The other cycles two servos between 0-180 degrees. To run the programs you will need pyserial installed. Also make sure you edit s = serial.Serial(2, 9600, timeout = 2) to reflect your serial port -1 .

    Here is a video of the "knight rider" style led display:

    [youtube]http://www.youtube.com/watch?v=gZtYLvO5PwU[/youtube]

    Thanks for reading!

    There is an error with the youtube video

    For now the youtube link is:

    http://www.youtube.com/watch?v=gZtYLvO5PwU

    Sorry!


    Attached Files
    • File Type: zip code (1.34 KB, 557 views)
    Dalton Caughell


Page 1 of 2 12 LastLast
Replies to Tutorial: Complete Control Of An Arduino Via Serial
  1. Join Date
    Dec 2008
    Location
    Washington
    Posts
    312

    Re: Advanced Control Of An Arduino Via Serial

    Problem fixed
    Dalton Caughell
        

  2. Join Date
    Mar 2011
    Location
    Kaliningrad
    Posts
    1

    Re: Complete Control Of An Arduino Via Serial

    What about Firmata protocol?
        

  3. Join Date
    Dec 2008
    Location
    Washington
    Posts
    312

    Re: Complete Control Of An Arduino Via Serial

    Firmata protocol, "which I researched quickly before starting this project" seems to be only for low level control of the arduino. The intent of my project was not low level control "although it is featured in the main examples" instead when I created the the code my intent was an easy way to call and pass variables to complex "functions" on the arduino.

    For example a specific motor drivers requires a specific signal type and style. Using my method all you could wright a complex unique motor control "function" for sending commands and keeping track of speed. All the computer would do is send a few numbers i.e. speed, duration, acceleration then the arduino would do all the complex calculations, pid, and communications.

    It may be possible to do the same thing using firmata but with much more unnecessary computer involvement and in the end much slower speeds.

    I hope this clarifies why you would choose a method like this over firmata, but I still feel I have done a bad job explaining it.
    Dalton Caughell
        

  4. Trossen Dynamixel High Thread Banner
  5. yeeho Guest

    Re: Complete Control Of An Arduino Via Serial



    Thank you for the tutour, but I dont get it work. I was using command '1/2/7/1/' in arduino IDE, but cant turn on pin7

  6. sojiro Guest

    Re: Complete Control Of An Arduino Via Serial

    Sir, thank you for your tutorial...why is that it doesn't have have output in my arduino! no lights "analog/digital" ! tnx..waiting for your reply!

  7. tomsteemson Guest

    Re: Complete Control Of An Arduino Via Serial

    '1/2/7/1/' in arduino IDE, but cant turn on pin7
    Try '1/2/7/2/' to switch on the digital pin 7

  8. tomsteemson Guest

    Re: Complete Control Of An Arduino Via Serial

    Found the answer to this question.....

  9. tomsteemson Guest

    Re: Complete Control Of An Arduino Via Serial

    No worries, I figured it out.

    The Arduino Sketch is exactly that which has been so kindly distributed in this article.

    The Python code I've been playing with looks like this:

    Code:
    import serial
    import time
    
    arduino=serial.Serial('/dev/ttyACM0', baudrate = 9600, timeout = 2)
    
    D3High = '1/2/3/2/'
    D3Low = '1/2/3/1/'
    A0Read = '2/1/0/'
    
    led = 'H'
    x = 800
    
    arduino.write(D3High)
    
    while 1:
        
        arduino.write(A0Read)
        time.sleep(0.02)
        s = arduino.readline()
        
        try:
            x = int(s)
        except ValueError, TypeError:
            x = x
        
        if ((x > 500) and (led == 'H')):
            print 'high level ' + led + ' Reading: ' + str(x)
            led = 'L'
            arduino.write(D3High)
        elif ((x < 500) and (led=='L')):
            print 'low level ' + led + ' Reading: ' + str(x)
            led = 'H'
            arduino.write(D3Low)
    The project uses a light-beam sensor that is sitting on analogue pin 0, with an LED that is operating as a visual indicator. When the beam is broken, the LED is lit. When the beam is clear, then the LED goes out. In this project then there are effectively only two states to worry about. However, the sensor could be anything else. A temperature sensor for example.

    Because there are only two states, rather then have to constantly write to the Arduino to tell it to put the light on, or switch it off, the code to change the state of the led is only called at a change of state. That cuts down on the amount of work that everything is doing, and the amount of traffic running over the serial line. I'm using a flag <led> that remembers what state the LED was last set to. When the return value of the analogue pin <x> is interrogated, if the value has changed such that it invokes a change of state, then the signal to change the state of the LED is sent and the led variable is set to the new state level.

    Let's start at the top of the Python code and work down then:

    Having imported the serial and time libraries, the serial connection is set up. I'm using a Raspberry Pi to communicate with the Arduino Uno over the USB cable, hence the '/dev/ttyACM0' parameter at the beginning of the serial.Serial set-up.

    I'm now setting up the variables with the code sequences I need to pass to the Arduino. Setting variables populated with the predefined codes rather then using a string of digits every time makes Python script so much easier to code up and read. I'm going to assume that the beam is open at initiation, so I'll set <x> to a high figure and <led> to a high state. I'll then pass the Digital pin 3 High code <D3High> to the Arduino and switch on the LED.

    Now I'm going to loop till I drop!

    I write the <A0Read> request to the Arduino, then read what comes back. I'm slowing the process down <time.sleep(0.02)> there in this example, but I'm not entirely sure that's necessary. Need a bit more experimentation there.

    I'm then going to try and convert the serial string that is returned from the <arduino.readline()> request in to an integer. I found that the readline call could sometimes return some spurious answers seemingly unrelated to what I was after, especially at start-up. The <try:> statement ignores anything that's not numeric, so the balance of the program is considerably more reliable. As a result the numeric value of the analogue pin <x> is either set to a valid value, or remains set to the previous valid value.

    The last block of code is the section that interrogates the value of <x> in association with the value of <led> and makes the state change to the program if required.

    Some notes here:

    There appears to be some conflicting advice with regards the code sequences required to operate the system in the body of the tutorial. The observant among you may have spotted that the codes I'm using to control the Arduino don't follow the pattern detailed. I've not tried using the Servo parameters, but with regards reading and writing to digital and analogue pins, the code sequencing appears to work like this:

    Reading from Pins:
    Sequence 'RW/AD/PN' where 'RW = Read Right / AD = Analogue or Digital / PN = Pin Number)
    RW = 1 for Write or 2 for Read
    AD = 1 for Analogue or 2 for Digital
    PN = The target pin number
    To READ / ANALOGUE / PIN 0 the code is '2/1/0/'

    Writing to Pins:
    Sequence 'RW/AD/PN/VA' where 'RW = Read Right / AD = Analogue or Digital / PN = Pin Number / VA = Value)
    RW = 1 for Write or 2 for Read
    AD = 1 for Analogue or 2 for Digital
    PN = The target pin number
    VA = (Digital Value is 1 for low or 2 for high) (Analogue Value is between 0 and 1024)
    To WRITE / DIGITAL / PIN 3/ HIGH the code is '1/2/3/2'



    Just wanted to thank ROBOTMAN for his sterling work here. I bought a Raspberry Pi a few weeks ago, and was having great fun with it, but it's limitations with regards dealing with the real world gave me some grief. Having discovered the Pi, I was quickly introduced to the Arduino. Now I've got the power of a Linux O/S driving a device that is designed specifically for dealing with the outside world. Can't wait to get deeper in to the possibilities that this combination of processing power offers. Complete Control of an Arduino Via Serial; I couldn't have wished for a more suitable marriage of technologies. Thanks ROBOTMAN.



  10. dvl12 Guest

    Re: Complete Control Of An Arduino Via Serial

    I found your article very useful, but I would like to ask one question on how I can personalize it for my code. How can I make it so when I write something like: 10//9/ ;the missing number between // will just leave variable as it was? This way I want to make the process of inputting variables faster and also I would not have to keep the number in my head if I just want to keep it constant. Here is my code to get better understanding of the problem:
    Code:
    #include <Servo.h>
    
    
    Servo servo_1;
    Servo servo_2;
    Servo servo_3;
    
    
    //introducing variables
    unsigned long serialdata;
    int inbyte;
    
    
    int V = 3; // variable to control speed  
    int Ampl = 180; // variable to control aplitude
    int Pd = 0.1; // phase difference variable from 0 to 1
    
    
    int pos1 = 180;
    int pos2 = 90;
    int pos3 = 90;
    
    
    void setup()
    {
      servo_1.attach(9, 800, 2200); 
      servo_2.attach(10, 800, 2200);
      servo_3.attach(11, 800, 2200); 
      
      Serial.begin(9600);
      Serial.setTimeout(50);
    }
    
    
    void loop()
    {
      while (Serial.available() > 0)
      {
      getSerial();
      int V1 = serialdata;
      getSerial();
      int Ampl1 = serialdata;
      getSerial();
      int Pd1 = serialdata;
      
      V = V1;
      Ampl = Ampl1;
      Pd = Pd1;
      }
      
      float value = millis()/(V*400.0);
       
      pos1 = 90.0+(Ampl/2)*sin((value+Pd)*PI);
      servo_1.write(pos1);
      
      pos2 = 90.0-(Ampl/2)*sin(value*PI);
      servo_2.write(pos2);
           
      pos3 = 90.0+(Ampl/3)*sin(value*PI);
      servo_3.write(pos3);
    }
    
    
    long getSerial()
    {
      serialdata = 0;
      while (inbyte != '/')
      {
        inbyte = Serial.read(); 
        if (inbyte > 0 && inbyte != '/')
        {
          serialdata = serialdata * 10 + inbyte - '0';
        }
      }
      inbyte = 0;
      return serialdata;
    }

  11. Re: Complete Control Of An Arduino Via Serial

    Thank you so much. This was exactly what I was looking for. Great tutorial. Keep up the good work.
        

Reply To Tutorial
Page 1 of 2 12 LastLast