/* * Author: Jon Trulson * Abhishek Malik * Copyright (c) 2016 Intel Corporation. * * Thanks to Adafruit for supplying a google translated version of the * Chinese datasheet and some clues in their code. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "urm37.h" #include "upm_utilities.h" #define URM37_MAX_DATA_LEN 4 #define URM37_WAIT_TIMEOUT 1000 #define URM37_MAX_RETRIES 10 urm37_context urm37_init(int a_pin, int reset_pin, int trigger_pin, float a_ref, int uart_bus, bool analog_mode) { // make sure MRAA is initialized int mraa_rv; if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) { printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); return NULL; } urm37_context dev = (urm37_context)malloc(sizeof(struct _urm37_context)); if (!dev) return NULL; // clear out context memset((void *)dev, 0, sizeof(struct _urm37_context)); // NULL out MRAA contexts for now (redundant with memset I know, but...) dev->aio = NULL; dev->gpio_reset = NULL; dev->gpio_trigger = NULL; dev->uart = NULL; dev->a_res = 0; dev->a_ref = a_ref; // set the mode dev->is_analog_mode = analog_mode; // initialize the MRAA contexts (only what we need) // analog only if (dev->is_analog_mode) { if (!(dev->aio = mraa_aio_init(a_pin))) { printf("%s: mraa_aio_init() failed.\n", __FUNCTION__); urm37_close(dev); return NULL; } // ADC resolution dev->a_res = (float)(1 << mraa_aio_get_bit(dev->aio)) - 1; if (!(dev->gpio_trigger = mraa_gpio_init(trigger_pin))) { printf("%s: mraa_gpio_init(trigger) failed.\n", __FUNCTION__); urm37_close(dev); return NULL; } mraa_gpio_dir(dev->gpio_trigger, MRAA_GPIO_OUT); mraa_gpio_write(dev->gpio_trigger, 1); } else { // UART only if (!(dev->uart = mraa_uart_init(uart_bus))) { printf("%s: mraa_uart_init() failed.\n", __FUNCTION__); urm37_close(dev); return NULL; } mraa_uart_set_baudrate(dev->uart, 9600); mraa_uart_set_non_blocking(dev->uart, false); } // reset - used by both analog and uart modes if (!(dev->gpio_reset = mraa_gpio_init(reset_pin))) { printf("%s: mraa_gpio_init(reset) failed.\n", __FUNCTION__); urm37_close(dev); return NULL; } mraa_gpio_dir(dev->gpio_reset, MRAA_GPIO_OUT); urm37_reset(dev); return dev; } void urm37_close(urm37_context dev) { if (dev->aio) mraa_aio_close(dev->aio); if (dev->gpio_reset) mraa_gpio_close(dev->gpio_reset); if (dev->gpio_trigger) mraa_gpio_close(dev->gpio_trigger); if (dev->uart) mraa_uart_stop(dev->uart); free(dev); } upm_result_t urm37_reset(urm37_context dev) { mraa_gpio_write(dev->gpio_reset, 0); upm_delay_us(100); mraa_gpio_write(dev->gpio_reset, 1); // wait for reset to complete upm_delay(3); return UPM_SUCCESS; } // private static bool urm37_data_available(urm37_context dev, uint32_t millis) { if (mraa_uart_data_available(dev->uart, millis)) return true; else return false; } // private static int urm37_read_data(urm37_context dev, char* data) { return mraa_uart_read(dev->uart, data, (size_t)URM37_MAX_DATA_LEN); } // private static int urm37_write_data(urm37_context dev, const char* data) { mraa_uart_flush(dev->uart); return mraa_uart_write(dev->uart, data, (size_t)URM37_MAX_DATA_LEN); } upm_result_t urm37_send_command(urm37_context dev, char* cmd, char* response) { if (dev->is_analog_mode) { printf("%s: UART commands are not available in analog mode\n", __FUNCTION__); return UPM_ERROR_NOT_SUPPORTED; } int tries = 0; while (tries++ < URM37_MAX_RETRIES) { if (urm37_write_data(dev, cmd) < 0) { // A write error of some kind. We don't try to continue // after this. printf("%s: write_data() failed\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } if (!urm37_data_available(dev, URM37_WAIT_TIMEOUT)) { // timeout, retry... continue; } int rv = urm37_read_data(dev, response); if (rv < 0) { printf("%s: read_data() failed\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } if (rv != URM37_MAX_DATA_LEN) { // read wrong number of bytes... printf("%s: read_data() returned %d bytes, expected %d, retrying\n", __FUNCTION__, rv, URM37_MAX_DATA_LEN); continue; } else { // we have data, verify cksum, return the response if it's // good, retry otherwise uint8_t cksum = (uint8_t)(response[0] + response[1] + response[2]); if ((uint8_t)response[3] != cksum) { printf("%s: checksum failure: got %d, expected %d, retrying\n", __FUNCTION__, (int)response[3], (int)cksum); continue; } // all good return UPM_SUCCESS; } } // If we are here, we timed out and all retries were exhausted return UPM_ERROR_TIMED_OUT; } upm_result_t urm37_get_distance(urm37_context dev, float *distance, int degrees) { if (dev->is_analog_mode) { // analog mode int val; // send the trigger pulse and sample mraa_gpio_write(dev->gpio_trigger, 0); val = mraa_aio_read(dev->aio); mraa_gpio_write(dev->gpio_trigger, 1); // convert to mV float volts = ((float)val * (dev->a_ref / dev->a_res)) * 1000.0; // 6.8 mV/cm *distance = volts/6.8; return UPM_SUCCESS; } // UART mode char cmd[URM37_MAX_DATA_LEN]; char resp[URM37_MAX_DATA_LEN]; // divide degrees by 6 - this is the encoding URM37 uses. uint8_t deg = (uint8_t)(degrees / 6); if (deg > 46) { printf("%s: Degrees out of range, must be between 0-270\n", __FUNCTION__); return UPM_ERROR_OUT_OF_RANGE; } uint8_t cksum = 0x22 + deg + 0x00; cmd[0] = 0x22; cmd[1] = deg; cmd[2] = 0x00; cmd[3] = cksum; if (urm37_send_command(dev, cmd, resp) != UPM_SUCCESS) { printf("%s: urm37_send_command() failed\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } uint8_t h = (uint8_t) resp[1]; uint8_t l = (uint8_t) resp[2]; *distance = (float)((h << 8) | l); return UPM_SUCCESS; } upm_result_t urm37_get_temperature(urm37_context dev, float* temperature) { if (dev->is_analog_mode) { printf("%s: Temperature measurement is not available in analog mode\n", __FUNCTION__); return UPM_ERROR_NOT_SUPPORTED; } // UART mode char cmd[URM37_MAX_DATA_LEN]; // get temperature sequence cmd[0] = 0x11; cmd[1] = 0x00; cmd[2] = 0x00; cmd[3] = 0x11; // cksum char resp[URM37_MAX_DATA_LEN]; if (urm37_send_command(dev, cmd, resp) != UPM_SUCCESS) { printf("%s: urm37_send_command() failed\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } uint8_t h = (uint8_t) resp[1]; uint8_t l = (uint8_t) resp[2]; *temperature = (float)((h & 0x0f) * 256 + l) / 10.0; if (h & 0xf0) *temperature *= -1; return UPM_SUCCESS; } upm_result_t urm37_read_eeprom(urm37_context dev, uint8_t addr, uint8_t* value) { if (dev->is_analog_mode) { printf("%s: EEPROM is not available in analog mode\n", __FUNCTION__); return UPM_ERROR_NOT_SUPPORTED; } if (addr > 0x04) { printf("Address must be between 0x00-0x04"); return UPM_ERROR_OUT_OF_RANGE; } char cmd[URM37_MAX_DATA_LEN]; uint8_t cksum = 0x33 + addr + 0x00; cmd[0] = 0x33; cmd[1] = addr; cmd[2] = 0x00; cmd[3] = cksum; char resp[URM37_MAX_DATA_LEN]; if (urm37_send_command(dev, cmd, resp) != UPM_SUCCESS) { printf("%s: urm37_send_command() failed\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } *value = resp[2]; return UPM_SUCCESS; } upm_result_t urm37_write_eeprom(urm37_context dev, uint8_t addr, uint8_t value) { if (dev->is_analog_mode) { printf("%s: EEPROM is not available in analog mode\n", __FUNCTION__); return UPM_ERROR_NOT_SUPPORTED; } if (addr > 0x04) { printf("Address must be between 0x00-0x04"); return UPM_ERROR_OUT_OF_RANGE; } char cmd[URM37_MAX_DATA_LEN]; uint8_t cksum = 0x44 + addr + value; cmd[0] = 0x44; cmd[1] = addr; cmd[2] = value; cmd[3] = cksum; char resp[URM37_MAX_DATA_LEN]; // throw away if (urm37_send_command(dev, cmd, resp) != UPM_SUCCESS) { printf("%s: urm37_send_command() failed\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } return UPM_SUCCESS; }