Hi,
I have a problem on Tinkercad where my display isn't working and it returns me "Invalid Header file", at the same time my code doesn't run in Arduino properly since it gets stuck on a loop.
In Tinkercad they don't use the library I needed which is NewLiquidCrystal so I added it by writing the .h file and .cpp on the header. On Arduino it's all good regarding the library.
The intention of this project is recreate the classic game "Simon says" but with levels and a more of interaction.
I am using Arduino UNO R3 and the LCD Display has the II2C attached to it.
Please help me out to solve how can I run this on Tinkercad, at the same time what's the problem with my code, I thought maybe it's a matter of the digitalRead "LOW" because the loop what happens is that it starts but give me no choice, so it runs on EASY mode for the first pulse and it goes GAME OVER.
Here I attach the code and picture of the circuit.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#ifndef _LCD_H_
#define _LCD_H_
#if (ARDUINO < 100)
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
#ifdef __AVR__
#include <avr/pgmspace.h>
#endif
#include <inttypes.h>
#include <Print.h>
#ifndef _BV
#define _BV(bit) (1 << (bit))
#endif
#ifdef __AVR__
#define FAST_MODE
#endif
inline static void waitUsec ( uint16_t uSec )
{
#ifndef FAST_MODE
delayMicroseconds ( uSec );
#endif // FAST_MODE
}
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
#define COMMAND 0
#define LCD_DATA 1
#define FOUR_BITS 2
#define HOME_CLEAR_EXEC 2000
#define BACKLIGHT_OFF 0
/
#define BACKLIGHT_ON 255
typedef enum { POSITIVE, NEGATIVE } t_backlightPol;
class LCD : public Print
{
public:
LCD ( );
virtual void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);
void clear();
void home();
void noDisplay();
void display();
void noBlink();
void blink();
void noCursor();
void cursor();
void scrollDisplayLeft();
void scrollDisplayRight();
void leftToRight();
void rightToLeft();
void moveCursorLeft();
void moveCursorRight();
void autoscroll();
void noAutoscroll();
void createChar(uint8_t location, uint8_t charmap[]);
#ifdef __AVR__
void createChar(uint8_t location, const char *charmap);
#endif // __AVR__
void setCursor(uint8_t col, uint8_t row);
void backlight ( void );
void noBacklight ( void );
void on ( void );
void off ( void );
virtual void setBacklightPin ( uint8_t value, t_backlightPol pol ) { };
virtual void setBacklight ( uint8_t value ) { };
#if (ARDUINO < 100)
virtual void write(uint8_t value);
#else
virtual size_t write(uint8_t value);
#endif
#if (ARDUINO < 100)
using Print::write;
#else
using Print::write;
#endif
protected:
uint8_t _displayfunction;
uint8_t _displaycontrol;
uint8_t _displaymode;
uint8_t _numlines;
uint8_t _cols;
t_backlightPol _polarity;
private:
void command(uint8_t value);
#if (ARDUINO < 100)
virtual void send(uint8_t value, uint8_t mode) { };
#else
virtual void send(uint8_t value, uint8_t mode) = 0;
#endif
};
#endif
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#if (ARDUINO < 100)
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
LCD::LCD ()
{
}
void LCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize)
{
if (lines > 1)
{
_displayfunction |= LCD_2LINE;
}
_numlines = lines;
_cols = cols;
if ((dotsize != LCD_5x8DOTS) && (lines == 1))
{
_displayfunction |= LCD_5x10DOTS;
}
delay (100);
if (! (_displayfunction & LCD_8BITMODE))
{
send(0x03, FOUR_BITS);
delayMicroseconds(4500);
send ( 0x03, FOUR_BITS );
delayMicroseconds(150);
send( 0x03, FOUR_BITS );
delayMicroseconds(150);
send ( 0x02, FOUR_BITS );
delayMicroseconds(150);
}
else
{
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(4500);
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(150);
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds(150);
}
command(LCD_FUNCTIONSET | _displayfunction);
delayMicroseconds ( 60 );
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
display();
clear();
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
command(LCD_ENTRYMODESET | _displaymode);
backlight();
}
void LCD::clear()
{
command(LCD_CLEARDISPLAY);
delayMicroseconds(HOME_CLEAR_EXEC);
}
void LCD::home()
{
command(LCD_RETURNHOME);
delayMicroseconds(HOME_CLEAR_EXEC);
}
void LCD::setCursor(uint8_t col, uint8_t row)
{
const byte row_offsetsDef[] = { 0x00, 0x40, 0x14, 0x54 };
const byte row_offsetsLarge[] = { 0x00, 0x40, 0x10, 0x50 };
if ( row >= _numlines )
{
row = _numlines-1;
}
if ( _cols == 16 && _numlines == 4 )
{
command(LCD_SETDDRAMADDR | (col + row_offsetsLarge[row]));
}
else
{
command(LCD_SETDDRAMADDR | (col + row_offsetsDef[row]));
}
}
void LCD::noDisplay()
{
_displaycontrol &= ~LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::display()
{
_displaycontrol |= LCD_DISPLAYON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::noCursor()
{
_displaycontrol &= ~LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::cursor()
{
_displaycontrol |= LCD_CURSORON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::noBlink()
{
_displaycontrol &= ~LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::blink()
{
_displaycontrol |= LCD_BLINKON;
command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LCD::scrollDisplayLeft(void)
{
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LCD::scrollDisplayRight(void)
{
command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}
void LCD::leftToRight(void)
{
_displaymode |= LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}
void LCD::rightToLeft(void)
{
_displaymode &= ~LCD_ENTRYLEFT;
command(LCD_ENTRYMODESET | _displaymode);
}
void LCD::moveCursorRight(void)
{
command(LCD_CURSORSHIFT | LCD_CURSORMOVE | LCD_MOVERIGHT);
}
void LCD::moveCursorLeft(void)
{
command(LCD_CURSORSHIFT | LCD_CURSORMOVE | LCD_MOVELEFT);
}
void LCD::autoscroll(void)
{
_displaymode |= LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
void LCD::noAutoscroll(void)
{
_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
command(LCD_ENTRYMODESET | _displaymode);
}
void LCD::createChar(uint8_t location, uint8_t charmap[])
{
location &= 0x7;
command(LCD_SETCGRAMADDR | (location << 3));
delayMicroseconds(30);
for (uint8_t i = 0; i < 8; i++)
{
write(charmap[i]);
delayMicroseconds(40);
}
}
#ifdef __AVR__
void LCD::createChar(uint8_t location, const char *charmap)
{
location &= 0x7;
command(LCD_SETCGRAMADDR | (location << 3));
delayMicroseconds(30);
for (uint8_t i = 0; i < 8; i++)
{
write(pgm_read_byte_near(charmap++));
delayMicroseconds(40);
}
}
#endif // __AVR__
void LCD::backlight ( void )
{
setBacklight(255);
}
void LCD::noBacklight ( void )
{
setBacklight(0);
}
void LCD::on ( void )
{
display();
backlight();
}
void LCD::off ( void )
{
noBacklight();
noDisplay();
}
void LCD::command(uint8_t value)
{
send(value, COMMAND);
}
#if (ARDUINO < 100)
void LCD::write(uint8_t value)
{
send(value, LCD_DATA);
}
#else
size_t LCD::write(uint8_t value)
{
send(value, LCD_DATA);
return 1;
}
#endif
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define SPEAKER 7
#define BUTTON_RED A0
#define BUTTON_GREEN A2
#define BUTTON_BLUE A1
#define BUTTON_YELLOW A3
#define LED_RED 6
#define LED_GREEN 3
#define LED_BLUE 4
#define LED_YELLOW 5
int melody_GameOver[] = {262, 196, 196, 220, 196, 0, 247, 262};
int melody_GameOverNOTES[] = {4, 8, 8, 4, 4, 4, 4, 4};
int currentLevel = 1;
int lvlSPEED = 500;
int levelSequencePlayer[20];
int levelSequence[20];
int levelEnd;
void setup(){
game_init();
}
void loop(){
if(currentLevel == 1){
display_start();
difficultySelect();
display_gameStatus();
showLevelSequence();
levelSequenceCheck();
}
if(currentLevel != 1){
display_gameStatus();
showLevelSequence();
levelSequenceCheck();
}
}
void display_start(){
boolean start = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("RetroBoy");
lcd.setCursor(0,1);
lcd.print("Press R to Start");
while(start == false){
if(digitalRead(BUTTON_RED) == LOW){
start = true;
sound_start();
}
}
}
void display_difficultySelection(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Select level");
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Easy-B Normal-G");
lcd.setCursor(0,1);
lcd.print("Hard-Y Lunatic-R");
}
void display_difficultySelected(String difficulty){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Selected:");
lcd.setCursor(0,1);
lcd.print(difficulty);
delay(2000);
}
void display_gameStatus(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Current Level:");
lcd.setCursor(0,1);
lcd.print(currentLevel);
}
void display_gameOverStatus(){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("GAME OVER");
lcd.setCursor(0, 1);
lcd.print("Final Score: ");
lcd.print(currentLevel);
}
void display_gameVictoryStatus(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Level Completed");
lcd.setCursor(0,1);
lcd.print("Final Score: ");
lcd.print(currentLevel);
}
void game_init(){
pinMode(BUTTON_RED, INPUT);
pinMode(BUTTON_GREEN, INPUT);
pinMode(BUTTON_BLUE, INPUT);
pinMode(BUTTON_YELLOW, INPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
int begin = 0;
ledsOFF();
lcd.begin(16,2);
}
void ledsON(){
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_BLUE, HIGH);
digitalWrite(LED_YELLOW, HIGH);
}
void ledsOFF(){
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_YELLOW, LOW);
}
void difficultySelect(){
ledsOFF();
display_difficultySelection();
int aux = 1;
while(aux == 1){
if(digitalRead(BUTTON_BLUE) == LOW){
digitalWrite(LED_BLUE, HIGH);
tone(SPEAKER, 500);
delay(300);
noTone(SPEAKER);
digitalWrite(LED_BLUE, LOW);
display_difficultySelected("Easy");
levelGenerator(0);
aux = 0;
}
else if(digitalRead(BUTTON_GREEN) == LOW){
digitalWrite(LED_GREEN, HIGH);
tone(SPEAKER, 500);
delay(300);
noTone(SPEAKER);
digitalWrite(LED_GREEN, LOW);
display_difficultySelected("Normal");
levelGenerator(1);
aux = 0;
}
else if(digitalRead(BUTTON_YELLOW) == LOW){
digitalWrite(LED_YELLOW, HIGH);
tone(SPEAKER, 500);
delay(300);
noTone(SPEAKER);
digitalWrite(LED_YELLOW, LOW);
display_difficultySelected("Hard");
levelGenerator(2);
aux = 0;
}
else if(digitalRead(BUTTON_RED) == LOW){
digitalWrite(LED_RED, HIGH);
tone(SPEAKER, 500);
delay(300);
noTone(SPEAKER);
digitalWrite(LED_RED, LOW);
display_difficultySelected("Lunatic");
levelGenerator(3);
aux = 0;
}
}
}
void levelGenerator(int difficulty){
randomSeed(millis());
if(difficulty==0){
for(int i = 0; i < 5; i++){
levelSequence[i] = rand() % (6 + 1 - 3) + 3;
}
levelEnd = 5;
}
else if(difficulty==1){
for(int i = 0; i < 10; i++){
levelSequence[i] = rand() % (6 + 1 - 3) + 3;
}
levelEnd = 10;
}
else if(difficulty==2){
for(int i = 0; i < 15; i++){
levelSequence[i] = rand() % (6 + 1 - 3) + 3;
}
levelEnd = 15;
}
else{
for(int i = 0; i < 20; i++){
levelSequence[i] = rand() % (6 + 1 - 3) + 3;
}
levelEnd = 20;
}
}
void showLevelSequence(){
ledsOFF();
for(int i = 0; i < currentLevel; i++){
if(levelSequence[i] == LED_RED){
tone(SPEAKER, 200);
delay(200);
noTone(SPEAKER);
}
if(levelSequence[i] == LED_GREEN){
tone(SPEAKER, 300);
delay(200);
noTone(SPEAKER);
}
if(levelSequence[i] == LED_BLUE){
tone(SPEAKER, 400);
delay(200);
noTone(SPEAKER);
}
if(levelSequence[i] == LED_YELLOW){
tone(SPEAKER, 500);
delay(200);
noTone(SPEAKER);
}
digitalWrite(levelSequence[i], HIGH);
delay(lvlSPEED);
digitalWrite(levelSequence[i], LOW);
delay(200);
}
}
void levelSequenceCheck(){
int aux = 0;
for(int i = 0; i < currentLevel; i++){
aux = 0;
while(aux == 0){
if(currentLevel == levelEnd+1){
victory();
}
if(digitalRead(BUTTON_YELLOW) == LOW){
digitalWrite(LED_YELLOW, HIGH);
tone(SPEAKER, 500);
delay(300);
noTone(SPEAKER);
levelSequencePlayer[i] = LED_YELLOW;
aux = 1;
delay(200);
if(levelSequencePlayer[i] != levelSequence[i]){
GameOver();
return;
}
digitalWrite(LED_YELLOW, LOW);
}
if(digitalRead(BUTTON_BLUE) == LOW){
digitalWrite(LED_BLUE, HIGH);
tone(SPEAKER, 400);
delay(300);
noTone(SPEAKER);
levelSequencePlayer[i] = LED_BLUE;
aux = 1;
delay(200);
if(levelSequencePlayer[i] != levelSequence[i]){
GameOver();
return;
}
digitalWrite(LED_BLUE, LOW);
}
if(digitalRead(BUTTON_GREEN) == LOW){
digitalWrite(LED_GREEN, HIGH);
tone(SPEAKER, 300);
delay(300);
noTone(SPEAKER);
levelSequencePlayer[i] = LED_GREEN;
aux = 1;
delay(200);
if(levelSequencePlayer[i] != levelSequence[i]){
GameOver();
return;
}
digitalWrite(LED_GREEN, LOW);
}
if(digitalRead(BUTTON_RED) == LOW){
digitalWrite(LED_RED, HIGH);
tone(SPEAKER, 200);
delay(300);
noTone(SPEAKER);
levelSequencePlayer[i] = LED_RED;
aux = 1;
delay(200);
if(levelSequencePlayer[i] != levelSequence[i]){
GameOver();
return;
}
digitalWrite(LED_RED, LOW);
}
}
}
if(currentLevel < 20){
currentLevel++;
lvlSPEED -= 50;
delay(200);
}
}
void sound_GameOver(){
for(int i = 0; i < 8; i++){
int notesDURATION = 1000/melody_GameOverNOTES[i];
tone(SPEAKER, melody_GameOver[i],melody_GameOverNOTES);
int notesPAUSE = notesDURATION * 1.30;
delay(notesPAUSE);
noTone(SPEAKER);
}
}
void sound_Victory(){
tone(SPEAKER,523.25,133);
delay(133);
tone(SPEAKER,523.25,133);
delay(133);
tone(SPEAKER,523.25,133);
delay(133);
tone(SPEAKER,523.25,400);
delay(400);
tone(SPEAKER,415.30,400);
delay(400);
tone(SPEAKER,466.16,400);
delay(400);
tone(SPEAKER,523.25,133);
delay(133);
delay(133);
tone(SPEAKER,466.16,133);
delay(133);
tone(SPEAKER,523.25,1200);
delay(1200);
}
void sound_start(){
tone(SPEAKER,523.25,133);
delay(133);
tone(SPEAKER,523.25,133);
delay(133);
tone(SPEAKER,523.25,133);
delay(133);
tone(SPEAKER,523.25,400);
delay(400);
}
void GameOver(){
ledsON();
delay(250);
ledsOFF();
delay(250);
display_gameOverStatus();
sound_GameOver();
delay(2000);
currentLevel = 1;
lvlSPEED = 500;
}
void victory(){
ledsON();
delay(250);
ledsOFF();
delay(250);
display_gameVictoryStatus();
sound_Victory();
delay(2000);
currentLevel = 1;
lvlSPEED = 500;
}