Re: Модуль ESP8266 wi-fi управление ректификационной колонной
Требует развития проект.
Да не то слово...
|
Форум самогонщиков, винокуров, виноделов, пивоваров, бондарей и очень хороших людей |
Alco Distillers → Электронное оборудование → Модуль ESP8266 wi-fi управление ректификационной колонной
с 161 по 180 из 201
Требует развития проект.
Да не то слово...
Papazol, получение данных надо делать через скрипты Аjax. Иначе через 10 мин постоянного обновления страницы (если речь о веб браузере-постоянного запроса html у есп) микруха наглухо виснет. Аякс позволяет получать данные с частой 2 сек без Висяков.Нужен просто грамотный программист.
Нужен просто грамотный программист.
А кто у нас такой? Подозреваю, что нет его. Так что придётся мелкими шажками самим продвигаться.
Вот сегодня я добился появления текста HELLO WORLD на дисплее. Уже победа 
Чтобы сохранить информацию, напишу здесь, что делалось и как.
1. Подключение дисплея. Я покупал дисплей 16х2 без преобразователя I2C. Преобразователь покупал отдельно, он на микросхеме PCF8574AT. Припаял преобразователь к дисплею через гребёнку Dupont. Для работы дисплея по I2C совершенно необходимо знать адрес устройства на шине. Значение адреса зависит от собственно микросхемы преобразователя и от установленных на плате перемычек.
Плата преобразователя имеет четыре контакта, через которые она подключается к микропроцессору: GND, VCC, SDA и SCL. Первые два вывода - питание +5 В, вторые - шина I2C. Дисплей подключается следующим образом:
провод GND - к контакту G модуля ESP8266,
провод VCC - к контакту VU модуля ESP8266,
провод SDA - к контакту D2 модуля ESP8266,
провод SCL - к контакту D1 модуля ESP8266.
В предыдущем посте я упоминал о проблеме запитки дисплея от контакта VIN. И даже собрался было закоротить диод. Но потом порылся в сети и нашёл совет какого-то доброго человека про контакт VU. измерил на нём напряжение - оказалось +5 В. Значит, схемы разных вариантов модуля отличаются.
2. Скетч для получения фразы HELLO WORLD. Их довольно много встречается в сети. Я взял такой:
#include <LiquidCrystal_I2C.h>
// Construct an LCD object and pass it the
// I2C address, width (in characters) and
// height (in characters). Depending on the
// Actual device, the IC2 address may change.
LiquidCrystal_I2C lcd(0x38, 16, 2);
void setup() {
// The begin call takes the width and height. This
// Should match the number provided to the constructor.
lcd.begin(16,2);
lcd.init();
// Turn on the backlight.
lcd.backlight();
// Move the cursor characters to the right and
// zero characters down (line 1).
lcd.setCursor(5, 0);
// Print HELLO to the screen, starting at 5,0.
lcd.print("HELLO");
// Move the cursor to the next line and print
// WORLD.
lcd.setCursor(5, 1);
lcd.print("WORLD");
}
void loop() {
}В строке LiquidCrystal_I2C lcd(0x38, 16, 2) 0x38 - это адрес дисплея на шине I2C. Его нужно поменять в соответствии с таблицей на картинке выше.
3. Библиотеки. С этим всё непросто. Есть стандартные Arduinoвские библиотеки, есть нестандартные. Перепробовать все их мне не удалось, к счастью. Для дисплея требуется библиотека LiquidCrystal_I2C, которую я взял здесь и положил в папку libraries Arduino IDE.
К сожалению, в Arduino IDE есть очень много мест, где могут находиться библиотеки. Разобраться, откуда программа берёт каждую из подключенных в каком-либо скетче библиотек, весьма сложно. Для этого нужно включить в настройках опцию Показать подробный вывод (при компиляции). Если окажется, что при компиляции программа использует не ту библиотеку, придётся принимать меры. Либо сносить ненужную библиотеку, либо наверняка есть другой способ, но до него я ещё не дошёл.
Изображения куда-то деваются постоянно, это плохо.
Поведаю дальнейшее развитие моё и программно-аппаратной части сабжа. За прошедшие сутки удалось сделать:
1. Подключить к ESP8266 дисплей и вывести на него показания всех датчиков температуры. Показания каждого датчика выводятся на отдельном экране, переключение экранов осуществляется кнопкой по кругу. Для упрощения схемы я задействовал кнопку FLASH, имеющуюся на модуле.
2. Немного причесать текст программы и добавить комментарии, иначе через одну-две недели всё вылетит из головы.
Поскольку максимум внимания я уделил дисплею, программа отработана именно в этой части. В серверную часть я только добавил нормальные знаки градусов Цельсия.
Я не подключал к модулю датчики температуры, это мне было бы крайне неудобно. Надеюсь, в части OneWire программа уже была отработана.
При тестировании программы выявились некоторые фичи и баги.
1. При автоматическом старте программы после её заливки на дисплее ничего не отображается. Только после ручного ресета начинает отображаться. Возможно, это связано с особенностями поведения самого дисплея.
2. Неправильно отрабатывает ввод дельты с браузера. Здесь надо внимательно рассмотреть код, возможно, я где-то что-то отредактировал 
3. Программа всё-таки виснет при мало-мальски активных действиях с сервером. Что с этим делать, пока не знаю.
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2 // GPIO 2 будет шиной OneWire
#define valve (13)// GPIO 13 будет управлять клапаном отбора (если LOW - клапан включен, если HIGH - выключен)
#define button (0) // кнопка подключена к GPIO0 (отпущена - HIGH, нажата - LOW)
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
#include <ESP8266WiFi.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x38, 16, 2); // адрес дисплея на шине I2C, количество знаков, количество строк
float setTmp; // переменная для хранения уставки по температуре вверху колонны, при достижении которой клапан отключается
const char* ssid = "XXXXXX"; // Имя вашей сети
const char* password = "XXXXXX"; //Пароль для входа
unsigned long pause;
boolean flag;
WiFiServer server(80);
static unsigned long time; // объявление переменной time типа static unsigned long
int value = LOW;
int value1 = LOW;
int value2 = LOW;
int value3 = LOW;
int value4= LOW;
int screen=1; // переменная "экран", от которой зависит, какие показания будут выводиться на дисплей
int pressed=0; // переменная "нажатие"
// Формируем массив байтов для символа градуса
byte gradus[8] = {
B01100,
B10010,
B10010,
B01100,
B00000,
B00000,
B00000,
B00000
};
// Инициализация
void setup()
{
lcd.begin(16,2); // задаём размерность дисплея
lcd.init(); // инициализируем дисплей
lcd.backlight(); // включаем подсветку
lcd.createChar(0, gradus); // создаём символ градуса
pinMode(valve,OUTPUT); // назначаем выводу клапана функцию выхода
Serial.begin(115200);
delay(10);
sensors.begin();
// Подключаемся к сети WiFi
Serial.println();
Serial.println(); // два перевода строки
Serial.print("Connecting to "); // пишем Connecting to
Serial.println(ssid); // пишем название сети
WiFi.begin(ssid, password); // передаём запрос на подключение к сети
while (WiFi.status() != WL_CONNECTED) // пока соединения нет
{
delay(500); // каждые 500 мс
Serial.print("."); // пишем точку
}
Serial.println(""); // как только соединение установлено, пишем пустую строку
Serial.println("WiFi connected"); // пишем WiFi connected
server.begin(); // стартуем сервер
Serial.println("Server started"); // пишем Server started
Serial.print("Use this URL to connect: "); // пишем IP адрес
Serial.print("http://"); // пишем http://
Serial.print(WiFi.localIP()); // пишем значение IP адреса
Serial.println("/"); // в конце добавляем /
}
// Основная программа
void loop()
{
// Обрабатываем нажатия кнопки и вывод данных на дисплей
if(digitalRead(button)==LOW&&pressed==0) //если кнопка нажата и переменная pressed равна 0 , то ...
{
screen++; // увеличиваем номер экрана на 1
pressed=1; //это нужно для того что бы с каждым нажатием кнопки происходило только одно действие плюс защита от "дребезга" 100%
lcd.clear(); // при нажатии кнопки очищаем дисплей
if(screen>4) { screen=1; } // так как мы используем только одну кнопку, то переключать экраны будем циклично
}
if(digitalRead(button)==HIGH&&pressed==1) { pressed=0; } //если кнопка НЕ нажата и переменная flag равна 1 ,то обнуляем переменную "нажатие"
if(screen==1) //первый экран: температура вверху колонны
{
lcd.setCursor(2, 0); // устанавливаем курсор на вторую позицию в верхней строке
lcd.print("Upper temp."); // пишем Upper temp.
lcd.setCursor(3, 1); // устанавливаем курсор на третью позицию в нижней строке
lcd.print(sensors.getTempCByIndex(0)); // пишем температуру вверху колонны
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
if(screen==2) //второй экран: температура на 2/3 колонны
{
lcd.setCursor(3, 0); // устанавливаем курсор на третью позицию в верхней строке
lcd.print("2/3 temp."); // пишем 2/3 temp.
lcd.setCursor(3, 1); // устанавливаем курсор на третью позицию в нижней строке
lcd.print(sensors.getTempCByIndex(1)); // пишем температуру на 2/3 колонны
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
if(screen==3) //третий экран: температура флегмы/охлаждающей воды
{
lcd.setCursor(2, 0); // устанавливаем курсор на вторую позицию в верхней строке
lcd.print("Water temp."); // пишем Water temp.
lcd.setCursor(3, 1); // устанавливаем курсор на третью позицию в нижней строке
lcd.print(sensors.getTempCByIndex(2)); // пишем температуру флегмы/охлаждающей воды
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
if(screen==4) //четвертый экран: температура в кубе
{
lcd.setCursor(3, 0); // устанавливаем курсор на третью позицию в верхней строке
lcd.print("Tank temp."); // пишем Tank temp.
lcd.setCursor(3, 1); // устанавливаем курсор на третью позицию в нижней строке
lcd.print(sensors.getTempCByIndex(3)); // пишем температуру в кубе
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
// Управляем клапаном отбора
if (flag = true) // если мы находимся в ожидании
{
if(millis()- pause >= 30000) { digitalWrite(valve, LOW); } // если время задержки ещё не вышло (30000 ms = 30 с), включаем клапан
if (sensors.getTempCByIndex(0) < setTmp) { flag = false; } // если температура вверху колонны ниже заданной, сбрасываем флаг
if (sensors.getTempCByIndex(0) >= setTmp) // если достигнута заданная температура вверху колонны
{
flag = true; // ставим флаг
pause = millis(); // заносим текущее время в переменную pause
digitalWrite(valve, HIGH ); // выключаем клапан отбора
}
}
// Обрабатываем запросы клиента
WiFiClient client = server.available(); // проверяем, подключен ли клиент
if (!client) { return; } // если нет, возврат
Serial.println("new client"); // если клиент подключен, пишем new client
while(!client.available()) { delay(1); } // ждём, пока клиент передаст какие-нибудь данные
String request = client.readStringUntil('\r'); // читаем первую строку запроса
Serial.println(request); // пишем первую строку запроса
client.flush(); // передаём то, что запросил клиент
if (request.indexOf("/Select=0.1") != -1 && setTmp == 0) // если выбрана дельта 0,1 градуса и ранее уставка не изменялась
{
setTmp=sensors.getTempCByIndex(0)+0,1; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,1 градуса
Serial.print("setTmp="); // выводим новое значение уставки на монитор
Serial.println(setTmp);
value = HIGH; // устанавливаем флаг дельты 0,1 градуса
}
if (request.indexOf("/Select=OFF") != -1) // если дельта не была выбрана
{
setTmp ==0; // устанавливаем уставку 0
value = LOW; // сбрасываем флаг дельты 0,1 градуса
}
if (request.indexOf("/Select=0.2") != -1 && setTmp == 0) // если выбрана дельта 0,2 градуса
{
setTmp=sensors.getTempCByIndex(0)+0,2; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,2 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
value1 = HIGH; // устанавливаем флаг дельты 0,2 градуса
}
if (request.indexOf("/Select=OFF") != -1) // если дельта не была выбрана
{
setTmp ==0; // устанавливаем уставку 0
value1 = LOW; // сбрасываем флаг дельты 0,2 градуса
}
if (request.indexOf("/Select=0.3") != -1 && setTmp == 0) // если выбрана дельта 0,3 градуса
{
setTmp=sensors.getTempCByIndex(0)+0,3; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,3 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
value2 = HIGH; // устанавливаем флаг дельты 0,3 градуса
}
if (request.indexOf("/Select=OFF") != -1) // если дельта не была выбрана
{
setTmp ==0; // устанавливаем уставку 0
value2 = LOW; // сбрасываем флаг дельты 0,3 градуса
}
if (request.indexOf("/Select=0.4") != -1 && setTmp == 0) // если выбрана дельта 0,4 градуса
{
setTmp=sensors.getTempCByIndex(0)+0,4; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,4 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
value3 = HIGH; // устанавливаем флаг дельты 0,4 градуса
}
if (request.indexOf("/Select=OFF") != -1) // если дельта не была выбрана
{
setTmp ==0; // устанавливаем уставку 0
value3 = LOW; // сбрасываем флаг дельты 0,4 градуса
}
if (request.indexOf("/Select=0.5") != -1 && setTmp == 0) // если выбрана дельта 0,5 градуса
{
setTmp=sensors.getTempCByIndex(0)+0,5; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,4 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
value4 = HIGH; // устанавливаем флаг дельты 0,5 градуса
}
if (request.indexOf("/Select=OFF") != -1) // если дельта не была выбрана
{
setTmp ==0; // устанавливаем уставку 0
value4 = LOW; // сбрасываем флаг дельты 0,5 градуса
}
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println(""); // do not forget this one
client.println("<!DOCTYPE HTML>");
//Индикация времени работы программы
if (time/60/60<10) { Serial.print ("0"); }
if (time/60/60<10) { client.print ("0"); }
Serial.print (time/60/60);
client.print (time/60/60);
Serial.print (":");
client.print (":");
if (time/60%60<10) { Serial.print ("0"); }
if (time/60%60<10) { client.print ("0"); }
Serial.print ((time/60)%60);
client.print ((time/60)%60);
Serial.print (":");
client.print (":");
if (time%60<10) { Serial.print ("0"); }
if (time%60<10) { client.print ("0"); }
Serial.println (time%60);
client.println (time%60);
client.println("Operation TIME");
time = millis()/1000;
client.println("<br><br>");
// Индикация температуры с датчиков
client.println("<h4>SENSORS READING</h4>");
client.println("<br><br>");
client.println("UPPER SENSOR ");
client.print(sensors.getTempCByIndex(0));
client.println(" °C");
client.println("<br><br>");
client.println("2/3 SENSOR ");
client.print(sensors.getTempCByIndex(1));
client.println(" °C");
client.println("<br><br>");
client.println("WATER/PHLEGM SENSOR ");
client.print(sensors.getTempCByIndex(2));
client.println(" °C");
client.println("<br><br>");
client.println("TANK SENSOR ");
client.print(sensors.getTempCByIndex(3));
client.println(" °C");
client.println("<br><br>");
client.println("CONTROL TEMPERATURE ");
client.print(setTmp);
client.println(" °C");
client.println("<br><br>");
if(value == HIGH) { client.print("<h2>Selected <a><button>+0,1 *C</button></a> <br></h2>"); }
else { client.print(""); }
if(value1 == HIGH) { client.print("<h2>Selected <a><button>+0,2 *C</button></a> <br></h2>"); }
else { client.print(""); }
if(value2 == HIGH) { client.print("<h2>Selected <a><button>+0,3 *C</button></a> <br></h2>"); }
else { client.print(""); }
if(value3 == HIGH) { client.print("<h2>Selected <a><button>+0,4 *C</button></a> <br></h2>"); }
else { client.print(""); }
if(value4 == HIGH) { client.print("<h2>Selected <a><button>+0,5 *C</button></a> <br></h2>"); }
else
{
client.print("Select");
client.println("<br><br>");
}
client.print("<a href=\"/Select=0.1\"><button> + 0.1</button></a> <br>");
client.print("<a href=\"/Select=0.2\"><button> + 0.2</button></a> <br>");
client.print("<a href=\"/Select=0.3\"><button> + 0.3</button></a> <br>");
client.print("<a href=\"/Select=0.4\"><button> + 0.4</button></a> <br>");
client.print("<a href=\"/Select=0.5\"><button> + 0.5</button></a> <br>");
}Появился вопрос по датчикам температуры DS18B20. В данном проекте их используется четыре. Подключаются они к одной шине OneWire. Как узнать, какой из датчиков куда должен быть вставлен?
Как узнать, какой из датчиков куда должен быть вставлен?
Какой куда хочешь - такой туда и вставляй. Без разницы. У каждого свой адрес и ID.
Papazol, датчики работают по одной шине. Они подключаются паралельно
В данном проекте их используется четыре. Подключаются они к одной шине OneWire. Как узнать, какой из датчиков куда должен быть вставлен?
Спрошу иначе: имеем четыре совершенно одинаковых на вид датчика. Имеем четыре разъёма, куда эти датчики должны втыкаться.
Обычно в аппаратуре разъёмы должны быть помечены надписями, типа какую именно температуру должен измерять датчик, воткнутый в этот разъём. Но в данном случае все четыре разъёма соединяются параллельно, так что нет никакой разницы, в какой из них вставлять какой датчик.
Но всё равно ведь нужно как-то определять, какой датчик вверху колонны, какой на 2/3, какой в кубе и т. д. Иначе мы будем измерять не те температуры.
Каждый датчик имеет внутренний уникальный номер, который считывается при инициализации шины OneWire. Программа тупо присваивает датчику с минимальным уникальным номером индекс [0], что в нашей программе соответствует температуре вверху колонны, следующему - индекс [1] (это температура на 2/3 колонны) и так далее.
То есть, подключая датчики к разъёмам в произвольном порядке, мы имеем лотерею. Нужно каждый датчик пометить биркой, и вставлять его в сугубо определённое место измерения.
Для этого нужно сначала определить уникальный номер каждого датчика. Затем расположить датчики в порядке возрастания уникальных номеров и навесить на каждый бирку.
Как определить уникальный номер датчика DS18B20?
Papazol, не усложняйте. Подсоединить все четыре, затустить любую прогу мониторинга, тот же темпкипер (крякнутый), посмотреть как и что распределилось, обозвать каждый именем каким удобно. И усё.
Я вот, верхний на нижний менял прямо в процессе ректификации вчера. Так надо было. И тут же в программе переименовал. Не вижу проблем.
Николай, давайте не будем путать TempKeeper с ESP8266. Сейчас речь идёт о последнем, а в нём тоже нужно как-то определяться с датчиками. Потому что в TempKeeperе легко переименовать датчики, поменяв их местами, а в ESP8266 всё определяется библиотеками и программой. Пока думаю, что будет непросто.
Сегодня весь день посвятил датчикам DS18B20. Не всё оказалось так просто. Подключив пару штук к модулю, я получил с них температуру 85 градусов - и всё! Пришлось несколько перелопатить программу, использовав пример из библиотеки DallasTemperature. В этом примере используются совсем не такие функции, какие были в исходном тексте программы. Я в этих функциях спец никакой, могу только воспользоваться примерами. И воспользовался. Пример под названием Multi заработал, стало показывать реальную температуру. Вбил части этого примера в программу, помучился несколько часов, но всё-таки получил работающую программу с четырьмя датчиками. Там есть нюансы, но работает.
Но самое главное препятствие на пути к рабочему устройству - просто непозволительная медлительность сервера. Передавать сотню байт информации за десять секунд - нонсенс. Всё, ради чего затевался этот проект (беспроводность), оказывается недостижимым.
Papazol, так прошивка какая? nodemcu ? ... там достаточно скрипта инициализации, она сама подхватит
- DS18B20 one wire module for NODEMCU
-- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT
-- Vowstar <vowstar@nodemcu.com>
-- 2015/02/14 sza2 <sza2trash@gmail.com> Fix for negative values
--------------------------------------------------------------------------------
-- Set module name as parameter of require
local modname = ...
local M = {}
_G[modname] = M
--------------------------------------------------------------------------------
-- Local used variables
--------------------------------------------------------------------------------
-- DS18B20 dq pin
local pin = nil
-- DS18B20 default pin
local defaultPin = 9
--------------------------------------------------------------------------------
-- Local used modules
--------------------------------------------------------------------------------
-- Table module
local table = table
-- String module
local string = string
-- One wire module
local ow = ow
-- Timer module
local tmr = tmr
-- Limited to local environment
setfenv(1,M)
--------------------------------------------------------------------------------
-- Implementation
--------------------------------------------------------------------------------
C = 'C'
F = 'F'
K = 'K'
function setup(dq)
pin = dq
if(pin == nil) then
pin = defaultPin
end
ow.setup(pin)
end
function addrs()
setup(pin)
tbl = {}
ow.reset_search(pin)
repeat
addr = ow.search(pin)
if(addr ~= nil) then
table.insert(tbl, addr)
end
tmr.wdclr()
until (addr == nil)
ow.reset_search(pin)
return tbl
end
function readNumber(addr, unit)
result = nil
setup(pin)
flag = false
if(addr == nil) then
ow.reset_search(pin)
count = 0
repeat
count = count + 1
addr = ow.search(pin)
tmr.wdclr()
until((addr ~= nil) or (count > 100))
ow.reset_search(pin)
end
if(addr == nil) then
return result
end
crc = ow.crc8(string.sub(addr,1,7))
if (crc == addr:byte(8)) then
if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then
-- print("Device is a DS18S20 family device.")
ow.reset(pin)
ow.select(pin, addr)
ow.write(pin, 0x44, 1)
-- tmr.delay(1000000)
present = ow.reset(pin)
ow.select(pin, addr)
ow.write(pin,0xBE,1)
-- print("P="..present)
data = nil
data = string.char(ow.read(pin))
for i = 1, 8 do
data = data .. string.char(ow.read(pin))
end
-- print(data:byte(1,9))
crc = ow.crc8(string.sub(data,1,8))
-- print("CRC="..crc)
if (crc == data:byte(9)) then
t = (data:byte(1) + data:byte(2) * 256)
if (t > 32767) then
t = t - 65536
end
if (addr:byte(1) == 0x28) then
t = t * 625 -- DS18B20, 4 fractional bits
else
t = t * 5000 -- DS18S20, 1 fractional bit
end
if(unit == nil or unit == 'C') then
-- do nothing
elseif(unit == 'F') then
t = t * 1.8 + 320000
elseif(unit == 'K') then
t = t + 2731500
else
return nil
end
t = t / 10000
return t
end
tmr.wdclr()
else
-- print("Device family is not recognized.")
end
else
-- print("CRC is not valid!")
end
return result
end
function read(addr, unit)
t = readNumber(addr, unit)
if (t == nil) then
return nil
else
return t
end
end
-- Return module table
return Mhttps://github.com/nodemcu/nodemcu-firm … es/ds18b20
Papazol, а я делал проще, подключал 4 датчика и по очередно опускал их в кипяток, затем маркёром обозначал какой это датчик (пар, 2/3, вода и тд)
Прошивка NodeMCU у меня. Но среда разработки под ней другая. Изучить сразу всё я не в силах. Копаясь в условно рабочих программах, можно чему-то научиться, что и пытаюсь сделать. И вот что ещё читал о прошивке NodeMCU: там проблема с подключением более одного датчика температуры. Стандартные библиотеки недорезанные. Однако, один товарищ исправил, но это все на языке lua. Может, когда -нибудь и до него очередь дойдёт.
Похоже, что единственный реальный способ правильно расставить датчики температуры - это выяснить адрес каждого из них и повесить на них бирки. Адреса можно посмотреть в мониторе порта при инициализации, это не проблема. Проблема может настать, если понадобится замена одного и более датчиков. Придётся всё проделать заново.
А пока ковыряю Arduino IDE. Сейчас сделано следующее:
1. Внедрён код инициализации датчиков DS18B20, позволяющий сразу получать реальную температуру. Кстати, датчики имеет смысл инициализировать на максимальное разрешение, иначе дельта +0,1 и даже +0,2 градуса бессмысленны. Но при разрешении 12 время опроса датчиков весьма сильно увеличивается. С одной стороны, это хорошо, ибо нам нет нужды опрашивать датчики слишком часто. А с другой стороны, общее время выполнения цикла основной программы тоже ведь увеличивается.
2. Опрос датчиков температуры сделан один раз за цикл. Затем полученные значения используются для выдачи в UART, на сервер и на дисплей.
3. Добавлены некоторые свистоперделки: заставка при подаче питания, переключающиеся кнопкой экраны на дисплее, на каждом из которых индицируется определённая температура, выдача некоторых служебных сообщений на UART.
Программа setup() работает так: после подачи питания или ресете сначала инициализируется UART, на него выдаётся заставка. Затем инициализируется дисплей и на него тоже выдаётся заставка. Затем инициализируются датчики температуры, и информация о них выдаётся на UART. Затем инициализируется WiFi, и модуль подключается к сети. Затем стартует сервер.
Основная программа loop() работает так: сначала обрабатываются нажатия кнопки переключения экранов дисплея. Затем с датчиков температуры считываются данные. Значения температур выдаются в UART. Затем, в зависимости от выбранного экрана значения температур выдаются на дисплей. Затем обрабатывается управление клапаном (эта часть программы оставлена без изменения). Затем обрабатываются запросы клиента.
Теперь об изменениях в серверной части программы.
1. Программа была написана так, что ввести дельту по температуре можно только один раз. То есть, если ошибочно ввести не то значение, то исправить его невозможно. Теперь же можно вводить и отменять дельту неограниченное количество раз. Пришлось ввести ещё одну кнопку на экране.
2. Изменил часть кода, отвечающую за вывод времени работы модуля. Просто чтобы текущее время считывалось перед его выводом на UART и на сервер, а то при первом обращении всегда показывало 00:00:00 вне зависимости от того, сколько времени прошло на самом деле.
Основное время, затрачиваемое на обработку запросов, приходится на две операции: передачу собственно HTML кода и ожидание передачи favicon.ico. Поскольку никакой фавиконки у нас нет, приходится тупо ждать таймаута, а в это время цикл программы стоит.
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2 // GPIO 2 будет шиной OneWire
#define valve (13)// GPIO 13 будет управлять клапаном отбора (если LOW - клапан включен, если HIGH - выключен)
#define button (0) // кнопка подключена к GPIO0 (отпущена - HIGH, нажата - LOW)
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
#include <ESP8266WiFi.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x38, 16, 2); // адрес дисплея на шине I2C, количество знаков, количество строк
float SteamTemp; // температура пара в верху колонны
float PipeTemp; // температура в царге на 2/3 высоты
float WaterTemp; // температура охлаждающей воды или флегмы
float TankTemp; // температура в кубе
float setTmp; // уставка по температуре пара вверху колонны, при достижении которой клапан отключается
const char* ssid = "XXXXXX"; // Имя вашей сети
const char* password = "XXXXXX"; //Пароль для входа
unsigned long pause;
boolean flag;
WiFiServer server(80);
static unsigned long time; // объявление переменной time типа static unsigned long
int delta = 0; // значение дельты по температуре
int screen=1; // переменная "экран", от которой зависит, какие показания будут выводиться на дисплей
int pressed=0; // переменная "нажатие"
// массив байтов для символа градуса
byte gradus[8] = {
B01100,
B10010,
B10010,
B01100,
B00000,
B00000,
B00000,
B00000
};
// массив адресов датчиков
DeviceAddress SteamSensor, PipeSensor, WaterSensor, TankSensor;
// Инициализация
void setup(void)
{
pinMode(valve,OUTPUT); // назначаем выводу клапана функцию выхода
Serial.begin(115200); // стартуем последовательный порт
delay(10); // пауза небольшая
Serial.println("AutoSam version 10.2");
lcd.begin(16,2); // задаём размерность дисплея
lcd.init(); // инициализируем дисплей
lcd.backlight(); // включаем подсветку
lcd.createChar(0, gradus); // создаём символ градуса
lcd.setCursor(4, 0); // устанавливаем курсор на вторую позицию в верхней строке
lcd.print("AutoSam"); // пишем AutoSam.
lcd.setCursor(2, 1); // устанавливаем курсор на вторую позицию в верхней строке
lcd.print("Version 10.2"); // пишем Version 10.2
sensors.begin(); // стартуем датчики температуры
// определяем устройства на шине
Serial.print("Locating devices...");
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
// Инициализируем датчики температуры
if (!sensors.getAddress(SteamSensor, 0)) Serial.println("Unable to find address for Device 0"); // если адрес датчика 0 не найден
if (!sensors.getAddress(PipeSensor, 1)) Serial.println("Unable to find address for Device 1"); // если адрес датчика 1 не найден
if (!sensors.getAddress(WaterSensor, 1)) Serial.println("Unable to find address for Device 2"); // если адрес датчика 2 не найден
if (!sensors.getAddress(TankSensor, 1)) Serial.println("Unable to find address for Device 3"); // если адрес датчика 3 не найден
Serial.print("SteamSensor Address: "); // пишем адрес датчика 0
printAddress(SteamSensor);
Serial.println();
Serial.print("PipeSensor Address: "); // пишем адрес датчика 1
printAddress(PipeSensor);
Serial.println();
Serial.print("WaterSensor Address: "); // пишем адрес датчика 2
printAddress(WaterSensor);
Serial.println();
Serial.print("TankSensor Address: "); // пишем адрес датчика 3
printAddress(TankSensor);
Serial.println();
sensors.setResolution(SteamSensor, 12); // устанавливаем разрешение для датчика 0
sensors.setResolution(PipeSensor, 12); // устанавливаем разрешение для датчика 1
sensors.setResolution(WaterSensor, 12); // устанавливаем разрешение для датчика 2
sensors.setResolution(TankSensor, 12); // устанавливаем разрешение для датчика 3
Serial.print("Device 0 Resolution: "); // пишем разрешение для датчика 0
Serial.print(sensors.getResolution(SteamSensor), DEC);
Serial.println();
Serial.print("Device 1 Resolution: "); // пишем разрешение для датчика 1
Serial.print(sensors.getResolution(PipeSensor), DEC);
Serial.println();
Serial.print("Device 2 Resolution: "); // пишем разрешение для датчика 2
Serial.print(sensors.getResolution(WaterSensor), DEC);
Serial.println();
Serial.print("Device 3 Resolution: "); // пишем разрешение для датчика 3
Serial.print(sensors.getResolution(TankSensor), DEC);
Serial.println();
// Подключаемся к сети WiFi
Serial.println();
Serial.println(); // два перевода строки
Serial.print("Connecting to "); // пишем Connecting to
Serial.println(ssid); // пишем название сети
WiFi.begin(ssid, password); // передаём запрос на подключение к сети
while (WiFi.status() != WL_CONNECTED) // пока соединения нет
{
delay(500); // каждые 500 мс
Serial.print("."); // пишем точку
}
Serial.println(""); // как только соединение установлено, пишем пустую строку
Serial.println("WiFi connected"); // пишем WiFi connected
server.begin(); // стартуем сервер
Serial.println("Server started"); // пишем Server started
Serial.print("Use this URL to connect: "); // пишем IP адрес
Serial.print("http://"); // пишем http://
Serial.print(WiFi.localIP()); // пишем значение IP адреса
Serial.println("/"); // в конце добавляем /
lcd.clear(); // очищаем дисплей
}
//**********************************************************************************
// функция печати адреса устройства
void printAddress(DeviceAddress deviceAddress)
{
for (uint8_t i = 0; i < 8; i++)
{
// zero pad the address if necessary
if (deviceAddress[i] < 16) Serial.print("0");
Serial.print(deviceAddress[i], HEX);
}
}
//**********************************************************************************
// Основная программа
void loop()
{
// Обрабатываем нажатия кнопки и вывод данных на дисплей
if(digitalRead(button)==LOW&&pressed==0) //если кнопка нажата и переменная pressed равна 0 , то ...
{
screen++; // увеличиваем номер экрана на 1
pressed=1; //это нужно для того что бы с каждым нажатием кнопки происходило только одно действие плюс защита от "дребезга" 100%
lcd.clear(); // при нажатии кнопки очищаем дисплей
if(screen>4) { screen=1; } // так как мы используем только одну кнопку, то переключать экраны будем циклично
}
if(digitalRead(button)==HIGH&&pressed==1) { pressed=0; } //если кнопка НЕ нажата и переменная flag равна 1 ,то обнуляем переменную "нажатие"
// считываем температуры с датчиков
Serial.print("Requesting temperatures..."); // пишем Requesting temperatures...
sensors.requestTemperatures(); // запрашиваем температуру у всех датчиков
Serial.println("DONE"); // пишем DONE
SteamTemp = sensors.getTempC(SteamSensor); // считываем температуру с датчика 0
PipeTemp = sensors.getTempC(PipeSensor); // считываем температуру с датчика 1
WaterTemp = sensors.getTempC(WaterSensor); // считываем температуру с датчика 2
TankTemp = sensors.getTempC(TankSensor); // считываем температуру с датчика 3
Serial.print("SteamTemp="); // пишем
Serial.println(SteamTemp);
Serial.print("PipeTemp=");
Serial.println(PipeTemp);
Serial.print("WaterTemp=");
Serial.println(WaterTemp);
Serial.print("TankTemp=");
Serial.println(TankTemp);
// обрабатываем дисплей и кнопку
if(screen==1) //первый экран: температура вверху колонны
{
lcd.setCursor(2, 0); // устанавливаем курсор на вторую позицию в верхней строке
lcd.print("Steam temp."); // пишем Upper temp.
lcd.setCursor(4, 1); // устанавливаем курсор на четвёртую позицию в нижней строке
lcd.print(SteamTemp); // пишем температуру вверху колонны
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
if(screen==2) //второй экран: температура на 2/3 колонны
{
lcd.setCursor(3, 0); // устанавливаем курсор на третью позицию в верхней строке
lcd.print("Pipe temp."); // пишем 2/3 temp.
lcd.setCursor(4, 1); // устанавливаем курсор на четвёртую позицию в нижней строке
lcd.print(PipeTemp); // пишем температуру на 2/3 колонны
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
if(screen==3) //третий экран: температура флегмы/охлаждающей воды
{
lcd.setCursor(2, 0); // устанавливаем курсор на вторую позицию в верхней строке
lcd.print("Water temp."); // пишем Water temp.
lcd.setCursor(4, 1); // устанавливаем курсор на четвёртую позицию в нижней строке
lcd.print(WaterTemp); // пишем температуру флегмы/охлаждающей воды
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
if(screen==4) //четвертый экран: температура в кубе
{
lcd.setCursor(3, 0); // устанавливаем курсор на третью позицию в верхней строке
lcd.print("Tank temp."); // пишем Tank temp.
lcd.setCursor(4, 1); // устанавливаем курсор на четвёртую позицию в нижней строке
lcd.print(TankTemp); // пишем температуру в кубе
lcd.write(0); // пишем символ градуса
lcd.print("C"); // пишем символ Цельсия
}
// Управляем клапаном отбора
if (flag = true) // если мы находимся в ожидании
{
if(millis()- pause >= 30000) { digitalWrite(valve, LOW); } // если время задержки ещё не вышло (30000 ms = 30 с), включаем клапан
if (SteamTemp < setTmp) { flag = false; } // если температура вверху колонны ниже заданной, сбрасываем флаг
if (SteamTemp >= setTmp) // если достигнута заданная температура вверху колонны
{
flag = true; // ставим флаг
pause = millis(); // заносим текущее время в переменную pause
digitalWrite(valve, HIGH ); // выключаем клапан отбора
}
}
// Обрабатываем запросы клиента
WiFiClient client = server.available(); // проверяем, подключен ли клиент
if (!client) { return; } // если нет, возврат
Serial.println("new client"); // если клиент подключен, пишем new client
while(!client.available()) { delay(1); } // ждём, пока клиент передаст какие-нибудь данные
String request = client.readStringUntil('\r'); // читаем первую строку запроса
Serial.println(request); // пишем первую строку запроса
client.flush(); // передаём то, что запросил клиент
if (request.indexOf("/Select=0") != -1 ) // если дельта не была выбрана или отменена
{
setTmp = 0; // устанавливаем уставку 0
delta = 0; // обнуляем дельту
}
if (request.indexOf("/Select=1") != -1 ) // если выбрана дельта 0,1 градуса
{
setTmp = SteamTemp + 0.1; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,1 градуса
Serial.print("setTmp="); // выводим новое значение уставки на монитор
Serial.println(setTmp);
delta = 1; // устанавливаем дельту 0,1 градуса
}
if (request.indexOf("/Select=2") != -1 ) // если выбрана дельта 0,2 градуса
{
setTmp = SteamTemp + 0.2; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,2 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
delta = 2; // устанавливаем дельту 0,2 градуса
}
if (request.indexOf("/Select=3") != -1 ) // если выбрана дельта 0,3 градуса
{
setTmp = SteamTemp + 0.3; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,3 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
delta = 3; // устанавливаем дельту 0,3 градуса
}
if (request.indexOf("/Select=4") != -1 ) // если выбрана дельта 0,4 градуса
{
setTmp = SteamTemp + 0.4; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,4 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
delta = 4; // устанавливаем дельту 0,4 градуса
}
if (request.indexOf("/Select=5") != -1 ) // если выбрана дельта 0,5 градуса
{
setTmp = SteamTemp + 0.5; // устанавливаем температуру отключения клапана равной текущей температуре плюс 0,4 градуса
Serial.print("setTmp "); // выводим новое значение уставки на монитор
Serial.println(setTmp);
delta = 5; // устанавливаем дельту 0,5 градуса
}
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println(""); // do not forget this one
client.println("<!DOCTYPE HTML>");
//Индикация времени работы программы
client.println("Operation Time ");
time = millis()/1000; // берём миллисекунды и делим на 1000, получаем секунды
int hours = time/3600; // считаем количество часов
int minutes = (time%3600)/60; // остаток от деления секунд на 3600 делим на 60 - получаем количество минут
int seconds = (time%3600)%60; // остаток от деления на 60 остатка от деления секунд на 3600 - это секунды
if (hours<10) // если количество часов меньше 10
{
Serial.print ("0"); // ставим впереди незначащий ноль
client.print ("0"); // ставим впереди незначащий ноль
}
Serial.print (hours); // пишем количество часов
Serial.print (":"); // пишем двоеточие
client.print (hours); // пишем количество часов
client.print (":"); // пишем двоеточие
if (minutes<10) // если количество минут меньше 10
{
Serial.print ("0"); // ставим впереди незначащий ноль
client.print ("0"); // ставим впереди незначащий ноль
}
Serial.print (minutes); // пишем количество минут
Serial.print (":"); // пишем двоеточие
client.print (minutes); // пишем количество минут
client.print (":"); // пишем двоеточие
Serial.print (seconds);
client.print (seconds);
client.println("<br><br>");
// Индикация температуры с датчиков
client.println("<h4>Sensors Reading</h4>");
client.println("<br><br>");
client.println("Steam Sensor ");
client.print(SteamTemp);
client.println(" °C");
client.println("<br><br>");
client.println("Pipe Sensor ");
client.print(PipeTemp);
client.println(" °C");
client.println("<br><br>");
client.println("Water/Phlegm Sensor ");
client.print(WaterTemp);
client.println(" °C");
client.println("<br><br>");
client.println("Tank Sensor ");
client.print(TankTemp);
client.println(" °C");
client.println("<br><br>");
client.println("Control Temperature ");
client.print(setTmp);
client.println(" °C");
client.println("<br><br>");
if(delta == 1) { client.print("<h2>Selected delta <a><button>+0.1 °C</button></a> <br></h2>"); }
if(delta == 2) { client.print("<h2>Selected delta <a><button>+0.2 °C</button></a> <br></h2>"); }
if(delta == 3) { client.print("<h2>Selected delta <a><button>+0.3 °C</button></a> <br></h2>"); }
if(delta == 4) { client.print("<h2>Selected delta <a><button>+0.4 °C</button></a> <br></h2>"); }
if(delta == 5) { client.print("<h2>Selected delta <a><button>+0.5 °C</button></a> <br></h2>"); }
client.print("Select delta");
client.println("<br><br>");
client.print("<a href=\"/Select=0\"><button> No delta </button></a> <br>");
client.print("<a href=\"/Select=1\"><button> + 0.1 °C </button></a> <br>");
client.print("<a href=\"/Select=2\"><button> + 0.2 °C </button></a> <br>");
client.print("<a href=\"/Select=3\"><button> + 0.3 °C </button></a> <br>");
client.print("<a href=\"/Select=4\"><button> + 0.4 °C </button></a> <br>");
client.print("<a href=\"/Select=5\"><button> + 0.5 °C </button></a> <br>");
}Ещё один день дал немного результатов, но всё это смахивает на игру.
Удалось значительно ускорить загрузку странички с данными. Ускорения достиг за счёт предварительной подготовки текста html. Если раньше скорость загрузки была 10-15 Б/с по показаниям Operы, то сейчас она составляет 212 Б/с. Визуально это практически мгновенно, но есть задержка между нажатием кнопки "Обновить" и появлением текста. Размер html странички 672 Б.
Есть один ***, у которого я присмотрел кое-что. А именно использование AJAX. Там всё оказалось не так сложно, как кажется, когда читаешь на латинице. К сожалению, информации на родном языке про это немного. Однако, для того, чтобы всё это работало как надо, требуется web-дизайн, если можно так выразиться. В голом текстовом виде всё это смотрится очень неказисто.
С блинк нормально получается, мне нравиться.
https://esp8266.ru/esp8266-blynk/
Papazol, ссылка битая и я её удалил. Предупреждаю, что все твои наработки этот "энтузиаст" возьмёт и выдаст за своё.. С третьяковым не связывайся.
этот "энтузиаст"
Я ему ничего не даю, наоборот, у него беру
То, что он не сам разрабатывает программы, а в лучшем случае собирает из кусков, видно по его видосам. Но зёрна ценной информации там есть, я уже набросал программку, которая работает. Есть некоторые вопросы, которые хотелось бы решить до выкладывания программы на всеобщее оборзение 
Но это работает, именно так, как хотелось бы, в реальном времени меняются показания температуры и др.
Кому интересно, выкладываю проект: https://drive.google.com/file/d/0B-FxHV … sp=sharing
В архиве есть текстовый файл с описанием.
Изображение не прикрепляется...
Papazol, не выходит каменный цветок. Не хочет заливать, не знаешь причину.![]()
Вроде пошла прошивка модуля. Чуть позже напишу что сделать нужно еще кроме того, что написано в инструкции.
Что бы появился плагин в Arduino IDE нужно сделать следующее
Пойти по ссылке https://github.com/esp8266/arduino-esp8266fs-plugin
Опуститься чуть ниже на странице и перейти как на картинке
Скачать зип файл который отмечен. Распаковать его в папку arduino в указанный каталог
![]()
После этого в IDE появиться аплодер и все нормально прошивается
Таким образом все прошилось, однако
18. При первом старте в модуль не прошиты SSID и пароль сети WiFi, к которой модуль должен подключаться. Поэтому после нескольких безуспешных попыток модуль переходит в режим точки доступа с названием AutoSamAP. Если подключиться к этой точке (без пароля) и войти в её Web-интерфейс по адресу http://192.168.4.1, то там можно воспользоваться менеджером WiFi и сделать всё, что нужно. Указанные SSID и пароль сети, к которой будет подключаться модуль, сохраняются в EEPROM. В следующий раз, когда модуль стартанёт, он сразу подключится к нужной сети.
Не появился он в моей сети, что то не так делаю наверно.
Кажется и это решил.
В системном мониторе IDE после перезагрузки модуля появляется IP адрес. Его вводим в адресную строку браузера.
Что не понятно- нужно будет каждый раз подключаться физически к модулю, что бы узнать его адрес. Ведь он будет меняться?
Сразу по расширению функционала, ну то есть что еще хотелось бы здесь:
1. Добавить работу клапана и по верхнему датчику, аналогично 2/3
2. Добавить выбор задержки времени после восстановления температуры на датчике 1.2.3.4.5 минут.
3. Контроль атмосферного давления. Показания после начала работы и текущее.
4. Показания температуры при которой начался процесс.
с 161 по 180 из 201