162.Arduino+game

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.
 * [[image:IMG_20161201_161014112.jpg width="375" height="214"]] || [[image:IMG_20161201_163224.jpg width="375" height="214"]] ||
 * = 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:
 * [[image:IMG_20161201_163209.jpg width="276" height="252"]] ||
 * = 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:

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

code

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).

code
 * 1) include 
 * 2) include 
 * 3) include "stc.h"
 * 4) include "platform.h"
 * 5) include "bitmaps.h"

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


 * 1) ifdef HACKVISION
 * 2) define STARTMSG "Press Fire"
 * 3) include 
 * 4) endif

ClassicController cc;
 * 1) ifdef VGS_CC
 * 2) include 
 * 1) define STARTMSG "Press Start"
 * 2) endif

//BOARD_X and NXT_X must be divisable by 8!!!!!!!!
 * 1) define BOARD_X (24)
 * 2) define BOARD_Y (0)
 * 3) define NXT_X (0)
 * 4) define NXT_Y (8)

// this must be for for the current render impimentation
 * 1) define TILE_SIZE (4)


 * 1) define W (112)
 * 2) 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); if (cc.begin(WII_PLAYER_1)) { TV.print("CC begin error"); while(1); } //ccpoll = 0; 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)); TV.printPGM(0,H-16,PSTR("To pause, press")); TV.printPGM(0,H-8,PSTR("< & > together")); int time = TV.millis + 2000; //yeah yeah bad form bla bla while(1) { if (Controller.firePressed) { while (Controller.firePressed); break; }   cc.update; if (cc.button_plus_start) { while (cc.button_plus_start) cc.update; break; } }  TV.delay(500); gameInit(&game); }
 * 1) ifdef VGS_CC
 * 1) endif
 * 2) ifndef DISABLE_INTRO
 * 1) ifdef HACKVISION
 * 1) endif
 * 1) ifdef HACKVISION
 * 1) elif defined(VGS_CC)
 * 1) endif
 * 1) endif

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); if (Controller.firePressed) return; cc.update; if (cc.button_plus_start) return; } for (unsigned char i = 0; i < (TV.vres - l)/2; i++) { TV.bitmap((TV.hres - w)/2,i,TVOlogo); TV.delay(50); if (Controller.firePressed) return; cc.update; if (cc.button_plus_start) return; } int time = TV.millis + 2000; while(time > TV.millis) { if (Controller.firePressed) return; cc.update; if (cc.button_plus_start) return; } }
 * 1) ifdef HACKVISION
 * 1) elif defined(VGS_CC)
 * 1) endif
 * 1) ifdef HACKVISION
 * 1) elif defined(VGS_CC)
 * 1) endif
 * 1) ifdef HACKVISION
 * 1) elif defined(VGS_CC)
 * 1) 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){
 * 1) ifdef HACKVISION


 * 1) define LEFTBIT 1
 * 2) define RIGHTBIT 2
 * 3) define UPBIT 4
 * 4) define DOWNBIT 8
 * 5) define FIREBIT 16
 * 6) define PAUSBIT 32
 * 7) define RESBIT 64

if (Controller.leftPressed && Controller.rightPressed) { if (!PPAUSE) { gameOnKeyDown(&game, EVENT_PAUSE); prev |= PAUSBIT; } }  else if (PAUSBIT) { if (!Controller.leftPressed && !Controller.rightPressed) { prev &= ~PAUSBIT; } }
 * 1) define PLEFT (prev &LEFTBIT)
 * 2) define PRIGHT (prev & RIGHTBIT)
 * 3) define PDOWN (prev &DOWNBIT)
 * 4) define PUP (prev & UPBIT)
 * 5) define PFIRE (prev & FIREBIT)
 * 6) define PPAUSE (prev & PAUSBIT)
 * 7) define PRES (prev & RESBIT)

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; }


 * 1) elif defined(VGS_CC)


 * 1) define LEFTBIT 1
 * 2) define RIGHTBIT 2
 * 3) define UPBIT 4
 * 4) define DOWNBIT 8
 * 5) define ABIT 16
 * 6) define STARTBIT 32
 * 7) define SELECTBIT 64


 * 1) define PLEFT (prev &LEFTBIT)
 * 2) define PRIGHT (prev & RIGHTBIT)
 * 3) define PDOWN (prev &DOWNBIT)
 * 4) define PUP (prev & UPBIT)
 * 5) define PA (prev & ABIT)
 * 6) define PSTART (prev & STARTBIT)
 * 7) 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; }
 * 1) 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++) { setCell(i,j,BOARD_X,BOARD_Y,game.map[i][j],0); setCell(i,j,BOARD_X,BOARD_Y,game.map[i][j],1); }   }
 * 1) ifdef STC_SHOW_GHOST_PIECE
 * 1) else
 * 1) endif

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); }    }
 * 1) ifdef STC_SHOW_GHOST_PIECE
 * 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; } code Ao compilar esse programa, o mesmo apresentou um erro. O mesmo foi solucionado ao retirar o comentário da seguinte linha:

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

Resultados

<span style="font-family: Arial,Helvetica,sans-serif; line-height: 1.5;">Como resultado, obtivemos os jogos listados abaixo


 * <span style="font-family: Arial,Helvetica,sans-serif; line-height: 1.5;">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.


 * [[image:IMG_20161201_163156.jpg width="268" height="221" align="center"]] || [[image:IMG_20161201_163142.jpg width="268" height="221" align="center"]] ||
 * = Imagem 4: Tela de Início do jogo Parachute ||= Imagem 5: Tela do jogo Parachute ||


 * Vídeo: **

media type="youtube" key="5EFoizkG13Y" height="244" width="432"


 * <span style="font-family: Arial,Helvetica,sans-serif;">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.


 * [[image:IMG_20161201_164007.jpg width="268" height="221" align="center"]] || [[image:IMG_20161201_164041.jpg width="268" height="221" align="center"]] ||
 * = Imagem 6: Tela de Introdução do jogo Asteroids ||= Imagem 7: Tela do jogo Asteroids ||


 * Vídeo:**

media type="youtube" key="h5wqFXPQvbg" height="251" width="447"

<span style="font-family: Arial,Helvetica,sans-serif;">**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.


 * [[image:IMG_20161201_165533.jpg width="268" height="221" align="center"]] || [[image:IMG_20161201_165515.jpg width="268" height="221" align="center"]] ||
 * = Imagem 8: Tela de introdução do jogo Tetris ||= Imagem 9: Tela do jogo Tetris ||


 * Vídeo:**

media type="youtube" key="grvwVRtKj5A" height="273" width="453"

<span style="background-color: #ffffff; font-family: Arial,Helvetica,sans-serif;">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.
 * <span style="background-color: #ffffff; font-family: Arial,Helvetica,sans-serif;">1 - Melhoramentos futuros **

<span style="background-color: #ffffff; font-family: Arial,Helvetica,sans-serif;">**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

<span style="font-family: Arial,Helvetica,sans-serif;">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.

<span style="font-family: Arial,Helvetica,sans-serif;">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="display: block; height: 1px; left: 0px; overflow: hidden; position: absolute; top: 3315.5px; width: 1px;"> code Arduino code code | code code +--Parachute code code |    | code code |    +--Parachute.pde code code |    | code code |    +--...many files code code | code code +--libraries code code | code code +--TVout code code |    | code code |    +--...many files code code | code code +--TVoutfonts code code |    | code code |    +--...many files code code | code code +--pollserial code code |    | code code |    +--...many files code code | code code +--Controllers code code | code code +--...many files code