Essa é uma revisão anterior do documento!
Tabela de conteúdos
C#
C# (pronunciado como C-Sharp) é a linguagem que foi utilizada para construir o simulador, usando a plataforma Unity. Ela é a linguagem injetada no seu robô ao iniciar uma Rotina, independente de que linguagem utilizou para programar (BlockEduc é convertido em rEduc que por fim é convertido em C#).
Programar em C# traz algumas GRANDES diferenças do rEduc, e pode permitir o usuário a usar métodos bem avançados de programação, porém a curva de aprendizado é mais difícil.
Dica: Uma das formas mais eficientes de se aprender o C# do simulador é construindo programas em rEduc e convertendo-os para esta linguagem, para usar de referência. É sempre possível visualizar os comandos do rEduc em C# clicando aqui.
A programação em C# no sBotics é dada através de Tarefas Assíncronas (async Tasks), permitindo que o usuário possa pausar a execução das tarefas através do comando await Time.Delay()
.
async Task Main() { IO.Print("Imprimi no console!"); await Time.Delay(2000); IO.PrintLine("Dois segundos se passaram!"); }
Todo programa C# sBotics precisa ter uma Task Main()
, bloco principal que será chamado no início da Rotina para executar o código.
async Task Main() { // código aqui }
Aviso Importante sobre Busy-Waiting
Como já informado na página de Programação, o simulador e o seu código rodam na mesma “camada” (thread). Então o seu código impacta DIRETAMENTE no FPS (“quadros por segundo”, fluidez) do simulador. Códigos pesados podem acabar pausando o simulador por um tempo considerável causando um crash. Você sempre deve usar await
para evitar segurar o simulador por tempo demais no seu código.
Exemplo: O código abaixo causa um crash no simulador:
async Task Main() { while(true) { IO.Print("Crash"); } }
Já o código abaixo não causa um crash no simulador, por conta da espera:
async Task Main() { while(true) { IO.Print("Limpei o console! Note que o while true não quebra o simulador aqui por causa da espera, usada para carregar o resto do simulador"); await Time.Delay(100); } }
Dependendo do quão pesado seu código for, pode ser que seu programa precise de esperas maiores, e sempre considere reiniciar o simulador de tempos em tempos, já que o compilador C# tende a deixar o simulador lento com sessões prolongadas de uso.
Funções Síncronas e Assíncronas
Conforme previamente mencionado, o código do usuário no sBotics opera de forma síncrona. Sendo assim, funções que envolvem movimentação do robô, repetições e a execução de comandos de componentes sBotics devem ser implementadas como tarefas assíncronas (async tasks), conforme exemplificado anteriormente. Entretanto, cálculos simples, que são processados em frações de segundo pelo próprio compilador C#, não exigem tal implementação.
Portanto, funções com retorno “void” ou outros tipos de retorno são permitidas. Porém devido à necessidade de utilizar “waits” para gerenciar repetições e controlar a movimentação do robô, métodos que lidam com essas operações devem ser criadas como tarefas assíncronas. Exemplo abaixo:
async Task Main() { await LerSensores(); } async Task LerSensores() { IO.PrintLine($"Irei ler {ComponentsCount()} componentes!"); await Time.Delay(500); // Outros comandos aqui // ... // Como preciso usar Time.Delay e mexer com o sBotics, preciso que seja uma async Task } // Já a função abaixo não é uma subrotina, apenas realiza cálculos que podem funcionar de forma "sícrona", e ela não precisa ser uma 'aync Task' int ComponentsCount() { // Retorna a quantidade de componentes no robô return Bot.Components.Length; }
Acessando Componentes
Todo componente acessível possui um nome, que serve como chave de acesso. Em robôs, tradicionais o programador tem que chamar uma função como cor(1)
e então descobrir qual sensor do robô corresponde ao sensor na porta de número 1
. Já no sBotics você nomeia seu próprio sensor, então você pode chamar seus sensores de cor como SensorCorEsquerdo e SensorCorDireito, e assim o código pode ser executado como cor(“SensorCorEsquerdo”)
ou cor(“SensorCorDireito”)
.
(cor
aqui representa a função real do sensor de cor, que é mais complexa que isso em C#)
Para acessar um componente, basta utilizar nosso método genérico “GetComponent”, seguido pela classe do componente e seu nome identificador. As classes dos componentes podem ser vistas mais abaixo no texto.
// Liga o componente Luz LED // Para saber mais sobre a classe de apoio "Color", ver "Classes de apoio e Enumeradores" Bot.GetComponent<Light>("nome da luz").TurnOn( new Color(255, 255, 255) ); // Espera 5 segundos // Para aprender sobre o await e a classe time, ver a parte de "programação assíncrona" do texto await Time.Delay(5000); // Desliga a luz Bot.GetComponent<Light>("nome da luz").TurnOff();
Sensores
Sensores são componentes utilizados para receber valores do mundo virtual e envia-los para o robô. Todos os sensores possuem duas propriedades: Digital
e Analog
(propriedades, para os menos familiarizados com C#, são como métodos, porém não levam parênteses na escrita).
Sobre as propriedades:
- Digital: É do tipo
boolean
, ou seja, retorna um valor de verdadeiro ou falso.- Sensor Cor:
bool
- Sensor Ultrassônico:
bool
- Sensor Toque:
bool
- Analógico: Retorna de uma forma diferente dependendo do sensor.
- Sensor Cor:
Color
- Sensor Ultrassônico:
double
- Sensor Toque: Não Possui
Classes dos Componentes
Sensor de Cor
class ColorSensor
O sensor de cor, de classe ColorSensor
, é um “cubo”/componente responsável por realizar leituras diretamente a frente de sua face de leitura. A leitura é realizada a uma distancia máxima de até 8 “blocos”, em uma área de 5×5 pixels a partir de seu centro. É calculada a média da leitura realizada e retornada ao usuário dependendo da forma de captura escolhida:
Digital
bool leitura = Bot.GetComponent<ColorSensor>("meu sensor").Digital; // Mesma coisa de: (Analog.Closest() != Colors.Black);
A captura digital do sensor de cor retorna verdadeiro caso o mesmo esteja vendo qualquer cor que seja distante de preto, e falso caso o mesmo esteja vendo a cor preta.
Analog
Color leitura = Bot.GetComponent<ColorSensor>("meu sensor").Analog;
Já a captura analógica do sensor de cor, retorna um objeto da classe de apoio Color, podendo realizar outros métodos dessa classe que são melhores descritos abaixo no texto. Mas já ilustrando superficialmente:
double d = leitura.Brightness; // retorna o valor de luminosidade da leitura em preto e branco double d = leitura.Red; // retorna o valor de vermelho da leitura double d = leitura.Green; // retorna o valor de verde da leitura double d = leitura.Blue; // retorna o valor de azul da leitura string s = leitura.ToString(); // retorna o nome da cor mais próxima lida
Sensor Ultrassônico
class UltrasonicSensor
O sensor ultrassônico emite 3 raios convergentes de sua superfície, como no esquema: /|\
. E caso um objeto esteja sendo visto por um dos três raios, sua distância pode ser captada. O alcance do ultrassônico é relativamente alto, indo de imediatamente a sua frente até o ponto de convergência dos raios múltiplos “blocos” de distância.
Digital
bool leitura = Bot.GetComponent<UltrasonicSensor>("meu sensor").Digital; // Mesma coisa de: (Analog != -1);
A captura digital do sensor ultrassônico retorna se o mesmo está vendo ou não algum objeto, independente de sua distância.
Analog
double leitura = Bot.GetComponent<UltrasonicSensor>("meu sensor").Analog;
Já a captura analógica do sensor ultrassônico retorna a distância no qual está vendo um objeto, ou -1 caso não esteja captando nada.
Sensor de Toque
class TouchSensor
O sensor de toque é o sensor mais simples do sBotics. Sua única função é retornar se o mesmo está sendo pressionado pelo contato de outro objeto.
Digital
bool leitura = Bot.GetComponent<TouchSensor>("meu sensor").Digital;
A captura digital do sensor de toque retorna se o mesmo está sendo pressionado ou não no momento da chamada.
Analog
A classe em questão não possui propriedade de acesso analógica.
Buzzer
class Buzzer
O buzzer é utilizado para tocar sons. É possível acessá-lo utilizando a sintaxe abaixo:
Bot.GetComponent<Buzzer>("meu buzzer");
Propriedades públicas:
bool .Playing:
Retorna se o buzzer está tocando algum som naquele momento;
Métodos públicos:
void .StopSound()
: Para de tocar o som saíndo do buzzer;void .PlaySound(double hertz, WaveFormats wave = WaveFormats.Square)
: Toca um som caso na frequencia especificada e em um determinado formato de onda (não obrigatório);void .PlaySound(string note, WaveFormats wave = WaveFormats.Square)
: Toca um som caso o texto “note” equivaler a algum item no enumerador de apoio Solfege ou Notes, em um determinado formato de onda (não obrigatório);void .PlaySound(Solfege note, WaveFormats wave = WaveFormats.Square)
: Toca um som do enumerador Solfege (Do, Re, Mi, Fa, Sol, La, Si, Do) em um determinado formato de onda (não obrigatório);void .PlaySound(Notes note, WaveFormats wave = WaveFormats.Square)
: Toca um som do enumerador Notes (C, D, E, F, G, A, B) em um determinado formato de onda (não obrigatório).
Luz LED
Servomotor
Sensores Internos do Robô / Controlador
Classes de Apoio
lorem ipsum
Color
Time
IO
Utils
Enumeradores
Buscando Comandos
lorem ipsum
Código Fonte
lorem ipśum
Do rEduc
lorem ipśum
Exemplo
Segue abaixo um código de exemplo de um simples seguidor de linha para o robô “Trekker” padrão do sBotics usando rEduc.
// Funções de Controle async Task Frente(double velocidade = 200) { Bot.GetComponent<Servomotor>("l").Locked = false; // Destrava o motor da esquerda Bot.GetComponent<Servomotor>("r").Locked = false; // Destrava o motor da direita Bot.GetComponent<Servomotor>("l").Apply(Math.Abs(velocidade), velocidade); // * Bot.GetComponent<Servomotor>("r").Apply(Math.Abs(velocidade), velocidade); // * } async Task Direita(double velocidade = 200) { Bot.GetComponent<Servomotor>("r").Apply(0, 0); Bot.GetComponent<Servomotor>("r").Locked = true; // Trava o motor da direita Bot.GetComponent<Servomotor>("l").Locked = false; // Destrava o motor da esquerda Bot.GetComponent<Servomotor>("l").Apply(Math.Abs(velocidade * 2), velocidade * 2); //* } async Task Esquerda(double velocidade = 200) { Bot.GetComponent<Servomotor>("l").Apply(0, 0); Bot.GetComponent<Servomotor>("l").Locked = true; // Trava o motor da esquerda Bot.GetComponent<Servomotor>("r").Locked = false; // Destrava o motor da direita Bot.GetComponent<Servomotor>("r").Apply(Math.Abs(velocidade * 2), velocidade * 2); // * } async Task Tras(double velocidade = 200) { Bot.GetComponent<Servomotor>("l").Locked = false; // Destrava o motor da esquerda Bot.GetComponent<Servomotor>("r").Locked = false; // Destrava o motor da direita Bot.GetComponent<Servomotor>("l").Apply(Math.Abs(0 - velocidade), 0 - velocidade); // * Bot.GetComponent<Servomotor>("r").Apply(Math.Abs(0 - velocidade), 0 - velocidade); // * } // * - // Math.Abs para tornar qualquer valor em positivo, já que o primeiro parâmetro não pode ser negativo. async Task LedFinal() { 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); // NECESSÁRIO PARA A APLICAÇÃO NÃO DAR CRASH IO.OpenConsole(); IO.PrintLine("Escrevendo no Console"); if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() == "Preto") && ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() != "Preto")) { Bot.GetComponent<Light>("led").TurnOn(new Color(0, 0, 255)); await Direita(300); } else if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() != "Preto") && ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() == "Preto")) { Bot.GetComponent<Light>("led").TurnOn(new Color(255, 0, 0)); await Esquerda(300); } else if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() == "Preto") && ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() == "Preto")) { Bot.GetComponent<Light>("led").TurnOn(new Color(0, 255, 0)); await Frente(); } else if (((Bot.GetComponent<ColorSensor>("rc").Analog).ToString() == "Vermelho") || ((Bot.GetComponent<ColorSensor>("lc").Analog).ToString() == "Vermelho")) { await Frente(0); await LedFinal(); } else { Bot.GetComponent<Light> ("led").TurnOn(new Color(255, 0, 255)); await Frente(); } } }
Caso seja um usuário menos avançado que busca apenas uma forma fácil de controlar o seu robô, cogite alterar para rEduc.