An Introduction To Scripting

 
#1

All script examples are on the EZ-Cloud for download and updated after each additional post Download Now

Index
Part 1 - You are here
Part 2 - Page 1 Post #6
Part 3 - Page 1 Post #8
Part 4 - Page 2 Post #6
Part 5 - Page 2 Post #9
Part 6 - Page 3 Post #5
Part 7 - Page 4 Post #3

I have some time on my hands and nobody seems to be asking questions at the moment so I though I'd just write up a nice and simple introduction in to EZ-Script. I may write more in the future but we will see how received this is first.

EZ-Script is a powerful scripting language with a wide variety of commands. The command user manual is displayed to the right of the EZ-Script environment within EZ-Builder and a PDF version of the manual is maintained and downloadable from here.

Scripting needn't be difficult. A lot of people see lines and lines of code in the examples and are overcome by this however the number one rule in scripting, at least for me is to think logically, dumb it down and tackle one piece at a time.

I will go through how to write an example script in a logical manner and have a working script as an end result. In this example we will define variables, look at If statements, control devices attached to the EZB and read data from the EZB.

First we need to work out the algorithm for the task we want to achieve. Think of it as you think of a recipe when making a meal. It's a sequence of events in a specific order to achieve the overall result.

This example we will make our robot scared of contact and run away when anyone or anything comes in close proximity. In order to achieve this there are some hardware requirements;
Drive motors or servos
Proximity detector

For this example we will assume the robot is driven by DC motors via a 4 wire HBridge, with a correctly set-up HBridge Movement Panel in EZ-Builder and for proximity detection a low range infra red sensor connected via the ADC port ADC0.

So first, we need to think about what it is we are doing. We need to know if anything is coming in close proximity to the robot so we need to read the information supplied to the EZB via the infra red detector.

EZ-Script has a neat command for this, the GetADC command;

Quote:


GetADC( Port )
Returns the ADC value of the specified port
Example: GetADC(adc0)



So the first part of our code would be to get that ADC value and store it in a variable for ease of use. We will call this variable $proxsense. Notice the $ symbol preceding the variable name, this is required for all variables.

Code:


$proxsense = GetADC(adc0)


The value of ADC0 is now stored as the variable $proxsense.

So now that we have the value of the proximity sensor we need to use it. We need to know if someone or something is in close proximity to the robot, so we need to compare the value of ADC0 to a known close proximity ADC value. This varies on different hardware and testing may be required for accurate results. For the benefit of this example we will assume an ADC value of 50 or less is close proximity.

So let's add this as another variable;

Code:


$proxclose = 50



Easy right? It doesn't get much harder either Smile

So we have the current sensed proximity value and the minimum allowed proximity value stored as variables. We want to be able to do something if the sensed proximity is more than the maximum allowed proximity. If you read that sentence you may be able to work out the next command we need to look at...

The IF command;

Quote:


If (Value Condition Value )
IF condition can support multiple comparisons and functions.
Condition tree must be closed with an ENDIF
See the Functions section of this document.
Condition can be =, , =, , AND, OR
Example:
If (GetDigital(D0) = 1)
Print("One")
EndIf
Example:
If ($Day = 2 AND $Hour = 3)
Print("Hello")
EndIf



So let's look at this logically, we have the sensed proximity, the maximum allowed proximity and want to see if one is higher than the other.

Code:


IF($proxsense > $proxclose)
ENDIF



This will look at $proxsense, check it against $proxclose and if it is greater than (>) $proxclose it will run the next commands. If not then it will jump down to the endif command. All if statements must be finished by an endif command.

So the code so far should look like this;

Code:


$proxsense = GetADC(adc0)
$proxclose = 50
IF($proxsense > $proxclose)
ENDIF



While the code will compare the values it still doesn't do anything. Nothing has been defined in the if statement. But what we have achieved is for the script to now be able to find out if someone, or something is in close proximity to the robot.

So now we want to have the robot do something if the statement is true. The next question is what do we want it to do if someone or something is too close? We want it to run away, all scared and full of panic.

So how do we do that?

First we decide if we want the robot to just reverse away, as if running backwards. We could, but do you run backwards? No, you turn and run. So we will make the robot turn 180 degrees (roughly) and run away.

How do we do this in EZ-Script? Easily!..

EZ-Script has all five of the Movement Panel commands. Forward, Reverse, Stop, Left and Right;

Quote:


Forward( [speed], [milliSeconds] )
Using a Movement Panel Control, this will start your robot in the Forward direction.
Optionally, you can provide the speed and/or number of milliseconds to move.
You will require at least one Movement Panel to be configured within the project. This function will control that Movement Panel.
Speed is a number between 0 and 255
Example: Forward()
Example: Forward(200)
Example: Forward(255, 5000)

Reverse( [speed], [milliSeconds] )
Using a Movement Panel Control, this will start your robot in the Reverse direction.
Optionally, you can provide the speed and/or number of milliseconds to move.
You will require at least one Movement Panel to be conf igured within the project. This func tion will control that Movement Panel.
Speed is a number between 0 and 255
Example: Reverse()
Example: Reverse(200)
Example: Reverse(255, 5000)

Stop()
Using Movement Panel Control, this will stop your robot.
You will require at least one Movement Panel to be configured within the project. This
function will control a Movement Panel.
Example: Stop()

Left( [speed], [milliSeconds] )
Using a Movement Panel Control, this will turn your robot left.
You will require at least one Movement Panel to be configured within the project. This function will control that Movement Panel.
Optionally, you can specify the speed and/or number of milliseconds to turn.
Speed is a number between 0 and 255
Example #1: Left()
Example #2: Left(200)
Example #2: Left(200, 5000)

Right( [speed], [milliSeconds] )
Using a Movement Panel Control, this will turn your robot right.
You will require at least one Movement Panel to be configured within the project. This function will control that Movement Panel.
Optionally, you can specify the speed and/or number of milliseconds to turn.
Speed is a number between 0 and 255
Example #1: Right()
Example #2: Right(200)
Example #2: Right(200, 5000)



Direction of turn doesn't matter so either the left or the right command would be suitable. For the purpose of this example we will turn left. To turn 180 degrees is not an exact science since the robot has no means of knowing it's direction. Turning is time based and this must be calculated by timing your robot for how long it takes to rotate 180 degrees. For the purpose of this script we will assume the robot rotates 1 degree in 0.1 milliseconds or 180 degrees in 1800 milliseconds.

Code:


LEFT(255,1800)



Now that the robot has done an about turn and is facing away from the scary thing in front of it it needs to run forwards to get away. So a forward command is issued. We will make it run away for 5 seconds.

Code:


FORWARD(255,5000)



And that's it. As simple as that. So the code so far should look like;

Code:


LEFT(255,1800)
FORWARD(255,5000)



Now let's put it all together. We can do this a couple of ways. One way would be to put the left and forward commands inside the if statement, and if you have a basic script there is nothing wrong with that at all but I want to show you the Label, Goto and Return commands too;

Quote:


:Label
Defines a label for a GOTO() command
Example: :My_Label

Goto( label )
Goto a specific :Label location
Example: Goto(My_Label)

Return()
Return from a Goto()
If you jump to a position of code with a Goto(), the Return statement will allow you to return back to that piece of code following the last Goto()
If you attempt to Return() with an empty stack, nothing will happen. The script will ignore the Return() statement.
Example: Return()



These commands can be used to set up what I like to call Sub Routines. A small piece of code which can be called up from many places within the code. These are great if your script is long and manages many different tasks that all may require the same sub routine.

So we set up the "runaway" sub routine;

Code:


:runaway
LEFT(255,1800)
FORWARD(255,5000)
RETURN()



And then we add in the Goto to the first part of the code we wrote;

Code:


$proxsense = GetADC(adc0)
$proxclose = 50
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF



And when we put it all together;

Code:


$proxsense = GetADC(adc0)
$proxclose = 50
IF($proxsense < $proxclose)
GOTO(runaway)
ENDIF

:runaway
LEFT(255,1800)
FORWARD(255,5000)
RETURN()



At this point there is an obvious problem. Once the script runs and the if statement is run it follows on to run the runaway code, regardless of the outcome of the if statement. It also only runs once. This is taken care of with another label and another goto command. The label at the start of the script and a goto after the endif. This loops the script around forever.

The code should now look like this.

Code:


:loop
$proxsense = GetADC(adc0)
$proxclose = 50
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
GOTO(loop)

:runaway
LEFT(255,1800)
FORWARD(255,5000)
RETURN()



The last thing to tackle is the demand the script above may have on the hardware and your PC. Reading from the ADC port can be demanding on the system. The way to overcome this is with a Sleep command, which pauses the script for the desired time.

Quote:


Sleep (milliseconds)
Pauses for specified milliseconds
Example sleeps for 1 second: Sleep(1000)



To restrict the number of reads of the ADC port we will add in the sleep right before it loops around, giving a sleep command for 1 second.

Code:


:loop
$proxsense = GetADC(adc0)
$proxclose = 50
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP(1000)
GOTO(loop)

:runaway
LEFT(255,1800)
FORWARD(255,5000)
RETURN()



And there we have it, the completed script. It's simple yet effective. The script will run, it will then check the value of ADC0, check if this is less than the minimum allowed value, if it is then it will jump down to the "runaway" sub routine, turn left, run forwards and return back to the main script. If not then it just loops around and around forever.

This post has not been written with the intention of showing a detection script but more for showing how to tackle writing any script. I hope you can now take what I have shared above and start creating your own scripts and routines to make the robot perform the tasks you require it to perform.

If you have any specific tasks that you would like to achieve but cannot quite figure out where to start please mention them and we can all discuss the way to do this, break it down piece by piece like the above and find you your solution.

#2

Thanks so very much Rich. This is exactly what I need. I am at the point now in my build where this will help me bring my project to life. All I need is a guiding hand.

Looking forward to more of your script classes and working with you on the Auto Docking Project Smile

Rex

#3

All I need is ideas, I'm itching to write some scripts. Although prefer giving everyone a menu and saying "pick your own meal" than spoon feeding them. It's just my way of giving back to this awesome community.

While I haven't used many yet I'll look in to doing something with all of the global variables such as the $direction, $CameraIsTracking etc. but first I need to use them and understand them myself (which wont take long, I pick stuff really quickly)

The auto docking project should be an interesting script, it needs to do a lot all at the same time and will use a few of the global variables. I'm really excited to start working on that one but until the navigation method is sorted I can't do more than the battery monitor part.

#4

This is an excellent idea
now our very own in house scripting lesson

well done

#5

Give a man a fish, he'll eat for a day.
Teach a man to script, and he'll code his robot to fish and possibly take over the world.
Grin

#6

Part 2: Script versatility

If anyone has ever looked at the scripts I have written and shared (or even the ones I don't share yet) they will notice that I make them as versatile as is possible so that anyone can pick them up and use them with minimum of effort.

Not only is this done for versatility but it is also done to make my life easier when it comes to testing out the script on the robot. There is nothing worse than having to go through hundreds of lines of scripts and change every line where a sleep value or the ms of a movement command.

So I'm going to explain how I do this.

We will use the example script from the first post;

Code:


:loop
$proxsense = GetADC(adc0)
$proxclose = 50
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP(1000)
GOTO(loop)

:runaway
LEFT(255,1800)
FORWARD(255,5000)
RETURN()



If we look at it there are a lot of values in this tiny script that may need to be changed for someone else to use it on their robot. The first step is to look at it and pick out all of these values.

Working our way down from the top to the bottom we have;
An ADC port
The minimum distance
The delay for the main loop
The turn speed
The turn length
The forward speed
The forward length

All of these can be replaced for variables at the start of the script which are easy to locate and change depending on your requirements.

I start a script off with the following as a rule;

Code:


# Script Title
# Script Version

# Script configuration - change the values below to suit



And then a bunch of variables with comments to explain each one.

Code:


# Scaredy Cat Robot Script
# Version 1.1

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms



After which I usually place another comment warning the user not to change anything below. And then the script with the newly added variables inserted where the values once were.

Code:


# Scaredy Cat Robot Script
# Version 1.1

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms

# Do not change anything below this line

:loop
$proxsense = GetADC($adcport)
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP($delay)
GOTO(loop)

:runaway
LEFT($turnspeed, $turnlength)
FORWARD($forwardspeed, $forwardlength)
RETURN()



And now the code can be easily adjusted by anyone to suit their specific robot, environment etc. While it may not look like that big a deal with the above script I'm sure you can imagine just how much of a time saver it can be for a large script with many lines of code, various IF statements to perform actions which all have similar values - for instance turning left or right depending on the surroundings.

Next time... Adding in options to either run or not run based on configuration variables and we will add in more sensors and turn in a direction to suit the values of the sensors. This will also show the importance of the variables added in this post Smile.

Again, this is not intended to provide a working avoidance script but show an example of how to write any script.

#7

from looking at your examples RICH it looks good,will have to try them soon

#8

Part 3 - Options

Again, any who have seen my scripts will notice that some have options. For instance an option for reversing before turning in the Object Avoidance script, or for testing in the Ping Sensor/Sweep script.

These options can be very useful when sharing scripts. Not everyone will want their robot to act the same so giving an option of only working if moving forward, reversing before turning or even turn direction makes scripts extremely versatile.

Another advantage is having a $testmode option if like me you write the scripts before physically building the robot. I first showed this in the Ping Sensor/Sweep script so that I could emulate the readings from the Ping sensor without having one set up and then could view the behaviour of the robot.

On second thoughts I will hold off on the $testmode until another post. Please ignore any references to it which I may have missed below.

How this works is very simple. First you set your configuration variables at the start of the script.

Code:


$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on



So now the code should look like;

Code:


# Scaredy Cat Robot Script
# Version 1.2

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms
$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on

# Do not change anything below this line

:loop
$proxsense = GetADC($adcport)
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP($delay)
GOTO(loop)

:runaway
LEFT($turnspeed, $turnlength)
FORWARD($forwardspeed, $forwardlength)
RETURN()



Alone those variables would do nothing but what they do add is the ability to run an IF condition on specific portions of the script and only execute that part if the conditions are met.

We will change the script to check if it should only work when moving forwards. For this we need to add an IF around the main portion of the code.

Code:


IF($onlyforwards=1)
ENDIF



Anything between the IF and the ENDIF will only be executed if the conditions within the if, $onlyforwards=1 is met.

So the code now looks like;

Code:


$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on



So now the code should look like;

Code:


# Scaredy Cat Robot Script
# Version 1.2

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms
$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on

# Do not change anything below this line

:loop
IF($onlyforwards=1)
$proxsense = GetADC($adcport)
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP($delay)
ENDIF
GOTO(loop)

:runaway
LEFT($turnspeed, $turnlength)
FORWARD($forwardspeed, $forwardlength)
RETURN()



With the current configuration the main loop will not execute the code to check the sensor or to runaway. There is currently no code for the script to know it is moving forwards. So we need to add it. There is a global variable available called $direction which stores the current direction of movement, either "forward", "reverse", "left", "right" or "stop". So we need to use this and check that the robot is moving forwards. Another IF condition to add in.

Code:


IF($direction="forward")
ENDIF



There are two ways in which we can add this to the script. The first way is to add the condition to the $onlyforwards IF using an AND operator.

Code:


IF($onlyforwards=1 and $direction="forward")
ENDIF



Or how I prefer to do it, by nesting another if inside the $onlyforwards IF.

Code:


IF($onlyforwards=1)
IF($direction="forward")
ENDIF
ENDIF



Reasons why I prefer the second option, despite it resulting in more code is it allows for future additions and changes much easier than when using the first option. It also reduces the length of the line of code however that is purely an aesthetic reason.

For this example I will use the second option (for reasons which will become apparent in future updates). So the code now needs to include the two statements.

Code:


# Scaredy Cat Robot Script
# Version 1.2

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms
$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on

# Do not change anything below this line

:loop
IF($onlyforwards=1)
IF($direction="forward")
$proxsense = GetADC($adcport)
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP($delay)
ENDIF
ENDIF
GOTO(loop)

:runaway
LEFT($turnspeed, $turnlength)
FORWARD($forwardspeed, $forwardlength)
RETURN()



But now what happens if the $onlyforwards variable is set to 0 so it works all of the time? No code would be executed and it would loop from :loop to GOTO(loop) infinitely. Not a useful script. So we need to take out the sensor checking code and put it as another sub routine, this allows for us to call it with a GOTO rather than having to write the code multiple times.

So take out the code and make it a sub routine, and add in the GOTO where the code once was. And also add in the ELSE for when the variable isn't set for it to only execute when moving forwards.

Code:


# Scaredy Cat Robot Script
# Version 1.2

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms
$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on

# Do not change anything below this line

:loop
IF($onlyforwards=1)
IF($direction="forward")
GOTO(sensorcheck)
ENDIF
ELSE
GOTO(sensorcheck)
ENDIF
GOTO(loop)

:runaway
LEFT($turnspeed, $turnlength)
FORWARD($forwardspeed, $forwardlength)
RETURN()

:sensorcheck
$proxsense = GetADC($adcport)
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP($delay)
RETURN()



Now the code will first check if the configuration is set to only execute while moving forwards, if it is it will check the direction variable and if met will run the sensorcheck code. If, on the other hand the configuration variable isn't set to check only if moving forwards the code goes directly to the sensorcheck code.

We also added in two other variables, $turndirection and $testmode. Let's take a look at turn direction.

The $turndirection is commented with 0 for left and 1 for right. So, in order for the script to know which way we want to turn we need to run a check on that variable in the :runaway code. There is one minor problem which is that the user may enter something other than 0 or 1. Rather than throw an error due to this we will counter the problem by having the robot turn left for anything other than a 1, where it turns right.

This will use the ELSE command rather than an ELSEIF. The ELSE doesn't require any parameters after it, what happens is if the condition of the IF is not met then the code below the ELSE is run.

So let's look at the :runaway code and add in the commands for direction checking and acting accordingly.

Code:


:runaway
LEFT($turnspeed, $turnlength)
FORWARD($forwardspeed, $forwardlength)
RETURN()



First we need to add in the IF and the condition to check for.

Code:


IF($turndirection=1)
# Right Turn
ELSE
# Left Turn
ENDIF



So let's wrap our existing code in a new IF blanket Smile

Code:


:runaway
IF($turndirection=1)
RIGHT($turnspeed, $turnlength)
ELSE
LEFT($turnspeed, $turnlength)
ENDIF
FORWARD($forwardspeed, $forwardlength)
RETURN()



Simple, now it checks the configuration and turns in the right direction. The $turnspeed and $turnlength variables from last time come in to play and allow us to adjust the turn and run by changing a single value rather than the same value multiple times.

The full code now looks like;

Code:


# Scaredy Cat Robot Script
# Version 1.2

# Script configuration - change the values below to suit
$adcport = ADC0 # Change to required ADC port
$proxclose = 50 # Change for minimum distance
$delay = 1000 # Change for delay between reading distance
$turnspeed = 255 # Change for turn speed (1-255)
$turnlength = 1800 # Change for length of turn in ms
$forwardspeed = 255 # Change for forward speed (1-255)
$forwardlength = 5000 # Change for length of forward movement in ms
$turndirection = 0 # Change for turn direction 0 = left 1 = right
$onlyforwards = 0 # Change for if only to run while moving forwards 0 = off 1 = on

# Do not change anything below this line

:loop
IF($onlyforwards=1)
IF($direction="forward")
GOTO(sensorcheck)
ENDIF
ELSE
GOTO(sensorcheck)
ENDIF
GOTO(loop)

:runaway
IF($turndirection=1)
RIGHT($turnspeed, $turnlength)
ELSE
LEFT($turnspeed, $turnlength)
ENDIF
FORWARD($forwardspeed, $forwardlength)
RETURN()

:sensorcheck
$proxsense = GetADC($adcport)
IF($proxsense > $proxclose)
GOTO(runaway)
ENDIF
SLEEP($delay)
RETURN()



Part 4 - Adding in those extra sensors (including options for different types). Coming soon...
Part 5 - Adding test mode and debug flags.

And, as always, this is not intended to provide a working avoidance script but show an example of how to write any script. If you have any questions please ask.

#9

Rich,

Thanks for doing this.

I'm learning lots. Just discovered the world of $variables!

#10

No thanks is necessary (although it is nice to hear/read), you're all very welcome. The more who learn how to script the more scripts should pop up and I'm running short on ideas of scripts for my robot Smile Plus it helps me learn too, thinking about what, how and why commands work makes it stick, I'm now almost to the point I can write scripts from memory (which helps when I'm stuck at work with nothing good to do).

I love my $variables Smile I do end up with a variable watcher that's far too big though but it makes everything so much easier, especially when it comes to fine tuning speeds, times, delays etc.