Tabela de conteúdos

C#

C# (pronounced as C-Sharp) is the language that was used to build the simulator, using the Unity platform. It is the language injected into your robot when starting a Routine, regardless of which language you used to program (BlockEduc is converted to rEduc which is finally converted to C#).

Programming in C# brings some BIG differences from rEduc, and can allow the user to use very advanced programming methods, but the learning curve is more difficult.

Tip: One of the most efficient ways to learn C# for the simulator is by building programs in rEduc and converting them to this language for reference. It is always possible to view rEduc commands in C# by clicking here.

Programming in C# in sBotics is done through Asynchronous Tasks (async Tasks), allowing the user to pause task execution through the await Time.Delay() command.

async Task Main()
{
    IO.Print("Printed to console!");
    await Time.Delay(2000);
    IO.PrintLine("Two seconds have passed!");
}

Every C# sBotics program needs to have a Task Main(), the main block that will be called at the beginning of the Routine to execute the code.

async Task Main()
{
    // code here
}

Important Warning about Busy-Waiting

As already mentioned on the Programming page, the simulator and your code run on the same “layer” (thread). So your code DIRECTLY impacts the FPS (“frames per second”, fluidity) of the simulator. Heavy code can end up pausing the simulator for a considerable time causing a crash. You always must use await to avoid holding the simulator too long in your code.

Example: The code below causes a crash in the simulator:

async Task Main()
{
    while(true)
    {
        IO.Print("Crash");
    }
}

The code below does not cause a crash in the simulator, because of the wait:

async Task Main()
{
    while(true)
    {
        IO.Print("Cleared the console! Note that the while true doesn't break the simulator here because of the wait, used to load the rest of the simulator");
        await Time.Delay(100);
    }
}

Depending on how heavy your code is, your program might need longer waits, and always consider restarting the simulator from time to time, as the C# compiler tends to slow down the simulator with prolonged usage sessions.

Synchronous and Asynchronous Functions

As previously mentioned, user code in sBotics operates synchronously. Therefore, functions involving robot movement, repetitions, and execution of sBotics component commands must be implemented as asynchronous tasks (async tasks), as exemplified earlier. However, simple calculations that are processed in fractions of a second by the C# compiler itself do not require such implementation.

Therefore, functions with “void” return or other return types are allowed. But due to the need to use “waits” to manage repetitions and control robot movement, methods that deal with these operations must be created as asynchronous tasks. Example below:

async Task Main()
{
    await ReadSensors();
}
 
async Task ReadSensors()
{
    IO.PrintLine($"I will read {ComponentsCount()} components!");
    await Time.Delay(500);
    // Other commands here
    // ...
    // Since I need to use Time.Delay and work with sBotics, I need this to be an async Task
}
 
// The function below is not a subroutine, it just performs calculations that can work "synchronously", and it doesn't need to be an 'async Task'
int ComponentsCount()
{
    // Returns the number of components in the robot
    return Bot.Components.Length;
}

Accessing Components

Every accessible component has a name, which serves as an access key. In traditional robots, the programmer has to call a function like color(1) and then figure out which robot sensor corresponds to the sensor on port number 1. In sBotics you name your own sensor, so you can call your color sensors LeftColorSensor and RightColorSensor, and then the code can be executed as color(“LeftColorSensor”) or color(“RightColorSensor”).

(color here represents the actual color sensor function, which is more complex than this in C#)

To access a component, just use our generic “GetComponent” method, followed by the component class and its identifier name. Component classes can be seen further down in the text.

// Turns on the LED Light component
// To learn more about the "Color" support class, see "Support Classes and Enumerators"
Bot.GetComponent<Light>("light name").TurnOn( new Color(255, 255, 255) );
 
// Wait 5 seconds
// To learn about await and the time class, see the "asynchronous programming" part of the text
await Time.Delay(5000);
 
// Turn off the light
Bot.GetComponent<Light>("light name").TurnOff();

Sensors

Sensors are components used to receive values from the virtual world and send them to the robot. All sensors have two properties: Digital and Analog (properties, for those less familiar with C#, are like methods, but don't take parentheses in writing).

About the properties:


Component Classes

Color Sensor

class ColorSensor

The color sensor, of class ColorSensor, is a “cube”/component responsible for performing readings directly in front of its reading face. The reading is performed at a maximum distance of up to 8 “blocks”, in a 5×5 pixel area from its center. The average of the reading performed is calculated and returned to the user depending on the chosen capture method:

Digital

bool reading = Bot.GetComponent<ColorSensor>("my sensor").Digital;
// Same as: (Analog.Closest() != Colors.Black);

The digital capture of the color sensor returns true if it is seeing any color that is distant from black, and false if it is seeing the color black.

Analog

Color reading = Bot.GetComponent<ColorSensor>("my sensor").Analog;

The analog capture of the color sensor returns an object of the Color support class, being able to perform other methods of this class that are better described below in the text. But already illustrating superficially:

double d = reading.Brightness; // returns the brightness value of the reading in black and white
double d = reading.Red; // returns the red value of the reading
double d = reading.Green; // returns the green value of the reading
double d = reading.Blue; // returns the blue value of the reading
string s = reading.ToString(); // returns the name of the closest color read

Ultrasonic Sensor

class UltrasonicSensor

The ultrasonic sensor emits 3 convergent rays from its surface, as in the scheme: /|\. And if an object is being seen by one of the three rays, its distance can be captured. The ultrasonic range is relatively high, going from immediately in front of it to the convergence point of the multiple rays “blocks” away.

Digital

bool reading = Bot.GetComponent<UltrasonicSensor>("my sensor").Digital;
// Same as: (Analog != -1);

The digital capture of the ultrasonic sensor returns whether it is seeing any object or not, regardless of its distance.

Analog

double reading = Bot.GetComponent<UltrasonicSensor>("my sensor").Analog;

The analog capture of the ultrasonic sensor returns the distance at which it is seeing an object, or -1 if it is not capturing anything.


Touch Sensor

class TouchSensor

The touch sensor is the simplest sensor in sBotics. Its only function is to return whether it is being pressed by contact with another object.

Digital

bool reading = Bot.GetComponent<TouchSensor>("my sensor").Digital;

The digital capture of the touch sensor returns whether it is being pressed or not at the moment of the call.

Analog

The class in question does not have analog access property.


Buzzer

class Buzzer

The buzzer is used to play sounds. It is possible to access it using the syntax below:

Bot.GetComponent<Buzzer>("my buzzer");

Public properties:

Public methods:


LED Light

class Light

The LED light is used to display colors. It is possible to access it using the syntax below:

Bot.GetComponent<Light>("my LED");

Public properties:

Public methods:

Note: To turn on the lamp using a string, see the static methods of the Color support class like Color.ToColor(“Black”).

Servomotor

class Servomotor

The servomotor is a component that can be rotated using a force, locked and (for not being just a motor, but a servomotor) can have its angle returned. To access a motor, use the syntax below:

Bot.GetComponent<Servomotor>("my motor");

Public properties:

Public Method:


Robot Internal Sensors / Controller

class Bot = __sBotics__RobotController

Bot is not “just another” component, it only represents the reserved class sBoticsRobotController, which cannot be directly called by the user (for having the sBotics prefix) and is included in only a single piece in the robot, like a “central component”. However, there are some properties and methods that can be used.

Public properties:

Public methods:

T .GetComponent<T>(string name)

Already discussed countless times in this text, searches for the component of class T with the specified name;

string[] .Components()

Returns a string array (string[]) with the name of all components.


Support Classes

Some support classes were created to facilitate development, offering functionalities that simplify common tasks and improve programming understanding.

Color

The Color class is the largest support class in the simulator. With it, it is possible to perform operations with colors, check which is the closest color, pull the lighting value, etc.

Constructor:

// Format: Color Color(double, double, double)
Color myColor = new Color(60, 128, 255);

Public properties:

Public methods:

Static methods:

Time

The Time class has methods for time management, and can do things like stopwatches and asynchronously wait for a time interval. These methods are:

Static properties:

Static methods:

IO

The IO class is used for printing data to a virtual console or to a local text file on the computer. It's worth noting that the functioning of the console and text file are different from how they were originally created in 2020. These methods are:

Static console writing methods:

Static file writing methods:

Other static methods/properties:

Utils

The Utils class has some mathematical methods to help C# and rEduc programmer development. These being:

Static methods:

Enumerators

Some enumerators were created to help in development.


Finding Commands

Although this tutorial is quite complete, it may not show all the depth that is possible to achieve with C# for sBotics (with objects like the camera or 3D pen, for example). Therefore, it is recommended that you as a user are always looking for content.

Source Code

The source code of the programmable part (sensors, components, etc.) of sBotics is available on Github: sBotics/Programming Reference.

There it is possible to understand exactly how the sensors work and know all the methods, classes, properties, enums, namespaces, etc. that the user has access to.

From rEduc

It is also possible to see the “translation” that rEduc makes to C# at sBotics Functions, and see exactly the name of each rEduc command and its equivalent C# code for study purposes.


Example

Below is an example code of a simple line follower for the default “Trekker” robot of sBotics using C#.

// Control Functions
async Task Forward(double speed = 200) {
    Bot.GetComponent<Servomotor>("l").Locked = false; // Unlocks the left motor
    Bot.GetComponent<Servomotor>("r").Locked = false; // Unlocks the right motor
    Bot.GetComponent<Servomotor>("l").Apply(Math.Abs(speed), speed); // *
    Bot.GetComponent<Servomotor>("r").Apply(Math.Abs(speed), speed); // *
}
async Task Right(double speed = 200) {
    Bot.GetComponent<Servomotor>("r").Apply(0, 0);
    Bot.GetComponent<Servomotor>("r").Locked = true; // Locks the right motor
    Bot.GetComponent<Servomotor>("l").Locked = false; // Unlocks the left motor
    Bot.GetComponent<Servomotor>("l").Apply(Math.Abs(speed * 2), speed * 2); //*
}
async Task Left(double speed = 200) {
    Bot.GetComponent<Servomotor>("l").Apply(0, 0);
    Bot.GetComponent<Servomotor>("l").Locked = true; // Locks the left motor
    Bot.GetComponent<Servomotor>("r").Locked = false; // Unlocks the right motor
    Bot.GetComponent<Servomotor>("r").Apply(Math.Abs(speed * 2), speed * 2); // *
}
async Task Backward(double speed = 200) {
    Bot.GetComponent<Servomotor>("l").Locked = false; // Unlocks the left motor
    Bot.GetComponent<Servomotor>("r").Locked = false; // Unlocks the right motor
    Bot.GetComponent<Servomotor>("l").Apply(Math.Abs(0 - speed), 0 - speed); // *
    Bot.GetComponent<Servomotor>("r").Apply(Math.Abs(0 - speed), 0 - speed); // *
}
 
// * - // Math.Abs to turn any value positive, since the first parameter cannot be negative.
 
async Task FinalLed() {
    for (int i = 1; i <= 5; i++) {
        Bot.GetComponent<Light>("led").TurnOn(new Color(255, 0, 0));
        await Time.Delay(50);
        Bot.GetComponent<Light>("led").TurnOn(new Color(0, 255, 0));
        await Time.Delay(50);
        Bot.GetComponent<Light>("led").TurnOn(new Color(0, 0, 255));
        await Time.Delay(50);
    }
}
 
async Task Main() {
    while (true) {
        await Time.Delay(1); // NECESSARY SO THE APPLICATION DOESN'T CRASH
        IO.OpenConsole();
        IO.PrintLine("Writing to Console");
        if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() == "Black") && ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() != "Black")) {
            Bot.GetComponent<Light>("led").TurnOn(new Color(0, 0, 255));
            await Right(300);
        } else if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() != "Black") && ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() == "Black")) {
            Bot.GetComponent<Light>("led").TurnOn(new Color(255, 0, 0));
            await Left(300);
        } else if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() == "Black") && ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() == "Black")) {
            Bot.GetComponent<Light>("led").TurnOn(new Color(0, 255, 0));
            await Forward();
        } else if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() == "Red") || ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() == "Red")) {
            await Forward(0);
            await FinalLed();
        } else {
            Bot.GetComponent<Light> ("led").TurnOn(new Color(255, 0, 255));
            await Forward();
        }
    }
}

If you are a less advanced user who is just looking for an easy way to control your robot, consider switching to rEduc.