first commit

This commit is contained in:
pandacraft 2025-03-21 16:04:17 +01:00
commit a5a0434432
1126 changed files with 439481 additions and 0 deletions

View file

@ -0,0 +1,136 @@
Library written for **Python 3**!
Possible fault warnings:
* undervoltage lockout
* overcurrent event
* extended current limit event
* overtemperature condition
* unknown condition
Available commands for the Grove Mini Motor Driver:
* `moveForward(speed)` : speed = `0-100`%
* `moveBackwards(speed)` : speed = `0-100`%
* `setLeftMotor(direction, speed)` : direction = `'FORWARD'`/`'REVERSE'`; speed = `0-100`%
* `setRightMotor(direction, speed)` : direction = `'FORWARD'/'REVERSE'`; speed = `0-100`%
* `stopLeftMotor()` : fast braking the motor
* `stopRightMotor()`: fast braking the motor
* `stopMotors()` : fast braking the motors
* `disableMotors()` : when power-sleeping the device
* `setDisplayFaults(choice = True)` : enable/disable terminal output
Sample output on terminal when `setDisplayFaults(True)` is set:
```
> [04-Apr-2017 19:06:08.186616][forward][speed = 0%]
> [04-Apr-2017 19:06:08.188770][left motor warning][undervoltage lockout]
> [04-Apr-2017 19:06:08.190704][right motor warning][undervoltage lockout]
> [04-Apr-2017 19:06:08.241182][forward][speed = 1%]
> [04-Apr-2017 19:06:08.294815][forward][speed = 2%]
> [04-Apr-2017 19:06:08.348432][forward][speed = 3%]
> [04-Apr-2017 19:06:08.402033][forward][speed = 4%]
> [04-Apr-2017 19:06:08.455697][forward][speed = 5%]
> [04-Apr-2017 19:06:08.509291][forward][speed = 6%]
> [04-Apr-2017 19:06:08.562869][forward][speed = 7%]
> [04-Apr-2017 19:06:08.616490][forward][speed = 8%]
> [04-Apr-2017 19:06:08.670143][forward][speed = 9%]
> [04-Apr-2017 19:06:08.723765][forward][speed = 10%]
> [04-Apr-2017 19:06:08.777373][forward][speed = 11%]
> [04-Apr-2017 19:06:08.831013][forward][speed = 12%]
> [04-Apr-2017 19:06:08.884608][forward][speed = 13%]
> [04-Apr-2017 19:06:08.938222][forward][speed = 14%]
> [04-Apr-2017 19:06:08.991827][forward][speed = 15%]
> [04-Apr-2017 19:06:09.045500][forward][speed = 16%]
> [04-Apr-2017 19:06:09.099190][forward][speed = 17%]
> [04-Apr-2017 19:06:09.152877][forward][speed = 18%]
> [04-Apr-2017 19:06:09.206449][forward][speed = 19%]
> [04-Apr-2017 19:06:09.260065][forward][speed = 20%]
> [04-Apr-2017 19:06:09.313825][forward][speed = 21%]
> [04-Apr-2017 19:06:09.367477][forward][speed = 22%]
> [04-Apr-2017 19:06:09.421111][forward][speed = 23%]
> [04-Apr-2017 19:06:09.474758][forward][speed = 24%]
> [04-Apr-2017 19:06:09.528349][forward][speed = 25%]
> [04-Apr-2017 19:06:09.581962][forward][speed = 26%]
> [04-Apr-2017 19:06:09.635656][forward][speed = 27%]
> [04-Apr-2017 19:06:09.689268][forward][speed = 28%]
> [04-Apr-2017 19:06:09.742892][forward][speed = 29%]
> [04-Apr-2017 19:06:09.796492][forward][speed = 30%]
> [04-Apr-2017 19:06:09.850105][forward][speed = 31%]
> [04-Apr-2017 19:06:09.903736][forward][speed = 32%]
> [04-Apr-2017 19:06:09.957375][forward][speed = 33%]
> [04-Apr-2017 19:06:10.011014][forward][speed = 34%]
> [04-Apr-2017 19:06:10.064492][forward][speed = 35%]
> [04-Apr-2017 19:06:10.118010][forward][speed = 36%]
> [04-Apr-2017 19:06:10.171460][forward][speed = 37%]
> [04-Apr-2017 19:06:10.224905][forward][speed = 38%]
> [04-Apr-2017 19:06:10.278310][forward][speed = 39%]
> [04-Apr-2017 19:06:10.331732][forward][speed = 40%]
> [04-Apr-2017 19:06:10.385252][forward][speed = 41%]
> [04-Apr-2017 19:06:10.441249][forward][speed = 42%]
> [04-Apr-2017 19:06:10.497588][forward][speed = 43%]
> [04-Apr-2017 19:06:10.553675][forward][speed = 44%]
> [04-Apr-2017 19:06:10.610019][forward][speed = 45%]
> [04-Apr-2017 19:06:10.666197][forward][speed = 46%]
> [04-Apr-2017 19:06:10.722392][forward][speed = 47%]
> [04-Apr-2017 19:06:10.778276][forward][speed = 48%]
> [04-Apr-2017 19:06:10.834530][forward][speed = 49%]
> [04-Apr-2017 19:06:10.890216][forward][speed = 50%]
> [04-Apr-2017 19:06:10.946259][forward][speed = 51%]
> [04-Apr-2017 19:06:11.002603][forward][speed = 52%]
> [04-Apr-2017 19:06:11.059224][forward][speed = 53%]
> [04-Apr-2017 19:06:11.115792][forward][speed = 54%]
> [04-Apr-2017 19:06:11.172354][forward][speed = 55%]
> [04-Apr-2017 19:06:11.228827][forward][speed = 56%]
> [04-Apr-2017 19:06:11.285348][forward][speed = 57%]
> [04-Apr-2017 19:06:11.341821][forward][speed = 58%]
> [04-Apr-2017 19:06:11.398590][forward][speed = 59%]
> [04-Apr-2017 19:06:11.455153][forward][speed = 60%]
> [04-Apr-2017 19:06:11.511631][forward][speed = 61%]
> [04-Apr-2017 19:06:11.568084][forward][speed = 62%]
> [04-Apr-2017 19:06:11.624746][forward][speed = 63%]
> [04-Apr-2017 19:06:11.681274][forward][speed = 64%]
> [04-Apr-2017 19:06:11.737571][forward][speed = 65%]
> [04-Apr-2017 19:06:11.794203][forward][speed = 66%]
> [04-Apr-2017 19:06:11.851029][forward][speed = 67%]
> [04-Apr-2017 19:06:11.907599][forward][speed = 68%]
> [04-Apr-2017 19:06:11.964150][forward][speed = 69%]
> [04-Apr-2017 19:06:12.020842][forward][speed = 70%]
> [04-Apr-2017 19:06:12.077695][forward][speed = 71%]
> [04-Apr-2017 19:06:12.134426][forward][speed = 72%]
> [04-Apr-2017 19:06:12.190989][forward][speed = 73%]
> [04-Apr-2017 19:06:12.247715][forward][speed = 74%]
> [04-Apr-2017 19:06:12.304526][forward][speed = 75%]
> [04-Apr-2017 19:06:12.361030][forward][speed = 76%]
> [04-Apr-2017 19:06:12.417870][forward][speed = 77%]
> [04-Apr-2017 19:06:12.474538][forward][speed = 78%]
> [04-Apr-2017 19:06:12.531014][forward][speed = 79%]
> [04-Apr-2017 19:06:12.587561][forward][speed = 80%]
> [04-Apr-2017 19:06:12.644190][forward][speed = 81%]
> [04-Apr-2017 19:06:12.700734][forward][speed = 82%]
> [04-Apr-2017 19:06:12.757283][forward][speed = 83%]
> [04-Apr-2017 19:06:12.813796][forward][speed = 84%]
> [04-Apr-2017 19:06:12.870284][forward][speed = 85%]
> [04-Apr-2017 19:06:12.927131][forward][speed = 86%]
> [04-Apr-2017 19:06:12.983632][forward][speed = 87%]
> [04-Apr-2017 19:06:13.040205][forward][speed = 88%]
> [04-Apr-2017 19:06:13.096743][forward][speed = 89%]
> [04-Apr-2017 19:06:13.153287][forward][speed = 90%]
> [04-Apr-2017 19:06:13.209824][forward][speed = 91%]
> [04-Apr-2017 19:06:13.266372][forward][speed = 92%]
> [04-Apr-2017 19:06:13.322886][forward][speed = 93%]
> [04-Apr-2017 19:06:13.379352][forward][speed = 94%]
> [04-Apr-2017 19:06:13.435882][forward][speed = 95%]
> [04-Apr-2017 19:06:13.492451][forward][speed = 96%]
> [04-Apr-2017 19:06:13.548921][forward][speed = 97%]
> [04-Apr-2017 19:06:13.605467][forward][speed = 98%]
> [04-Apr-2017 19:06:13.662275][forward][speed = 99%]
> [04-Apr-2017 19:06:13.718809][forward][speed = 100%]
> [04-Apr-2017 19:06:15.777457][reverse][speed = 50%]
> [04-Apr-2017 19:06:17.786247][left motor][stop]
> [04-Apr-2017 19:06:17.789597][right motor][stop]
> [04-Apr-2017 19:06:18.793787][right motor][speed = 70%]
> [04-Apr-2017 19:06:23.802272][right motor][speed = 70%]
> [04-Apr-2017 19:06:28.810728][right motor][speed = 50%]
> [04-Apr-2017 19:06:28.814155][left motor][speed = 50%]
> [04-Apr-2017 19:06:29.818316][standby motors]
```

View file

@ -0,0 +1,125 @@
#!/usr/bin/env python3
#
# The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi
#
# Have a question about this example? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi
#
'''
## License
The MIT License (MIT)
GrovePi for the Raspberry Pi: an open source platform for connecting Grove Sensors to the Raspberry Pi.
Copyright (C) 2017 Dexter Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
'''
from grove_mini_motor_driver import MiniMotorDriver
from grove_mini_motor_driver import left_channel, right_channel
from time import sleep
import sys
# if you use the incorporated feedbacker (aka setDisplayFaults)
# whenever there's a shortage of current or if the motors stalls
# it appears on the screen as a warning
#
# Please take a look in the terminal_log_example.txt to get a feeling
# of what this program outputs
#
# Here is some fault feedback:
#
# [04-Apr-2017 19:06:08.188770][left motor warning][undervoltage lockout]
# [04-Apr-2017 19:06:08.190704][right motor warning][undervoltage lockout]
# [04-Apr-2017 19:21:17.990704][right motor warning][overcurrent event]
# [04-Apr-2017 19:23:11.768320][right motor warning][extended current limit event]
# [04-Apr-2017 19:25:08.330140][right motor warning][overtemperature condition]
# [04-Apr-2017 19:38:54.781218][right motor warning][unknown condition]
# Don't forget to run it with Python 3 !!
# Don't forget to run it with Python 3 !!
# Don't forget to run it with Python 3 !!
def Main():
# initialize an object of the motor driver class
# with the appropiate channel address
# we can also add a 3rd argument which is a SMBus objeclt
# in case we don't want to let the class instantiate it
driver = MiniMotorDriver(left_channel, right_channel)
# enable display feedback/output of motors status
# alternatively we can use driver.setDisplayFaults(False) to disable it
driver.setDisplayFaults()
# increase the power to the motors from 0% -> 100% in 5 seconds
# motors rotate in tandem
for percent in range(101):
driver.moveForward(percent)
sleep(0.05)
# stay at 100% power for 2 seconds
sleep(2)
# and then move backwards at 50% thrust for another 2 seconds
driver.moveBackwards(50)
sleep(2)
# stop the motors immediately -> driver opposes an electromotive force
# in order to stop the motors faster rather then cutting the power
driver.stopMotors()
# stay off for 1 second
sleep(1)
# and then set the right motor to FORWARD direction at 70% thrust
driver.setRightMotor('FORWARD', 70)
# and keep this going for 5 seconds
sleep(5)
# while it's spinning, completely reverse the thrust
# for another 5 seconds
driver.setRightMotor('REVERSE', 70)
sleep(5)
# then set the motors rotate in opposing directions
# such a command would make a GopiGo rotate in the same spot
# do this for one second
driver.setRightMotor('FORWARD', 50)
driver.setLeftMotor('REVERSE', 50)
sleep(1)
# and disable motors
# it's different then the stopMotors() function
# because it just cuts power definitely and
# puts the motor driver in a low-power state
driver.disableMotors()
if __name__ == "__main__":
try:
# it's the above function we call
Main()
# in case CTRL-C / CTRL-D keys are pressed (or anything else that might interrupt)
except KeyboardInterrupt:
print('[Keyboard interrupted]')
sys.exit(0)
# in case there's an IO error aka I2C
except IOError:
print('[IO Error]')
sys.exit(0)

View file

@ -0,0 +1,264 @@
# Released under the MIT license (http://choosealicense.com/licenses/mit/).
# For more information see https://github.com/DexterInd/GrovePi/blob/master/LICENSE
from sys import platform
import datetime
# Library written for Python 3!
left_channel = 0x60
right_channel = 0x62
# function for returning a SMBus object
# checks the Rpi version before it selects the bus
def getNewSMBus():
bus = None
if platform == 'uwp':
import winrt_smbus as smbus
bus = smbus.SMBus(1)
else:
import smbus
import RPi.GPIO as GPIO
revision = GPIO.RPI_REVISION
if revision == 2 or revision == 3:
bus = smbus.SMBus(1)
else:
bus = smbus.SMBus(0)
return bus
# function for returning a formatted time date
def getTime():
return datetime.datetime.now().strftime("%m-%b-%Y %H:%M:%S.%f")
# function for mapping a value which goes from
# left_min to left_max to right_min & right_max
def translateValues(value, left_min, left_max, right_min, right_max):
# figure out how 'wide' each range is
left_span = left_max - left_min
right_span = right_max - right_min
# convert the left range into a 0-1 range (float)
value_scaled = float(value - left_min) / float(left_span)
# convert the 0-1 range into a value in the right range.
return right_min + (value_scaled * right_span)
# class for the DRV8830 driver
# the Grove Mini Motor driver is made of 2 DRV8830 drivers
# each of it controlling a motor channel
# the driver communicates via I2C
class DRV8830:
# the constructor takes an I2C address and an optional SMBus object
# if the SMBus object is not provided, it will create one on its own
def __init__(self, channel_address, _bus = None):
self.address = channel_address
if _bus is None:
self.bus = getNewSMBus()
else:
self.bus = _bus
# fault register address and bit positions for each fault condition
self.FAULT_REG = 0x01
self.CLEAR = 0x80
self.ILIMIT = 0x10
self.OTS = 0x08
self.UVLO = 0x04
self.OCP = 0x02
self.FAULT = 0x01
# control register address and byte commands for it
self.CONTROL_REG = 0x00
self.STANDBY = 0x00
self.REVERSE = 0x01
self.FORWARD = 0x02
self.BRAKE = 0x03
# minimum speed in hexa
self.MIN_SPEED = 0x06
# maximum speed in hexa
self.MAX_SPEED = 0x3F
# dictionary for the fault register
self.FAULT_TABLE = {
self.ILIMIT : 'extended current limit event',
self.OTS : 'overtemperature condition',
self.UVLO : 'undervoltage lockout',
self.OCP : 'overcurrent event',
self.FAULT : 'unknown condition'
}
self.FAULT_TABLE_KEYS = list(self.FAULT_TABLE.keys())
# function for actuating the motor on the given address
# state might be = {STANDBY, REVERSE, FORWARD, BRAKE}
# percentage_speed can be = 0 -> 100 (represents the percentage of the maximum available thrust)
def motorWrite(self, state, percentage_speed = 0):
calculated_speed = int(translateValues(percentage_speed, 0, 100, self.MIN_SPEED, self.MAX_SPEED))
register_value = (calculated_speed << 2) + state
self.bus.write_byte_data(self.address, self.CONTROL_REG, register_value)
fault_strings = self.__readFaults()
self.bus.write_byte_data(self.address, self.FAULT_REG, self.CLEAR)
# in case we detect a fault
# raise a RuntimeWarning with the detailed information
if not fault_strings is None:
result = "; ".join(fault_strings)
raise RuntimeWarning(result)
# private function for reading fault reports from the DRV8830 driver
# returns a list of strings and each of the strings is about an encountered fault
def __readFaults(self):
faults_data = self.bus.read_byte_data(self.address, self.FAULT_REG)
string = None
for fault_key in self.FAULT_TABLE_KEYS:
if faults_data & fault_key > 0:
if string is None:
string = []
string.append(self.FAULT_TABLE[fault_key])
return string
# whenever the object is freed, we shutdown the motors
# if the motors aren't shut down, then they will continue to work/spin
# even if you "reboot" the motor driver
def __del__(self):
self.motorWrite(self.STANDBY)
# class for managing the 2 DRV8830 drivers the Grove Mini Motor Driver has
class MiniMotorDriver:
# obviously, we need the two I2C addresses of the DRV8830 drivers
# the 2 addresses are: 0x60 & 0x62
def __init__(self, ch1, ch2, _bus = None):
if _bus is None:
self.bus = getNewSMBus()
else:
self.bus = _bus
self.left_motor = DRV8830(ch1)
self.right_motor = DRV8830(ch2)
self.display_faults = False
# variables for the implementation I'm bringing on 24th of April
#self.wheel_track = 0.01
#self.wheel_diameter = 0.05
#self.max_wheel_rpm = 1.2
# private function for printing in a nicely formatted way the strings
def __print(self, *strings):
message_string = ""
for string in strings:
message_string += "[" + string + "]"
print(message_string)
# enable / disable displaying driver operations / statuses / warnings
def setDisplayFaults(self, choice = True):
self.display_faults = choice
# private function which is centered on raising exceptions
# you don't need to care about it
# leave it to the pro: haha!
def __writeMotor(self, motor, state, fail_description, speed = 0):
try:
motor.motorWrite(state, speed)
except RuntimeWarning as message:
if self.display_faults:
self.__print(getTime(), fail_description, str(message))
# command the 2 motors to go forward
# speed = 0-100 %
def moveForward(self, speed):
self.__print(getTime(), "forward", "speed = " + str(speed) + "%")
self.__writeMotor(self.left_motor, self.left_motor.FORWARD, "left motor warning", speed)
self.__writeMotor(self.right_motor, self.right_motor.FORWARD, "right motor warning", speed)
# command the 2 motors to go backwards
# speed = 0-100%
def moveBackwards(self, speed):
self.__print(getTime(), "reverse", "speed = " + str(speed) + "%")
self.__writeMotor(self.left_motor, self.left_motor.REVERSE, "left motor warning", speed)
self.__writeMotor(self.right_motor, self.right_motor.REVERSE, "right motor warning", speed)
# command the left motor to go in one of the set directions at a certain speed
# direction = {'FORWARD', 'REVERSE'}
# speed = 0-100%
def setLeftMotor(self, direction, speed):
if direction == "FORWARD":
self.__print(getTime(), "left motor", "speed = " + str(speed) + "%")
self.__writeMotor(self.left_motor, self.left_motor.FORWARD, "left motor warning", speed)
elif direction == "REVERSE":
self.__print(getTime(), "left motor", "speed = " + str(speed) + "%")
self.__writeMotor(self.left_motor, self.left_motor.REVERSE, "left motor warning", speed)
# command the right motor to go in one of the set directions at a certain speed
# direction = {'FORWARD', 'REVERSE'}
# speed = 0-100%
def setRightMotor(self, direction, speed):
if direction == "FORWARD":
self.__print(getTime(), "right motor", "speed = " + str(speed) + "%")
self.__writeMotor(self.right_motor, self.right_motor.FORWARD, "right motor warning", speed)
elif direction == "REVERSE":
self.__print(getTime(), "right motor", "speed = " + str(speed) + "%")
self.__writeMotor(self.right_motor, self.right_motor.REVERSE, "right motor warning", speed)
# command which forces the left motor to stop ASAP
# it uses counter acts with an electromotive force in order to stop the motor from spinning faster
# might raise some warnings depending on how good the power supply is
def stopLeftMotor(self):
self.__print(getTime(), "left motor", "stop")
self.__writeMotor(self.left_motor, self.left_motor.BRAKE, "left motor warning")
# command which forces the right motor to stop ASAP
# it uses counter acts with an electromotive force in order to stop the motor from spinning faster
# might raise some warnings depending on how good the power supply is
def stopRightMotor(self):
self.__print(getTime(), "right motor", "stop")
self.__writeMotor(self.right_motor, self.right_motor.BRAKE, "right motor warning")
# command which forces both the motor to stop ASAP
# it uses counter acts with an electromotive force in order to stop the motors from spinning faster
# might raise some warnings depending on how good the power supply is
def stopMotors(self):
self.stopLeftMotor()
self.stopRightMotor()
# command which kills the power to the motors
def disableMotors(self):
self.__print(getTime(), "standby motors")
self.__writeMotor(self.left_motor, self.left_motor.STANDBY, "left motor warning")
self.__writeMotor(self.right_motor, self.right_motor.STANDBY, "right motor warning")
"""
Will be implemented on Monday
def setWheelTrack(self, centimeters):
self.wheel_track = centimeters / 100
def setWheelDiameter(self, centimeters):
self.wheel_diameter = centimeters / 100
def setWheelMaxRPM(self, RPM):
self.max_wheel_rpm = RPM
def bangLeft(self, degrees, direction, speed):
# code
#
def bangRight(self, degrees, direction,speed):
# code
#
def rotateOnTheSpot(self, degrees, orientation):
# code
#
"""