Skip to content

Startup

XIANG edited this page Jun 1, 2022 · 1 revision

📖 1. Python Packages Requirement

Package Version
absl-py 1.0.0
Adafruit-Blinka 6.17.0
adafruit-circuitpython-busdevice 5.1.1
adafruit-circuitpython-motor 3.3.1
adafruit-circuitpython-pca9685 3.3.9
adafruit-circuitpython-register 1.9.6
adafruit-circuitpython-servokit 1.3.6
Adafruit-PlatformDetect 3.18.0
Adafruit-PureIO 1.1.9
aiohttp 3.8.1
aiosignal 1.2.0
asn1crypto 0.24.0
astor 0.8.1
astroid 2.1.0
asttokens 1.1.13
async-timeout 4.0.2
asynctest 0.13.0
attrs 21.4.0
automationhat 0.2.0
beautifulsoup4 4.7.1
blinker 1.4
blinkt 0.1.2
buttonshim 0.0.2
cached-property 1.5.2
cachetools 4.2.4
Cap1xxx 0.1.3
certifi 2018.8.24
chardet 3.0.4
charset-normalizer 2.0.10
Click 7.0
colorama 0.3.7
colorzero 1.1
cookies 2.2.1
cryptography 2.6.1
cupshelpers 1.0
docutils 0.14
drumhat 0.1.0
entrypoints 0.3
envirophat 1.0.0
ExplorerHAT 0.4.2
Flask 1.0.2
fourletterphat 0.1.0
frozenlist 1.2.0
future 0.18.2
gast 0.2.2
google-auth 1.35.0
google-auth-oauthlib 0.4.6
google-pasta 0.2.0
gpiozero 1.6.2
grpcio 1.43.0
h5py 3.6.0
html5lib 1.0.1
idna 2.6
importlib-metadata 4.10.0
isort 4.3.4
itsdangerous 0.24
jedi 0.13.2
Jinja2 2.10
keras 2.7.0
Keras-Applications 1.0.8
Keras-Preprocessing 1.1.2
keyring 17.1.1
keyrings.alt 3.1.1
lazy-object-proxy 1.3.1
line-bot-sdk 2.0.1
logilab-common 1.4.2
lxml 4.3.2
Markdown 3.3.6
MarkupSafe 1.1.0
mccabe 0.6.1
microdotphat 0.2.1
mote 0.0.4
motephat 0.0.3
multidict 5.2.0
mypy 0.670
mypy-extensions 0.4.1
numpy 1.16.2
oauthlib 2.1.0
olefile 0.46
opencv-python 4.5.5.62
opt-einsum 3.3.0
pantilthat 0.0.7
parso 0.3.1
pexpect 4.6.0
pgzero 1.2
phatbeat 0.1.1
pianohat 0.1.0
picamera 1.13
piglow 1.2.5
pigpio 1.78
Pillow 5.4.1
pip 18.1
protobuf 3.19.1
psutil 5.5.1
pyasn1 0.4.8
pyasn1-modules 0.2.8
pycairo 1.16.2
pycrypto 2.6.1
pycups 1.9.73
pyftdi 0.53.3
pygame 1.9.4.post1
Pygments 2.3.1
PyGObject 3.30.4
pyinotify 0.9.6
PyJWT 1.7.0
pylint 2.2.2
pyOpenSSL 19.0.0
pyserial 3.4
pysmbc 1.0.15.6
python-apt 1.8.4.3
pyusb 1.2.1
pyxdg 0.25
rainbowhat 0.1.0
reportlab 3.5.13
requests 2.21.0
requests-oauthlib 1.0.0
responses 0.9.0
roman 2.0.0
rpi-ws281x 4.3.1
RPi.GPIO 0.7.0
rsa 4.8
RTIMULib 7.2.1
scrollphat 0.0.7
scrollphathd 1.2.1
SecretStorage 2.3.1
Send2Trash 1.5.0
sense-hat 2.2.0
setuptools 60.2.0
simplejson 3.16.0
six 1.12.0
skywriter 0.0.7
sn3218 1.2.7
soupsieve 1.8
spidev 3.4
ssh-import-id 5.7
sysv-ipc 1.1.0
tensorboard 2.0.2
tensorflow 2.0.0
tensorflow-estimator 2.0.1
termcolor 1.1.0
thonny 3.3.6
touchphat 0.0.1
twython 3.7.0
typed-ast 1.3.1
typing-extensions 4.0.1
unicornhathd 0.0.4
urllib3 1.24.1
webencodings 0.5.1
Werkzeug 0.14.1
wheel 0.32.3
wrapt 1.13.3
yarl 1.7.2
zipp 3.7.0

2. Basic Settings for Rasberry Pi

Interfaces Configuration

  • Enableed
    • Camera
    • SSH
    • VNC
    • I2C

Enabled I2C under CMD

Before using the Adafruit PCA9685 16-Channel Servo Driver, you should enable the I2C, or the error will happen while running the code.

  1. sudo raspi-config

  1. Enter Interface Options

  1. Select I2C

  1. Enable I2C

  1. Enabled

  1. Go back and finish

3. The Circuit Diagram

Reference

https://github.com/adafruit/Fritzing-Library/blob/master/parts/retired/PCA9685%2016x12-bit%20PWM%20Breakout.fzpz

4. Line Bot Source Code

Code

import picamera
from servoMotion import *
import os, shutil
from flask import Flask, request, abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import *
import json

from datetime import datetime
app = Flask(__name__)
# LINE BOT info
line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN')
handler = WebhookHandler('YOUR_CHANNEL_SECRET')
camera = picamera.PiCamera()

#object to control servo motor
servoControl = servoMotion(1,1)

folder = 'imagepreview'
for filename in os.listdir(folder):
    file_path = os.path.join(folder, filename)
    try:
        if os.path.isfile(file_path) or os.path.islink(file_path):
            os.unlink(file_path)
        elif os.path.isdir(file_path):
            shutil.rmtree(file_path)
    except Exception as e:
        print('Failed to delete %s. Reason: %s' % (file_path, e))

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)
    print(body)
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'


contents= json.load(open('BasicTable.json','r',encoding='utf-8'))
print(contents)

flex_message = FlexSendMessage(
    alt_text='hello',
    contents= json.load(open('BasicTable.json','r',encoding='utf-8'))
)
# Message event
@handler.add(MessageEvent)
def handle_message(event):
    message_type = event.message.type
    user_id = event.source.user_id
    reply_token = event.reply_token
    message = str(event.message.text)
    if(message == 'right'):
        FlexMessage = json.load(open('BasicTable.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('test1',FlexMessage))
        servoControl.turnRight()
    elif(message == 'left'):
        FlexMessage = json.load(open('returnPhotoTable.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('test2',FlexMessage))
        servoControl.turnLeft()
    elif(message == 'reset'):
        FlexMessage = json.load(open('returnPhotoTable.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('test2',FlexMessage))
        servoControl.resetStatus()
    else:
        line_bot_api.reply_message(reply_token, TextSendMessage(text=message))

arr = [0]

#handle postbackdata
@handler.add(PostbackEvent)
def handle_postback(event):
    postback_data = event.postback.data
    user_id = event.source.user_id
    reply_token = event.reply_token
    if postback_data == "getCurrentPhoto" or postback_data == "takePhoto":
        DIR = 'imagepreview/fit'+str(len(arr))+'.jpg'
        camera.capture(DIR)
        print('camera take the picture')
        FlexMessage = json.load(open('returnPhotoTable.json','r',encoding='utf-8'))
        url = "https://b1e3-140-115-214-31.ngrok.io/fit"+str(len(arr))+'.jpg'
        print(url)
        FlexMessage["hero"]["url"] = url
        FlexMessage["body"]["contents"][1]["contents"][2]["contents"][1]["text"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        line_bot_api.reply_message(reply_token, FlexSendMessage('即時照片查看',FlexMessage))
        arr.append(0)
    elif postback_data == "resetStatus":
        line_bot_api.reply_message(reply_token, TextSendMessage(text="重置狀態中..."))
        servoControl.resetStatus()
    elif postback_data == "backToFunctionTable":
        FlexMessage = json.load(open('BasicTable.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('綜合功能表',FlexMessage))
    elif postback_data == "changeAngle":
        FlexMessage = json.load(open('changeRotation.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('鏡頭角度調整',FlexMessage))
    elif postback_data == "turnLeft":
        servoControl.turnLeft()
        if servoControl.getLeft()==0:
            line_bot_api.reply_message(reply_token, TextSendMessage(text="不能再向左轉囉~"))
        FlexMessage = json.load(open('changeRotation.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('鏡頭角度調整',FlexMessage))
    elif postback_data == "turnRight":
        servoControl.turnRight()
        if servoControl.getRight()==0:
            line_bot_api.reply_message(reply_token, TextSendMessage(text="不能再向右轉囉~"))
        FlexMessage = json.load(open('changeRotation.json','r',encoding='utf-8'))
        line_bot_api.reply_message(reply_token, FlexSendMessage('鏡頭角度調整',FlexMessage))
    '''elif postback_data == "takePhoto":
        FlexMessage = json.load(open('returnPhotoTable.json','r',encoding='utf-8')) 
        FlexMessage["body"]["contents"][1]["contents"][2]["contents"][1]["text"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        line_bot_api.reply_message(reply_token, FlexSendMessage('即時照片查看',FlexMessage))'''
    

import os
if __name__ == "__main__":
    port = int(os.environ.get('PORT', 80))
    app.run(host='0.0.0.0', port=port)

Result

1. postback_data == "getCurrentPhoto" or postback_data == "takePhoto":

2. postback_data == "resetStatus":

3. postback_data == "backToFunctionTable":

4. postback_data == "changeAngle":

Reference

https://stackoverflow.com/questions/185936/how-to-delete-the-contents-of-a-folder

https://stackoverflow.com/questions/10851906/python-3-unboundlocalerror-local-variable-referenced-before-assignment

https://developers.line.biz/flex-simulator/

5. Servo Motor Control

Source Code

import time
from adafruit_servokit import ServoKit

# Set channels to the number of servo channels on your kit.
# 8 for FeatherWing, 16 for Shield/HAT/Bonnet.
kit = ServoKit(channels=16)

class servoMotion():
    def __init__(self, right, left):
        self.right = right
        self.left = left

    def getRight(self):
        return self.right

    def getLeft(self):
        return self.left

    def turnRight(self):
        if self.right >= 1:
            kit.continuous_servo[0].throttle = 0.5
            time.sleep(0.2)
            kit.continuous_servo[0].throttle = 0
            time.sleep(1)
            print("sleeep")
            self.right = self.right-1
            self.left = self.left+1
            print('turn right')
        else:
            print("You have no chance to turn right")
    
        print(self.right,self.left)

    def turnLeft(self):
        if self.left >= 1:
            kit.continuous_servo[0].throttle = -0.5
            time.sleep(0.2)
            kit.continuous_servo[0].throttle = 0
            time.sleep(1)
            print("sleeep")
            self.left -= 1
            self.right += 1
            print('turn left')
        else:
            print("You have no chance to turn left")
        
        print(self.right,self.left)

    def resetStatus(self):
        if self.right > 1:
            kit.continuous_servo[0].throttle = 0.5
            time.sleep(0.2)
            kit.continuous_servo[0].throttle = 0
            time.sleep(1)
            print("sleeep")
            print('reset turn right')
            self.right -= 1
            self.left += 1
        elif self.left > 1:
            kit.continuous_servo[0].throttle = -0.5
            time.sleep(0.2)
            kit.continuous_servo[0].throttle = 0
            time.sleep(1)
            print("sleeep")
            print('reset turn left')
            self.left -= 1
            self.right += 1;

Reference

https://learn.adafruit.com/adafruit-16-channel-servo-driver-with-raspberry-pi/using-the-adafruit-library

6. Camera Control

This section is just for testing the camera can work properly with the picamera package.

Source Code

from typing import Counter
from ImageClassification.Predict import Prediction
import picamera
import time


counter = 1
while counter <= 5:
# take pic
    camera = picamera.PiCamera()
    camera.capture('fit.jpg')
    print('camera take the picture')
    Prediction('fit.jpg')
    counter += 1 

Reference

https://picamera.readthedocs.io/en/release-1.13/

7. Start two localhost Server

Ngrok

Use ngrok to make the image captured by the camera visible on the outer web browser.

Next, we can map the directory to the HTTPS URL and the directory can be accessed from outside with a web browser.

Access the image on a web browser with the URL.

Don't forget to change the URL in the python file every time you restart the ngrok.

After changing the URL, you can execute the python file again.

SocketXP

We will use SocketXP to take the place of ngrok and get the webhook URL. Remember that we assign the port number to be 80 in the python file, so we should connect to port 80 with SocketXP.

Next, we get the public URL that SocketXP issues for us.

Fill the Webhook URL section with the URL we gained from SocketXP. Don't forget to add /callback.

Reference

https://www.socketxp.com/docs/guide/

https://ngrok.com/docs

8. Interact with Line Bot

If you finish the above steps successfully, then you can interact with the Line Bot happily.

If there is any problem you want to ask, you can issue me here.

Have fun and stay cool~