Maker Pro
Maker Pro

Tic-Tac-Toe game

bancroft

Apr 23, 2021
81
Joined
Apr 23, 2021
Messages
81
A couple of days ago, I had a sudden flash of inspiration that maybe learning a new LCD needs to start with a simple and interesting project, so I put my previous project on hold and decided to make a tic-tac-toe game first.
The same configuration as before, I have an LCD touch screen and an Arduino UNO, this is a newbie project, I uploaded the program to the board after I finished programming it, but that's when the problem occurred.
Code:
Arduino:1.8.12 (Windows 10), Development board: "Arduino Uno"

The project uses 6336 bytes, which occupies (19%) of the program storage space. The maximum is 32256 bytes. data section exceeds available space in board

Global variables used 3046 bytes, (148%) of dynamic memory, leaving -998 bytes for local variables. The maximum is 2048 bytes.
There is not enough memory; visit the following URL to follow the instructions to reduce memory usage.
http://www.arduino.cc/en/Guide/Troubleshooting#size
Error while compiling for development board Arduino Uno.

Turn on in File -> Preferences
"Show detailed output during compilation" option
This report will contain more information.
How should I solve this problem?
 

Bluejets

Oct 5, 2014
6,901
Joined
Oct 5, 2014
Messages
6,901
Use an Arduino with more memory.
Maybe a mega.
You might be able to add extra memory but it appears you don't know how.
If you visit the Arduino site I'm sure they will have many details there.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,700
Joined
Nov 17, 2011
Messages
13,700
Upload your code for inspection.
The "data section" is where the constant data of the program is stored. A typical reason for exceeding the available space is the excessive use of Strings in e.g. Serial.Println() statements which are often used for debugging.
  • remove (comment) all Serial.Print and Serial.Println statements that are not strictly necessary.
  • use PROGMEM to store large data sections. See also here.
  • Check the sizes (memory requirements) of your variables, especially of arrays. Use the smallest possible variable type where possible. Re-use variables if possible instead of creating new ones (e.g. loop counters).
See also here.
 

bancroft

Apr 23, 2021
81
Joined
Apr 23, 2021
Messages
81
Upload your code for inspection.
The "data section" is where the constant data of the program is stored. A typical reason for exceeding the available space is the excessive use of Strings in e.g. Serial.Println() statements which are often used for debugging.
  • remove (comment) all Serial.Print and Serial.Println statements that are not strictly necessary.
  • use PROGMEM to store large data sections. See also here.
  • Check the sizes (memory requirements) of your variables, especially of arrays. Use the smallest possible variable type where possible. Re-use variables if possible instead of creating new ones (e.g. loop counters).
See also here.
Thanks for the reminder that I should put up my code.
Code:
unsigned int r_flag1 = 0;
uint8_t   RecievedTemp1[30]       = {0};

uint8_t   cout_i = 0;
unsigned int quan_hang1 = 0;
unsigned int quan_hang2 = 0;
unsigned int quan_hang3 = 0;
unsigned int quan_lie1 = 0;
unsigned int quan_lie2 = 0;
unsigned int quan_lie3 = 0;
unsigned int quan_zuoxia = 0;
unsigned int quan_youxia = 0;


unsigned int cha_hang1 = 0;
unsigned int cha_hang2 = 0;
unsigned int cha_hang3 = 0;
unsigned int cha_lie1 = 0;
unsigned int cha_lie2 = 0;
unsigned int cha_lie3 = 0;
unsigned int cha_zuoxia = 0;
unsigned int cha_youxia = 0;
unsigned int cha[8]={0};
void setup()
{
   Serial.begin(115200);
   for(int i=3; i<=8; i++)
   pinMode(i,OUTPUT);
}

void loop(){

  if(Serial.available() != 0)
  {
//    for(cout_i = 0; cout_i < 30; cout_i ++)
//    {
//        //RecievedTemp1[cout_i] = Serial.readBytes(RecievedTemp1, 15);
//        Serial.readBytes(RecievedTemp1, 20);
//        //Serial.println(RecievedTemp1[cout_i]);
//    }
    Serial.readBytes(RecievedTemp1, 20);
//    for(cout_i = 0; cout_i < 19; cout_i ++)
//    {
//    Serial.println(RecievedTemp1[cout_i]);
//    }
    switch(RecievedTemp1[13])
  {
  case 49:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image2\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang1++;
        quan_lie1++;
        quan_youxia++;
//        quan[0]++;
//        quan[3]++;
//        quan[7]++;
        Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button1\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image2\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
            cha_hang1++;
            cha_lie1++;
            cha_youxia++;
//        cha[0]++;
//        cha[3]++;
//        cha[7]++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button1\",\"enable\":false}>ET");
        }
        
        break;
  case 50:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image3\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang1++;
            quan_lie2++;
//        quan[0]++;
//        quan[4]++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button2\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image3\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         cha_hang1++;
           cha_lie2++;
//        cha[0]++;
//        cha[4]++;
           Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button2\",\"enable\":false}>ET");
        }
        break;
  case 51:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image4\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang1++;
            quan_lie3++;
            quan_zuoxia++;
//        quan[0]++;
//        quan[5]++;
//        quan[6]++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button3\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image4\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         cha_hang1++;
           cha_lie3++;
            cha_zuoxia++;
//        cha[0]++;
//        cha[5]++;
//        cha[6]++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button3\",\"enable\":false}>ET");
        }
        break;
  case 52:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image5\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang2++;
            quan_lie1++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button4\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image5\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         cha_hang2++;
           cha_lie1++;
           Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button4\",\"enable\":false}>ET");
        }
        break;
  case 53:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image6\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang2++;
            quan_lie2++;
            quan_zuoxia++;
            quan_youxia++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button5\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image6\",\"image\":\"x\"}>ET");
          r_flag1 = 0;
          cha_hang2++;
            cha_lie2++;
            cha_zuoxia++;
            cha_youxia++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button5\",\"enable\":false}>ET");
        }
        break;       
  case 54:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image7\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang2++;
            quan_lie3++;
//        quan[1]++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button6\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image7\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         cha_hang2++;
           cha_lie3++;
           Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button6\",\"enable\":false}>ET");
        }
        break;       
  case 55:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image8\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang3++;
            quan_lie1++;
            quan_zuoxia++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button7\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image8\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         cha_hang3++;
           cha_lie1++;
            cha_zuoxia++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button7\",\"enable\":false}>ET");
        }
        break;       
  case 56:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image9\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang3++;
            quan_lie2++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button8\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image9\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         cha_hang3++;
           cha_lie2++;
           Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button8\",\"enable\":false}>ET");
        }
        break;       
  case 57:
        if((r_flag1 == 0)&&(RecievedTemp1[14]==2))
        {
        Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image10\",\"image\":\"circle\"}>ET");
        r_flag1 = 1;
        quan_hang3++;
            quan_lie3++;
            quan_youxia++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button9\",\"enable\":false}>ET");
        }
        else if((r_flag1 == 1)&&(RecievedTemp1[14]==2))
        {
          Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image10\",\"image\":\"x\"}>ET");
         r_flag1 = 0;
         quan_hang3++;
           quan_lie3++;
            quan_youxia++;
            Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button9\",\"enable\":false}>ET");
        }
        break;
  }
  if((quan_hang1==3)||(cha_hang1==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif4\",\"visible\":true}>ET");
      }
      else if((quan_hang2==3)||(cha_hang2==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif5\",\"visible\":true}>ET");
      }
      else if((quan_hang3==3)||(cha_hang3==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif6\",\"visible\":true}>ET");
      }
      else if((quan_lie1==3)||(cha_lie1==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif7\",\"visible\":true}>ET");
      }
      else if((quan_lie2==3)||(cha_lie2==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif8\",\"visible\":true}>ET");
      }
      else if((quan_lie3==3)||(cha_lie3==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif9\",\"visible\":true}>ET");
      }
      else if((quan_zuoxia==3)||(cha_zuoxia==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif10\",\"visible\":true}>ET");
      }
      else if((quan_youxia==3)||(cha_youxia==3))
      {
            Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif11\",\"visible\":true}>ET");
      }
  }
}
 

bancroft

Apr 23, 2021
81
Joined
Apr 23, 2021
Messages
81
Use an Arduino with more memory.
Maybe a mega.
You might be able to add extra memory but it appears you don't know how.
If you visit the Arduino site I'm sure they will have many details there.
Thanks for the reply, I was wondering if there was any other way to fix it, except for replacing the board.
I will also look for a solution on Arduino's website, thanks for the suggestion.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,700
Joined
Nov 17, 2011
Messages
13,700
The error is:
"Global variables use 3046 Bytes (148 %) of dynamic memory. -998 Bytes remaining for lokal variables " (or similar, I translated the German output from my Arduino IDE).
My initial assumption was right. You have a total of 42 Serial.Println() statements containing 3495 characters (quick count, give or take a few).
You have these options:
  1. If these print statements are for debugging only, delete or comment those you do not absolutely need.
  2. If this is not an option, put the print statements into subroutines and call these routines. See below.
  3. Improve the code structure so you don't need a SerialPrintln() statement in every then...else statement but rather set a variable within the statement, then use a single SerialPrintln() at the end of the sequence of if...then...else using this variable. I'm not going to elaborate that as I think it is unlikely you go that way.

Improving your code:
You have 42 print statements.
16 of the type code: set_enable
18 of the type code: set_image
8 of the type code: set_visible
Instead of placing a SerialPrintln() statement in each position, create 3 subroutines acc. to the following scheme:
Code:
void Print_Set_Enable(int number)
{
  Serial.print("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button");
  Serial.print(number);
  Serial.println("\",\"enable\":false}>ET");
}
Instead of the Serial.println() calls call this function with the number of the button as an argument like this:
Code:
//           Serial.println("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button2\",\"enable\":false}>ET");
           Print_Set_Enable(2);

Doing this for the above type of print statement alone reduces the data size to 2390 Bytes (116 %), still a bit too much but a noticeable improvement.
For statements with more than 1 variable content you need accordingliy multiple variables:
Code:
Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image2\",\"image\":\"circle\"}>ET");
Here you have image number (1) and image type ("circle") as variable content to be considered in the function call.
Do this for the other two types of print statement and you should be god to go. I'll leave it as an exercise to you to fill in the details:
Code:
void Print_Set_Enable(int number)
{
  Serial.print("ST<{\"cmd_code\":\"set_enable\",\"type\":\"widget\",\"widget\":\"button");
  Serial.print(number);
  Serial.println("\",\"enable\":false}>ET");
}

void Print_Set_Image()
{
}

void Print_Set_Visible()
{
 
}
void setup()
{
   Serial.begin(115200);
   for(int i=3; i<=8; i++)
   pinMode(i,OUTPUT);
}

void loop(){
...
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,700
Joined
Nov 17, 2011
Messages
13,700
As a completely different alternative, suitable for cases where you have many different lengthy print statements that do no invite for such a method using subroutines as described above: transfer the burden of storing lots of text to the PC.
You do this by assigning each event a unique identifier (code), e.g. an unsigned 16 Bit number (good for >65000 codes). Instead of printing a string, you print only a code and have the PC look up the respective long form of the text in a table. Easy to do e.g. within script that reads the output from the serial port and translates it into the text looked up from a table.

You can also combine the code method with the use of variable where required. Example:
Instead of 81 characters for this:
Code:
Serial.println("ST<{\"cmd_code\":\"set_image\",\"type\":\"image\",\"widget\":\"image2\",\"image\":\"circle\"}>ET");
You use 6 characters for this:
Code:
Serial.println("00AA1c");
Where
- 001A would be the code for "ST<{cmd_code:set_image,type:image,widget:'image_number', image:'image_type'}ET"
- 1 would be the code for image1 -> image_number = 1
- c would be the code for circle -> image_type = "circle"

This code snippet in Python shows the principle:
Code:
# dummy variable assignment. Put a read from the serial console here
SerialRead = "001A1c"
# split into code, arg1 and arg2
code = SerialRead[0:4]
arg1 = SerialRead[4:5]
arg2 = SerialRead[5:6]

if (code == "001A"):
   image_number = "image" + arg1
   if (arg2 == "c"):
      image_type = "circle"
   print("ST<{cmd_code:set_image,type:image,widget:" + image_number + ",image:" + image_type + "}ET")
Of course this can be solved much more elegantly using e.g. dictionaries. Or maybe you don't have to convert the code to text at all but use it directly to perform some other action on the PC. Also I used Python only as an example. Any other programming language will serve as well. Best one you are familiar with.
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,700
Joined
Nov 17, 2011
Messages
13,700
Maybe the most simple solution: use the F-macro. This places the strings into program memory, not data memory. Instead of
Code:
 Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif6\",\"visible\":true}>ET");
use
Code:
 Serial.println(F("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif6\",\"visible\":true}>ET"));
this reduces the data size to 252 Bytes (12 %).
 

bancroft

Apr 23, 2021
81
Joined
Apr 23, 2021
Messages
81
Maybe the most simple solution: use the F-macro. This places the strings into program memory, not data memory. Instead of
Code:
 Serial.println("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif6\",\"visible\":true}>ET");
use
Code:
 Serial.println(F("ST<{\"cmd_code\":\"set_visible\",\"type\":\"widget\",\"widget\":\"gif6\",\"visible\":true}>ET"));
this reduces the data size to 252 Bytes (12 %).
Thank you so much for your enthusiasm and patience in replying, you gave a lot of helpful advice, thank you very much ! I've decided to take the approach you mentioned F-macro to make the changes, and I will update if there are any new developments.
 

DBingaman

Jun 27, 2021
103
Joined
Jun 27, 2021
Messages
103
Do you need the range for unsigned int (4 bytes). Can you use throughout your code more unsigned short (2 bytes) or unsigned char (1 byte)?
 

bancroft

Apr 23, 2021
81
Joined
Apr 23, 2021
Messages
81
Do you need the range for unsigned int (4 bytes). Can you use throughout your code more unsigned short (2 bytes) or unsigned char (1 byte)?
Thank you for your reply, I have solved this problem by F().
 

shumifan50

Jan 16, 2014
579
Joined
Jan 16, 2014
Messages
579
Be aware that printing to serial has a small buffer and printing large strings will cause loop() to wait while the string is output. This can cause unpredictable results, especially if coms is used.
 

bancroft

Apr 23, 2021
81
Joined
Apr 23, 2021
Messages
81
I've solved the problem successfully and shared the demo video on youtube, thanks for helping me guys!
 

Wazir iqbal

Mar 10, 2022
2
Joined
Mar 10, 2022
Messages
2
I decided to create the classic game of Noughts and Crosses on the Arduino after being asked by my daughter if we could build a game together. Here is a video her playing the final version on the Arduino Mega. The second video is me demonstrating the Arduino Uno version. I included some artificial intelligence (rather than just random placement) for the Arduino so that it tries to win, tries to stop you from winning if it can't, and only then places randomly.
I used the following for the Arduino Mega version:
Here is the example of game: tic-tac-toe-beast
  • 1x Arduino (or Genuino) Mega
  • 1x very large breadboard
  • 1x half size breadboard
  • 10x green LEDs
  • 10x red LEDs
  • 10x push buttons
  • 1x LCD display
  • 20x 220Ω resistors
  • 10x 10kΩ resistors
  • 1x 10kΩ potentiometer (variable resistor)
  • Lots of jumper cables and wires
 
Top