Grupo

Yuri Campos
Thel Godoy
Raul Pavani
Matheus Martins Capuccin
Matheus Theodoro
Guilherme Spolidoro

Projeto

Video Game de fácil construção com jogos simples, baseado em Arduino


Resumo

O projeto constitui na montagem de um vídeo game simples, com a utilização do Arduino UNO, uma biblioteca para a exibição de imagens na TV, e outras duas bibliotecas para os jogos e controles dos mesmos.


Software/materiais utilizados

----HARDWARE----

1 x Arduino Uno
1 x Protoboard
5 x Push Bottons (botões)
6 x Resistores de 1K Ohms
2 x Resistores de 470 Ohms
1 x Display de TV
1 x Fonte do Display de TV
1 x Cabo AV
1 x Computador
1 x Cabo de Conexão USB Tipo-B

Conectores


----SOFTWARE---

Arduino Software IDE
Biblioteca TVout
Biblioteca Hackvision Controller
Código Fonte e arquivos do Jogo Asteroids
Código Fonte e arquivos do Jogo Tetris
Código Fonte e arquivos do Jogo Parachute

Metodologia

Utiliza-se o arduino como um console de video game, com auxilio da protoboard para a coneção dos botões e da saída RCA ( Video Composto).
  • A biblioteca TVout proporciona interação do arduino com a TV por meio da saída RCA, tornando a TV um grande display, podendo assim exibir gráficos simples. O esquema de instalação, peças utilizadas, estão no link da biblioteca, no tópico acima.
  • Os comandos (iteração do jogador com o jogo) são feitos através de botões simples (NO). Cada botão tem um comando específico. Temos, então, quatro botões direcionais (esquerda, direita, cima e baixo) e um botão de ação (fire). Nem todos os botões são utilizados em todos os jogos.
  • Os jogos são carregados no Arduino através da comunicação via USB com o computador, através da sua IDE.

Desenvolvimento

1.Montagem

Como primeiro passo, monta-se a parte eletrônica, a qual faz parte os botões e a saída RCA. A Imagem 1 mostra a conexão dos botões e da saída RCA na protoboard. A Imagem 2 mostra os pinos utilizados para a coxão dos botões e da saída RCA no Arduino.
IMG_20161201_161014112.jpg
IMG_20161201_163224.jpg
Imagem 1: Conexão na Protoboard
Imagem 2: Conexão no Arduino

Os pinos utilizados no Arduino, são:

Botões de ação dos jogos

Pino 2 - Botão Esquerdo
Pino 3 - Botão Direito
Pino 4 - Botão Cima
Pino 5 - Botão Baixo
Pino 10 - Botão Fire

Saída de TV RCA

Pino 7 - Video
Pino 9 - Sync
Pino 11 - Audio (opcional, não utilizado nesse projeto)
Pino GND - Aterramento do Vídeo/Audio

Para cada botão de ação, utilizou-se de 1 resistor de 1k Ohm. A saída RCA utilizou-se de 1 resistor de 470 Ohms e 1 resistor de 1k Ohm.

Montada a estrutura, a mesma ficará similar a Imagem 3, logo abaixo:
IMG_20161201_163209.jpg
Imagem 3: Arduino e Protoboard conectados

Feito isso, é necessário estruturar as bibliotecas previamente baixadas, assim como os códigos dos jogos.


2. Organização das pastas

Abaixo, temos o esquema de pastas do jogo Asteroids:

Arduino
    |
    +--Parachute
    |     |
    |     +--Parachute.pde
    |     |
    |     +--...many files
    |
    +--libraries
          |
          +--TVout
          |     |
          |     +--...many files
          |
          +--TVoutfonts
          |     |
          |     +--...many files
          |
          +--pollserial
          |     |
          |     +--...many files
          |
          +--Controllers
                |
                +--...many files
 



Na pasta Arduino, a qual é estabelecida na instalação da IDE, coloca-se a pasta Asteroids, do arquivo previamente baixado (da lista de softwares). Após isso, encontramos a pasta Libraries, contida dentro da pasta Arduino. Estando nela, copia-se as pastas contendo as bibliotecas Controllers, TVout e TVoutfonts (também se encontram na lista de softwares).
Para executar o jogo, entra-se na pasta Asteroids, e encontra-se o arquivo Asteroids.pde. Aberto o arquivo na IDE do Arduino, compila-se o mesmo para a verificação de erros, e após isso, carrega-se o código no Arduino. Se tudo estiver correto, aparecerá a tela do jogo.

Os outros jogos seguem de forma similar e sua organização pode ser encontrada neste site.

3. Exemplo do código fonte do jogo Tetris.

Abaixo, temos os códigos do jogo Tetris (tetris.pde). Porém o mesmo depende de outros arquivos, que funcionam em conjunto com o arquivo há ser compilado (bitmaps.cpp/bitmaps.h, platform.cpp/platform.h e stc.cpp/stc.h).

#include <TVout.h>
#include <fontALL.h>
#include "stc.h"
#include "platform.h"
#include "bitmaps.h"
 
//#define HACKVISION  //compile this for a hackvision
#define VGS_CC  //compile this for a video game shield with a classic controller
                //on p1
//#define DISABLE_INTRO
 
#ifdef HACKVISION
#define STARTMSG "Press Fire"
#include <ButtonController.h>
#endif
 
#ifdef VGS_CC
#include <ClassicController.h>
ClassicController cc;
#define STARTMSG "Press Start"
#endif
 
//BOARD_X and NXT_X must be divisable by 8!!!!!!!!
#define BOARD_X (24)
#define BOARD_Y (0)
#define NXT_X (0)
#define NXT_Y (8)
 
// this must be for for the current render impimentation
#define TILE_SIZE (4)
 
#define W (112)
#define H (90)
 
TVout TV;
StcGame game;
char prev = 0;
char restarted = 0;
 
void intro();
void printboard();
void setCell(char x, char y,char bx, char by, char c, char f);
 
void setup() {
  int error;
 
  TV.begin(PAL,W,H);
  TV.select_font(font6x8);
#ifdef VGS_CC
  if (cc.begin(WII_PLAYER_1)) {
    TV.print("CC begin error");
    while(1);
  }
  //ccpoll = 0;
#endif
#ifndef DISABLE_INTRO
  intro();
  TV.clear_screen();
  TV.select_font(font8x8);
  TV.printPGM(28,35,PSTR("Arduino"));
  TV.printPGM(32,43,PSTR("TETRIS"));
  TV.select_font(font6x8);
  TV.delay(500);
  TV.printPGM(25,60,PSTR(STARTMSG));
#ifdef HACKVISION
  TV.printPGM(0,H-16,PSTR("To pause, press"));
  TV.printPGM(0,H-8,PSTR("< & > together"));
#endif
  int time = TV.millis() + 2000;
  //yeah yeah bad form bla bla
  while(1) {
#ifdef HACKVISION
  if (Controller.firePressed()) {
    while (Controller.firePressed());
    break;
  }
#elif defined(VGS_CC)
    cc.update();
    if (cc.button_plus_start()) {
      while (cc.button_plus_start())
        cc.update();
      break;
    }
#endif
  }
#endif
  TV.delay(500);
  gameInit(&game);
}
 
void loop() {
  gameUpdate(&game);
}
 
void intro() {
unsigned char w,l,wb;
  int index;
  w = pgm_read_byte(TVOlogo);
  l = pgm_read_byte(TVOlogo+1);
  if (w&7)
    wb = w/8 + 1;
  else
    wb = w/8;
  index = wb*(l-1) + 2;
  for ( unsigned char i = 1; i < l; i++ ) {
    TV.bitmap((TV.hres() - w)/2,0,TVOlogo,index,w,i);
    index-= wb;
    TV.delay(50);
#ifdef HACKVISION
  if (Controller.firePressed())
    return;
#elif defined(VGS_CC)
    cc.update();
    if (cc.button_plus_start())
      return;
#endif
  }
  for (unsigned char i = 0; i < (TV.vres() - l)/2; i++) {
    TV.bitmap((TV.hres() - w)/2,i,TVOlogo);
    TV.delay(50);
#ifdef HACKVISION
  if (Controller.firePressed())
    return;
#elif defined(VGS_CC)
    cc.update();
    if (cc.button_plus_start())
      return;
#endif
  }
  int time = TV.millis() + 2000;
  while(time > TV.millis()) {
#ifdef HACKVISION
  if (Controller.firePressed())
    return;
#elif defined(VGS_CC)
    cc.update();
    if (cc.button_plus_start())
      return;
#endif
  }
}
 
//define these for the arduino platform
int platformInit(StcGame *gameInstance) {
  printboard();
  return 0;
}
 
void printboard() {
  TV.clear_screen();
 
  // label the next piece
  TV.printPGM(NXT_X,NXT_Y-8,PSTR("Next"));
 
  //print arduino tetris vertically allong the left
  TV.select_font(font8x8);
  TV.printPGM(0,NXT_Y+TETROMINO_SIZE*TILE_SIZE+8,PSTR("A\nrT\ndE\nuT\niR\nnI\noS"));
  TV.select_font(font6x8);
  TV.bitmap(W-39,H-40,TVoutMini);
 
  // label the Score:
  TV.printPGM(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE+4,0,PSTR("lvl"));
  TV.printPGM(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE+4,12,PSTR("Score"));
  TV.printPGM(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE+4,28,PSTR("Lines"));
 
  //draw the game boarders
  TV.draw_line(BOARD_X-2,BOARD_Y, BOARD_X-2, BOARD_Y+BOARD_TILEMAP_HEIGHT*TILE_SIZE, WHITE);
  TV.draw_line(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE,
               BOARD_Y,BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE,
               BOARD_Y+BOARD_TILEMAP_HEIGHT*TILE_SIZE,
               WHITE);
  TV.draw_line(BOARD_X-2,
               BOARD_Y+BOARD_TILEMAP_HEIGHT*TILE_SIZE+1,
               BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE,
               BOARD_Y+BOARD_TILEMAP_HEIGHT*TILE_SIZE+1,
               WHITE);
}
void platformEnd(StcGame *gameInstance) {
}
 
//modify this to read controller inputs
//there is something wrong in here but there is temp fix at the end
void platformReadInput(StcGame *gameInstance){
#ifdef HACKVISION
 
#define LEFTBIT 1
#define RIGHTBIT 2
#define UPBIT 4
#define DOWNBIT 8
#define FIREBIT 16
#define PAUSBIT 32
#define RESBIT 64
 
#define PLEFT (prev &LEFTBIT)
#define PRIGHT (prev & RIGHTBIT)
#define PDOWN (prev &DOWNBIT)
#define PUP (prev & UPBIT)
#define PFIRE (prev & FIREBIT)
#define PPAUSE (prev & PAUSBIT)
#define PRES (prev & RESBIT)
  if (Controller.leftPressed() && Controller.rightPressed()) {
    if (!PPAUSE) {
      gameOnKeyDown(&game, EVENT_PAUSE);
      prev |= PAUSBIT;
    }
  }
  else if (PAUSBIT) {
    if (!Controller.leftPressed() && !Controller.rightPressed()) {
      prev &= ~PAUSBIT;
    }
  }
 
  if (game.isOver && Controller.firePressed())
    prev |= RESBIT;
  else if (PRES) {
    if (!Controller.firePressed()) {
      gameOnKeyDown(&game, EVENT_RESTART);
      prev &= ~RESBIT;
    }
  }
 
  if (Controller.upPressed()) {
    if (!PUP) {
      gameOnKeyDown(&game, EVENT_ROTATE_CW);
      prev |= UPBIT;
    }
  }
  else if (PUP){
    prev &= ~UPBIT;
  }
 
  if (!game.isOver && Controller.firePressed()) {
    if (!PFIRE) {
      gameOnKeyDown(&game, EVENT_DROP);
      prev |= FIREBIT;
    }
  }
  else if (PFIRE) {
    prev &= ~FIREBIT;
  }
 
  if (Controller.leftPressed() && !PPAUSE) {
    if (!PLEFT) {
      gameOnKeyDown(&game, EVENT_MOVE_LEFT);
      prev |= LEFTBIT;
    }
  }
  else if (PLEFT) {
    gameOnKeyUp(&game, EVENT_MOVE_LEFT);
    prev &= ~LEFTBIT;
  }
 
  if (Controller.rightPressed() && !PPAUSE) {
    if (!PRIGHT) {
      gameOnKeyDown(&game, EVENT_MOVE_RIGHT);
      prev |= RIGHTBIT;
    }
  }
  else if (PRIGHT) {
    gameOnKeyUp(&game, EVENT_MOVE_RIGHT);
    prev &= ~RIGHTBIT;
  }
 
  if (Controller.downPressed()) {
    if (!PDOWN) {
      gameOnKeyDown(&game, EVENT_MOVE_DOWN);
      prev |= DOWNBIT;
    }
  }
  else if (PDOWN) {
    gameOnKeyUp(&game, EVENT_MOVE_DOWN);
    prev &= ~DOWNBIT;
  }
 
#elif defined(VGS_CC)
 
#define LEFTBIT 1
#define RIGHTBIT 2
#define UPBIT 4
#define DOWNBIT 8
#define ABIT 16
#define STARTBIT 32
#define SELECTBIT 64
 
#define PLEFT (prev &LEFTBIT)
#define PRIGHT (prev & RIGHTBIT)
#define PDOWN (prev &DOWNBIT)
#define PUP (prev & UPBIT)
#define PA (prev & ABIT)
#define PSTART (prev & STARTBIT)
#define PSELECT (prev & SELECTBIT)
 
  cc.update();
 
  if (cc.button_up()) {
    if (!PUP) {
      gameOnKeyDown(&game, EVENT_ROTATE_CW);
      prev |= UPBIT;
    }
  }
  else if (PUP){
    prev &= ~UPBIT;
  }
 
  if (cc.button_plus_start()) {
    if (!PSTART) {
      gameOnKeyDown(&game, EVENT_PAUSE);
      prev |= STARTBIT;
    }
  }
  else if (PSTART){
    prev &= ~STARTBIT;
  }
 
  if (cc.button_minus_select()) {
    if (!PSELECT) {
      gameOnKeyDown(&game, EVENT_RESTART);
      prev |= SELECTBIT;
    }
  }
  else if (PSELECT){
    prev &= ~SELECTBIT;
  }
 
  if (cc.button_a()) {
    if (!PA) {
      gameOnKeyDown(&game, EVENT_DROP);
      prev |= ABIT;
    }
  }
  else if (PA) {
    prev &= ~ABIT;
  }
 
  if (cc.button_left()) {
    if (!PLEFT) {
      gameOnKeyDown(&game, EVENT_MOVE_LEFT);
      prev |= LEFTBIT;
    }
  }
  else if (PLEFT) {
    gameOnKeyUp(&game, EVENT_MOVE_LEFT);
    prev &= ~LEFTBIT;
  }
 
  if (cc.button_right()) {
    if (!PRIGHT) {
      gameOnKeyDown(&game, EVENT_MOVE_RIGHT);
      prev |= RIGHTBIT;
    }
  }
  else if (PRIGHT) {
    gameOnKeyUp(&game, EVENT_MOVE_RIGHT);
    prev &= ~RIGHTBIT;
  }
 
  if (cc.button_down()) {
    if (!PDOWN) {
      gameOnKeyDown(&game, EVENT_MOVE_DOWN);
      prev |= DOWNBIT;
    }
  }
  else if (PDOWN) {
    gameOnKeyUp(&game, EVENT_MOVE_DOWN);
    prev &= ~DOWNBIT;
  }
#endif
 
}
 
void setCell(char x, char y,char bx, char by, char c, char f) {
  int index = bx/8 + x/2 + by*W/8 + y*4*W/8;
  if (c) {
    if (x & 1) {
      TV.screen[index] &= 0b11110000;
      TV.screen[index] |= 0b00001110;
      index+=W/8;
      TV.screen[index] &= 0b11110000;
      TV.screen[index] |= 0b00001010;
      if (f) TV.screen[index] &= 0b11111011;
      index+=W/8;
      TV.screen[index] &= 0b11110000;
      TV.screen[index] |= 0b00001110;
      index+=W/8;
      TV.screen[index] &= 0b11110000;
    }
    else {
      TV.screen[index] &= 0b00001111;
      TV.screen[index] |= 0b11100000;
      index+=W/8;
      TV.screen[index] &= 0b00001111;
      TV.screen[index] |= 0b10100000;
      if (f) TV.screen[index] &= 0b10111111;
      index+=W/8;
      TV.screen[index] &= 0b00001111;
      TV.screen[index] |= 0b11100000;
      index+=W/8;
      TV.screen[index] &= 0b00001111;
    }
  }
  else {
    if (x & 1) {
      TV.screen[index] &= 0b11110000;
      index+=W/8;
      TV.screen[index] &= 0b11110000;
      if (f) TV.screen[index] |= 0b00000100;
      index+=W/8;
      TV.screen[index] &= 0b11110000;
      index+=W/8;
      TV.screen[index] &= 0b11110000;
    }
    else {
      TV.screen[index] &= 0b00001111;
      index+=W/8;
      TV.screen[index] &= 0b00001111;
      if (f) TV.screen[index] |= 0b01000000;
      index+=W/8;
      TV.screen[index] &= 0b00001111;
      index+=W/8;
      TV.screen[index] &= 0b00001111;
    }
  }
}
 
//modify this to render the game TVout redering is defined here.
void platformRenderGame(StcGame *gameInstance) {
  char i,j;
  TV.delay_frame(1);
if (!game.isPaused && !game.isOver) {
    //draw the static blocks
    for (i = 0; i < BOARD_TILEMAP_WIDTH; i++) {
      for (j = 0; j < BOARD_TILEMAP_HEIGHT; j++) {
#ifdef STC_SHOW_GHOST_PIECE
        setCell(i,j,BOARD_X,BOARD_Y,game.map[i][j],0);
#else
        setCell(i,j,BOARD_X,BOARD_Y,game.map[i][j],1);
#endif
      }
    }
 
#ifdef STC_SHOW_GHOST_PIECE
    for (i = 0; i < TETROMINO_SIZE; i++) {
      for (j = 0; j < TETROMINO_SIZE; j++) {
        if (game.fallingBlock.cells[i][j] != EMPTY_CELL)
          setCell(game.fallingBlock.x + i, game.fallingBlock.y + game.shadowGap + j, BOARD_X, BOARD_Y, 0, 1);
       }
     }
#endif
 
    // falling block
    for (i = 0; i < TETROMINO_SIZE; i++) {
      for (j = 0; j < TETROMINO_SIZE; j++) {
        if (game.fallingBlock.cells[i][j]) {
          setCell(game.fallingBlock.x + i,game.fallingBlock.y + j,BOARD_X,BOARD_Y,1,1);
        }
      }
    }
 
    // nextblock block
    TV.draw_rect(NXT_X,NXT_Y,TETROMINO_SIZE*TILE_SIZE,TETROMINO_SIZE*TILE_SIZE,BLACK,BLACK);
    for (i = 0; i < TETROMINO_SIZE; i++) {
      for (j = 0; j < TETROMINO_SIZE; j++) {
        setCell (game.nextBlock.x + i+1,game.nextBlock.y + j+1,NXT_X,NXT_Y,game.nextBlock.cells[i][j],0);
      }
    }
 
    //update the stats
    TV.print(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE+26,0,game.stats.level);
    TV.print(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE+10,20,game.stats.score);
    TV.print(BOARD_X+BOARD_TILEMAP_WIDTH*TILE_SIZE+10,36,game.stats.lines);
  }
 
  if (game.isPaused)
    TV.printPGM(BOARD_X+4,BOARD_Y+40,PSTR("Pause"));
  if (game.isOver) {
    TV.printPGM(BOARD_X+8,BOARD_Y+36,PSTR("Game"));
    TV.printPGM(BOARD_X+8,BOARD_Y+44,PSTR("Over"));
    restarted = 1;
  }
 
  if (restarted == 1 && game.isOver == 0) {
    printboard();
    restarted = 0;
  }
 
  game.stateChanged = 0;
}
 
//return millis for this since this uses TVout we need to return TVouts version of millis.
long platformGetSystemTime() {
  return TV.millis();
}
 
void platformSeedRandom(long seed) {
  srand(analogRead(0)*seed);
}
 
int platformRandom() {
  return rand();
}
Ao compilar esse programa, o mesmo apresentou um erro. O mesmo foi solucionado ao retirar o comentário da seguinte linha:

#define HACKVISION  //compile this for a hackvision
//#define VGS_CC  //compile this for a video game shield with a classic controller
                //on p1
//#define DISABLE_INTRO

Resultados

Como resultado, obtivemos os jogos listados abaixo

1. Parachute

Breve descrição:

Na tela de seleção do jogo, podemos escolher entre o modo A (fácil) e o modo B (difícil). O objetivo é acumular pontos, salvando os paraquedistas, evitando que os tubarões os comam.
A maior pontuação fica registrada, sendo possível inserir iniciais para a mesma.

IMG_20161201_163156.jpg
IMG_20161201_163142.jpg
Imagem 4: Tela de Início do jogo Parachute
Imagem 5: Tela do jogo Parachute

Vídeo:




2. Asteroids

Breve descrição:

Ao pressionar o botão Fire, o jogo inicia. Com botões Direita e Esquerda, giramos a nave. Com Cima e Baixo, a transportamos para lugares predefinidos da tela. O botão Fire atira. O objetivo do jogo é acumular a maior quantidade de pontos, destruindo os asteroides que surgem na tela.
A maior pontuação fica registrada, sendo possível inserir iniciais para a mesma.

IMG_20161201_164007.jpg
IMG_20161201_164041.jpg
Imagem 6: Tela de Introdução do jogo Asteroids
Imagem 7: Tela do jogo Asteroids

Vídeo:




3. Tetris

Breve descrição:

Ao pressionar o botão Fire, o jogo inicia. Com botões Direita e Esquerda, controlamos as peças em sua queda. Com Cima e Baixo, rotacionamos as peças. O botão Fire faz a peça cair mais rápido. O objetivo do jogo é acumular a maior quantidade de pontos, destruindo as linhas que surgem com a queda dos blocos.
A maior pontuação fica registrada, sendo possível inserir iniciais para a mesma.

IMG_20161201_165533.jpg
IMG_20161201_165515.jpg
Imagem 8: Tela de introdução do jogo Tetris
Imagem 9: Tela do jogo Tetris

Vídeo:




1 - Melhoramentos futuros
Como implementação para um futura melhoria do projeto, o controle poderá ser montado em uma placa de fenolito cobreado e devidamente soldado, não havendo assim a necessidade da protoboard. O sistema de cabos da conexão RCA também pode ser embutido na mesma placa.

OBS: O shield para Arduino da própria Hackvision já é um modelo pronto de todo o projeto, contando com os botões de ação, saída RCA e o C.I AT MEGA 2560. Todo o projeto foi executado com auxilio das bibliotecas e recomendações do site já citado.

Conclusão

O Arduino é um dispositivo que possui várias aplicações, seja em automação, ou em IoT. Uma aplicação pouco explorada é a de gamificação de conteúdo para o mesmo. Com esse projeto, há a possibilidade de construir um vídeo game de baixo custo e de simples montagem, para que alunos em formação acadêmica no curso de Jogos Digitais entendam o funcionamento de um vídeo game antigo, o qual utilizavam cartuchos com memória EEPROM nos qual era salva a programação dos jogos. Com as devidas bibliotecas e montagem do circuito elétrico, pode-se exportar o jogo programado na interface do Arduino para a memória do mesmo, similares aos antigos jogos de cartucho.

Com isso, observou-se a capacidade de transformar o Arduino em uma plataforma de jogos, mesmo que simples entendimento e de fácil construção eletrônica, cumprindo o objetivo do projeto.

<span style="font-size: 9.0pt;">Arduino </span>
<span style="font-size: 9.0pt;">    | </span>
<span style="font-size: 9.0pt;">    +--Parachute </span>
<span style="font-size: 9.0pt;">    |     | </span>
<span style="font-size: 9.0pt;">    |     +--Parachute.pde </span>
<span style="font-size: 9.0pt;">    |     | </span>
<span style="font-size: 9.0pt;">    |     +--...many files </span>
<span style="font-size: 9.0pt;">    | </span>
<span style="font-size: 9.0pt;">    +--libraries </span>
<span style="font-size: 9.0pt;">          | </span>
<span style="font-size: 9.0pt;">          +--TVout </span>
<span style="font-size: 9.0pt;">          |     | </span>
<span style="font-size: 9.0pt;">          |     +--...many files </span>
<span style="font-size: 9.0pt;">          | </span>
<span style="font-size: 9.0pt;">          +--TVoutfonts </span>
<span style="font-size: 9.0pt;">          |     | </span>
<span style="font-size: 9.0pt;">          |     +--...many files </span>
<span style="font-size: 9.0pt;">          | </span>
<span style="font-size: 9.0pt;">          +--pollserial </span>
<span style="font-size: 9.0pt;">          |     | </span>
<span style="font-size: 9.0pt;">          |     +--...many files </span>
<span style="font-size: 9.0pt;">          | </span>
<span style="font-size: 9.0pt;">          +--Controllers </span>
<span style="font-size: 9.0pt;">                | </span>
<span style="font-size: 9.0pt;">                +--...many files </span>