diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8649d80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,254 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/python,pycharm,code +# Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm,code + +### Code ### +.vscode/* +!.vscode/tasks.json +!.vscode/launch.json +*.code-workspace + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# profiling data +.prof + +# End of https://www.toptal.com/developers/gitignore/api/python,pycharm,code \ No newline at end of file diff --git a/compLIB/ADC.py b/compLIB/ADC.py index 2343221..bb7e5b8 100644 --- a/compLIB/ADC.py +++ b/compLIB/ADC.py @@ -1,94 +1,35 @@ import smbus -import time + +SINGLE_ENDED = 0x84 +ADDRESS = 0x48 + +bus = smbus.SMBus(1) -class Adc: - def __init__(self): - # Get I2C bus - self.bus = smbus.SMBus(1) +class ADC: - # I2C address of the device - self.ADDRESS = 0x48 + @staticmethod + def read(channel) -> float: + """ + Read from adc channel + 0 -> Left IR Sensor + 1 -> Right IR Sensor + 2 -> Battery voltage / 3 + :param channel: Channel between 0 and 2 + :return: voltage + """ - # PCF8591 Command - self.PCF8591_CMD = 0x40 # Command - - # ADS7830 Command - self.ADS7830_CMD = 0x84 # Single-Ended Inputs - - for i in range(3): - aa = self.bus.read_byte_data(self.ADDRESS, 0xf4) - if aa < 150: - self.Index = "PCF8591" - else: - self.Index = "ADS7830" - - def analogReadPCF8591(self, chn): # PCF8591 read ADC value,chn:0,1,2,3 - value = [0, 0, 0, 0, 0, 0, 0, 0, 0] - for i in range(9): - value[i] = self.bus.read_byte_data(self.ADDRESS, self.PCF8591_CMD + chn) - value = sorted(value) - return value[4] - - # TODO: bug in original code??? - #def analogWritePCF8591(self, value): # PCF8591 write DAC value - # self.bus.write_byte_data(self.ADDRESS, cmd, value) - - def recvPCF8591(self, channel): # PCF8591 write DAC value - while (1): - value1 = self.analogReadPCF8591(channel) # read the ADC value of channel 0,1,2, - value2 = self.analogReadPCF8591(channel) - if value1 == value2: - break; - voltage = value1 / 256.0 * 3.3 # calculate the voltage value - voltage = round(voltage, 2) - return voltage - - def recvADS7830(self, channel): """Select the Command data from the given provided value above""" - COMMAND_SET = self.ADS7830_CMD | ((((channel << 2) | (channel >> 1)) & 0x07) << 4) - self.bus.write_byte(self.ADDRESS, COMMAND_SET) - while (1): - value1 = self.bus.read_byte(self.ADDRESS) - value2 = self.bus.read_byte(self.ADDRESS) + command = SINGLE_ENDED | ((((channel << 2) | (channel >> 1)) & 0x07) << 4) + bus.write_byte(ADDRESS, command) + + while "Koka is great": + value1 = bus.read_byte(ADDRESS) + value2 = bus.read_byte(ADDRESS) if value1 == value2: - break; + break + voltage = value1 / 255.0 * 3.3 # calculate the voltage value voltage = round(voltage, 2) + return voltage - - def recvADC(self, channel): - if self.Index == "PCF8591": - data = self.recvPCF8591(channel) - elif self.Index == "ADS7830": - data = self.recvADS7830(channel) - return data - - def i2cClose(self): - self.bus.close() - - -def loop(): - adc = Adc() - while True: - Left_IDR = adc.recvADC(0) - print(Left_IDR) - Right_IDR = adc.recvADC(1) - print(Right_IDR) - Power = adc.recvADC(2) * 3 - print(Power) - time.sleep(1) - print('----') - - -def destroy(): - pass - - -# Main program logic follows: -if __name__ == '__main__': - print('Program is starting ... ') - try: - loop() - except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed. - destroy() diff --git a/compLIB/Battery.py b/compLIB/Battery.py new file mode 100644 index 0000000..bb08b03 --- /dev/null +++ b/compLIB/Battery.py @@ -0,0 +1,22 @@ +from compLIB.ADC import ADC + + +BATTERY_CHANNEL = 2 +BATTERY_COUNT = 2 +BATTERY_MULTIPLIER = 3 +BATTERY_MIN_VOLTAGE = 3.6 +BATTERY_MAX_VOLTAGE = 4.1 + +adc = ADC() + + +class Battery(object): + + @staticmethod + def percent() -> int: + """ + Get battery percentage between 0 and 100 + :return: + """ + voltage = adc.read(BATTERY_CHANNEL) * BATTERY_MULTIPLIER + return int((voltage - BATTERY_MIN_VOLTAGE) / BATTERY_MAX_VOLTAGE * 100) diff --git a/compLIB/Buzzer.py b/compLIB/Buzzer.py index b6d6965..67869dd 100644 --- a/compLIB/Buzzer.py +++ b/compLIB/Buzzer.py @@ -1,6 +1,5 @@ -import time import RPi.GPIO as GPIO -from Command import COMMAND as cmd +import atexit GPIO.setwarnings(False) Buzzer_Pin = 17 @@ -9,15 +8,17 @@ GPIO.setup(Buzzer_Pin, GPIO.OUT) class Buzzer: - def run(self, command): - if command != "0": - GPIO.output(Buzzer_Pin, True) - else: - GPIO.output(Buzzer_Pin, False) + @staticmethod + def set(on: bool): + """ + Turn the buzzer on / off + :param on: True if on, False if off + :return: None + """ + GPIO.output(Buzzer_Pin, on) -if __name__ == '__main__': - B = Buzzer() - B.run('1') - time.sleep(3) - B.run('0') +def exit(): + Buzzer.set(0) + +atexit.register(exit) \ No newline at end of file diff --git a/compLIB/Command.py b/compLIB/Command.py deleted file mode 100644 index 9faddae..0000000 --- a/compLIB/Command.py +++ /dev/null @@ -1,13 +0,0 @@ -class COMMAND: - CMD_MOTOR = "CMD_MOTOR" - CMD_LED = "CMD_LED" - CMD_LED_MOD = "CMD_LED_MOD" - CMD_SERVO = "CMD_SERVO" - CMD_BUZZER = "CMD_BUZZER" - CMD_SONIC = "CMD_SONIC" - CMD_LIGHT = "CMD_LIGHT" - CMD_POWER = "CMD_POWER" - CMD_MODE = "CMD_MODE" - - def __init__(self): - pass diff --git a/compLIB/IRSensor.py b/compLIB/IRSensor.py new file mode 100644 index 0000000..64f673d --- /dev/null +++ b/compLIB/IRSensor.py @@ -0,0 +1,65 @@ +from compLIB.ADC import ADC +import RPi.GPIO as GPIO + +TOP_LEFT_CHANNEL = 0 +TOP_RIGHT_CHANNEL = 1 + +TOP_IR_MIN_VOLTAGE = 0.0 +TOP_IR_MAX_VOLTAGE = 3.3 + +BOTTOM_LEFT_PIN = 14 +BOTTOM_MIDDLE_PIN = 15 +BOTTOM_RIGHT_PIN = 23 + +adc = ADC() + +GPIO.setmode(GPIO.BCM) + +GPIO.setup(BOTTOM_LEFT_PIN, GPIO.IN) +GPIO.setup(BOTTOM_MIDDLE_PIN, GPIO.IN) +GPIO.setup(BOTTOM_RIGHT_PIN, GPIO.IN) + + +class IRSensor(object): + + @staticmethod + def top_left_percent() -> int: + """ + Get left infrared sensor percentage between 0 and 100 + :return: + """ + voltage = adc.read(TOP_LEFT_CHANNEL) + return int((voltage - TOP_IR_MIN_VOLTAGE) / TOP_IR_MAX_VOLTAGE * 100) + + @staticmethod + def top_right_percent() -> int: + """ + Get right infrared sensor percentage between 0 and 100 + :return: + """ + voltage = adc.read(TOP_RIGHT_CHANNEL) + return int((voltage - TOP_IR_MIN_VOLTAGE) / TOP_IR_MAX_VOLTAGE * 100) + + @staticmethod + def bottom_left() -> bool: + """ + Get status of bottom infrared sensor + :return: bool + """ + return GPIO.input(BOTTOM_LEFT_PIN) + + @staticmethod + def bottom_middle() -> bool: + """ + Get status of bottom infrared sensor + :return: bool + """ + return GPIO.input(BOTTOM_MIDDLE_PIN) + + @staticmethod + def bottom_right() -> bool: + """ + Get status of bottom infrared sensor + :return: bool + """ + return GPIO.input(BOTTOM_RIGHT_PIN) \ No newline at end of file diff --git a/compLIB/Motor.py b/compLIB/Motor.py index 634c06a..dd9177d 100644 --- a/compLIB/Motor.py +++ b/compLIB/Motor.py @@ -1,107 +1,51 @@ -import time -from PCA9685 import PCA9685 +import atexit + +from compLIB.PCA9685 import PCA9685 + +pwm = PCA9685(0x40, debug=True) +pwm.setPWMFreq(50) + +MOTOR_COUNT = 4 +MAX_MOTOR_SPEED = 4095.0 +MOTOR_PERCENTAGE_MULT = MAX_MOTOR_SPEED / 100.0 -class Motor: - def __init__(self): - self.pwm = PCA9685(0x40, debug=True) - self.pwm.setPWMFreq(50) +class Motor(object): - def duty_range(self, duty1, duty2, duty3, duty4): - if duty1 > 4095: - duty1 = 4095 - elif duty1 < -4095: - duty1 = -4095 + @staticmethod + def power(port: int, percent: int): + """ + Set specified motor to percentage power + :param port: Port, which the motor is connected to. 0-3, 0 -> top left, 3 -> top right + :param percent: Percentage of max speed. between -100 and 100 + :return: None + """ + forward = True + if percent < 0: + percent = abs(percent) + forward = False - if duty2 > 4095: - duty2 = 4095 - elif duty2 < -4095: - duty2 = -4095 + # bottom left motor is inverted - REEEEEEEEEEEE + if port == 1: + forward = not forward - if duty3 > 4095: - duty3 = 4095 - elif duty3 < -4095: - duty3 = -4095 + adjusted_speed = int(min(max(0, percent), 100) * MOTOR_PERCENTAGE_MULT) - if duty4 > 4095: - duty4 = 4095 - elif duty4 < -4095: - duty4 = -4095 - return duty1, duty2, duty3, duty4 - - def left_Upper_Wheel(self, duty): - if duty > 0: - self.pwm.setMotorPwm(0, 0) - self.pwm.setMotorPwm(1, duty) - elif duty < 0: - self.pwm.setMotorPwm(1, 0) - self.pwm.setMotorPwm(0, abs(duty)) + if forward: + pwm.setMotorPwm(port * 2, 0) + pwm.setMotorPwm(port * 2 + 1, adjusted_speed) else: - self.pwm.setMotorPwm(0, 4095) - self.pwm.setMotorPwm(1, 4095) + pwm.setMotorPwm(port * 2, adjusted_speed) + pwm.setMotorPwm(port * 2 + 1, 0) - def left_Lower_Wheel(self, duty): - if duty > 0: - self.pwm.setMotorPwm(3, 0) - self.pwm.setMotorPwm(2, duty) - elif duty < 0: - self.pwm.setMotorPwm(2, 0) - self.pwm.setMotorPwm(3, abs(duty)) - else: - self.pwm.setMotorPwm(2, 4095) - self.pwm.setMotorPwm(3, 4095) - - def right_Upper_Wheel(self, duty): - if duty > 0: - self.pwm.setMotorPwm(6, 0) - self.pwm.setMotorPwm(7, duty) - elif duty < 0: - self.pwm.setMotorPwm(7, 0) - self.pwm.setMotorPwm(6, abs(duty)) - else: - self.pwm.setMotorPwm(6, 4095) - self.pwm.setMotorPwm(7, 4095) - - def right_Lower_Wheel(self, duty): - if duty > 0: - self.pwm.setMotorPwm(4, 0) - self.pwm.setMotorPwm(5, duty) - elif duty < 0: - self.pwm.setMotorPwm(5, 0) - self.pwm.setMotorPwm(4, abs(duty)) - else: - self.pwm.setMotorPwm(4, 4095) - self.pwm.setMotorPwm(5, 4095) - - def setMotorModel(self, duty1, duty2, duty3, duty4): - duty1, duty2, duty3, duty4 = self.duty_range(duty1, duty2, duty3, duty4) - self.left_Upper_Wheel(duty1) - self.left_Lower_Wheel(duty2) - self.right_Upper_Wheel(duty3) - self.right_Lower_Wheel(duty4) + @staticmethod + def all_off(): + """ + Turns of all motors + :return: + """ + for i in range(0, MOTOR_COUNT): + Motor.power(i, 0) -PWM = Motor() - - -def loop(): - PWM.setMotorModel(2000, 2000, 2000, 2000) # Forward - time.sleep(3) - PWM.setMotorModel(-2000, -2000, -2000, -2000) # Back - time.sleep(3) - PWM.setMotorModel(-500, -500, 2000, 2000) # Left - time.sleep(3) - PWM.setMotorModel(2000, 2000, -500, -500) # Right - time.sleep(3) - PWM.setMotorModel(0, 0, 0, 0) # Stop - - -def destroy(): - PWM.setMotorModel(0, 0, 0, 0) - - -if __name__ == '__main__': - try: - loop() - except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed. - destroy() +atexit.register(Motor.all_off()) diff --git a/compLIB/Servo.py b/compLIB/Servo.py new file mode 100644 index 0000000..0a6ba68 --- /dev/null +++ b/compLIB/Servo.py @@ -0,0 +1,28 @@ +from compLIB.PCA9685 import PCA9685 + +pwm = PCA9685(0x40, debug=True) +pwm.setPWMFreq(50) + + +class Servo: + + @staticmethod + def set_position(channel: int, angle: int): + """ + Set position of servo connected to port + :param channel: channel between 0 and 7 + :param angle: Angle of servo + :return: None + + """ + angle = abs(angle) + + if channel == 0: + pwm.setServoPulse(8 + channel, 2500 - int(angle / 0.09)) + elif channel < 8: + pwm.setServoPulse(8 + channel, 500 - int(angle / 0.09)) + + @staticmethod + def setup_position(): + pwm.setServoPulse(8, 1500) + pwm.setServoPulse(9, 1500) diff --git a/compLIB/Ultrasonic.py b/compLIB/Ultrasound.py similarity index 98% rename from compLIB/Ultrasonic.py rename to compLIB/Ultrasound.py index 12bff45..6cfbd89 100644 --- a/compLIB/Ultrasonic.py +++ b/compLIB/Ultrasound.py @@ -103,4 +103,4 @@ if __name__ == '__main__': ultrasonic.run() except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed. PWM.setMotorModel(0, 0, 0, 0) - ultrasonic.pwm_S.setServoPwm('0', 90) + ultrasonic.pwm_S.setServoPwm('0', 90) \ No newline at end of file diff --git a/compLIB/__init__.py b/compLIB/__init__.py index f102a9c..b3c06d4 100644 --- a/compLIB/__init__.py +++ b/compLIB/__init__.py @@ -1 +1 @@ -__version__ = "0.0.1" +__version__ = "0.0.1" \ No newline at end of file diff --git a/copy.sh b/copy.sh new file mode 100755 index 0000000..ba4c513 --- /dev/null +++ b/copy.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +scp -r compLIB pi@10.20.85.225:/home/pi/compLIB \ No newline at end of file