0

Line Robot - Basic programming

First program

Now, let's make Your first program. In MRMS_ESP32.ino there is function setup(). Locate line "robot = ..." and be sure that it looks like below:

void setup() {
	robot = new RobotLine(); // RobotLine, RobotSoccer, or Your custom robot
	robot->run();
}
The robot must be "RobotLine". There are other possibilities, like RobotSoccer or RobotMaze, which have different functionality predefined, used for other disciplines. You can also define Your own robot but this is a subject for a later phase.

You do not have to understand why this part of the program looks like this. It implements a rather complicated topic in object-oriented programming, known as inheritance.

The .ino program is easily edited with Arduino IDE You installed, but its usage for writing code ends here.

So, where will we actually be coding then? Arduino, besides .ino file, uses libraries. We will choose one and that will be the place.

Unfortunately, Arduino IDE cannot be used for programming libraries so we will need another editor. Let's download and install excellent Notepad++. You do not have to mourn Arduino too much as it really wasn't very good for code editing anyway.

There is an excellent alternative to Arduino IDE, Microsoft Visual Studio, but we will not discuss it here.

You may want to make a shortcut to the library we will be using, C:\Users\[user name]\Documents\Arduino\libraries\mrm-robot\src. [user name] is, of course, Your user name.

Use Windows Explorer to go to the directory: in the left margin, more to the top, find "Documents". Click on this directory name, then, in the right pane, double-click on "Arduino", "libraries", "mrm-robot", and "src".

In this directory are all the files we will be interested in. Now just mrm-robot-line.cpp, but later a few others.

The shortcut will be placed on Your desktop. If You like, You can make (another) shortcut to the mrm-robot-line.cpp file itself, located inside the directory we already made the shortcut for.

Open mrm-robot-line.cpp file in Notepad++. You may want to associate .cpp name with Notepad++ in Windows Explorer so that double-clicking on mrm-robot-line.cpp opens Notepad++ automatically.

Your first program: millis()

Usually the first program is "Hello world" but we will change it a little by making a program that shows scrolling time in milliseconds. Find function "loop()". Do not change anything else except the text inside curly brackets ({}). If You change something else, the program may break, giving You a hard time trying to fix it. If that happens, by far the best cure will be "undo" command (Ctrl-Z). So, change the text and the function should look like:
void RobotLine::loop() {
	print("%i\n\r", millis());
}
Save changes. Do not forget this step! The changes are not saved automatically. You must do that before compiling the program. Otherwise the old program will be uploaded into the robot, making You wondering why it is not working.

Open Arduino IDE, load and run MRMS_ESP32.ino sketch. Open terminal. Type "x" and "Send" to get menu.
Type "loo" and "Send". The program will be scrolling current time, milliseconds since starting the microprocessor. You can stop it with "x" command.

loop is a C++ function. In our top-down approach we will try to stay on the top as long as possible. Hence, not explaining the details. If a "function" term is not clear to You, it will be a good idea to study a basic C++ course. Explaining all the details would clutter the text, obfuscating the concepts.

So, loop() is a function and is conceptually similar to Arduino's loop() function: it is being run constantly. This is a crucial concept. It is not run once, but continually, pass after pass. That is the reason time is being displayed in many lines.

print() is another function, used for displaying some text in all the connected terminals. If follows C-like format used in printf function and that is the reason there is the cryptic text "%i\n\r", meaning:
  1. display the first integer variable after format string ("%i"),
  2. jump to new line ("\n"),
  3. return cursor to the beginning of the line ("\r").
It is not that hard. There are a ton of examples in internet, like this one. Here we encountered a big C++ problem. It is an old language, lacking many very handy features new variants (C#, Java, Python, etc.) possess. This is definitely not a beginner-friendly language and we will be coping with problems, like this format string, many times. That is also a reason we want to stay on the top of the top-down approach, to shield You from the traps.

On the other hand, C++ is the language of robotics. It is highly beneficial to learn it. It has a huge code base, is extremely fast, and produces very small code. The last feature is the most important. The languages with a lot of nice-to-have features cannot do that, and their compiled code simply will not fit into a microcontroller. Even C++ has newer variants that replace the mentioned format string, but the code will be much bigger. That is the reason why Arduino C++ does not support it.

IMU

millis() is an Arduino function and any Arduino board can execute it. Let's try a more advanced function that interacts with hardware. There is a peripheral device called IMU (Inertial Measurement Unit), featuring a compass, accelerometer and gyroscope, enabling us to measure robot's direction and inclination against its vertical axis. Change loop():
void RobotLine::loop() {
	print("%i\n\r", (int)heading());
}
The program is very similar to the last one. Look at the new expression "(int)heading()":
  1. "(int)" changes floating point (decimal) number into an integer (no decimals) - yes, Arduino cannot display decimals,
  2. "heading()" is the function returning compass' value, in our example 341º.

Motors

A mobile robot always has motors so let's learn how to control them. Change loop() again:

void RobotLine::loop(){
	go(50, 90);
}
Upload the program and start it with menu command "loo" again. The function go() follows the pattern we introduced in the last paragraph, although "motorGroup" is not a peripheral hardware itself, but that doesn't matter. Remember this command, which has format, for our robot:

go(speedOfLeftMotors, speedOfRightMotors); // Speeds are in interval [-127, 127]

Therefore, speed of the left 2 motors will be 50 and of the right ones 90. As the right wheels rotate faster, the robot should go to the left. Well, maybe not. If depends on the direction of rotation of every wheel: clockwise (CW) or counterclockwise (CCW). Even if You are lucky and all the motors spin in the right direction, look at the procedure how to correct directions.

By now, the only function we were talking about was loop(), similar to Arduino's loop(). Arduino sketch must contain at least 2 functions. Besides loop(), there is another one setup(). Contrary to loop(), which runs constantly, setup() is executed only once, when the program begins. As this initialization functionality is universally necessary, ML-R has a similar one. Browse the code in mrm-robot.cpp and find the function:
RobotLine::RobotLine(char name[]) : Robot(name) {
...
	mrm_mot4x3_6can->directionChange(0); // Uncomment to change 1st wheel's rotation direction
	mrm_mot4x3_6can->directionChange(1); // Uncomment to change 2nd wheel's rotation direction
	mrm_mot4x3_6can->directionChange(2); // Uncomment to change 3rd wheel's rotation direction
	mrm_mot4x3_6can->directionChange(3); // Uncomment to change 4th wheel's rotation direction
}
"..." of course means that there is some code between curly brackets, not that the content is literally "...". We will call this function "constructor" and it runs only once indeed, when the program starts. If we want to change some motors' rotational directions, this will be a natural place to do so.

Let's say that Your motors 2 and 3 (the front pair) need direction change. It can be discussed if it is appropriate or not, but C++ always counts from 0, not from 1. Therefore, the first motor (number 1 in picture) is motor with ordinal number 0. Following the same logic, we want to change the motors 1 and 2 (instead of 2 and 3 in the picture).

The function that can perform our task has the familiar form, [peripheral's name]->[function's name](). Our peripheral is a motor controller named "mrm_mot4x3_6can" and function is "directionChange(motorsOrdinalNumber)".

After some time, circular movement gets boring, but we can do better. One of the key concepts of robotics is called "feedback": a process constantly monitors its output and changes behaviour according to it. What devices monitor output? Sensors, of course. So, all we have to do is to find a sensor and feed its output back into the process, like the one that drives the motors.

Feedback

Let's set a new task for the robot: going straight ahead. A naive approach is to drive all the motors equally. However, the robot will soon wander off the set course. The motors are not perfectly equal, neither are the wheels, etc. We need some correction, a feedback loop, for which we will use IMU (compass). To set 180º course, change loop() like this:

void RobotLine::loop() {
	if (heading() < 180)
		go(60, 40);
	else
		go(40, 60);
}
The code is simple: if the current direction is less than 180º (robot going too much to the left), turn to the right, and a similar maneuver if the direction is more than 180º.