Модераторы: healix, Модераторы форума
/*=========================================================================
SSD1306 Displays
-----------------------------------------------------------------------
The driver is used in multiple displays (128x64, 128x32, etc.).
Select the appropriate display below to create an appropriately
sized framebuffer, etc.
SSD1306_128_64 128x64 pixel display
SSD1306_128_32 128x32 pixel display
SSD1306_96_16
-----------------------------------------------------------------------*/
#define SSD1306_128_64
// #define SSD1306_128_32
// #define SSD1306_96_16
#include <OneWire.h>
// Контроллер солнечного коллектора.
// OneWire DS18S20, DS18B20, DS1822
//
// http://www.pjrc.com/teensy/td_libs_OneWire.html
int boiler_line = 2;
int collector_line = 3;
int t_dif_max = 10; // разница температур для включения насоса
int t_dif_min = 2; // разница температур для выключения насоса
int pump_pin = 5; // вывод управления реле насоса
int gv_boiler_max_temp = 85; // максимальная температура в бойлере. При ее достижении насос включаться не будет.
//*****************************************************************************************************
//#define DEBUG 1
boolean pump_state = false; // состояние насоса вкл/выкл
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define NUM_EL_IN_HIST 80
byte gl_col_temp[NUM_EL_IN_HIST];
byte gl_boiler_temp[NUM_EL_IN_HIST];
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
unsigned long gv_time;
//*****************************************************************************************************
void setup(void) {
#ifdef DEBUG
Serial.begin(9600);
#endif
pinMode(pump_pin, OUTPUT);
digitalWrite(pump_pin, HIGH);//релюшка включается низким уровнем
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
// Clear the buffer.
display.clearDisplay();
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { // заполняем историю для красивости начального графика
gl_col_temp[x] = 32;
gl_boiler_temp[x] = 20;
}
}
void loop(void) {
float t_col_max, t_boiler_max, difference;
t_col_max = readTempSensor( collector_line );
t_boiler_max = readTempSensor( boiler_line );
display_temp( t_col_max, t_boiler_max );
difference = t_col_max - t_boiler_max ;
#ifdef DEBUG
Serial.print(" t_boiler_max = ");
Serial.print(t_boiler_max);
Serial.print(" t_col_max = ");
Serial.print(t_col_max);
Serial.print(" Diff = ");
Serial.print(difference);
Serial.println(" ");
#endif
if ( t_boiler_max == -100 || t_col_max == -100 ) { // не прочитаны датчики на одной из линий
return;
}
if (difference > t_dif_max && pump_state == false && int(t_boiler_max) <= gv_boiler_max_temp ) {
digitalWrite(pump_pin, LOW);
#ifdef DEBUG
Serial.println(" Pump On ");
#endif
pump_state = true;
}
else if (difference < t_dif_min && pump_state == true) {
digitalWrite(pump_pin, HIGH);
#ifdef DEBUG
Serial.println(" Pump OFF ");
#endif
pump_state = false;
}
delay(5000);
}
float readTempSensor(int line) {
OneWire ds(line); // on pin line (a 4.7K resistor is necessary) Провод к датчикам
float lv_temperature = -100;
byte i, j;
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
float celsius;
for ( j = 0; j < 3; j++) { // делаем чтение датчиков на линии накопителя и находим максимальную температуру
if ( !ds.search(addr)) {
ds.reset_search();
delay(1000);
break;
}
if (OneWire::crc8(addr, 7) != addr[7]) {
#ifdef DEBUG
Serial.println("CRC is not valid!");
#endif
return;
}
ds.reset();
ds.select(addr);
ds.write(0x44); // start conversion, with parasite power on at the end
delay(1000); // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
// Convert the data to actual temperature
// because the result is a 16 bit signed integer, it should
// be stored to an "int16_t" type, which is always 16 bits
// even when compiled on a 32 bit processor.
int16_t raw = (data[1] <<| data[0];
if (type_s) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
// "count remain" gives full 12 bit resolution
raw = (raw & 0xFFF0) + 12 - data[6];
}
} else {
byte cfg = (data[4] & 0x60);
// at lower res, the low bits are undefined, so let's zero them
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
//// default is 12 bit resolution, 750 ms conversion time
}
celsius = (float)raw / 16.0;
#ifdef DEBUG
Serial.print(" T");
Serial.print(line);
Serial.print(j);
Serial.print(" = ");
Serial.println(celsius);
#endif
if (lv_temperature < celsius) {
lv_temperature = celsius;
}
}
return lv_temperature;
}
void display_temp(float iv_col_max, float iv_boiler_max) {
int lv_col_max_int;
int lv_boiler_max_int;
int lv_max_hist;
int lv_min_hist;
// int lv_y;
// int lv_x;
unsigned long lv_time;
unsigned long lv_time_dif;
//******************************************************
lv_col_max_int = int(iv_col_max );
lv_boiler_max_int = int(iv_boiler_max );
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println(lv_col_max_int );
display.setCursor(0, display.height() - 16);
display.println(lv_boiler_max_int );
if (pump_state == true) {
display.fillCircle(115, display.height() / 2, 3, WHITE);
}
else
{
display.drawCircle(115, display.height() / 2, 2, WHITE);
}
lv_time = millis();
if ( lv_time > gv_time ) {
lv_time_dif = lv_time - gv_time;
}
else { // таймер времени раз в 50 дней обнуляется
gv_time = lv_time;
}
if (lv_time_dif > 360000) { // историю собираем раз в 6 минут. 6*80 = 480 мин = 8 часов истории
gv_time = lv_time;
shift_hist(gl_col_temp, lv_col_max_int);
shift_hist(gl_boiler_temp, lv_boiler_max_int);
}
lv_min_hist = 100;
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { //Определяем максимальное и минимальное значение для масштабирования графика
if ( byte_to_int(gl_col_temp[x]) > lv_max_hist ) {
lv_max_hist = byte_to_int(gl_col_temp[x]);
}
if ( byte_to_int(gl_col_temp[x]) < lv_min_hist ) {
lv_min_hist = byte_to_int(gl_col_temp[x]);
}
}
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { //Определяем максимальное и минимальное значение для масштабирования графика
if ( byte_to_int(gl_boiler_temp[x]) > lv_max_hist ) {
lv_max_hist = byte_to_int(gl_boiler_temp[x]);
}
if ( byte_to_int(gl_boiler_temp[x]) < lv_min_hist ) {
lv_min_hist = byte_to_int(gl_boiler_temp[x]);
}
}
drow_graph(gl_col_temp, lv_min_hist, lv_max_hist);
drow_graph(gl_boiler_temp, lv_min_hist, lv_max_hist);
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(112, 0);
display.println(lv_max_hist );
if (lv_min_hist < 0 ) {
display.setCursor(104, display.height() - 8 );
}
else
{
display.setCursor(112, display.height() - 8 );
}
display.println(lv_min_hist );
display.display();
}
int byte_to_int(byte iv_byte) {
//храним в byte отрицательные значения температур тоже
int lv_int;
lv_int = int(iv_byte);
if (lv_int >= 128 ) {
lv_int = lv_int - 256;
}
return lv_int;
}
void shift_hist(byte ct_hist[NUM_EL_IN_HIST], int iv_new_temp) {
byte lv_temp_hist;
byte lv_temp_hist_prev;
lv_temp_hist_prev = byte(iv_new_temp);
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { // Сдвигаем историю. Более старый элемент имеет больше номер
lv_temp_hist = ct_hist[x];
ct_hist[x] = lv_temp_hist_prev;
lv_temp_hist_prev = lv_temp_hist;
}
}
void drow_graph(byte ct_hist[NUM_EL_IN_HIST],byte iv_min_hist, byte iv_max_hist ) {
int lv_y;
int lv_x;
int lv_y_prew = map(byte_to_int(ct_hist[0]), iv_min_hist, iv_max_hist, display.height() - 1, 0);;
int lv_x_prew = 28;
for (int x = 0; x < NUM_EL_IN_HIST ; x++) {
lv_y = map(byte_to_int(ct_hist[x]), iv_min_hist, iv_max_hist, display.height() - 1, 0);
lv_x = x + 28;
// display.drawPixel(lv_x, lv_y, WHITE);
display.drawLine( lv_x_prew,lv_y_prew,lv_x,lv_y, WHITE);
#ifdef DEBUG
Serial.print(lv_x );
Serial.print( lv_y );
Serial.print( lv_x_prew );
Serial.println( lv_y_prew );
#endif
lv_y_prew = lv_y;
lv_x_prew = lv_x;
}
}
int16_t raw = (data[1] << 8) | data[0];
if (type_s) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
// "count remain" gives full 12 bit resolution
raw = (raw & 0xFFF0) + 12 - data[6];
}
} else {
byte cfg = (data[4] & 0x60);
// at lower res, the low bits are undefined, so let's zero them
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
//// default is 12 bit resolution, 750 ms conversion time
}
celsius = (float)raw / 16.0;
#ifdef DEBUG
Serial.print(" T");
Serial.print(line);
Serial.print(j);
Serial.print(" = ");
Serial.println(celsius);
#endif
if (lv_temperature < celsius) {
lv_temperature = celsius;
}
healix писал(а):Лучше использовать тег Code для вставки кода... А то, вообще не читается.
#include <OneWire.h>
// Контроллер солнечного коллектора.
// OneWire DS18S20, DS18B20, DS1822
//
// http://www.pjrc.com/teensy/td_libs_OneWire.html
int boiler_line = 2;
int collector_line = 3;
int t_dif_max = 10; // разница температур для включения насоса
int t_dif_min = 2; // разница температур для выключения насоса
int pump_pin = 5; // вывод управления реле насоса
int gv_boiler_max_temp = 85; // максимальная температура в бойлере. При ее достижении насос включаться не будет.
//*****************************************************************************************************
//#define DEBUG 1
boolean pump_state = false; // состояние насоса вкл/выкл
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define NUM_EL_IN_HIST 80
byte gl_col_temp[NUM_EL_IN_HIST];
byte gl_boiler_temp[NUM_EL_IN_HIST];
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
unsigned long gv_time;
//*****************************************************************************************************
void setup(void) {
#ifdef DEBUG
Serial.begin(9600);
#endif
pinMode(pump_pin, OUTPUT);
digitalWrite(pump_pin, HIGH);//релюшка включается низким уровнем
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
// Clear the buffer.
display.clearDisplay();
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { // заполняем историю для красивости начального графика :-)
gl_col_temp[x] = 32;
gl_boiler_temp[x] = 20;
}
}
void loop(void) {
float t_col_max, t_boiler_max, difference;
t_col_max = readTempSensor( collector_line );
// временно, т.к. нету второго датчика
// t_col_max = 32;
// временно, т.к. нету второго датчика
t_boiler_max = readTempSensor( boiler_line );
display_temp( t_col_max, t_boiler_max );
difference = t_col_max - t_boiler_max ;
#ifdef DEBUG
Serial.print(" t_boiler_max = ");
Serial.print(t_boiler_max);
Serial.print(" t_col_max = ");
Serial.print(t_col_max);
Serial.print(" Diff = ");
Serial.print(difference);
Serial.println(" ");
#endif
if ( t_boiler_max == -100 || t_col_max == -100 ) { // не прочитаны датчики на одной из линий
return;
}
if (difference > t_dif_max && pump_state == false && int(t_boiler_max) <= gv_boiler_max_temp ) {
digitalWrite(pump_pin, LOW);
#ifdef DEBUG
Serial.println(" Pump On ");
#endif
pump_state = true;
}
else if (difference < t_dif_min && pump_state == true) {
digitalWrite(pump_pin, HIGH);
#ifdef DEBUG
Serial.println(" Pump OFF ");
#endif
pump_state = false;
}
delay(5000);
}
float readTempSensor(int line) {
OneWire ds(line); // on pin line (a 4.7K resistor is necessary) Провод к датчикам
float lv_temperature = -100;
byte i, j;
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
float celsius;
for ( j = 0; j < 3; j++) { // делаем чтение датчиков на линии накопителя и находим максимальную температуру
if ( !ds.search(addr)) {
ds.reset_search();
delay(1000);
break;
}
if (OneWire::crc8(addr, 7) != addr[7]) {
#ifdef DEBUG
Serial.println("CRC is not valid!");
#endif
return;
}
ds.reset();
ds.select(addr);
ds.write(0x44); // start conversion, with parasite power on at the end
delay(1000); // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.
present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for ( i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
// Convert the data to actual temperature
// because the result is a 16 bit signed integer, it should
// be stored to an "int16_t" type, which is always 16 bits
// even when compiled on a 32 bit processor.
int16_t raw = (data[1] << 8) | data[0];
if (type_s) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
// "count remain" gives full 12 bit resolution
raw = (raw & 0xFFF0) + 12 - data[6];
}
} else {
byte cfg = (data[4] & 0x60);
// at lower res, the low bits are undefined, so let's zero them
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
//// default is 12 bit resolution, 750 ms conversion time
}
celsius = (float)raw / 16.0;
#ifdef DEBUG
Serial.print(" T");
Serial.print(line);
Serial.print(j);
Serial.print(" = ");
Serial.println(celsius);
#endif
if (lv_temperature < celsius) {
lv_temperature = celsius;
}
}
return lv_temperature;
}
void display_temp(float iv_col_max, float iv_boiler_max) {
int lv_col_max_int;
int lv_boiler_max_int;
int lv_max_hist;
int lv_min_hist;
// int lv_y;
// int lv_x;
unsigned long lv_time;
unsigned long lv_time_dif;
//******************************************************
lv_col_max_int = int(iv_col_max );
lv_boiler_max_int = int(iv_boiler_max );
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println(lv_col_max_int );
display.setCursor(0, display.height() - 16);
display.println(lv_boiler_max_int );
if (pump_state == true) {
display.fillCircle(115, display.height() / 2, 3, WHITE);
}
else
{
display.drawCircle(115, display.height() / 2, 2, WHITE);
}
lv_time = millis();
if ( lv_time > gv_time ) {
lv_time_dif = lv_time - gv_time;
}
else { // таймер времени раз в 50 дней обнуляется
gv_time = lv_time;
}
if (lv_time_dif > 360000) { // историю собираем раз в 6 минут. 6*80 = 480 мин = 8 часов истории
gv_time = lv_time;
shift_hist(gl_col_temp, lv_col_max_int);
shift_hist(gl_boiler_temp, lv_boiler_max_int);
}
lv_min_hist = 100;
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { //Определяем максимальное и минимальное значение для масштабирования графика
if ( byte_to_int(gl_col_temp[x]) > lv_max_hist ) {
lv_max_hist = byte_to_int(gl_col_temp[x]);
}
if ( byte_to_int(gl_col_temp[x]) < lv_min_hist ) {
lv_min_hist = byte_to_int(gl_col_temp[x]);
}
}
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { //Определяем максимальное и минимальное значение для масштабирования графика
if ( byte_to_int(gl_boiler_temp[x]) > lv_max_hist ) {
lv_max_hist = byte_to_int(gl_boiler_temp[x]);
}
if ( byte_to_int(gl_boiler_temp[x]) < lv_min_hist ) {
lv_min_hist = byte_to_int(gl_boiler_temp[x]);
}
}
drow_graph(gl_col_temp, lv_min_hist, lv_max_hist);
drow_graph(gl_boiler_temp, lv_min_hist, lv_max_hist);
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(112, 0);
display.println(lv_max_hist );
if (lv_min_hist < 0 ) {
display.setCursor(104, display.height() - 8 );
}
else
{
display.setCursor(112, display.height() - 8 );
}
display.println(lv_min_hist );
display.display();
}
int byte_to_int(byte iv_byte) {
//храним в byte отрицательные значения температур тоже
int lv_int;
lv_int = int(iv_byte);
if (lv_int >= 128 ) {
lv_int = lv_int - 256;
}
return lv_int;
}
void shift_hist(byte ct_hist[NUM_EL_IN_HIST], int iv_new_temp) {
byte lv_temp_hist;
byte lv_temp_hist_prev;
lv_temp_hist_prev = byte(iv_new_temp);
for (int x = 0; x < NUM_EL_IN_HIST ; x++) { // Сдвигаем историю. Более старый элемент имеет больше номер
lv_temp_hist = ct_hist[x];
ct_hist[x] = lv_temp_hist_prev;
lv_temp_hist_prev = lv_temp_hist;
}
}
void drow_graph(byte ct_hist[NUM_EL_IN_HIST], byte iv_min_hist, byte iv_max_hist ) {
int lv_y;
int lv_x;
int lv_y_prew = map(byte_to_int(ct_hist[0]), iv_min_hist, iv_max_hist, display.height() - 1, 0);;
int lv_x_prew = 28;
for (int x = 0; x < NUM_EL_IN_HIST ; x++) {
lv_y = map(byte_to_int(ct_hist[x]), iv_min_hist, iv_max_hist, display.height() - 1, 0);
lv_x = x + 28;
// display.drawPixel(lv_x, lv_y, WHITE);
display.drawLine( lv_x_prew, lv_y_prew, lv_x, lv_y, WHITE);
#ifdef DEBUG
Serial.print(lv_x );
Serial.print( lv_y );
Serial.print( lv_x_prew );
Serial.println( lv_y_prew );
#endif
lv_y_prew = lv_y;
lv_x_prew = lv_x;
}
}
healix писал(а):Может, ещё поставить кнопку? Чтоб экран не всегда горел.
Или PIR, например
Torpedo писал(а):BIS, может быть есть смысл, после того, когда разберёшься с автоматикой для солнечного коллектора, задуматься о счётчике тепла?
healix писал(а):Кстати, а чё ты Nano берёшь?.. Они ж типа для девелопинга, а не для продакшина. USB на борту заметно повышает цену.
Есть Pro Mini, которым красная цена $1.20 с 328-й Мегой на борту. К ним, только, USB-Serial переходник нужен, для программирования.
mutexx писал(а):блютузы старые или 4 (BLE)? Как оказалось это совершенно разные устройства
BIS писал(а):Блютузина у меня HC-06 старенький.
Подключил сегодня на макетке. Разремил в скетче строчку #define DEBUG 1 (она активирует у меня весь вывод на serial)
Работает
Выводит на монитор в телефоне температуру.
Осталось малость. Научится писать проги под андроид и написать какую-нить софтину.
Зарегистрированные пользователи: нет зарегистрированных пользователей