То с чего начинают все ардуинщики (а часто и заканчивают):
Обычная "метеостанция" с тремя датчиками c aliexpress. Но мне понравилось запрограммировать её в терминах message loop.
/*
Complex Meteo
Arduino Nano V3.0
Processor: ATmega328P (Old Bootloader)
Programmator: AVRISP mkII
*/
// Arduino pins:
#define PIN_PHOTO_SENSOR A0
#define PIN_LED 13
#define PIN_TEMP_OUT_D 10
#define PIN_DHT_D 2 // Digital pin connected to the DHT sensor
#define DHTTYPE DHT21 // DHT 21 (AM2301)
#define QUEUE_LENGTH 50 // max number of messages in the queue
#define PRECISION 30 // ms. to pause each cycle
#define BLINK_RATE 1000 // default is 1000 = 1 sec
#define DISPLAY_ON_TIME 3000 // default is 5000 = 5 seconds
#define LIGHT_DELAY 1000 // default is 1000 = 1 seconds - read lights conditions every 1 second
#define TEMP_OUT_DELAY 3000 // default is 3000 = read outdoor conditions every 3 seconds
#define TEMP_IN_DELAY 3000 // default is 3000 = read indoor conditions every 3 seconds
#define HIST_SIZE 10 // how many temp/humidity values to keep
#define OLED_SCREEN_ADDRESS 0x3C
#include "SPI.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "math.h"
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define xy(x,y,z) display.setCursor(x,y); display.print(z); /* output z starting at x,y */
// message types:
#define M_BLINK_ON 1 // turn light on
#define M_BLINK_OFF 2 // turn light off
#define M_GET_TEMP_INDOORS 3 // fetch temp & humidity
#define M_GET_TEMP_OUTDOORS 4 // fetch temp outdoors
#define M_OUT_OLED 5 // output temp and humidity data
#define M_GET_LIGHT 6 // read light conditions using photo sensor
#define M_OLED_FADE 7 // fade display
#define M_OUT_OLED_TEMP_INDOORS 8 // output indoors temperature
#define M_OUT_OLED_TEMP_OUTDOORS 9 // output outdoors temp
#define M_OUT_OLED_HUMIDITY 10 // output humidity data
#define M_OUT_OLED_DISPLAY 11 // output all
#define M_OUT_OLED_DISPLAY_OFF 12 // end of output all
#define M_OUT_OLED_DISPLAY_3 13 // end of output all
#define M_OUT_OLED_DISPLAY_2 14 // end of output all
#define M_RECALCULATE_TRENDS_TO 15 // recalculate grows or falls of sensor data (trends.ti, trends.to, trends.h)
#define M_RECALCULATE_TRENDS_TI 16 // recalculate grows or falls of sensor data (trends.ti, trends.to, trends.h)
#define M_RECALCULATE_TRENDS_HU 17 // recalculate grows or falls of sensor data (trends.ti, trends.to, trends.h)
#define M_BLINK 18 // blink the red light
#define arrow_up 0x18
#define arrow_down 0x19
// message queue:
struct {
unsigned long m_when; // time in ms. when queued
unsigned long m_delay; // delay from m_when - time to be executed
char m_what; // func
} m_queue[QUEUE_LENGTH]; // the queue of ops
#include "OneWire.h"
#include "DallasTemperature.h"
#include "DHT.h"
// Feather HUZZAH ESP8266 note: use pins 3, 4, 5, 12, 13 or 14 --
// Pin 15 can work but DHT must be disconnected during program upload.
// Uncomment whatever type you're using!
//#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (RED) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1 to 3.3V instead of 5V!
// Connect pin 2 (YELLOW) of the sensor to whatever your DHTPIN is
// Connect pin 4 (BLACK) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (YELLOW, data) to pin 1 (RED, power) of the sensor
// Initialize DHT sensor.
// Note that older versions of this library took an optional third parameter to
// tweak the timings for faster processors. This parameter is no longer needed
// as the current DHT reading algorithm adjusts itself to work on faster procs.
DHT dht(PIN_DHT_D, DHTTYPE);
OneWire oneWire(PIN_TEMP_OUT_D); // порт подключения датчика (D10)
DallasTemperature ds(&oneWire);
// temperature and humidity data:
short to[HIST_SIZE], ti[HIST_SIZE], hu[HIST_SIZE];
struct {
char to; // temp outdoors
char ti; // temp indoors
char hu; // humidity indoors
} index, trends, first;
char display_all_state = 0;
char led_is_on = 0;
short light_prev = 0;
short blink_rate = BLINK_RATE;
void setup() {
unsigned long ms = micros();
Serial.begin(9600);
Serial.println("Hello world");
index.to = index.ti = index.hu = 0; // array index
trends.to = trends.ti = trends.hu = ' ';
first.to = first.ti = first.hu = 1;
ds.begin(); // инициализация датчика ds18b20
dht.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
for (short i = 0; i < QUEUE_LENGTH; i++) m_queue[i].m_what = 0; // clear queue
// Push initial messages into the queue:
msg_queue(M_OLED_FADE, ms, 0); // clear display
msg_queue(M_BLINK, ms, 0); // launch the first blink
msg_queue(M_GET_TEMP_INDOORS, ms, 1000); // launch the DTH sensor
msg_queue(M_GET_TEMP_OUTDOORS, ms, 1000); //
msg_queue(M_RECALCULATE_TRENDS_TO, ms, 5 * 1000); // recalculate trends when data accomulated
msg_queue(M_RECALCULATE_TRENDS_TI, ms, 6 * 1000); // recalculate trends when data accomulated
msg_queue(M_RECALCULATE_TRENDS_HU, ms, 7 * 1000); // recalculate trends when data accomulated
msg_queue(M_GET_LIGHT, ms, 1000); // read photo sensor
msg_queue(M_OUT_OLED_DISPLAY, ms, 3000); // output data to display
}
static inline char sign(short val) {
return ( (val < 0) ? -1 : ((val > 0) ? +1 : 0));
}
char trend(char what, short *data, char ind) { // is data growing or not?
char ind_next = (ind + 2) % HIST_SIZE;
char ind_prev = (ind + 1) % HIST_SIZE;
short s = 0;
short delta = data[ind] - data[ind_prev];
/*
for (;;) {
s += sign(data[ind_next] - data[ind_prev]);
if (ind_next == ind) break;
ind_prev = ind_next;
ind_next = (ind_next + 1) % HIST_SIZE;
}
*/
//Serial.print(what); Serial.print(": delta/signs = "); Serial.print(delta); Serial.print("/"); Serial.println(s); delay(100);
return (abs(delta) < HIST_SIZE) ? ' ' : ((delta > 0) ? arrow_up : arrow_down);
}
void loop() {
// put your main code here, to run repeatedly:
unsigned long time_now;
short light;
time_now = millis();
for (short i = QUEUE_LENGTH; i >= 0; i--) { // any op to execute?
if (!m_queue[i].m_what) continue;
if ((time_now - m_queue[i].m_when) < m_queue[i].m_delay) continue; // the only way to measure intervals properly
switch (m_queue[i].m_what) {
case M_RECALCULATE_TRENDS_TO:
trends.to = trend('o',to, index.to);
if(trends.to!=' ') msg_queue(M_OUT_OLED_TEMP_OUTDOORS, time_now, 0);
msg_queue(M_RECALCULATE_TRENDS_TO, time_now, TEMP_IN_DELAY);
break;
case M_RECALCULATE_TRENDS_TI:
trends.ti = trend('i',ti, index.ti);
if(trends.ti!=' ') msg_queue(M_OUT_OLED_TEMP_INDOORS, time_now, 0);
msg_queue(M_RECALCULATE_TRENDS_TI, time_now, TEMP_IN_DELAY);
break;
case M_RECALCULATE_TRENDS_HU:
trends.hu = trend('h',hu, index.hu);
if(trends.hu!=' ') msg_queue(M_OUT_OLED_HUMIDITY, time_now, 0);
msg_queue(M_RECALCULATE_TRENDS_HU, time_now, TEMP_IN_DELAY);
break;
case M_BLINK:
msg_queue(M_BLINK, time_now, blink_rate); // to blink again in a few moments
msg_queue(M_BLINK_ON, time_now, 0); // turn on
msg_queue(M_BLINK_OFF, time_now, blink_rate / 20); // turn off
break;
case M_BLINK_ON:
digitalWrite(PIN_LED, HIGH);
break;
case M_BLINK_OFF:
digitalWrite(PIN_LED, LOW);
break;
case M_GET_TEMP_OUTDOORS:
msg_queue(M_GET_TEMP_OUTDOORS, time_now, TEMP_OUT_DELAY);
ds.requestTemperatures(); // считываем температуру с датчика
// Read temperature outdoors:
to[index.to = (index.to + 1) % HIST_SIZE] = round(ds.getTempCByIndex(0) * 10);
if(first.to) { first.to=0; for(char i=0;i < HIST_SIZE;i++) to[i]=to[index.to]; } // fill in all the historical data with the same value
break;
case M_GET_TEMP_INDOORS:
msg_queue(M_GET_TEMP_INDOORS, time_now, TEMP_IN_DELAY); // launch the DTH sensor again
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (a very slow sensor)
// Read humidity:
hu[index.hu = (index.hu + 1) % HIST_SIZE] = round(dht.readHumidity() * 10);
if(first.hu) { first.hu=0; for(i=0;i < HIST_SIZE;i++) hu[i]=hu[index.hu]; } // fill in all the historical data with the same value
// Read temperature as Celsius (the default):
ti[index.ti = (index.ti + 1) % HIST_SIZE] = round(dht.readTemperature() * 10);
if(first.ti) { first.ti=0; for(i=0;i < HIST_SIZE;i++) ti[i]=ti[index.ti]; } // fill in all the historical data with the same value
//if (isnan(hu) || isnan(ti)) break; // Check if any reads failed and exit early (to try again).
break;
case M_OUT_OLED_DISPLAY_OFF:
display_all_state = 0;
break;
case M_OUT_OLED_DISPLAY:
if (display_all_state) break;
display_all_state = 1;
msg_queue(M_OUT_OLED_DISPLAY_3, time_now, 0);
msg_queue(M_OUT_OLED_DISPLAY_2, time_now, 1 * (DISPLAY_ON_TIME + 500));
msg_queue(M_OUT_OLED_TEMP_OUTDOORS, time_now, 2 * (DISPLAY_ON_TIME + 500));
msg_queue(M_OUT_OLED_TEMP_INDOORS, time_now, 3 * (DISPLAY_ON_TIME + 500));
msg_queue(M_OUT_OLED_HUMIDITY, time_now, 4 * (DISPLAY_ON_TIME + 500));
msg_queue(M_OUT_OLED_DISPLAY_OFF, time_now, 2 * DISPLAY_ON_TIME + 500 + 1000);
break;
case M_OUT_OLED_DISPLAY_3:
display_3();
break;
case M_OUT_OLED_DISPLAY_2:
display_2();
break;
case M_OUT_OLED_TEMP_INDOORS:
display_temp(ti[index.ti], 1);
break;
case M_OUT_OLED_TEMP_OUTDOORS:
display_temp(to[index.to], 0);
break;
case M_OUT_OLED_HUMIDITY:
display_humidity(1);
break;
case M_OLED_FADE:
oled_clear();
break;
case M_GET_LIGHT:
msg_queue(M_GET_LIGHT, time_now, LIGHT_DELAY); // get light conditions again in 1 second
light = analogRead(PIN_PHOTO_SENSOR); // [1..1024] 300 +/-
if ((abs(light - light_prev) > 100) && (!display_all_state))
msg_queue(M_OUT_OLED_DISPLAY, time_now, 0);
light_prev = light;
break;
default:
break;
}
m_queue[i].m_what = 0; // Processed. Do not forget to clear it
break; // only one per loop
}
delay(PRECISION);
}
void msg_queue(char my_what, unsigned long my_when, unsigned long my_delay) { // add op to the queue
for (short i = 0; i < QUEUE_LENGTH; i++) {
if (!m_queue[i].m_what) { // empty slot found
m_queue[i].m_what = my_what;
m_queue[i].m_when = my_when,
m_queue[i].m_delay = my_delay;
return;
}
}
}
void oled_clear() { // clear display
display.clearDisplay();
display.display();
led_is_on = 0;
blink_rate = BLINK_RATE;
}
void short_to_string(short input, char *output) {
sprintf(output, "%2d.%d", input / 10, input % 10);
}
#define x(coordinate) dx+coordinate
#define y(coordinate) dy+coordinate
void display_humidity(uint16_t c) { // display humidity only
short h = hu[index.hu];
char os[10]; short dx = 0, dy = 2;
char arrow[2] = "?";
if (led_is_on) {
//msg_queue(M_OUT_OLED_HUMIDITY, millis(), 1000);
return;
}
led_is_on = 1;
msg_queue(M_OLED_FADE, millis(), DISPLAY_ON_TIME); // fade out
display.fillRect(0, 0, 128, 64, 1 - c);
// blink_rate = 20;
display.setTextSize(4);
display.setTextColor(c);
short_to_string(h, os); strcat(os, "%"); xy(x(0), y(0), os);
arrow[0] = trends.hu; display.setTextSize(2); xy(x(55), y(0), arrow);
display.display();
}
void display_temp(short t, uint16_t c) { // display only one temperature value
char os[10]; short dx = 0, dy = 2;
char arrow[2] = "?";
if (led_is_on) {
//msg_queue(c ? M_OUT_OLED_TEMP_INDOORS : M_OUT_OLED_TEMP_OUTDOORS, millis(), 1000);
return;
}
led_is_on = 1;
msg_queue(M_OLED_FADE, millis(), DISPLAY_ON_TIME); // fade out
display.fillRect(0, 0, 128, 64, 1 - c);
// blink_rate = 20;
display.setTextSize(4);
display.setTextColor(c);
short_to_string(abs(t), os + 1);
os[0] = (t >= 0) ? '+' : '-';
xy(x(0), y(0), os);
arrow[0] = c ? trends.ti : trends.to; display.setTextSize(2); xy(x(79), y(0), arrow);
display.drawCircle(x(123), y(4), 4, c); display.drawCircle(x(123), y(4), 3, c);
display.display();
}
void display_2() { // display 2 temperature values: in & out
char os[10]; short dx = 0, dy = 1;
char c[4] = "C ?";
if (led_is_on) {
//msg_queue(M_OUT_OLED_DISPLAY_2, millis(), 1000);
return;
}
led_is_on = 1;
msg_queue(M_OLED_FADE, millis(), DISPLAY_ON_TIME); // fade out
led_is_on = 1;
// blink_rate = 20;
display.fillRect(0, 0, 128, 64, 0);
display.setTextSize(2);
display.setTextColor(WHITE);
c[0] = 'C';
short_to_string(abs(to[index.to]), os + 1); os[0] = (to[index.to] >= 0) ? '+' : '-'; xy(x(3), y(0), os);
c[2] = trends.to; xy(x(80), y(00), c); display.drawCircle(x(72), y(2), 2, 1);
short_to_string(abs(ti[index.ti]), os + 1); os[0] = (ti[index.ti] >= 0) ? '+' : '-'; xy(x(3), y(16), os);
c[2] = trends.ti; xy(x(80), y(16), c); display.drawCircle(x(72), y(18), 2, 1);
display.display();
}
void display_3() { // display 3 values: 2 temperature (in & out) & humidity
char os[10];
char c[4] = "C ?";
short dx = 0, dy = 1;
char degree[2] = {248, 0};
if (led_is_on) {
//msg_queue(M_OUT_OLED_DISPLAY_3, millis(), 1000);
return;
}
led_is_on = 1;
msg_queue(M_OLED_FADE, millis(), DISPLAY_ON_TIME); // fade out
led_is_on = 1;
// blink_rate = 20;
display.fillRect(0, 0, 128, 64, 0);
display.setTextSize(1);
display.setTextColor(WHITE);
xy(x(0), y(0), "Temp.out: ");
xy(x(0), y(10), "Temp. in: ");
xy(x(0), y(20), "Humidity: ");
short_to_string(abs(to[index.to]), os + 1); os[0] = (to[index.to] >= 0) ? '+' : '-'; xy(x(60), y(00), os);
short_to_string(abs(ti[index.ti]), os + 1); os[0] = (ti[index.ti] >= 0) ? '+' : '-'; xy(x(60), y(10), os);
short_to_string(abs(hu[index.hu]), os + 1); os[0] = ' '; xy(x(60), y(20), os);
display.drawCircle(x(97), y(1), 1, 1);
display.drawCircle(x(97), y(11), 1, 1);
c[0] = 'C';
c[2] = trends.to; xy(x(100), y(00), c);
c[2] = trends.ti; xy(x(100), y(10), c);
c[0] = '%';
c[2] = trends.hu; xy(x(100), y(20), c);
display.display();
}