Skip to content

Programming

For me, I will be using C language with Arduino IDE to do coding.

Get I2C address

First thing first, we need to get the I2C address to use the I2C LCD.
And I found this code from Here:

#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("\nI2C Scanner");
}

void loop() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
  delay(5000);          
}

So my I2C LCD address is 0x26

Testing the I2C LCD & IR sensor

If the IR sensor detects nothing, reset IR_status, and shows the score and status of the sensor in the LCD.
If the IR sensor detects anything, the score increase by 1, and shows the score and status of the sensor in the LCD.
If the IR sensor keeps detecting nothing, shows the score and status of the sensor on the LCD.

Note: IR_status is a value used to prevent the score keep increasing in a detection.

#include <LiquidCrystal_I2C.h>

#define IRSensor (14)

LiquidCrystal_I2C lcd(0x26,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

int Score = 0;

int IR_staus = 0;

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

  pinMode(IRSensor, INPUT); // sensor pin INPUT

  lcd.init();                      // initialize the lcd
  lcd.backlight();
  lcd.print(">   --------   <");
  lcd.setCursor(0,1);
  lcd.print("Hello, world!");

  lcd.setCursor(0,1);
  lcd.print("IR: ");
  lcd.setCursor(6,1);
  lcd.print("Count: ");
}

void loop() {

  int statusSensor = digitalRead(IRSensor);

  if (statusSensor == 1 && IR_staus == 1) {
    IR_staus = 0;    
    lcd.setCursor(4,1);
    lcd.print(statusSensor);
    lcd.setCursor(13,1);
    lcd.print(Score);
  } else if (statusSensor == 0 && IR_staus == 0) {
    Score = Score + 1;
    IR_staus = 1; 
    lcd.setCursor(4,1);
    lcd.print(statusSensor);
    lcd.setCursor(13,1);
    lcd.print(Score);
  } else {
    lcd.setCursor(4,1);
    lcd.print(statusSensor);
    lcd.setCursor(13,1);
    lcd.print(Score);
  }

}

Result

And here is the result:


Final Code

The difficulties can be changed by the white button.
Start the game by pushing the black button.
After 3 sec countdown, the ESP32 will count the number of things that pass through the IR sensor.
The score and time willing showing on the I2C LCD.
At the same time the brushless DC motor will make the balls fly. The frequency of changing speed were depends on the difficulties selected by the player.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#include <pitches.h>
#include <Tone32.h>

#include <ESP32Servo.h> // ESP32Servo library installed by Library Manager
#include "ESC.h" // RC_ESP library installed by Library Manager
#include <analogWrite.h>
#include <ESP32Tone.h>
#include <ESP32PWM.h>

#include <LiquidCrystal_I2C.h>

//#include <digitalWriteFast.h>

#define RotateServo (25)
#define IRSensor (14)
#define ESC_PIN (13) // connected to ESC control wire
#define Button_1 (27)
#define Button_2 (32)
#define Button_3 (26)

#define MIN_SPEED 1244 // speed just slow enough to turn motor off
#define MAX_SPEED 1900 // speed where my motor drew 3.6 amps at 12v.

LiquidCrystal_I2C lcd(0x26,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

Servo Rotate;     // create servo object to control the ESC

int angle = 0;

int angleMin = 30;
int angleMax = 150;

ESC MotorESC (ESC_PIN, 1000, 2000, 500); // ESC_Name (PIN, Minimum Value, Maximum Value, Arm Value)

int Speed = 0;

int PlayTime = 31;

int countDownSec = PlayTime;

int Score = 0;

int IR_staus = 0;
int BT_staus = 0;
int waiting_staus = 0;
int rotate_staus = 0;
int motor_staus = 0;

int started = 0;

float waiting = 0;

int difficulty = 0;

int counter1 = 0;
int counter2 = 0;

unsigned long previousMillis = 0; 

const long interval = 1000;

void setup() {
  Serial.begin(115200);
  delay(1000);
  pinMode(ESC_PIN, OUTPUT);
  MotorESC.arm(); // Send the Arm command to ESC
  delay(5000); // Wait a while

  Rotate.attach(RotateServo);
  Rotate.write((angleMax + angleMin) / 2);

  randomSeed(analogRead(0));

  pinMode(IRSensor, INPUT); // sensor pin INPUT
  pinMode(Button_1  , INPUT_PULLUP);
  pinMode(Button_2  , INPUT_PULLUP);
  pinMode(Button_3  , INPUT_PULLUP);

  lcd.init();                      // initialize the lcd
  lcd.backlight();
  lcd.print(">   --------   <");
  lcd.setCursor(0,1);
  lcd.print(">START     EASY<");

  // the following loop turns on the motor slowly, so get ready
  for (int i=0; i<350; i++){ // run speed from 840 to 1190
    MotorESC.speed(MIN_SPEED-200+i); // motor starts up about half way through loop
    delay(10);
  }
  MotorESC.speed(0);
}

void waitforuser() {
  if (waiting <= 1) {
    lcd.setCursor(0,0);
    lcd.print(">BALL-IN       <");
  } else if (waiting <= 2) {
    lcd.setCursor(0,0);
    lcd.print("> BALL-IN      <");
  } else if (waiting <= 3) {
    lcd.setCursor(0,0);
    lcd.print(">  BALL-IN     <");
  } else if (waiting <= 4) {
    lcd.setCursor(0,0);
    lcd.print(">   BALL-IN    <");
  } else if (waiting <= 5) {
    lcd.setCursor(0,0);
    lcd.print(">    BALL-IN   <");
  } else if (waiting <= 6) {
    lcd.setCursor(0,0);
    lcd.print(">     BALL-IN  <");
  } else if (waiting <= 7) {
    lcd.setCursor(0,0);
    lcd.print(">      BALL-IN <");
  } else if (waiting <= 8) {
    lcd.setCursor(0,0);
    lcd.print(">       BALL-IN<");
  } else {
    lcd.setCursor(0,0);
    lcd.print("> ERROR  ERROR <");
  }    
}

void showScore() {
  lcd.setCursor(7,0);
  lcd.print(Score);
}

void showTime() {
  lcd.setCursor(6,1);
  lcd.print(countDownSec);
  lcd.print("  ");
  Serial.println((String) "Sec: " + countDownSec);
}

void reset() {
  difficulty = 0;
  countDownSec = PlayTime;
  Score = 0;
  counter1 = 0;
  counter2 = 0;
  Speed = 0;
  MotorESC.speed(Speed);
  lcd.setCursor(0,1);
  lcd.print(">START     EASY<");
  started = 0;
}

void easy_mode() {
  if (counter1 == 80) {
    if (angle >= angleMax && rotate_staus != 1) {
      rotate_staus = 1;
    } else if (angle <= angleMin && rotate_staus != 0) {
      rotate_staus = 0;
    } else if (rotate_staus == 0) {
      angle = angle + random(2, 4);
    } else if (rotate_staus == 1) {
      angle = angle - random(2, 4);
    } 
    if (angle > angleMax) {
      angle = angleMax;
    } 
    if (angle < angleMin) {
      angle = angleMin;
    }
    Serial.println((String) "Angle: " + angle);
    Rotate.write(angle);
    counter1 = 0;
  }
  else {
    counter1++;
  }


  if (counter2 == 100) {
    if (Speed >= MAX_SPEED && motor_staus != 1) {
      motor_staus = 1;
    } else if (Speed <= MIN_SPEED && motor_staus != 0) {
      motor_staus = 0;
    } else if (motor_staus == 0) {
      Speed = Speed + 75;
    } else if (motor_staus == 1) {
      Speed = Speed - 75;
    } 
    if (Speed > MAX_SPEED) {
      Speed = MAX_SPEED;
    } 
    if (Speed < MIN_SPEED) {
      Speed = MIN_SPEED;
    }
    Serial.println((String) "Speed: " + Speed);
    MotorESC.speed(Speed);
    counter2 = 0;
  }
  else {
    counter2++;
  }
}

void normal_mode() {
  if (counter1 == 80) {
    if (angle >= angleMax && rotate_staus != 1) {
      rotate_staus = 1;
    } else if (angle <= angleMin && rotate_staus != 0) {
      rotate_staus = 0;
    } else if (rotate_staus == 0) {
      angle = angle + random(1, 8);
    } else if (rotate_staus == 1) {
      angle = angle - random(1, 8);
    } 
    if (angle > angleMax) {
      angle = angleMax;
    } 
    if (angle < angleMin) {
      angle = angleMin;
    }
    Serial.println((String) "Angle: " + angle);
    Rotate.write(angle);
    counter1 = 0;
  }
  else {
    counter1++;
  }

  if (counter2 == 100) {
    if (Speed >= MAX_SPEED && motor_staus != 1) {
      motor_staus = 1;
    } else if (Speed <= MIN_SPEED && motor_staus != 0) {
      motor_staus = 0;
    } else if (motor_staus == 0) {
      Speed = Speed + random(25, 100);
    } else if (motor_staus == 1) {
      Speed = Speed - random(25, 200);
    } 
    if (Speed > MAX_SPEED) {
      Speed = MAX_SPEED;
    } 
    if (Speed < MIN_SPEED) {
      Speed = MIN_SPEED;
    }
    Serial.println((String) "Speed: " + Speed);
    MotorESC.speed(Speed);
    counter2 = 0;
  }
  else {
    counter2++;
  }
}

void hard_mode() {
  if (counter1 == 80) {
    if (angle >= angleMax && rotate_staus != 1) {
      rotate_staus = 1;
    } else if (angle <= angleMin && rotate_staus != 0) {
      rotate_staus = 0;
    } else if (rotate_staus == 0) {
      angle = angle + random(1, 16);
    } else if (rotate_staus == 1) {
      angle = angle - random(1, 16);
    } 
    if (angle > angleMax) {
      angle = angleMax;
    } 
    if (angle < angleMin) {
      angle = angleMin;
    }
    Serial.println((String) "Angle: " + angle);
    Rotate.write(angle);
    counter1 = 0;
  }
  else {
    counter1++;
  }


  if (counter2 == 100) {
    if (Speed >= MAX_SPEED && motor_staus != 1) {
      motor_staus = 1;
    } else if (Speed <= MIN_SPEED && motor_staus != 0) {
      motor_staus = 0;
    } else if (motor_staus == 0) {
      Speed = Speed + random(50, 200);
    } else if (motor_staus == 1) {
      Speed = Speed - random(50, 200);
    } 
    if (Speed > MAX_SPEED) {
      Speed = MAX_SPEED;
    } 
    if (Speed < MIN_SPEED) {
      Speed = MIN_SPEED;
    }
    Serial.println((String) "Speed: " + Speed);
    MotorESC.speed(Speed);
    counter2 = 0;
  }
  else {
    counter2++;
  }
}

void loop() {

  unsigned long currentMillis = millis();

  int statusSensor = digitalRead(IRSensor);

  int Pushed_1 = digitalRead(Button_1);
  int Pushed_2 = digitalRead(Button_2);
  int Pushed_3 = digitalRead(Button_3);

  Serial.println(Pushed_1);
  Serial.println(Pushed_2);
  Serial.println(Pushed_3);

  if (started == 1) {
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      countDownSec--;
      if (countDownSec < 0) {
        countDownSec = 0;
      }
      showTime();
    }
    if (countDownSec > 0 && Pushed_1 != LOW && Pushed_3 == LOW) {
      if (difficulty == 0){         // easy mode
        easy_mode();
      } else if (difficulty == 1) { // normal mode
        normal_mode();
      } else if (difficulty == 2) { // hard mode
        hard_mode();
      } 
      Serial.println((String) "IR: " + statusSensor);
      if (statusSensor == 1 && IR_staus == 1) {
        IR_staus = 0;    
        showScore();
      } else if (statusSensor == 0 && IR_staus == 0) {
        Score++;
        IR_staus = 1; 
        showScore();
      } else {
        showScore();
      }
    } else {
      Speed = 0;
      MotorESC.speed(Speed);
      lcd.clear();
      lcd.setCursor(0,1);
      lcd.print("Gameover!");
      lcd.setCursor(0,0);
      lcd.print("Score: ");
      lcd.print(Score);
      lcd.setCursor(15,1);
      lcd.print("3");
      delay(1000);
      lcd.setCursor(15,1);
      lcd.print("2");
      delay(1000);
      lcd.setCursor(15,1);
      lcd.print("1");
      delay(1000);
      reset();
    }
  } else {
    if (Pushed_1 == LOW) {
      started = 1;
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Start In...");
      lcd.setCursor(0,1);
      lcd.print("3");
      delay(1000);
      lcd.setCursor(0,1);
      lcd.print("2");
      delay(1000);
      lcd.setCursor(0,1);
      lcd.print("1");
      delay(1000);
      lcd.clear();
      lcd.setCursor(0,1);
      lcd.print("Time:");
      lcd.setCursor(0,0);
      lcd.print("Score:");
    } else if (Pushed_2 == LOW) {
      if (difficulty == 0 && BT_staus == 0) {
        BT_staus = 1;
        difficulty = 1;   // easy > normal
        lcd.setCursor(9,1);
        lcd.print("NORMAL");
      } else if (difficulty == 1 && BT_staus == 0) {
        BT_staus = 1;
        difficulty = 2;   // normal > hard
        lcd.setCursor(9,1);
        lcd.print("  HARD");
      } else if (difficulty == 2 && BT_staus == 0) {
        BT_staus = 1;
        difficulty = 0;   // hard > easy
        lcd.setCursor(9,1);
        lcd.print("  EASY");
      }
    } else {
      BT_staus = 0;
      if (waiting == 8 && waiting_staus != 1) {
        waiting_staus = 1;
      } else if (waiting == 0 && waiting_staus != 0) {
        waiting_staus = 0;
      } else if (waiting_staus == 0) {
        waiting = waiting + 0.25;
        waitforuser();
      } else if (waiting_staus == 1)
        waiting = waiting - 0.25;
        waitforuser();
      } 
  }
}

Result

And here is the result:


Reference

For making the countdown, I referenced to this example code:

/*
  Blink without Delay

  Turns on and off a light emitting diode (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

  The circuit:
  - Use the onboard LED.
  - Note: Most Arduinos have an on-board LED you can control. On the UNO, MEGA
    and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN
    is set to the correct LED pin independent of which board is used.
    If you want to know what pin the on-board LED is connected to on your
    Arduino model, check the Technical Specs of your board at:
    https://www.arduino.cc/en/Main/Products

  created 2005
  by David A. Mellis
  modified 8 Feb 2010
  by Paul Stoffregen
  modified 11 Nov 2013
  by Scott Fitzgerald
  modified 9 Jan 2017
  by Arturo Guadalupi

  This example code is in the public domain.

  https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
*/

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

For driving a brushless motor with an ESC from a ESP32, I referenced to this:
https://esp32.com/viewtopic.php?t=20450#p74915


Last update: June 29, 2022