LCD not working on Tinkercad - Also Loop on the code

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

Show the complete error in a code box just like you showed your sketch.

The error is literally just

invalid header file

One of your ".h" files is not in Tinkercad.

Use a Arduino "starter" in Tinkercad for the I2C display. That works. Then slowly adjust the code while keeping it working. You can not just use any library or any code. Only the combination that they provided in the "starter".

There is an other simulator: "Wokwi".
Wokwi simulates the hardware. Any code or any language or any library or any build environment can be used for that simulated hardware.

But first, you have to use the buttons in the right way.
Here are two examples for the buttons:

  1. Button Example One (active high) - Wokwi ESP32, STM32, Arduino Simulator
  2. Button Example Two (active low) - Wokwi ESP32, STM32, Arduino Simulator

When making the circuit, can you be more careful ? Use black for GND wires and red for 5V wires.

1 Like

Here is an example of what it could be in Wokwi:

The code uses arrays:

// A framework for 4 leds with 4 buttons and a display
// 24 May 2024
// by Koepel, Public Domain
// Using the hd44780 library.

#include <Wire.h>
#include <hd44780.h>                       // common hd44780 include
#include <hd44780ioClass/hd44780_I2Cexp.h> // specific I2C lcd

hd44780_I2Cexp lcd;

// Order: R G B Y
const int ledPins[4] = {4, 5, 6, 7};
const int buttonPins[4] = {10, 9, 12, 11};
const int buzzerPin = 8;

const char *Label[4] = { "Red", "Green", "Blue", "Yellow"};

int oldButtonValue[4] = {false};

void setup() 
{
  Serial.begin(115200);

  for(int i=0; i<4; i++)
  {
    pinMode(ledPins[i], OUTPUT);
    pinMode(buttonPins[i], INPUT_PULLUP);
  }

  int error = lcd.begin(16, 2);
  if(error != 0)
  {
    Serial.println("Display not found !");
  }

  // Show something on the display
  lcd.print("Hello");

  // Blink the leds
  for(int i=0; i<4; i++)
  {
    for(int j=0; j<4; j++)
    {
      digitalWrite(ledPins[i], HIGH);
      delay(150);
      digitalWrite(ledPins[i], LOW);
      delay(150);
    }
  }

  // Make sound with the buzzer
  for(int i=330; i<8000; i*=1.1)
  {
    tone(buzzerPin, i);
    delay(40);
  }
  noTone(buzzerPin);

  Serial.println("Press the buttons");
}

void loop() 
{
  bool button[4] = {false};

  // Read the buttons
  for(int i=0; i<4; i++)
  {
    int value = digitalRead(buttonPins[i]);
    if(value == LOW)
      button[i] = true;
    else
      button[i] = false;
  }

  for(int i=0; i<4; i++)
  {
    if(button[i] != oldButtonValue[i])
    {
      Serial.print("The ");
      Serial.print(Label[i]);
      Serial.print(" is ");
      Serial.print( button[i] ? "pressed" : "released");
      Serial.println();
      oldButtonValue[i] = button[i];
    }
  }

  delay(25);  // also for debouncing the buttons
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.