• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Esp8266 + дисплей +энкодер + датчики

Написал код с выводом пунктов меню динамически(в цикле) из конструкции.
const char *menu_item[] = { "Контраст", "Подсвет", "Сброс", "Пункт 4", "Пункт 5"};
Сам код ниже
#include "PCF8574_PCD8544.h"
#include "GyverEncoder.h"

//display status variables
//boolean backlightStatus = 0;
int contrast = 30;

static uint32_t tmr; // переменная времени
int t_light = 2000; // время работы подсветки


//menu variables
int currentItem = 1;
int menuItems = 5;//количество выводимых пунктов меню

boolean up = false;
boolean down = false;
boolean middle = false;

#define ENC_A 12 //D6
#define ENC_B 14 //D5
#define ENC_KEY 13 //D7
#define NOKIA_BL 3 //ножка P3 на расширителе портов PCF8574

//EncButton2<EB_ENCBTN> enc(INPUT, ENC_A, ENC_B, ENC_KEY);
Encoder enc1(ENC_A, ENC_B, ENC_KEY);

// I2C to SPI via PCF8574 interface (slower updates, less pins):
// address (LCD interface: 0x20-0x27 selectable by connecting pads, all open=0x27)
// pcf-P7 - 5. Serial clock out (SCLK, CLK)
// pcf-P6 - 4. Serial data out (DIN)
// pcf-P5 - 3. Data/Command select (D/C, DC)
// pcf-P4 - 2. LCD chip select (CS, CE), can be set to -1 if not used (tie line low)
// pcf-P3 - 7. Backlight control (LIGHT), not used in i2c display constructor
// pcf-P2 - 1. LCD reset (RST), can be set to -1 if not used (tie line high or to reset of MCU)
PCF8574_PCD8544 display = PCF8574_PCD8544(0x27, 7, 6, 5, 4, 2);

const char *menu_item[] = { "Контраст", "Подсвет", "Сброс", "Пункт 4", "Пункт 5"};

void setup() {
Serial.begin(115200);
enc1.setType(TYPE2); // тип энкодера TYPE1 одношаговый, TYPE2 двухшаговый. Если ваш энкодер работает странно, смените тип\=
// attachInterrupt(0, isr_ENC_A, CHANGE); // прерывание на 12 пине! CLK у энка
// attachInterrupt(1, isr_ENC_B, CHANGE); // прерывание на 14 пине! DT у энка
// attachInterrupt(2, isr_ENC_KEY, CHANGE);

display.cp437(true);
display.begin();
display.clearDisplay();
setContrast();
//updateBacklight();
drawMenu();
}

//ICACHE_RAM_ATTR void isr_ENC_A() {
// enc1.tick(); // отработка в прерывании
//}
//
//ICACHE_RAM_ATTR void isr_ENC_B() {
// enc1.tick(); // отработка в прерывании
//}
//
//ICACHE_RAM_ATTR void isr_ENC_KEY() {
// enc1.tick(); // отработка в прерывании
//}

void loop() {
readRotaryEncoder();

// bool buttonPressed = (up || down || middle);
//
if (up || down || middle) {
if (up && currentItem > 1) {
up = false;
currentItem --;
Serial.print("\n");
Serial.println(currentItem);
//drawMenu();
}
if (down && currentItem < menuItems) {
down = false;
currentItem ++;
Serial.print("\n");
Serial.println(currentItem);
//drawMenu();
}
//adjust contrast
if (middle && currentItem == 1) {
middle = false;
contrast = contrast + 2;
if (contrast > 60) {
contrast = 10;
}
Serial.print("\n");
Serial.println(currentItem);
//setContrast();
//drawMenu();
}

//toggle backlight status
if (middle && currentItem == 2) {
middle = false;
// if (backlightStatus = 0) {
// backlightStatus = 1;
// } else {
// backlightStatus = 0;
// }
Serial.print("\n");
Serial.println(currentItem);
//updateBacklight();
}
//toggle reset
if (middle && currentItem == 3) {
middle = false;
//backlightStatus = 0;
contrast = 30;
//updateBacklight();
Serial.print("\n");
Serial.println(currentItem);
//setContrast();
}
drawMenu();
setContrast();
// updateBacklight();
}
}

//обработка энкодера
void readRotaryEncoder() {
//enc.tick();
enc1.tick();
if (enc1.isRight()) { //Если вправо то курсор по меню вниз
turnBacklightOnFewSec();
Serial.print("\n");
Serial.println("Вправо");
// display.setTextSize(1);
// display.clearDisplay();
// display.setTextColor(BLACK, WHITE);
// display.setCursor(15, 0);
// display.print("RIGHT");
// display.display();
down = true;
//delay(150);
} else if (enc1.isLeft()) { //Если влево то курсор по меню вверх
turnBacklightOnFewSec();
Serial.print("\n");
Serial.println("Влево");
// display.setTextSize(1);
// display.clearDisplay();
// display.setTextColor(BLACK, WHITE);
// display.setCursor(15, 0);
// display.print("LEFT");
// display.display();
up = true;
//delay(150);
}
if (enc1.isClick()) //Если кликнуть то "проваливаемся" в пункт меню
{
turnBacklightOnFewSec();
Serial.print("\n");
Serial.println("1 Клик");
// display.setTextSize(1);
// display.clearDisplay();
// display.setTextColor(BLACK, WHITE);
// display.setCursor(15, 0);
// display.print("CLICK");
// display.display();
middle = true;
}
if (millis() - tmr >= t_light) { //Задаем время работы подсветки 1000ms = 1sec
turnBacklightOff();
}
}

void turnBacklightOff()
{
//digitalWrite(NOKIA_BL, LOW);
display.digitalWrite(NOKIA_BL, LOW);
}

void turnBacklightOn()
{
//digitalWrite(NOKIA_BL, HIGH);
display.digitalWrite(NOKIA_BL, HIGH);//на экспандере 2.2 вольта на ножке
}

void turnBacklightOnFewSec()//отключение подсветки через заданное время
{
turnBacklightOn();
tmr = millis();
}

//отрисовка меню
void drawMenu() {
display.setTextSize(1);
display.clearDisplay();
display.setTextColor(BLACK, WHITE);
display.setCursor(0, 0);
display.print(" DISPLAY MENU ");
display.drawFastHLine(0, 10, 83, BLACK);

for (int i = 0; i <= menuItems - 1; i++) {
drawMenuItem(i);
}
display.display();
}

void drawMenuItem(int item) {
int y = (item * 10) + 15;//вычисление вертикальной позиции меню
if (item == currentItem - 1) {
display.setTextColor(WHITE, BLACK);
} else {
display.setTextColor(BLACK, WHITE);
}
display.setCursor(0, y);
//русские шрифты заработали после замены файла
//glcdfont.c и добавление отдельного скетча utf8rus.ino
display.print(utf8rus(menu_item[item]));
//display.print(menu_item[item]);
}

void setContrast() {
display.setContrast(contrast);
display.display();
}

//void updateBacklight() {
// display.digitalWrite(NOKIA_BL, backlightStatus);//запись в пин через расширитель PCF8574
// //digitalWrite(NOKIA_BL, backlightStatus);
//}

Не могу додумать, как задать условия, чтобы когда гуляешь по меню вниз или вверх, то меню "листалось".
photo1665603624 (1).jpeg
Сейчас у меня обновление экрана нет, когда пункт уходит за пределы экрана.
photo1665603624.jpeg
Как бы это тоже, динамически сделать.
 

Mоnk

Member
Как бы это тоже, динамически сделать.
Может поиграться с int y = (item * 10) + 15;//вычисление вертикальной позиции меню
В зависимости от положения "курсора" display.setTextColor(BLACK, WHITE); "писать" строки меню не с нулевой, а с первой, и так далее.
 
Параллельно с "накоживанием" или "накодиванием" своего меню, взялся за адаптацию этого кода
с библиотекой N5110_SPI.h автора
Код заточен под avr чипы, ардуино нано или atmega 328P - на последней проверял, все работает, но мне надо переписать его под esp8266(esp12f).
В коде меняю методы вывода на дисплей, под библиотеку
Которая совместима также с adafruit для дисплея нокия 5110.
Задал вопросы в ветке автора
Но боюсь ответов не будет...
Может кто-то посообразительнее меня, и подскажет, что да как?
Как допустим такой код
lcd.fillWin(0, 2, encoderPos, 1, 0xfc);
Переписать, чтобы выводил как в библиотеке adafruit?
Код:
  display.setTextSize(1);
  display.clearDisplay();
  display.setTextColor(BLACK, WHITE);
  display.setCursor(0, 0);
  display.print(" DISPLAY MENU ");
  display.drawFastHLine(0, 10, 83, BLACK);
  display.display();
 
Экодер надо не опрашивать в цикле, а вешать на прерывания
Только как понять, какие у esp8266 ноги для прерываний?
esp-12-pinout.png
У ардуино нано с этим проще - D2(INT0) и D3(INT1) штатные ноги на которых отслеживается прерывание по умолчанию вроде.
45f3202e545d485faa6d1b5fb71bc574[1].jpg
Даже в таблице по esp8266 не ясно где там прерывания по даташиту
pin_functions.png
У меня по определенным причинам энкодер "прикручен" на ноги
#define ENC_A 12 //D5
#define ENC_B 14 //D6
#define ENC_KEY 13 //D7

Получается мне никак прерывания не задействовать на них?
 
P.S. Нашел все таки информацию по пинам для прерываний на esp8266
Interrupts
Pin interrupts are supported through attachInterrupt(), detachInterrupt() functions. Interrupts may be attached to any GPIO pin except GPIO16, but since GPIO6-GPIO11 are typically used to interface with the flash memory ICs on most esp8266 modules, applying interrupts to these pins are likely to cause problems. Standard Arduino interrupt types are supported: CHANGE, RISING, FALLING.
Получается можно использовать прерывания на любом пине кроме gpio16 и GPIO6-GPIO11(они используются для flash памяти).
Код:
attachInterrupt(ENC_A, isr_ENC_A, CHANGE);   
attachInterrupt(ENC_B, isr_ENC_B, CHANGE);
И функции
Код:
ICACHE_RAM_ATTR  void isr_ENC_A() {
  enc1.tick();  // отработка в прерывании
}

ICACHE_RAM_ATTR void isr_ENC_B() {
  enc1.tick();  // отработка в прерывании
}
Только результата и изменений, при опросе энкодера без прерываний и с ними я не заметил:confused:
 

Mоnk

Member
Нашел все таки информацию по пинам
Вот простенько описано информация.
Я так понимаю, что в сторону ESP-8285 Вы смотреть не хотите? Там на два пина больше, если взять ESP-01M. А мне на этой неделе пришли модули с двумя метрами
image.png
 

Mоnk

Member
пока выжимаю максимум из esp12f.
Ну что же, могу только пожелать Вам удачи на этом тернистом пути.
Максимум, что мне удалось "выжать" - три реле, энкодер, rtc-lcd24x2-bme280, четыре ds18b20, управление через iotManager (старый, теперь заброшенный автором) и web-морду. Фоторезистор само собой. Пищалка. И еще одна нога RX без дела осталась. Пока не представляю, для чего мне еще больше ног? Если только цветной дисплей с тачем приклячить? Нафига он в пыльном курятнике нужен?
 
Когда работаешь с wifi на esp8266 то для подключения к сети, используется конструкция
Код:
  while (WiFi.status() != WL_CONNECTED) {
      delay(1000);
      Serial.print(".");
  }
То есть пока условие
не true, то в порт рисуются точки, с периодом 1 секунда.
Пробую заменить delay(); на millis();
Код:
  while (WiFi.status() != WL_CONNECTED) {
    if (millis() - tmr >= 1000) {
      //delay(1000);
      Serial.print(".");
      tmr = millis();
    }
  }
И вижу, что в случае использования delay(); с периодом 1 секунда, подключение к сети wifi происходит через 3 попытки.
В случае же использования millis(); происходят чудеса.
Во первых с периодом 1 секунда подключение к сети wifi происходит через 20-30 попыток.
Если увеличить период до 1500 например, то esp8266 вообще перезагружается.
Удалось подключать к wifi esp8266 с периодом 200 мс но через 5-10 попыток.
Что делаю не так, или библиотека
не предполагает использование иного способа "разнести" время подключений, кроме delay?
 

enjoynering

Well-known member
У ардуино esp что то типа псведо операционки с 2я стеками. В первом крутится код wifi и все критические вещи, во втором ваш код.

Когда вы делаете delay(), то чтобы не кушать электричество за зря, ардуино esp начинает исполнятся код wifi. Когда вы вызываете mills() или delayMicroseconds() такого не происходит и критический код из первого стека не исполняется - система вешается и перегружается.
 

CodeNameHawk

Moderator
Команда форума
не предполагает использование иного способа "разнести" время подключений, кроме delay?
Это не "время подключений", а время ожидания.
Можете просто его выкинуть из программы, если перед работой с сетью проверяете, подключены ли к сети.
Вместо того, что бы просто ждать, успеете уже дисплей инициализировать и вывести информацию.
Но в основном цикле не должно быть долгих операций, тут вам и пригодиться millis, но не для задержки, а для выполнения кода по времени. Нп. Так, в начале цикла проверили время и если ещё не пора, просто вышли из цикла. А когда время пришло, быстро выполнили операцию(в идеале в коде не должно быть никаких задержек) и вышли из цикла.
Если хотите ускорить подключение, используйте подключение с заданными ip адресами, номером канала роутера и его МАС адресом. Подключается примерно за одну секунду.
 
Проблема в том, что большинство примеров кода из интернета(а именно из них мы черпаем свои вдохновения и учимся), подключаются к wifi единожды, потому что конструкция
Код:
 WiFi.mode(WIFI_STA);
 WiFi.begin(ssid, password);
прописала в секции
Поэтому в секции loop(){} требуется вызывать запрос на подключение повторно, в случае отсоединения сети.
 

CodeNameHawk

Moderator
Команда форума
Код:
    WiFi.mode(WIFI_STA);
    WiFi.config(ip, ip_gate, ip_subnet, ip_dns1, ip_dns2); //zadajemy statyczny adres
    WiFi.begin(ssid, password, 5, BSSID_Routera, true);
https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/station-class.html#config
Посмотрите на setAutoConnect, setAutoReconnect.
прописала в секции
Что мешает вам учиться, поменять, попробовать?
Вынесите процедуру подключения в отдельную функцию...
 
Последнее редактирование:

CodeNameHawk

Moderator
Команда форума
на esp8266 похоже не работает.
Включите AutoConnect и AutoReconnect.
В ардуиноиде включите Debug Level: WIFI
Подключитесь к роутеру.
Отключите роутер, потом снова подключите.
Покажите результаты вывода в сериал. И скетч тоже.
 
С реконнектом разобрался.
Не могу понять, для ESP8266 количество millis(); в коде ограничено?
Пытаюсь одно из двух реле включаться на 8 секунд позже, но получаю одновременное включение.
Код:
if (temp < (temp_set - gisterezis))
    {
      relay1 = true;
      if (millis() - tmr >= 8000) {
        tmr = millis();
        relay1 = true;
      }
      updateStatePins();
    }


void updateStatePins(void) {
  if (relay1) {
    PCF.write(0, LOW);
  } else {
    PCF.write(0, HIGH);
  }
  if (relay2) {
    PCF.write(0, LOW);
  } else {
    PCF.write(0, HIGH);
  }
}
 

CodeNameHawk

Moderator
Команда форума
Не могу понять,
Нет комментариев, вот и не можете понять.
но получаю одновременное включение.
Ну, так может быть, а может и не быть, все будет зависеть от скрытого кода.
В верхней функции возможно ошибка копи-пасте.
 
Сверху Снизу