Banana Pi BPI-P2 Zero Getting Started

I purchased a few sensors and a Banana Pi BPI-P2Zero partly to teach my daughter, but mostly because I had an excuse to do so.

Overview

My overall goal is to get the Scratch programming language to control the I/O connected to this demo setup as an introduction to my 6-year-old. The initial tasks documented within this section are basic configuration, simple LCD interface test and simple relay interface testing.

Part List

The following are the major components purchased for this project. Other items, such as the perf board and terminal blocks, I had on hand.

CategoryDescriptionUnit PriceQuantityLink
ControllerBanana Pi BPI-P2 Zero Allwinner H3 Quad-core Single Board Computer$34.991https://www.amazon.com/gp/product/B0B9RLGH3Z/ref=ppx_yo_dt_b_asin_title_o04_s01?ie=UTF8&psc=1
LCDGeeekPi 2-Pack I2C 1602 LCD Display Module 16X2$9.99 (pack of 2)1https://www.amazon.com/gp/product/B0BCWFB5P3/ref=ppx_yo_dt_b_asin_title_o04_s01?ie=UTF8&psc=1
Motion SensorWWZMDiB 5Pcs AM312 Mini Pir Motion Sensor Module HC-SR312$9.99 (pack of 5)1https://www.amazon.com/gp/product/B0CCF52DVJ/ref=ppx_yo_dt_b_asin_title_o04_s01?ie=UTF8&psc=1
Relay ModuleSonghe DC 1 Channel Optocoupler 3V/3.3V Relay High Level Driver Module Isolated Drive Control Board 3V/3.3V Relay Module$9.99 (pack of 5)1https://www.amazon.com/gp/product/B07XGZSYJV/ref=ppx_yo_dt_b_asin_title_o04_s00?ie=UTF8&psc=1

Wiring Diagram

I'll transfer this to an actual diagram later, but here is my napkin sketch.

BPi Setup

I chose the Banana Pi for a potentially different project that requires a physical Ethernet connection.

Image Install

  1. Install balenaEtcher if you don't already have it

  2. Download the target image (I went with 2020-04-28-raspbian-stretch-bpi-m2z-sd-emmc.img)

    1. https://wiki.banana-pi.org/Banana_Pi_BPI-P2_Zero

    2. I chose "2020-04-28 update, Debian 9, Rasbian Stretch, Ubuntu 16.04 Mate Desktop and Ubuntu 16.04 Server images, base on kernel 4.4" to start with from https://drive.google.com/drive/folders/1uRE8BppgDjK2TXH5kUIJ1_YrbAAW3HKF

  3. Flashed a 32 GB micro SD card with balenaEtcher from Windows with the target image.

Ethernet Configuration

By default, the Ethernet connection is DHCP. You have a few options for initial boot.

Initial Boot - Easiest Method

The easiest is if you have on hand a micro HDMI converter, USB hub, keyboard and mouse. You can use the terminal through the GUI and don't need to worry about setting.

Initial Boot - Headless

If you don't have the micro HDMI converter like I didn't when I first powered it up, you'll need to find the assigned IP address. (I have used Wireshark and/or checked my router for the assigned)

You'll also need to enable the SSH server. To do this, you can simply create an empty file named "ssh" in the root of the SDcard. (https://phoenixnap.com/kb/enable-ssh-raspberry-pi)

When you power-up and have the IP address, use PuTTy or equivalent to connect. (default credentials: u:pi, p:bananapi)

# Upon attempting to edit /etc/network/interfaces, a comment in the file redirected me to /etc/dhcpcd.conf
sudo nano  /etc/dhcpcd.conf

# Since there was an issue with Wifi for me, I configured for DHCP, but with a fallback profile
# it was mostly a matter of uncommenting a few lines and specifying the fallback address.
# Save and exit.

I2C Testing - LCD

Enable I2C

sudo raspi-config
  goto "Interfacing Options" > "I2C" and select "Yes" when prompted to enable

# ensure that i2c-tools are installed
sudo apt-get update
sudo apt-get install i2c-tools

# Check I2C for the LCD
sudo i2cdetect -y 0

Simple Python Test Example

I2C LCD driver python script that I found within references further below

I2C_LCD_driver.py

# i2c bus (0 -- original Pi, 1 -- Rev 2 Pi)
I2CBUS = 0

# LCD Address
ADDRESS = 0x27

import smbus
from time import sleep

class i2c_device:
   def __init__(self, addr, port=I2CBUS):
      self.addr = addr
      self.bus = smbus.SMBus(port)

# Write a single command
   def write_cmd(self, cmd):
      self.bus.write_byte(self.addr, cmd)
      sleep(0.0001)

# Write a command and argument
   def write_cmd_arg(self, cmd, data):
      self.bus.write_byte_data(self.addr, cmd, data)
      sleep(0.0001)

# Write a block of data
   def write_block_data(self, cmd, data):
      self.bus.write_block_data(self.addr, cmd, data)
      sleep(0.0001)

# Read a single byte
   def read(self):
      return self.bus.read_byte(self.addr)

# Read
   def read_data(self, cmd):
      return self.bus.read_byte_data(self.addr, cmd)

# Read a block of data
   def read_block_data(self, cmd):
      return self.bus.read_block_data(self.addr, cmd)


# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80

# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00

# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00

# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00

# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00

# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit

class lcd:
   #initializes objects and lcd
   def __init__(self):
      self.lcd_device = i2c_device(ADDRESS)

      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x02)

      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
      self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
      sleep(0.2)


   # clocks EN to latch command
   def lcd_strobe(self, data):
      self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
      sleep(.0005)
      self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
      sleep(.0001)

   def lcd_write_four_bits(self, data):
      self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
      self.lcd_strobe(data)

   # write a command to lcd
   def lcd_write(self, cmd, mode=0):
      self.lcd_write_four_bits(mode | (cmd & 0xF0))
      self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))

   # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
   # works!
   def lcd_write_char(self, charvalue, mode=1):
      self.lcd_write_four_bits(mode | (charvalue & 0xF0))
      self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))

   # put string function with optional char positioning
   def lcd_display_string(self, string, line=1, pos=0):
    if line == 1:
      pos_new = pos
    elif line == 2:
      pos_new = 0x40 + pos
    elif line == 3:
      pos_new = 0x14 + pos
    elif line == 4:
      pos_new = 0x54 + pos

    self.lcd_write(0x80 + pos_new)

    for char in string:
      self.lcd_write(ord(char), Rs)

   # clear lcd and set to home
   def lcd_clear(self):
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_RETURNHOME)

   # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
   def backlight(self, state): # for state, 1 = on, 0 = off
      if state == 1:
         self.lcd_device.write_cmd(LCD_BACKLIGHT)
      elif state == 0:
         self.lcd_device.write_cmd(LCD_NOBACKLIGHT)

   # add custom characters (0 - 7)
   def lcd_load_custom_chars(self, fontdata):
      self.lcd_write(0x40);
      for char in fontdata:
         for line in char:
            self.lcd_write_char(line)

helloworld.py

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()
mylcd.lcd_display_string("Hello World!", 1)

GPIO Testing

Simple Python Test Example

I believe that RPi.GPIO is installed by default in this image, but simply install via pip3.

# install raspberry Pi GPIO python module if not installed.
pip3 install RPi.GPIO

gpiotest.py

This simply toggles each relay a few times on a slow interval. There is an LED on each relay driver and the relay itself is pretty loud.

import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(36, GPIO.OUT)
GPIO.setup(38, GPIO.OUT)

for i in range(5):
    GPIO.output(36, True)
    time.sleep(2)
    GPIO.output(36, False)
    time.sleep(2)

for i in range(2):
    GPIO.output(38, True)
    time.sleep(2)
    GPIO.output(38, False)
    time.sleep(2)

GPIO.cleanup()

Conclusion

There were a few hiccups, but largely smooth sailing to get the basics up and running.

References

https://www.circuitbasics.com/raspberry-pi-i2c-lcd-set-up-and-programming/

https://learn.sparkfun.com/tutorials/raspberry-gpio/all