From 85196faccddb3ed1b3ffc3290f278797621703f9 Mon Sep 17 00:00:00 2001 From: Syping Date: Fri, 23 Apr 2021 12:41:16 +0200 Subject: [PATCH] initial commit --- CMakeLists.txt | 48 ++++++ src/dht/freebsd/gpio.c | 260 ++++++++++++++++++++++++++++++++ src/dht/freebsd/libdht.c | 119 +++++++++++++++ src/dht/freebsd/libdht.h | 38 +++++ src/dht/freebsd/libgpio.h | 105 +++++++++++++ src/dht/linux/common_dht_read.c | 66 ++++++++ src/dht/linux/common_dht_read.h | 51 +++++++ src/dht/linux/libdht.c | 17 +++ src/dht/linux/libdht.h | 12 ++ src/dht/linux/pi_2_dht_read.c | 158 +++++++++++++++++++ src/dht/linux/pi_2_dht_read.h | 32 ++++ src/dht/linux/pi_2_mmio.c | 71 +++++++++ src/dht/linux/pi_2_mmio.h | 62 ++++++++ src/main.cpp | 56 +++++++ 14 files changed, 1095 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/dht/freebsd/gpio.c create mode 100644 src/dht/freebsd/libdht.c create mode 100644 src/dht/freebsd/libdht.h create mode 100644 src/dht/freebsd/libgpio.h create mode 100644 src/dht/linux/common_dht_read.c create mode 100644 src/dht/linux/common_dht_read.h create mode 100644 src/dht/linux/libdht.c create mode 100644 src/dht/linux/libdht.h create mode 100644 src/dht/linux/pi_2_dht_read.c create mode 100644 src/dht/linux/pi_2_dht_read.h create mode 100644 src/dht/linux/pi_2_mmio.c create mode 100644 src/dht/linux/pi_2_mmio.h create mode 100644 src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ee745a1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.7) + +project(dhtserver LANGUAGES C CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network REQUIRED) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(DHT_SOURCES + src/dht/linux/common_dht_read.c + src/dht/linux/common_dht_read.h + src/dht/linux/libdht.c + src/dht/linux/libdht.h + src/dht/linux/pi_2_dht_read.c + src/dht/linux/pi_2_dht_read.h + src/dht/linux/pi_2_mmio.c + src/dht/linux/pi_2_mmio.h + ) + list(APPEND DHT_INCLUDEDIR + src/dht/linux + ) +elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + set(DHT_SOURCES + src/dht/freebsd/gpio.c + src/dht/freebsd/libdht.c + src/dht/freebsd/libdht.h + src/dht/freebsd/libgpio.h + ) + list(APPEND DHT_INCLUDEDIR + src/dht/freebsd + ) +endif() + +add_executable(dhtserver + src/main.cpp + ${DHT_SOURCES} +) +target_include_directories(dhtserver PRIVATE ${DHT_INCLUDEDIR}) +target_link_libraries(dhtserver Qt${QT_VERSION_MAJOR}::Network) diff --git a/src/dht/freebsd/gpio.c b/src/dht/freebsd/gpio.c new file mode 100644 index 0000000..1345e48 --- /dev/null +++ b/src/dht/freebsd/gpio.c @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 2013-2014 Rui Paulo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +gpio_handle_t +gpio_open(unsigned int unit) +{ + char device[16]; + + snprintf(device, sizeof(device), "/dev/gpioc%u", unit); + + return (gpio_open_device(device)); +} + +gpio_handle_t +gpio_open_device(const char *device) +{ + int fd, maxpins; + int serr; + + fd = open(device, O_RDONLY); + if (fd < 0) + return (GPIO_INVALID_HANDLE); + /* + * Check whether a simple ioctl works. + */ + if (ioctl(fd, GPIOMAXPIN, &maxpins) < 0) { + serr = errno; + close(fd); + errno = serr; + return (GPIO_INVALID_HANDLE); + } + + return (fd); +} + +void +gpio_close(gpio_handle_t handle) +{ + close(handle); +} + +int +gpio_pin_list(gpio_handle_t handle, gpio_config_t **pcfgs) +{ + int maxpins, i; + gpio_config_t *cfgs; + + *pcfgs = NULL; + if (ioctl(handle, GPIOMAXPIN, &maxpins) < 0) + return (-1); + /* Reasonable values. */ + if (maxpins < 0 || maxpins > 4096) { + errno = EINVAL; + return (-1); + } + cfgs = calloc(maxpins, sizeof(*cfgs)); + if (cfgs == NULL) + return (-1); + for (i = 0; i <= maxpins; i++) { + cfgs[i].g_pin = i; + gpio_pin_config(handle, &cfgs[i]); + } + *pcfgs = cfgs; + + return (maxpins); +} + +int +gpio_pin_config(gpio_handle_t handle, gpio_config_t *cfg) +{ + struct gpio_pin gppin; + + if (cfg == NULL) + return (-1); + gppin.gp_pin = cfg->g_pin; + if (ioctl(handle, GPIOGETCONFIG, &gppin) < 0) + return (-1); + strlcpy(cfg->g_name, gppin.gp_name, GPIOMAXNAME); + cfg->g_caps = gppin.gp_caps; + cfg->g_flags = gppin.gp_flags; + + return (0); +} + +int +gpio_pin_set_flags(gpio_handle_t handle, gpio_config_t *cfg) +{ + struct gpio_pin gppin; + + if (cfg == NULL) + return (-1); + gppin.gp_pin = cfg->g_pin; + gppin.gp_flags = cfg->g_flags; + if (ioctl(handle, GPIOSETCONFIG, &gppin) < 0) + return (-1); + + return (0); +} + +gpio_value_t +gpio_pin_get(gpio_handle_t handle, gpio_pin_t pin) +{ + struct gpio_req gpreq; + + bzero(&gpreq, sizeof(gpreq)); + gpreq.gp_pin = pin; + if (ioctl(handle, GPIOGET, &gpreq) < 0) + return (GPIO_VALUE_INVALID); + + return (gpreq.gp_value); +} + +int +gpio_pin_set(gpio_handle_t handle, gpio_pin_t pin, gpio_value_t value) +{ + struct gpio_req gpreq; + + if (value == GPIO_VALUE_INVALID) + return (-1); + bzero(&gpreq, sizeof(gpreq)); + gpreq.gp_pin = pin; + gpreq.gp_value = value; + if (ioctl(handle, GPIOSET, &gpreq) < 0) + return (-1); + + return (0); +} + +int +gpio_pin_toggle(gpio_handle_t handle, gpio_pin_t pin) +{ + gpio_value_t value; + + value = gpio_pin_get(handle, pin); + if (value == GPIO_VALUE_INVALID) + return (-1); + value = !value; + + return (gpio_pin_set(handle, pin, value)); +} + +int +gpio_pin_low(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set(handle, pin, GPIO_VALUE_LOW)); +} + +int +gpio_pin_high(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set(handle, pin, GPIO_VALUE_HIGH)); +} + +static int +gpio_pin_set_flag(gpio_handle_t handle, gpio_pin_t pin, uint32_t flag) +{ + gpio_config_t cfg; + + bzero(&cfg, sizeof(cfg)); + cfg.g_pin = pin; + if (gpio_pin_config(handle, &cfg) < 0) + return (-1); + cfg.g_flags = flag; + + return (gpio_pin_set_flags(handle, &cfg)); +} + +int +gpio_pin_input(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INPUT)); +} + +int +gpio_pin_output(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_OUTPUT)); +} + +int +gpio_pin_opendrain(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_OPENDRAIN)); +} + +int +gpio_pin_pushpull(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PUSHPULL)); +} + +int +gpio_pin_tristate(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_TRISTATE)); +} + +int +gpio_pin_pullup(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULLUP)); +} + +int +gpio_pin_pulldown(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULLDOWN)); +} + +int +gpio_pin_invin(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INVIN)); +} + +int +gpio_pin_invout(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INVOUT)); +} + +int +gpio_pin_pulsate(gpio_handle_t handle, gpio_pin_t pin) +{ + return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULSATE)); +} diff --git a/src/dht/freebsd/libdht.c b/src/dht/freebsd/libdht.c new file mode 100644 index 0000000..d7a8db4 --- /dev/null +++ b/src/dht/freebsd/libdht.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 2015 lex + * Copyright (c) 2020-2021 Syping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "libdht.h" +#include "libgpio.h" +#include +#include +#include +#include + +#define DHT_PULSES 40 +#define DHT_MAXCOUNT 32000 + +int getdhtstring(int PIN, char* string) +{ + // these values seem to work best + int firstSleep = 5000; + int secondSleep = 200; + + uint8_t rawData[DHT_PULSES] = {0}; + uint8_t values[4] = {0}; + + gpio_handle_t handle = gpio_open(0); + + // wake up the sensor + gpio_pin_output(handle, PIN); + gpio_pin_low(handle, PIN); + usleep(firstSleep); + gpio_pin_high(handle, PIN); + usleep(secondSleep); + + // begin to receive data + gpio_pin_input(handle, PIN); + + for (volatile int i = 0; i < 50; ++i) { + } + + uint total = 0; + + for (int i = 0; i < DHT_MAXCOUNT; ++i) { + int c = 0; + while (gpio_pin_get(handle, PIN) != 1) { + } + while (gpio_pin_get(handle, PIN) != 0 && c < 256) { + // transmitting, 26-28 us = 0, 70 us = 1 + // results should be something like 3-4 c for 0, 12-14 for 1 + c++; + } + + if (c == 256) { + // reading probably failed + break; + } + + total++; + rawData[i] = c; + + if (total == DHT_PULSES) { + // got all we need + break; + } + } + + if (total != DHT_PULSES) { + gpio_close(handle); + return 1; + } + + for (int i = 0; i < 4; ++i) { + uint8_t value = 0; + + for (int j = 0; j < 8; ++j) { + uint8_t measuredLength = rawData[i * 8 + j]; + + // hax + if (measuredLength > 9) + value |= 0x01; + + if (j != 7) + value <<= 1; + } + + values[i] = value; + } + + float humid = (values[0] * 256 + values[1]) / 10.0f; + float temp = ((values[2] & 0x7F) * 256 + values[3]) / 10.0f; + if (values[2] & 0x80) { + temp *= -1.0f; + } + + sprintf(string, "{\"humidity\": %.1f, \"temp\": %.1f}\n", humid, temp); + + gpio_close(handle); + return 0; +} diff --git a/src/dht/freebsd/libdht.h b/src/dht/freebsd/libdht.h new file mode 100644 index 0000000..a0ce4a7 --- /dev/null +++ b/src/dht/freebsd/libdht.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2021 Syping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LIBDHT_H_ +#define _LIBDHT_H_ + +#include + +__BEGIN_DECLS + +int getdhtstring(int PIN, char* string); + +__END_DECLS + +#endif /* _LIBDHT_H_ */ diff --git a/src/dht/freebsd/libgpio.h b/src/dht/freebsd/libgpio.h new file mode 100644 index 0000000..fbab522 --- /dev/null +++ b/src/dht/freebsd/libgpio.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2013-2014 Rui Paulo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LIBGPIO_H_ +#define _LIBGPIO_H_ + +#include +#include + +__BEGIN_DECLS + +#define GPIO_INVALID_HANDLE -1 +typedef int gpio_handle_t; + +typedef uint32_t gpio_pin_t; + +/* + * Structure describing a GPIO pin configuration. + */ +typedef struct { + gpio_pin_t g_pin; + char g_name[GPIOMAXNAME]; + uint32_t g_caps; + uint32_t g_flags; +} gpio_config_t; + +typedef enum { + GPIO_VALUE_INVALID = -1, + GPIO_VALUE_LOW = GPIO_PIN_LOW, + GPIO_VALUE_HIGH = GPIO_PIN_HIGH +} gpio_value_t; + +/* + * Open /dev/gpiocN or a specific device. + */ +gpio_handle_t gpio_open(unsigned int); +gpio_handle_t gpio_open_device(const char *); +void gpio_close(gpio_handle_t); +/* + * Get a list of all the GPIO pins. + */ +int gpio_pin_list(gpio_handle_t, gpio_config_t **); +/* + * GPIO pin configuration. + * + * Retrieve the configuration of a specific GPIO pin. The pin number is + * passed through the gpio_config_t structure. + */ +int gpio_pin_config(gpio_handle_t, gpio_config_t *); +/* + * Sets the GPIO flags on a specific GPIO pin. The pin number and the flags + * to be set are passed through the gpio_config_t structure. + */ +int gpio_pin_set_flags(gpio_handle_t, gpio_config_t *); +/* + * GPIO pin values. + */ +int gpio_pin_get(gpio_handle_t, gpio_pin_t); +int gpio_pin_set(gpio_handle_t, gpio_pin_t, int); +int gpio_pin_toggle(gpio_handle_t, gpio_pin_t); +/* + * Helper functions to set pin states. + */ +int gpio_pin_low(gpio_handle_t, gpio_pin_t); +int gpio_pin_high(gpio_handle_t, gpio_pin_t); +/* + * Helper functions to configure pins. + */ +int gpio_pin_input(gpio_handle_t, gpio_pin_t); +int gpio_pin_output(gpio_handle_t, gpio_pin_t); +int gpio_pin_opendrain(gpio_handle_t, gpio_pin_t); +int gpio_pin_pushpull(gpio_handle_t, gpio_pin_t); +int gpio_pin_tristate(gpio_handle_t, gpio_pin_t); +int gpio_pin_pullup(gpio_handle_t, gpio_pin_t); +int gpio_pin_pulldown(gpio_handle_t, gpio_pin_t); +int gpio_pin_invin(gpio_handle_t, gpio_pin_t); +int gpio_pin_invout(gpio_handle_t, gpio_pin_t); +int gpio_pin_pulsate(gpio_handle_t, gpio_pin_t); + +__END_DECLS + +#endif /* _LIBGPIO_H_ */ diff --git a/src/dht/linux/common_dht_read.c b/src/dht/linux/common_dht_read.c new file mode 100644 index 0000000..3bbb663 --- /dev/null +++ b/src/dht/linux/common_dht_read.c @@ -0,0 +1,66 @@ +// Copyright (c) 2014 Adafruit Industries +// Author: Tony DiCola + +// 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 +#include +#include +#include + +#include "common_dht_read.h" + +void busy_wait_milliseconds(uint32_t millis) { + // Set delay time period. + struct timeval deltatime; + deltatime.tv_sec = millis / 1000; + deltatime.tv_usec = (millis % 1000) * 1000; + struct timeval walltime; + // Get current time and add delay to find end time. + gettimeofday(&walltime, NULL); + struct timeval endtime; + timeradd(&walltime, &deltatime, &endtime); + // Tight loop to waste time (and CPU) until enough time as elapsed. + while (timercmp(&walltime, &endtime, <)) { + gettimeofday(&walltime, NULL); + } +} + +void sleep_milliseconds(uint32_t millis) { + struct timespec sleep; + sleep.tv_sec = millis / 1000; + sleep.tv_nsec = (millis % 1000) * 1000000L; + while (clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, &sleep) && errno == EINTR); +} + +void set_max_priority(void) { + struct sched_param sched; + memset(&sched, 0, sizeof(sched)); + // Use FIFO scheduler with highest priority for the lowest chance of the kernel context switching. + sched.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &sched); +} + +void set_default_priority(void) { + struct sched_param sched; + memset(&sched, 0, sizeof(sched)); + // Go back to default scheduler with default 0 priority. + sched.sched_priority = 0; + sched_setscheduler(0, SCHED_OTHER, &sched); +} diff --git a/src/dht/linux/common_dht_read.h b/src/dht/linux/common_dht_read.h new file mode 100644 index 0000000..bb56c67 --- /dev/null +++ b/src/dht/linux/common_dht_read.h @@ -0,0 +1,51 @@ +// Copyright (c) 2014 Adafruit Industries +// Author: Tony DiCola + +// 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. +#ifndef COMMON_DHT_READ_H +#define COMMON_DHT_READ_H + +#include + +// Define errors and return values. +#define DHT_ERROR_TIMEOUT -1 +#define DHT_ERROR_CHECKSUM -2 +#define DHT_ERROR_ARGUMENT -3 +#define DHT_ERROR_GPIO -4 +#define DHT_SUCCESS 0 + +// Define sensor types. +#define DHT11 11 +#define DHT22 22 +#define AM2302 22 + +// Busy wait delay for most accurate timing, but high CPU usage. +// Only use this for short periods of time (a few hundred milliseconds at most)! +void busy_wait_milliseconds(uint32_t millis); + +// General delay that sleeps so CPU usage is low, but accuracy is potentially bad. +void sleep_milliseconds(uint32_t millis); + +// Increase scheduling priority and algorithm to try to get 'real time' results. +void set_max_priority(void); + +// Drop scheduling priority back to normal/default. +void set_default_priority(void); + +#endif diff --git a/src/dht/linux/libdht.c b/src/dht/linux/libdht.c new file mode 100644 index 0000000..7d0f4f3 --- /dev/null +++ b/src/dht/linux/libdht.c @@ -0,0 +1,17 @@ +#include "libdht.h" +#include +#include +#include + +int getdhtstring(int PIN, char* string) +{ + float humid; + float temp; + while (1) { + if (pi_2_dht_read(PIN, PIN, &humid, &temp) == DHT_SUCCESS) { + sprintf(string, "{\"humidity\": %.1f, \"temp\": %.1f}", humid, temp); + break; + } + } + return 0; +} diff --git a/src/dht/linux/libdht.h b/src/dht/linux/libdht.h new file mode 100644 index 0000000..ae7b2d0 --- /dev/null +++ b/src/dht/linux/libdht.h @@ -0,0 +1,12 @@ +#ifndef _LIBDHT_H_ +#define _LIBDHT_H_ + +#include + +__BEGIN_DECLS + +int getdhtstring(int PIN, char* string); + +__END_DECLS + +#endif /* _LIBDHT_H_ */ diff --git a/src/dht/linux/pi_2_dht_read.c b/src/dht/linux/pi_2_dht_read.c new file mode 100644 index 0000000..dd0b9e1 --- /dev/null +++ b/src/dht/linux/pi_2_dht_read.c @@ -0,0 +1,158 @@ +// Copyright (c) 2014 Adafruit Industries +// Author: Tony DiCola + +// 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 + +#include "pi_2_dht_read.h" +#include "pi_2_mmio.h" + +// This is the only processor specific magic value, the maximum amount of time to +// spin in a loop before bailing out and considering the read a timeout. This should +// be a high value, but if you're running on a much faster platform than a Raspberry +// Pi or Beaglebone Black then it might need to be increased. +#define DHT_MAXCOUNT 32000 + +// Number of bit pulses to expect from the DHT. Note that this is 41 because +// the first pulse is a constant 50 microsecond pulse, with 40 pulses to represent +// the data afterwards. +#define DHT_PULSES 41 + +int pi_2_dht_read(int type, int pin, float* humidity, float* temperature) { + // Validate humidity and temperature arguments and set them to zero. + if (humidity == NULL || temperature == NULL) { + return DHT_ERROR_ARGUMENT; + } + *temperature = 0.0f; + *humidity = 0.0f; + + // Initialize GPIO library. + if (pi_2_mmio_init() < 0) { + return DHT_ERROR_GPIO; + } + + // Store the count that each DHT bit pulse is low and high. + // Make sure array is initialized to start at zero. + int pulseCounts[DHT_PULSES*2] = {0}; + + // Set pin to output. + pi_2_mmio_set_output(pin); + + // Bump up process priority and change scheduler to try to try to make process more 'real time'. + set_max_priority(); + + // Set pin high for ~500 milliseconds. + pi_2_mmio_set_high(pin); + sleep_milliseconds(500); + + // The next calls are timing critical and care should be taken + // to ensure no unnecssary work is done below. + + // Set pin low for ~20 milliseconds. + pi_2_mmio_set_low(pin); + busy_wait_milliseconds(20); + + // Set pin at input. + pi_2_mmio_set_input(pin); + // Need a very short delay before reading pins or else value is sometimes still low. + for (volatile int i = 0; i < 50; ++i) { + } + + // Wait for DHT to pull pin low. + uint32_t count = 0; + while (pi_2_mmio_input(pin)) { + if (++count >= DHT_MAXCOUNT) { + // Timeout waiting for response. + set_default_priority(); + return DHT_ERROR_TIMEOUT; + } + } + + // Record pulse widths for the expected result bits. + for (int i=0; i < DHT_PULSES*2; i+=2) { + // Count how long pin is low and store in pulseCounts[i] + while (!pi_2_mmio_input(pin)) { + if (++pulseCounts[i] >= DHT_MAXCOUNT) { + // Timeout waiting for response. + set_default_priority(); + return DHT_ERROR_TIMEOUT; + } + } + // Count how long pin is high and store in pulseCounts[i+1] + while (pi_2_mmio_input(pin)) { + if (++pulseCounts[i+1] >= DHT_MAXCOUNT) { + // Timeout waiting for response. + set_default_priority(); + return DHT_ERROR_TIMEOUT; + } + } + } + + // Done with timing critical code, now interpret the results. + + // Drop back to normal priority. + set_default_priority(); + + // Compute the average low pulse width to use as a 50 microsecond reference threshold. + // Ignore the first two readings because they are a constant 80 microsecond pulse. + uint32_t threshold = 0; + for (int i=2; i < DHT_PULSES*2; i+=2) { + threshold += pulseCounts[i]; + } + threshold /= DHT_PULSES-1; + + // Interpret each high pulse as a 0 or 1 by comparing it to the 50us reference. + // If the count is less than 50us it must be a ~28us 0 pulse, and if it's higher + // then it must be a ~70us 1 pulse. + uint8_t data[5] = {0}; + for (int i=3; i < DHT_PULSES*2; i+=2) { + int index = (i-3)/16; + data[index] <<= 1; + if (pulseCounts[i] >= threshold) { + // One bit for long pulse. + data[index] |= 1; + } + // Else zero bit for short pulse. + } + + // Useful debug info: + //printf("Data: 0x%x 0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3], data[4]); + + // Verify checksum of received data. + if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { + if (type == DHT11) { + // Get humidity and temp for DHT11 sensor. + *humidity = (float)data[0]; + *temperature = (float)data[2]; + } + else if (type == DHT22) { + // Calculate humidity and temp for DHT22 sensor. + *humidity = (data[0] * 256 + data[1]) / 10.0f; + *temperature = ((data[2] & 0x7F) * 256 + data[3]) / 10.0f; + if (data[2] & 0x80) { + *temperature *= -1.0f; + } + } + return DHT_SUCCESS; + } + else { + return DHT_ERROR_CHECKSUM; + } +} diff --git a/src/dht/linux/pi_2_dht_read.h b/src/dht/linux/pi_2_dht_read.h new file mode 100644 index 0000000..acae7a7 --- /dev/null +++ b/src/dht/linux/pi_2_dht_read.h @@ -0,0 +1,32 @@ +// Copyright (c) 2014 Adafruit Industries +// Author: Tony DiCola + +// 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. +#ifndef PI_2_DHT_READ_H +#define PI_2_DHT_READ_H + +#include "common_dht_read.h" + +// Read DHT sensor connected to GPIO pin (using BCM numbering). Humidity and temperature will be +// returned in the provided parameters. If a successfull reading could be made a value of 0 +// (DHT_SUCCESS) will be returned. If there was an error reading the sensor a negative value will +// be returned. Some errors can be ignored and retried, specifically DHT_ERROR_TIMEOUT or DHT_ERROR_CHECKSUM. +int pi_2_dht_read(int sensor, int pin, float* humidity, float* temperature); + +#endif diff --git a/src/dht/linux/pi_2_mmio.c b/src/dht/linux/pi_2_mmio.c new file mode 100644 index 0000000..4c40fe8 --- /dev/null +++ b/src/dht/linux/pi_2_mmio.c @@ -0,0 +1,71 @@ +// Copyright (c) 2014 Adafruit Industries +// Author: Tony DiCola +// Based on code from Gert van Loo & Dom: http://elinux.org/RPi_Low-level_peripherals#GPIO_Code_examples + +// 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 +#include +#include +#include +#include +#include +#include + +#include "pi_2_mmio.h" + +#define GPIO_BASE_OFFSET 0x200000 +#define GPIO_LENGTH 4096 + +volatile uint32_t* pi_2_mmio_gpio = NULL; + +int pi_2_mmio_init(void) { + if (pi_2_mmio_gpio == NULL) { + // Check for GPIO and peripheral addresses from device tree. + // Adapted from code in the RPi.GPIO library at: + // http://sourceforge.net/p/raspberry-gpio-python/ + FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb"); + if (fp == NULL) { + return MMIO_ERROR_OFFSET; + } + fseek(fp, 4, SEEK_SET); + unsigned char buf[4]; + if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) { + return MMIO_ERROR_OFFSET; + } + uint32_t peri_base = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; + uint32_t gpio_base = peri_base + GPIO_BASE_OFFSET; + fclose(fp); + + int fd = open("/dev/gpiomem", O_RDWR | O_SYNC); + if (fd == -1) { + // Error opening /dev/gpiomem. + return MMIO_ERROR_DEVMEM; + } + // Map GPIO memory to location in process space. + pi_2_mmio_gpio = (uint32_t*)mmap(NULL, GPIO_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, gpio_base); + close(fd); + if (pi_2_mmio_gpio == MAP_FAILED) { + // Don't save the result if the memory mapping failed. + pi_2_mmio_gpio = NULL; + return MMIO_ERROR_MMAP; + } + } + return MMIO_SUCCESS; +} diff --git a/src/dht/linux/pi_2_mmio.h b/src/dht/linux/pi_2_mmio.h new file mode 100644 index 0000000..cda66db --- /dev/null +++ b/src/dht/linux/pi_2_mmio.h @@ -0,0 +1,62 @@ +// Copyright (c) 2014 Adafruit Industries +// Author: Tony DiCola +// Based on code from Gert van Loo & Dom: http://elinux.org/RPi_Low-level_peripherals#GPIO_Code_examples + +// 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. + +// Simple fast memory-mapped GPIO library for the Raspberry Pi. +#ifndef PI_2_MMIO_H +#define PI_2_MMIO_H + +#include + +#define MMIO_SUCCESS 0 +#define MMIO_ERROR_DEVMEM -1 +#define MMIO_ERROR_MMAP -2 +#define MMIO_ERROR_OFFSET -3 + +extern volatile uint32_t* pi_2_mmio_gpio; + +int pi_2_mmio_init(void); + +static inline void pi_2_mmio_set_input(const int gpio_number) { + // Set GPIO register to 000 for specified GPIO number. + *(pi_2_mmio_gpio+((gpio_number)/10)) &= ~(7<<(((gpio_number)%10)*3)); +} + +static inline void pi_2_mmio_set_output(const int gpio_number) { + // First set to 000 using input function. + pi_2_mmio_set_input(gpio_number); + // Next set bit 0 to 1 to set output. + *(pi_2_mmio_gpio+((gpio_number)/10)) |= (1<<(((gpio_number)%10)*3)); +} + +static inline void pi_2_mmio_set_high(const int gpio_number) { + *(pi_2_mmio_gpio+7) = 1 << gpio_number; +} + +static inline void pi_2_mmio_set_low(const int gpio_number) { + *(pi_2_mmio_gpio+10) = 1 << gpio_number; +} + +static inline uint32_t pi_2_mmio_input(const int gpio_number) { + return *(pi_2_mmio_gpio+13) & (1 << gpio_number); +} + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..706cc7c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +extern "C" { +#include "libdht.h" +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + const QStringList args = a.arguments(); + + int PIN = 22; // PIN 22 is default for dhtserver + if (args.length() == 2) { + bool ok; + const int aPIN = args.at(1).toInt(&ok); + if (ok) { + PIN = aPIN; + } + } + + QByteArray dhtJson; + QLocalServer localServer; + localServer.setSocketOptions(QLocalServer::WorldAccessOption); + bool isListen = localServer.listen("/var/run/dhtserver/dhtserver.sock"); + if (!isListen) { + QTextStream(stderr) << "dhtserver: Unix socket can't listen!" << Qt::endl; + return -1; + } + QObject::connect(&localServer, &QLocalServer::newConnection, [&]() { + QLocalSocket *localSocket = localServer.nextPendingConnection(); + if (localSocket) { + QObject::connect(localSocket, &QLocalSocket::disconnected, localSocket, &QLocalSocket::deleteLater); + localSocket->write(dhtJson); + localSocket->flush(); + localSocket->disconnectFromServer(); + } + }); + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, [&]() { + char *dhtstring = static_cast(malloc(512)); + int result = getdhtstring(PIN, dhtstring); + if (result == 0) { + dhtJson = QByteArray(dhtstring).trimmed(); + } + free(dhtstring); + }); + timer.start(1000); + + QTextStream(stderr) << "dhtserver: Server started successfully!" << Qt::endl; + + return a.exec(); +}