Added board test suite

This commit is contained in:
ShawnHymel 2018-12-08 16:47:17 -06:00
parent 3d597aae92
commit 52dbbcb23f
26 changed files with 1509 additions and 0 deletions

View File

@ -0,0 +1,56 @@
Introduction
============
Board test suite for CircuitPython. Run these tests to ensure that a CircuitPython port was created correctly, individual pin mappings are correct, and buses (e.g. SPI) work.
Tests can be run individually. Copy code found in each *<name>_test.py* module (found in *source* directory) to main.py in your CIRCUITPYTHON device drive.
Alternatively, tests can be imported as modules. Copy the *lib* directory to CIRCUITPYTHON device drive and import the test in your own code. Each test can be run with the `run_test(pins)` function.
The *main.py* example shows how to call tests from within a script. *main.py* runs the following tests:
* LED Test
* GPIO Test
* Voltage Monitor Test
* UART Test
* SPI Test
* I2C Test
Dependencies
=============
This test suite depends on:
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
* `SD Card <https://github.com/adafruit/Adafruit_CircuitPython_SD>`_
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
`the Adafruit library and driver bundle <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_.
Usage Example
=============
You will need the following components:
* Multimeter
* LED
* 1x 330 Ohm resistor
* 2x 4.7k Ohm resistor
* Microchip 25AA040A SPI EEPROM
* Microchip AT24HC04B I2C EEPROM
* Breadboard
* Wires
Connect the components as shown to your board.
![Test jig Fritzing diagram](doc/test_jig.png)
Copy the *lib* folder to the CIRCUITPYTHON drive. Copy *main.py* to the root directory of your CIRCUITPYTHON drive. Open a Serial terminal and connect to the board. Follow the directions given to run through the tests.
Building
========
Individual test modules can be built with the mpy-cross cross-compiler. This is required to save RAM space if you plan to run more than one test at a time. See [the mpy-cross directory in circuitpython](https://github.com/adafruit/circuitpython/tree/master/mpy-cross) to learn more.

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,235 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`board_test_suite`
====================================================
CircuitPython board hardware test suite
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Run this to test various input/output abilities of a board. Tests the following:
* Onboard LEDs
* GPIO output
* Onboard battery voltage monitor
* SPI
* I2C
You will need the following components:
* Multimeter
* LED
* 1x 330 Ohm resistor
* 2x 4.7k Ohm resistor
* Microchip 25AA040A SPI EEPROM
* Microchip AT24HC04B I2C EEPROM
* Breadboard
* Wires
Copy lib directory to CIRCUITPYTHON drive. Copy the contents of this file to
main.py in root of CIRCUITPYTHON. Open Serial terminal to board and follow
prompts given.
"""
import board
import led_test
import gpio_test
import voltage_monitor_test
import uart_test
import spi_test
import i2c_test
# Constants
UART_TX_PIN_NAME = 'TX'
UART_RX_PIN_NAME = 'RX'
UART_BAUD_RATE = 9600
SPI_MOSI_PIN_NAME = 'MOSI'
SPI_MISO_PIN_NAME = 'MISO'
SPI_SCK_PIN_NAME = 'SCK'
SPI_CS_PIN_NAME = 'D2'
I2C_SDA_PIN_NAME = 'SDA'
I2C_SCL_PIN_NAME = 'SCL'
# Results dictionary
test_results = {}
# Save tested pins
pins_tested = []
# Print welcome message
print()
print(" .... ")
print(" #@@%%%%%%&@@/ ")
print(" (&@%%%%%%%%%%%%%@& ")
print(" .(@&%%%@* *&%%%%%%@. ")
print(" ,@@&&%%%%%%%%//@%,/ /&%%%%%%@ ")
print(" %@%%%&%%%%%%%#(@@@&&%%%%%%%%@* ")
print(" @&%%&%%%%%%%%%%%%%%%%%%%%%%@/ ")
print(" &@@&%%%%&&&%%%%%%%%%%%%%%@, ")
print(" ,/ &@&&%%%%%%%%%%%%%%%%%@ ")
print(" ,* *@&%%%%%%%%%%%%# ")
print(" ( @%%%%%%%%%%%@ ")
print(" , @%%%%%%%%%%&@ ")
print(" #&%%%%%%%%%%@. ")
print(" #@###%%%%%%%@/ ")
print(" (@##(%%%%%%%@% ")
print(" /@###(#%%%%%&@ ")
print(" #@####%%%%%%%@ ")
print(" (@###(%%%%%%%@, ")
print(" .@##(((#%%%%%&( .,,. ")
print(" ,@#####%%%%%%%@ ,%@@%%%%%%%&@% ")
print(" ,#&@####(%%%%%%%@@@@@&%%%%%%%%%%%###& ")
print(" @%%@%####(#%%%%%&@%%%%%%%%%%%%%%##/((@@@@&* ")
print(" (##@%#####%%%%%%%@(#%%%(/####(/####(%@%%%%%%@/ ")
print(" (@&%@@###(#%%%%%%@&/####(/#####/#&@@&%%%%%%%##@ ")
print(" #@%%%%@#####(#%%%%%%@@@@@@@@@@@@@&%%%%%%%%%%%%#/(@@@@@/ ")
print(" @%(/#@%######%%%%%%%@%%%%%%%%%%%%%%%%%%%%%(/(###@%%%%%%@% ")
print(" .@@#(#@#####(#%%%%%%&@###//#####/#####/(####/#%@&%%%%%%%%&& ")
print(" /@%%&@@@(#((((#%%%%%%&@###((#####/#####((##%@@&%%%%%%%%%%%/@. ")
print(" ,@%%%%%%#####%%%%%%%%@@@@&&&&&&&%&@@@@@@&%%%%%%%%%%%%%%%##@, ")
print(" %%%%%%%%@######(%%%%%%%@&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#/(#&& ")
print(" (@###/(%@##((##(%%%%%%%%@%%%%%%%%%%%%%%%%%%%%%%%%%##%###/(&& ")
print(" ,@@%@%##((#%@#######%%%%%%%%@&%%%%##%%%%##%%%%#/#####((####(@* ")
print(" *&(, %@@%##%@#######(%%%%%%%%@#/#####((#####(#####(/#&@&. ")
print(" .@###((#%%%%%%%%%&@@###((#####(###%@@&, ")
print(" #@#(#######%&@@&* .*#&@@@@@@@%(, ")
print(" .,,,.. ")
print()
print("**********************************************************************")
print("* Welcome to the CircuitPython board test suite! *")
print("* Follow the directions to run each test. *")
print("**********************************************************************")
print()
# List out all the pins available to us
pins = [p for p in dir(board)]
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run LED test
print("@)}---^----- LED TEST -----^---{(@")
print()
result = led_test.run_test(pins)
test_results["LED Test"] = result[0]
pins_tested.append(result[1])
print()
print(result[0])
print()
# Run GPIO test
print("@)}---^----- GPIO TEST -----^---{(@")
print()
result = gpio_test.run_test(pins)
test_results["GPIO Test"] = result[0]
pins_tested.append(result[1])
print()
print(result[0])
print()
# Run voltage monitor test
print("@)}---^----- VOLTAGE MONITOR TEST -----^---{(@")
print()
result = voltage_monitor_test.run_test(pins)
test_results["Voltage Monitor Test"] = result[0]
pins_tested.append(result[1])
print()
print(result[0])
print()
# Run UART test
print("@)}---^----- UART TEST -----^---{(@")
print()
result = uart_test.run_test(pins, UART_TX_PIN_NAME, UART_RX_PIN_NAME, UART_BAUD_RATE)
test_results["UART Test"] = result[0]
pins_tested.append(result[1])
print()
print(result[0])
print()
# Run SPI test
print("@)}---^----- SPI TEST -----^---{(@")
print()
result = spi_test.run_test( pins,
mosi_pin=SPI_MOSI_PIN_NAME,
miso_pin=SPI_MISO_PIN_NAME,
sck_pin=SPI_SCK_PIN_NAME,
cs_pin=SPI_CS_PIN_NAME)
test_results["SPI Test"] = result[0]
pins_tested.append(result[1])
print()
print(result[0])
print()
# Run I2C test
print("@)}---^----- I2C TEST -----^---{(@")
print()
result = i2c_test.run_test(pins, sda_pin=I2C_SDA_PIN_NAME, scl_pin=I2C_SCL_PIN_NAME)
test_results["I2C Test"] = result[0]
pins_tested.append(result[1])
print()
print(result[0])
print()
# Print out test results
print("@)}---^----- TEST RESULTS -----^---{(@")
print()
# Find appropriate spaces for printing test results
num_spaces = 0
for key in test_results:
if len(key) > num_spaces:
num_spaces = len(key)
# Print test results
for key in test_results:
print(key + ":", end=' ')
for i in range(num_spaces - len(key)):
print(end=' ')
print(test_results[key])
print()
# Figure out which pins were tested and not tested
tested = []
for sublist in pins_tested:
for p in sublist:
tested.append(p)
not_tested = list(set(pins).difference(set(tested)))
# Print tested pins
print("The following pins were tested:", end=' ')
for p in tested:
print(p, end=' ')
print('\n')
# Print pins not tested
print("The following pins were NOT tested:", end=' ')
for p in not_tested:
print(p, end=' ')
print('\n')

Binary file not shown.

View File

@ -0,0 +1,154 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`gpio_test`
====================================================
GPIO Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Toggles all available GPIO on a board. Verify their operation with an LED,
multimeter, another microcontroller, etc.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import digitalio
import supervisor
import time
# Constants
LED_ON_DELAY_TIME = 0.2 # Seconds
LED_OFF_DELAY_TIME = 0.2 # Seconds
LED_PIN_NAMES = ['L', 'LED', 'RED_LED', 'GREEN_LED', 'BLUE_LED']
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
# Determine if given value is a number
def _is_number(s):
try:
float(s)
return True
except ValueError:
return False
# Release pins
def _deinit_pins(gpios):
for g in gpios:
g.deinit()
# Toggle IO pins while waiting for answer
def _toggle_wait(gpios):
global test_results
timestamp = time.monotonic()
led_state = False
print("Are the pins listed above toggling? [y/n]")
while True:
if led_state:
if time.monotonic() > timestamp + LED_ON_DELAY_TIME:
led_state = False
timestamp = time.monotonic()
else:
if time.monotonic() > timestamp + LED_OFF_DELAY_TIME:
led_state = True
timestamp = time.monotonic()
for gpio in gpios:
gpio.value = led_state
if supervisor.runtime.serial_bytes_available:
answer = input()
if answer == 'y':
return True
else:
return False
break
def run_test(pins):
# Create a list of analog GPIO pins
analog_pins = [p for p in pins if p[0] == 'A' and _is_number(p[1])]
# Create a list of digital GPIO
digital_pins = [p for p in pins if p[0] == 'D' and _is_number(p[1])]
# Toggle LEDs if we find any
gpio_pins = analog_pins + digital_pins
if gpio_pins:
# Create a list of IO objects for us to toggle
gpios = [digitalio.DigitalInOut(getattr(board, p)) for p in gpio_pins]
# Print out the LEDs found
print("GPIO pins found:", end=' ')
for p in gpio_pins:
print(p, end=' ')
print('\n')
# Set all IO to output
for gpio in gpios:
gpio.direction = digitalio.Direction.OUTPUT
# Toggle pins while waiting for user to verify LEDs blinking
result = _toggle_wait(gpios)
# Release pins
_deinit_pins(gpios)
if result:
return PASS, gpio_pins
else:
return FAIL, gpio_pins
else:
print("No GPIO pins found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,191 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`i2c_test`
====================================================
I2C Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Performs random writes and reads to I2C EEPROM.
Requires Microchip AT24HC04B I2C EEPROM.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import busio
import random
import time
# Constants
SDA_PIN_NAME = 'SDA'
SCL_PIN_NAME = 'SCL'
NUM_I2C_TESTS = 10 # Number of times to write and read EEPROM values
EEPROM_I2C_MAX_ADDR = 255 # Self-imposed max memory address
# Microchip AT24HC04B EEPROM I2C address
EEPROM_I2C_ADDR = 0x50
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
# Open comms to I2C EEPROM by trying a write to memory address
def _eeprom_i2c_wait(i2c, i2c_addr, mem_addr, timeout = 1.0):
# Try to access the I2C EEPROM (it becomes unresonsive during a write)
timestamp = time.monotonic()
while time.monotonic() < timestamp + timeout:
try:
i2c.writeto(i2c_addr, bytearray([mem_addr]), end=1, stop=False)
return True
except:
pass
return False
# Write to address. Returns status (True for successful write, False otherwise)
def _eeprom_i2c_write_byte(i2c, i2c_addr, mem_addr, mem_data, timeout = 1.0):
# Make sure address is only one byte:
if mem_addr > 255:
return False
# Make sure data is only one byte:
if mem_data > 255:
return False
# Write data to memory at given address
try:
i2c.writeto(i2c_addr, bytearray([mem_addr, mem_data]))
except:
return False
return True
# Read from address. Returns tuple [status, result]
def _eeprom_i2c_read_byte(i2c, i2c_addr, mem_addr, timeout = 1.0):
# Make sure address is only one byte:
if mem_addr > 255:
return False, bytearray()
# Try writing to address (EEPROM is unresponsive while writing)
if _eeprom_i2c_wait(i2c, i2c_addr, mem_addr, timeout) == False:
return False, bytearray()
# Finish the read
buf = bytearray(1)
i2c.readfrom_into(i2c_addr, buf)
return True, buf
def run_test(pins, sda_pin=SDA_PIN_NAME, scl_pin=SCL_PIN_NAME):
# Write values to I2C EEPROM and verify the values match
if list(set(pins).intersection(set([sda_pin, scl_pin]))):
# Tell user to connect EEPROM chip
print("Connect a Microchip AT24HC04B EEPROM I2C chip. " +
"Press enter to continue.")
input()
# Set up I2C
i2c = busio.I2C(getattr(board, scl_pin), getattr(board, sda_pin))
# Wait for I2C lock
while not i2c.try_lock():
pass
# Pick a random address, write to it, read from it, and see if they match
pass_test = True
for i in range(NUM_I2C_TESTS):
# Randomly pick an address and a data value (one byte)
mem_addr = random.randint(0, EEPROM_I2C_MAX_ADDR)
mem_data = random.randint(0, 255)
print("Address:\t" + hex(mem_addr))
print("Writing:\t" + hex(mem_data))
# Try writing this random value to the random address
result = _eeprom_i2c_write_byte(i2c, EEPROM_I2C_ADDR, mem_addr, mem_data)
if result == False:
print("FAIL: I2C could not communicate")
pass_test = False
break
# Try reading the written value back from EEPROM
result = _eeprom_i2c_read_byte(i2c, EEPROM_I2C_ADDR, mem_addr)
print("Read:\t\t" + hex(result[1][0]))
print()
if result[0] == False:
print("FAIL: I2C could not communicate")
pass_test = False
break
# Compare the read value to the original value
if result[1][0] != mem_data:
print("FAIL: Data does not match")
pass_test = False
break
# Release I2C pins
i2c.deinit()
# Store results
if pass_test:
return PASS, [sda_pin, scl_pin]
else:
return FAIL, [sda_pin, scl_pin]
else:
print("No I2C pins found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,142 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`led_test`
====================================================
LED Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Toggles all available onboard LEDs. You will need to manually verify their
operation by watching them.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import digitalio
import supervisor
import time
# Constants
LED_ON_DELAY_TIME = 0.2 # Seconds
LED_OFF_DELAY_TIME = 0.2 # Seconds
LED_PIN_NAMES = ['L', 'LED', 'RED_LED', 'GREEN_LED', 'BLUE_LED']
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
# Release pins
def _deinit_pins(gpios):
for g in gpios:
g.deinit()
# Toggle IO pins while waiting for answer
def _toggle_wait(gpios):
global test_results
timestamp = time.monotonic()
led_state = False
print("Are the pins listed above toggling? [y/n]")
while True:
if led_state:
if time.monotonic() > timestamp + LED_ON_DELAY_TIME:
led_state = False
timestamp = time.monotonic()
else:
if time.monotonic() > timestamp + LED_OFF_DELAY_TIME:
led_state = True
timestamp = time.monotonic()
for gpio in gpios:
gpio.value = led_state
if supervisor.runtime.serial_bytes_available:
answer = input()
if answer == 'y':
return True
else:
return False
break
def run_test(pins):
# Look for pins with LED names
led_pins = list(set(pins).intersection(set(LED_PIN_NAMES)))
# Toggle LEDs if we find any
if led_pins:
# Print out the LEDs found
print("LEDs found:", end=' ')
for p in led_pins:
print(p, end=' ')
print('\n')
# Create a list of IO objects for us to toggle
leds = [digitalio.DigitalInOut(getattr(board, p)) for p in led_pins]
# Set all LEDs to output
for led in leds:
led.direction = digitalio.Direction.OUTPUT
# Blink LEDs and wait for user to verify test
result = _toggle_wait(leds)
# Release pins
_deinit_pins(leds)
if result:
return PASS, led_pins
else:
return FAIL, led_pins
else:
print("No LED pins found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,110 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`sd_cd_test`
====================================================
SD CD Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Reports the output of an SD card's chip detect (CD) pin.
Requires SD card.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import digitalio
# Constants
SD_CD_PIN_NAME = 'SD_CD'
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
def run_test(pins, cd_pin=SD_CD_PIN_NAME):
# Ask user to insert and remove SD card
if list(set(pins).intersection(set([cd_pin]))):
# Configure CD pin as input with pullup
cd = digitalio.DigitalInOut(getattr(board, cd_pin))
cd.direction = digitalio.Direction.INPUT
cd.pull = digitalio.Pull.UP
# Tell user to insert SD card
print("Connect " + cd_pin + " to CD pin on SD card holder.")
print("Insert SD card into holder.")
print("Press enter to continue.")
input()
# Make sure we see that the pin is low
if cd.value == True:
print("Error: Card not detected")
return FAIL, [cd_pin]
# Tell user to remove SD card
print("Card detected. Remove card and press enter to continue.")
input()
# Make sure we see that the pin is high
if cd.value == False:
print("Error: Card detected")
return FAIL, [cd_pin]
# Test passed
print("Card removed")
return PASS, [cd_pin]
else:
print("No CD pin found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,157 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`sd_test`
====================================================
SD Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Performs random writes and reads to SD card over SPI.
Requires SD card.
Requires adafruit_sdcard.mpy and adafruit_bus_device modules.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import adafruit_sdcard
import board
import busio
import digitalio
import storage
import random
# Constants
MOSI_PIN_NAME = 'SD_MOSI'
MISO_PIN_NAME = 'SD_MISO'
SCK_PIN_NAME = 'SD_SCK'
CS_PIN_NAME = 'SD_CS'
FILENAME = "test.txt" # File that will be written to
BAUD_RATE = 100000 # Bits per second
NUM_UART_BYTES = 40 # Number of bytes to transmit over UART
ASCII_MIN = 0x21 # '!' Lowest ASCII char in random range (inclusive)
ASCII_MAX = 0x7E # '~' Highest ASCII char in random range (inclusive)
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
def run_test( pins,
mosi_pin=MOSI_PIN_NAME,
miso_pin=MISO_PIN_NAME,
sck_pin=SCK_PIN_NAME,
cs_pin=CS_PIN_NAME,
filename=FILENAME):
# Write characters to file on SD card and verify they were written
if list(set(pins).intersection(set([mosi_pin, miso_pin, sck_pin]))):
# Tell user to connect SD card
print("Insert SD card into holder and connect SPI lines to holder.")
print("Connect " + cs_pin + " to the CS (CD/DAT3) pin on the SD " +
"card holder.")
print("WARNING: " + filename + " will be created or overwritten.")
print("Press enter to continue.")
input()
# Configure CS pin
cs = digitalio.DigitalInOut(getattr(board, cs_pin))
cs.direction = digitalio.Direction.OUTPUT
cs.value = True
# Set up SPI
spi = busio.SPI(getattr(board, sck_pin),
MOSI=getattr(board, mosi_pin),
MISO=getattr(board, miso_pin))
# Try to connect to the card and mount the filesystem
try:
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, "/sd")
except:
print("Could not mount SD card")
return FAIL, [mosi_pin, miso_pin, sck_pin]
# Generate test string
test_str = ""
for i in range(NUM_UART_BYTES):
test_str += chr(random.randint(ASCII_MIN, ASCII_MAX))
# Write test string to a text file on the card
try:
with open("/sd/" + filename, "w") as f:
print("Writing:\t" + test_str)
f.write(test_str)
except:
print("Could not write to SD card")
return FAIL, [mosi_pin, miso_pin, sck_pin]
# Read from test file on the card
read_str = ""
try:
with open("/sd/" + filename, "r") as f:
lines = f.readlines()
for line in lines:
read_str += line
print("Read:\t\t" + read_str)
except:
print("Could not read from SD card")
return FAIL, [mosi_pin, miso_pin, sck_pin]
# Release SPI
spi.deinit()
# Compare strings
if read_str == test_str:
return PASS, [mosi_pin, miso_pin, sck_pin]
else:
return FAIL, [mosi_pin, miso_pin, sck_pin]
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,232 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`spi_test`
====================================================
SPI Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Performs random writes and reads to SPI EEPROM.
Requires Microchip 25AA040A SPI EEPROM.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import digitalio
import busio
import random
import time
# Constants
MOSI_PIN_NAME = 'MOSI'
MISO_PIN_NAME = 'MISO'
SCK_PIN_NAME = 'SCK'
CS_PIN_NAME = 'D2'
BAUD_RATE = 100000 # Bits per second
NUM_SPI_TESTS = 10 # Number of times to write and read EEPROM values
# Microchip 25AA040A EEPROM SPI commands and bits
EEPROM_SPI_WRSR = 0x01
EEPROM_SPI_WRITE = 0x02
EEPROM_SPI_READ = 0x03
EEPROM_SPI_WRDI = 0x04
EEPROM_SPI_RDSR = 0x05
EEPROM_SPI_WREN = 0x06
EEPROM_SPI_WIP_BIT = 0
EEPROM_SPI_MAX_ADDR = 255 # Self-imposed max memory address
EEPROM_I2C_MAX_ADDR = 255 # Self-imposed max memory address
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
# Wait for WIP bit to go low
def _eeprom_spi_wait(spi, cs, timeout = 1.0):
# Continually read from STATUS register
timestamp = time.monotonic()
while time.monotonic() < timestamp + timeout:
# Perfrom RDSR operation
cs.value = False
result = bytearray(1)
spi.write(bytearray([EEPROM_SPI_RDSR]))
spi.readinto(result)
cs.value = True
# Mask out and compare WIP bit
if (result[0] & (1 << EEPROM_SPI_WIP_BIT)) == 0:
return True
return False
# Write to address. Returns status (True for successful write, False otherwise)
def _eeprom_spi_write_byte(spi, cs, address, data, timeout = 1.0):
# Make sure address is only one byte:
if address > 255:
return False
# Make sure data is only one byte:
if data > 255:
return False
# Wait for WIP to be low
if _eeprom_spi_wait(spi, cs, timeout) == False:
return False
# Enable writing
cs.value = False
spi.write(bytearray([EEPROM_SPI_WREN]))
cs.value = True
# Write to address
cs.value = False
spi.write(bytearray([EEPROM_SPI_WRITE, address, data]))
cs.value = True
return True
# Read from address. Returns tuple [status, result]
def _eeprom_spi_read_byte(spi, cs, address, timeout = 1.0):
# Make sure address is only one byte:
if address > 255:
return False, bytearray()
# Wait for WIP to be low
if _eeprom_spi_wait(spi, cs, timeout) == False:
return False, bytearray()
# Read byte from address
cs.value = False
result = bytearray(1)
spi.write(bytearray([EEPROM_SPI_READ, address]))
spi.readinto(result)
cs.value = True
return True, result
def run_test( pins,
mosi_pin=MOSI_PIN_NAME,
miso_pin=MISO_PIN_NAME,
sck_pin=SCK_PIN_NAME,
cs_pin=CS_PIN_NAME):
# Write values to SPI EEPROM and verify the values match
if list(set(pins).intersection(set([mosi_pin, miso_pin, sck_pin]))):
# Tell user to connect EEPROM chip
print("Connect a Microchip 25AA040A EEPROM SPI chip.")
print("Connect " + cs_pin + " to the CS pin on the 25AA040.")
print("Press enter to continue.")
input()
# Configure CS pin
cs = digitalio.DigitalInOut(getattr(board, cs_pin))
cs.direction = digitalio.Direction.OUTPUT
cs.value = True
# Set up SPI
spi = busio.SPI(getattr(board, sck_pin),
MOSI=getattr(board, mosi_pin),
MISO=getattr(board, miso_pin))
# Wait for SPI lock
while not spi.try_lock():
pass
spi.configure(baudrate=BAUD_RATE, phase=0, polarity=0)
# Pick a random address, write to it, read from it, and see if they match
pass_test = True
for i in range(NUM_SPI_TESTS):
# Randomly pick an address and a data value (one byte)
mem_addr = random.randint(0, EEPROM_SPI_MAX_ADDR)
mem_data = random.randint(0, 255)
print("Address:\t" + hex(mem_addr))
print("Writing:\t" + hex(mem_data))
# Try writing this random value to the random address
result = _eeprom_spi_write_byte(spi, cs, mem_addr, mem_data)
if result == False:
print("FAIL: SPI could not communicate")
pass_test = False
break
# Try reading the written value back from EEPRom
result = _eeprom_spi_read_byte(spi, cs, mem_addr)
print("Read:\t\t" + hex(result[1][0]))
print()
if result[0] == False:
print("FAIL: SPI could not communicate")
pass_test = False
break
# Compare the read value to the original value
if result[1][0] != mem_data:
print("FAIL: Data does not match")
pass_test = False
break
# Release SPI pins
spi.deinit()
# Return results
if pass_test:
return PASS, [mosi_pin, miso_pin, sck_pin]
else:
return FAIL, [mosi_pin, miso_pin, sck_pin]
else:
print("No SPI pins found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,121 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`uart_test`
====================================================
UART Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Performs random writes and reads across UART.
You will need to connect a loopback wire from TX to RX on your board.
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import busio
import random
# Constants
TX_PIN_NAME = 'TX'
RX_PIN_NAME = 'RX'
BAUD_RATE = 9600
NUM_UART_BYTES = 40 # Number of bytes to transmit over UART
ASCII_MIN = 0x21 # '!' Lowest ASCII char in random range (inclusive)
ASCII_MAX = 0x7E # '~' Highest ASCII char in random range (inclusive)
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
def run_test(pins, tx_pin=TX_PIN_NAME, rx_pin=RX_PIN_NAME, baud_rate=BAUD_RATE):
# Echo some values over the UART
if list(set(pins).intersection(set([tx_pin, rx_pin]))):
# Tell user to create loopback connection
print("Connect a wire from TX to RX. Press enter to continue.")
input()
# Initialize UART
uart = busio.UART(getattr(board, tx_pin),
getattr(board, rx_pin),
baudrate=baud_rate)
uart.reset_input_buffer()
# Generate test string
test_str = ""
for i in range(NUM_UART_BYTES):
test_str += chr(random.randint(ASCII_MIN, ASCII_MAX))
# Transmit test string
uart.write(test_str)
print("Transmitting:\t" + test_str)
# Wait for received string
data = uart.read(len(test_str))
recv_str = ''
if data is not None:
recv_str = ''.join([chr(b) for b in data])
print("Received:\t" + recv_str)
# Release UART pins
uart.deinit()
# Compare strings
if recv_str == test_str:
return PASS, [tx_pin, rx_pin]
else:
return FAIL, [tx_pin, rx_pin]
else:
print("No UART pins found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()

View File

@ -0,0 +1,111 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Shawn Hymel for Adafruit 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.
"""
`voltage_monitor_test`
====================================================
Voltage Monitor Test Module
* Author(s): Shawn Hymel
* Date: December 8, 2018
Implementation Notes
--------------------
Prints out the measured voltage on any onboard voltage/battery monitor pins.
Note that these pins sometimes have an onboard voltage divider to decrease
the voltage.
Requires multimeter
Run this script as its own main.py to individually run the test, or compile
with mpy-cross and call from separate test script.
"""
import board
import analogio
# Constants
VOLTAGE_MONITOR_PIN_NAMES = ['VOLTAGE_MONITOR', 'BATTERY']
ANALOG_REF = 3.3 # Reference analog voltage
ANALOGIN_BITS = 16 # ADC resolution (bits) for CircuitPython
# Test result strings
PASS = "PASS"
FAIL = "FAIL"
NA = "N/A"
def run_test(pins):
# Look for pins with battery monitoring names
monitor_pins = list(set(pins).intersection(set(VOLTAGE_MONITOR_PIN_NAMES)))
# Print out voltage found on these pins
if monitor_pins:
# Print out the monitor pins found
print("Voltage monitor pins found:", end=' ')
for p in monitor_pins:
print(p, end=' ')
print('\n')
# Print out the voltage found on each pin
for p in monitor_pins:
monitor = analogio.AnalogIn(getattr(board, p))
voltage = (monitor.value * ANALOG_REF) / (2**ANALOGIN_BITS)
print(p + ": {:.2f}".format(voltage) + " V")
monitor.deinit()
print()
# Ask the user to check these voltages
print("Use a multimeter to verify these voltages.")
print("Note that some battery monitor pins might have onboard " +
"voltage dividers.")
print("Do the values look reasonable? [y/n]")
if input() == 'y':
return PASS, monitor_pins
else:
return FAIL, monitor_pins
else:
print("No battery monitor pins found")
return NA, []
def _main():
# List out all the pins available to us
pins = [p for p in dir(board)]
print()
print("All pins found:", end=' ')
# Print pins
for p in pins:
print(p, end=' ')
print('\n')
# Run test
result = run_test(pins)
print()
print(result[0])
print("Pins tested: " + str(result[1]))
# Execute only if run as main.py or code.py
if __name__ == "__main__":
_main()