Apostolos Fanakis
6 years ago
commit
37f9532826
10 changed files with 1646 additions and 0 deletions
@ -0,0 +1,36 @@ |
|||
# Microprocessors and Peripherals semester assignment, 2019, AUTH |
|||
> Temperature and distance sensing and data sending over the internet |
|||
|
|||
insertCoolProjectNameHere is an *experimental* application developed as part of the course "Microprocessors and Peripherals" assignment, that took place in the Department of Electrical & Computer Engineering at Aristotle University of Thessaloniki in 2019. |
|||
|
|||
--- |
|||
|
|||
## Dependencies |
|||
|
|||
In order to compile and execute this application the bellow referenced libraries and their dependencies are needed. |
|||
|
|||
Libraries: |
|||
- SevSeg |
|||
- DHT |
|||
- Adafruit_Sensor (DHT.h dependency) |
|||
|
|||
All the libraries' latest versions (as of the completion of the project) can be found in the `dependencies` directory of this repository. Instructions for installing them can be found in the links bellow: |
|||
- https://www.arduino.cc/en/Guide/Libraries#toc5 |
|||
- https://www.arduino.cc/en/hacking/libraries |
|||
|
|||
--- |
|||
|
|||
## Execution |
|||
|
|||
To execute the code just open the `todoFindCoolNameForProject\todoFindCoolNameForProject.ino` file using the Arduino IDE and click the Verify and Upload buttons. All necessery peripherials need to be correctly connected to the Arduino board used, more information about the connections can be found inside the `todoFindCoolNameForProject.ino` file and the report. |
|||
|
|||
--- |
|||
|
|||
## Support |
|||
|
|||
Reach out to me: |
|||
|
|||
- [apostolof's email](mailto:apotwohd@gmail.com "apotwohd@gmail.com") |
|||
- [anapt's email](mailto:ana.pachni.tsitiridou@gmail.com "ana.pachni.tsitiridou@gmail.com") |
|||
|
|||
--- |
@ -0,0 +1,156 @@ |
|||
/*
|
|||
* Copyright (C) 2008 The Android Open Source Project |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software< /span> |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and
|
|||
* extended sensor support to include color, voltage and current */ |
|||
|
|||
#ifndef _ADAFRUIT_SENSOR_H |
|||
#define _ADAFRUIT_SENSOR_H |
|||
|
|||
#ifndef ARDUINO |
|||
#include <stdint.h> |
|||
#elif ARDUINO >= 100 |
|||
#include "Arduino.h" |
|||
#include "Print.h" |
|||
#else |
|||
#include "WProgram.h" |
|||
#endif |
|||
|
|||
/* Intentionally modeled after sensors.h in the Android API:
|
|||
* https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h */
|
|||
|
|||
/* Constants */ |
|||
#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */ |
|||
#define SENSORS_GRAVITY_MOON (1.6F) /**< The moon's gravity in m/s^2 */ |
|||
#define SENSORS_GRAVITY_SUN (275.0F) /**< The sun's gravity in m/s^2 */ |
|||
#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH) |
|||
#define SENSORS_MAGFIELD_EARTH_MAX (60.0F) /**< Maximum magnetic field on Earth's surface */ |
|||
#define SENSORS_MAGFIELD_EARTH_MIN (30.0F) /**< Minimum magnetic field on Earth's surface */ |
|||
#define SENSORS_PRESSURE_SEALEVELHPA (1013.25F) /**< Average sea level pressure is 1013.25 hPa */ |
|||
#define SENSORS_DPS_TO_RADS (0.017453293F) /**< Degrees/s to rad/s multiplier */ |
|||
#define SENSORS_GAUSS_TO_MICROTESLA (100) /**< Gauss to micro-Tesla multiplier */ |
|||
|
|||
/** Sensor types */ |
|||
typedef enum |
|||
{ |
|||
SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */ |
|||
SENSOR_TYPE_MAGNETIC_FIELD = (2), |
|||
SENSOR_TYPE_ORIENTATION = (3), |
|||
SENSOR_TYPE_GYROSCOPE = (4), |
|||
SENSOR_TYPE_LIGHT = (5), |
|||
SENSOR_TYPE_PRESSURE = (6), |
|||
SENSOR_TYPE_PROXIMITY = (8), |
|||
SENSOR_TYPE_GRAVITY = (9), |
|||
SENSOR_TYPE_LINEAR_ACCELERATION = (10), /**< Acceleration not including gravity */ |
|||
SENSOR_TYPE_ROTATION_VECTOR = (11), |
|||
SENSOR_TYPE_RELATIVE_HUMIDITY = (12), |
|||
SENSOR_TYPE_AMBIENT_TEMPERATURE = (13), |
|||
SENSOR_TYPE_VOLTAGE = (15), |
|||
SENSOR_TYPE_CURRENT = (16), |
|||
SENSOR_TYPE_COLOR = (17) |
|||
} sensors_type_t; |
|||
|
|||
/** struct sensors_vec_s is used to return a vector in a common format. */ |
|||
typedef struct { |
|||
union { |
|||
float v[3]; |
|||
struct { |
|||
float x; |
|||
float y; |
|||
float z; |
|||
}; |
|||
/* Orientation sensors */ |
|||
struct { |
|||
float roll; /**< Rotation around the longitudinal axis (the plane body, 'X axis'). Roll is positive and increasing when moving downward. -90°<=roll<=90° */ |
|||
float pitch; /**< Rotation around the lateral axis (the wing span, 'Y axis'). Pitch is positive and increasing when moving upwards. -180°<=pitch<=180°) */ |
|||
float heading; /**< Angle between the longitudinal axis (the plane body) and magnetic north, measured clockwise when viewing from the top of the device. 0-359° */ |
|||
}; |
|||
}; |
|||
int8_t status; |
|||
uint8_t reserved[3]; |
|||
} sensors_vec_t; |
|||
|
|||
/** struct sensors_color_s is used to return color data in a common format. */ |
|||
typedef struct { |
|||
union { |
|||
float c[3]; |
|||
/* RGB color space */ |
|||
struct { |
|||
float r; /**< Red component */ |
|||
float g; /**< Green component */ |
|||
float b; /**< Blue component */ |
|||
}; |
|||
}; |
|||
uint32_t rgba; /**< 24-bit RGBA value */ |
|||
} sensors_color_t; |
|||
|
|||
/* Sensor event (36 bytes) */ |
|||
/** struct sensor_event_s is used to provide a single sensor event in a common format. */ |
|||
typedef struct |
|||
{ |
|||
int32_t version; /**< must be sizeof(struct sensors_event_t) */ |
|||
int32_t sensor_id; /**< unique sensor identifier */ |
|||
int32_t type; /**< sensor type */ |
|||
int32_t reserved0; /**< reserved */ |
|||
int32_t timestamp; /**< time is in milliseconds */ |
|||
union |
|||
{ |
|||
float data[4]; |
|||
sensors_vec_t acceleration; /**< acceleration values are in meter per second per second (m/s^2) */ |
|||
sensors_vec_t magnetic; /**< magnetic vector values are in micro-Tesla (uT) */ |
|||
sensors_vec_t orientation; /**< orientation values are in degrees */ |
|||
sensors_vec_t gyro; /**< gyroscope values are in rad/s */ |
|||
float temperature; /**< temperature is in degrees centigrade (Celsius) */ |
|||
float distance; /**< distance in centimeters */ |
|||
float light; /**< light in SI lux units */ |
|||
float pressure; /**< pressure in hectopascal (hPa) */ |
|||
float relative_humidity; /**< relative humidity in percent */ |
|||
float current; /**< current in milliamps (mA) */ |
|||
float voltage; /**< voltage in volts (V) */ |
|||
sensors_color_t color; /**< color in RGB component values */ |
|||
}; |
|||
} sensors_event_t; |
|||
|
|||
/* Sensor details (40 bytes) */ |
|||
/** struct sensor_s is used to describe basic information about a specific sensor. */ |
|||
typedef struct |
|||
{ |
|||
char name[12]; /**< sensor name */ |
|||
int32_t version; /**< version of the hardware + driver */ |
|||
int32_t sensor_id; /**< unique sensor identifier */ |
|||
int32_t type; /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */ |
|||
float max_value; /**< maximum value of this sensor's value in SI units */ |
|||
float min_value; /**< minimum value of this sensor's value in SI units */ |
|||
float resolution; /**< smallest difference between two values reported by this sensor */ |
|||
int32_t min_delay; /**< min delay in microseconds between events. zero = not a constant rate */ |
|||
} sensor_t; |
|||
|
|||
class Adafruit_Sensor { |
|||
public: |
|||
// Constructor(s)
|
|||
Adafruit_Sensor() {} |
|||
virtual ~Adafruit_Sensor() {} |
|||
|
|||
// These must be defined by the subclass
|
|||
virtual void enableAutoRange(bool enabled) { (void)enabled; /* suppress unused warning */ }; |
|||
virtual bool getEvent(sensors_event_t*) = 0; |
|||
virtual void getSensor(sensor_t*) = 0; |
|||
|
|||
private: |
|||
bool _autoRange; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,299 @@ |
|||
/* DHT library
|
|||
|
|||
MIT license |
|||
written by Adafruit Industries |
|||
*/ |
|||
|
|||
#include "DHT.h" |
|||
|
|||
#define MIN_INTERVAL 2000 |
|||
#define TIMEOUT -1 |
|||
|
|||
DHT::DHT(uint8_t pin, uint8_t type, uint8_t count) { |
|||
_pin = pin; |
|||
_type = type; |
|||
#ifdef __AVR |
|||
_bit = digitalPinToBitMask(pin); |
|||
_port = digitalPinToPort(pin); |
|||
#endif |
|||
_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for
|
|||
// reading pulses from DHT sensor.
|
|||
// Note that count is now ignored as the DHT reading algorithm adjusts itself
|
|||
// based on the speed of the processor.
|
|||
} |
|||
|
|||
// Optionally pass pull-up time (in microseconds) before DHT reading starts.
|
|||
// Default is 55 (see function declaration in DHT.h).
|
|||
void DHT::begin(uint8_t usec) { |
|||
// set up the pins!
|
|||
pinMode(_pin, INPUT_PULLUP); |
|||
// Using this value makes sure that millis() - lastreadtime will be
|
|||
// >= MIN_INTERVAL right away. Note that this assignment wraps around,
|
|||
// but so will the subtraction.
|
|||
_lastreadtime = millis() - MIN_INTERVAL; |
|||
DEBUG_PRINT("DHT max clock cycles: "); DEBUG_PRINTLN(_maxcycles, DEC); |
|||
pullTime = usec; |
|||
} |
|||
|
|||
//boolean S == Scale. True == Fahrenheit; False == Celcius
|
|||
float DHT::readTemperature(bool S, bool force) { |
|||
float f = NAN; |
|||
|
|||
if (read(force)) { |
|||
switch (_type) { |
|||
case DHT11: |
|||
f = data[2]; |
|||
if (data[3] & 0x80) { |
|||
f = -1 - f ; |
|||
} |
|||
f += (data[3] & 0x0f) * 0.1; |
|||
if(S) { |
|||
f = convertCtoF(f); |
|||
} |
|||
break; |
|||
case DHT12: |
|||
f = data[2]; |
|||
f += (data[3] & 0x0f) * 0.1; |
|||
if (data[2] & 0x80) { |
|||
f *= -1; |
|||
} |
|||
if(S) { |
|||
f = convertCtoF(f); |
|||
} |
|||
break; |
|||
case DHT22: |
|||
case DHT21: |
|||
f = ((word)(data[2] & 0x7F)) << 8 | data[3]; |
|||
f *= 0.1; |
|||
if (data[2] & 0x80) { |
|||
f *= -1; |
|||
} |
|||
if(S) { |
|||
f = convertCtoF(f); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
return f; |
|||
} |
|||
|
|||
float DHT::convertCtoF(float c) { |
|||
return c * 1.8 + 32; |
|||
} |
|||
|
|||
float DHT::convertFtoC(float f) { |
|||
return (f - 32) * 0.55555; |
|||
} |
|||
|
|||
float DHT::readHumidity(bool force) { |
|||
float f = NAN; |
|||
if (read(force)) { |
|||
switch (_type) { |
|||
case DHT11: |
|||
case DHT12: |
|||
f = data[0] + data[1] * 0.1; |
|||
break; |
|||
case DHT22: |
|||
case DHT21: |
|||
f = ((word)data[0]) << 8 | data[1]; |
|||
f *= 0.1; |
|||
break; |
|||
} |
|||
} |
|||
return f; |
|||
} |
|||
|
|||
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
|
|||
float DHT::computeHeatIndex(bool isFahrenheit) { |
|||
float hi = computeHeatIndex(readTemperature(isFahrenheit), readHumidity(), |
|||
isFahrenheit); |
|||
return hi; |
|||
} |
|||
|
|||
//boolean isFahrenheit: True == Fahrenheit; False == Celcius
|
|||
float DHT::computeHeatIndex(float temperature, float percentHumidity, |
|||
bool isFahrenheit) { |
|||
// Using both Rothfusz and Steadman's equations
|
|||
// http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
|
|||
float hi; |
|||
|
|||
if (!isFahrenheit) |
|||
temperature = convertCtoF(temperature); |
|||
|
|||
hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094)); |
|||
|
|||
if (hi > 79) { |
|||
hi = -42.379 + |
|||
2.04901523 * temperature + |
|||
10.14333127 * percentHumidity + |
|||
-0.22475541 * temperature*percentHumidity + |
|||
-0.00683783 * pow(temperature, 2) + |
|||
-0.05481717 * pow(percentHumidity, 2) + |
|||
0.00122874 * pow(temperature, 2) * percentHumidity + |
|||
0.00085282 * temperature*pow(percentHumidity, 2) + |
|||
-0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2); |
|||
|
|||
if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) |
|||
hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882); |
|||
|
|||
else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)) |
|||
hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2); |
|||
} |
|||
|
|||
return isFahrenheit ? hi : convertFtoC(hi); |
|||
} |
|||
|
|||
bool DHT::read(bool force) { |
|||
// Check if sensor was read less than two seconds ago and return early
|
|||
// to use last reading.
|
|||
uint32_t currenttime = millis(); |
|||
if (!force && ((currenttime - _lastreadtime) < MIN_INTERVAL)) { |
|||
return _lastresult; // return last correct measurement
|
|||
} |
|||
_lastreadtime = currenttime; |
|||
|
|||
// Reset 40 bits of received data to zero.
|
|||
data[0] = data[1] = data[2] = data[3] = data[4] = 0; |
|||
|
|||
#if defined(ESP8266) |
|||
yield(); // Handle WiFi / reset software watchdog
|
|||
#endif |
|||
|
|||
// Send start signal. See DHT datasheet for full signal diagram:
|
|||
// http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf
|
|||
|
|||
// Go into high impedence state to let pull-up raise data line level and
|
|||
// start the reading process.
|
|||
pinMode(_pin, INPUT_PULLUP); |
|||
delay(1); |
|||
|
|||
// First set data line low for a period according to sensor type
|
|||
pinMode(_pin, OUTPUT); |
|||
digitalWrite(_pin, LOW); |
|||
switch(_type) { |
|||
case DHT22: |
|||
case DHT21: |
|||
delayMicroseconds(1100); // data sheet says "at least 1ms"
|
|||
break; |
|||
case DHT11: |
|||
default: |
|||
delay(20); //data sheet says at least 18ms, 20ms just to be safe
|
|||
break; |
|||
} |
|||
|
|||
uint32_t cycles[80]; |
|||
{ |
|||
// End the start signal by setting data line high for 40 microseconds.
|
|||
pinMode(_pin, INPUT_PULLUP); |
|||
|
|||
// Delay a moment to let sensor pull data line low.
|
|||
delayMicroseconds(pullTime); |
|||
|
|||
// Now start reading the data line to get the value from the DHT sensor.
|
|||
|
|||
// Turn off interrupts temporarily because the next sections
|
|||
// are timing critical and we don't want any interruptions.
|
|||
InterruptLock lock; |
|||
|
|||
// First expect a low signal for ~80 microseconds followed by a high signal
|
|||
// for ~80 microseconds again.
|
|||
if (expectPulse(LOW) == TIMEOUT) { |
|||
DEBUG_PRINTLN(F("DHT timeout waiting for start signal low pulse.")); |
|||
_lastresult = false; |
|||
return _lastresult; |
|||
} |
|||
if (expectPulse(HIGH) == TIMEOUT) { |
|||
DEBUG_PRINTLN(F("DHT timeout waiting for start signal high pulse.")); |
|||
_lastresult = false; |
|||
return _lastresult; |
|||
} |
|||
|
|||
// Now read the 40 bits sent by the sensor. Each bit is sent as a 50
|
|||
// microsecond low pulse followed by a variable length high pulse. If the
|
|||
// high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds
|
|||
// then it's a 1. We measure the cycle count of the initial 50us low pulse
|
|||
// and use that to compare to the cycle count of the high pulse to determine
|
|||
// if the bit is a 0 (high state cycle count < low state cycle count), or a
|
|||
// 1 (high state cycle count > low state cycle count). Note that for speed all
|
|||
// the pulses are read into a array and then examined in a later step.
|
|||
for (int i=0; i<80; i+=2) { |
|||
cycles[i] = expectPulse(LOW); |
|||
cycles[i+1] = expectPulse(HIGH); |
|||
} |
|||
} // Timing critical code is now complete.
|
|||
|
|||
// Inspect pulses and determine which ones are 0 (high state cycle count < low
|
|||
// state cycle count), or 1 (high state cycle count > low state cycle count).
|
|||
for (int i=0; i<40; ++i) { |
|||
uint32_t lowCycles = cycles[2*i]; |
|||
uint32_t highCycles = cycles[2*i+1]; |
|||
if ((lowCycles == TIMEOUT) || (highCycles == TIMEOUT)) { |
|||
DEBUG_PRINTLN(F("DHT timeout waiting for pulse.")); |
|||
_lastresult = false; |
|||
return _lastresult; |
|||
} |
|||
data[i/8] <<= 1; |
|||
// Now compare the low and high cycle times to see if the bit is a 0 or 1.
|
|||
if (highCycles > lowCycles) { |
|||
// High cycles are greater than 50us low cycle count, must be a 1.
|
|||
data[i/8] |= 1; |
|||
} |
|||
// Else high cycles are less than (or equal to, a weird case) the 50us low
|
|||
// cycle count so this must be a zero. Nothing needs to be changed in the
|
|||
// stored data.
|
|||
} |
|||
|
|||
DEBUG_PRINTLN(F("Received from DHT:")); |
|||
DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", ")); |
|||
DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", ")); |
|||
DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", ")); |
|||
DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", ")); |
|||
DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? ")); |
|||
DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); |
|||
|
|||
// Check we read 40 bits and that the checksum matches.
|
|||
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { |
|||
_lastresult = true; |
|||
return _lastresult; |
|||
} |
|||
else { |
|||
DEBUG_PRINTLN(F("DHT checksum failure!")); |
|||
_lastresult = false; |
|||
return _lastresult; |
|||
} |
|||
} |
|||
|
|||
// Expect the signal line to be at the specified level for a period of time and
|
|||
// return a count of loop cycles spent at that level (this cycle count can be
|
|||
// used to compare the relative time of two pulses). If more than a millisecond
|
|||
// ellapses without the level changing then the call fails with a 0 response.
|
|||
// This is adapted from Arduino's pulseInLong function (which is only available
|
|||
// in the very latest IDE versions):
|
|||
// https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_pulse.c
|
|||
uint32_t DHT::expectPulse(bool level) { |
|||
#if (F_CPU > 16000000L) |
|||
uint32_t count = 0; |
|||
#else |
|||
uint16_t count = 0; // To work fast enough on slower AVR boards
|
|||
#endif |
|||
// On AVR platforms use direct GPIO port access as it's much faster and better
|
|||
// for catching pulses that are 10's of microseconds in length:
|
|||
#ifdef __AVR |
|||
uint8_t portState = level ? _bit : 0; |
|||
while ((*portInputRegister(_port) & _bit) == portState) { |
|||
if (count++ >= _maxcycles) { |
|||
return TIMEOUT; // Exceeded timeout, fail.
|
|||
} |
|||
} |
|||
// Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266
|
|||
// right now, perhaps bugs in direct port access functions?).
|
|||
#else |
|||
while (digitalRead(_pin) == level) { |
|||
if (count++ >= _maxcycles) { |
|||
return TIMEOUT; // Exceeded timeout, fail.
|
|||
} |
|||
} |
|||
#endif |
|||
|
|||
return count; |
|||
} |
@ -0,0 +1,81 @@ |
|||
/* DHT library
|
|||
|
|||
MIT license |
|||
written by Adafruit Industries |
|||
*/ |
|||
#ifndef DHT_H |
|||
#define DHT_H |
|||
|
|||
#if ARDUINO >= 100 |
|||
#include "Arduino.h" |
|||
#else |
|||
#include "WProgram.h" |
|||
#endif |
|||
|
|||
|
|||
// Uncomment to enable printing out nice debug messages.
|
|||
//#define DHT_DEBUG
|
|||
|
|||
// Define where debug output will be printed.
|
|||
#define DEBUG_PRINTER Serial |
|||
|
|||
// Setup debug printing macros.
|
|||
#ifdef DHT_DEBUG |
|||
#define DEBUG_PRINT(...) { DEBUG_PRINTER.print(__VA_ARGS__); } |
|||
#define DEBUG_PRINTLN(...) { DEBUG_PRINTER.println(__VA_ARGS__); } |
|||
#else |
|||
#define DEBUG_PRINT(...) {} |
|||
#define DEBUG_PRINTLN(...) {} |
|||
#endif |
|||
|
|||
// Define types of sensors.
|
|||
#define DHT11 11 |
|||
#define DHT12 12 |
|||
#define DHT22 22 |
|||
#define DHT21 21 |
|||
#define AM2301 21 |
|||
|
|||
|
|||
class DHT { |
|||
public: |
|||
DHT(uint8_t pin, uint8_t type, uint8_t count=6); |
|||
void begin(uint8_t usec=55); |
|||
float readTemperature(bool S=false, bool force=false); |
|||
float convertCtoF(float); |
|||
float convertFtoC(float); |
|||
float computeHeatIndex(bool isFahrenheit=true); |
|||
float computeHeatIndex(float temperature, float percentHumidity, bool isFahrenheit=true); |
|||
float readHumidity(bool force=false); |
|||
bool read(bool force=false); |
|||
|
|||
private: |
|||
uint8_t data[5]; |
|||
uint8_t _pin, _type; |
|||
#ifdef __AVR |
|||
// Use direct GPIO access on an 8-bit AVR so keep track of the port and bitmask
|
|||
// for the digital pin connected to the DHT. Other platforms will use digitalRead.
|
|||
uint8_t _bit, _port; |
|||
#endif |
|||
uint32_t _lastreadtime, _maxcycles; |
|||
bool _lastresult; |
|||
uint8_t pullTime; // Time (in usec) to pull up data line before reading
|
|||
|
|||
uint32_t expectPulse(bool level); |
|||
|
|||
}; |
|||
|
|||
class InterruptLock { |
|||
public: |
|||
InterruptLock() { |
|||
#if !defined(ARDUINO_ARCH_NRF52) |
|||
noInterrupts(); |
|||
#endif |
|||
} |
|||
~InterruptLock() { |
|||
#if !defined(ARDUINO_ARCH_NRF52) |
|||
interrupts(); |
|||
#endif |
|||
} |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,195 @@ |
|||
// DHT Temperature & Humidity Unified Sensor Library
|
|||
// 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 "DHT_U.h" |
|||
|
|||
DHT_Unified::DHT_Unified(uint8_t pin, uint8_t type, uint8_t count, int32_t tempSensorId, int32_t humiditySensorId): |
|||
_dht(pin, type, count), |
|||
_type(type), |
|||
_temp(this, tempSensorId), |
|||
_humidity(this, humiditySensorId) |
|||
{} |
|||
|
|||
void DHT_Unified::begin() { |
|||
_dht.begin(); |
|||
} |
|||
|
|||
void DHT_Unified::setName(sensor_t* sensor) { |
|||
switch(_type) { |
|||
case DHT11: |
|||
strncpy(sensor->name, "DHT11", sizeof(sensor->name) - 1); |
|||
break; |
|||
case DHT12: |
|||
strncpy(sensor->name, "DHT12", sizeof(sensor->name) - 1); |
|||
break; |
|||
case DHT21: |
|||
strncpy(sensor->name, "DHT21", sizeof(sensor->name) - 1); |
|||
break; |
|||
case DHT22: |
|||
strncpy(sensor->name, "DHT22", sizeof(sensor->name) - 1); |
|||
break; |
|||
default: |
|||
// TODO: Perhaps this should be an error? However main DHT library doesn't enforce
|
|||
// restrictions on the sensor type value. Pick a generic name for now.
|
|||
strncpy(sensor->name, "DHT?", sizeof(sensor->name) - 1); |
|||
break; |
|||
} |
|||
sensor->name[sizeof(sensor->name)- 1] = 0; |
|||
} |
|||
|
|||
void DHT_Unified::setMinDelay(sensor_t* sensor) { |
|||
switch(_type) { |
|||
case DHT11: |
|||
sensor->min_delay = 1000000L; // 1 second (in microseconds)
|
|||
break; |
|||
case DHT12: |
|||
sensor->min_delay = 2000000L; // 2 second (in microseconds)
|
|||
break; |
|||
case DHT21: |
|||
sensor->min_delay = 2000000L; // 2 seconds (in microseconds)
|
|||
break; |
|||
case DHT22: |
|||
sensor->min_delay = 2000000L; // 2 seconds (in microseconds)
|
|||
break; |
|||
default: |
|||
// Default to slowest sample rate in case of unknown type.
|
|||
sensor->min_delay = 2000000L; // 2 seconds (in microseconds)
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
DHT_Unified::Temperature::Temperature(DHT_Unified* parent, int32_t id): |
|||
_parent(parent), |
|||
_id(id) |
|||
{} |
|||
|
|||
bool DHT_Unified::Temperature::getEvent(sensors_event_t* event) { |
|||
// Clear event definition.
|
|||
memset(event, 0, sizeof(sensors_event_t)); |
|||
// Populate sensor reading values.
|
|||
event->version = sizeof(sensors_event_t); |
|||
event->sensor_id = _id; |
|||
event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; |
|||
event->timestamp = millis(); |
|||
event->temperature = _parent->_dht.readTemperature(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void DHT_Unified::Temperature::getSensor(sensor_t* sensor) { |
|||
// Clear sensor definition.
|
|||
memset(sensor, 0, sizeof(sensor_t)); |
|||
// Set sensor name.
|
|||
_parent->setName(sensor); |
|||
// Set version and ID
|
|||
sensor->version = DHT_SENSOR_VERSION; |
|||
sensor->sensor_id = _id; |
|||
// Set type and characteristics.
|
|||
sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE; |
|||
_parent->setMinDelay(sensor); |
|||
switch (_parent->_type) { |
|||
case DHT11: |
|||
sensor->max_value = 50.0F; |
|||
sensor->min_value = 0.0F; |
|||
sensor->resolution = 2.0F; |
|||
break; |
|||
case DHT12: |
|||
sensor->max_value = 60.0F; |
|||
sensor->min_value = -20.0F; |
|||
sensor->resolution = 0.5F; |
|||
break; |
|||
case DHT21: |
|||
sensor->max_value = 80.0F; |
|||
sensor->min_value = -40.0F; |
|||
sensor->resolution = 0.1F; |
|||
break; |
|||
case DHT22: |
|||
sensor->max_value = 125.0F; |
|||
sensor->min_value = -40.0F; |
|||
sensor->resolution = 0.1F; |
|||
break; |
|||
default: |
|||
// Unknown type, default to 0.
|
|||
sensor->max_value = 0.0F; |
|||
sensor->min_value = 0.0F; |
|||
sensor->resolution = 0.0F; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
DHT_Unified::Humidity::Humidity(DHT_Unified* parent, int32_t id): |
|||
_parent(parent), |
|||
_id(id) |
|||
{} |
|||
|
|||
bool DHT_Unified::Humidity::getEvent(sensors_event_t* event) { |
|||
// Clear event definition.
|
|||
memset(event, 0, sizeof(sensors_event_t)); |
|||
// Populate sensor reading values.
|
|||
event->version = sizeof(sensors_event_t); |
|||
event->sensor_id = _id; |
|||
event->type = SENSOR_TYPE_RELATIVE_HUMIDITY; |
|||
event->timestamp = millis(); |
|||
event->relative_humidity = _parent->_dht.readHumidity(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void DHT_Unified::Humidity::getSensor(sensor_t* sensor) { |
|||
// Clear sensor definition.
|
|||
memset(sensor, 0, sizeof(sensor_t)); |
|||
// Set sensor name.
|
|||
_parent->setName(sensor); |
|||
// Set version and ID
|
|||
sensor->version = DHT_SENSOR_VERSION; |
|||
sensor->sensor_id = _id; |
|||
// Set type and characteristics.
|
|||
sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY; |
|||
_parent->setMinDelay(sensor); |
|||
switch (_parent->_type) { |
|||
case DHT11: |
|||
sensor->max_value = 80.0F; |
|||
sensor->min_value = 20.0F; |
|||
sensor->resolution = 5.0F; |
|||
break; |
|||
case DHT12: |
|||
sensor->max_value = 95.0F; |
|||
sensor->min_value = 20.0F; |
|||
sensor->resolution = 5.0F; |
|||
break; |
|||
case DHT21: |
|||
sensor->max_value = 100.0F; |
|||
sensor->min_value = 0.0F; |
|||
sensor->resolution = 0.1F; |
|||
break; |
|||
case DHT22: |
|||
sensor->max_value = 100.0F; |
|||
sensor->min_value = 0.0F; |
|||
sensor->resolution = 0.1F; |
|||
break; |
|||
default: |
|||
// Unknown type, default to 0.
|
|||
sensor->max_value = 0.0F; |
|||
sensor->min_value = 0.0F; |
|||
sensor->resolution = 0.0F; |
|||
break; |
|||
} |
|||
} |
@ -0,0 +1,78 @@ |
|||
// DHT Temperature & Humidity Unified Sensor Library
|
|||
// 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 DHT_U_H |
|||
#define DHT_U_H |
|||
|
|||
#include <Adafruit_Sensor.h> |
|||
#include <DHT.h> |
|||
|
|||
#define DHT_SENSOR_VERSION 1 |
|||
|
|||
class DHT_Unified { |
|||
public: |
|||
DHT_Unified(uint8_t pin, uint8_t type, uint8_t count=6, int32_t tempSensorId=-1, int32_t humiditySensorId=-1); |
|||
void begin(); |
|||
|
|||
class Temperature : public Adafruit_Sensor { |
|||
public: |
|||
Temperature(DHT_Unified* parent, int32_t id); |
|||
bool getEvent(sensors_event_t* event); |
|||
void getSensor(sensor_t* sensor); |
|||
|
|||
private: |
|||
DHT_Unified* _parent; |
|||
int32_t _id; |
|||
|
|||
}; |
|||
|
|||
class Humidity : public Adafruit_Sensor { |
|||
public: |
|||
Humidity(DHT_Unified* parent, int32_t id); |
|||
bool getEvent(sensors_event_t* event); |
|||
void getSensor(sensor_t* sensor); |
|||
|
|||
private: |
|||
DHT_Unified* _parent; |
|||
int32_t _id; |
|||
|
|||
}; |
|||
|
|||
Temperature temperature() { |
|||
return _temp; |
|||
} |
|||
|
|||
Humidity humidity() { |
|||
return _humidity; |
|||
} |
|||
|
|||
private: |
|||
DHT _dht; |
|||
uint8_t _type; |
|||
Temperature _temp; |
|||
Humidity _humidity; |
|||
|
|||
void setName(sensor_t* sensor); |
|||
void setMinDelay(sensor_t* sensor); |
|||
|
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,582 @@ |
|||
/* SevSeg Library
|
|||
* |
|||
* Copyright 2017 Dean Reading |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
* |
|||
* |
|||
* This library allows an Arduino to easily display numbers and letters on a |
|||
* 7-segment display without a separate 7-segment display controller. |
|||
* |
|||
* Direct any questions or suggestions to deanreading@hotmail.com |
|||
* See the included readme for instructions. |
|||
* https://github.com/DeanIsMe/SevSeg
|
|||
*/ |
|||
|
|||
#include "SevSeg.h" |
|||
|
|||
#define BLANK_IDX 36 // Must match with 'digitCodeMap'
|
|||
#define DASH_IDX 37 |
|||
#define PERIOD_IDX 38 |
|||
#define ASTERISK_IDX 39 |
|||
|
|||
static const long powersOf10[] = { |
|||
1, // 10^0
|
|||
10, |
|||
100, |
|||
1000, |
|||
10000, |
|||
100000, |
|||
1000000, |
|||
10000000, |
|||
100000000, |
|||
1000000000 |
|||
}; // 10^9
|
|||
|
|||
static const long powersOf16[] = { |
|||
0x1, // 16^0
|
|||
0x10, |
|||
0x100, |
|||
0x1000, |
|||
0x10000, |
|||
0x100000, |
|||
0x1000000, |
|||
0x10000000 |
|||
}; // 16^7
|
|||
|
|||
// digitCodeMap indicate which segments must be illuminated to display
|
|||
// each number.
|
|||
static const byte digitCodeMap[] = { |
|||
// GFEDCBA Segments 7-segment map:
|
|||
B00111111, // 0 "0" AAA
|
|||
B00000110, // 1 "1" F B
|
|||
B01011011, // 2 "2" F B
|
|||
B01001111, // 3 "3" GGG
|
|||
B01100110, // 4 "4" E C
|
|||
B01101101, // 5 "5" E C
|
|||
B01111101, // 6 "6" DDD
|
|||
B00000111, // 7 "7"
|
|||
B01111111, // 8 "8"
|
|||
B01101111, // 9 "9"
|
|||
B01110111, // 65 'A'
|
|||
B01111100, // 66 'b'
|
|||
B00111001, // 67 'C'
|
|||
B01011110, // 68 'd'
|
|||
B01111001, // 69 'E'
|
|||
B01110001, // 70 'F'
|
|||
B00111101, // 71 'G'
|
|||
B01110110, // 72 'H'
|
|||
B00000110, // 73 'I'
|
|||
B00001110, // 74 'J'
|
|||
B01110110, // 75 'K' Same as 'H'
|
|||
B00111000, // 76 'L'
|
|||
B00000000, // 77 'M' NO DISPLAY
|
|||
B01010100, // 78 'n'
|
|||
B00111111, // 79 'O'
|
|||
B01110011, // 80 'P'
|
|||
B01100111, // 81 'q'
|
|||
B01010000, // 82 'r'
|
|||
B01101101, // 83 'S'
|
|||
B01111000, // 84 't'
|
|||
B00111110, // 85 'U'
|
|||
B00111110, // 86 'V' Same as 'U'
|
|||
B00000000, // 87 'W' NO DISPLAY
|
|||
B01110110, // 88 'X' Same as 'H'
|
|||
B01101110, // 89 'y'
|
|||
B01011011, // 90 'Z' Same as '2'
|
|||
B00000000, // 32 ' ' BLANK
|
|||
B01000000, // 45 '-' DASH
|
|||
B10000000, // 46 '.' PERIOD
|
|||
B01100011, // 42 '*' DEGREE ..
|
|||
}; |
|||
|
|||
// Constant pointers to constant data
|
|||
const byte * const numeralCodes = digitCodeMap; |
|||
const byte * const alphaCodes = digitCodeMap + 10; |
|||
|
|||
// SevSeg Constructor
|
|||
/******************************************************************************/ |
|||
SevSeg::SevSeg() { |
|||
// Initial value
|
|||
ledOnTime = 2000; // Corresponds to a brightness of 100
|
|||
waitOffTime = 0; |
|||
waitOffActive = false; |
|||
numDigits = 0; |
|||
prevUpdateIdx = 0; |
|||
prevUpdateTime = 0; |
|||
resOnSegments = 0; |
|||
updateWithDelays = 0; |
|||
} |
|||
|
|||
|
|||
// begin
|
|||
/******************************************************************************/ |
|||
// Saves the input pin numbers to the class and sets up the pins to be used.
|
|||
// If you use current-limiting resistors on your segment pins instead of the
|
|||
// digit pins, then set resOnSegments as true.
|
|||
// Set updateWithDelays to true if you want to use the 'pre-2017' update method
|
|||
// In that case, the processor is occupied with delay functions while refreshing
|
|||
// leadingZerosIn indicates whether leading zeros should be displayed
|
|||
// disableDecPoint is true when the decimal point segment is not connected, in
|
|||
// which case there are only 7 segments.
|
|||
void SevSeg::begin(byte hardwareConfig, byte numDigitsIn, byte digitPinsIn[], |
|||
byte segmentPinsIn[], bool resOnSegmentsIn, |
|||
bool updateWithDelaysIn, bool leadingZerosIn, bool disableDecPoint) { |
|||
|
|||
resOnSegments = resOnSegmentsIn; |
|||
updateWithDelays = updateWithDelaysIn; |
|||
leadingZeros = leadingZerosIn; |
|||
|
|||
numDigits = numDigitsIn; |
|||
numSegments = disableDecPoint ? 7 : 8; // Ternary 'if' statement
|
|||
//Limit the max number of digits to prevent overflowing
|
|||
if (numDigits > MAXNUMDIGITS) numDigits = MAXNUMDIGITS; |
|||
|
|||
switch (hardwareConfig) { |
|||
|
|||
case 0: // Common cathode
|
|||
digitOnVal = LOW; |
|||
segmentOnVal = HIGH; |
|||
break; |
|||
|
|||
case 1: // Common anode
|
|||
digitOnVal = HIGH; |
|||
segmentOnVal = LOW; |
|||
break; |
|||
|
|||
case 2: // With active-high, low-side switches (most commonly N-type FETs)
|
|||
digitOnVal = HIGH; |
|||
segmentOnVal = HIGH; |
|||
break; |
|||
|
|||
case 3: // With active low, high side switches (most commonly P-type FETs)
|
|||
digitOnVal = LOW; |
|||
segmentOnVal = LOW; |
|||
break; |
|||
} |
|||
|
|||
digitOffVal = !digitOnVal; |
|||
segmentOffVal = !segmentOnVal; |
|||
|
|||
// Save the input pin numbers to library variables
|
|||
for (byte segmentNum = 0 ; segmentNum < numSegments ; segmentNum++) { |
|||
segmentPins[segmentNum] = segmentPinsIn[segmentNum]; |
|||
} |
|||
|
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
digitPins[digitNum] = digitPinsIn[digitNum]; |
|||
} |
|||
|
|||
// Set the pins as outputs, and turn them off
|
|||
for (byte digit = 0 ; digit < numDigits ; digit++) { |
|||
pinMode(digitPins[digit], OUTPUT); |
|||
digitalWrite(digitPins[digit], digitOffVal); |
|||
} |
|||
|
|||
for (byte segmentNum = 0 ; segmentNum < numSegments ; segmentNum++) { |
|||
pinMode(segmentPins[segmentNum], OUTPUT); |
|||
digitalWrite(segmentPins[segmentNum], segmentOffVal); |
|||
} |
|||
|
|||
blank(); // Initialise the display
|
|||
} |
|||
|
|||
|
|||
// refreshDisplay
|
|||
/******************************************************************************/ |
|||
// Turns on the segments specified in 'digitCodes[]'
|
|||
// There are 4 versions of this function, with the choice depending on the
|
|||
// location of the current-limiting resistors, and whether or not you wish to
|
|||
// use 'update delays' (the standard method until 2017).
|
|||
// For resistors on *digits* we will cycle through all 8 segments (7 + period),
|
|||
// turning on the *digits* as appropriate for a given segment, before moving on
|
|||
// to the next segment.
|
|||
// For resistors on *segments* we will cycle through all __ # of digits,
|
|||
// turning on the *segments* as appropriate for a given digit, before moving on
|
|||
// to the next digit.
|
|||
// If using update delays, refreshDisplay has a delay between each digit/segment
|
|||
// as it cycles through. It exits with all LEDs off.
|
|||
// If not using updateDelays, refreshDisplay exits with a single digit/segment
|
|||
// on. It will move to the next digit/segment after being called again (if
|
|||
// enough time has passed).
|
|||
|
|||
void SevSeg::refreshDisplay() { |
|||
|
|||
if (!updateWithDelays) { |
|||
unsigned long us = micros(); |
|||
|
|||
// Exit if it's not time for the next display change
|
|||
if (waitOffActive) { |
|||
if (us - prevUpdateTime < waitOffTime) return; |
|||
} |
|||
else { |
|||
if (us - prevUpdateTime < ledOnTime) return; |
|||
} |
|||
prevUpdateTime = us; |
|||
|
|||
if (!resOnSegments) { |
|||
/**********************************************/ |
|||
// RESISTORS ON DIGITS, UPDATE WITHOUT DELAYS
|
|||
|
|||
if (waitOffActive) { |
|||
waitOffActive = false; |
|||
} |
|||
else { |
|||
// Turn all lights off for the previous segment
|
|||
segmentOff(prevUpdateIdx); |
|||
|
|||
if (waitOffTime) { |
|||
// Wait a delay with all lights off
|
|||
waitOffActive = true; |
|||
return; |
|||
} |
|||
} |
|||
|
|||
prevUpdateIdx++; |
|||
if (prevUpdateIdx >= numSegments) prevUpdateIdx = 0; |
|||
|
|||
// Illuminate the required digits for the new segment
|
|||
segmentOn(prevUpdateIdx); |
|||
} |
|||
else { |
|||
/**********************************************/ |
|||
// RESISTORS ON SEGMENTS, UPDATE WITHOUT DELAYS
|
|||
|
|||
if (waitOffActive) { |
|||
waitOffActive = false; |
|||
} |
|||
else { |
|||
// Turn all lights off for the previous digit
|
|||
digitOff(prevUpdateIdx); |
|||
|
|||
if (waitOffTime) { |
|||
// Wait a delay with all lights off
|
|||
waitOffActive = true; |
|||
return; |
|||
} |
|||
} |
|||
|
|||
prevUpdateIdx++; |
|||
if (prevUpdateIdx >= numDigits) prevUpdateIdx = 0; |
|||
|
|||
// Illuminate the required segments for the new digit
|
|||
digitOn(prevUpdateIdx); |
|||
} |
|||
} |
|||
|
|||
else { |
|||
if (!resOnSegments) { |
|||
/**********************************************/ |
|||
// RESISTORS ON DIGITS, UPDATE WITH DELAYS
|
|||
for (byte segmentNum = 0 ; segmentNum < numSegments ; segmentNum++) { |
|||
|
|||
// Illuminate the required digits for this segment
|
|||
segmentOn(segmentNum); |
|||
|
|||
// Wait with lights on (to increase brightness)
|
|||
delayMicroseconds(ledOnTime); |
|||
|
|||
// Turn all lights off
|
|||
segmentOff(segmentNum); |
|||
|
|||
// Wait with all lights off if required
|
|||
if (waitOffTime) delayMicroseconds(waitOffTime); |
|||
} |
|||
} |
|||
else { |
|||
/**********************************************/ |
|||
// RESISTORS ON SEGMENTS, UPDATE WITH DELAYS
|
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
|
|||
// Illuminate the required segments for this digit
|
|||
digitOn(digitNum); |
|||
|
|||
// Wait with lights on (to increase brightness)
|
|||
delayMicroseconds(ledOnTime); |
|||
|
|||
// Turn all lights off
|
|||
digitOff(digitNum); |
|||
|
|||
// Wait with all lights off if required
|
|||
if (waitOffTime) delayMicroseconds(waitOffTime); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// segmentOn
|
|||
/******************************************************************************/ |
|||
// Turns a segment on, as well as all corresponding digit pins
|
|||
// (according to digitCodes[])
|
|||
void SevSeg::segmentOn(byte segmentNum) { |
|||
digitalWrite(segmentPins[segmentNum], segmentOnVal); |
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
|
|||
digitalWrite(digitPins[digitNum], digitOnVal); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// segmentOff
|
|||
/******************************************************************************/ |
|||
// Turns a segment off, as well as all digit pins
|
|||
void SevSeg::segmentOff(byte segmentNum) { |
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
digitalWrite(digitPins[digitNum], digitOffVal); |
|||
} |
|||
digitalWrite(segmentPins[segmentNum], segmentOffVal); |
|||
} |
|||
|
|||
// digitOn
|
|||
/******************************************************************************/ |
|||
// Turns a digit on, as well as all corresponding segment pins
|
|||
// (according to digitCodes[])
|
|||
void SevSeg::digitOn(byte digitNum) { |
|||
digitalWrite(digitPins[digitNum], digitOnVal); |
|||
for (byte segmentNum = 0 ; segmentNum < numSegments ; segmentNum++) { |
|||
if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
|
|||
digitalWrite(segmentPins[segmentNum], segmentOnVal); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// digitOff
|
|||
/******************************************************************************/ |
|||
// Turns a digit off, as well as all segment pins
|
|||
void SevSeg::digitOff(byte digitNum) { |
|||
for (byte segmentNum = 0 ; segmentNum < numSegments ; segmentNum++) { |
|||
digitalWrite(segmentPins[segmentNum], segmentOffVal); |
|||
} |
|||
digitalWrite(digitPins[digitNum], digitOffVal); |
|||
} |
|||
|
|||
// setBrightness
|
|||
/******************************************************************************/ |
|||
// Sets ledOnTime according to the brightness given. Standard brightness range
|
|||
// is 0 to 100. Flickering is more likely at brightness > 100, and < -100.
|
|||
// A positive brightness introduces a delay while the LEDs are on, and a
|
|||
// negative brightness introduces a delay while the LEDs are off.
|
|||
void SevSeg::setBrightness(int brightness) { |
|||
brightness = constrain(brightness, -200, 200); |
|||
if (brightness > 0) { |
|||
ledOnTime = map(brightness, 0, 100, 1, 2000); |
|||
waitOffTime = 0; |
|||
waitOffActive = false; |
|||
} |
|||
else { |
|||
ledOnTime = 0; |
|||
waitOffTime = map(brightness, 0, -100, 1, 2000); |
|||
} |
|||
} |
|||
|
|||
|
|||
// setNumber
|
|||
/******************************************************************************/ |
|||
// This function only receives the input and passes it to 'setNewNum'.
|
|||
// It is overloaded for all number data types, so that floats can be handled
|
|||
// correctly.
|
|||
void SevSeg::setNumber(long numToShow, char decPlaces, bool hex) { //long
|
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
void SevSeg::setNumber(unsigned long numToShow, char decPlaces, bool hex) { //unsigned long
|
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
void SevSeg::setNumber(int numToShow, char decPlaces, bool hex) { //int
|
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
void SevSeg::setNumber(unsigned int numToShow, char decPlaces, bool hex) { //unsigned int
|
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
void SevSeg::setNumber(char numToShow, char decPlaces, bool hex) { //char
|
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
void SevSeg::setNumber(byte numToShow, char decPlaces, bool hex) { //byte
|
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
void SevSeg::setNumber(float numToShow, char decPlaces, bool hex) { //float
|
|||
char decPlacesPos = constrain(decPlaces, 0, MAXNUMDIGITS); |
|||
if (hex) { |
|||
numToShow = numToShow * powersOf16[decPlacesPos]; |
|||
} |
|||
else { |
|||
numToShow = numToShow * powersOf10[decPlacesPos]; |
|||
} |
|||
// Modify the number so that it is rounded to an integer correctly
|
|||
numToShow += (numToShow >= 0) ? 0.5f : -0.5f; |
|||
setNewNum(numToShow, decPlaces, hex); |
|||
} |
|||
|
|||
|
|||
// setNewNum
|
|||
/******************************************************************************/ |
|||
// Changes the number that will be displayed.
|
|||
void SevSeg::setNewNum(long numToShow, char decPlaces, bool hex) { |
|||
byte digits[numDigits]; |
|||
findDigits(numToShow, decPlaces, hex, digits); |
|||
setDigitCodes(digits, decPlaces); |
|||
} |
|||
|
|||
|
|||
// setSegments
|
|||
/******************************************************************************/ |
|||
// Sets the 'digitCodes' that are required to display the desired segments.
|
|||
// Using this function, one can display any arbitrary set of segments (like
|
|||
// letters, symbols or animated cursors). See setDigitCodes() for common
|
|||
// numeric examples.
|
|||
//
|
|||
// Bit-segment mapping: 0bHGFEDCBA
|
|||
// Visual mapping:
|
|||
// AAAA 0000
|
|||
// F B 5 1
|
|||
// F B 5 1
|
|||
// GGGG 6666
|
|||
// E C 4 2
|
|||
// E C 4 2 (Segment H is often called
|
|||
// DDDD H 3333 7 DP, for Decimal Point)
|
|||
void SevSeg::setSegments(byte segs[]) { |
|||
for (byte digit = 0; digit < numDigits; digit++) { |
|||
digitCodes[digit] = segs[digit]; |
|||
} |
|||
} |
|||
|
|||
// setChars
|
|||
/******************************************************************************/ |
|||
// Displays the string on the display, as best as possible.
|
|||
// Only alphanumeric characters plus '-' and ' ' are supported
|
|||
void SevSeg::setChars(char str[]) { |
|||
for (byte digit = 0; digit < numDigits; digit++) { |
|||
digitCodes[digit] = 0; |
|||
} |
|||
|
|||
byte strIdx = 0; // Current position within str[]
|
|||
for (byte digitNum = 0; digitNum < numDigits; digitNum++) { |
|||
char ch = str[strIdx]; |
|||
if (ch == '\0') break; // NULL string terminator
|
|||
if (ch >= '0' && ch <= '9') { // Numerical
|
|||
digitCodes[digitNum] = numeralCodes[ch - '0']; |
|||
} |
|||
else if (ch >= 'A' && ch <= 'Z') { |
|||
digitCodes[digitNum] = alphaCodes[ch - 'A']; |
|||
} |
|||
else if (ch >= 'a' && ch <= 'z') { |
|||
digitCodes[digitNum] = alphaCodes[ch - 'a']; |
|||
} |
|||
else if (ch == ' ') { |
|||
digitCodes[digitNum] = digitCodeMap[BLANK_IDX]; |
|||
} |
|||
else if (ch == '.') { |
|||
digitCodes[digitNum] = digitCodeMap[PERIOD_IDX]; |
|||
} |
|||
else if (ch == '*') { |
|||
digitCodes[digitNum] = digitCodeMap[ASTERISK_IDX]; |
|||
} |
|||
else { |
|||
// Every unknown character is shown as a dash
|
|||
digitCodes[digitNum] = digitCodeMap[DASH_IDX]; |
|||
} |
|||
|
|||
strIdx++; |
|||
// Peek at next character. It it's a period, add it to this digit
|
|||
if (str[strIdx] == '.') { |
|||
digitCodes[digitNum] |= digitCodeMap[PERIOD_IDX]; |
|||
strIdx++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// blank
|
|||
/******************************************************************************/ |
|||
void SevSeg::blank(void) { |
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
digitCodes[digitNum] = digitCodeMap[BLANK_IDX]; |
|||
} |
|||
segmentOff(0); |
|||
digitOff(0); |
|||
} |
|||
|
|||
// findDigits
|
|||
/******************************************************************************/ |
|||
// Decides what each digit will display.
|
|||
// Enforces the upper and lower limits on the number to be displayed.
|
|||
|
|||
void SevSeg::findDigits(long numToShow, char decPlaces, bool hex, byte digits[]) { |
|||
const long * powersOfBase = hex ? powersOf16 : powersOf10; |
|||
const long maxNum = powersOfBase[numDigits] - 1; |
|||
const long minNum = -(powersOfBase[numDigits - 1] - 1); |
|||
|
|||
// If the number is out of range, just display dashes
|
|||
if (numToShow > maxNum || numToShow < minNum) { |
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
digits[digitNum] = DASH_IDX; |
|||
} |
|||
} |
|||
else { |
|||
byte digitNum = 0; |
|||
|
|||
// Convert all number to positive values
|
|||
if (numToShow < 0) { |
|||
digits[0] = DASH_IDX; |
|||
digitNum = 1; // Skip the first iteration
|
|||
numToShow = -numToShow; |
|||
} |
|||
|
|||
// Find all digits for base's representation, starting with the most
|
|||
// significant digit
|
|||
for ( ; digitNum < numDigits ; digitNum++) { |
|||
long factor = powersOfBase[numDigits - 1 - digitNum]; |
|||
digits[digitNum] = numToShow / factor; |
|||
numToShow -= digits[digitNum] * factor; |
|||
} |
|||
|
|||
// Find unnnecessary leading zeros and set them to BLANK
|
|||
if (decPlaces < 0) decPlaces = 0; |
|||
if (!leadingZeros) { |
|||
for (digitNum = 0 ; digitNum < (numDigits - 1 - decPlaces) ; digitNum++) { |
|||
if (digits[digitNum] == 0) { |
|||
digits[digitNum] = BLANK_IDX; |
|||
} |
|||
// Exit once the first non-zero number is encountered
|
|||
else if (digits[digitNum] <= 9) { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
// setDigitCodes
|
|||
/******************************************************************************/ |
|||
// Sets the 'digitCodes' that are required to display the input numbers
|
|||
|
|||
void SevSeg::setDigitCodes(byte digits[], char decPlaces) { |
|||
|
|||
// Set the digitCode for each digit in the display
|
|||
for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { |
|||
digitCodes[digitNum] = digitCodeMap[digits[digitNum]]; |
|||
// Set the decimal point segment
|
|||
if (decPlaces >= 0) { |
|||
if (digitNum == numDigits - 1 - decPlaces) { |
|||
digitCodes[digitNum] |= digitCodeMap[PERIOD_IDX]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// END ///
|
@ -0,0 +1,95 @@ |
|||
/* SevSeg Library
|
|||
* |
|||
* Copyright 2017 Dean Reading |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
* |
|||
* |
|||
* This library allows an Arduino to easily display numbers and letters on a |
|||
* 7-segment display without a separate 7-segment display controller. |
|||
* |
|||
* Direct any questions or suggestions to deanreading@hotmail.com |
|||
* See the included readme for instructions. |
|||
* https://github.com/DeanIsMe/SevSeg
|
|||
*/ |
|||
|
|||
#ifndef MAXNUMDIGITS |
|||
#define MAXNUMDIGITS 8 // Can be increased, but the max number is 2^31
|
|||
#endif |
|||
|
|||
#ifndef SevSeg_h |
|||
#define SevSeg_h |
|||
|
|||
#if defined(ARDUINO) && ARDUINO >= 100 |
|||
#include "Arduino.h" |
|||
#else |
|||
#include "WProgram.h" |
|||
#endif |
|||
|
|||
// Use defines to link the hardware configurations to the correct numbers
|
|||
#define COMMON_CATHODE 0 |
|||
#define COMMON_ANODE 1 |
|||
#define N_TRANSISTORS 2 |
|||
#define P_TRANSISTORS 3 |
|||
#define NP_COMMON_CATHODE 1 |
|||
#define NP_COMMON_ANODE 0 |
|||
|
|||
|
|||
class SevSeg |
|||
{ |
|||
public: |
|||
SevSeg(); |
|||
|
|||
void refreshDisplay(); |
|||
void begin(byte hardwareConfig, byte numDigitsIn, byte digitPinsIn[], |
|||
byte segmentPinsIn[], bool resOnSegmentsIn=0, |
|||
bool updateWithDelaysIn=0, bool leadingZerosIn=0, |
|||
bool disableDecPoint=0); |
|||
void setBrightness(int brightnessIn); // A number from 0..100
|
|||
|
|||
void setNumber(long numToShow, char decPlaces=-1, bool hex=0); |
|||
void setNumber(unsigned long numToShow, char decPlaces=-1, bool hex=0); |
|||
void setNumber(int numToShow, char decPlaces=-1, bool hex=0); |
|||
void setNumber(unsigned int numToShow, char decPlaces=-1, bool hex=0); |
|||
void setNumber(char numToShow, char decPlaces=-1, bool hex=0); |
|||
void setNumber(byte numToShow, char decPlaces=-1, bool hex=0); |
|||
void setNumber(float numToShow, char decPlaces=-1, bool hex=0); |
|||
|
|||
void setSegments(byte segs[]); |
|||
void setChars(char str[]); |
|||
void blank(void); |
|||
|
|||
private: |
|||
void setNewNum(long numToShow, char decPlaces, bool hex=0); |
|||
void findDigits(long numToShow, char decPlaces, bool hex, byte digits[]); |
|||
void setDigitCodes(byte nums[], char decPlaces); |
|||
void segmentOn(byte segmentNum); |
|||
void segmentOff(byte segmentNum); |
|||
void digitOn(byte digitNum); |
|||
void digitOff(byte digitNum); |
|||
|
|||
bool digitOnVal,digitOffVal,segmentOnVal,segmentOffVal; |
|||
bool resOnSegments, updateWithDelays, leadingZeros; |
|||
byte digitPins[MAXNUMDIGITS]; |
|||
byte segmentPins[8]; |
|||
byte numDigits; |
|||
byte numSegments; |
|||
byte prevUpdateIdx; // The previously updated segment or digit
|
|||
byte digitCodes[MAXNUMDIGITS]; // The active setting of each segment of each digit
|
|||
unsigned long prevUpdateTime; // The time (millis()) when the display was last updated
|
|||
int ledOnTime; // The time (us) to wait with LEDs on
|
|||
int waitOffTime; // The time (us) to wait with LEDs off
|
|||
bool waitOffActive; // Whether the program is waiting with LEDs off
|
|||
}; |
|||
|
|||
#endif //SevSeg_h
|
|||
/// END ///
|
@ -0,0 +1,27 @@ |
|||
/*
|
|||
* Simple code to test the display function and electrical connections. |
|||
*/ |
|||
#include <SevSeg.h> |
|||
|
|||
//Instantiates a seven segment object
|
|||
SevSeg sevseg; |
|||
|
|||
void setup() { |
|||
byte numDigits = 4; |
|||
byte digitPins[] = {14, 17, 18, 12}; |
|||
byte segmentPins[] = {15, 19, 7, 9, 6, 16, 8}; |
|||
bool resistorsOnSegments = false; |
|||
byte hardwareConfig = COMMON_CATHODE; |
|||
bool updateWithDelays = false; |
|||
bool leadingZeros = false; |
|||
bool disableDecPoint = true; |
|||
|
|||
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, |
|||
updateWithDelays, leadingZeros, disableDecPoint); |
|||
} |
|||
|
|||
void loop() { |
|||
sevseg.setNumber(555,0); // Displays '3.141'
|
|||
sevseg.refreshDisplay(); |
|||
sevseg.setBrightness(100); |
|||
} |
@ -0,0 +1,97 @@ |
|||
/*
|
|||
* TODO: write something here |
|||
*/ |
|||
#include <SevSeg.h> |
|||
#include "DHT.h" |
|||
|
|||
// Digital pin connected to the DHT sensor
|
|||
#define DHTPIN 2 |
|||
#define DHTTYPE DHT11 // DHT 11
|
|||
#define BUFFER_SIZE 24 |
|||
#define LOW_TEMP_LED_PIN 3 |
|||
#define HIGH_TEMP_LED_PIN 4 |
|||
#define RELAY_PIN 5 |
|||
#define LOW_TEMP 28 |
|||
#define HIGH_TEMP 28 |
|||
|
|||
// Initializes DHT sensor.
|
|||
DHT dht(DHTPIN, DHTTYPE); |
|||
|
|||
int temperatureReadingsCounter; |
|||
float temperatures[BUFFER_SIZE]; |
|||
|
|||
void setup() { |
|||
Serial.begin(9600); |
|||
Serial.println("Running setup function"); |
|||
|
|||
dht.begin(); |
|||
temperatureReadingsCounter = 0; |
|||
pinMode(LOW_TEMP_LED_PIN, OUTPUT); |
|||
pinMode(HIGH_TEMP_LED_PIN, OUTPUT); |
|||
pinMode(RELAY_PIN, OUTPUT); |
|||
} |
|||
|
|||
void loop() { |
|||
float lastTemperature = getNewTemp(); |
|||
|
|||
if (lastTemperature >= HIGH_TEMP) { |
|||
// Turns red led and relay on
|
|||
digitalWrite(HIGH_TEMP_LED_PIN, HIGH); |
|||
digitalWrite(RELAY_PIN, HIGH); |
|||
// Turns blue led off
|
|||
digitalWrite(LOW_TEMP_LED_PIN, LOW); |
|||
} else if (lastTemperature < LOW_TEMP) { |
|||
// Turns blue led on
|
|||
digitalWrite(LOW_TEMP_LED_PIN, HIGH); |
|||
// Turns red led off
|
|||
digitalWrite(HIGH_TEMP_LED_PIN, LOW); |
|||
digitalWrite(RELAY_PIN, LOW); |
|||
} else { |
|||
// Turns red led off
|
|||
digitalWrite(LOW_TEMP_LED_PIN, LOW); |
|||
// Turns blue led off
|
|||
digitalWrite(HIGH_TEMP_LED_PIN, LOW); |
|||
digitalWrite(RELAY_PIN, LOW); |
|||
} |
|||
|
|||
if (temperatureReadingsCounter == BUFFER_SIZE) { |
|||
float avgTemp = calcAverageTempAndReset(temperatures); |
|||
Serial.println(avgTemp); |
|||
temperatureReadingsCounter = 0; |
|||
} |
|||
// Wait a few seconds between measurements.
|
|||
Serial.println(temperatureReadingsCounter); |
|||
delay(1000); |
|||
} |
|||
|
|||
float getNewTemp(){ |
|||
// Reads temperature as Celsius (the default)
|
|||
float t = dht.readTemperature(); |
|||
|
|||
// Checks if the read failed and handles the failure
|
|||
if (isnan(t)) { |
|||
Serial.print(F("Failed to read from DHT sensor!")); |
|||
return; |
|||
} |
|||
|
|||
for (int i = BUFFER_SIZE - 1; i>0; i--) { |
|||
temperatures[i] = temperatures[i-1]; |
|||
} |
|||
temperatures[0] = t; |
|||
++temperatureReadingsCounter; |
|||
|
|||
Serial.print(F("Temperature: ")); |
|||
Serial.print(t); |
|||
Serial.println(F("°C ")); |
|||
|
|||
return t; |
|||
} |
|||
|
|||
float calcAverageTempAndReset(float *array) { |
|||
float sum = 0 ; // sum will be larger than an item, float for safety.
|
|||
for (int i = 0; i<BUFFER_SIZE; i++){ |
|||
sum += array[i]; |
|||
} |
|||
|
|||
return sum / BUFFER_SIZE ; // average will be fractional
|
|||
} |
Loading…
Reference in new issue