Skip to content

Commit

Permalink
add read methods & change advanced_module for version 0.4.0 (#4)
Browse files Browse the repository at this point in the history
* use direct values instead of variables for gigglebot module

* fix conflict induced by having the same address on the I2C line

* simplify the gigglebot_advanced module and rename it to gb_diag

* add read functions to each add-on sensor in the gigglebot module

* add little comment in the distance sensor's constructor

* correct version name in the changelog's title
  • Loading branch information
RobertLucian authored Jan 24, 2019
1 parent 60f3235 commit 68563bf
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 91 deletions.
8 changes: 8 additions & 0 deletions changelog.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
v0.4.0:
title: GiggleBot MicroPython Firmware v0.4.0
body:
- Fix the Distance Sensor getting into conflict with the Light and Color sensor on the I2C line - change the
address to a different one. Add note about the conflicts induced by the Distance Sensor in the docs.
- Simplify the `gigglebot_advanced.py` module and rename it to `gb_diag.py`.
- Add read methods for each add-on sensor in the `gigglebot.py` module; the read methods abstract the init
and slightly more complex read methods from each sensor class.
v0.3.2:
title: GiggleBot MicroPython Firmware v0.3.2
body:
Expand Down
10 changes: 5 additions & 5 deletions docs/source/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ characteristics of HSV format `here <http://learn.leighcotnoir.com/artspeak/elem
.. autofunction:: translate_to_hsv
.. autofunction:: guess_color_hsv

***************************
GiggleBot - Advanced Module
***************************
*****************************
GiggleBot - Diagnostic Module
*****************************

The module, should you want to use it in another firmware instead of using the *GiggleBot MicroPython Firmware* (that already comes with all the modules in it),
can be downloaded from :s3-storage-module:`here <gigglebot_advanced.py>` (version |fwversion|).
can be downloaded from :s3-storage-module:`here <gb_diag.py>` (version |fwversion|).

.. automodule:: gigglebot_advanced
.. automodule:: gb_diag
:members:
:special-members:
:exclude-members: __weakref__
Expand Down
4 changes: 2 additions & 2 deletions docs/source/firmware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Modules
The GiggleBot MicroPython firmware comes with the following modules:

* :py:mod:`gigglebot` - contains an API to interface with the actual `GiggleBot`_: think motors, line sensor, light sensors, LEDs, servos, etc.
* :py:mod:`gigglebot_advanced`- still an API to interface with the `GiggleBot`_, but slightly more advanced and useful for debugging purposes.
* :py:mod:`gb_diag`- still an API to interface with the `GiggleBot`_, but slightly more advanced and useful for debugging purposes.
* :py:mod:`distance_sensor` - add-on sensor library for the `Distance Sensor`_.
* :py:mod:`thp` - add-on sensor library for the `Temperature Humidity Pressure Sensor`_.
* :py:mod:`lightcolor` - add-on sensor library for the `Light and Color Sensor`_.
Expand Down Expand Up @@ -65,7 +65,7 @@ And here's what kind of modules can be used on the `BBC microbit`_ hardware:
+===============================+========================+=======================+====================+
|:py:mod:`gigglebot` | yes | yes | yes |
+-------------------------------+------------------------+-----------------------+--------------------+
|:py:mod:`gigglebot_advanced` | yes | yes | yes |
|:py:mod:`gb_diag` | yes | yes | yes |
+-------------------------------+------------------------+-----------------------+--------------------+
|:py:mod:`distance_sensor` | no | no | yes |
+-------------------------------+------------------------+-----------------------+--------------------+
Expand Down
2 changes: 1 addition & 1 deletion docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Downloading the Modules
Apart from being able to download the firmware, the ``.py`` and ``.mpy`` modules can also be downloaded. And just like with the firmware,
you can either download the modules from the `release page <https://github.com/RobertLucian/micropython-gigglebot/releases>`_ or by accessing the following direct links for version |fwversion|:
:s3-storage-module:`gigglebot.py <gigglebot.py>`, :s3-storage-module:`distance_sensor.py <distance_sensor.py>`, :s3-storage-module:`thp.py <thp.py>`, :s3-storage-module:`lightcolor.py <lightcolor.py>`,
:s3-storage-module:`gigglebot_advanced.py <gigglebot_advanced.py>`.
:s3-storage-module:`gb_diag.py <gb_diag.py>`.

You can also check this artifact explorer `here <https://dexind.s3.amazonaws.com/index.html#micropython-gigglebot/firmware/>`__.

Expand Down
12 changes: 10 additions & 2 deletions src/distance_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,21 @@ class DistanceSensor():
did_timeout = False
addr = _DEFAULT_ADDRESS

def __init__(self, address = 0x29, timeout = 500):
def __init__(self, address = 0x2A, timeout = 500):
'''
Constructor for initializing a :py:class:`~distance_sensor.DistanceSensor` object.
:param int address = 0x29: Address of the Distance Sensor. Generally this address does not change.
:param int address = 0x2A: Address of the Distance Sensor. Default address of the device is **0x29**.
:param int timeout = 500: Timeout value for when :py:meth:`~distance_sensor.DistanceSensor.read_range_continuous` is used.
.. important::
When instantiating this object, keep in mind that the `Distance Sensor`_ has by-default, the same address as the
:py:class:`~lightcolor.LightColorSensor`, but gets changed immediately after that, right within the constructor.
What still has to be done is to make sure the distance sensor is the first sensor to be connected and initialiazed and
only after that the `Light and Color Sensor`_ can be plugged in and initialized too on the GiggleBot.
'''

try:
Expand Down
115 changes: 46 additions & 69 deletions src/gigglebot_advanced.py → src/gb_diag.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,115 +23,92 @@

class GiggleBot():
'''
Slightly more advanced class to interface with the `GiggleBot`_ hardware.
Class to get details and check the status of the `GiggleBot`_ hardware.
'''

def __init__(self):
'''
Constructor to initialize a :py:class:`~gigglebot_advanced.GiggleBot` object.
Constructor to initialize a :py:class:`~gb_diag.GiggleBot` object.
'''
self.write = microbit.i2c.write
self.read = microbit.i2c.read
self.buff = bytearray(8)

def get_manufacturer(self):
def get_details(self):
'''
Gets the manufacturer name.
Gets details about the Gigglebot robot.
:returns: The manufacturer name of the board.
:rtype: string
:returns: The manufacturer name of the board, the name of the board and the firmware version of it in this specific order.
:rtype: tuple(string, string, int)
'''
self.write(_GIGGLEBOT_ADDRESS, _GET_MANUFACTURER)
return str(self.read(_GIGGLEBOT_ADDRESS, 20), 'utf-8').strip()

def get_board(self):
'''
Gets the name of the board.
:returns: The name of the board.
:rtype: string
'''
man = str(self.read(_GIGGLEBOT_ADDRESS, 20), 'utf-8').strip()
self.write(_GIGGLEBOT_ADDRESS, _GET_BOARD)
return str(self.read(_GIGGLEBOT_ADDRESS, 20), 'utf-8').strip()
board = str(self.read(_GIGGLEBOT_ADDRESS, 20), 'utf-8').strip()
self.write(_GIGGLEBOT_ADDRESS, _GET_FIRMWARE_VERSION)
fw = ustruct.unpack('>H', self.read(_GIGGLEBOT_ADDRESS, 2))[0]

def get_version_firmware(self):
'''
Gets the firmware version of the GiggleBot board (not the micro:bit's).
return (man, board, fw)

:returns: Firmware version of the GiggleBot board.
:rtype: int
'''
self.write(_GIGGLEBOT_ADDRESS, _GET_FIRMWARE_VERSION)
return ustruct.unpack('>H', self.read(_GIGGLEBOT_ADDRESS, 2))[0]

def get_voltage_battery(self):
def get_voltages(self):
'''
Gets voltage of the battery.
Gets battery and rail voltages.
If the power switch is off, the value returned is unequivocally close to **0**.
If the power switch is off, the value returned for the battery voltage is unequivocally close to **0**.
:returns: The battery voltage.
:rtype: float
:returns: The battery and rail voltages in this order.
:rtype: (float, float)
'''
self.write(_GIGGLEBOT_ADDRESS, _GET_VOLTAGE_BATTERY)
return ustruct.unpack('>H', self.read(_GIGGLEBOT_ADDRESS, 2))[0] / 1000

def get_voltage_rail(self):
'''
Gets the rail voltage.
:returns: Rail voltage.
:rtype: float
'''
bat = ustruct.unpack('>H', self.read(_GIGGLEBOT_ADDRESS, 2))[0] / 1000
self.write(_GIGGLEBOT_ADDRESS, _GET_VOLTAGE_RAIL)
return ustruct.unpack('>H', self.read(_GIGGLEBOT_ADDRESS, 2))[0] / 1000
rail = ustruct.unpack('>H', self.read(_GIGGLEBOT_ADDRESS, 2))[0] / 1000

def get_line_sensors(self):
'''
The line sensor values on the GiggleBot.
return (bat, rail)

# def get_line_sensors(self):
# '''
# The line sensor values on the GiggleBot.

Lower values indicate a darker material and on the opposite, higher values signify lighter colors (closing to white).
# Lower values indicate a darker material and on the opposite, higher values signify lighter colors (closing to white).

:returns: A 2-element tuple with the values of the left and right sensor in this specific order.
:rtype: tuple(int,int)
# :returns: A 2-element tuple with the values of the left and right sensor in this specific order.
# :rtype: tuple(int,int)

'''
self.write(_GIGGLEBOT_ADDRESS, _GET_LINE_SENSORS)
array = self.read(_GIGGLEBOT_ADDRESS, 3)
# '''
# self.write(_GIGGLEBOT_ADDRESS, _GET_LINE_SENSORS)
# array = self.read(_GIGGLEBOT_ADDRESS, 3)

for i in b'\x00\x01':
ustruct.pack_into('f', self.buff, 1 - i, (1023 - (array[i] << 2) | (((array[2] << (i * 2)) & 0xC0) >> 6)) / 1023.0)
# for i in b'\x00\x01':
# ustruct.pack_into('f', self.buff, 1 - i, (1023 - (array[i] << 2) | (((array[2] << (i * 2)) & 0xC0) >> 6)) / 1023.0)

return ustruct.unpack('ff', self.buff)
# return ustruct.unpack('ff', self.buff)

def get_light_sensors(self):
'''
The light sensor values on the GiggleBot.
# def get_light_sensors(self):
# '''
# The light sensor values on the GiggleBot.

The lower the values, the darker is the environment around the GiggleBot. It's vice-versa for higher values.
# The lower the values, the darker is the environment around the GiggleBot. It's vice-versa for higher values.

:returns: A 2-element tuple with the values of the left and right light sensor in this specific order.
:rtype: tuple(int,int)
# :returns: A 2-element tuple with the values of the left and right light sensor in this specific order.
# :rtype: tuple(int,int)

'''
self.write(_GIGGLEBOT_ADDRESS, _GET_LIGHT_SENSORS)
array = self.read(_GIGGLEBOT_ADDRESS, 3)
# '''
# self.write(_GIGGLEBOT_ADDRESS, _GET_LIGHT_SENSORS)
# array = self.read(_GIGGLEBOT_ADDRESS, 3)

for i in b'\x00\x01':
ustruct.pack_into('f', self.buff, 1 - i, (1023 - (array[i] << 2) | (((array[2] << (i * 2)) & 0xC0) >> 6)) / 1023.0)
# for i in b'\x00\x01':
# ustruct.pack_into('f', self.buff, 1 - i, (1023 - (array[i] << 2) | (((array[2] << (i * 2)) & 0xC0) >> 6)) / 1023.0)

return ustruct.unpack('ff', self.buff)
# return ustruct.unpack('ff', self.buff)

def set_motor_power(self, port, power):
'''
Sets the power of a single motor on the GiggleBot.
:param port: Either :py:attr:`~gigglebot_advanced.MOTOR_LEFT` or :py:attr:`~gigglebot_advanced.MOTOR_RIGHT` depending on which motor has to be controlled.
:param port: Either :py:attr:`~gb_diag.MOTOR_LEFT` or :py:attr:`~gb_diag.MOTOR_RIGHT` depending on which motor has to be controlled.
:param int power: **-100** to **0** for reverse and from **0** to **100** for full speed ahead.
'''
Expand All @@ -151,7 +128,7 @@ def get_motor_status(self, port):
'''
Returns a report of the status of a given motor on the GiggleBot.
:param port: Either :py:attr:`~gigglebot_advanced.MOTOR_LEFT` or :py:attr:`~gigglebot_advanced.MOTOR_RIGHT`.
:param port: Either :py:attr:`~gb_diag.MOTOR_LEFT` or :py:attr:`~gb_diag.MOTOR_RIGHT`.
This method returns a 2-element list where the 1st element is called the ``status_flag`` and the 2nd one is
represents the current speed set for the given motor.
Expand Down
85 changes: 73 additions & 12 deletions src/gigglebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@

_BUFFER = bytearray(10)
_GIGGLEBOT_ADDRESS = const(0x04)
_GET_VOLTAGE_BATTERY = b'\x04' #: I2C command to query voltage level of the battery.
_SET_MOTOR_POWERS = b'\x0a' #: I2C command to write new power values to the motor.
# _GET_VOLTAGE_BATTERY = b'\x04' #: I2C command to query voltage level of the battery.
# _SET_MOTOR_POWERS = b'\x0a' #: I2C command to write new power values to the motor.

_DS = None
_THP = None
_LCS = None

def init():
"""
Expand Down Expand Up @@ -73,9 +77,9 @@ def set_eye_color_on_start():
This is called by the :py:meth:`~init()`, usually at the start of the program.
You are free to call this method whenever you want if you need to keep a closer watch on the voltage level.
"""
i2c.write(_GIGGLEBOT_ADDRESS, _GET_VOLTAGE_BATTERY)
i2c.write(0x04, b'\x04')

if unpack('>H', i2c.read(_GIGGLEBOT_ADDRESS, 2))[0] < 3400:
if unpack('>H', i2c.read(0x04, 2))[0] < 3400:
neopixelstrip[0]=(10, 0, 0)
neopixelstrip[1]=(10, 0, 0)
else:
Expand Down Expand Up @@ -113,7 +117,7 @@ def drive(dir=FORWARD, milliseconds=-1):
:param int dir = FORWARD: Possible values are :py:attr:`~gigglebot.FORWARD` (1) or :py:attr:`~gigglebot.BACKWARD` (-1). Please note there are no tests done on this value. One could theoretically use 2 to double the speed.
:param int milliseconds = -1: If this parameter is omitted, or a negative value is supplied, the robot will keep on going until told to do something else, like turning or stopping. If a positive value is supplied, the robot will drive for that quantity of milliseconds.
'''
i2c.write(_GIGGLEBOT_ADDRESS, _SET_MOTOR_POWERS + pack('BB', int(motor_power_left*dir) & 0xFF, int(motor_power_right*dir) & 0xFF))
i2c.write(0x04, b'\x0a' + pack('BB', int(motor_power_left*dir) & 0xFF, int(motor_power_right*dir) & 0xFF))
if milliseconds >= 0:
sleep(milliseconds)
stop()
Expand All @@ -126,9 +130,9 @@ def turn(dir=LEFT, milliseconds=-1):
:param int milliseconds=-1: If this parameter is omitted, or a negative value is supplied, the robot will keep on going until told to do something else, like turning or stopping. If a positive value is supplied, the robot will drive for that quantity of milliseconds.
"""
if dir == LEFT:
i2c.write(_GIGGLEBOT_ADDRESS, _SET_MOTOR_POWERS + pack('BB', int(motor_power_left) & 0xFF, 0))
i2c.write(0x04, b'\x0a' + pack('BB', int(motor_power_left) & 0xFF, 0))
if dir == RIGHT:
i2c.write(_GIGGLEBOT_ADDRESS, _SET_MOTOR_POWERS + pack('BB', 0, int(motor_power_right) & 0xFF))
i2c.write(0x04, b'\x0a' + pack('BB', 0, int(motor_power_right) & 0xFF))
if milliseconds >= 0:
sleep(milliseconds)
stop()
Expand All @@ -137,7 +141,7 @@ def stop():
"""
Stops the GiggleBot right away.
"""
i2c.write(_GIGGLEBOT_ADDRESS, _SET_MOTOR_POWERS + b'\x00\x00')
i2c.write(0x04, b'\x0a' + b'\x00\x00')

def set_speed(power_left, power_right):
"""
Expand Down Expand Up @@ -208,8 +212,8 @@ def read_sensor(which_sensor, which_side):
right, left = read_sensor(LIGHT_SENSOR, BOTH)
"""
i2c.write(_GIGGLEBOT_ADDRESS, pack('B', which_sensor))
buf = i2c.read(_GIGGLEBOT_ADDRESS, 3)
i2c.write(0x04, pack('B', which_sensor))
buf = i2c.read(0x04, 3)
pack_into('>HH', _BUFFER, 0, 1023 - (buf[0] << 2 | ((buf[2] & 0xC0) >> 6)), 1023 - (buf[1] << 2 | ((buf[2] & 0x30) >> 4)))

if which_side == LEFT:
Expand All @@ -219,11 +223,68 @@ def read_sensor(which_sensor, which_side):
else:
return unpack_from('>HH', _BUFFER)

def read_distance_sensor():
"""
Read the detected range by the distance sensor. Uses the :py:meth:`~distance_sensor.DistanceSensor.read_range_single` method.
:returns: The distance to the object as measured in millimeters. Range is up to 2.3 meters.
:rtype: int
:raises OSError: When there's trouble reaching the sensor.
:raises ImportError: If this module is run from a firmware that doesn't have the :py:mod:`distance_sensor` module.
"""
global _DS
if _DS is None:
from distance_sensor import DistanceSensor
_DS = DistanceSensor()

return _DS.read_range_single()

def read_thp_sensor():
"""
Read the temperature, the atmospheric pressure, humidity and dewpoint temperature.
Uses the :py:class:`~thp.TempHumPress` class to do that.
:returns: In this specific order: **temp** in *Celsius*, **temp** in *Fahrenheit*, **pressure** in *Pascals* unit, **humidity** as percentage, **dewpoint** in *Celsius*, **dewpoint** in *Fahrenheit*.
:rtype: 6-element float tuple
:raises OSError: When there's trouble reaching the sensor.
:raises ImportError: If this module is run from a firmware that doesn't have the :py:mod:`thp` module.
"""
global _THP
if _THP is None:
from thp import TempHumPress
_THP = TempHumPress()

return (
_THP.get_temperature_celsius(),
_THP.get_temperature_fahrenheit(),
_THP.get_pressure(),
_THP.get_humidity(),
_THP.get_dewpoint_celsius(),
_THP.get_dewpoint_fahrenheit()
)

def read_light_color_sensor():
"""
Detect the color with the Light and Color sensor. Uses the :py:meth:`~lightcolor.LightColorSensor.get_color` method.
:returns:
:rtype: tuple(string, tuple(int,int,int))
:raises OSError: When there's trouble reaching the sensor.
:raises ImportError: If this module is run from a firmware that doesn't have the :py:mod:`lightcolor` module.
"""
global _LCS
if _LCS is None:
from lightcolor import LightColorSensor
_LCS = LightColorSensor()
_LCS.set_led(True)

return _LCS.get_color()

def volt():
"""
Returns the voltage level of the batteries.
:returns: Voltage level of the batteries.
"""
i2c.write(_GIGGLEBOT_ADDRESS, _GET_VOLTAGE_BATTERY)
return unpack('>H', i2c.read(_GIGGLEBOT_ADDRESS, 2))[0] / 1000.0
i2c.write(0x04, b'\x04')
return unpack('>H', i2c.read(0x04, 2))[0] / 1000.0

0 comments on commit 68563bf

Please sign in to comment.