Tabela de conteúdos

Programação

O Eduardoino precisava ser capaz de executar três tarefas: uma que envolvia apenas movimentação, outra que era apenas detecção de cores utilizando os sensores e a última combinava as duas coisas de uma vez. O código, portanto, pode ser dividido em três partes principais, para melhor entendimento.

Parte 1: Movimentação

O Trabalho consistia em fazer o robô se movimentar de três diferentes maneiras de acordo com a escolha do usuário, que eram:

  1. Uma Reta de 30cm (ida e volta)
  2. Um triângulo retângulo (catetos de 30cm)
  3. Um quadrado com lados de 30cm

Para executar esses movimentos, criamos e utilizamos funções que executavam cada diferente etapa do movimento. Elas foram:

Reta:

Girar 90 graus para a direita:

Girar 135 graus para a direita:

Girar 180 graus:

Essas funções utilizam os delays e velocidades definidos através de tentativa e erro, com os valores abaixo

Cada movimento foi composto da chamada da sua função.

Parte 2: Sensores

O funcionamento dos sensores foi explicado aqui

Parte 3: Multitarefa

A implementação do movimento foi similar à movimentação da parte 1. Porém, ao invés de delays, foram usadas a diferença de tempo entre as últimas leituras, e o movimento a ser realizado era decidido através de uma variável que representava um estado, simulando uma máquina de estados, com tempo de duração de cada estado definido também através de tentativa e erro.

Durante cada um dos estados, o robô realizava uma ação. Ex: Estado que faz com que o robô gire 90 graus para a esquerda:

A troca de estados era realizada ao detectar um bloco e após conseguir determinar sua cor. Ela era feita através da seguinte passagem do código:

Abaixo desse print, existe um if para cada cor detectada e sua transição de estado resultante.

A detecção da cor do bloco utiliza o mesmo código da parte 2.

O funcionamento do multitarefas é mostrado no vídeo acima, foram necessários apenas ajustes nos delays para que o robô realizasse as tarefas com mais precisão

Controle geral: função loop

Nossa função loop, no final, ficou assim.

As variáveis detectarCor e noMultitarefa são variáveis booleanas de controle pra execução das tarefas 2 e 3. A definição do que fazer na tarefa 1 é realizada dentro da função loop_LCD.

Curiosidade: Loop infinito no For de 1 a 10.

Durante a implementação do código do trabalho, encontramos um bug exótico, pra não dizer bizarro. O código entrava em loop infinito no seguinte for:

descobrimos isso utilizando impressões antes e depois do for. As após o for não foram imprimidas, enquanto as anteriores foram.

Isso nos deixou assustados, pois um simples for dando loop infinito já era bastante estranho. Foi aí que decidimos imprimir o valor da variável j dentro do for.

Compilamos e executamos o código. O j recebeu valores de 0 a 9 corretamente e a execução do programa saiu do for, o que significa que simplesmente imprimir o valor de j foi suficiente para que o loop infinito fosse quebrado.

Isso nos deixou muito confuso, afinal, como o for entrava em loop infinito, e simplesmente imprimir a variável j foi o suficiente para que ele não entrasse mais em loop, sem nunca alterar seu valor?

A resposta estava em um bug encontrado um tempo depois. O seguinte vetor, que era passado por referência para outra função e depois percorrido pelo for, estava com o tamanho 9:

O que fazia com que o for acessasse um int à frente dele. Provavelmente, esse int à frente dele é o lugar na memória onde, após compilar, o j era armazenado. Portanto, ao realizar as 10 medições, a última medição caía dentro desse lugar na memória durante a execução do programa, alterando o valor que j ia receber futuramente.

Porém, quando imprimimos o valor de j, alteramos o código nesse local, o que levou a uma recompilação dessa parte do código, que consequentemente fez com que o j fosse definido em outro lugar da memória, quebrando assim o loop.

Este erro, que corrigimos depois, nos mostra um pouco da permissividade que a linguagem C++ nos dá. O controle de acesso à memória é responsabilidade total do programador, o que pode causar problemas como o citado acima. Além disso, este erro, que carinhosamente nomeamos como o mistério da variável quântica, que apenas quando observada ela assumiu um valor, nos dá um insight no modo como a compilação de um código funciona, e como a linguagem de alto nível é traduzida pra linguagem de máquina: a cada compilação, cada variável recebe um espaço na memória dedicada a ela. Neste caso, a variável j recebeu o espaço logo em seguida do vetor de cores, que estava com tamanho errado, e que acessamos indevidamente. Ao mudar o código no escopo do j, fizemos com que o compilador precisasse “atualizar” sua posição na memória, tirando ela de lá. Apesar de sair do loop, o programa provavelmente ainda não iria funcionar completamente até corrigir o tamanho do vetor, pois provavelmente aquela posição ali onde estava o j seria de outra variável que também teria seu valor substituído pela leitura das cores.