`Although it is an article on Mac environment, the procedure is the same for Windows environment. Please read and try the environment-dependent part. ``
Create a math game using LCD1602A 16x2 I2C.
After reading this article to the end, you will be able to:
| No. | Overview | keyword | 
|---|---|---|
| 1 | Electronic circuit | |
| 2 | LCD control | SMBus | 
| 3 | Button control | gpiozero | 
| screen | Button 1 | Button 2 | 
|---|---|---|
| menu | Selection of addition and subtraction | Decision | 
| addition | Return to the menu by displaying the next question and displaying the 11th question | Displaying the answer | 
| subtraction | Return to the menu by displaying the next question and displaying the 11th question | Displaying the answer | 
| menu | addition | subtraction | 
|---|---|---|
![]()  | 
![]()  | 
![]()  | 
![]()  | 
![]()  | 
![]()  | 
![]()  | 
![]()  | 
![]()  | 
| environment | Ver. | 
|---|---|
| macOS Catalina | 10.15.6 | 
| Raspberry Pi 4 Model B 4GB RAM | - | 
| Raspberry Pi OS (Raspbian) | 10 | 
| Python | 3.7.3 | 
| gpiozero | 1.5.1 | 
| RPi.GPIO | 0.7.0 | 
| smbus2 | 0.3.0 | 
I think that understanding will deepen if you read while actually following the implementation contents and source code. Please use it by all means.
-From Raspberry Pi setup to Python environment installation -Create a remote control car with Raspberry Pi and Python
command.sh
~$ sudo raspi-config
5 Interfacing OptionsP5 I2Ccommand.sh
~$ sudo reboot
command.sh
~$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
The I2C communication address will be 0x27
command.sh
~$ git clone https://github.com/nsuhara/raspi-lcdgame.git -b master
~$ cd raspi-lcdgame
~$ python -m venv .venv
~$ source .venv/bin/activate
~$ pip install -r requirements.txt
~$ source config
~$ python app/main.py
~$ Control Key + C
target.sh
/app
├─ addition.py
├─ i2clcd1602a
│      ├─ __init__.py
│      ├─ katakana.py
│      └─ pcf8574t.py
├─ main.py
├─ menu.py
├─ story_board.py
└─ subtraction.py
LCD
target.sh
/app
└─ i2clcd1602a
       ├─ __init__.py
       ├─ katakana.py
       └─ pcf8574t.py
pcf8574t.py
"""i2clcd1602a/pcf8574t.py
"""
import time
from smbus2 import SMBus
from i2clcd1602a.katakana import convert
class LCD():
    """LCD
    """
    SMBUS_REV1 = 0
    SMBUS_REV2 = 1
    I2C_ADDR = 0x27
    ENABLE_BIT = 0b00000100
    LINE_1 = 0x80
    LINE_2 = 0xC0
    LINE_SIZE = 16
    MODE_COMMAND = 0
    MODE_CHAR = 1
    MODE_OPTION_BACKLIGHT = 0x08
    CMD_INITIALISE = 0x33
    CMD_SET_4_BIT_MODE = 0x32
    CMD_CURSOR_MOVE_DIRECTION = 0x06
    CMD_TURN_CURSOR_OFF = 0x0C
    CMD_2_LINE_DISPLAY = 0x28
    CMD_CLEAR_DISPLAY = 0x01
    CMD_WAIT = 0.0005
    def __init__(self):
        self.smbus = SMBus(self.SMBUS_REV2)
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_INITIALISE)
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_SET_4_BIT_MODE)
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_CURSOR_MOVE_DIRECTION)
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_TURN_CURSOR_OFF)
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_2_LINE_DISPLAY)
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_CLEAR_DISPLAY)
    def destroy(self):
        """destroy
        """
        self._send(mode=self.MODE_COMMAND, bits=self.CMD_CLEAR_DISPLAY)
        self.smbus.close()
    def message(self, message, line):
        """message
        """
        message = convert(message=message)
        message = message[0:self.LINE_SIZE]
        message = message.ljust(self.LINE_SIZE, ' ')
        self._send(mode=self.MODE_COMMAND | self.MODE_OPTION_BACKLIGHT,
                   bits=line)
        for char in message:
            self._send(mode=self.MODE_CHAR | self.MODE_OPTION_BACKLIGHT,
                       bits=ord(char))
    def clear(self):
        """close
        """
        self._send(mode=self.MODE_COMMAND | self.MODE_OPTION_BACKLIGHT,
                   bits=self.CMD_CLEAR_DISPLAY)
    def off(self):
        """off
        """
        self.destroy()
    def _send(self, mode, bits):
        """_send
        """
        higher_bits = mode | (bits & 0xF0)
        self._write(bits=higher_bits)
        lower_bit = mode | ((bits << 4) & 0xF0)
        self._write(bits=lower_bit)
    def _write(self, bits):
        """_write
        """
        self.smbus.write_byte(self.I2C_ADDR, bits)
        time.sleep(self.CMD_WAIT)
        self.smbus.write_byte(self.I2C_ADDR, (bits | self.ENABLE_BIT))
        time.sleep(self.CMD_WAIT)
        self.smbus.write_byte(self.I2C_ADDR, (bits & ~self.ENABLE_BIT))
        time.sleep(self.CMD_WAIT)
    def loop(self):
        """loop
        """
        while True:
            self.message(message='1234567890123456', line=self.LINE_1)
            self.message(message='abcdefghijklmnop', line=self.LINE_2)
            time.sleep(2)
            self.message(message='ABCDEFGHIJKLMNOP', line=self.LINE_1)
            self.message(message='Sashisuseso', line=self.LINE_2)
            time.sleep(2)
if __name__ == '__main__':
    lcd = LCD()
    try:
        print('start')
        lcd.loop()
    except KeyboardInterrupt:
        lcd.destroy()
        print('stop')
katakana.sh
"""i2clcd1602a/katakana.py
"""
SP_CODE = 0xfec0
def convert(message):
    """convert
    """
    converted = []
    for char in message:
        if ord(char) > SP_CODE:
            converted.append(chr(ord(char)-SP_CODE))
        else:
            converted.append(char)
    return ''.join(converted)
target.sh
/app
└─ main.py
main.py
"""app/main.py
"""
from signal import pause
from time import sleep
from gpiozero import Button
from addition import Addition
from i2clcd1602a.pcf8574t import LCD
from menu import Menu
from subtraction import Subtraction
class Raspi():
    """Raspi
    """
    GPIO_BCM_BUTTON1 = 15
    GPIO_BCM_BUTTON2 = 25
    def __init__(self):
        self.lcd = LCD()
        self.button1 = Button(self.GPIO_BCM_BUTTON1)
        self.button1.when_pressed = self.button1_pressed
        self.button1.when_released = self.button1_released
        self.button2 = Button(self.GPIO_BCM_BUTTON2)
        self.button2.when_pressed = self.button2_pressed
        self.button2.when_released = self.button2_released
        self.status = 'menu'
        self.display = {
            'menu': Menu(self),
            'addition': Addition(self),
            'subtraction': Subtraction(self)
        }
        self.display.get(self.status).top()
    def destroy(self):
        """destroy
        """
        self.lcd.off()
    def button1_pressed(self):
        """button1_pressed
        """
        self.display.get(self.status).button1_pressed()
    def button1_released(self):
        """button1_released
        """
        self.display.get(self.status).button1_released()
    def button2_pressed(self):
        """button2_pressed
        """
        self.display.get(self.status).button2_pressed()
    def button2_released(self):
        """button2_released
        """
        self.display.get(self.status).button2_released()
    def pause(self):
        """pause
        """
        pause()
if __name__ == '__main__':
    app = Raspi()
    try:
        print('start')
        app.pause()
    except KeyboardInterrupt:
        app.lcd.clear()
        app.lcd.message(message='Good by...', line=app.lcd.LINE_1)
        sleep(1)
        app.destroy()
        print('stop')
target.sh
/app
└─ story_board.py
story_board.py
"""app/story_board.py
"""
import abc
class StoryBoard(metaclass=abc.ABCMeta):
    """StoryBoard
    """
    def __init__(self):
        pass
    @abc.abstractmethod
    def top(self):
        pass
    @abc.abstractmethod
    def button1_pressed(self):
        pass
    @abc.abstractmethod
    def button1_released(self):
        pass
    @abc.abstractmethod
    def button2_pressed(self):
        pass
    @abc.abstractmethod
    def button2_released(self):
        pass
target.sh
/app
├─ menu.py
└─ story_board.py
menu.py
"""app/menu.py
"""
from story_board import StoryBoard
class Menu(StoryBoard):
    """Menu
    """
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.lcd = app.lcd
        self.status = None
    def top(self):
        """top
        """
        self.lcd.message(message='Menu', line=self.lcd.LINE_1)
        self.lcd.message(message='>Tashizan Hikizan', line=self.lcd.LINE_2)
        self.status = 'addition'
    def button1_pressed(self):
        """button1_pressed
        """
        if self.status == 'addition':
            self.lcd.message(message='Tashizan>Hiki Zan', line=self.lcd.LINE_2)
            self.status = 'subtraction'
        else:
            self.lcd.message(message='>Tashizan Hikizan', line=self.lcd.LINE_2)
            self.status = 'addition'
    def button1_released(self):
        """button1_released
        """
        print('>>> button1_released')
    def button2_pressed(self):
        """button2_pressed
        """
        if self.status == 'addition':
            self.app.status = 'addition'
        else:
            self.app.status = 'subtraction'
        self.app.display.get(self.app.status).top()
    def button2_released(self):
        """button2_released
        """
        print('>>> button2_released')
target.sh
/app
├─ addition.py
└─ story_board.py
addition.py
"""app/addition.py
"""
from random import randint
from story_board import StoryBoard
class Addition(StoryBoard):
    """Addition
    """
    MAXIMUM = 5
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.lcd = app.lcd
        self.question = 0
        self.num1 = 0
        self.num2 = 0
    def top(self):
        """top
        """
        self.question = 0
        self.num1 = 0
        self.num2 = 0
        self.lcd.message(message='Addition game^o^', line=self.lcd.LINE_1)
        self.lcd.message(message='Kimiha Wakaru Kana?', line=self.lcd.LINE_2)
    def button1_pressed(self):
        """button1_pressed
        """
        self.question = self.question + 1
        if self.question > 10:
            self.app.status = 'menu'
            self.app.display.get(self.app.status).top()
            return
        self.num1 = randint(1, self.MAXIMUM)
        self.num2 = randint(1, self.MAXIMUM)
        self.lcd.message(message='Q{}) {} + {} = ?'.format(str(self.question).zfill(2), self.num1,
                                                           self.num2), line=self.lcd.LINE_1)
        self.lcd.message(message='Kotae)', line=self.lcd.LINE_2)
    def button1_released(self):
        """button1_released
        """
        print('>>> button1_released')
    def button2_pressed(self):
        """button2_pressed
        """
        if self.num1 == 0 or self.num2 == 0:
            return
        self.lcd.message(message='Kotae) {}'.format(
            self.num1+self.num2), line=self.lcd.LINE_2)
    def button2_released(self):
        """button2_released
        """
        print('>>> button2_released')
target.sh
/app
├─ story_board.py
└─ subtraction.py
subtraction.py
"""app/subtraction.py
"""
from random import randint
from story_board import StoryBoard
class Subtraction(StoryBoard):
    """Subtraction
    """
    MAXIMUM = 5
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.lcd = app.lcd
        self.question = 0
        self.num1 = 0
        self.num2 = 0
    def top(self):
        """top
        """
        self.question = 0
        self.num1 = 0
        self.num2 = 0
        self.lcd.message(message='Hiki Zan Game^o^', line=self.lcd.LINE_1)
        self.lcd.message(message='Kimiha Wakaru Kana?', line=self.lcd.LINE_2)
    def button1_pressed(self):
        """button1_pressed
        """
        self.question = self.question + 1
        if self.question > 10:
            self.app.status = 'menu'
            self.app.display.get(self.app.status).top()
            return
        self.num1 = randint(1, self.MAXIMUM)
        self.num2 = randint(1, self.MAXIMUM)
        if self.num1 < self.num2:
            temp = self.num1
            self.num1 = self.num2
            self.num2 = temp
        self.lcd.message(message='Q{}) {} - {} = ?'.format(str(self.question).zfill(2), self.num1,
                                                           self.num2), line=self.lcd.LINE_1)
        self.lcd.message(message='Kotae)', line=self.lcd.LINE_2)
    def button1_released(self):
        """button1_released
        """
        print('>>> button1_released')
    def button2_pressed(self):
        """button2_pressed
        """
        if self.num1 == 0 or self.num2 == 0:
            return
        self.lcd.message(message='Kotae) {}'.format(
            self.num1-self.num2), line=self.lcd.LINE_2)
    def button2_released(self):
        """button2_released
        """
        print('>>> button2_released')
        Recommended Posts