├── .coveragerc ├── .gitignore ├── .travis.yml ├── CONTRIBUTORS.txt ├── LICENSE ├── Makefile ├── README.rst ├── changelog.md ├── docs ├── API.rst ├── Makefile ├── NOTES.rst ├── README.rst ├── _static │ ├── nature.css │ ├── pingo-favicon32.ico │ ├── pingo-favicon32.png │ └── pingo-logo-160x252.png ├── conf.py ├── contributing.rst ├── index.rst └── make.bat ├── pingo ├── __init__.py ├── arduino │ ├── README.md │ ├── __init__.py │ ├── examples │ │ ├── analog_demo.py │ │ ├── blink.py │ │ ├── dojo.py │ │ ├── snake.py │ │ └── wild_snake.py │ ├── firmata.py │ ├── pyun.py │ ├── pyun_tests.py │ ├── test_util_firmata.py │ ├── tests │ │ ├── README.md │ │ ├── __init__.py │ │ ├── test_arduino.py │ │ ├── test_schematic_arduino.fz │ │ └── test_schematic_arduino_bb.jpg │ └── util_firmata.py ├── bbb │ ├── __init__.py │ ├── bbb.py │ └── tests │ │ ├── __init__.py │ │ └── test_bbb.py ├── board.py ├── detect │ ├── __init__.py │ ├── detect.py │ └── test_detect.py ├── examples │ ├── analog_bars.py │ ├── blink.py │ ├── blink_firmata.py │ ├── blink_firmata_auto.py │ ├── dimmer.py │ ├── dojo.py │ ├── dojo2.py │ ├── dojoxxl.py │ ├── double_blink.py │ ├── dual7seg.py │ ├── dual_analog_bars.py │ ├── dualboard.py │ ├── galileo_analog_bars.py │ ├── galileo_garoa_dojo_shield.py │ ├── garoa7seg.py │ ├── map7segment.py │ ├── parts │ │ ├── 7seg_demo.py │ │ ├── led_blink.py │ │ └── led_demo.py │ ├── pongo.py │ ├── probe_pins.py │ ├── pushbutton_led │ │ ├── button.png │ │ └── pushbutton_led.py │ ├── pwm.py │ ├── rgb_led.py │ ├── rpi_examples │ │ ├── display7.py │ │ ├── display7_anim.py │ │ ├── display7_probe.py │ │ ├── display7_snake.py │ │ ├── dojo_display7.py │ │ └── pin_map.py │ └── switch.py ├── ghost │ ├── __init__.py │ ├── examples │ │ ├── drill.py │ │ ├── drill_schematic.jpg │ │ └── drill_sensors.py │ ├── ghost.py │ └── tests │ │ ├── __init__.py │ │ ├── digital.rst │ │ └── test_ghost.py ├── intel │ ├── __init__.py │ ├── intel.py │ └── tests │ │ ├── __init__.py │ │ ├── test_edison.py │ │ └── test_galileo.py ├── iot │ ├── client.py │ └── server.py ├── parts │ ├── __init__.py │ ├── button.py │ ├── led.py │ ├── serial │ │ ├── __init__.py │ │ └── lcd.py │ ├── servo.py │ ├── spi │ │ ├── __init__.py │ │ └── mcp3008.py │ └── test │ │ ├── __init__.py │ │ └── test_switch.py ├── pcduino │ ├── README.md │ ├── __init__.py │ ├── pcduino.py │ └── tests │ │ ├── __init__.py │ │ ├── digital.rst │ │ └── test_pcduino.py ├── pinGUIm │ ├── __init__.py │ └── pinGUIm.py ├── rpi │ ├── __init__.py │ ├── grove.py │ ├── rpi.py │ └── tests │ │ ├── __init__.py │ │ ├── rpi.rst │ │ └── test_rpi.py ├── test │ ├── __init__.py │ ├── level0 │ │ ├── __init__.py │ │ └── cases.py │ ├── level1 │ │ ├── __init__.py │ │ └── cases.py │ └── level2 │ │ ├── __init__.py │ │ └── cases.py ├── test_utils.py ├── udoo │ ├── __init__.py │ ├── examples │ │ └── blink.py │ ├── tests │ │ ├── __init__.py │ │ ├── digital.rst │ │ └── test_udoo.py │ └── udoo.py └── util.py ├── requirements.txt ├── scripts ├── README.rst ├── flake8_githook.py ├── purge_whitespace.sh ├── vimrc └── yunserver.py ├── setup.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | *tests* 4 | pingo/test/* 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | *.py[cod] 3 | htmlcov/ 4 | .env* 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Packages 10 | *.egg 11 | *.egg-info 12 | dist 13 | build 14 | eggs 15 | bin 16 | var 17 | sdist 18 | develop-eggs 19 | .installed.cfg 20 | lib 21 | lib64 22 | __pycache__ 23 | 24 | # Installer logs 25 | pip-log.txt 26 | 27 | # Unit test / coverage reports 28 | .coverage 29 | .tox 30 | nosetests.xml 31 | 32 | # Translations 33 | *.mo 34 | 35 | # Mr Developer 36 | .mr.developer.cfg 37 | .project 38 | .pydevproject 39 | 40 | # editors 41 | *~ 42 | *___jb_old___ 43 | .idea/ 44 | *.sw[po] 45 | *.geany 46 | *.sublime-* 47 | 48 | # Sphinx 49 | _build/ 50 | 51 | # OSX 52 | .DS_Store 53 | 54 | # Pingo generated files 55 | pin_states.json 56 | test_output 57 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 2.7 3 | install: pip install -qq flake8 pytest pytest-cov tox 4 | script: 5 | - py.test pingo -vrsx --cov pingo 6 | after_success: 7 | - flake8 --version 8 | - flake8 --ignore=E501 pingo 9 | - pip install -qq coveralls 10 | - coveralls 11 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | ------------------- 2 | Name (Github user) 3 | ------------------- 4 | 5 | Alexandre Souza (alexandre) 6 | Danilo J. S. Bellini (danilobellini) 7 | Estevão U. P. Vieira 8 | Kang Yang (linksprite) 9 | Kemel Zaidan (kemelzaidan) 10 | Leonardo Rochael (leorochael) 11 | Lucas S. Simões 12 | Luciano Ramalho (ramalho) 13 | Lucas Vido (Vido) 14 | Łukasz Nowak 15 | Paulo R. O. Castro (brk00) 16 | Thiago M. Sanches 17 | Valder Gallo (valdergallo) 18 | Humberto Rocha (humrochagf) 19 | Diego Guimaraes (diegoguimaraes) 20 | Héctor Velarde (hvelarde) 21 | Theo A. Monteiro (theoamonteiro) 22 | Davi Garcia (davivcgarcia) 23 | Ricardo Bánffy (rbanffy) 24 | Afonso Coutinho (afonso) 25 | Ricardo Martinelli (rimolive) 26 | Gabriel Almeida de Souza (gfth4x07) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Garoa Hacker Clube 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "dev install development packages" 3 | @echo "setup install pingo" 4 | @echo "test run default test suit" 5 | @echo "test-cov run default test suit with coverage" 6 | @echo "test-pep run default test suit with pep8" 7 | 8 | 9 | dev: 10 | pip install -r requirements.txt 11 | 12 | 13 | setup: 14 | python setup.py install 15 | 16 | 17 | test: 18 | py.test pingo 19 | 20 | 21 | test-cov: 22 | py.test pingo --cov pingo --cov-report html 23 | 24 | 25 | test-pep: 26 | py.test pingo --pep8 27 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Pingo means "pin, go!" 2 | ====================== 3 | 4 | .. image:: https://secure.travis-ci.org/pingo-io/pingo-py.png?branch=master 5 | :alt: Travis CI badge 6 | :target: http://travis-ci.org/pingo-io/pingo-py 7 | 8 | .. image:: https://coveralls.io/repos/pingo-io/pingo-py/badge.png?branch=master 9 | :alt: Coveralls badge 10 | :target: https://coveralls.io/r/pingo-io/pingo-py 11 | 12 | Pingo provides a uniform API to program devices like the Raspberry Pi, BeagleBone Black, pcDuino etc. just like the Python DBAPI provides an uniform API for database programming in Python. 13 | 14 | The API is object-oriented but easy to use: a board is an instance of a ``Board`` subclass. Every board has a dictionary called ``pins`` which lists all GPIO pins on the board. Each pin is an instance of a ``Pin`` subclass with attributes that you can inspect to learn about its capabilities. 15 | 16 | The name `Pingo`_ is a tribute to `Garoa Hacker Clube`_, where the project started (the words *pingo* and *garoa* are related in Portuguese). To our English-speaking friends we like to say that it means **"pin, go!"** -- the main purpose of this package. 17 | 18 | .. _Pingo: https://garoa.net.br/wiki/Pingo 19 | .. _Garoa Hacker Clube: https://garoa.net.br/wiki/Garoa_Hacker_Clube:About 20 | 21 | ----- 22 | Media 23 | ----- 24 | 25 | Watch it on Youtube: https://www.youtube.com/watch?v=pAOooxPL_tQ 26 | 27 | .. image:: https://img.youtube.com/vi/pAOooxPL_tQ/maxresdefault.jpg 28 | :alt: Intel IoT RoadShow São Paulo 2014 29 | :target: https://www.youtube.com/watch?v=pAOooxPL_tQ 30 | :align: center 31 | 32 | Intel IoT RoadShow São Paulo 2014 33 | 34 | .. _basic-usage: 35 | 36 | ----------- 37 | Basic usage 38 | ----------- 39 | 40 | To use ``pingo``, the first step is to instantiate a ``Board``. Each Pingo driver is a concrete board subclass, for example, ``pingo.rpi.RaspberryPi`` and ``pingo.arduino.ArduinoFirmata`` are two such classes. 41 | 42 | Pingo can automatically detect the board in most common cases. If the script is running on a supported board, ``pingo.detect.MyBoard()`` returns an suitable board instance. If Pingo is running on an unsupported machine (eg. a notebook), it will try to find a connected Arduino using the Firmata protocol via USB and -- if successful -- will return a ``pingo.arduino.ArduinoFirmata`` instance. 43 | 44 | Once you have a board instance, it's possible to access its pins through the ``board.pins`` dictionary: 45 | 46 | .. code-block:: python 47 | 48 | import pingo 49 | from time import sleep 50 | 51 | board = pingo.detect.get_board() 52 | led_pin = board.pins[13] 53 | led_pin.mode = pingo.OUT 54 | 55 | while True: 56 | led_pin.hi() 57 | sleep(1) 58 | led_pin.lo() 59 | sleep(1) 60 | 61 | .. _drivers-table: 62 | 63 | ------- 64 | Drivers 65 | ------- 66 | 67 | ``pingo.pcduini.PcDuino`` ``pingo.galileo.Galileo2`` are examples of drivers, and the respective ``PcDuino`` and ``Galileo2`` are subclasses of the ``pingo.board.Board`` abstract class that defines the common API for all boards. 68 | 69 | The following table lists the drivers currently planned or under development. 70 | 71 | ===================== ======== =================== ======== ================================================== 72 | Board Type Module/Package Status Notes 73 | ===================== ======== =================== ======== ================================================== 74 | Arduino Firmata remote ``arduino.firmata`` level 1 requires `firmata protocol`_ on any Arduino board 75 | Arduino Yún on-board ``arduino.yun`` level 2 requires `Bridge sketch`_ on the Arduino Yún 76 | BeagleBone Black on-board ``bbb`` level 1 requires `Adafruit_BBIO`_ on the BeagleBone Black 77 | Intel Galileo Gen 2 on-board ``intel.galileo`` level 2 requires Intel IoT Dev Kit `mraa`_ library 78 | Intel Edison on-board ``intel.edison`` level 1 requires Intel IoT Dev Kit `mraa`_ library 79 | LinkSprite pcDuino on-board ``pcduino`` level 1 80 | RaspberryPi on-board ``rpi`` level 0 requires `RPi.GPIO`_ on the Raspberry Pi 81 | SECO UDOO on-board ``udoo`` level 0 82 | ===================== ======== =================== ======== ================================================== 83 | 84 | .. _Firmata protocol: http://arduino.cc/en/reference/firmata 85 | .. _Bridge sketch: http://arduino.cc/en/Reference/YunBridgeLibrary 86 | .. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO 87 | .. _mraa: https://github.com/intel-iot-devkit/mraa 88 | 89 | We are also interested in supporting: Banana Pi, Cubietech Cubieboard, SolidRun HummingBoard, TI MSP430 (via `firmata protocol`_ ). 90 | 91 | In a addition, Pingo implements ``ghost``, a mock software-only board for testing the API. 92 | 93 | 94 | Types of drivers 95 | ---------------- 96 | 97 | on-board 98 | Pingo and user code run on the board itself, using the Python interpreter installed in it. 99 | 100 | remote 101 | Pingo and user code run on host computer connected to board, controlling the board remotely. Useful for boards that are unable to run Python, like the Arduino UNO. 102 | 103 | fake 104 | Pingo and user code run on host computer emulating a dummy board in software. Useful for testing base classes from ``board.py`` and for teaching and demonstration. 105 | 106 | .. _status-of-drivers: 107 | 108 | Status of drivers 109 | ----------------- 110 | 111 | level 0 112 | Digital I/O: get/set high/low status of digital pins (no PWM support). 113 | 114 | level 1 115 | Analog input: read values from analog pins. 116 | 117 | level 2 118 | PWM output: set variable value for digital pins with PWM capability. 119 | 120 | experiments 121 | Some Python experiments have been done with the board. See the ``experiments/`` directory for code that may be helpful to start a new driver for a board. 122 | 123 | none 124 | Nothing has been done. Great opportunity for you to contribute with experiments and/or start a new driver. 125 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## v0.1: 2 | ###### NO TAG `commit 3887596426a2b5e3b09c09288545281a36e0f2d2` 3 | ###### Github tag: No 4 | - ??? 5 | 6 | ## v0.1.1: 7 | ###### Release date: 2014-04-16 8 | ###### Revision: `commit a247dc787dd0fdc7ba8ca5c20f255f1ffbb451bc` 9 | ###### Github tag: No 10 | - ??? 11 | 12 | ### v0.1.5: 13 | ###### Release date: 2014-04-16 14 | ###### Revision: `commit 02e277f45ae8a8044118e85b48484cda5e0a59ca` 15 | ###### Github tag: No 16 | - ??? 17 | 18 | ### v0.1.6: 19 | ###### Release date: 2014-04-16 20 | ###### Revision: `commit eff5dd7af0945cc1265c6427ca1ac93bb919318a` 21 | ###### Github tag: No 22 | - ??? 23 | 24 | ### v0.1.7 25 | ###### Release date: 2014-04-16 26 | ###### Revision: `commit ffb9727e41f4ee813d59da54732494e33c22c318` 27 | ###### Github tag: No 28 | - improved error message for RPi.GPIO dependency when using pingo.rpi 29 | 30 | ### v0.1.8 31 | ###### Revision: ??? 32 | ###### Release date: 2014-04-16 33 | ###### Github tag: No 34 | - ??? 35 | 36 | ### v0.1.9 37 | ###### Release date: 2014-06-13 38 | ###### Revision: `commit 51dc60acf75625fd5af2b156b81199f83a40f6c6` 39 | ###### Github tag: https://github.com/pingo-io/pingo-py/releases/tag/v0.1.9 40 | - PcDuino (only digital pins support) 41 | - Arduino support via Firmata Protocol 42 | - Analog pins support 43 | - AutoDetection 44 | - pytest replaces unittest 45 | 46 | ### v0.2.0 47 | ###### Release date: 2014-08-13 48 | ###### Revision: `commit f6f3f8b591434c73a858310d9c06fb19787d762d` 49 | ###### Github tag: No 50 | - RaspberryPiBPlus() 51 | - pcDuino V3 52 | - StrKeyDict() 53 | - PyMata replaces pyfirmata 54 | -------------------------------------------------------------------------------- /docs/API.rst: -------------------------------------------------------------------------------- 1 | API 2 | ============ 3 | 4 | .. autoclass:: pingo.Board 5 | :members: 6 | :private-members: 7 | 8 | .. autoclass:: pingo.AnalogInputCapable 9 | :members: 10 | :private-members: 11 | 12 | .. autoclass:: pingo.Pin 13 | :members: 14 | :private-members: 15 | 16 | .. autoclass:: pingo.DigitalPin 17 | :members: 18 | :private-members: 19 | 20 | .. autoclass:: pingo.AnalogPin 21 | :members: __init__, value, ratio, percent 22 | :private-members: 23 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Pingo.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Pingo.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Pingo" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Pingo" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/NOTES.rst: -------------------------------------------------------------------------------- 1 | Configuring a Raspberry Pi for Pingo 2 | ====================================== 3 | 4 | sudo apt-get update 5 | 6 | 7 | http://pip.readthedocs.org/en/latest/installing.html 8 | 9 | curl -O https://bootstrap.pypa.io/get-pip.py 10 | 11 | sudo python get-pip.py 12 | -------------------------------------------------------------------------------- /docs/README.rst: -------------------------------------------------------------------------------- 1 | The rendered documentation is at `pingo.io`_. 2 | 3 | .. _pingo.io: http://pingo.io 4 | -------------------------------------------------------------------------------- /docs/_static/nature.css: -------------------------------------------------------------------------------- 1 | /* 2 | * nature.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- nature theme. 6 | * 7 | * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: Arial, sans-serif; 18 | font-size: 100%; 19 | background-color: #ddd; 20 | color: #555; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 0 0 0 230px; 32 | } 33 | 34 | hr { 35 | border: 1px solid #B1B4B6; 36 | } 37 | 38 | div.document { 39 | background-color: #eee; 40 | } 41 | 42 | div.body { 43 | background-color: #ffffff; 44 | color: #3E4349; 45 | padding: 0 30px 30px 30px; 46 | font-size: 0.9em; 47 | } 48 | 49 | div.footer { 50 | color: #555; 51 | width: 100%; 52 | padding: 13px 0; 53 | text-align: center; 54 | font-size: 75%; 55 | } 56 | 57 | div.footer a { 58 | color: #444; 59 | text-decoration: underline; 60 | } 61 | 62 | div.related { 63 | background-color: #024; 64 | line-height: 32px; 65 | color: #fff; 66 | text-shadow: 0px 1px 0 #444; 67 | font-size: 0.9em; 68 | } 69 | 70 | div.related a { 71 | color: #E2F3CC; 72 | } 73 | 74 | div.sphinxsidebar { 75 | font-size: 0.75em; 76 | line-height: 1.5em; 77 | } 78 | 79 | div.sphinxsidebarwrapper{ 80 | padding: 20px 0; 81 | } 82 | 83 | div.sphinxsidebar h3, 84 | div.sphinxsidebar h4 { 85 | font-family: Arial, sans-serif; 86 | color: #222; 87 | font-size: 1.2em; 88 | font-weight: normal; 89 | margin: 0; 90 | padding: 5px 10px; 91 | background-color: #ddd; 92 | text-shadow: 1px 1px 0 white 93 | } 94 | 95 | div.sphinxsidebar h4{ 96 | font-size: 1.1em; 97 | } 98 | 99 | div.sphinxsidebar h3 a { 100 | color: #444; 101 | } 102 | 103 | 104 | div.sphinxsidebar p { 105 | color: #888; 106 | padding: 5px 20px; 107 | } 108 | 109 | div.sphinxsidebar p.topless { 110 | } 111 | 112 | div.sphinxsidebar ul { 113 | margin: 10px 20px; 114 | padding: 0; 115 | color: #000; 116 | } 117 | 118 | div.sphinxsidebar a { 119 | color: #444; 120 | } 121 | 122 | div.sphinxsidebar input { 123 | border: 1px solid #ccc; 124 | font-family: sans-serif; 125 | font-size: 1em; 126 | } 127 | 128 | div.sphinxsidebar input[type=text]{ 129 | margin-left: 20px; 130 | } 131 | 132 | /* -- body styles ----------------------------------------------------------- */ 133 | 134 | a { 135 | color: #005B81; 136 | text-decoration: none; 137 | } 138 | 139 | a:hover { 140 | color: #E32E00; 141 | text-decoration: underline; 142 | } 143 | 144 | div.body h1, 145 | div.body h2, 146 | div.body h3, 147 | div.body h4, 148 | div.body h5, 149 | div.body h6 { 150 | font-family: Arial, sans-serif; 151 | background-color: #BED4EB; 152 | font-weight: normal; 153 | color: #212224; 154 | margin: 30px 0px 10px 0px; 155 | padding: 5px 0 5px 10px; 156 | text-shadow: 0px 1px 0 white 157 | } 158 | 159 | div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } 160 | div.body h2 { font-size: 150%; background-color: #C8D5E3; } 161 | div.body h3 { font-size: 120%; background-color: #D8DEE3; } 162 | div.body h4 { font-size: 110%; background-color: #D8DEE3; } 163 | div.body h5 { font-size: 100%; background-color: #D8DEE3; } 164 | div.body h6 { font-size: 100%; background-color: #D8DEE3; } 165 | 166 | a.headerlink { 167 | color: #c60f0f; 168 | font-size: 0.8em; 169 | padding: 0 4px 0 4px; 170 | text-decoration: none; 171 | } 172 | 173 | a.headerlink:hover { 174 | background-color: #c60f0f; 175 | color: white; 176 | } 177 | 178 | div.body p, div.body dd, div.body li { 179 | line-height: 1.5em; 180 | } 181 | 182 | div.admonition p.admonition-title + p { 183 | display: inline; 184 | } 185 | 186 | div.highlight{ 187 | background-color: white; 188 | } 189 | 190 | div.note { 191 | background-color: #eee; 192 | border: 1px solid #ccc; 193 | } 194 | 195 | div.seealso { 196 | background-color: #ffc; 197 | border: 1px solid #ff6; 198 | } 199 | 200 | div.topic { 201 | background-color: #eee; 202 | } 203 | 204 | div.warning { 205 | background-color: #ffe4e4; 206 | border: 1px solid #f66; 207 | } 208 | 209 | p.admonition-title { 210 | display: inline; 211 | } 212 | 213 | p.admonition-title:after { 214 | content: ":"; 215 | } 216 | 217 | pre { 218 | padding: 10px; 219 | background-color: White; 220 | color: #222; 221 | line-height: 1.2em; 222 | border: 1px solid #C6C9CB; 223 | font-size: 1.1em; 224 | margin: 1.5em 0 1.5em 0; 225 | -webkit-box-shadow: 1px 1px 1px #d8d8d8; 226 | -moz-box-shadow: 1px 1px 1px #d8d8d8; 227 | } 228 | 229 | tt { 230 | background-color: #ecf0f3; 231 | color: #222; 232 | /* padding: 1px 2px; */ 233 | font-size: 1.1em; 234 | font-family: monospace; 235 | } 236 | 237 | .viewcode-back { 238 | font-family: Arial, sans-serif; 239 | } 240 | 241 | div.viewcode-block:target { 242 | background-color: #f4debf; 243 | border-top: 1px solid #ac9; 244 | border-bottom: 1px solid #ac9; 245 | } -------------------------------------------------------------------------------- /docs/_static/pingo-favicon32.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/docs/_static/pingo-favicon32.ico -------------------------------------------------------------------------------- /docs/_static/pingo-favicon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/docs/_static/pingo-favicon32.png -------------------------------------------------------------------------------- /docs/_static/pingo-logo-160x252.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/docs/_static/pingo-logo-160x252.png -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Pingo documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Apr 18 02:00:54 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | sys.path.insert(0, os.path.abspath('..')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.todo', 34 | 'sphinx.ext.viewcode', 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix of source filenames. 41 | source_suffix = '.rst' 42 | 43 | # The encoding of source files. 44 | #source_encoding = 'utf-8-sig' 45 | 46 | # The master toctree document. 47 | master_doc = 'index' 48 | 49 | # General information about the project. 50 | project = u'Pingo' 51 | copyright = u'2014, Pingo Team @ Garoa Hacker Clube' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.1' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.1.x' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | #language = None 65 | 66 | # There are two options for replacing |today|: either, you set today to some 67 | # non-false value, then it is used: 68 | #today = '' 69 | # Else, today_fmt is used as the format for a strftime call. 70 | #today_fmt = '%B %d, %Y' 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | exclude_patterns = ['_build'] 75 | 76 | # The reST default role (used for this markup: `text`) to use for all 77 | # documents. 78 | #default_role = None 79 | 80 | # If true, '()' will be appended to :func: etc. cross-reference text. 81 | #add_function_parentheses = True 82 | 83 | # If true, the current module name will be prepended to all description 84 | # unit titles (such as .. function::). 85 | #add_module_names = True 86 | 87 | # If true, sectionauthor and moduleauthor directives will be shown in the 88 | # output. They are ignored by default. 89 | #show_authors = False 90 | 91 | # The name of the Pygments (syntax highlighting) style to use. 92 | pygments_style = 'sphinx' 93 | 94 | # A list of ignored prefixes for module index sorting. 95 | #modindex_common_prefix = [] 96 | 97 | # If true, keep warnings as "system message" paragraphs in the built documents. 98 | #keep_warnings = False 99 | 100 | 101 | # -- Options for HTML output ---------------------------------------------- 102 | 103 | # The theme to use for HTML and HTML Help pages. See the documentation for 104 | # a list of builtin themes. 105 | html_theme = 'nature' 106 | 107 | # Theme options are theme-specific and customize the look and feel of a theme 108 | # further. For a list of options available for each theme, see the 109 | # documentation. 110 | # html_theme_options = {'headerbg': '#0047ff'} 111 | 112 | # Add any paths that contain custom themes here, relative to this directory. 113 | #html_theme_path = [] 114 | 115 | # The name for this set of Sphinx documents. If None, it defaults to 116 | # " v documentation". 117 | #html_title = None 118 | 119 | # A shorter title for the navigation bar. Default is the same as html_title. 120 | html_short_title = 'Pingo' 121 | 122 | # The name of an image file (relative to this directory) to place at the top 123 | # of the sidebar. 124 | html_logo = '_static/pingo-logo-160x252.png' 125 | 126 | # The name of an image file (within the static path) to use as favicon of the 127 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 128 | # pixels large. 129 | html_favicon = '_static/pingo-favicon32.ico' 130 | 131 | # Add any paths that contain custom static files (such as style sheets) here, 132 | # relative to this directory. They are copied after the builtin static files, 133 | # so a file named "default.css" will overwrite the builtin "default.css". 134 | html_static_path = ['_static'] 135 | 136 | # Add any extra paths that contain custom files (such as robots.txt or 137 | # .htaccess) here, relative to this directory. These files are copied 138 | # directly to the root of the documentation. 139 | #html_extra_path = [] 140 | 141 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 142 | # using the given strftime format. 143 | #html_last_updated_fmt = '%b %d, %Y' 144 | 145 | # If true, SmartyPants will be used to convert quotes and dashes to 146 | # typographically correct entities. 147 | #html_use_smartypants = True 148 | 149 | # Custom sidebar templates, maps document names to template names. 150 | #html_sidebars = {} 151 | 152 | # Additional templates that should be rendered to pages, maps page names to 153 | # template names. 154 | #html_additional_pages = {} 155 | 156 | # If false, no module index is generated. 157 | #html_domain_indices = True 158 | 159 | # If false, no index is generated. 160 | #html_use_index = True 161 | 162 | # If true, the index is split into individual pages for each letter. 163 | #html_split_index = False 164 | 165 | # If true, links to the reST sources are added to the pages. 166 | #html_show_sourcelink = True 167 | 168 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 169 | #html_show_sphinx = True 170 | 171 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 172 | #html_show_copyright = True 173 | 174 | # If true, an OpenSearch description file will be output, and all pages will 175 | # contain a tag referring to it. The value of this option must be the 176 | # base URL from which the finished HTML is served. 177 | #html_use_opensearch = '' 178 | 179 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 180 | #html_file_suffix = None 181 | 182 | # Output file base name for HTML help builder. 183 | htmlhelp_basename = 'Pingodoc' 184 | 185 | 186 | # -- Options for LaTeX output --------------------------------------------- 187 | 188 | latex_elements = { 189 | # The paper size ('letterpaper' or 'a4paper'). 190 | #'papersize': 'letterpaper', 191 | 192 | # The font size ('10pt', '11pt' or '12pt'). 193 | #'pointsize': '10pt', 194 | 195 | # Additional stuff for the LaTeX preamble. 196 | #'preamble': '', 197 | } 198 | 199 | # Grouping the document tree into LaTeX files. List of tuples 200 | # (source start file, target name, title, 201 | # author, documentclass [howto, manual, or own class]). 202 | latex_documents = [ 203 | ('index', 'Pingo.tex', u'Pingo Documentation', 204 | u'Pingo Team @ Garoa Hacker Clube', 'manual'), 205 | ] 206 | 207 | # The name of an image file (relative to this directory) to place at the top of 208 | # the title page. 209 | #latex_logo = None 210 | 211 | # For "manual" documents, if this is true, then toplevel headings are parts, 212 | # not chapters. 213 | #latex_use_parts = False 214 | 215 | # If true, show page references after internal links. 216 | #latex_show_pagerefs = False 217 | 218 | # If true, show URL addresses after external links. 219 | #latex_show_urls = False 220 | 221 | # Documents to append as an appendix to all manuals. 222 | #latex_appendices = [] 223 | 224 | # If false, no module index is generated. 225 | #latex_domain_indices = True 226 | 227 | 228 | # -- Options for manual page output --------------------------------------- 229 | 230 | # One entry per manual page. List of tuples 231 | # (source start file, name, description, authors, manual section). 232 | man_pages = [ 233 | ('index', 'pingo', u'Pingo Documentation', 234 | [u'Pingo Team @ Garoa Hacker Clube'], 1) 235 | ] 236 | 237 | # If true, show URL addresses after external links. 238 | #man_show_urls = False 239 | 240 | 241 | # -- Options for Texinfo output ------------------------------------------- 242 | 243 | # Grouping the document tree into Texinfo files. List of tuples 244 | # (source start file, target name, title, author, 245 | # dir menu entry, description, category) 246 | texinfo_documents = [ 247 | ('index', 'Pingo', u'Pingo Documentation', 248 | u'Pingo Team @ Garoa Hacker Clube', 'Pingo', 'One line description of project.', 249 | 'Miscellaneous'), 250 | ] 251 | 252 | # Documents to append as an appendix to all manuals. 253 | #texinfo_appendices = [] 254 | 255 | # If false, no module index is generated. 256 | #texinfo_domain_indices = True 257 | 258 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 259 | #texinfo_show_urls = 'footnote' 260 | 261 | # If true, do not generate a @detailmenu in the "Top" node's menu. 262 | #texinfo_no_detailmenu = False 263 | 264 | # -- Autodoc 265 | 266 | autodoc_member_order = 'bysource' 267 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | There are several ways of contributing to the Pingo project. 5 | 6 | ----------- 7 | Use Pingo 8 | ----------- 9 | 10 | If you use Pingo and spread the word about it, that is already very helpful! 11 | 12 | In the :ref:`Drivers Table `, check the *Status* column: drivers with *level 0*, *level 1* and *level 2* are ready to use, with the capabilities described in :ref:`status-of-drivers`. 13 | 14 | If you find bugs or have suggestions for improvements, please report them in the Git Hub issue tracker at: 15 | 16 | https://github.com/pingo-io/pingo-py/issues 17 | 18 | ------------- 19 | Get in touch 20 | ------------- 21 | 22 | If have trouble using Pingo, or would like to make suggestions or share your discoveries, our main forum is the *pingo-io* users and developers group at: 23 | 24 | https://groups.google.com/forum/#!forum/pingo-io 25 | 26 | If you are in São Paulo, mini-sprints for working on Pingo happen often at `Garoa Hacker Clube`_ on Wednesday evenings starting at 19:30. 27 | 28 | .. _Garoa Hacker Clube: https://garoa.net.br/wiki/Pingo 29 | 30 | 31 | ---------------- 32 | Write examples 33 | ---------------- 34 | 35 | If you'd like to contribute with code, the easiest way to get started is writing examples using Pingo to control any board that you may have available. Many of the `Arduino examples`_ may be adapted to use Pingo. 36 | 37 | The steps are: 38 | 39 | 0. Create an account on `Github`_ if you don't have one. 40 | 1. Fork the `pingo-io/pingo-py repository`_ on Github. 41 | 2. Clone your fork to work on it. 42 | 3. Code your examples. 43 | 4. Commit and push your changes to your fork. 44 | 5. Use the Github Web interface to submit a pull request. 45 | 6. If approved, your changes will be merged. 46 | 7. You are now a `contributor`_ to Pingo! Celebrate!! 47 | 48 | 49 | Clone the repository, create your examples in a board subdirectory, for example ``pingo/pcduino/examples/blink.py``. 50 | 51 | .. _Github: https://github.com 52 | .. _pingo-io/pingo-py repository: https://github.com/pingo-io/pingo-py 53 | .. _Arduino examples: http://arduino.cc/en/Tutorial/HomePage 54 | .. _contributor: https://github.com/pingo-io/pingo-py/blob/master/CONTRIBUTORS.txt 55 | 56 | 57 | ---------------------------------- 58 | Contribute to an existing driver 59 | ---------------------------------- 60 | 61 | If a driver is at :ref:`level 0`, implementing analog input will take it to :ref:`level 1`, an important step (*level 1* is the minimum functionality needed for the classic Coding Dojo with Arduino-or-MiniPC invented at Garoa). Then there is :ref:`level 2` and other levels yet to be defined. 62 | 63 | Within a driver level, we can always fix bugs or improve the performance. 64 | 65 | Before contributing to an existing driver, please coordinate with the other contributors. Send a message to the `mailing list`_ explaining what you intend to do and wait for some feedback: somebody may be working on the same driver, or have other ideas to help you. 66 | 67 | .. _mailing list: https://groups.google.com/forum/#!forum/pingo-io 68 | 69 | ---------------------------------- 70 | Create new drivers 71 | ---------------------------------- 72 | 73 | A growing number of boards capable of running Python is coming to market. Our goal is to have Pingo support all of them. 74 | 75 | To create a new driver the best way to start may be to copy the module and tests of a similar board. For example, UDOO and pcDuino both use ``sysfs`` as the main means of controlling the GPIO pins. At this writing, there is no pcDuino driver but there is a :ref:`level 0` UDOO driver, so a :ref:`level 0` pcDuino driver can be created and tested in a few hours (by the way, analog input is much easier on pcDuino than on UDOO, so a :ref:`level 1` driver for the pcDuino would not take much longer. See the `experiments/pcduino/dojo/`_ directory). 76 | 77 | .. _experiments/pcduino/dojo/: https://github.com/pingo-io/pingo-py/tree/master/experiments/pcduino/dojo 78 | 79 | The `experiments/`_ directory in the main repository has scripts in Python and other languages that show basic programming of the boards. Look in that directory for code that may help you get started, or create your own experiments there to understand how to interact with the board using Python. 80 | 81 | .. _experiments/: https://github.com/pingo-io/pingo-py/tree/master/experiments 82 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Pingo provides a uniform API to program devices like the Raspberry Pi, pcDuino, Intel Galileo etc. just like the Python DBAPI provides an uniform API for database programming in Python. 5 | 6 | The API is object-oriented but easy to use: each board is an instance of a ``Board`` subclass. Every board has a dictionary called ``pins`` which lists all GPIO pins on the board. Each pin is an instance of a ``Pin`` subclass with attributes that you can inspect to learn about its capabilities. 7 | 8 | A single script can easily control more than board at the same time. For example, a program running on the pcDuino can control the pcDuino itself and two Arduinos connected to the pcDuino via USB using the Firmata protocol. 9 | 10 | The name `Pingo`_ is a tribute to `Garoa Hacker Clube`_, where the project started (in Portuguese, "pingo" is drop and "garoa" is drizzle). To our English-speaking friends we like to say **Pingo means: "pin, go!"** -- this nicely sums up the purpose of this package. 11 | 12 | .. _Pingo: https://garoa.net.br/wiki/Pingo 13 | .. _Garoa Hacker Clube: https://garoa.net.br/wiki/Garoa_Hacker_Clube:About 14 | 15 | .. _basic-usage: 16 | 17 | .. include:: ../README.rst 18 | :start-after: _basic-usage: 19 | 20 | ------------ 21 | Installation 22 | ------------ 23 | 24 | There are two ways of installing Pingo: 25 | 26 | 1. from Python PyPI 27 | 2. from Github (recommended) 28 | 29 | Installing from PyPI 30 | -------------------------- 31 | 32 | To install Pingo from PyPI (Python Package Index), first, make sure you have ``pip`` installed in your machine. On Linux machines, this usually means you have the `python-pip` package installed. You can check if you have ``pip`` in your machine by typing the following text on a shell prompt:: 33 | 34 | $ pip --version 35 | pip 1.5.4 from /usr/lib/python2.7/dist-packages (python 2.7) 36 | 37 | If the output is similar from the one above, you can install Pingo by simple typing ``pip install pingo`` as root on you terminal. That's it! 38 | 39 | 40 | Installing from Github 41 | ---------------------------- 42 | 43 | Since Pingo is currently in alpha state and under heavy develpment, installing Pingo from Github may be a good idea. Besides that, it will be easy for you to contribute to the project, if you wish. See the Contributing section on the left menu. 44 | 45 | To install Pingo from Github, you must have Git installed. Presuming you already have that, just type:: 46 | 47 | $ git clone https://github.com/pingo-io/pingo-py.git 48 | 49 | After that, get into the pingo directory and setup Python to use your brand new directory as a library:: 50 | 51 | $ python setup.py develop 52 | 53 | Done! You are ready to program using Pingo! 54 | 55 | 56 | ------------------- 57 | More documentation 58 | ------------------- 59 | 60 | Contents: 61 | 62 | .. toctree:: 63 | :maxdepth: 2 64 | 65 | API 66 | contributing 67 | 68 | Indices and tables 69 | -------------------- 70 | 71 | * :ref:`genindex` 72 | * :ref:`modindex` 73 | * :ref:`search` 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Pingo.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Pingo.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /pingo/__init__.py: -------------------------------------------------------------------------------- 1 | # api 2 | from board import ANALOG # noqa 3 | from board import IN # noqa 4 | from board import OUT # noqa 5 | from board import PWM # noqa 6 | from board import HIGH # noqa 7 | from board import LOW # noqa 8 | from board import ModeNotSuported # noqa 9 | from board import WrongPinMode # noqa 10 | from board import PwmOutputCapable # noqa 11 | from board import AnalogInputCapable # noqa 12 | from board import Board # noqa 13 | from board import PwmPin # noqa 14 | from board import AnalogPin # noqa 15 | from board import DigitalPin # noqa 16 | from board import GroundPin # noqa 17 | from board import Pin # noqa 18 | from board import VccPin # noqa 19 | import parts # noqa 20 | 21 | # boards 22 | import rpi # noqa 23 | import ghost # noqa 24 | import intel # noqa 25 | import udoo # noqa 26 | import pcduino # noqa 27 | import arduino # noqa 28 | import bbb # noqa 29 | 30 | # resources 31 | import detect # noqa 32 | import test # noqa 33 | -------------------------------------------------------------------------------- /pingo/arduino/README.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Board Auto Discovery Failed!, Shutting Down 4 | If anyone is facing some error like this: 5 | ``` 6 | Please wait while Arduino is being detected. This can take up to 30 seconds ... 7 | Board Auto Discovery Failed!, Shutting Down 8 | ``` 9 | 10 | Please, upload the StandardFirmata.ino sketch. 11 | It can be found on Arduino >> Examples >> Firmata >> StandardFirmata 12 | * https://github.com/firmata/arduino/blob/master/examples/StandardFirmata/StandardFirmata.ino 13 | * https://www.arduino.cc/en/Reference/Firmata 14 | 15 | 16 | ## Arduino IDE errors 17 | If anyone is facing some error like this: 18 | ``` 19 | $ arduino 20 | java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path thrown while loading gnu.io.RXTXCommDriver 21 | Exception in thread "main" java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path 22 | at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1681) 23 | at java.lang.Runtime.loadLibrary0(Runtime.java:840) 24 | at java.lang.System.loadLibrary(System.java:1047) 25 | at gnu.io.CommPortIdentifier.(CommPortIdentifier.java:123) 26 | at processing.app.Editor.populateSerialMenu(Editor.java:962) 27 | at processing.app.Editor.buildToolsMenu(Editor.java:691) 28 | at processing.app.Editor.buildMenuBar(Editor.java:476) 29 | at processing.app.Editor.(Editor.java:205) 30 | at processing.app.Base.handleOpen(Base.java:704) 31 | at processing.app.Base.handleOpen(Base.java:669) 32 | at processing.app.Base.handleNew(Base.java:565) 33 | at processing.app.Base.(Base.java:305) 34 | at processing.app.Base.main(Base.java:194) 35 | ``` 36 | The fix is this: 37 | ``` 38 | $ sudo ln -s /usr/lib/jni/librxtxSerial-2.2pre1.so /usr/lib/jni/librxtxSerial.so 39 | ``` 40 | PS: I'm running here a CrunchBang 11. But Debian/Ubuntu/Mint user may have this problem too. 41 | -------------------------------------------------------------------------------- /pingo/arduino/__init__.py: -------------------------------------------------------------------------------- 1 | from firmata import ArduinoFirmata # noqa 2 | from firmata import get_arduino # noqa 3 | from pyun import YunBridge # noqa 4 | from pyun import ArduinoYun # noqa 5 | -------------------------------------------------------------------------------- /pingo/arduino/examples/analog_demo.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | ard = pingo.arduino.get_arduino() 6 | 7 | print('Found: %r' % ard) 8 | 9 | pin = ard.pins['A0'] 10 | 11 | while True: 12 | sleep(.02) 13 | print '%4d' % pin.value, int(70 * pin.ratio()) * '*' 14 | -------------------------------------------------------------------------------- /pingo/arduino/examples/blink.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | ard = pingo.arduino.get_arduino() 6 | 7 | led_pin = ard.pins[10] 8 | led_pin.mode = pingo.OUT 9 | 10 | while True: 11 | led_pin.hi() 12 | print(led_pin.state) 13 | sleep(1) 14 | led_pin.low() 15 | print(led_pin.state) 16 | sleep(1) 17 | -------------------------------------------------------------------------------- /pingo/arduino/examples/dojo.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | ard = pingo.arduino.get_arduino() 6 | 7 | display_map = {'a': 12, 'b': 13, 'c': 7, 'd': 8, 'e': 9, 'f': 11} # 'g': 10, 'dp': 6 8 | 9 | pins = [ard.pins[p] for p in sorted(display_map.values())] 10 | 11 | for pin in pins: 12 | pin.mode = pingo.OUT 13 | 14 | pot = ard.pins['A0'] 15 | 16 | while True: 17 | for pin in pins: 18 | pin.high() 19 | sleep(pot.ratio()) 20 | pin.low() 21 | -------------------------------------------------------------------------------- /pingo/arduino/examples/snake.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | ard = pingo.arduino.get_arduino() 6 | 7 | display_map = {'a': 12, 'b': 13, 'c': 7, 'd': 8, 'e': 9, 'f': 11} # 'g': 10, 'dp': 6} 8 | 9 | pins = [ard.pins[p] for p in sorted(display_map.values())] 10 | 11 | for pin in pins: 12 | pin.mode = pingo.OUT 13 | 14 | tail = pins[-1] 15 | 16 | pot = ard.pins['A0'] 17 | 18 | while True: 19 | for head in pins: 20 | head.high() 21 | sleep(pot.ratio()) 22 | tail.low() 23 | tail = head 24 | -------------------------------------------------------------------------------- /pingo/arduino/examples/wild_snake.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | 4 | Snake head position and direction is coded like pictured below, i.e. when 5 | the snake head is at the middle segment going right, the code is 6, going left 6 | in the same place the code is 13. 7 | 8 | >:0 9 | <:7 10 | ---- 11 | ^:5 | | v:1 12 | v:12 |>:6 | ^:8 13 | ---- 14 | ^:4 |<:13| v:2 15 | v:11 | | ^:9 16 | ---- 17 | <:3 18 | >:10 19 | 20 | To understand this diagram, read: 21 | 22 | > as a right arrow 23 | < as a left arrow 24 | v as a down arrow 25 | ^ as an up arrow 26 | """ 27 | 28 | from time import sleep 29 | from random import choice 30 | 31 | import pingo 32 | 33 | ard = pingo.arduino.get_arduino() 34 | 35 | # A B C D E F G 36 | display = [12, 13, 7, 8, 9, 11, 10] 37 | 38 | pins = [ard.pins[p] for p in display] 39 | 40 | # move choices are ordered with the following logic: 41 | # (1) when both choices are turns, right turn is first; 42 | # (2) when one choice is a turn and the other is straight, turn is first 43 | moves = { 44 | 0: [1], 45 | 1: [13, 2], 46 | 2: [3], 47 | 3: [4], 48 | 4: [6, 5], 49 | 5: [0], 50 | 6: [2, 8], 51 | 7: [12], 52 | 8: [7], 53 | 9: [13, 8], 54 | 10: [9], 55 | 11: [10], 56 | 12: [6, 11], 57 | 13: [5, 11] 58 | } 59 | 60 | for pin in pins: 61 | pin.mode = pingo.OUT 62 | 63 | head = 0 # code 0 -> top segment, going left 64 | tail = 5 65 | 66 | pot = ard.pins['A0'] 67 | 68 | while True: 69 | pins[head % 7].high() 70 | sleep(pot.ratio()) 71 | pins[tail % 7].low() 72 | tail = head 73 | next = moves[head] 74 | head = choice(next) 75 | -------------------------------------------------------------------------------- /pingo/arduino/firmata.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Firmata protocol client for Pingo 4 | Works on Arduino 5 | """ 6 | 7 | import time 8 | import platform 9 | 10 | import pingo 11 | from pingo.board import Board, DigitalPin, AnalogPin, PwmPin 12 | from pingo.board import AnalogInputCapable, PwmOutputCapable 13 | from pingo.detect import detect 14 | from util_firmata import pin_list_to_board_dict 15 | 16 | PyMata = None 17 | 18 | PIN_STATES = { 19 | False: 0, 20 | True: 1, 21 | 0: 0, 22 | 1: 1, 23 | pingo.LOW: 0, 24 | pingo.HIGH: 1, 25 | } 26 | 27 | # TODO: PyMata suports Input, Output, PWM, Servo, Encoder and Tone 28 | PIN_MODES = { 29 | pingo.IN: 0, 30 | pingo.OUT: 1, 31 | } 32 | 33 | VERBOSE = False 34 | 35 | 36 | def get_arduino(): 37 | serial_port = detect._find_arduino_dev(platform.system()) 38 | if not serial_port: 39 | raise LookupError('Serial port not found') 40 | return ArduinoFirmata(serial_port) 41 | 42 | 43 | class ArduinoFirmata(Board, AnalogInputCapable, PwmOutputCapable): 44 | 45 | def __init__(self, port=None): 46 | try: 47 | from PyMata.pymata import PyMata as PyMata # noqa 48 | except ImportError: 49 | msg = 'pingo.arduino.Arduino requires PyMata installed' 50 | raise ImportError(msg) 51 | 52 | super(ArduinoFirmata, self).__init__() 53 | self.port = port 54 | self.firmata_client = PyMata(self.port, verbose=VERBOSE) 55 | 56 | self.firmata_client.capability_query() 57 | time.sleep(10) # TODO: Find a small and safe value 58 | capability_query_results = self.firmata_client.get_capability_query_results() 59 | capability_dict = pin_list_to_board_dict(capability_query_results) 60 | 61 | self._add_pins( 62 | [DigitalPin(self, location) 63 | for location in capability_dict['digital']] + 64 | [PwmPin(self, location) 65 | for location in capability_dict['pwm']] + 66 | [AnalogPin(self, 'A%s' % location, resolution=10) 67 | for location in capability_dict['analog']] 68 | ) 69 | 70 | def cleanup(self): 71 | # self.firmata_client.close() has sys.exit(0) 72 | if hasattr(self, 'PyMata'): 73 | try: 74 | self.firmata_client.transport.close() 75 | except AttributeError: 76 | pass 77 | 78 | def __repr__(self): 79 | cls_name = self.__class__.__name__ 80 | return '<{cls_name} {self.port!r}>'.format(**locals()) 81 | 82 | def _set_digital_mode(self, pin, mode): 83 | self.firmata_client.set_pin_mode( 84 | pin.location, 85 | PIN_MODES[mode], 86 | self.firmata_client.DIGITAL 87 | ) 88 | 89 | def _set_analog_mode(self, pin, mode): 90 | pin_id = int(pin.location[1:]) 91 | self.firmata_client.set_pin_mode( 92 | pin_id, 93 | self.firmata_client.INPUT, 94 | self.firmata_client.ANALOG 95 | ) 96 | 97 | def _set_pwm_mode(self, pin, mode): 98 | pin_id = int(pin.location) 99 | self.firmata_client.set_pin_mode( 100 | pin_id, 101 | self.firmata_client.PWM, 102 | self.firmata_client.DIGITAL 103 | ) 104 | 105 | def _get_pin_state(self, pin): 106 | _state = self.firmata_client.digital_read(pin.location) 107 | if _state == self.firmata_client.HIGH: 108 | return pingo.HIGH 109 | return pingo.LOW 110 | 111 | def _set_pin_state(self, pin, state): 112 | self.firmata_client.digital_write( 113 | pin.location, 114 | PIN_STATES[state] 115 | ) 116 | 117 | def _get_pin_value(self, pin): 118 | pin_id = int(pin.location[1:]) 119 | return self.firmata_client.analog_read(pin_id) 120 | 121 | def _set_pwm_duty_cycle(self, pin, value): 122 | pin_id = int(pin.location) 123 | firmata_value = int(value * 255) 124 | return self.firmata_client.analog_write(pin_id, firmata_value) 125 | 126 | def _set_pwm_frequency(self, pin, value): 127 | raise NotImplementedError 128 | -------------------------------------------------------------------------------- /pingo/arduino/pyun.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import urllib 4 | import time 5 | 6 | import pingo 7 | 8 | # INPUT = 'input' 9 | # OUTPUT = 'output' 10 | # HIGH = 1 11 | # LOW = 0 12 | # PINS = range(0, 14) 13 | 14 | 15 | class YunBridge(object): 16 | 17 | """ 18 | Pyun: Python interface to Arduino Yún via HTTP to Bridge sketch 19 | 20 | WARNING: this requires the Bridge sketch running on the Arduino. 21 | 22 | About Bridge: 23 | http://arduino.cc/en/Tutorial/Bridge 24 | 25 | Bridge REST cheat-sheet: 26 | 27 | * "/arduino/digital/13" -> digitalRead(13) 28 | * "/arduino/digital/13/1" -> digitalWrite(13, HIGH) 29 | * "/arduino/analog/2/123" -> analogWrite(2, 123) 30 | * "/arduino/analog/2" -> analogRead(2) 31 | * "/arduino/mode/13/input" -> pinMode(13, INPUT) 32 | * "/arduino/mode/13/output" -> pinMode(13, OUTPUT) 33 | """ 34 | 35 | def __init__(self, host, verbose=False): 36 | self.host = host 37 | self.base_url = 'http://%s/arduino/' % host 38 | self.verbose = verbose 39 | # for pin in PINS: 40 | # self.digitalWrite(pin, 0) 41 | # self.pinMode(pin, INPUT) 42 | 43 | def makeURL(self, command, pin, *args): 44 | rest_args = '/'.join(str(x) for x in args) 45 | if rest_args: 46 | url = self.base_url + '%s/%d/%s' % (command, pin, rest_args) 47 | else: 48 | url = self.base_url + '%s/%d' % (command, pin) 49 | if self.verbose: 50 | print '[YunBridge] url: ', url 51 | return url 52 | 53 | def get(self, command, pin, *args): 54 | url = self.makeURL(command, pin, *args) 55 | res = urllib.urlopen(url).read() 56 | return res 57 | 58 | def pinMode(self, pin, mode): 59 | mode = mode.lower() 60 | if mode not in ['input', 'output']: 61 | raise TypeError('mode should be "input" or "output" ') 62 | res = self.get('mode', pin, mode) 63 | # Pin D13 configured as INPUT! 64 | return res.split()[-1][:-1].lower() 65 | 66 | def digitalRead(self, pin): 67 | res = self.get('digital', pin) 68 | # Pin D13 set to 1 69 | return int(res.split()[-1]) 70 | 71 | def digitalWrite(self, pin, value): 72 | res = self.get('digital', pin, value) 73 | # Pin D13 set to 0 74 | return int(res.split()[-1]) 75 | 76 | def analogRead(self, pin): 77 | res = self.get('analog', pin) 78 | # Pin A5 reads analog 185 79 | return int(res.split()[-1]) 80 | 81 | def analogWrite(self, pin, value): 82 | res = self.get('analog', pin, value) 83 | return int(res.split()[-1]) 84 | 85 | def delay(self, ms): 86 | seconds = float(ms) / 1000 87 | time.sleep(seconds) 88 | 89 | 90 | class ArduinoYun(pingo.Board, pingo.AnalogInputCapable, pingo.PwmOutputCapable): 91 | 92 | def __init__(self, host, verbose=False): 93 | 94 | super(ArduinoYun, self).__init__() 95 | 96 | self.yun = YunBridge(host, verbose) 97 | 98 | self.PIN_MODES = { 99 | pingo.IN: 'input', 100 | pingo.OUT: 'output', 101 | } 102 | 103 | self.PIN_STATES = { 104 | pingo.HIGH: 1, 105 | pingo.LOW: 0, 106 | } 107 | 108 | pwm_pin_numbers = [3, 5, 6, 9, 10, 11, 13] 109 | digital_pin_numbers = [0, 1, 2, 4, 7, 8, 12] 110 | 111 | self._add_pins( 112 | [pingo.PwmPin(self, location) 113 | for location in pwm_pin_numbers] + 114 | 115 | [pingo.DigitalPin(self, location) 116 | for location in digital_pin_numbers] + 117 | 118 | [pingo.AnalogPin(self, 'A' + location, 10) 119 | for location in '012345'] 120 | ) 121 | 122 | def _set_digital_mode(self, pin, mode): 123 | if mode == pingo.IN: 124 | self.yun.pinMode(pin.location, 'input') 125 | else: 126 | self.yun.pinMode(pin.location, 'output') 127 | 128 | def _set_analog_mode(self, pin, mode): 129 | self.yun.pinMode(int(pin.location[-1]), 'input') 130 | 131 | def _set_pwm_mode(self, pin, mode): 132 | self._set_digital_mode(pin, mode) 133 | 134 | def _set_pin_state(self, pin, state): 135 | self.yun.digitalWrite(pin.location, self.PIN_STATES[state]) 136 | 137 | def _get_pin_state(self, pin): 138 | value = self.yun.digitalRead(pin.location) 139 | return pingo.HIGH if value == 1 else pingo.LOW 140 | 141 | def _get_pin_value(self, pin): 142 | return self.yun.analogRead(int(pin.location[-1])) 143 | 144 | def _get_pwm_duty_cycle(self, pin): 145 | if hasattr(pin, '_duty_cycle'): 146 | return pin._duty_cycle 147 | else: 148 | return 0.0 149 | 150 | def _set_pwm_duty_cycle(self, pin, value): 151 | self.yun.analogWrite(pin.location, value) 152 | pin._duty_cycle = value 153 | -------------------------------------------------------------------------------- /pingo/arduino/pyun_tests.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | 5 | >>> from pyun import * 6 | >>> yun = YunBridge('192.168.2.9') 7 | >>> yun.pinMode(13, INPUT) 8 | 'input' 9 | >>> yun.digitalRead(13) 10 | 0 11 | >>> yun.digitalWrite(13, 1) 12 | 1 13 | >>> yun.digitalWrite(13, 0) 14 | 0 15 | >>> 0 <= yun.analogRead(5) < 1024 16 | True 17 | 18 | 19 | """ 20 | 21 | import doctest 22 | doctest.testmod(optionflags=doctest.REPORT_ONLY_FIRST_FAILURE) 23 | -------------------------------------------------------------------------------- /pingo/arduino/test_util_firmata.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from util_firmata import pin_list_to_board_dict 4 | 5 | 6 | class FirmataCapabilityDetect(unittest.TestCase): 7 | 8 | def test_capability_response(self): 9 | test_layout = { 10 | 'digital': (0, 1), 11 | 'analog': (0,), # Analog are numbered from zero 12 | 'pwm': (1,), 13 | 'i2c': (2,), 14 | 'disabled': (0,), 15 | } 16 | 17 | # Eg: (127) 18 | unavailible_pin = [ 19 | 0x7F, # END_SYSEX (Pin delimiter) 20 | ] 21 | 22 | # Eg: (0, 1, 1, 1, 3, 8, 4, 14, 127) 23 | digital_pin = [ 24 | 0x00, # INPUT 25 | 0x01, 26 | 0x01, # OUTPUT 27 | 0x01, 28 | 0x03, # PWM 29 | 0x08, 30 | 0x7F, # END_SYSEX (Pin delimiter) 31 | ] 32 | 33 | # Eg. (0, 1, 1, 1, 4, 14, 127) 34 | analog_pin = [ 35 | 0x00, # INPUT 36 | 0x01, 37 | 0x01, # OUTPUT 38 | 0x01, 39 | 0x02, # ANALOG 40 | 0x0A, 41 | 0x06, # I2C 42 | 0x01, 43 | 0x7F, # END_SYSEX (Pin delimiter) 44 | ] 45 | 46 | data_arduino = list( 47 | # [0x6C] # CAPABILITY_RESPONSE 48 | unavailible_pin 49 | + digital_pin 50 | + analog_pin 51 | ) 52 | 53 | pinmap = pin_list_to_board_dict(data_arduino) 54 | for key in test_layout.keys(): 55 | self.assertEqual(pinmap[key], test_layout[key]) 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | -------------------------------------------------------------------------------- /pingo/arduino/tests/README.md: -------------------------------------------------------------------------------- 1 | # Schematics 2 | !["Arduino Test Schematics"](https://raw.githubusercontent.com/Vido/pingo/master/pingo/arduino/tests/test_schematic_arduino_bb.jpg) 3 | 4 | In order to test Pingo over a Arduino this is the recomended wiring schematics 5 | 6 | -------------------------------------------------------------------------------- /pingo/arduino/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/arduino/tests/__init__.py -------------------------------------------------------------------------------- /pingo/arduino/tests/test_arduino.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import unittest 3 | 4 | import pingo 5 | from pingo.test import level0 6 | from pingo.test import level1 7 | from pingo.detect import has_module, check_board 8 | 9 | running_on_arduino = check_board(pingo.arduino.ArduinoFirmata) 10 | 11 | 12 | class ArduinoFirmataTest(unittest.TestCase): 13 | 14 | def setUp(self): 15 | device = pingo.detect.detect._find_arduino_dev(platform.system()) 16 | self.board = pingo.arduino.ArduinoFirmata(device) 17 | 18 | # Level0 Parameters 19 | # self.vdd_pin_number = 0 20 | self.digital_output_pin_number = 13 21 | self.digital_input_pin_number = 2 22 | self.total_pins = 14 23 | 24 | # Level1 Parameters 25 | self.analog_input_pin_number = 'A4' 26 | self.expected_analog_input = 1004 27 | self.expected_analog_ratio = 0.98 28 | 29 | def tearDown(self): 30 | self.board.cleanup() 31 | 32 | 33 | @unittest.skipIf(not running_on_arduino, 'Arduino not detected') 34 | @unittest.skipIf( 35 | not has_module('PyMata'), 'pingo.arduino.Arduino requires PyMata installed') 36 | class ArduinoBasics(ArduinoFirmataTest, level0.BoardBasics): 37 | 38 | @unittest.skip('ArduinoFirmata does not recognize VddPins') 39 | def test_list_pins(self): 40 | pass 41 | 42 | 43 | @unittest.skipIf(not running_on_arduino, 'Arduino not detected') 44 | @unittest.skipIf( 45 | not has_module('PyMata'), 'pingo.arduino.Arduino requires PyMata installed') 46 | class ArduinoDigitalExceptions(ArduinoFirmataTest, level0.BoardExceptions): 47 | pass 48 | 49 | 50 | @unittest.skipIf(not running_on_arduino, "Arduino not detected") 51 | @unittest.skipIf( 52 | not has_module('PyMata'), "pingo.arduino.Arduino requires PyMata installed") 53 | class ArduinoAnalogRead(ArduinoFirmataTest, level1.AnalogReadBasics): 54 | pass 55 | 56 | 57 | @unittest.skipIf(not running_on_arduino, 'Arduino not detected') 58 | @unittest.skipIf( 59 | not has_module('PyMata'), 'pingo.arduino.Arduino requires PyMata installed') 60 | class ArduinoAnalogExceptions(ArduinoFirmataTest, level1.AnalogExceptions): 61 | pass 62 | 63 | if __name__ == '__main__': 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /pingo/arduino/tests/test_schematic_arduino.fz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/arduino/tests/test_schematic_arduino.fz -------------------------------------------------------------------------------- /pingo/arduino/tests/test_schematic_arduino_bb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/arduino/tests/test_schematic_arduino_bb.jpg -------------------------------------------------------------------------------- /pingo/arduino/util_firmata.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copied from: 3 | https://github.com/tino/pyFirmata/blob/master/pyfirmata/util.py 4 | """ 5 | 6 | 7 | def pin_list_to_board_dict(capability_query_response): 8 | """ 9 | Capability Response codes: 10 | INPUT: 0, 1 11 | OUTPUT: 1, 1 12 | ANALOG: 2, 10 13 | PWM: 3, 8 14 | SERV0: 4, 14 15 | I2C: 6, 1 16 | """ 17 | 18 | board_dict = { 19 | 'digital': [], 20 | 'analog': [], 21 | 'pwm': [], 22 | 'servo': [], 23 | 'i2c': [], 24 | 'disabled': [], 25 | } 26 | 27 | # i split pins of list: 28 | pin_list = [[]] 29 | for b in capability_query_response: 30 | if b == 127: 31 | pin_list.append([]) 32 | else: 33 | pin_list[-1].append(b) 34 | 35 | pin_list.pop() # removes the empty [] on end 36 | 37 | # Finds the capability of each pin 38 | for i, pin in enumerate(pin_list): 39 | if not pin: 40 | board_dict['disabled'] += [i] 41 | board_dict['digital'] += [i] 42 | continue 43 | 44 | for j, _ in enumerate(pin): 45 | # Iterate over evens 46 | if j % 2 == 0: 47 | # This is safe. try: range(10)[5:50] 48 | if pin[j:j + 4] == [0, 1, 1, 1]: 49 | board_dict['digital'] += [i] 50 | 51 | if pin[j:j + 2] == [2, 10]: 52 | board_dict['analog'] += [i] 53 | 54 | if pin[j:j + 2] == [3, 8]: 55 | board_dict['pwm'] += [i] 56 | 57 | if pin[j:j + 2] == [4, 14]: 58 | board_dict['servo'] += [i] 59 | 60 | if pin[j:j + 2] == [6, 1]: 61 | board_dict['i2c'] += [i] 62 | 63 | # We have to deal with analog pins: 64 | # - (14, 15, 16, 17, 18, 19) 65 | # + (0, 1, 2, 3, 4, 5) 66 | diff = set(board_dict['digital']) - set(board_dict['analog']) 67 | board_dict['analog'] = [n for n, _ in enumerate(board_dict['analog'])] 68 | 69 | # Digital pin problems: 70 | # - (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) 71 | # + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) 72 | 73 | board_dict['digital'] = [n for n, _ in enumerate(diff)] 74 | # Based on lib Arduino 0017 75 | board_dict['servo'] = board_dict['digital'] 76 | 77 | # Turn lists into tuples 78 | # Using dict for Python 2.6 compatibility 79 | board_dict = dict([ 80 | (key, tuple(value)) 81 | for key, value 82 | in board_dict.items() 83 | ]) 84 | 85 | return board_dict 86 | -------------------------------------------------------------------------------- /pingo/bbb/__init__.py: -------------------------------------------------------------------------------- 1 | from bbb import BeagleBoneBlack # noqa 2 | -------------------------------------------------------------------------------- /pingo/bbb/bbb.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | GPIO = None 4 | 5 | 6 | class BeagleBoneBlack(pingo.Board): 7 | # TODO: use labels as in the docs (https://github.com/adafruit/adafruit-beaglebone-io-python) 8 | 9 | PINS = { 10 | 'P8_3': 38, 11 | 'P8_4': 39, 12 | 'P8_5': 34, 13 | 'P8_6': 35, 14 | 'P8_7': 66, 15 | 'P8_8': 67, 16 | 'P8_9': 69, 17 | 'P8_10': 68, 18 | 'P8_11': 45, 19 | 'P8_12': 44, 20 | 'P8_13': 23, 21 | 'P8_14': 26, 22 | 'P8_15': 47, 23 | 'P8_16': 46, 24 | 'P8_17': 27, 25 | 'P8_18': 65, 26 | 'P8_19': 22, 27 | 'P8_20': 63, 28 | 'P8_21': 62, 29 | 'P8_22': 37, 30 | 'P8_23': 36, 31 | 'P8_24': 33, 32 | 'P8_25': 32, 33 | 'P8_26': 61, 34 | 'P8_27': 86, 35 | 'P8_28': 88, 36 | 'P8_29': 87, 37 | 'P8_30': 89, 38 | 'P8_31': 10, 39 | 'P8_32': 11, 40 | 'P8_33': 9, 41 | 'P8_34': 81, 42 | 'P8_35': 8, 43 | 'P8_36': 80, 44 | 'P8_37': 78, 45 | 'P8_38': 79, 46 | 'P8_39': 76, 47 | 'P8_40': 77, 48 | 'P8_41': 74, 49 | 'P8_42': 75, 50 | 'P8_43': 72, 51 | 'P8_44': 73, 52 | 'P8_45': 70, 53 | 'P8_46': 71, 54 | 'P9_11': 30, 55 | 'P9_12': 60, 56 | 'P9_13': 31, 57 | 'P9_14': 40, 58 | 'P9_15': 48, 59 | 'P9_16': 51, 60 | 'P9_17': 4, 61 | 'P9_18': 5, 62 | 'P9_21': 3, 63 | 'P9_22': 2, 64 | 'P9_23': 49, 65 | 'P9_24': 15, 66 | 'P9_25': 117, 67 | 'P9_26': 14, 68 | 'P9_27': 125, 69 | 'P9_28': 123, 70 | 'P9_29': 121, 71 | 'P9_30': 122, 72 | 'P9_31': 120, 73 | 'P9_41': 20, 74 | 'P9_42': 7, 75 | } 76 | 77 | VCC_PINS = { 78 | 'P9_3': 3.3, 79 | 'P9_4': 3.3, 80 | 'P9_5': 5, 81 | 'P9_6': 5, 82 | 'P9_7': 5, 83 | 'P9_8': 5, 84 | 'P9_32': 5 # VDD_ADC 85 | } 86 | 87 | GND_PINS = ['P8_1', 'P8_2', 'P9_1', 'P9_2', 'P9_34', 'P9_43', 'P9_44', 88 | 'P9_45', 'P9_46'] 89 | 90 | ANALOG_PINS = { 91 | 'P9_33': 'AIN4', 92 | 'P9_35': 'AIN6', 93 | 'P9_36': 'AIN5', 94 | 'P9_37': 'AIN2', 95 | 'P9_38': 'AIN3', 96 | 'P9_39': 'AIN0', 97 | 'P9_40': 'AIN1', 98 | } 99 | 100 | # TODO: PWR_BUT, SYS_RESET, I2C2_SCL, I2C2_SDA 101 | 102 | _import_error_msg = 'pingo.bbb.BeagleBoneBlack requires Adafruit_BBIO installed' 103 | 104 | def __init__(self): 105 | global GPIO 106 | try: 107 | import Adafruit_BBIO.GPIO as GPIO 108 | except ImportError: 109 | raise ImportError(self._import_error_msg) 110 | 111 | super(BeagleBoneBlack, self).__init__() 112 | 113 | self.PIN_MODES = { 114 | pingo.IN: GPIO.IN, 115 | pingo.OUT: GPIO.OUT, 116 | } 117 | 118 | self.PIN_STATES = { 119 | pingo.HIGH: GPIO.HIGH, 120 | pingo.LOW: GPIO.LOW, 121 | } 122 | 123 | gpio_pins = [pingo.DigitalPin(self, location, gpio_id) 124 | for location, gpio_id in self.PINS.items()] 125 | ground_pins = [pingo.GroundPin(self, location) 126 | for location in self.GND_PINS] 127 | vcc_pins = [pingo.VccPin(self, location, voltage) 128 | for location, voltage in self.VCC_PINS.items()] 129 | 130 | self._add_pins(gpio_pins + ground_pins + vcc_pins) 131 | 132 | def cleanup(self): 133 | pass 134 | 135 | def _set_digital_mode(self, pin, mode): 136 | GPIO.setup(pin.location, self.PIN_MODES[mode]) 137 | 138 | def _set_pin_state(self, pin, state): 139 | GPIO.output(pin.location, self.PIN_STATES[state]) 140 | 141 | def _get_pin_state(self, pin): 142 | return pingo.HIGH if GPIO.input(pin.location) else pingo.LOW 143 | -------------------------------------------------------------------------------- /pingo/bbb/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/bbb/tests/__init__.py -------------------------------------------------------------------------------- /pingo/bbb/tests/test_bbb.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | from pingo.test import level0 5 | from pingo.detect import check_board 6 | 7 | running_on_beaglebone = check_board(pingo.bbb.BeagleBoneBlack) 8 | 9 | 10 | class BeagleBoneBlackTest(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.board = pingo.bbb.BeagleBoneBlack() 14 | self.vdd_pin_number = 0 15 | self.digital_output_pin_number = 0 16 | self.digital_input_pin_number = 0 17 | self.total_pins = 0 18 | 19 | def tearDown(self): 20 | self.board.cleanup() 21 | 22 | 23 | @unittest.skipIf( 24 | not running_on_beaglebone, "BeagleBoneBlack not detected") 25 | class BeagleBoneBlackBasics(BeagleBoneBlackTest, level0.BoardBasics): 26 | pass 27 | 28 | 29 | @unittest.skipIf( 30 | not running_on_beaglebone, "BeagleBoneBlack not detected") 31 | class BeagleBoneBlackExceptions(BeagleBoneBlackTest, level0.BoardExceptions): 32 | pass 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /pingo/board.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import atexit 4 | from operator import attrgetter 5 | from abc import ABCMeta, abstractmethod 6 | 7 | from .util import StrKeyDict 8 | 9 | HIGH = 'HIGH' 10 | LOW = 'LOW' 11 | 12 | # TODO: 4 states implementation: IN, OUT, ANALOG, PWM 13 | IN = 'IN' 14 | OUT = 'OUT' 15 | ANALOG = 'ANALOG' 16 | PWM = 'PWM' 17 | 18 | 19 | class WrongPinMode(Exception): 20 | value = 'Operation not supported in current mode.' 21 | 22 | 23 | class ModeNotSuported(Exception): 24 | value = 'Mode not suported by Pin or Board.' 25 | 26 | 27 | class ArgumentOutOfRange(Exception): 28 | value = 'Argument not in the range 0.0 to 1.0' 29 | 30 | 31 | class Board(object): 32 | """Abstract class defining common interface for all boards. 33 | 34 | Instance attributes of interest to end-users: 35 | 36 | ``«board».pins`` 37 | A ``dict`` with physical pin locations as keys and ``Pin`` instances 38 | as values. 39 | 40 | ``«board».cleanup()`` 41 | This should be called to release the pins for other applications on 42 | some boards. It is called automatically when the script finishes. 43 | 44 | Implementations of ``Board`` subclasses should: 45 | 46 | * Call ``super(«BoardSubclass», self).__init__()`` and 47 | ``self._add_pins(«pins»)`` in their ``__init__`` method. 48 | 49 | * Implement ``_set_digital_mode()`` and ``_set_pin_state()``. 50 | 51 | * Override ``cleanup()``, if the board needs it. 52 | 53 | """ 54 | __metaclass__ = ABCMeta 55 | 56 | def __init__(self): 57 | """Registers ``self.cleanup`` for calling at script exit. 58 | 59 | This ``__init__`` method should be called by the ``__init__`` of all 60 | ``Board`` subclasses using ``super(BoardSubclass, self).__init__()``. 61 | The ``__init__`` of board subclasses should also call 62 | ``self._add_pins(pins)`` with an iterable of ``Pin`` instances. 63 | """ 64 | atexit.register(self.cleanup) 65 | 66 | def filter_pins(self, *pin_types): 67 | """Get a list of pins that are instances of the given pin_types 68 | 69 | See the ``digital_pins`` property for an example of use. 70 | 71 | Arguments: 72 | ``pin_types``: an iterable of types (usually, ``Pin`` subclasses) 73 | """ 74 | filtered = [] 75 | for pin_type in pin_types: 76 | sub = [x for x in self.pins.values() if isinstance(x, pin_type)] 77 | filtered += sub 78 | 79 | return filtered 80 | 81 | def select_pins(self, locations): 82 | """Get list of pins from iterable of locations""" 83 | locations = list(locations) 84 | return [self.pins[location] for location in locations] 85 | 86 | @property 87 | def digital_pins(self): 88 | """[property] Get list of digital pins""" 89 | 90 | return sorted(self.filter_pins(DigitalPin), key=attrgetter('location')) 91 | 92 | def cleanup(self): 93 | """Releases pins for use by other applications. 94 | 95 | Overriding this stub may or may not be needed in specific drivers. 96 | For example, scripts running on boards using standard ``sysfs`` 97 | GPIO access should ``unexport`` the pins before exiting. 98 | """ 99 | pass 100 | 101 | ###################################################################### 102 | # the following methods are of interest only to implementers of 103 | # drivers, i.e. concrete Board subclasses 104 | 105 | def _add_pins(self, pins): 106 | """Populate ``board.pins`` mapping from ``Pin`` instances. 107 | 108 | The ``__init__`` method of concrete ``Board`` subclasses should 109 | call this method to populate the board instance ``pins`` mapping. 110 | 111 | Arguments: 112 | ``pins``: an iterable of ``Pin`` instances 113 | """ 114 | self.pins = StrKeyDict() 115 | self.gpio = StrKeyDict() 116 | for pin in pins: 117 | self.pins[pin.location] = pin 118 | if hasattr(pin, 'gpio_id'): 119 | self.gpio[pin.gpio_id] = pin 120 | 121 | @abstractmethod 122 | def _set_digital_mode(self, pin, mode): 123 | """Abstract method to be implemented by each ``Board`` subclass. 124 | 125 | The ``«pin».mode(…)`` property calls this method because 126 | the procedure to set pin mode changes from board to board. 127 | """ 128 | 129 | @abstractmethod 130 | def _set_pin_state(self, pin, state): 131 | """Abstract method to be implemented by each ``Board`` subclass 132 | 133 | The ``«pin».__change_state(…)`` method calls this method because 134 | the procedure to set pin state changes from board to board. 135 | """ 136 | 137 | @abstractmethod 138 | def _get_pin_state(self, pin): 139 | """Abstract method to be implemented by each ``Board`` subclass 140 | """ 141 | 142 | 143 | class AnalogInputCapable(object): 144 | """Mixin interface for boards that support AnalogInputPin 145 | 146 | Concrete ``AnalogInputCapable`` subclasses should implement 147 | ``_get_pin_value`` to read the values of analog pins. 148 | """ 149 | 150 | __metaclass__ = ABCMeta 151 | 152 | @abstractmethod 153 | def _get_pin_value(self, pin): 154 | """Abstract method to be implemented by each ``Board`` subclass. 155 | 156 | The ``«AnalogPin».value(…)`` method calls this method because 157 | the procedure to read pin analog signal changes from board to board. 158 | """ 159 | 160 | @abstractmethod 161 | def _set_analog_mode(self, pin, mode): 162 | """Abstract method to be implemented by each ``Board`` subclass. 163 | 164 | The ``«pin».mode(…)`` property calls this method because 165 | the procedure to set pin mode changes from board to board. 166 | """ 167 | 168 | 169 | class PwmOutputCapable(object): 170 | """Mixin interface for boards that support PwmOutputPin 171 | 172 | Concrete ``PwmOutputCapable`` subclasses should implement 173 | ``_get_pin_value`` to write the PWM signal of analog pins. 174 | """ 175 | 176 | __metaclass__ = ABCMeta 177 | 178 | @abstractmethod 179 | def _set_pwm_mode(self, pin): 180 | """Abstract method to be implemented by each ``Board`` subclass.""" 181 | 182 | @abstractmethod 183 | def _set_pwm_frequency(self, pin, value): 184 | """Abstract method to be implemented by each ``Board`` subclass. 185 | 186 | The ``«PwmPin».frequency(…)`` method calls this method because 187 | the procedure to set the PWM's frequency changes from board to board. 188 | """ 189 | 190 | @abstractmethod 191 | def _set_pwm_duty_cycle(self, pin, value): 192 | """Abstract method to be implemented by each ``Board`` subclass. 193 | 194 | The ``«PwmPin».value(…)`` method calls this method because 195 | the procedure to set PWM's duty cycle changes from board to board. 196 | """ 197 | 198 | def _get_pwm_duty_cycle(self, pin): 199 | """ 200 | This method should be overwritten if the ``Board`` subclass 201 | has this feature. 202 | """ 203 | if hasattr(pin, '_duty_cycle'): 204 | return pin._duty_cycle 205 | return 0.0 206 | 207 | def _get_pwm_frequency(self, pin): 208 | """ 209 | This method should be overwritten if the ``Board`` subclass 210 | has this feature. 211 | """ 212 | if hasattr(pin, '_frequency'): 213 | return pin._frequency 214 | return 0.0 215 | 216 | 217 | class Pin(object): 218 | """Abstract class defining common interface for all pins.""" 219 | __metaclass__ = ABCMeta 220 | 221 | suported_modes = [] 222 | 223 | def __init__(self, board, location, gpio_id=None): 224 | """Initialize ``Pin`` instance with 225 | 226 | Arguments: 227 | ``board`` 228 | The board to which the pin is attached. 229 | ``location`` 230 | Physical location of pin; ``int`` and ``str`` are 231 | acceptable. 232 | ``gpio_id`` 233 | Logical name of GPIO pin (e.g. ``sysfs`` file name). 234 | """ 235 | self.board = board 236 | self.location = location 237 | if gpio_id is not None: 238 | self.gpio_id = gpio_id 239 | self._mode = None 240 | 241 | def __repr__(self): 242 | cls_name = self.__class__.__name__ 243 | location = self.location 244 | if hasattr(self, 'gpio_id'): 245 | gpio_id = 'gpio%s' % self.gpio_id 246 | else: 247 | gpio_id = '' 248 | return '<{cls_name} {gpio_id}@{location}>'.format(**locals()) 249 | 250 | @property 251 | def mode(self): 252 | """[property] Get/set pin mode to ``pingo.IN``, ``pingo.OUT`` 253 | ``pingo.ANALOG`` or ``pingo.PWM``""" 254 | return self._mode 255 | 256 | @mode.setter 257 | def mode(self, value): 258 | if value not in self.suported_modes: 259 | raise ModeNotSuported() 260 | 261 | if value in [IN, OUT]: 262 | self.board._set_digital_mode(self, value) 263 | elif value == ANALOG: 264 | self.board._set_analog_mode(self, value) 265 | elif value == PWM: 266 | self.board._set_pwm_mode(self, value) 267 | 268 | self._mode = value 269 | 270 | @property 271 | def is_analog(self): 272 | return issubclass(type(self), AnalogPin) 273 | 274 | 275 | class DigitalPin(Pin): 276 | """Defines common interface for all digital pins. 277 | 278 | The ``repr`` of a digital pin looks like ```` 279 | where ``21`` is the logical pin identifier and ``40`` is the 280 | physical location of the pin in the connector. 281 | 282 | Implementers of board drivers do not need to subclass this class 283 | because pins delegate all board-dependent behavior to the board. 284 | """ 285 | 286 | suported_modes = [IN, OUT] 287 | 288 | def __init__(self, board, location, gpio_id=None): 289 | Pin.__init__(self, board, location, gpio_id) 290 | self._state = None 291 | 292 | @property 293 | def state(self): 294 | """[property] Get/set pin state to ``pingo.HIGH`` or ``pingo.LOW``""" 295 | if self.mode not in [IN, OUT]: 296 | raise WrongPinMode() 297 | 298 | if self.mode == IN: 299 | self._state = self.board._get_pin_state(self) 300 | 301 | return self._state 302 | 303 | @state.setter 304 | def state(self, value): 305 | if self.mode != OUT: 306 | raise WrongPinMode() 307 | 308 | self.board._set_pin_state(self, value) 309 | self._state = value 310 | 311 | def low(self): 312 | """Set voltage of pin to ``pingo.LOW`` (GND).""" 313 | self.state = LOW 314 | 315 | lo = low # shortcut for interactive use 316 | 317 | def high(self): 318 | """Set state of the pin to ``pingo.HIGH`` (Vcc).""" 319 | self.state = HIGH 320 | 321 | hi = high # shortcut for interactive use 322 | 323 | def toggle(self): 324 | """Change state of the pin.""" 325 | self.state = HIGH if self.state == LOW else LOW 326 | 327 | def pulse(self): 328 | """Generate a pulse in state of the pin.""" 329 | if self.state == LOW: 330 | self.state = HIGH 331 | self.state = LOW 332 | else: 333 | self.state = LOW 334 | self.state = HIGH 335 | 336 | 337 | class PwmPin(DigitalPin): 338 | 339 | suported_modes = [IN, OUT, PWM] 340 | 341 | def __init__(self, board, location, gpio_id=None, frequency=None): 342 | DigitalPin.__init__(self, board, location, gpio_id) 343 | self._frequency = frequency 344 | self._duty_cycle = None 345 | 346 | # TUDO: 347 | # Write a decorator to test mode == 'MODE' 348 | 349 | @property 350 | def value(self): 351 | if self.mode != PWM: 352 | raise WrongPinMode() 353 | return self.board._get_pwm_duty_cycle(self) 354 | 355 | @value.setter 356 | def value(self, value): 357 | if self.mode != PWM: 358 | raise WrongPinMode() 359 | if not 0.0 <= value <= 100.0: 360 | raise ArgumentOutOfRange() 361 | self.board._set_pwm_duty_cycle(self, value) 362 | self._duty_cycle = value 363 | 364 | @property 365 | def frequency(self): 366 | if self.mode != PWM: 367 | raise WrongPinMode() 368 | return self.board._get_pwm_frequency(self) 369 | 370 | @frequency.setter 371 | def frequency(self, new_frequency): 372 | if self.mode != PWM: 373 | raise WrongPinMode() 374 | if new_frequency <= 0.0: 375 | raise ArgumentOutOfRange() 376 | self.board._set_pwm_frequency(self, new_frequency) 377 | self._frequency = new_frequency 378 | 379 | 380 | class AnalogPin(Pin): 381 | """Defines common interface for all analog pins. 382 | 383 | Implementers of board drivers do not need to subclass this class 384 | because pins delegate all board-dependent behavior to the board. 385 | 386 | This pin type supports read operations only. 387 | """ 388 | 389 | suported_modes = [IN, ANALOG] 390 | 391 | def __init__(self, board, location, resolution, gpio_id=None): 392 | """ 393 | :param board: the board to which this ping belongs 394 | :param location: the physical location of the pin on the board 395 | :param resolution: resolution of the AD converter in bits 396 | :param gpio_id: the logical id for GPIO access, if applicable 397 | """ 398 | Pin.__init__(self, board, location, gpio_id) 399 | self.bits = resolution 400 | self._mode = None 401 | 402 | @property 403 | def value(self): 404 | """[property] Pin value as integer from 0 to 2 ** resolution - 1""" 405 | return self.board._get_pin_value(self) 406 | 407 | def ratio(self, from_min=0, from_max=None, to_min=0.0, to_max=1.0): 408 | """ Pin value as a float, by default from 0.0 to 1.0. 409 | 410 | The ``from...`` and ``to...`` parameters work like in the Arduino map_ 411 | function, converting values from an expected input range to a desired 412 | output range. 413 | 414 | .. _map: http://arduino.cc/en/reference/map 415 | """ 416 | if from_max is None: 417 | from_max = 2 ** self.bits - 1 418 | 419 | _value = self.value 420 | return (float(_value - from_min) * (to_max - to_min) / 421 | (from_max - from_min) + to_min) 422 | 423 | @property 424 | def percent(self): 425 | """[property] Pin value as float from 0.0 to 100.0""" 426 | return self.ratio(to_max=100.0) 427 | 428 | 429 | class GroundPin(Pin): 430 | 431 | def __repr__(self): 432 | return '<%s>' % self.__class__.__name__ 433 | 434 | 435 | class VccPin(Pin): 436 | 437 | def __init__(self, board, location, voltage): 438 | Pin.__init__(self, board, location) 439 | self.voltage = voltage # e.g. 3.3, 5.0 440 | 441 | def __repr__(self): 442 | return '<%s %0.1fV>' % (self.__class__.__name__, 443 | self.voltage) 444 | -------------------------------------------------------------------------------- /pingo/detect/__init__.py: -------------------------------------------------------------------------------- 1 | from detect import get_board 2 | 3 | 4 | def has_module(module_name): 5 | try: 6 | __import__(module_name) 7 | return True 8 | except ImportError: 9 | return False 10 | 11 | 12 | def check_board(pingo_board): 13 | current = get_board() 14 | return isinstance(current, pingo_board) 15 | -------------------------------------------------------------------------------- /pingo/detect/detect.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import string 4 | import platform 5 | 6 | import pingo 7 | 8 | 9 | class DetectionFailed(Exception): 10 | def __init__(self): 11 | super(DetectionFailed, self).__init__() 12 | self.message = 'Pingo is not able to detect your board.' 13 | 14 | 15 | def _read_cpu_info(): 16 | cpuinfo = {} 17 | # pattern = '(?P[^\t\n]*)\t{1,2}: (?P\.*)\n' 18 | with open('/proc/cpuinfo', 'r') as fp: 19 | for line in fp: 20 | line = line.strip() 21 | if line: 22 | tokens = tuple( 23 | token.strip() for token in line.split(':')) 24 | cpuinfo[tokens[0]] = tokens[-1] 25 | return cpuinfo 26 | 27 | 28 | def _find_arduino_dev(system): 29 | if system == 'Linux': 30 | # TODO: filter possible devices with glob 31 | devices = [] 32 | for dev in os.listdir('/dev/'): 33 | if ('ttyUSB' in dev) or ('ttyACM' in dev): 34 | devices.append(dev) 35 | if len(devices) == 1: 36 | return os.path.join(os.path.sep, 'dev', devices[0]) 37 | 38 | elif system == 'Darwin': 39 | devices = (glob.glob('/dev/tty.usbmodem*') 40 | + glob.glob('/dev/tty.usbserial*')) 41 | if len(devices) == 1: 42 | return os.path.join(os.path.sep, 'dev', devices[0]) 43 | return False 44 | 45 | 46 | def get_board(): 47 | machine = platform.machine() 48 | system = platform.system() 49 | 50 | if machine in ['x86', 'x86_64', 'AMD64']: 51 | if system in ['Linux', 'Darwin', 'Windows']: 52 | # TODO: Try to find 'Arduino' inside dmesg output 53 | device = _find_arduino_dev(system) 54 | if device: 55 | return pingo.arduino.ArduinoFirmata(device) 56 | 57 | print('Using GhostBoard...') 58 | # TODO decide which board return 59 | return pingo.ghost.GhostBoard() 60 | 61 | elif machine == 'i586': 62 | # TODO: assume it's a Galileo2 63 | # FIXME: detect Galileo gen1. 64 | return pingo.intel.Galileo2() 65 | 66 | elif machine == 'i686': 67 | return pingo.intel.Edison() 68 | 69 | elif machine == 'armv6l': 70 | # FIXME: Regex does not work. 71 | # with open('/proc/cpuinfo', 'r') as fp: 72 | # info = fp.read() 73 | # #TODO: Use this code in _read_cpu_info 74 | # pattern = '(?P[^\t\n]*)\t{1,2}: (?P\.*)\n' 75 | 76 | cpuinfo = _read_cpu_info() 77 | revision = string.atoi(cpuinfo['Revision'], 16) # str to hex 78 | 79 | if revision < 16: 80 | print('Using RaspberryPi...') 81 | return pingo.rpi.RaspberryPi() 82 | else: 83 | print('Using RaspberryPi Model B+...') 84 | return pingo.rpi.RaspberryPiBPlus() 85 | 86 | elif machine == 'armv7l': 87 | if system == 'Linux': 88 | hardware = _read_cpu_info()['Hardware'] 89 | lsproc = os.listdir('/proc/') 90 | adcx = [p for p in lsproc if p.startswith('adc')] 91 | 92 | if len(adcx) == 6: 93 | print('Using PcDuino...') 94 | return pingo.pcduino.PcDuino() 95 | 96 | if 'Generic AM33XX' in hardware: 97 | print('Using Beaglebone...') 98 | return pingo.bbb.BeagleBoneBlack() 99 | 100 | if 'SECO i.Mx6 UDOO Board' in hardware: 101 | print('Using Udoo...') 102 | return pingo.udoo.Udoo() 103 | 104 | if hardware == 'BCM2709': 105 | print('Using RaspberryPi 2 Model B...') 106 | return pingo.rpi.RaspberryPi2B() 107 | 108 | raise DetectionFailed() 109 | 110 | # TODO: deprecate legacy "MyBoard" factory name 111 | MyBoard = get_board 112 | -------------------------------------------------------------------------------- /pingo/detect/test_detect.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import platform 3 | 4 | import pingo 5 | import detect 6 | 7 | 8 | class DetectBasics(unittest.TestCase): 9 | 10 | def test_board(self): 11 | board = pingo.detect.get_board() 12 | assert isinstance(board, pingo.Board) 13 | 14 | @unittest.skipIf(not platform.system() == 'Linux', 'Not Linux') 15 | def test_read_cpu_info(self): 16 | info = detect._read_cpu_info() 17 | assert isinstance(info, dict) 18 | 19 | if __name__ == '__main__': 20 | unittest.main() 21 | -------------------------------------------------------------------------------- /pingo/examples/analog_bars.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | import time 3 | 4 | board = pingo.detect.get_board() 5 | # board = pingo.arduino.get_arduino() 6 | 7 | pot = board.pins['A0'] 8 | pot.mode = pingo.ANALOG 9 | 10 | 11 | def bar(pin): 12 | print "*" * int(pin.ratio() * 70) 13 | 14 | while True: 15 | bar(pot) 16 | time.sleep(0.05) 17 | -------------------------------------------------------------------------------- /pingo/examples/blink.py: -------------------------------------------------------------------------------- 1 | """Blink an LED 2 | 3 | This script assumes: 4 | 5 | - ``board.pins[13]`` is a ``DigitalPin`` 6 | - there is an LED attached to it 7 | 8 | """ 9 | 10 | import time 11 | import pingo 12 | 13 | board = pingo.detect.get_board() 14 | led = board.pins[13] 15 | led.mode = pingo.OUT 16 | 17 | while True: 18 | led.toggle() 19 | print(led.state) 20 | time.sleep(.5) 21 | -------------------------------------------------------------------------------- /pingo/examples/blink_firmata.py: -------------------------------------------------------------------------------- 1 | """Blink an LED on a remote Arduino 2 | 3 | This script assumes: 4 | 5 | - this computer is connected to an Arduino on port ``/dev/tty.usbmodemfa1341`` 6 | - the Arduino is running the Examples->Firmata->StandardFirmata sketch 7 | 8 | """ 9 | 10 | import time 11 | import pingo 12 | 13 | ard = pingo.arduino.ArduinoFirmata('/dev/tty.usbmodemfa1341') 14 | print('Connected to: %s' % ard) 15 | led = ard.pins[13] 16 | led.mode = pingo.OUT 17 | 18 | while True: 19 | led.toggle() 20 | time.sleep(.1) 21 | -------------------------------------------------------------------------------- /pingo/examples/blink_firmata_auto.py: -------------------------------------------------------------------------------- 1 | """Blink an LED on a remote Arduino 2 | 3 | This script assumes: 4 | 5 | - this computer is connected to an Arduino 6 | - the Arduino is running the Examples->Firmata->StandardFirmata sketch 7 | 8 | """ 9 | 10 | import time 11 | import pingo 12 | 13 | ard = pingo.arduino.get_arduino() 14 | print('Connected to: %s' % ard) 15 | led = ard.pins[13] 16 | led.mode = pingo.OUT 17 | 18 | while True: 19 | led.toggle() 20 | time.sleep(.5) 21 | -------------------------------------------------------------------------------- /pingo/examples/dimmer.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | This example is controlled by the knob in A0 4 | It shows the level of power on the display 5 | and lights a PWM Led on 6 (or the dicimal point) 6 | 7 | """ 8 | 9 | from pprint import pprint 10 | import pingo 11 | import time 12 | 13 | board = pingo.detect.get_board() 14 | 15 | pot = board.pins['A0'] 16 | pot.mode = pingo.ANALOG 17 | 18 | led = board.pins[6] 19 | led.mode = pingo.PWM 20 | 21 | display_pins = [board.pins[i] for i in range(8, 14) + [7]] 22 | seg_display = pingo.parts.led.SevenSegments(*display_pins) 23 | for pin in display_pins: 24 | pin.mode = pingo.OUT 25 | 26 | 27 | def bar(pin): 28 | reading = pot.ratio() 29 | n = int(reading * 10) 30 | seg_display.digit = n 31 | led.value = reading 32 | 33 | 34 | pprint(board.pins, indent=4, width=1) 35 | 36 | while True: 37 | bar(pot) 38 | time.sleep(0.05) 39 | -------------------------------------------------------------------------------- /pingo/examples/dojo.py: -------------------------------------------------------------------------------- 1 | """Classic Garoa Hardware Dojo Exercise 2 | 3 | Light up segments on perimeter of display in sequence, 4 | with delay set by potentiometer. 5 | 6 | This script assumes: 7 | 8 | - 7-segment display connected to pins 6-13 9 | - segment G is pin 13, decimal point is pin 9 10 | - potentiometer connected to analog pin 'A0' 11 | 12 | """ 13 | 14 | import time 15 | import pingo 16 | 17 | board = pingo.detect.get_board() 18 | print('board: %s' % board) 19 | pot = board.pins['A0'] 20 | pot.mode = pingo.ANALOG 21 | leds = board.digital_pins[6:14] 22 | 23 | for led in leds: 24 | led.mode = pingo.OUT 25 | led.low() 26 | 27 | while True: 28 | for led in leds: 29 | if led.location == 9: 30 | continue 31 | led.high() 32 | time.sleep(pot.ratio()) 33 | print pot.ratio() 34 | led.low() 35 | -------------------------------------------------------------------------------- /pingo/examples/dojo2.py: -------------------------------------------------------------------------------- 1 | """Classic Garoa Hardware Dojo Exercise 2 | 3 | Light up segments on perimeter of display in sequence, 4 | with delay set by potentiometer. 5 | 6 | This script assumes: 7 | 8 | - ``board.pins[13]`` is a ``DigitalPin`` 9 | - there is an LED attached to it 10 | 11 | """ 12 | 13 | from time import sleep 14 | import pingo 15 | 16 | POT_LOCATION = 'A0' 17 | PIN_LOCATIONS = range(6, 14) 18 | 19 | board = pingo.detect.get_board() 20 | pot = pingo.pins[POT_LOCATION] 21 | leds = (pingo.pins[loc] for loc in PIN_LOCATIONS if loc != 9) 22 | 23 | for led in leds: 24 | led.mode = pingo.OUT 25 | 26 | while True: 27 | for led in leds: 28 | led.high() 29 | sleep(pot.ratio()) 30 | led.low() 31 | -------------------------------------------------------------------------------- /pingo/examples/dojoxxl.py: -------------------------------------------------------------------------------- 1 | """ 2 | Arduino coding dojo script for the XXL panel 3 | """ 4 | 5 | import pingo 6 | import time 7 | 8 | ard = pingo.arduino.get_arduino() 9 | print '*' * 60 10 | 11 | segments = [ 12 | 11, # A 13 | 10, # B 14 | 8, # C 15 | 7, # D 16 | 6, # E 17 | 12, # F 18 | # 13, # G 19 | ] 20 | 21 | for seg in segments: 22 | pin = ard.pins[seg] 23 | pin.mode = pingo.OUT 24 | pin.low() 25 | 26 | pot = ard.pins['A0'] 27 | pot.mode = pingo.IN 28 | 29 | while True: 30 | for seg in segments: 31 | pin = ard.pins[seg] 32 | pin.high() 33 | delay = pot.ratio() + 0.01 # add 0.01s delay for communication 34 | print '%0.3f' % delay 35 | time.sleep(delay) 36 | pin.low() 37 | -------------------------------------------------------------------------------- /pingo/examples/double_blink.py: -------------------------------------------------------------------------------- 1 | """Blink an LED via the current board and another via an Arduino 2 | 3 | This script assumes: 4 | 5 | - ``board.pins[13]`` is a ``DigitalPin`` 6 | - ``ard.pins[13]`` is a ``DigitalPin`` 7 | - there is an LED attached to each of them 8 | 9 | """ 10 | 11 | import time 12 | import pingo 13 | 14 | board = pingo.rpi.RaspberryPiBPlus() 15 | local_led = board.pins[13] 16 | local_led.mode = pingo.OUT 17 | 18 | ard = pingo.arduino.get_arduino() 19 | remote_led = ard.pins[13] 20 | remote_led.mode = pingo.OUT 21 | 22 | local_led.low() # for an common anode RGB LED 23 | remote_led.low() 24 | 25 | print('LOCAL: ' + local_led.state) 26 | print('REMOTE: ' + local_led.state) 27 | 28 | while True: 29 | local_led.toggle() 30 | remote_led.toggle() 31 | print('LOCAL: ' + local_led.state) 32 | print('REMOTE: ' + local_led.state) 33 | time.sleep(.5) 34 | -------------------------------------------------------------------------------- /pingo/examples/dual7seg.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | # board = pingo.detect.get_board() # auto-detect board 6 | galileo = pingo.galileo.Galileo2() # explicit board selection 7 | arduino = pingo.arduino.get_arduino() # get Arduino via serial 8 | 9 | galileo_pins = galileo.select_pins(range(8, 14) + [7]) 10 | arduino_pins = arduino.select_pins(range(8, 14) + [7]) 11 | 12 | galileo_d7s = pingo.parts.led.SevenSegments(*galileo_pins) 13 | arduino_d7s = pingo.parts.led.SevenSegments(*arduino_pins) 14 | 15 | displays = [galileo_d7s, arduino_d7s] 16 | 17 | while True: 18 | for i in range(16): 19 | display = displays[i % 2] 20 | display.digit = i 21 | sleep(.5) 22 | -------------------------------------------------------------------------------- /pingo/examples/dual_analog_bars.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | import time 3 | 4 | galileo = pingo.detect.get_board() 5 | arduino = pingo.arduino.get_arduino() 6 | 7 | pot_galileo = galileo.pins['A0'] 8 | pot_galileo.mode = pingo.ANALOG 9 | 10 | pot_arduino = arduino.pins['A0'] 11 | pot_arduino.mode = pingo.ANALOG 12 | 13 | 14 | def bar(pin1, pin2): 15 | bar1 = ('*' * int(pin1.ratio() * 40)).ljust(40) 16 | bar2 = ('*' * int(pin2.ratio() * 40)).rjust(40) 17 | print bar2 + bar1 18 | 19 | while True: 20 | bar(pot_galileo, pot_arduino) 21 | time.sleep(0.05) 22 | -------------------------------------------------------------------------------- /pingo/examples/dualboard.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | # board = pingo.detect.get_board() # auto-detect board 6 | galileo = pingo.galileo.Galileo2() # explicit board selection 7 | arduino = pingo.arduino.get_arduino() # get Arduino via serial 8 | 9 | galileo_pins = galileo.select_pins(range(8, 14) + [7]) 10 | arduino_pins = arduino.select_pins(range(8, 14) + [7]) 11 | 12 | galileo_d7s = pingo.parts.led.SevenSegments(*galileo_pins) 13 | arduino_d7s = pingo.parts.led.SevenSegments(*arduino_pins) 14 | 15 | displays = [galileo_d7s, arduino_d7s] 16 | 17 | while True: 18 | for i in range(16): 19 | display = displays[i % 2] 20 | display.digit = i 21 | sleep(.5) 22 | -------------------------------------------------------------------------------- /pingo/examples/galileo_analog_bars.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | import time 3 | 4 | board = pingo.galileo.Galileo2() 5 | 6 | pot = board.pins['A0'] 7 | pot.mode = pingo.ANALOG 8 | 9 | 10 | def bar(pin): 11 | print "*" * int(pin.ratio() * 70) 12 | 13 | while True: 14 | bar(pot) 15 | time.sleep(0.05) 16 | -------------------------------------------------------------------------------- /pingo/examples/galileo_garoa_dojo_shield.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | gg = pingo.galileo.Galileo2() 6 | 7 | pins = [gg.pins[i] for i in range(8, 14) + [7]] 8 | 9 | d7 = pingo.parts.led.SevenSegments(*pins) 10 | 11 | for i in range(16): 12 | d7.digit = i 13 | sleep(.5) 14 | -------------------------------------------------------------------------------- /pingo/examples/garoa7seg.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pingo 4 | 5 | gg = pingo.detect.get_board() 6 | 7 | pins = gg.select_pins(range(8, 14) + [7]) 8 | 9 | d7 = pingo.parts.led.SevenSegments(*pins) 10 | 11 | for char in 'GAr0A': 12 | try: 13 | d7.digit = int(char, 16) 14 | except ValueError: 15 | d7.digit = char 16 | sleep(.5) 17 | -------------------------------------------------------------------------------- /pingo/examples/map7segment.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility to map Arduino pins connected to a 7-segment display 3 | """ 4 | 5 | import pingo 6 | 7 | ard = pingo.arduino.get_arduino() 8 | print '*' * 60 9 | segs = {} 10 | 11 | for i in range(14): 12 | pin = ard.pins[i] 13 | pin.mode = pingo.OUT 14 | pin.hi() 15 | seg = raw_input('Pin %s -> segment: ' % i).strip() 16 | if seg: 17 | segs[seg] = i 18 | pin.low() 19 | 20 | for seg, pin in sorted(segs.items()): 21 | print '%s, # %s' % (pin, seg) 22 | -------------------------------------------------------------------------------- /pingo/examples/parts/7seg_demo.py: -------------------------------------------------------------------------------- 1 | """ Seven segment display demo using the Garoa Dojo Shield """ 2 | 3 | from time import sleep 4 | from pingo import OUT, detect, parts 5 | 6 | INTERVAL = 0.3 7 | 8 | ard = detect.get_board() 9 | 10 | display_pins = [ard.pins[i] for i in range(8, 14) + [7]] 11 | 12 | for pin in display_pins: 13 | pin.mode = OUT 14 | 15 | seg_display = parts.led.SevenSegments(*display_pins) 16 | 17 | for num in range(16): 18 | seg_display.digit = num 19 | print('Current digit: {:x}'.format(seg_display.digit)) 20 | sleep(INTERVAL) 21 | 22 | for i in range(3): 23 | seg_display.off() 24 | sleep(INTERVAL) 25 | seg_display.on() 26 | sleep(INTERVAL) 27 | 28 | seg_display.off() 29 | -------------------------------------------------------------------------------- /pingo/examples/parts/led_blink.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | board = pingo.detect.get_board() 4 | 5 | a_led = pingo.parts.Led(board.pins[13]) 6 | a_led.blink(0) # times=0 means "forever" 7 | 8 | raw_input('Hit to stop blinking') 9 | a_led.stop() 10 | -------------------------------------------------------------------------------- /pingo/examples/parts/led_demo.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | import time 3 | 4 | board = pingo.detect.get_board() 5 | 6 | a_led = pingo.parts.Led(board.pins[13]) 7 | a_led.blink(3) 8 | 9 | b_led = pingo.parts.Led(board.pins[12]) 10 | b_led.blink(6, .6, .2) 11 | 12 | while any([a_led.blinking, b_led.blinking]): 13 | a_state = '*A*' if a_led.lit else ' a ' 14 | b_state = '*B*' if b_led.lit else ' b ' 15 | 16 | print time.strftime('%H:%M:%S'), a_state, b_state 17 | time.sleep(.1) 18 | -------------------------------------------------------------------------------- /pingo/examples/pongo.py: -------------------------------------------------------------------------------- 1 | """ 2 | A simplified PONG clone developed in a couple of hours at the 3 | Intel IoT Roadshow in São Paulo, Brazil, by Ricardo Banffy and 4 | Luciano Ramalho. 5 | 6 | The hardware setup is a Galileo Gen 2 connected to an Arduino, 7 | each with a potentiometer in pin A0 (we used Garoa Dojo shields). 8 | The script runs on the Galileo, and the display is a terminal 9 | on any computer connected to the Galileo. 10 | """ 11 | 12 | 13 | # flake8: noqa 14 | 15 | import curses 16 | import pingo 17 | import time 18 | import random 19 | 20 | PADDLE_SIZE = 5 21 | MIN_X = 0 22 | MIN_Y = 0 23 | MAX_X = 79 24 | MAX_Y = 24 25 | 26 | score_1 = 0 27 | score_2 = 0 28 | 29 | def init_ball(): 30 | return ([39, 12], 31 | random.choice([ # [1, 0], 32 | [1, 1], # [-1, 0], 33 | [-1, 1], [1, -1]])) 34 | 35 | def new_ball_pos(pos, velocity): 36 | x = pos[0] + velocity[0] 37 | y = pos[1] + velocity[1] 38 | return (x, y) 39 | 40 | def draw_paddle(x, y, color): 41 | for offset in range(PADDLE_SIZE): 42 | screen.addstr(y + offset, x, ' ', color) 43 | 44 | if __name__ == '__main__': 45 | 46 | screen = curses.initscr() 47 | curses.noecho() 48 | curses.cbreak() 49 | try: 50 | curses.curs_set(False) 51 | except: 52 | pass 53 | screen.clear() 54 | curses.start_color() 55 | 56 | # Initializes the color pairs we'll use (black on white and white on black) 57 | curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK) 58 | curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) 59 | 60 | ball_pos, ball_velocity = init_ball() 61 | 62 | # Start communicating with the local and remote sensors 63 | galileo = pingo.detect.get_board() 64 | arduino = pingo.arduino.get_arduino() 65 | 66 | pot_galileo = galileo.pins['A0'] 67 | pot_galileo.mode = pingo.ANALOG 68 | 69 | pot_arduino = arduino.pins['A0'] 70 | pot_arduino.mode = pingo.ANALOG 71 | 72 | # Read the initial values of the paddles 73 | paddle_1_pos = int(pot_arduino.ratio(to_min=MIN_Y, to_max=MAX_Y-PADDLE_SIZE)) 74 | paddle_2_pos = int(pot_galileo.ratio(to_min=MIN_Y, to_max=MAX_Y-PADDLE_SIZE)) 75 | 76 | while True: 77 | # Erases the paddles for the previous moment 78 | draw_paddle(MIN_X, paddle_1_pos, curses.color_pair(2)) 79 | draw_paddle(MAX_X, paddle_2_pos, curses.color_pair(2)) 80 | 81 | # Erase the ball 82 | screen.addstr(ball_pos[1], ball_pos[0], ' ', curses.color_pair(2)) 83 | 84 | # Read current paddle positions 85 | paddle_1_pos = int(pot_arduino.ratio(to_min=MIN_Y, to_max=MAX_Y-PADDLE_SIZE)) 86 | paddle_2_pos = int(pot_galileo.ratio(to_min=MIN_Y, to_max=MAX_Y-PADDLE_SIZE)) 87 | 88 | # If left border collision, increase score and invert vx 89 | if ball_pos[0] <= MIN_X: 90 | ball_velocity[0] = - ball_velocity[0] 91 | # Check whether we collided with a paddle 92 | if not paddle_1_pos < ball_pos[1] < paddle_1_pos + PADDLE_SIZE: 93 | ball_pos, ball_velocity = init_ball() 94 | 95 | # If right border collision, increase score and invert vx 96 | if ball_pos[0] >= MAX_X: 97 | ball_velocity[0] = - ball_velocity[0] 98 | # Check whether we collided with a paddle 99 | if not paddle_2_pos < ball_pos[1] < paddle_2_pos + PADDLE_SIZE: 100 | ball_pos, ball_velocity = init_ball() 101 | 102 | # If top or botton collision, invert vy 103 | if not MIN_Y < ball_pos[1] < MAX_Y: 104 | ball_velocity[1] = - ball_velocity[1] 105 | 106 | ball_pos = new_ball_pos(ball_pos, ball_velocity) 107 | 108 | screen.addstr(ball_pos[1], ball_pos[0], ' ', curses.color_pair(1)) 109 | draw_paddle(MIN_X, paddle_1_pos, curses.color_pair(1)) 110 | draw_paddle(MAX_X, paddle_2_pos, curses.color_pair(1)) 111 | 112 | screen.refresh() 113 | 114 | time.sleep(0.05) 115 | -------------------------------------------------------------------------------- /pingo/examples/probe_pins.py: -------------------------------------------------------------------------------- 1 | """ Sets each pin to high and waits for [ENTER] 2 | 3 | This script is useful to map the connections of digital output pins to 4 | a circuit such as a 7-segment display. 5 | """ 6 | 7 | import sys 8 | 9 | import pingo 10 | 11 | 12 | def probe(first, last): 13 | board = pingo.detect.get_board() 14 | print('Found: %r' % board) 15 | 16 | pins = board.digital_pins[first:last + 1] 17 | 18 | for pin in pins: 19 | pin.mode = pingo.OUT 20 | 21 | for pin in pins: 22 | pin.hi() 23 | raw_input(pin) 24 | pin.lo() 25 | 26 | 27 | if __name__ == '__main__': 28 | try: 29 | first, last = (int(arg) for arg in sys.argv[1:]) 30 | probe(first, last) 31 | except ValueError: 32 | print('Usage: %s ' % sys.argv[0]) 33 | sys.exit(-1) 34 | -------------------------------------------------------------------------------- /pingo/examples/pushbutton_led/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/examples/pushbutton_led/button.png -------------------------------------------------------------------------------- /pingo/examples/pushbutton_led/pushbutton_led.py: -------------------------------------------------------------------------------- 1 | """Pushbutton led. 2 | 3 | The led comes on when you press the button. 4 | 5 | Connections example found on ./button.png 6 | 7 | """ 8 | # -*- coding: utf-8 -*- 9 | import pingo 10 | import sys 11 | 12 | 13 | try: 14 | print("Loading board...") 15 | board = pingo.detect.get_board() 16 | print("Its ok...") 17 | except Exception as e: 18 | print("Error on get_board: {}".format(e)) 19 | sys.exit(1) 20 | 21 | led_pin = board.pins[13] 22 | led_pin.mode = pingo.OUT 23 | button_pin = board.pins[5] 24 | button_pin.mode = pingo.IN 25 | 26 | while True: 27 | if button_pin.state == pingo.HIGH: 28 | led_pin.hi() 29 | else: 30 | led_pin.lo() 31 | -------------------------------------------------------------------------------- /pingo/examples/pwm.py: -------------------------------------------------------------------------------- 1 | """ 2 | Using PWM on pin #6 to dim a LED 3 | 4 | """ 5 | 6 | from pprint import pprint 7 | import pingo 8 | import time 9 | 10 | board = pingo.detect.get_board() 11 | print board 12 | 13 | 14 | led = board.pins[6] 15 | led.mode = pingo.PWM 16 | 17 | pprint(board.pins, indent=4, width=1) 18 | 19 | for p in range(1, 100): 20 | led.value = 1.0 / p 21 | time.sleep(0.05) 22 | -------------------------------------------------------------------------------- /pingo/examples/rgb_led.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import pingo 4 | 5 | board = pingo.detect.get_board() 6 | 7 | rgb = [board.pins[i] for i in (11, 13, 15)] 8 | 9 | for pin in rgb: 10 | pin.mode = pingo.OUT 11 | pin.low() 12 | 13 | while True: 14 | for pin in rgb: 15 | pin.low() 16 | print(pin, pin.state) 17 | time.sleep(.5) 18 | pin.high() 19 | break 20 | -------------------------------------------------------------------------------- /pingo/examples/rpi_examples/display7.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | import time 3 | 4 | rpi = pingo.rpi.RaspberryPi() 5 | 6 | led_locations = [7, 11, 13, 15, 19, 21, 24, 26] 7 | 8 | pins = [pin for _, pin in sorted(rpi.pins.items()) 9 | if pin.location in led_locations] 10 | 11 | for pin in pins: 12 | pin.mode = pingo.OUT 13 | 14 | for pin in pins: 15 | pin.high() 16 | 17 | time.sleep(2) 18 | -------------------------------------------------------------------------------- /pingo/examples/rpi_examples/display7_anim.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | from time import sleep 3 | 4 | rpi = pingo.rpi.RaspberryPi() 5 | 6 | # A B C D E F G dp 7 | led_locations = [11, 7, 21, 24, 26, 13, 15, 19] 8 | 9 | pins = [rpi.pins[loc] for loc in led_locations[:6]] 10 | 11 | for pin in pins: 12 | pin.mode = pingo.OUT 13 | pin.low() 14 | 15 | while True: 16 | for pin in pins: 17 | pin.high() 18 | sleep(.04) 19 | pin.low() 20 | -------------------------------------------------------------------------------- /pingo/examples/rpi_examples/display7_probe.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | rpi = pingo.rpi.RaspberryPi() 4 | 5 | led_locations = [7, 11, 13, 15, 19, 21, 24, 26] 6 | 7 | pins = [rpi.pins[loc] for loc in led_locations] 8 | 9 | for pin in pins: 10 | pin.mode = pingo.OUT 11 | pin.low() 12 | 13 | for pin in pins: 14 | pin.high() 15 | raw_input('Lit: pin %s' % pin.location) 16 | pin.low() 17 | -------------------------------------------------------------------------------- /pingo/examples/rpi_examples/display7_snake.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | from time import sleep 3 | 4 | rpi = pingo.rpi.RaspberryPi() 5 | 6 | # F A B G E D C G 7 | led_sequence = [13, 11, 7, 15, 26, 24, 21, 15] 8 | 9 | pins = [rpi.pins[loc] for loc in led_sequence] 10 | 11 | for pin in pins: 12 | pin.mode = pingo.OUT 13 | pin.low() 14 | 15 | prev_pin = pins[-1] 16 | while True: 17 | for pin in pins: 18 | pin.high() 19 | sleep(.11) 20 | prev_pin.low() 21 | prev_pin = pin 22 | -------------------------------------------------------------------------------- /pingo/examples/rpi_examples/dojo_display7.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import atexit 5 | import time 6 | import RPi.GPIO as GPIO 7 | import pingo 8 | 9 | # call cleanup at program exit 10 | atexit.register(GPIO.cleanup) 11 | 12 | # use BCM logic numbering 13 | GPIO.setmode(GPIO.BCM) 14 | 15 | DISPLAY = [17, 4, 9, 11, 7, 27, 22, 10] 16 | 17 | SPI_CLK = 18 # @12 (physical pin) 18 | SPI_MISO = 23 # @16 19 | SPI_MOSI = 24 # @18 20 | SPI_CS = 25 # @22 21 | ad_converter = pingo.spi.Mcp3008(SPI_CLK, SPI_MISO, SPI_MOSI, SPI_CS) 22 | 23 | POT_CHANNEL = 1 24 | 25 | for led in DISPLAY[:6]: 26 | GPIO.setup(led, GPIO.OUT) 27 | GPIO.output(led, 0) 28 | 29 | while True: 30 | for led in DISPLAY[:6]: 31 | GPIO.output(led, 1) 32 | delay = ad_converter.read(POT_CHANNEL) / 1000.0 33 | time.sleep(delay) 34 | GPIO.output(led, 0) 35 | -------------------------------------------------------------------------------- /pingo/examples/rpi_examples/pin_map.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | rpi = pingo.rpi.RaspberryPi() 4 | 5 | fmt = '{:>22s} {:2d} {:<2d} {}' 6 | for loc1, pin1 in sorted(rpi.pins.items())[::2]: 7 | loc2 = loc1 + 1 8 | pin2 = rpi.pins[loc2] 9 | print fmt.format(pin1, loc1, loc2, pin2) 10 | -------------------------------------------------------------------------------- /pingo/examples/switch.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example is controlled by the switch in 3 3 | It shows 1 if its closed, or 0 if its open. 4 | """ 5 | 6 | import pingo 7 | import time 8 | 9 | board = pingo.detect.get_board() 10 | 11 | display_pins = [board.pins[i] for i in range(8, 14) + [7]] 12 | seg_display = pingo.parts.led.SevenSegments(*display_pins) 13 | 14 | 15 | def show_f(): 16 | global seg_display 17 | seg_display.digit = 0xF 18 | print 'Fechado' # Closed 19 | 20 | 21 | def show_a(): 22 | global seg_display 23 | seg_display.digit = 0xA 24 | print 'Aberto' # Open 25 | 26 | pin_sw = board.pins[3] 27 | my_switch = pingo.parts.Switch(pin_sw) 28 | my_switch.set_callback_down(show_a) 29 | my_switch.set_callback_up(show_f) 30 | 31 | seg_display.digit = 8 32 | 33 | my_switch.start() 34 | 35 | try: 36 | while True: 37 | time.sleep(.5) 38 | print pin_sw.state 39 | except: 40 | my_switch.stop() 41 | else: 42 | my_switch.stop() 43 | -------------------------------------------------------------------------------- /pingo/ghost/__init__.py: -------------------------------------------------------------------------------- 1 | from ghost import GhostBoard # noqa 2 | -------------------------------------------------------------------------------- /pingo/ghost/examples/drill.py: -------------------------------------------------------------------------------- 1 | # Example code for the drill 2 | 3 | import pingo 4 | import time 5 | 6 | board = pingo.ghost.GhostBoard('foo.json') # gets an instance of your board's driver 7 | 8 | # this is equivalent to init_io from your code 9 | a_rotation = board.pins[12] # this pin controls a relay that turns the drill on/off 10 | a_direction = board.pins[13] # defines which direction the drill will move up/down 11 | a_move = board.pins[14] # turns on the drill's actuators 12 | 13 | s_on = board.pins[9] # on/off switch that the user presses 14 | s_down = board.pins[10] # Bottom switch 15 | s_up = board.pins[11] # Top switch 16 | 17 | a_rotation.mode = pingo.OUT 18 | a_direction.mode = pingo.OUT 19 | a_move.mode = pingo.OUT 20 | 21 | s_on.mode = pingo.IN 22 | s_down.mode = pingo.IN 23 | s_up.mode = pingo.IN 24 | 25 | if __name__ == '__main__': 26 | # this is equivalent to affect_outputs, read_inputs from your code 27 | m_state = 1 # Using the finite-state machine abstraction 28 | step, max_steps = 0, 10 # From your code 29 | 30 | while True: # Loop function from your code 31 | # resets the drill to the initial position 32 | a_direction.lo() # Sets the drill to go up 33 | a_rotation.lo() # Turns the drill off 34 | while not s_up.state == pingo.HIGH: 35 | a_move.hi() # Moves the drill down 36 | time.sleep(1) 37 | a_move.low() # Stops moving 38 | 39 | # This inner loop represents the working cicle. 40 | while s_on.state == pingo.HIGH: 41 | if m_state == 1: 42 | a_rotation.hi() # Turns the drill on 43 | a_direction.hi() # Sets the drill to go down 44 | a_move.hi() # Moves the drill down 45 | 46 | if s_down.state == pingo.HIGH: 47 | m_state = 2 # if the drill presses s_down, we go to the next m_state 48 | step += 1 # Counts the numbers of steps 49 | break 50 | 51 | elif m_state == 2: 52 | a_rotation.hi() # Turns the drill on 53 | a_direction.lo() # Sets the drill to go up 54 | a_move.hi() # Moves the drill up 55 | 56 | if s_up.state == pingo.HIGH: 57 | m_state = 1 # if the drill presses s_up, we go to the previous m_state 58 | step += 1 # Counts the numbers of steps 59 | break 60 | 61 | time.sleep(1) 62 | 63 | time.sleep(1) 64 | if step < max_steps: 65 | pass 66 | # sys.exit() 67 | -------------------------------------------------------------------------------- /pingo/ghost/examples/drill_schematic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/ghost/examples/drill_schematic.jpg -------------------------------------------------------------------------------- /pingo/ghost/examples/drill_sensors.py: -------------------------------------------------------------------------------- 1 | # Sensor input 2 | # run: python -i sensor.py 3 | 4 | import pingo 5 | 6 | board = pingo.ghost.GhostBoard('foo.json') # gets an instance of your board's driver 7 | 8 | s_on = board.pins[9] # on/off switch that the user presses 9 | s_down = board.pins[10] # Bottom switch 10 | s_up = board.pins[11] # Top switch 11 | 12 | s_on.mode = pingo.OUT 13 | s_down.mode = pingo.OUT 14 | s_up.mode = pingo.OUT 15 | 16 | s_up.hi() 17 | -------------------------------------------------------------------------------- /pingo/ghost/ghost.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | 4 | class GhostBoard( 5 | pingo.Board, 6 | pingo.AnalogInputCapable, 7 | pingo.PwmOutputCapable 8 | ): 9 | 10 | def __init__(self, filepath=None): 11 | super(GhostBoard, self).__init__() 12 | 13 | # Arduino ATmega168/328 pin mapping 14 | pins = set([ 15 | pingo.DigitalPin(self, 0), 16 | pingo.DigitalPin(self, 1), 17 | pingo.DigitalPin(self, 2), 18 | pingo.PwmPin(self, 3), 19 | pingo.DigitalPin(self, 4), 20 | 21 | pingo.VccPin(self, 'VCC', 5.0), 22 | pingo.GroundPin(self, 'GND'), 23 | pingo.PwmPin(self, 5), 24 | pingo.PwmPin(self, 6), 25 | pingo.DigitalPin(self, 7), 26 | 27 | pingo.DigitalPin(self, 8), 28 | pingo.AnalogPin(self, 'A5', 10), 29 | pingo.AnalogPin(self, 'A4', 10), 30 | pingo.AnalogPin(self, 'A3', 10), 31 | pingo.AnalogPin(self, 'A2', 10), 32 | 33 | pingo.AnalogPin(self, 'A1', 10), 34 | pingo.AnalogPin(self, 'A0', 10), 35 | pingo.GroundPin(self, 'GND'), 36 | pingo.VccPin(self, 'AREF', 5.0), 37 | pingo.VccPin(self, 'AVCC', 5.0), 38 | 39 | pingo.DigitalPin(self, 12), 40 | pingo.DigitalPin(self, 13), 41 | pingo.PwmPin(self, 11), 42 | pingo.PwmPin(self, 10), 43 | pingo.PwmPin(self, 9), 44 | ]) 45 | 46 | self._add_pins(pins) 47 | 48 | self._pin_states = pingo.util.StrKeyDict() 49 | # All pins start on LOW 50 | # FIXME: use "LOW" instead of 0 51 | for location, pin in self.pins.iteritems(): 52 | self._pin_states[location] = 0 if hasattr(pin, 'state') else None 53 | 54 | # Pin 8 starts on HIGH 55 | self._pin_states[8] = 1 56 | 57 | def cleanup(self): 58 | print('GhostBoard: cleaning up.') 59 | 60 | def _set_digital_mode(self, pin, mode): 61 | print('GhostBoard: %r mode -> %s' % (pin, mode)) 62 | 63 | def _set_analog_mode(self, pin, mode): 64 | self._set_digital_mode(pin, mode) 65 | 66 | def _set_pwm_mode(self, pin, mode): 67 | self._set_digital_mode(pin, mode) 68 | 69 | def _set_pin_state(self, pin, state): 70 | print('GhostBoard: %r state -> %s' % (pin, state)) 71 | _state = 1 if state == pingo.HIGH else 0 72 | self._pin_states[pin.location] = _state 73 | 74 | def _get_pin_state(self, pin): 75 | state = self._pin_states[pin.location] 76 | return pingo.HIGH if state else pingo.LOW 77 | 78 | def _get_pin_value(self, pin): 79 | return self._pin_states[pin.location] 80 | 81 | def _get_pwm_duty_cycle(self, pin): 82 | return self._pin_states[pin.location] 83 | 84 | def _set_pwm_duty_cycle(self, pin, value): 85 | self._pin_states[pin.location] = value 86 | 87 | def _set_pwm_frequency(self, pin, value): 88 | pass 89 | -------------------------------------------------------------------------------- /pingo/ghost/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/ghost/tests/__init__.py -------------------------------------------------------------------------------- /pingo/ghost/tests/digital.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Basic usage 3 | ============= 4 | 5 | Turn on a led for 1s 6 | -------------------- 7 | 8 | :: 9 | 10 | >>> import pingo 11 | >>> sleep = lambda x: None 12 | >>> 13 | >>> board = pingo.ghost.GhostBoard() 14 | >>> board.pins[13] 15 | 16 | >>> 17 | >>> led_pin = board.pins[13] 18 | >>> led_pin.mode = pingo.OUT 19 | GhostBoard: mode -> OUT 20 | >>> led_pin.on() 21 | GhostBoard: state -> HIGH 22 | >>> led_pin.state 23 | 'HIGH' 24 | >>> sleep(1) # 1 second 25 | >>> led_pin.off() 26 | GhostBoard: state -> LOW 27 | >>> led_pin.state 28 | 'LOW' 29 | >>> led_pin.toggle() 30 | GhostBoard: state -> HIGH 31 | >>> led_pin.toggle() 32 | GhostBoard: state -> LOW 33 | >>> board.cleanup() 34 | GhostBoard: cleaning up. 35 | -------------------------------------------------------------------------------- /pingo/ghost/tests/test_ghost.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | 5 | from pingo.test import level0 6 | from pingo.test import level1 7 | from pingo.test import level2 8 | 9 | 10 | class GhostBoardTest(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.board = pingo.ghost.GhostBoard() 14 | 15 | # Level1 Parameters 16 | self.vdd_pin_number = 'VCC' 17 | self.digital_output_pin_number = 13 18 | self.digital_input_pin_number = 8 19 | self.total_pins = 24 20 | 21 | # Level1 Parameters 22 | self.analog_input_pin_number = 'A4' 23 | self.expected_analog_input = 1004 24 | self.expected_analog_ratio = 0.98 25 | self.board._pin_states['A4'] = 1004 # Workaround 26 | 27 | # Level2 Parameters 28 | self.pwm_pin_number = 3 29 | 30 | def tearDown(self): 31 | self.board.cleanup() 32 | 33 | 34 | class GhostBoardBasics(GhostBoardTest, level0.BoardBasics): 35 | pass 36 | 37 | 38 | class GhostBoardExceptions(GhostBoardTest, level0.BoardExceptions): 39 | pass 40 | 41 | 42 | class GhostAnalogRead(GhostBoardTest, level1.AnalogReadBasics): 43 | pass 44 | 45 | 46 | class GhostAnalogExceptions(GhostBoardTest, level1.AnalogExceptions): 47 | pass 48 | 49 | 50 | class GhostPwm(GhostBoardTest, level2.PwmBasics): 51 | pass 52 | 53 | 54 | class GhostPwmExceptions(GhostBoardTest, level2.PwmExceptions): 55 | pass 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | -------------------------------------------------------------------------------- /pingo/intel/__init__.py: -------------------------------------------------------------------------------- 1 | from intel import Galileo2 # noqa 2 | from intel import Edison # noqa 3 | -------------------------------------------------------------------------------- /pingo/intel/intel.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | mraa = None 4 | 5 | 6 | class BaseMraa(pingo.Board, pingo.AnalogInputCapable, pingo.PwmOutputCapable): 7 | 8 | _import_error_msg = 'pingo.intel.BaseMraa requires mraa installed' 9 | 10 | def __init__(self): 11 | global mraa 12 | try: 13 | import mraa as mraa 14 | except ImportError: 15 | raise ImportError(self._import_error_msg) 16 | 17 | super(Galileo2, self).__init__() 18 | 19 | self.PIN_MODES = { 20 | pingo.IN: mraa.DIR_IN, 21 | pingo.OUT: mraa.DIR_OUT, 22 | } 23 | 24 | self.PIN_STATES = { 25 | pingo.HIGH: 1, 26 | pingo.LOW: 0, 27 | } 28 | 29 | pwm_pin_numbers = [3, 5, 6, 9, 10, 11, 13] 30 | digital_pin_numbers = [1, 2, 4, 7, 8, 12] 31 | 32 | self._add_pins( 33 | [pingo.PwmPin(self, location) 34 | for location in pwm_pin_numbers] + 35 | 36 | [pingo.DigitalPin(self, location) 37 | for location in digital_pin_numbers] + 38 | 39 | [pingo.AnalogPin(self, 'A' + location, 12) 40 | for location in '012345'] 41 | ) 42 | 43 | self.mraa_pins, self.mraa_analogs, self.mraa_pwms = {}, {}, {} 44 | 45 | def _set_digital_mode(self, pin, mode): 46 | if pin.mode == pingo.PWM: 47 | self.mraa_pwms[pin.location].enable(False) 48 | self.mraa_pins[pin.location] = mraa.Gpio(pin.location) 49 | self.mraa_pins[pin.location].dir(self.PIN_MODES[mode]) 50 | 51 | def _set_analog_mode(self, pin, mode): 52 | mraa_id = int(pin.location[1]) 53 | self.mraa_analogs[pin.location] = mraa.Aio(mraa_id) 54 | 55 | def _set_pwm_mode(self, pin, mode): 56 | if pin.mode == pingo.IN: 57 | self.mraa_pins[pin.location].dir(mraa.DIR_OUT) 58 | self.mraa_pwms[pin.location] = mraa.Pwm(pin.location) 59 | self.mraa_pwms[pin.location].enable(True) 60 | 61 | def _set_pin_state(self, pin, state): 62 | self.mraa_pins[pin.location].write(self.PIN_STATES[state]) 63 | 64 | def _get_pin_state(self, pin): 65 | value = self.mraa_pins[pin.location].read() 66 | return pingo.HIGH if value == 1 else pingo.LOW 67 | 68 | def _get_pin_value(self, pin): 69 | return self.mraa_analogs[pin.location].read() 70 | 71 | def _set_pwm_duty_cycle(self, pin, value): 72 | self.mraa_pwms[pin.location].write(value) 73 | 74 | def _set_pwm_frequency(self, pin, value): 75 | raise NotImplementedError 76 | 77 | 78 | class Galileo2(BaseMraa): 79 | _import_error_msg = 'pingo.intel.Galileo2 requires mraa installed' 80 | 81 | 82 | class Edison(BaseMraa): 83 | _import_error_msg = 'pingo.intel.Edison requires mraa installed' 84 | -------------------------------------------------------------------------------- /pingo/intel/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/intel/tests/__init__.py -------------------------------------------------------------------------------- /pingo/intel/tests/test_edison.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | from pingo.test import level0 5 | from pingo.test import level1 6 | from pingo.detect import check_board 7 | 8 | running_on_galileo = check_board(pingo.intel.Edison) 9 | 10 | 11 | class EdisonTest(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.board = pingo.intel.Edison() 15 | # Level0 Parameters 16 | self.digital_output_pin_number = 6 17 | self.digital_input_pin_number = 3 18 | self.total_pins = 20 19 | 20 | # Level1 Parameters 21 | self.analog_input_pin_number = 'A3' 22 | self.expected_analog_input = 4096 23 | self.expected_analog_ratio = 0.98 24 | 25 | def tearDown(self): 26 | pass 27 | # self.board.cleanup() 28 | 29 | 30 | @unittest.skipIf(not running_on_galileo, 'Edison not detected') 31 | class EdisonBasics(EdisonTest, level0.BoardBasics): 32 | def test_list_pins(self): 33 | pin = self.board.pins[self.digital_output_pin_number] 34 | assert isinstance(pin, pingo.DigitalPin) 35 | 36 | data_pins = len(self.board.pins) 37 | assert data_pins == self.total_pins 38 | 39 | 40 | @unittest.skipIf(not running_on_galileo, 'Edison not detected') 41 | class EdisonExceptions(EdisonTest, level0.BoardExceptions): 42 | pass 43 | 44 | 45 | @unittest.skipIf(not running_on_galileo, 'Edison not detected') 46 | class EdisonAnalogRead(EdisonTest, level1.AnalogReadBasics): 47 | pass 48 | 49 | 50 | @unittest.skipIf(not running_on_galileo, 'Edison not detected') 51 | class EdisonAnalogExceptions(EdisonTest, level1.AnalogExceptions): 52 | pass 53 | 54 | 55 | if __name__ == '__main__': 56 | unittest.main() 57 | -------------------------------------------------------------------------------- /pingo/intel/tests/test_galileo.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | from pingo.test import level0 5 | from pingo.test import level1 6 | from pingo.detect import check_board 7 | 8 | running_on_galileo = check_board(pingo.intel.Galileo2) 9 | 10 | 11 | class GalileoTest(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.board = pingo.intel.Galileo2() 15 | # Level0 Parameters 16 | self.digital_output_pin_number = 6 17 | self.digital_input_pin_number = 3 18 | self.total_pins = 20 19 | 20 | # Level1 Parameters 21 | self.analog_input_pin_number = 'A3' 22 | self.expected_analog_input = 4096 23 | self.expected_analog_ratio = 0.98 24 | 25 | def tearDown(self): 26 | pass 27 | # self.board.cleanup() 28 | 29 | 30 | @unittest.skipIf(not running_on_galileo, 'Galileo not detected') 31 | class GalileoBasics(GalileoTest, level0.BoardBasics): 32 | def test_list_pins(self): 33 | pin = self.board.pins[self.digital_output_pin_number] 34 | assert isinstance(pin, pingo.DigitalPin) 35 | 36 | data_pins = len(self.board.pins) 37 | assert data_pins == self.total_pins 38 | 39 | 40 | @unittest.skipIf(not running_on_galileo, 'Galileo not detected') 41 | class GalileoExceptions(GalileoTest, level0.BoardExceptions): 42 | pass 43 | 44 | 45 | @unittest.skipIf(not running_on_galileo, 'Galileo not detected') 46 | class GalileoAnalogRead(GalileoTest, level1.AnalogReadBasics): 47 | pass 48 | 49 | 50 | @unittest.skipIf(not running_on_galileo, 'Galileo not detected') 51 | class GalileoAnalogExceptions(GalileoTest, level1.AnalogExceptions): 52 | pass 53 | 54 | 55 | if __name__ == '__main__': 56 | unittest.main() 57 | -------------------------------------------------------------------------------- /pingo/iot/client.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pingo 3 | try: 4 | # Python 2.7 5 | from urllib2 import urlopen 6 | except ImportError: 7 | # Python 3+ 8 | from urrlib.requests import urlopen 9 | 10 | 11 | class HTTPBoard(pingo.Board): 12 | """ 13 | """ 14 | 15 | def __init__(self, server): 16 | self.server = server 17 | response = urlopen(server) 18 | if response.code != 200: 19 | raise Exception(u'HTTPBoard not found on server {}'.format(server)) 20 | response = json.load(response) 21 | pins = json.loads(response['pins']) 22 | gpio_pins = [] 23 | ground_pins = [] 24 | vcc_pins = [] 25 | pwm_pins = [] 26 | for pin, value in pins.items(): 27 | # TODO: serialize the pin (and/or the board) to do this in a better way 28 | if 'GroundPin' in value: 29 | ground_pins.append(pingo.GroundPin(self, pin)) 30 | elif 'VccPin' in value: 31 | voltage = float(value[:-2].split(' ')[1]) 32 | vcc_pins.append(pingo.VccPin(self, pin, voltage)) 33 | elif 'PwmPin' in value: 34 | gpio_id = value.split(' ')[1].split('@')[0] 35 | pwm_pins.append(pingo.PwmPin(self, pin, gpio_id)) 36 | elif 'DigitalPin' in value: 37 | gpio_id = value.split(' ')[1].split('@')[0] 38 | gpio_pins.append(pingo.DigitalPin(self, pin, gpio_id)) 39 | self._add_pins(ground_pins + vcc_pins + gpio_pins + pwm_pins) 40 | 41 | def _set_digital_mode(self, pin, mode): 42 | mode = 'input' if pingo.IN else 'output' 43 | url = '{server}mode/{mode}/{pin}'.format(server=self.server, 44 | mode=mode, pin=pin.location) 45 | urlopen(url) 46 | 47 | def _set_pin_state(self, pin, state): 48 | mode = 'analog' if pin.is_analog else 'digital' 49 | state = 1 if state == pingo.HIGH else 0 50 | url = '{server}{mode}/{pin}/{state}'.format(server=self.server, mode=mode, 51 | pin=str(pin.location), 52 | state=str(state)) 53 | print(url) 54 | response = urlopen(url) 55 | if response.code != 200: 56 | message = u'Pin {} could not be set to {}. HTTPBoard response: {}' 57 | message.format(repr(pin), state, response.code) 58 | raise Exception(message) 59 | 60 | def _get_pin_state(self, pin): 61 | mode = 'analog' if pin.is_analog else 'digital' 62 | url = '{server}{mode}/{pin}'.format(server=self.server, mode=mode, 63 | pin=pin.location) 64 | response = urlopen(url) 65 | if response.code != 200: 66 | message = u'Pin {} could not be read: HTTPBoard response: {}' 67 | message.format(repr(pin), response.code) 68 | raise Exception(message) 69 | return response['input'] 70 | -------------------------------------------------------------------------------- /pingo/iot/server.py: -------------------------------------------------------------------------------- 1 | from bottle import Bottle 2 | import json 3 | import pingo 4 | import sys 5 | 6 | 7 | app = Bottle(__name__) 8 | board = pingo.detect.get_board() 9 | 10 | 11 | @app.route('/') 12 | def main(): 13 | pins = {key: repr(value) for key, value in board.pins} 14 | return { 15 | 'board': repr(board), 16 | 'pins': json.dumps(pins) 17 | } 18 | 19 | 20 | @app.route('/mode//') 21 | def mode(mode, pin): 22 | assert mode in ('input', 'output') 23 | mode = pingo.IN if 'input' else pingo.OUT 24 | pin = board.pins[pin] 25 | pin.mode = mode 26 | 27 | 28 | @app.route('/analog') 29 | def analog_pins(): 30 | pins = {location: pin for location, pin in board.pins 31 | if pin.is_analog} 32 | return {'pins': str(pins)} 33 | 34 | 35 | @app.route('/analog/') 36 | def analog_input(pin): 37 | pin = board.pins[pin] 38 | pin.mode = pingo.IN 39 | return {'input': pin.state} 40 | 41 | 42 | @app.route('/analog//') 43 | def analog_output(pin, signal): 44 | pin = board.pins[pin] 45 | pin.mode = pingo.OUT 46 | pin.value = signal 47 | return {'output': signal} 48 | 49 | 50 | @app.route('/digital') 51 | def digital_pins(): 52 | pins = board.pins() 53 | return {'pins': str(pins)} 54 | 55 | 56 | @app.route('/digital/') 57 | def digital_input(pin): 58 | pin = board.pins[pin] 59 | pin.mode = pingo.IN 60 | return {'input': pin.state} 61 | 62 | 63 | @app.route('/digital//') 64 | def digital_output(pin, signal): 65 | pin = board.pins[pin] 66 | pin.mode = pingo.OUT 67 | pin.high() if signal else pin.low() 68 | return {'output': signal} 69 | 70 | 71 | if __name__ == '__main__': 72 | try: 73 | kwargs = {'host': sys.argv[1]} 74 | except IndexError: 75 | kwargs = {} 76 | app.run(debug=True, **kwargs) 77 | -------------------------------------------------------------------------------- /pingo/parts/__init__.py: -------------------------------------------------------------------------------- 1 | from .led import Led # noqa 2 | from .button import Switch # noqa 3 | from .servo import Servo # noqa 4 | import serial # noqa 5 | -------------------------------------------------------------------------------- /pingo/parts/button.py: -------------------------------------------------------------------------------- 1 | import time 2 | import threading 3 | 4 | 5 | class Switch(object): 6 | """Button like component with two stable states""" 7 | 8 | def __init__(self, pin): 9 | """ 10 | :param pin: A instance of DigitalPin 11 | """ 12 | self.pin = pin 13 | self.pin.mode = 'IN' 14 | self.polling_task = None 15 | self._up_callback = lambda: None 16 | self._down_callback = lambda: None 17 | 18 | def set_callback_up(self, callback, *args, **kwargs): 19 | def callback_wrapper(): 20 | return callback(*args, **kwargs) 21 | self._up_callback = callback_wrapper 22 | 23 | def set_callback_down(self, callback, *args, **kwargs): 24 | def callback_wrapper(): 25 | return callback(*args, **kwargs) 26 | self._down_callback = callback_wrapper 27 | 28 | def stop(self): 29 | if self.polling_task is not None: 30 | if self.polling_task.active: 31 | self.polling_task.terminate() 32 | self.polling_task = None 33 | 34 | def start(self): 35 | if self.polling_task is not None: 36 | if self.polling_task.active: 37 | self.stop() 38 | self.polling_task = PollingTask(self) 39 | threading.Thread(target=self.polling_task.run).start() 40 | 41 | 42 | class PollingTask(object): 43 | def __init__(self, switch): 44 | """ 45 | :param switch: Switch instance to poll 46 | """ 47 | self.switch = switch 48 | self.active = False 49 | 50 | def terminate(self): 51 | self.active = False 52 | 53 | def run(self): 54 | self.active = True 55 | last_state = self.switch.pin.state 56 | while self.active: 57 | current_state = self.switch.pin.state 58 | if current_state != last_state: 59 | if current_state == 'HIGH': 60 | last_state = current_state 61 | self.switch._up_callback() 62 | elif current_state == 'LOW': 63 | last_state = current_state 64 | self.switch._down_callback() 65 | time.sleep(0.05) 66 | -------------------------------------------------------------------------------- /pingo/parts/led.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | import time 4 | import threading 5 | 6 | 7 | class Led(object): 8 | """A single LED""" 9 | 10 | def __init__(self, pin, lit_state=pingo.HIGH): 11 | """Set lit_state to pingo.LOW to turn on led by bringing 12 | cathode to low state. 13 | 14 | :param lit_state: use pingo.HI for anode control, pingo.LOW 15 | for cathode control 16 | """ 17 | 18 | pin.mode = pingo.OUT 19 | self.pin = pin 20 | self.lit_state = lit_state 21 | self.blink_task = None 22 | 23 | def on(self): 24 | if self.lit_state == pingo.HIGH: 25 | self.pin.high() 26 | else: 27 | self.pin.low() 28 | 29 | def off(self): 30 | if self.lit_state == pingo.HIGH: 31 | self.pin.low() 32 | else: 33 | self.pin.high() 34 | 35 | @property 36 | def lit(self): 37 | return self.pin.state == self.lit_state 38 | 39 | @lit.setter 40 | def lit(self, new_state): 41 | if new_state: 42 | self.on() 43 | else: 44 | self.off() 45 | 46 | @property 47 | def blinking(self): 48 | return self.blink_task is not None and self.blink_task.active 49 | 50 | def toggle(self): 51 | self.pin.toggle() 52 | 53 | def blink(self, times=3, on_delay=.5, off_delay=None): 54 | """ 55 | :param times: number of times to blink (0=forever) 56 | :param on_delay: delay while LED is on 57 | :param off_delay: delay while LED is off 58 | """ 59 | if self.blinking: 60 | self.stop() 61 | self.blink_task = BlinkTask(self, times, on_delay, off_delay) 62 | threading.Thread(target=self.blink_task.run).start() 63 | 64 | def stop(self): 65 | """Stop blinking""" 66 | if self.blinking: 67 | self.blink_task.terminate() 68 | self.blink_task = None 69 | 70 | 71 | PURE_COLORS = [ 72 | ('RED', [1, 0, 0]), 73 | ('YELLOW', [1, 1, 0]), 74 | ('GREEN', [0, 1, 0]), 75 | ('CYAN', [0, 1, 1]), 76 | ('BLUE', [0, 0, 1]), 77 | ('PURPLE', [1, 0, 1]), 78 | ('WHITE', [1, 1, 1]), 79 | ('BLACK', [0, 0, 0]), 80 | ] 81 | 82 | 83 | class RGBLed(object): 84 | 85 | pure_colors_map = dict(PURE_COLORS) 86 | 87 | def __init__(self, red_pin, green_pin, blue_pin, 88 | lit_state=pingo.LOW): 89 | self._leds = [Led(red_pin, lit_state), Led(green_pin, lit_state), 90 | Led(blue_pin, lit_state)] 91 | 92 | self.color = 'BLACK' 93 | 94 | @property 95 | def color(self): 96 | return self._color 97 | 98 | @color.setter 99 | def color(self, new_color): 100 | new_color = new_color.upper() 101 | if new_color in RGBLed.pure_colors_map: 102 | self._color = new_color 103 | states = RGBLed.pure_colors_map[new_color] 104 | for led, state in zip(self._leds, states): 105 | led.lit = state 106 | else: 107 | raise ValueError('Unknown color %s', new_color) 108 | 109 | def cycle(self, delay=.15): 110 | colors = PURE_COLORS[:6] # exclude white and black 111 | for color, _ in colors: 112 | self.color = color 113 | time.sleep(delay) 114 | 115 | 116 | class BlinkTask(object): 117 | 118 | def __init__(self, led, times, on_delay, off_delay): 119 | """ 120 | :param led: Led instance to to blink 121 | :param times: number of times to blink (0=forever) 122 | :param on_delay: delay while LED is on 123 | :param off_delay: delay while LED is off 124 | """ 125 | self.led = led 126 | self.led_pin_state_initial = self.led.pin.state 127 | self.active = True 128 | self.forever = times == 0 129 | self.times_remaining = times 130 | self.on_delay = on_delay 131 | self.off_delay = off_delay if off_delay is not None else on_delay 132 | self.led.off() 133 | 134 | def terminate(self): 135 | self.active = False 136 | 137 | def run(self): 138 | while self.active and (self.forever or self.times_remaining): 139 | self.led.toggle() 140 | if self.led.lit: 141 | time.sleep(self.on_delay) 142 | if not self.forever: 143 | self.times_remaining -= 1 144 | else: 145 | time.sleep(self.off_delay) 146 | else: 147 | self.led.pin.state = self.led_pin_state_initial 148 | self.active = False 149 | 150 | 151 | DIGIT_MAP = { 152 | 0: '1111110', 153 | 1: '0110000', 154 | 2: '1101101', 155 | 3: '1111001', 156 | 4: '0110011', 157 | 5: '1011011', 158 | 6: '1011111', 159 | 7: '1110000', 160 | 8: '1111111', 161 | 9: '1111011', 162 | 10: '1110111', 163 | 11: '0011111', 164 | 12: '1001110', 165 | 13: '0111101', 166 | 14: '1001111', 167 | 15: '1000111', 168 | 'G': '1011110', # to spell GAr0A 169 | 'r': '0000101', # to spell GAr0A 170 | } 171 | 172 | 173 | class SevenSegments(object): 174 | 175 | def __init__(self, pin_a, pin_b, pin_c, pin_d, 176 | pin_e, pin_f, pin_g, pin_dp=None, 177 | lit_state=pingo.HIGH): 178 | self._leds = [Led(pin_a, lit_state), Led(pin_b, lit_state), 179 | Led(pin_c, lit_state), Led(pin_d, lit_state), 180 | Led(pin_e, lit_state), Led(pin_f, lit_state), 181 | Led(pin_g, lit_state)] 182 | 183 | if pin_dp: 184 | self._leds.append(Led(pin_dp, lit_state)) 185 | 186 | self._digit = 0 187 | self._dot = False 188 | 189 | def _configure(self, pattern): 190 | for segment, state in zip(self._leds, pattern): 191 | segment.lit = state == '1' 192 | 193 | @property 194 | def digit(self): 195 | return self._digit 196 | 197 | @digit.setter 198 | def digit(self, digit): 199 | self._digit = digit 200 | pattern = DIGIT_MAP[digit] 201 | self._configure(pattern) 202 | 203 | def on(self): 204 | self.digit = self._digit 205 | 206 | def off(self): 207 | self._configure('0' * 7) 208 | 209 | @property 210 | def dot(self): 211 | return self._dot 212 | 213 | @dot.setter 214 | def dot(self, state): 215 | if len(self._leds) < 8: 216 | raise LookupError('Decimal point LED undefined') 217 | if state: 218 | self._dot = True 219 | self._leds[7].on() 220 | else: 221 | self._dot = False 222 | self._leds[7].off() 223 | -------------------------------------------------------------------------------- /pingo/parts/serial/__init__.py: -------------------------------------------------------------------------------- 1 | from lcd import LCD16x2 # noqa 2 | -------------------------------------------------------------------------------- /pingo/parts/serial/lcd.py: -------------------------------------------------------------------------------- 1 | SPECIAL_CMD = b'\x7C' 2 | MOVE_CMD = b'\xFE' 3 | 4 | 5 | class LCD16x2(object): 6 | 7 | def __init__(self, port, baudrate=9600): 8 | import serial as py_serial 9 | self.uart = py_serial.Serial(port, baudrate) 10 | 11 | def clear(self): 12 | self.uart.write(SPECIAL_CMD + b'\x01') 13 | 14 | def set_cursor(self, line=0, column=0): 15 | first_pos = (128, 192) 16 | code = first_pos[line] + column 17 | self.uart.write(MOVE_CMD + chr(code)) 18 | 19 | def write(self, octets): 20 | self.uart.write(octets) 21 | 22 | def __repr__(self): 23 | cls_name = self.__class__.__name__ 24 | return '<{} at {!r}>'.format(cls_name, self.uart.port) 25 | -------------------------------------------------------------------------------- /pingo/parts/servo.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | from pingo.board import ArgumentOutOfRange 3 | 4 | 5 | class Servo(object): 6 | 7 | def __init__(self, pin): 8 | pin.mode = pingo.PWM 9 | pin.frequency = 50 10 | self.pin = pin 11 | # Percent of dutycycle that represents zero 12 | # Default: 2.5% ~ 0.5ms pulse @ 50Hz 13 | self._botton_end = 2.5 14 | # Percent of dutycycle that represents 180 15 | # Default: 12.5% ~ 2.5ms pulse @ 50Hz 16 | self._top_end = 12.5 17 | 18 | def move(self, position): 19 | if not 0 <= position <= 180: 20 | raise ArgumentOutOfRange() 21 | pmin, pmax = self._botton_end, self._top_end 22 | self.pin.value = float(position) * (pmax - pmin) / (180.0) + pmin 23 | -------------------------------------------------------------------------------- /pingo/parts/spi/__init__.py: -------------------------------------------------------------------------------- 1 | from mcp3008 import Mcp3008 # noqa 2 | -------------------------------------------------------------------------------- /pingo/parts/spi/mcp3008.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Object interface for MCP3008 A/D converter using bit-banged SPI 6 | """ 7 | 8 | import time 9 | import atexit 10 | import RPi.GPIO as GPIO 11 | 12 | # make sure GPIO.cleanup will be called when script exits 13 | atexit.register(GPIO.cleanup) 14 | 15 | 16 | class Mcp3008(object): 17 | def __init__(self, spi_clock, spi_miso, spi_mosi, spi_cs): 18 | self.clock = spi_clock 19 | self.miso = spi_miso 20 | self.mosi = spi_mosi 21 | self.cs = spi_cs 22 | GPIO.setmode(GPIO.BCM) 23 | for port in [self.clock, self.mosi, self.cs]: 24 | GPIO.setup(port, GPIO.OUT) 25 | GPIO.setup(self.miso, GPIO.IN) 26 | 27 | def read(self, channel): 28 | assert 0 <= channel <= 7, 'channel must be 0...7' 29 | GPIO.output(self.cs, True) 30 | GPIO.output(self.clock, False) 31 | GPIO.output(self.cs, False) 32 | cmd = channel 33 | cmd |= 0x18 # start bit + "single-ended" config bit 34 | cmd <<= 3 # discard 3 bits; we need just 5 35 | for i in range(5): 36 | GPIO.output(self.mosi, cmd & 0x80) 37 | cmd <<= 1 38 | GPIO.output(self.clock, True) 39 | GPIO.output(self.clock, False) 40 | 41 | res = 0 42 | # read null bit plus 10 bits for ADC value 43 | for i in range(11): 44 | GPIO.output(self.clock, True) 45 | GPIO.output(self.clock, False) 46 | res <<= 1 47 | if (GPIO.input(self.miso)): 48 | res |= 0x1 49 | 50 | GPIO.output(self.cs, True) 51 | return res 52 | 53 | 54 | def test(): 55 | # select pins for SPI 56 | SPI_CLK = 18 57 | SPI_MISO = 23 58 | SPI_MOSI = 24 59 | SPI_CS = 25 60 | ad_chip = Mcp3008(SPI_CLK, SPI_MISO, SPI_MOSI, SPI_CS) 61 | count = 0 62 | display = '{0:6d} {1:010b} {1:4} {2:3.2f} V {3}' 63 | while True: 64 | res = ad_chip.read(1) 65 | volts = float(res) / 1023 * 3.3 66 | ticks = int(round(float(res) / 1023 * 40)) * '=' 67 | print display.format(count, res, volts, ticks) 68 | time.sleep(.2) 69 | count += 1 70 | 71 | if __name__ == '__main__': 72 | test() 73 | -------------------------------------------------------------------------------- /pingo/parts/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/parts/test/__init__.py -------------------------------------------------------------------------------- /pingo/parts/test/test_switch.py: -------------------------------------------------------------------------------- 1 | import time 2 | import unittest 3 | 4 | from pingo.parts import Switch 5 | 6 | 7 | class FakeDigitalPin(object): 8 | def __init__(self): 9 | self.mode = 'IN' 10 | self.state = 'LOW' 11 | 12 | 13 | class TestSwitch(unittest.TestCase): 14 | def setUp(self): 15 | self.pin = FakeDigitalPin() 16 | self.my_switch = Switch(self.pin) 17 | self.my_switch.test_list = [] 18 | 19 | def test_run_down_callback(self): 20 | def callback_down(): 21 | self.my_switch.test_list += ['down'] 22 | self.my_switch.set_callback_down(callback_down) 23 | 24 | self.my_switch.start() 25 | time.sleep(.1) 26 | self.pin.state = 'HIGH' 27 | time.sleep(.1) 28 | self.pin.state = 'LOW' 29 | time.sleep(.1) 30 | self.pin.state = 'HIGH' 31 | time.sleep(.1) 32 | self.my_switch.stop() 33 | 34 | self.assertEqual(self.my_switch.test_list, ['down']) 35 | 36 | def test_run_both_callback(self): 37 | def callback_down(): 38 | self.my_switch.test_list += ['down'] 39 | 40 | def callback_up(): 41 | self.my_switch.test_list += ['up'] 42 | 43 | self.my_switch.set_callback_down(callback_down) 44 | self.my_switch.set_callback_up(callback_up) 45 | 46 | self.my_switch.start() 47 | time.sleep(.1) 48 | self.pin.state = 'HIGH' 49 | time.sleep(.1) 50 | self.pin.state = 'LOW' 51 | time.sleep(.1) 52 | self.pin.state = 'HIGH' 53 | time.sleep(.1) 54 | 55 | self.my_switch.stop() 56 | 57 | self.assertEqual(self.my_switch.test_list, ['up', 'down', 'up']) 58 | 59 | if __name__ == '__main__': 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /pingo/pcduino/README.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## pcDuino V1: 4 | 5 | ### Boot 6 | If the board does not show any image on the monitor, right after the power 7 | cable is plugged, you should wait about 2:30 minutes. If it's taking 8 | longer then 3 minutes, your board has a tilt. 9 | 10 | ### Power Supply 11 | pcDuino V1 draw many mA (I don't know the exact figure). Try to use a higher 12 | power PSU. If a low current power supply is used, pcDuino might not boot or 13 | freeze after some time. A 2000mA PSU is recommended. 14 | 15 | Other solution is a USB Hub with with its own power source, and plug the 16 | keyboard and mouse there. 17 | 18 | ### Wire Network 19 | The correct /etc/network/interfaces configuration: 20 | ``` 21 | auto lo 22 | iface lo inet loopback 23 | ``` 24 | Notice that NO eth0 interface is used. 25 | 26 | On /etc/NetworkManager/NetworkManager.conf set: 27 | ``` 28 | [ifupdown] 29 | managed=true 30 | ``` 31 | 32 | Then restart the service: 33 | ``` 34 | sudo service network-manager restart 35 | ``` 36 | -------------------------------------------------------------------------------- /pingo/pcduino/__init__.py: -------------------------------------------------------------------------------- 1 | from pcduino import PcDuino # noqa 2 | -------------------------------------------------------------------------------- /pingo/pcduino/pcduino.py: -------------------------------------------------------------------------------- 1 | from pingo.board import Board, DigitalPin, AnalogPin, IN, OUT, HIGH, LOW 2 | from pingo.board import AnalogInputCapable 3 | 4 | 5 | class PcDuino(Board, AnalogInputCapable): 6 | """ 7 | pcDuino board (works on V1 and V3) 8 | """ 9 | DIGITAL_PINS_PATH = '/sys/devices/virtual/misc/gpio/' 10 | ADC_PATH = '/proc/' 11 | 12 | DIGITAL_PIN_MODES = {IN: '0', OUT: '1'} 13 | DIGITAL_PIN_STATES = {HIGH: '1', LOW: '0'} 14 | LEN_DIGITAL_PINS = 14 15 | ANALOG_PIN_RESOLUTIONS = [6, 6, 12, 12, 12, 12] 16 | 17 | def __init__(self): 18 | self._add_pins( 19 | [DigitalPin(self, location) 20 | for location in range(self.LEN_DIGITAL_PINS)] + 21 | [AnalogPin(self, 'A%s' % location, resolution=bits) 22 | for location, bits in enumerate(self.ANALOG_PIN_RESOLUTIONS)]) 23 | 24 | def _set_digital_mode(self, pin, mode): 25 | err_msg = '%r not in %r' % (mode, self.DIGITAL_PIN_MODES) 26 | assert mode in self.DIGITAL_PIN_MODES, err_msg 27 | sys_string = self.DIGITAL_PINS_PATH + 'mode/gpio%s' % pin.location 28 | with open(sys_string, 'w') as fp: 29 | fp.write(self.DIGITAL_PIN_MODES[mode]) 30 | 31 | def _set_analog_mode(self, pin, mode): 32 | pass 33 | 34 | def _set_pin_state(self, pin, state): 35 | sys_string = self.DIGITAL_PINS_PATH + 'pin/gpio%s' % pin.location 36 | with open(sys_string, 'w') as fp: 37 | fp.write(self.DIGITAL_PIN_STATES[state]) 38 | 39 | def _get_pin_state(self, pin): 40 | sys_string = self.DIGITAL_PINS_PATH + 'pin/gpio%s' % pin.location 41 | with open(sys_string, 'r') as fp: 42 | state = fp.read().strip() 43 | return HIGH if state == '1' else LOW 44 | 45 | def _get_pin_value(self, pin): 46 | sys_string = self.ADC_PATH + 'adc%s' % pin.location[1:] # eg. A5 47 | with open(sys_string) as fp: 48 | fp.seek(0) 49 | return int(fp.read(16).split(':')[1]) 50 | -------------------------------------------------------------------------------- /pingo/pcduino/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/pcduino/tests/__init__.py -------------------------------------------------------------------------------- /pingo/pcduino/tests/digital.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Basic usage 3 | ============= 4 | 5 | Turn on a led for 1s 6 | -------------------- 7 | 8 | :: 9 | 10 | >>> import pingo 11 | >>> from time import sleep 12 | >>> 13 | >>> board = pingo.pcduino.PcDuino() 14 | >>> board.pins[10] 15 | 16 | >>> 17 | >>> led_pin = board.pins[10] 18 | >>> led_pin.set_mode(pingo.OUT) 19 | >>> led_pin.on() 20 | >>> led_pin.state 21 | 1 22 | >>> sleep(1) # 1 second 23 | >>> led_pin.off() 24 | >>> led_pin.state 25 | 0 26 | -------------------------------------------------------------------------------- /pingo/pcduino/tests/test_pcduino.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | from pingo.test import level0 5 | from pingo.test import level1 6 | from pingo.detect import check_board 7 | 8 | running_on_pcduino = check_board(pingo.pcduino.PcDuino) 9 | 10 | 11 | class PcDuinoTest(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.board = pingo.pcduino.PcDuino() 15 | # Level0 Parameters 16 | self.digital_output_pin_number = 3 17 | self.digital_input_pin_number = 0 18 | self.total_pins = 20 19 | 20 | # Level1 Parameters 21 | self.analog_input_pin_number = 'A3' 22 | self.expected_analog_input = 4096 23 | self.expected_analog_ratio = 0.98 24 | 25 | def tearDown(self): 26 | self.board.cleanup() 27 | 28 | 29 | @unittest.skipIf(not running_on_pcduino, 'PcDuino not detected') 30 | class PcDuinoBasics(PcDuinoTest, level0.BoardBasics): 31 | def test_list_pins(self): 32 | pin = self.board.pins[self.digital_output_pin_number] 33 | assert isinstance(pin, pingo.DigitalPin) 34 | 35 | data_pins = len(self.board.pins) 36 | assert data_pins == self.total_pins 37 | 38 | 39 | @unittest.skipIf(not running_on_pcduino, 'PcDuino not detected') 40 | class PcDuinoExceptions(PcDuinoTest, level0.BoardExceptions): 41 | pass 42 | 43 | 44 | @unittest.skipIf(not running_on_pcduino, 'PcDuino not detected') 45 | class PcDuinoAnalogRead(PcDuinoTest, level1.AnalogReadBasics): 46 | pass 47 | 48 | 49 | @unittest.skipIf(not running_on_pcduino, 'PcDuino not detected') 50 | class PcDuinoAnalogExceptions(PcDuinoTest, level1.AnalogExceptions): 51 | pass 52 | 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /pingo/pinGUIm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/pinGUIm/__init__.py -------------------------------------------------------------------------------- /pingo/pinGUIm/pinGUIm.py: -------------------------------------------------------------------------------- 1 | import Tkinter 2 | 3 | root = Tkinter.Tk() 4 | 5 | 6 | root.mainloop() 7 | -------------------------------------------------------------------------------- /pingo/rpi/__init__.py: -------------------------------------------------------------------------------- 1 | from rpi import RaspberryPi # noqa 2 | from rpi import RaspberryPiBPlus # noqa 3 | from rpi import RaspberryPi2B # noqa 4 | from grove import GrovePi # noqa 5 | -------------------------------------------------------------------------------- /pingo/rpi/grove.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | grovepi = None 4 | 5 | 6 | class GrovePi(pingo.Board, pingo.AnalogInputCapable, pingo.PwmOutputCapable): 7 | 8 | def __init__(self): 9 | global grovepi 10 | try: 11 | import grovepi as grovepi # noqa 12 | except ImportError: 13 | raise ImportError('pingo.rpi.GrovePi requires grovepi installed') 14 | 15 | super(GrovePi, self).__init__() 16 | 17 | # location: gpio_id 18 | self.ANALOG_PINS = {'A0': 14, 'A1': 15, 'A2': 16, 'A3': 17} 19 | 20 | self.PIN_MODES = { 21 | pingo.IN: 'INPUT', 22 | pingo.OUT: 'OUTPUT' 23 | } 24 | 25 | self.PIN_STATES = { 26 | pingo.HIGH: 1, 27 | pingo.LOW: 0 28 | } 29 | 30 | pwm_pins = [3, 5, 6] 31 | digital_pins = [0, 1, 2, 4, 7, 8, 9] 32 | 33 | self._add_pins( 34 | [pingo.PwmPin(self, location) 35 | for location in pwm_pins] + 36 | 37 | [pingo.DigitalPin(self, location) 38 | for location in digital_pins] + 39 | 40 | [pingo.AnalogPin(self, location, 10, gpio_id) 41 | for location, gpio_id in self.ANALOG_PINS.items()] 42 | ) 43 | 44 | def _set_digital_mode(self, pin, mode): 45 | grovepi.pinMode(pin.location, self.PIN_MODES[mode]) 46 | 47 | def _set_pin_state(self, pin, state): 48 | grovepi.digitalWrite(pin.location, self.PIN_STATES[state]) 49 | 50 | def _get_pin_state(self, pin): 51 | return pingo.LOW if grovepi.digitalRead(pin.location) == 0 else pingo.HIGH 52 | 53 | def _get_pin_value(self, pin): 54 | return (grovepi.analogRead(self.ANALOG_PINS[pin.location]) / 10.23) 55 | 56 | def _set_analog_mode(self, pin, mode): 57 | grovepi.pinMode(self.ANALOG_PINS[pin.location], 'INPUT') 58 | 59 | def _set_pwm_mode(self, pin, mode): 60 | grovepi.pinMode(pin.location, 'OUTPUT') 61 | 62 | def _set_pwm_frequency(self, pin, value): 63 | raise NotImplementedError 64 | 65 | def _set_pwm_duty_cycle(self, pin, value): 66 | grovepi.analogWrite(pin.location, int(value * 2.55)) 67 | -------------------------------------------------------------------------------- /pingo/rpi/rpi.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | GPIO = None 4 | 5 | 6 | class RaspberryPi(pingo.Board, pingo.PwmOutputCapable): 7 | 8 | # connector_p1_location: gpio_id 9 | PWM_PIN_MAP = { 10 | 3: 2, 11 | 5: 3, 12 | 7: 4, 13 | 8: 14, 14 | 10: 15, 15 | 11: 17, 16 | 12: 18, 17 | 13: 27, 18 | 15: 22, 19 | 16: 23, 20 | 18: 24, 21 | 19: 10, 22 | 21: 9, 23 | 22: 25, 24 | 23: 11, 25 | 24: 8, 26 | 26: 7, 27 | } 28 | 29 | GROUNDS_LIST = [6, 9, 14, 20, 25] 30 | 31 | def __init__(self): 32 | global GPIO 33 | try: 34 | import RPi.GPIO as GPIO 35 | except ImportError: 36 | raise ImportError('pingo.rpi.RaspberryPi requires RPi.GPIO installed') 37 | 38 | super(RaspberryPi, self).__init__() 39 | GPIO.setmode(GPIO.BCM) 40 | GPIO.setwarnings(True) 41 | 42 | pins = [pingo.VccPin(self, 1, 3.3), 43 | pingo.VccPin(self, 2, 5.0), 44 | pingo.VccPin(self, 4, 5.0), 45 | pingo.VccPin(self, 17, 3.3)] 46 | 47 | pins += [pingo.GroundPin(self, n) for n in self.GROUNDS_LIST] 48 | 49 | pins += [pingo.PwmPin(self, location, gpio_id) 50 | for location, gpio_id in self.PWM_PIN_MAP.items()] 51 | 52 | self._add_pins(pins) 53 | self._rpi_pwm = {} 54 | 55 | def cleanup(self): 56 | for pin in self.pins.values(): 57 | if hasattr(pin, 'enabled') and pin.enabled: 58 | GPIO.cleanup(int(pin.gpio_id)) 59 | pin.enabled = False 60 | 61 | def _set_digital_mode(self, pin, mode): 62 | # Cleans previous PWM mode 63 | if pin.mode == pingo.PWM: 64 | if int(pin.location) in self._rpi_pwm: 65 | self._rpi_pwm[int(pin.location)].stop() 66 | del self._rpi_pwm[int(pin.location)] 67 | # Sets up new modes 68 | if mode == pingo.IN: 69 | GPIO.setup(int(pin.gpio_id), GPIO.IN, pull_up_down=GPIO.PUD_DOWN) 70 | elif mode == pingo.OUT: 71 | GPIO.setup(int(pin.gpio_id), GPIO.OUT) 72 | 73 | def _set_pwm_mode(self, pin, mode): 74 | if pin.mode != pingo.PWM: 75 | GPIO.setup(int(pin.gpio_id), GPIO.OUT) 76 | pwm_ctrl = GPIO.PWM(int(pin.gpio_id), 60.) # TODO set frequency 77 | self._rpi_pwm[int(pin.location)] = pwm_ctrl 78 | pwm_ctrl.start(0.0) # TODO set DutyCycle 79 | 80 | def _set_pin_state(self, pin, state): 81 | rpi_state = GPIO.HIGH if state == pingo.HIGH else GPIO.LOW 82 | GPIO.output(int(pin.gpio_id), rpi_state) 83 | 84 | def _get_pin_state(self, pin): 85 | return pingo.HIGH if GPIO.input(int(pin.gpio_id)) else pingo.LOW 86 | 87 | def _set_pwm_duty_cycle(self, pin, value): 88 | self._rpi_pwm[int(pin.location)].ChangeDutyCycle(value) 89 | 90 | def _set_pwm_frequency(self, pin, value): 91 | self._rpi_pwm[int(pin.location)].ChangeFrequency(value) 92 | 93 | 94 | class RaspberryPiBPlus(RaspberryPi): 95 | 96 | # header_j8_location: gpio_id 97 | PWM_PIN_MAP = { 98 | # 1: 3.3v DC Power 99 | # 2: 5v DC Power 100 | 3: 2, # SDA1, I2C 101 | # 4: 5v DC Power 102 | 5: 3, # SCL1, I2C 103 | # 6: Ground 104 | 7: 4, # GPIO_GCLK 105 | 8: 14, # TXD0 106 | # 9: Ground 107 | 10: 15, # RXD0 108 | 11: 17, # GPIO_GEN0 109 | 12: 18, # GPIO_GEN1 110 | 13: 27, # GPIO_GEN2 111 | # 14: Ground 112 | 15: 22, # GPIO_GEN3 113 | 16: 23, # GPIO_GEN4 114 | # 17: 3.3v DC Power 115 | 18: 24, # GPIO_GEN5 116 | 19: 10, # SPI_MOSI 117 | # 20: Ground 118 | 21: 9, # SPI_MOSO 119 | 22: 25, # GPIO_GEN6 120 | 23: 11, # SPI_CLK 121 | 24: 8, # SPI_CE0_N 122 | # 25: Ground 123 | 26: 7, # SPI_CE1_N 124 | # 27: ID_SD (I2C ID EEPROM) 125 | # 28: ID_SC (I2C ID EEPROM) 126 | 29: 5, 127 | # 30: Ground 128 | 31: 6, 129 | 32: 12, 130 | 33: 13, 131 | # 34: Ground 132 | 35: 19, 133 | 36: 16, 134 | 37: 26, 135 | 38: 20, 136 | # 39: Ground 137 | 40: 21, 138 | } 139 | 140 | GROUNDS_LIST = [6, 9, 14, 20, 25, 30, 34, 39] 141 | 142 | 143 | class RaspberryPi2B(RaspberryPiBPlus): 144 | """TODO: for now, this works""" 145 | -------------------------------------------------------------------------------- /pingo/rpi/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/rpi/tests/__init__.py -------------------------------------------------------------------------------- /pingo/rpi/tests/rpi.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Basic usage 3 | ============= 4 | 5 | Turn on a led for 1s 6 | -------------------- 7 | 8 | :: 9 | 10 | >>> from time import sleep 11 | >>> import pingo 12 | >>> 13 | >>> board = pingo.rpi.RaspberryPi() 14 | >>> board.pins[11] 15 | 16 | >>> 17 | >>> led_pin = board.pins[11] 18 | >>> led_pin.set_mode(pingo.OUT) 19 | >>> led_pin.on() 20 | >>> led_pin.state 21 | 1 22 | >>> sleep(1) # 1 second 23 | >>> led_pin.off() 24 | >>> led_pin.state 25 | 0 26 | >>> board.cleanup() 27 | 28 | Builds the correct GPIO Device 29 | ------------------------------ 30 | 31 | :: 32 | 33 | >>> import pingo 34 | >>> board2 = pingo.rpi.RaspberryPi() 35 | >>> pin = board2.pins[11] 36 | >>> board2._render_path(pin, 'direction') 37 | '/sys/class/gpio/gpio17/direction' 38 | >>> board2._render_path(pin, 'value') 39 | '/sys/class/gpio/gpio17/value' 40 | 41 | -------------------------------------------------------------------------------- /pingo/rpi/tests/test_rpi.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | from pingo.test import level0 5 | from pingo.detect import has_module, check_board 6 | 7 | running_on_raspberry = check_board(pingo.rpi.RaspberryPi) 8 | 9 | 10 | class RaspberryTest(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.board = pingo.rpi.RaspberryPi() 14 | self.vdd_pin_number = 2 15 | self.digital_output_pin_number = 13 16 | self.digital_input_pin_number = 26 17 | self.total_pins = 26 18 | 19 | def tearDown(self): 20 | self.board.cleanup() 21 | 22 | 23 | @unittest.skipIf(not running_on_raspberry, "RaspberryPi not detected") 24 | @unittest.skipIf( 25 | not has_module('RPi'), "pingo.rpi requires RPi.GPIO installed") 26 | class RaspberryBasics(RaspberryTest, level0.BoardBasics): 27 | pass 28 | 29 | 30 | @unittest.skipIf(not running_on_raspberry, "RaspberryPi not detected") 31 | @unittest.skipIf( 32 | not has_module('RPi'), "pingo.rpi requires RPi.GPIO installed") 33 | class RaspberryExceptions(RaspberryTest, level0.BoardExceptions): 34 | pass 35 | 36 | 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /pingo/test/__init__.py: -------------------------------------------------------------------------------- 1 | import level0 # noqa 2 | import level1 # noqa 3 | import level2 # noqa 4 | -------------------------------------------------------------------------------- /pingo/test/level0/__init__.py: -------------------------------------------------------------------------------- 1 | from cases import BoardBasics # noqa 2 | from cases import BoardExceptions # noqa 3 | -------------------------------------------------------------------------------- /pingo/test/level0/cases.py: -------------------------------------------------------------------------------- 1 | import time 2 | import unittest 3 | 4 | import pingo 5 | 6 | ''' 7 | In order to use this set of cases, it is necessary to set 8 | the following attributes on your TestCase setUp: 9 | self.vdd_pin_number = 2 10 | self.digital_output_pin_number = 13 11 | self.digital_input_pin_number = 8 12 | self.total_pins = 26 13 | 14 | AND the VDD pin must be connected to the digital_input_pin_number 15 | ''' 16 | 17 | 18 | class BoardBasics(object): 19 | 20 | def test_list_pins(self): 21 | vdd_pin = self.board.pins[self.vdd_pin_number] 22 | assert isinstance(vdd_pin, pingo.VccPin) 23 | 24 | pin = self.board.pins[self.digital_output_pin_number] 25 | assert isinstance(pin, pingo.DigitalPin) 26 | 27 | assert len(self.board.pins) == self.total_pins 28 | 29 | def test_led(self): 30 | pin = self.board.pins[self.digital_output_pin_number] 31 | pin.mode = pingo.OUT 32 | pin.high() 33 | 34 | def test_filter(self): 35 | pins_subset = self.board.filter_pins(pingo.DigitalPin) 36 | assert all(isinstance(pin, pingo.DigitalPin) for pin in pins_subset) 37 | 38 | other_pins = set(self.board.pins.values()) - set(pins_subset) 39 | assert not any(isinstance(pin, pingo.DigitalPin) for pin in other_pins) 40 | 41 | @unittest.skip("Not automatic enough.") 42 | def test_button(self): 43 | pin = self.board.pins[self.digital_input_pin_number] 44 | pin.mode = pingo.IN 45 | output = pingo.LOW 46 | t0 = time.time() 47 | delay = 5 48 | 49 | while output == pingo.LOW: 50 | output = pin.state 51 | if time.time() - t0 > delay: 52 | break 53 | 54 | # TODO: show message on fail 55 | # msg = 'The button must be pressed in %ss for this test to pass' % delay 56 | assert output == pingo.HIGH 57 | 58 | def test_jumpwire(self): 59 | ''' Wire this DigitalPin directly into VDD ''' 60 | pin = self.board.pins[self.digital_input_pin_number] 61 | pin.mode = pingo.IN 62 | output = pin.state 63 | 64 | assert output == pingo.HIGH 65 | 66 | def test_toggle(self): 67 | pin = self.board.pins[self.digital_input_pin_number] 68 | pin.mode = pingo.OUT 69 | pin.high() 70 | pin.toggle() 71 | 72 | assert pin.state == pingo.LOW 73 | 74 | def test_digital_pins(self): 75 | digital_pins = self.board.digital_pins 76 | # TODO: 77 | # assert len(digital_pins) == self.len_digital_pins 78 | assert len(digital_pins) > 0 79 | 80 | def test_select(self): 81 | selected_pins = self.board.select_pins([ 82 | self.digital_input_pin_number, 83 | self.digital_output_pin_number 84 | ]) 85 | assert len(selected_pins) == 2 86 | 87 | 88 | class BoardExceptions(object): 89 | 90 | def test_disabled_pin(self): 91 | pin = self.board.pins[self.digital_output_pin_number] 92 | with self.assertRaises(pingo.WrongPinMode): 93 | pin.high() 94 | 95 | def test_wrong_pin_mode_in(self): 96 | pin = self.board.pins[self.digital_input_pin_number] 97 | pin.mode = pingo.IN 98 | 99 | with self.assertRaises(pingo.WrongPinMode): 100 | pin.high() 101 | 102 | with self.assertRaises(pingo.WrongPinMode): 103 | pin.state = pingo.HIGH 104 | 105 | # def test_wrong_pin_mode_out(self): 106 | # pin = self.board.pins[digital_output_pin_number] 107 | # pin.mode = pingo.OUT 108 | # with self.assertRaises(pingo.WrongPinMode) as cm: 109 | # pin.state 110 | -------------------------------------------------------------------------------- /pingo/test/level1/__init__.py: -------------------------------------------------------------------------------- 1 | from cases import AnalogReadBasics # noqa 2 | from cases import AnalogExceptions # noqa 3 | -------------------------------------------------------------------------------- /pingo/test/level1/cases.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | ''' 4 | In order to use this set of cases, it is necessary to set 5 | the following attributes on your TestCase setUp: 6 | self.analog_input_pin_number = 0 7 | self.expected_analog_input = 1004 8 | self.expected_analog_ratio = 0.98 9 | ''' 10 | 11 | 12 | class AnalogReadBasics(object): 13 | ''' 14 | Wire a 10K Ohm resistence from the AnalogPin to the GND. 15 | Then wire a 200 Ohm from the AnalogPin to the VND. 16 | This schema will provide a read of ~98% 17 | ''' 18 | 19 | def test_200ohmRead(self): 20 | pin = self.board.pins[self.analog_input_pin_number] 21 | pin.mode = pingo.ANALOG 22 | _input = pin.value 23 | # print "Value Read: ", _input 24 | 25 | assert self.expected_analog_input - 3 <= _input <= self.expected_analog_input + 3 26 | 27 | def test_pin_ratio(self): 28 | pin = self.board.pins[self.analog_input_pin_number] 29 | pin.mode = pingo.ANALOG 30 | bits_resolution = (2 ** pin.bits) - 1 31 | _input = pin.ratio(0, bits_resolution, 0.0, 1.0) 32 | # print "Value Read: ", _input 33 | 34 | # Two decimal places check 35 | assert abs(_input - self.expected_analog_ratio) < 10e-1 36 | 37 | 38 | class AnalogExceptions(object): 39 | 40 | def test_wrong_output_mode(self): 41 | pin = self.board.pins[self.analog_input_pin_number] 42 | with self.assertRaises(pingo.ModeNotSuported): 43 | pin.mode = pingo.OUT 44 | -------------------------------------------------------------------------------- /pingo/test/level2/__init__.py: -------------------------------------------------------------------------------- 1 | from cases import PwmBasics # noqa 2 | from cases import PwmExceptions # noqa 3 | -------------------------------------------------------------------------------- /pingo/test/level2/cases.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | 3 | ''' 4 | In order to use this set of cases, it is necessary to set 5 | the following attributes on your TestCase setUp: 6 | self.pwm_pin_number = 0 7 | ''' 8 | 9 | 10 | class PwmBasics(object): 11 | 12 | def test_dot50_duty_cycle(self): 13 | pin = self.board.pins[self.pwm_pin_number] 14 | pin.mode = pingo.PWM 15 | pin.value = 0.50 16 | 17 | _duty_cycle = pin.value 18 | # print "Value Read: ", _duty_cycle 19 | 20 | assert 0.49 <= _duty_cycle <= 0.51 21 | 22 | def test_frequency(self): 23 | pin = self.board.pins[self.pwm_pin_number] 24 | pin.mode = pingo.PWM 25 | pin.frequency = 440 26 | 27 | _frequency = pin.frequency 28 | assert 439 <= _frequency <= 441 29 | 30 | 31 | class PwmExceptions(object): 32 | 33 | def test_wrong_analog_mode(self): 34 | pin = self.board.pins[self.pwm_pin_number] 35 | with self.assertRaises(pingo.ModeNotSuported): 36 | pin.mode = pingo.ANALOG 37 | 38 | def test_wrong_read_state(self): 39 | pin = self.board.pins[self.pwm_pin_number] 40 | pin.mode = pingo.PWM 41 | 42 | with self.assertRaises(pingo.WrongPinMode): 43 | pin.state 44 | 45 | with self.assertRaises(pingo.WrongPinMode): 46 | pin.low() 47 | -------------------------------------------------------------------------------- /pingo/test_utils.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import util 4 | 5 | 6 | class StrKeyDictTest(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.d = util.StrKeyDict( 10 | [('2', 'two'), ('4', 'four')] 11 | ) 12 | 13 | def test_getitem(self): 14 | with self.assertRaises(KeyError): 15 | self.d['1'] 16 | with self.assertRaises(KeyError): 17 | self.d[1] 18 | assert self.d['2'] == 'two' 19 | assert self.d[2] == 'two' 20 | 21 | def test_in(self): 22 | assert 2 in self.d 23 | assert '2' in self.d 24 | 25 | def test_setitem(self): 26 | self.d[0] = 'zero' 27 | assert self.d['0'] == 'zero' 28 | 29 | def test_normalize(self): 30 | self.d['A0'] = 'A-zero' 31 | assert self.d['a0'] == 'A-zero' 32 | 33 | def test_update(self): 34 | self.d.update({6: 'six', '8': 'eight'}) 35 | assert set(self.d.keys()) == set(['2', '4', '6', '8']) 36 | self.d.update([(10, 'ten'), ('12', 'twelve')]) 37 | assert set(self.d.keys()) == set(['2', '4', '6', '8', '10', '12']) 38 | 39 | with self.assertRaises(TypeError): 40 | self.d.update([1, 3, 5]) 41 | 42 | if __name__ == '__main__': 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /pingo/udoo/__init__.py: -------------------------------------------------------------------------------- 1 | from udoo import Udoo # noqa 2 | -------------------------------------------------------------------------------- /pingo/udoo/examples/blink.py: -------------------------------------------------------------------------------- 1 | import pingo 2 | from time import sleep 3 | 4 | board = pingo.udoo.Udoo() 5 | led_pin = board.pins[13] 6 | led_pin.mode = pingo.OUT 7 | 8 | while True: 9 | led_pin.high() 10 | print '%r -> %r' % (led_pin, led_pin.state) 11 | sleep(1) 12 | led_pin.low() 13 | print '%r -> %r' % (led_pin, led_pin.state) 14 | sleep(1) 15 | -------------------------------------------------------------------------------- /pingo/udoo/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pingo-io/pingo-py/5d7081f99ff13973404dc6361560f30ce8f7009c/pingo/udoo/tests/__init__.py -------------------------------------------------------------------------------- /pingo/udoo/tests/digital.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Basic usage 3 | ============= 4 | 5 | Turn on a led for 1s 6 | -------------------- 7 | 8 | :: 9 | 10 | >>> import pingo 11 | >>> from time import sleep 12 | >>> 13 | >>> board = pingo.udoo.Udoo() 14 | >>> board.pins[13] 15 | 16 | >>> 17 | >>> led_pin = board.pins[13] 18 | >>> led_pin.set_mode(pingo.OUT) 19 | >>> led_pin.on() 20 | >>> led_pin.state 21 | 'HIGH' 22 | >>> sleep(1) # 1 second 23 | >>> led_pin.off() 24 | >>> led_pin.state 25 | 'LOW' 26 | -------------------------------------------------------------------------------- /pingo/udoo/tests/test_udoo.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import pingo 4 | from pingo.test import level0 5 | from pingo.detect import check_board 6 | 7 | running_on_udoo = check_board(pingo.udoo.Udoo) 8 | 9 | 10 | class UdooTest(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.board = pingo.udoo.Udoo() 14 | self.vdd_pin_number = 0 15 | self.digital_output_pin_number = 0 16 | self.digital_input_pin_number = 0 17 | self.total_pins = 0 18 | 19 | def tearDown(self): 20 | self.board.cleanup() 21 | 22 | 23 | @unittest.skipIf(not running_on_udoo, 'Udoo not detected') 24 | class UdooBasics(UdooTest, level0.BoardBasics): 25 | pass 26 | 27 | 28 | @unittest.skipIf(not running_on_udoo, 'Udoo not detected') 29 | class UdooExceptions(UdooTest, level0.BoardExceptions): 30 | pass 31 | 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /pingo/udoo/udoo.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | UDOO board 4 | 5 | Reference: 6 | http://www.udoo.org/ProjectsAndTutorials/linux-gpio-manipulation/ 7 | """ 8 | 9 | import os 10 | 11 | from pingo.board import Board, DigitalPin, IN, OUT, HIGH, LOW 12 | 13 | # there are no gaps in the Arduino digital pin numbering 14 | # of the Arduino Due embedded in the Udoo 15 | 16 | # pin_list[physical_arduino_pin] -> logical_gpio_pin 17 | pin_list = [ 18 | 116, 112, 20, 16, 17, 18, 41, 42, 21, 19, 1, 9, 3, 40, # <-- 13 19 | 150, 162, 160, 161, 158, 159, 92, 85, 123, 124, 125, 126, 127, 20 | 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 54, 205, 21 | 32, 35, 34, 33, 101, 144, 145, 89, 105, 104, 57, 56, 55, 88 22 | ] 23 | 24 | # /sys/class/gpio/gpio40/ --> Arduino pin #13 25 | DIGITAL_PINS_PATH = '/sys/class/gpio' 26 | DIGITAL_PIN_MASK = 'gpio%d' 27 | DIGITAL_PIN_STATE_FILENAME = 'value' 28 | DIGITAL_PIN_MODE_FILENAME = 'direction' 29 | DIGITAL_PIN_MODES = {IN: 'in', OUT: 'out'} 30 | DIGITAL_PIN_STATES = {HIGH: '1', LOW: '0'} 31 | 32 | 33 | class Udoo(Board): 34 | 35 | def __init__(self): 36 | Board.__init__(self) 37 | self._add_pins(self._list_pins()) 38 | # FIXME: decide what to do with enabling/disabling pins on board like 39 | # the Udoo where they are always enabled 40 | for pin in (p for p in self.pins.values() if 41 | isinstance(p, DigitalPin)): 42 | pin.enabled = True 43 | self.pin_path_mask = '/sys/class/gpio/gpio%d/' 44 | 45 | def _list_pins(self): 46 | pins = [] 47 | for location, gpio_id in enumerate(pin_list): 48 | path = self._base_pin_path(gpio_id) 49 | if os.path.exists(path): 50 | pins.append(DigitalPin(self, location, gpio_id)) 51 | return pins 52 | 53 | def _base_pin_path(self, gpio_id): 54 | return os.path.join(DIGITAL_PINS_PATH, DIGITAL_PIN_MASK % gpio_id) 55 | 56 | def _pin_mode_filename(self, gpio_id): 57 | path = self._base_pin_path(gpio_id) 58 | return os.path.join(path, DIGITAL_PIN_MODE_FILENAME) 59 | 60 | def _pin_state_filename(self, gpio_id): 61 | return os.path.join( 62 | self._base_pin_path(gpio_id), DIGITAL_PIN_STATE_FILENAME) 63 | 64 | def _set_digital_mode(self, pin, mode): 65 | assert mode in DIGITAL_PIN_MODES, '%r not in %r' % ( 66 | mode, DIGITAL_PIN_MODES) 67 | with open(self._pin_mode_filename(pin.gpio_id), "wb") as fp: 68 | fp.write(DIGITAL_PIN_MODES[mode]) 69 | 70 | def _set_pin_state(self, pin, state): 71 | assert state in DIGITAL_PIN_STATES, '%r not in %r' % ( 72 | state, DIGITAL_PIN_STATES) 73 | with open(self._pin_state_filename(pin.gpio_id), "wb") as fp: 74 | fp.write(DIGITAL_PIN_STATES[state]) 75 | 76 | def cleanup(self): 77 | for pin in (p for p in self.pins.values() 78 | if isinstance(p, DigitalPin)): 79 | if pin.mode == OUT: 80 | pin.low() 81 | pin.mode = IN 82 | -------------------------------------------------------------------------------- /pingo/util.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import UserDict 3 | 4 | 5 | class StrKeyDict(UserDict.UserDict): 6 | """StrKeyDict always converts non-string keys to `str` 7 | 8 | Tests for item retrieval using `d[key]` notation:: 9 | 10 | >>> d = StrKeyDict([('2', 'two'), ('4', 'four')]) 11 | >>> d['2'] 12 | 'two' 13 | >>> d[4] 14 | 'four' 15 | >>> d[1] 16 | Traceback (most recent call last): 17 | ... 18 | KeyError: '1' 19 | 20 | Tests for the `in` operator:: 21 | 22 | >>> 2 in d 23 | True 24 | >>> 1 in d 25 | False 26 | 27 | Test for item assignment using non-string key:: 28 | 29 | >>> d[0] = 'zero' 30 | >>> d['0'] 31 | 'zero' 32 | 33 | Test for case-insensitive retrieval:: 34 | 35 | >>> d['A0'] = 'A-zero' 36 | >>> d['a0'] 37 | 'A-zero' 38 | >>> del d['A0'] 39 | 40 | Tests for update using a `dict` or a sequence of pairs:: 41 | 42 | >>> d.update({6:'six', '8':'eight'}) 43 | >>> sorted(d.keys()) 44 | ['0', '2', '4', '6', '8'] 45 | >>> d.update([(10, 'ten'), ('12', 'twelve')]) 46 | >>> sorted(d.keys()) 47 | ['0', '10', '12', '2', '4', '6', '8'] 48 | >>> d.update([1, 3, 5]) 49 | Traceback (most recent call last): 50 | ... 51 | TypeError: 'int' object is not iterable 52 | 53 | """ 54 | 55 | def normalize(self, key): 56 | return str(key).upper() 57 | 58 | def __missing__(self, key): 59 | if isinstance(key, str) and key == self.normalize(key): 60 | raise KeyError(key) 61 | return self[self.normalize(key)] 62 | 63 | def __contains__(self, key): 64 | return self.normalize(key) in self.data 65 | 66 | def __setitem__(self, key, item): 67 | self.data[self.normalize(key)] = item 68 | 69 | def __iter__(self): 70 | return self.iterkeys() 71 | 72 | def update(self, iterable=None, **kwds): 73 | if iterable is not None: 74 | if isinstance(iterable, collections.Mapping): 75 | pairs = iterable.items() 76 | else: 77 | pairs = ((k, v) for k, v in iterable) 78 | for key, value in pairs: 79 | self[key] = value 80 | if kwds: 81 | self.update(kwds) 82 | 83 | 84 | # Decorator 85 | # def mode_restricted(mode): 86 | # def restriction_decorator(method): 87 | # def method_wrapper(self, *args, **kwargs): 88 | # if self.mode != mode: 89 | # raise WrongPinMode() 90 | # return method(self, *args, **kwargs) 91 | # return method_wrapper 92 | # return mode_restricted 93 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.7.2 2 | MarkupSafe==0.23 3 | Pygments==1.6 4 | Sphinx==1.2.2 5 | cov-core==1.12 6 | coverage==3.7.1 7 | docutils==0.11 8 | execnet==1.2.0 9 | mock==1.0.1 10 | pep8==1.5.6 11 | py==1.4.20 12 | pytest==2.5.2 13 | pytest-cache==1.0 14 | pytest-cov==1.6 15 | pytest-pep8==1.0.6 16 | wsgiref==0.1.2 17 | pyserial==2.7 18 | -------------------------------------------------------------------------------- /scripts/README.rst: -------------------------------------------------------------------------------- 1 | pingo-scripts 2 | ============= 3 | 4 | Here some tools are found. They a designed to help our development. 5 | 6 | If you use ``vim`` text editor, you can use the given ``vimrc`` to replace or 7 | update something in your ``~/.vimrc`` file. Previously we had some problems 8 | with TABs instead of spaces, as well as undesired trailing whitespaces. 9 | 10 | The basic is to use expandtabs ``:set et``, which would avoid TABs by expanding 11 | it to spaces. To ensure 4 spaces while keeping the indentation in new lines, a 12 | it would be ``:set et ai ts=4 sw=4``. The ``vimrc`` here does that and also 13 | several other useful configuration, like highlighting for the undesired 14 | whitespaces. 15 | 16 | In any case, to replace all tabs in a file to spaces using ``vim``, just type 17 | ``:ret``. For breaking text lines up to textwidth (79 chars in ``vimrc``, 18 | following PEP8), type ``3gqj``, replacing the ``3`` by the number of lines that 19 | makes one single paragraph (before the new line breaking). That's useful for 20 | RST files (reStructuredText). 21 | 22 | There is also a `purge_whitespace.sh` script. It fixes the white space issue in 23 | all .py and .sh and files of the project. So when adding new files that my not 24 | the ok about whitespace, run this script. 25 | 26 | 27 | ----------- 28 | Basic usage 29 | ----------- 30 | 31 | .. code-block:: bash 32 | 33 | cp vimrc ~/.vimrc 34 | ./purge_whitespace.sh 35 | 36 | -------------------------------------------------------------------------------- /scripts/flake8_githook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # To use the Git hook on any commit, add a pre-commit file in the 4 | # .git/hooks directory containing this scropt 5 | # Source: 6 | # https://flake8.readthedocs.org/en/2.0/vcs.html 7 | 8 | import sys 9 | from flake8.run import git_hook 10 | 11 | COMPLEXITY = 10 12 | STRICT = False 13 | 14 | if __name__ == '__main__': 15 | sys.exit(git_hook(complexity=COMPLEXITY, strict=STRICT, ignore='E501')) 16 | -------------------------------------------------------------------------------- /scripts/purge_whitespace.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cd .. 4 | find -name '*.py' -print0 | xargs -r0 sed -e 's/[[:blank:]]\+$//' -i 5 | find -name '*.sh' -print0 | xargs -r0 sed -e 's/[[:blank:]]\+$//' -i 6 | 7 | find -name '*.py' -print0 | xargs -r0 sed -e 's/\t\ /g' -i 8 | -------------------------------------------------------------------------------- /scripts/vimrc: -------------------------------------------------------------------------------- 1 | filetype plugin on 2 | filetype indent on 3 | set background=dark 4 | syntax on 5 | 6 | " General configuration 7 | set encoding=utf-8 8 | set grepprg=grep\ -nH\ $* 9 | set showmode number ruler " Aesthetics (numbers around and mode we're in) 10 | set nocompatible nomodeline " Use Vim defaults 11 | set ts=4 sw=4 et ai " Indents with 4 spaces 12 | set history=50 " CLI history 13 | set bs=indent,eol,start " Backspace for lines and indentation 14 | set ww=b,s,<,>,[,] " Arrows & [back]space wordwraps between lines 15 | set tw=79 nowrap " PEP-8 line break without visual wordwrapping 16 | 17 | " Whitespaces 18 | set list lcs=tab:¤·,trail:· " Shows TABs and trailing chars 19 | hi TABs ctermbg=Red ctermfg=LightRed guibg=Red guifg=LightRed term=italic 20 | hi TrSp ctermbg=DarkGray ctermfg=Red guibg=DarkGray guifg=Red term=italic 21 | call matchadd("TABs", "\t") " All TABs but the trailing ones 22 | call matchadd("TrSp", "\\s\\+$") " Trailing TABs and spaces 23 | autocmd BufWritePre * :%s/\s\+$//e " Removes trailing spaces when save buffer 24 | 25 | " Paste mouse selected text with Shift+Insert 26 | if has('gui_running') 27 | map 28 | map! 29 | endif 30 | -------------------------------------------------------------------------------- /scripts/yunserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import random 3 | 4 | from flask import Flask 5 | from flask import make_response, abort 6 | 7 | app = Flask(__name__) 8 | 9 | """ 10 | Arduino Yun REST-API dev-server. 11 | 12 | This server mimics Arduino's Examples > Bridge 13 | http://arduino.cc/en/Tutorial/Bridge 14 | http://arduino.cc/en/Tutorial/Bridge?action=sourceblock&num=13 15 | """ 16 | 17 | 18 | @app.errorhandler(404) 19 | def not_found(error): 20 | return make_response('error', 404) 21 | 22 | 23 | @app.route('/') 24 | def index(): 25 | return "Pingo's Arduino Yun REST-API dev-server." 26 | 27 | 28 | @app.route('/arduino/mode//') 29 | def mode(pin, mode): 30 | if mode not in ['input', 'output'] or pin not in range(14): 31 | abort(404) 32 | umode = mode.upper() 33 | return "Pin D{pin} configured as {umode}!".format(**locals()) 34 | 35 | 36 | @app.route('/arduino/digital/', defaults={'value': None}) 37 | @app.route('/arduino/digital//') 38 | def digital(pin, value): 39 | if value is None: 40 | uvalue = random.choice((0, 1)) 41 | else: 42 | uvalue = 1 if value else 0 43 | return "Pin D{pin} set to {uvalue}".format(**locals()) 44 | 45 | 46 | @app.route('/arduino/analog/', defaults={'value': None}) 47 | @app.route('/arduino/analog//') 48 | def analog(pin, value): 49 | if value is None: 50 | if pin in range(6): 51 | uvalue = random.choice(range(1024)) 52 | rw = "reads" 53 | else: 54 | abort(404) 55 | else: 56 | if pin in [3, 5, 6, 9, 10, 11, 13]: 57 | uvalue = value % 256 58 | rw = "set to" 59 | else: 60 | abort(404) 61 | return "Pin A{pin} {rw} analog {uvalue}".format(**locals()) 62 | 63 | 64 | if __name__ == '__main__': 65 | app.run(debug=True) 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # MIT Licensed. See LICENSE for more information. 4 | """ Pingo setup module """ 5 | 6 | from setuptools import setup, find_packages 7 | from setuptools.command.test import test as TestCommand 8 | import io 9 | import os 10 | import sys 11 | 12 | pkgname = 'pingo' 13 | version = '0.2.0' 14 | 15 | 16 | class PyTest(TestCommand): 17 | def finalize_options(self): 18 | TestCommand.finalize_options(self) 19 | self.test_args = ['pingo', '-vrsx'] 20 | self.test_suite = True 21 | 22 | def run_tests(self): 23 | #import here, cause outside the eggs aren't loaded 24 | import pytest 25 | errno = pytest.main(self.test_args) 26 | sys.exit(errno) 27 | 28 | 29 | install_requires = [ 30 | '', 31 | ] 32 | 33 | tests_require = [ 34 | 'pytest', 35 | ] 36 | 37 | metadata = { 38 | "name": pkgname, 39 | "version": version, 40 | "author": "Pingo Team @ Garoa Hacker Clube", 41 | "author_email": "luciano at sign ramalho within the dot org tld", 42 | "url": "http://github.com/pingo-io/pingo-py", 43 | "description": "Generic API to control boards with programmable IO pins.", 44 | "license": "MIT", 45 | "include_package_data": True, 46 | "install_requires": install_requires, 47 | "tests_require": tests_require, 48 | "cmdclass": {'test': PyTest}, 49 | } 50 | 51 | readme_path = os.path.join(os.path.dirname(__file__), 'README.rst') 52 | 53 | try: 54 | with io.open(readme_path, encoding='utf-8') as readme: 55 | metadata["long_description"] = readme.read() 56 | except IOError: # FIXME: how to reliably read the README.rst file in root installs? 57 | metadata["long_description"] = 'See README.rst' 58 | 59 | metadata["classifiers"] = [ 60 | 'Development Status :: 2 - Pre-Alpha', 61 | 'Environment :: Console', 62 | "Environment :: Handhelds/PDA's", 63 | 'Environment :: Other Environment', 64 | 'Intended Audience :: Developers', 65 | 'Intended Audience :: Education', 66 | 'Intended Audience :: End Users/Desktop', 67 | 'Intended Audience :: Information Technology', 68 | 'Intended Audience :: Manufacturing', 69 | 'Intended Audience :: Other Audience', 70 | 'Intended Audience :: Science/Research', 71 | 'Intended Audience :: Telecommunications Industry', 72 | 'License :: OSI Approved :: MIT License', 73 | 'Natural Language :: English', 74 | 'Operating System :: POSIX :: Linux', 75 | 'Programming Language :: Python', 76 | 'Programming Language :: Python :: 2', 77 | 'Programming Language :: Python :: 2.7', 78 | 'Programming Language :: Python :: 2 :: Only', 79 | 'Programming Language :: Python :: Implementation :: CPython', 80 | 'Topic :: Education', 81 | 'Topic :: Home Automation', 82 | 'Topic :: Internet', 83 | 'Topic :: Other/Nonlisted Topic', 84 | 'Topic :: Software Development', 85 | 'Topic :: Software Development :: Embedded Systems', 86 | 'Topic :: Software Development :: Libraries :: Python Modules', 87 | ] 88 | 89 | metadata["packages"] = find_packages(where='.') 90 | 91 | setup(**metadata) 92 | 93 | # Upload to PyPI: 94 | # python setup.py sdist upload -r PyPI 95 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | norecursedirs = .svn _build tmp* .git 3 | --------------------------------------------------------------------------------