//-----------------------------------------------------------------
// Author: RSP @KMUTNB
// Date: 2023-02-10 (rev1)
//-----------------------------------------------------------------
// Don't forget to include libraries
#include <Adafruit_NeoPixel.h>
#include <LiquidCrystal_I2C.h>
#include <SimpleDHT.h>
#include <RTClib.h>
#include <Arduino_FreeRTOS.h>
#include "semphr.h"
#define BTN_PIN (6)
#define AIN_PIN (A0)
#define PWM_PIN (9)
#define NEOPIXEL_PIN (5)
#define ECHO_PIN (2)
#define TRIG_PIN (4)
#define DHT_PIN (8)
#define DHT_TYPE DHT22 // DHT 22 (AM2302), AM2321
// I2C pins SDA/SCL = A4/A5 pins
#define LCD_I2C_ADDR (0x27)
Adafruit_NeoPixel pixels( 8, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
LiquidCrystal_I2C lcd( LCD_I2C_ADDR, 16, 2 );
SimpleDHT22 dht(DHT_PIN);
RTC_DS1307 rtc; // Adafruit RTClib uses 0x68 as the default address.
extern unsigned int __heap_start;
extern char *__brkval;
/*
__brkval is a symbol in the AVR (Atmel microcontroller) environment that is used by the GCC (GNU Compiler Collection) C library to keep track of the current position of the heap end (i.e., the end of the dynamically-allocated memory). It's used by the malloc() and free() functions to allocate and deallocate memory dynamically at runtime.
In AVR-GCC, __brkval is defined in the "malloc.c" file and is updated by the sbrk() function, which is called by malloc() when it needs to allocate more memory. The value of __brkval is the address of the first byte after the last block of memory that has been allocated by malloc(). If __brkval is zero, it means that no memory has been allocated yet, or all previously allocated memory has been freed.
*/
int freeMemory() {
char top_of_stack;
if (__brkval == 0) {
return (((int)&top_of_stack - (int)&__heap_start));
} else {
return ((int)&top_of_stack - (int)__brkval);
}
}
// global variables
uint16_t duration_us, distance_cm;
int temperature, humidity;
uint16_t adc_value, pwm_value;
DateTime datetime;
SemaphoreHandle_t xMutex;
void task1( void *pvParameters ) {
uint8_t disp_mode = 0;
String str1 = "", str2 = "";
for (;;) {
// Detect a button press to toggle LCD display mode
if (digitalRead(BTN_PIN) == LOW) {
while (!digitalRead(BTN_PIN)) {
vTaskDelay(40 / portTICK_PERIOD_MS);
}
disp_mode = (disp_mode + 1) % 4; // up to 4 modes
}
if ( xSemaphoreTake(xMutex, (TickType_t) 5) != pdTRUE ) {
continue;
}
// Update the LCD display
switch (disp_mode) {
case 0: // show ADC value and NeoPixel light level
char lcd_buf[17];
sprintf_P( lcd_buf, (PGM_P)F("ADC: %04d (10b)"), adc_value );
str1 = lcd_buf;
sprintf_P( lcd_buf, (PGM_P)F("PWM: %3d%%"), (pwm_value + 1) * 100 / 256 );
str2 = lcd_buf;
break;
case 1: // show pulse width and distance
str1 = F(" PW[us]: ");
str1 += duration_us;
str2 = F("Dist.[cm]: ");
str2 += distance_cm / 10;
str2 += '.';
str2 += distance_cm % 10;
break;
case 2: // show temperature and relative humidity
str1 = F(" Temp.[C]: ");
str1 += temperature / 10;
str1 += '.';
str1 += temperature % 10;
str2 = F("Humid.[%]: ");
str2 += humidity / 10;
str2 += '.';
str2 += humidity % 10;
break;
case 3:
str1 = String(F("Date: "));
str1 += datetime.timestamp(DateTime::TIMESTAMP_DATE);
str2 = String(F("Time: "));
str2 += datetime.timestamp(DateTime::TIMESTAMP_TIME);
break;
default:
disp_mode = 0;
break;
}
xSemaphoreGive(xMutex);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print( str1.c_str() );
str1 = "";
lcd.setCursor(0, 1);
lcd.print( str2.c_str() );
str2 = "";
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
void task2( void *pvParameters ) {
uint16_t _value;
for (;;) {
// Send a Trigger pulse
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(20);
digitalWrite(TRIG_PIN, LOW);
// Measure a pulse width
_value = pulseIn(ECHO_PIN, HIGH, 25000 /*timeout*/ );
if ( xSemaphoreTake(xMutex, (TickType_t) 1) == pdTRUE ) {
// Update the pulse width [usec]
duration_us = _value;
// Update the distance [cm]
distance_cm = (duration_us / 2) * 343UL / 1000;
xSemaphoreGive(xMutex);
}
taskYIELD();
if ( xSemaphoreTake(xMutex, (TickType_t) 1) == pdTRUE ) {
// Read RTC
datetime = rtc.now();
xSemaphoreGive(xMutex);
}
taskYIELD();
// Read DHT22 (temperature and relative humidity)
float _temp, _humid;
if (dht.read2(&_temp, &_humid, NULL) == SimpleDHTErrSuccess) {
if ( xSemaphoreTake(xMutex, (TickType_t) 1) == pdTRUE ) {
temperature = (int)10 * _temp;
humidity = (int)10 * _humid;
xSemaphoreGive(xMutex);
}
}
taskYIELD();
// Read a value from the analog input pin
_value = analogRead( AIN_PIN );
if ( xSemaphoreTake(xMutex, (TickType_t) 1) == pdTRUE ) {
// Update the ADC value
adc_value = _value;
// Update the PWM duty cycle
pwm_value = adc_value / 4;
xSemaphoreGive(xMutex);
}
taskYIELD();
// Update the PWM output
analogWrite( PWM_PIN, pwm_value );
// Update the colors of NeoPixels
for (uint8_t i = 0; i < 8; i++) {
_value = (pwm_value + 16) / 32;
pixels.setPixelColor(i, (i < _value) ?
pixels.Color(200, 0, 0) :
pixels.Color(0, 0, 0) );
pixels.show();
}
taskYIELD();
}
}
void setup() {
Serial.begin(115200);
Serial.println( F("Arduino Nano Demo...") );
Serial.println( F("- Analog input reading (potentiometer)") );
Serial.println( F("- PWM LED dimming") );
Serial.println( F("- Push button") );
Serial.println( F("- LCD16x2 I2C display module") );
Serial.println( F("- 8-pixel NeoPixel RGB ring") );
Serial.println( F("- DHT22 temperature & humidity sensor") );
Serial.println( F("- RTC I2C DS1307") );
pinMode( BTN_PIN, INPUT_PULLUP );
pinMode( TRIG_PIN, OUTPUT );
pinMode( ECHO_PIN, INPUT );
pinMode( PWM_PIN, OUTPUT );
digitalWrite( TRIG_PIN, LOW );
digitalWrite( PWM_PIN, LOW );
pixels.begin(); // Initialize Neopixel ring
pixels.clear(); // Clear Neopixel ring
lcd.init(); // Initialize LCD display
lcd.backlight(); // Turn on LCD backlight
Wire.setClock(400000);
rtc.begin();
if (!rtc.isrunning()) { // use compilation time to set date and time for RTC
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// Create a mutex
xMutex = xSemaphoreCreateMutex();
// Create tasks
xTaskCreate(task1, "T1", 196, NULL, 2, NULL);
xTaskCreate(task2, "T2", 196, NULL, 1, NULL);
Serial.print( F("Free memory: ") );
Serial.println( freeMemory() );
}
void loop() {
}
//-----------------------------------------------------------------