diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6344b2f..2916315 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,47 +1,13 @@ -Thank you for opening an issue on an Adafruit Python library repository. To -improve the speed of resolution please review the following guidelines and -common troubleshooting steps below before creating the issue: +Thank you for opening an issue on the Adafruit BeagleBone Python library repository. -- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use - the forums at http://forums.adafruit.com to ask questions and troubleshoot why - something isn't working as expected. In many cases the problem is a common issue - that you will more quickly receive help from the forum community. GitHub issues - are meant for known defects in the code. If you don't know if there is a defect - in the code then start with troubleshooting on the forum first. -- **If following a tutorial or guide be sure you didn't miss a step.** Carefully - check all of the steps and commands to run have been followed. Consult the - forum if you're unsure or have questions about steps in a guide/tutorial. +In order to understand your system configuration better, please run: +``` +sudo /opt/scripts/tools/version.sh +``` -- **For Python/Raspberry Pi projects check these very common issues to ensure they don't apply**: +and paste the output in a reply. - - If you are receiving an **ImportError: No module named...** error then a - library the code depends on is not installed. Check the tutorial/guide or - README to ensure you have installed the necessary libraries. Usually the - missing library can be installed with the `pip` tool, but check the tutorial/guide - for the exact command. +This script should be present for any image downloaded from: +https://beagleboard.org/ or https://rcn-ee.com/ - - **Be sure you are supplying adequate power to the board.** Check the specs of - your board and power in an external power supply. In many cases just - plugging a board into your computer is not enough to power it and other - peripherals. - - - **Double check all soldering joints and connections.** Flakey connections - cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. - -If you're sure this issue is a defect in the code and checked the steps above -please fill in the following fields to provide enough troubleshooting information. -You may delete the guideline and text above to just leave the following details: - -- Platform/operating system (i.e. Raspberry Pi with Raspbian operating system, - Windows 32-bit, Windows 64-bit, Mac OSX 64-bit, etc.): **INSERT PLATFORM/OPERATING - SYSTEM HERE** - -- Python version (run `python -version` or `python3 -version`): **INSERT PYTHON - VERSION HERE** - -- Error message you are receiving, including any Python exception traces: **INSERT - ERROR MESAGE/EXCEPTION TRACES HERE*** - -- List the steps to reproduce the problem below (if possible attach code or commands - to run): **LIST REPRO STEPS BELOW** diff --git a/CHANGELOG.md b/CHANGELOG.md index a3895c2..4f9325e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ +1.1.2 +--- +Daniel Nguyen (2): + Update common.c + Update common.c + +Drew Fustini (28): + upload to PyPI againt to resolve #293 + do not set pinmux on the beaglebone blue + remove deug output + Fix dead link to bone.js #296 + force Encoder period to be an integer #299 + Ignore new compiler warnings in gcc 8.2.0 + Update setup.py + do not set pin mode for built-in USRn LEDs + Change name of P1_3 to match bone.js + Fix warning about casting incompatible function types #308 + Fix warning print format strings being truncated #308 + Fix warning about casting incompatible function types #308 + Fix warnings on format truncation and sizeof in strncpy #308 + Fix warning about casting incompatible function types #308 + Update travis config to specify Python 3.6 + Update tox.ini to Python 3.6 + Merge pull request #321 from adafruit/issue308 + Update ISSUE_TEMPLATE.md + Update README.md + Update README.md + Merge pull request #327 from zer0cod3r/master + Merge pull request #337 from SamPovilus/docfix + Update README.md + Update README.md + Update README.md + remove -Werror from CFLAGS + Remove suppression of gcc warnings in CFLAGS #336 + Update version in setup.py to v1.2 + +Sam Povilus (1): + fixing document locaiton and version as current location dosn't load + 1.1.1 --- Attempt upload to PyPI again to avoid diff --git a/README.md b/README.md index cc179c2..fba6863 100644 --- a/README.md +++ b/README.md @@ -7,36 +7,37 @@ Adafruit BBIO is an API to enable [GPIO](README.md#gpio-setup), [PWM](README.md#pwm), [ADC](README.md#adc), [UART](README.md#uart), [SPI](README.md#spi) and [eQEP](README.md#eqep) (Quadrature Encoder) hardware access from Python applications running on the Beaglebone. * It is recommended to use an [official BeagleBoard.org Debian image](https://beagleboard.org/latest-images) - * **Currently recommended image: [Debian 9.4 "Stretch" IoT (2018-06-17)](http://debian.beagleboard.org/images/bone-debian-9.4-iot-armhf-2018-06-17-4gb.img.xz)** - * Install [Linux kernel](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Kernel_Options) [4.14.x](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Mainline_.284.14.x_lts.29) to enable [non-root control of GPIO](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/80-gpio-noroot.rules) and [PWM](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/81-pwm-noroot.rules) [_(commit)_](https://github.com/adafruit/adafruit-beaglebone-io-python/commit/b65cbf8e41b444bad7c4ef6cfd4f88a30210fd78) + * **Currently recommended image: [Debian 10.3 "Buster" IoT (2020-04-06)](http://beagleboard.org/latest-images)** _(default kernel is 4.19.x-ti)_ -* Adafruit_BBIO supports Linux kernels 3.8 through 4.14 +* Adafruit_BBIO supports Linux kernels 3.8 through 4.19 * New versions of Adafruit_BBIO may break backwards compatibility. Please read the [changelog](CHANGELOG.md). +* It is recommended to use Python 3 + ## Installation on Debian -Easiest: +Note: Follow the instructions on BeagleBoard.org to [get connected to the Internet](https://beagleboard.org/upgrade#connect) + +**Easiest:** ``` -sudo ntpdate pool.ntp.org sudo apt-get update -sudo apt-get install build-essential python-dev python-pip -y -sudo pip install Adafruit_BBIO +sudo apt-get install build-essential python3-dev python3-pip -y +sudo pip3 install Adafruit_BBIO ``` -Manual: +**Manual:** ``` -sudo ntpdate pool.ntp.org sudo apt-get update -sudo apt-get install build-essential python-dev python-pip -y +sudo apt-get install build-essential python3-dev python3-pip -y git clone git://github.com/adafruit/adafruit-beaglebone-io-python.git cd adafruit-beaglebone-io-python -sudo python setup.py install +sudo python3 setup.py install ``` Upgrade Adafruit_BBIO to latest version on [PyPI](https://pypi.python.org/pypi/Adafruit_BBIO): ``` -sudo pip install --upgrade Adafruit_BBIO +sudo pip3 install --upgrade Adafruit_BBIO ``` ## Usage @@ -265,13 +266,25 @@ To use the enhanced Quadrature Encoder Pulse (eQEP) module, please refer to the Install py.test to run the tests. You'll also need the python compiler package for pytest: ``` -sudo pip install pytest +sudo pip3 install pytest ``` Execute the following in the root of the project: ``` -sudo pytest +pytest +``` +NOTE: `sudo` should not be required as udev configures group ownership and permission for [GPIO](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/80-gpio-noroot.rules) and [PWM](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/81-pwm-noroot.rules) + +## Reporting issues + +When reporting issues, plesae run the following script which will print the system configuration: +``` +sudo /opt/scripts/tools/version.sh ``` -NOTE: `sudo` should not be required when running [Debian 9.2 "Stretch" iot (2017-10-29)](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#microSD.2FStandalone:_.28stretch-iot.29_.28All_BeagleBone_Variants_.26_PocketBeagle.29) with [Linux kernel](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Kernel_Options) [4.14.x](https://elinux.org/Beagleboard:BeagleBoneBlack_Debian#Mainline_.284.14.x_lts.29) as udev configures group ownership and permission for [GPIO](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/80-gpio-noroot.rules) and [PWM](https://github.com/rcn-ee/repos/blob/master/bb-customizations/suite/stretch/debian/81-pwm-noroot.rules) +and paste the output in a reply. + +This script should be present for any Debian or Ubunut image downloaded from: +https://beagleboard.org/ or https://rcn-ee.com/ + ## Credits diff --git a/setup.py b/setup.py index fd63bd8..a6f5891 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ else: kernel41 = None -CFLAGS = ['-Wall', '-Werror', '-Wextra', '-Wno-missing-field-initializers', '-Wno-strict-aliasing' ] +CFLAGS = ['-Wall'] classifiers = ['Development Status :: 3 - Alpha', 'Operating System :: POSIX :: Linux', @@ -40,11 +40,12 @@ } setup(name = 'Adafruit_BBIO', - version = '1.1.1', + version = '1.2.0', author = 'Justin Cooper', author_email = 'justin@adafruit.com', description = 'A module to control BeagleBone IO channels', long_description = open_as_utf8('README.md').read() + open_as_utf8('CHANGELOG.md').read(), + long_description_content_type = 'text/markdown', license = 'MIT', keywords = 'Adafruit BeagleBone IO GPIO PWM ADC', url = 'https://github.com/adafruit/adafruit-beaglebone-io-python/', diff --git a/source/c_pwm.c b/source/c_pwm.c index f03e1bd..a5ef5ec 100644 --- a/source/c_pwm.c +++ b/source/c_pwm.c @@ -94,6 +94,14 @@ void export_pwm(struct pwm_exp *new_pwm) } } +int is_dmtimer_pin(pwm_t *p) { + char temp[6]; + strncpy(temp, p->module, 5); + temp[5] = '\0'; + + return (strcmp(temp, "timer") == 0); +} + BBIO_err initialize_pwm(void) { #ifdef BBBVERSION41 // don't load overlay in 4.1+ @@ -375,16 +383,27 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri return err; } - err = build_path(ocp_dir, p->chip, pwm_dev_path, sizeof(pwm_dev_path)); - if (err != BBIO_OK) { - syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_dev_path: %i", key, err); - return err; - } + int dmtimer_pin = is_dmtimer_pin(p); - err = build_path(pwm_dev_path, p->addr, pwm_addr_path, sizeof(pwm_addr_path)); - if (err != BBIO_OK) { - syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_addr_path: %i", key, err); - return err; + if(!dmtimer_pin) { + err = build_path(ocp_dir, p->chip, pwm_dev_path, sizeof(pwm_dev_path)); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_dev_path: %i", key, err); + return err; + } + + err = build_path(pwm_dev_path, p->addr, pwm_addr_path, sizeof(pwm_addr_path)); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_addr_path: %i", key, err); + return err; + } + } + else { + err = build_path("/sys/devices/platform", p->addr, pwm_addr_path, sizeof(pwm_addr_path)); + if (err != BBIO_OK) { + syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_addr_path, are you sure you've loaded the correct dmtimer device tree overlay?: %i", key, err); + return err; + } } err = build_path(pwm_addr_path, "pwm/pwmchip", pwm_chip_path, sizeof(pwm_chip_path)); @@ -397,10 +416,10 @@ BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attri syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, pwm_path: %s", key, pwm_path); //pwm with udev patch - snprintf(pwm_path_udev, sizeof(pwm_path_udev), "%s/pwm-%c:%d", pwm_chip_path, pwm_path[66], p->index); + snprintf(pwm_path_udev, sizeof(pwm_path_udev), "%s/pwm-%c:%d", pwm_chip_path, dmtimer_pin ? pwm_path[47] : pwm_path[66], p->index); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, pwm_path_udev: %s", key, pwm_path_udev); //ecap output with udev patch - snprintf(ecap_path_udev, sizeof(ecap_path_udev), "%s/pwm-%c:%d", pwm_chip_path, pwm_path[66], p->index); + snprintf(ecap_path_udev, sizeof(ecap_path_udev), "%s/pwm-%c:%d", pwm_chip_path, dmtimer_pin ? pwm_path[47] : pwm_path[67], p->index); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, ecap_path_udev: %s", key, ecap_path_udev); // Export PWM if hasn't already been diff --git a/source/common.c b/source/common.c index cd03bd9..f911d49 100644 --- a/source/common.c +++ b/source/common.c @@ -81,10 +81,10 @@ pins_t table[] = { { "GPIO1_7", "P8_4", 39, -1, -1}, { "GPIO1_2", "P8_5", 34, -1, -1}, { "GPIO1_3", "P8_6", 35, -1, -1}, - { "TIMER4", "P8_7", 66, -1, -1}, - { "TIMER7", "P8_8", 67, -1, -1}, - { "TIMER5", "P8_9", 69, -1, -1}, - { "TIMER6", "P8_10", 68, -1, -1}, + { "TIMER4", "P8_7", 66, 2, -1}, + { "TIMER7", "P8_8", 67, 2, -1}, + { "TIMER5", "P8_9", 69, 2, -1}, + { "TIMER6", "P8_10", 68, 2, -1}, { "GPIO1_13", "P8_11", 45, -1, -1}, { "GPIO1_12", "P8_12", 44, -1, -1}, { "EHRPWM2B", "P8_13", 23, 4, -1}, @@ -206,15 +206,15 @@ pins_t table[] = { { "VREFN", "P1_17", 0, -1, -1}, { "VREFP", "P1_18", 0, -1, -1}, { "AIN0", "P1_19", 0, -1, 0}, - { "GPIO0_20", "P1_20", 20, -1, -1}, + { "GPIO0_20", "P1_20", 20, 4, -1}, { "AIN1", "P1_21", 0, -1, 1}, { "GND", "P1_22", 0, -1, -1}, { "AIN2", "P1_23", 0, -1, 2}, { "VOUT-5V", "P1_24", 0, -1, -1}, { "AIN3", "P1_25", 0, -1, 3}, - { "I2C2_SDA", "P1_26", 12, -1, -1}, + { "I2C2_SDA", "P1_26", 12, 1, -1}, { "AIN4", "P1_27", 0, -1, 4}, - { "I2C2_SCL", "P1_28", 13, -1, -1}, + { "I2C2_SCL", "P1_28", 13, 1, -1}, { "GPIO3_21", "P1_29", 117, -1, -1}, { "UART0_TXD", "P1_30", 43, -1, -1}, { "GPIO3_18", "P1_31", 114, -1, -1}, @@ -249,11 +249,11 @@ pins_t table[] = { { "GPIO1_12", "P2_24", 44, -1, -1}, { "SPI1_CS0", "P2_25", 41, -1, -1}, { "RESET#", "P2_26", 0, -1, -1}, - { "SPI1_D0", "P2_27", 40, -1, -1}, + { "SPI1_D0", "P2_27", 40, 5, -1}, { "GPIO3_20", "P2_28", 116, -1, -1}, { "SPI1_SCLK", "P2_29", 7, -1, -1}, { "GPIO3_17", "P2_30", 113, -1, -1}, - { "SPI1_CS1", "P2_31", 19, -1, -1}, + { "SPI1_CS1", "P2_31", 19, 2, -1}, { "GPIO3_16", "P2_32", 112, -1, -1}, { "GPIO1_13", "P2_33", 45, -1, -1}, { "GPIO3_19", "P2_34", 115, -1, -1}, @@ -271,6 +271,8 @@ pins_t table[] = { // P2_09 uart1_txd // P1_08 uart2_rxd // P1_10 uart2_txd +// P2_05 uart4_rxd +// P2_07 uart4_txd uart_t uart_table[] = { { "UART1", "/dev/ttyO1", "ADAFRUIT-UART1", "P9_26", "P9_24"}, @@ -281,12 +283,13 @@ uart_t uart_table[] = { { "PB-UART0", "/dev/ttyO0", "ADAFRUIT-UART0", "P1_30", "P1_32"}, { "PB-UART1", "/dev/ttyO1", "ADAFRUIT-UART1", "P2_11", "P2_09"}, { "PB-UART2", "/dev/ttyO2", "ADAFRUIT-UART2", "P1_08", "P1_10"}, + { "PB-UART4", "/dev/ttyO4", "ADAFRUIT-UART4", "P2_05", "P2_07"}, { NULL, NULL, 0, 0, 0 } }; // Copied from https://github.com/jadonk/bonescript/blob/master/src/bone.js // See am335x technical manual, p. 183, for more info: -// http://www.ti.com/lit/ug/spruh73n/spruh73n.pdf +// https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf pwm_t pwm_table[] = { { "ehrpwm2", 6, 1, 4, "ehrpwm.2:1", "EHRPWM2B", "48304000", "48304200", "P8_13"}, { "ehrpwm2", 5, 0, 4, "ehrpwm.2:0", "EHRPWM2A", "48304000", "48304200", "P8_19"}, @@ -302,12 +305,21 @@ pwm_t pwm_table[] = { { "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P9_29"}, { "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P9_31"}, { "ecap0", 2, 0, 0, "ecap.0", "ECAPPWM0", "48300000", "48300100", "P9_42"}, + { "timer4", 0, 0, 2, "", "", "", "dmtimer-pwm-4", "P8_7" }, + { "timer7", 0, 0, 2, "", "", "", "dmtimer-pwm-7", "P8_8" }, + { "timer5", 0, 0, 2, "", "", "", "dmtimer-pwm-5", "P8_9" }, + { "timer6", 0, 0, 2, "", "", "", "dmtimer-pwm-6", "P8_10" }, { "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P1_8"}, { "ehrpwm0", 0, 0, 1, "ehrpwm.0:0", "EHRPWM0A", "48300000", "48300200", "P1_36"}, { "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P1_10"}, { "ehrpwm0", 1, 1, 1, "ehrpwm.0:1", "EHRPWM0B", "48300000", "48300200", "P1_33"}, { "ehrpwm1", 3, 0, 6, "ehrpwm.1:0", "EHRPWM1A", "48302000", "48302200", "P2_1"}, { "ehrpwm2", 6, 1, 3, "ehrpwm.2:1", "EHRPWM2B", "48304000", "48304200", "P2_3"}, + { "timer7", 0, 0, 4, "", "", "", "dmtimer-pwm-7", "P1_20" }, + { "timer6", 0, 0, 1, "", "", "", "dmtimer-pwm-6", "P1_26" }, + { "timer5", 0, 0, 1, "", "", "", "dmtimer-pwm-5", "P1_28" }, + { "timer7", 0, 0, 5, "", "", "", "dmtimer-pwm-7", "P2_27" }, + { "timer4", 0, 0, 2, "", "", "", "dmtimer-pwm-4", "P2_31" }, { NULL, 0, 0, 0, NULL, NULL, NULL, NULL, NULL } }; diff --git a/source/constants.c b/source/constants.c index 5e284c8..23753c6 100644 --- a/source/constants.c +++ b/source/constants.c @@ -35,39 +35,41 @@ SOFTWARE. void define_constants(PyObject *module) { - high = Py_BuildValue("i", HIGH); - PyModule_AddObject(module, "HIGH", high); + PyObject *object; - low = Py_BuildValue("i", LOW); - PyModule_AddObject(module, "LOW", low); + object = Py_BuildValue("i", HIGH); + PyModule_AddObject(module, "HIGH", object); - output = Py_BuildValue("i", OUTPUT); - PyModule_AddObject(module, "OUT", output); + object = Py_BuildValue("i", LOW); + PyModule_AddObject(module, "LOW", object); - input = Py_BuildValue("i", INPUT); - PyModule_AddObject(module, "IN", input); + object = Py_BuildValue("i", OUTPUT); + PyModule_AddObject(module, "OUT", object); - alt0 = Py_BuildValue("i", ALT0); - PyModule_AddObject(module, "ALT0", alt0); + object = Py_BuildValue("i", INPUT); + PyModule_AddObject(module, "IN", object); - pud_off = Py_BuildValue("i", PUD_OFF); - PyModule_AddObject(module, "PUD_OFF", pud_off); + object = Py_BuildValue("i", ALT0); + PyModule_AddObject(module, "ALT0", object); - pud_up = Py_BuildValue("i", PUD_UP); - PyModule_AddObject(module, "PUD_UP", pud_up); + object = Py_BuildValue("i", PUD_OFF); + PyModule_AddObject(module, "PUD_OFF", object); - pud_down = Py_BuildValue("i", PUD_DOWN); - PyModule_AddObject(module, "PUD_DOWN", pud_down); + object = Py_BuildValue("i", PUD_UP); + PyModule_AddObject(module, "PUD_UP", object); + + object = Py_BuildValue("i", PUD_DOWN); + PyModule_AddObject(module, "PUD_DOWN", object); - rising_edge = Py_BuildValue("i", RISING_EDGE); - PyModule_AddObject(module, "RISING", rising_edge); + object = Py_BuildValue("i", RISING_EDGE); + PyModule_AddObject(module, "RISING", object); - falling_edge = Py_BuildValue("i", FALLING_EDGE); - PyModule_AddObject(module, "FALLING", falling_edge); + object = Py_BuildValue("i", FALLING_EDGE); + PyModule_AddObject(module, "FALLING", object); - both_edge = Py_BuildValue("i", BOTH_EDGE); - PyModule_AddObject(module, "BOTH", both_edge); + object = Py_BuildValue("i", BOTH_EDGE); + PyModule_AddObject(module, "BOTH", object); - version = Py_BuildValue("s", "0.0.20"); - PyModule_AddObject(module, "VERSION", version); + object = Py_BuildValue("s", "0.0.20"); + PyModule_AddObject(module, "VERSION", object); } diff --git a/source/constants.h b/source/constants.h index 0f6fdf5..82ebe5e 100644 --- a/source/constants.h +++ b/source/constants.h @@ -1,19 +1,6 @@ #ifndef CONSTANTS_H #define CONSTANTS_H -PyObject *high; -PyObject *low; -PyObject *input; -PyObject *output; -PyObject *alt0; -PyObject *pud_off; -PyObject *pud_up; -PyObject *pud_down; -PyObject *rising_edge; -PyObject *falling_edge; -PyObject *both_edge; -PyObject *version; - void define_constants(PyObject *module); #endif diff --git a/source/py_gpio.c b/source/py_gpio.c index 2721cca..99cb0e7 100644 --- a/source/py_gpio.c +++ b/source/py_gpio.c @@ -599,8 +599,10 @@ PyMODINIT_FUNC initGPIO(void) initlog(LOG_INFO, NULL, BBIO_LOG_OPTION); +#if PY_VERSION_HEX < 0x03090000 if (!PyEval_ThreadsInitialized()) PyEval_InitThreads(); +#endif if (Py_AtExit(event_cleanup) != 0) {