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

Диммер на ESP-01

CodeNameHawk

Moderator
Команда форума
Но TX при загрузке шлет информацию в последовательный порт, от этого не уйдешь.
Простой резистор спасет есп от кз. И скорее всего сериал останется работоспособным, сможете нормально отлаживать.
 
Последнее редактирование:

nikolz

Well-known member
Но TX при загрузке шлет информацию в последовательный порт, от этого не уйдешь. Это то же самое, что товарищ, который сделал диммер на ESP, использовал для детектора 0 пин GPIO0. И на переднюю панель этого диммера вывел кнопку перезапуска. У него спрашивают, а зачем вам кнопка перезапуска на передней панели, а он отвечает, что ESP стартует не с первого раза и приходится перезапускать несколько раз для нормального старта.
Вы обновите схему, чтобы можно было на нее ссылаться.
------------
Ваш товарищ, который использовал GPIO0 для ввода просто не изучал документацию и учебники.
Поэтому сделал все через жопу.
-----------------------
Объясняю как надо делать правильно.
пин GPIO надо использовать для управления триаком.
В этом случае все будет работать правильно.
--------------
В результате у вас TX будет свободным и пусть шлет все что хочется.
 

nikolz

Well-known member
а если будете делать на ESP-12, то можно использовать не только GPIO0, но и GPIO15 (даже лучше для триака).
 

Melandr

Member
Насчет ESP-12 вопросов вообще нет. Просто валялась ESP-01 и решил макетку сделать, для отлаживания таймера для ванной. А в будущем планирую заказать платы в Китае, под смд версию ESP-12. А на ней проблем а с выводами свободными ощущается не так. Позже найду ссылку на видео этого товарища, там и схема есть. Вечером переделаю детектор на инверсный сигнал и посмотрю что получится.
 

Melandr

Member
Добрый вечер!
Переделал детектор нуля по следующей схеме
Посмотреть вложение схема.png
Сигнал на детекторе нуля
SDS00005.png
Сигнал управления на светодиод симистора с ESP
SDS00006.png
тот же сигнал, растянутый по горизонтали
SDS00007.png
Вывод одновременно сигналов управления симистором и детектора перехода через 0
SDS00008.png
Правда яркость пока на минимальном уровне и не увеличивается.
Код скетча, прошитый в ESP
Код:
//#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial    // Comment this out to disable prints and save space
#include <CayenneMQTTESP8266.h>                                     // Include library file for MQTT

#define TRIAC_PIN     2         //GPIO2
#define ZC_PIN        1         //GPIO1 - TXD
#define BUTTON_PIN    3         //GPIO3 - RXD

void ICACHE_RAM_ATTR zero_crosss_int();

char ssid[] = "ASUS";                                               // Your WIFI Name   
char wifiPassword[] = "gCU8YNZs";                                    // Your WIFI password
char username[] = "***";           // Your MQTT cayenne username
char Password[] = "****";       // Your MQTT cayenne Password
char clientID[] = "***";           // Your MQTT cayenne clientID

int DIMMING_VALUES = 0;
int DIMMING_TIME = 0;
int BUTTON = 0;
int lightStatus = 0;

volatile unsigned long countZeroCross = 0;
unsigned long lastMillis = 0;
/*
// This function sends Arduino's up time every second to Virtual Pin 5.
void sendUptime()
{
  // Send values using the virtualWrite function. Cayenne currently accepts int and float values.
  // Please don't send more that 10 values per second.
  Cayenne.virtualWrite(V5, millis() / 1000);
}*/

//=====================Basic Setup ============================

void setup(){ 
//   Serial.begin(115200);                                              // Setup Debug uart port if you want ?
   pinMode(TRIAC_PIN, OUTPUT);                                         // Set AC Load pin as output
   digitalWrite(TRIAC_PIN, LOW);
   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(ZC_PIN, INPUT_PULLUP);
  
   attachInterrupt(digitalPinToInterrupt(ZC_PIN), zero_crosss_int, FALLING);         // Choose the zero cross interrupt egde selection
   Cayenne.begin(username, Password, clientID, ssid, wifiPassword); // Setup cayenne server for MQTT protocol

}

void loop()
  {

      if(millis() - lastMillis > 10000){
        lastMillis = millis();
      }
      BUTTON = digitalRead(BUTTON_PIN);
  }

void ICACHE_RAM_ATTR zero_crosss_int()                   //function to be fired at the zero crossing to dim the light
{
  DIMMING_TIME = (75 * DIMMING_VALUES);  // For 60Hz =>65
  delayMicroseconds(DIMMING_TIME);       // Wait till firing the TRIAC
  digitalWrite(TRIAC_PIN, HIGH);            // Fire the TRIAC
  delayMicroseconds(10);                 // triac On propogation delay
                                         // (for 60Hz use 8.33) Some Triacs need a longer period
  digitalWrite(TRIAC_PIN, LOW);             // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
  countZeroCross += countZeroCross;
}

CAYENNE_OUT_DEFAULT()
{

  Cayenne.virtualWrite(V0, millis());

  Cayenne.virtualWrite(V5, BUTTON);

  Cayenne.virtualWrite(V6, countZeroCross);   

}

CAYENNE_IN(1)
{
  int Dimm_Val=getValue.asInt();
  DIMMING_VALUES = (120-Dimm_Val);

}

CAYENNE_IN(2)
{
  digitalWrite(TRIAC_PIN, !getValue.asInt());  // to get the value from the website 
}
//==================================================================
 

Melandr

Member
Правда, при такой схеме подключения детектора нуля не получается прошить с подключенным pull-down резистором на 10к. И импульсы управления симистором вроде бы присутствуют, но при изменении значения диммирования, импульс открытия симистора не перемещается относительно перехода через 0.
И наблюдается мерцание лампы. Походу нужно переписать обработчик прерывания. Так как вроде бы яркость регулируется, но с уменьшение яркости усиливается мерцание (тухнут вразнобой на доли секунд)
 

nikolz

Well-known member
Правда, при такой схеме подключения детектора нуля не получается прошить с подключенным pull-down резистором на 10к. И импульсы управления симистором вроде бы присутствуют, но при изменении значения диммирования, импульс открытия симистора не перемещается относительно перехода через 0.
И наблюдается мерцание лампы. Походу нужно переписать обработчик прерывания. Так как вроде бы яркость регулируется, но с уменьшение яркости усиливается мерцание (тухнут вразнобой на доли секунд)
Я выше выкладывал алгоритм. Попробуйте запрограммировать его. Он до безобразия простой.
И выложите изменения в схеме.
Детектор нуля должен давать стабильные импульсы около нуля. И ВСЕ. Если они есть и стабильные то забудьте про него.

Еще рекомендую Вам совместить OK1 и OK2 и убрать диодный мост на входе OK2 т е сделать как в OK1
Смысл ловить половину периода нет , а стабильность с одним периодом будет выше, так как импульсы существенно шире, чем сейчас.

Алгоритм у вас вроде бы с ошибкой.
 

Melandr

Member
ОК1 и ОК2 совместить не получится. При выключенном выключателе света, сигнала с ОК1 не будет. Но вентилятор должен работать. Поэтому и пришлось лепить две оптопары по входу.
 

nikolz

Well-known member
ОК1 и ОК2 совместить не получится. При выключенном выключателе света, сигнала с ОК1 не будет. Но вентилятор должен работать. Поэтому и пришлось лепить две оптопары по входу.
пардон, не увидел что вход разный. Вопрос снимаю.
 

Melandr

Member
Доброй ночи!
Посмотрел я на СИ-код, ранее делал диммер на ATmega8
И понял, что нужно конкретно переписывать код, который используется сейчас. Проблема в том, что на Си для AVR я знаю как писать, а для ESP могу только Arduino (wiring)
Код:
/*******************************************************
  Chip type               : ATmega8
  AVR Core Clock frequency: 8,000000 MHz
*******************************************************/

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define BUT_PLUS     PC1            //кнопка увеличения оборотов
#define BUT_MINUS     PC0            //кнопка уменьшения оборотов
#define ZERO_CROSS    PD2            //Вход импульсов ZERO-CROSS (INT0)

#define CONTROL_TRIAC PD1          //Вывод на управляющий электрод симистора
#define V_CHANGE_SPEED 10         //Шаг регулировки скорости изменения оборотов
#define T_PULSE_TRIAC 30           //Длительность отпирающего импульса симистора 20 мкс (смотреть по ТУ для конкретного симистора)

volatile unsigned char speed = 0xB8;        //256-216 = 40 тиков по 0,128мс = 5,12 мс

//Обработчик прерывания от INT0 при переходе сетевого напряжения через 0
ISR (INT0_vect)
{

  TCNT0 = speed;
  TCCR1B = 0;
  asm("cli");
  //прерывание по переполнению T1 каждые 10мс (8 МГц/8 = 1 МГц, 1 тик - 1 мкс)
  //Число тактов таймера до переполнения = 0x2710 (10000 тиков по 1 мкс)
  TCNT1H = 0xFF - 0x27;
  TCNT1L = 0xFF - 0x10;
  TCCR1B |= (1 << CS11);            // Коэффициент деления предделителя - 8.
  asm("sei");
}

// Обработчик прерывания по переполнению Timer 0
ISR (TIMER0_OVF_vect)
{
  // Импульс открытия на симистор
  PORTD &= ~(1 << CONTROL_TRIAC);
  _delay_us(T_PULSE_TRIAC);
  PORTD |= (1 << CONTROL_TRIAC);
}

// Обработчик прерывания по переполнению Timer 1 (второй полупериод сетевого напряжения)
ISR (TIMER1_OVF_vect)
{
  // инициализируем знчение счетного регистра таймера/счетчика 0
  TCNT0 = speed;

  PORTB = !PORTB;
}

int main(void)
{
  // Инициализация портов ввода/вывода
  DDRB = 0b00000001;            //PB0 - на вывод (для отладки меандр)
  PORTB = 0b00000000;

  DDRC = 0b00000000;            //PC0, PC1 - на ввод (кнопки "ПЛЮС" и "МИНУС")
  PORTC = 0b00000011;            //подтягивающий резистор включен на PC0 и PC1

  DDRD = 0b00000011;            //PD1, PD0 - на вывод (симистор)
  PORTD = 0b00000000;           

  // Инициализация таймера/счетчика 0
  TCCR0 |= (1 << CS02) | (1 << CS00);    //Коэффициент деления предделителя - 1024.
  //прерывание по переполнению T0 каждые 10мс (8 МГц/1024 = 7,813 кГц, 1 тик - 128 мкс)
  TCNT0 = 0xB2;

  // Инициализация таймера/счетчика 1
  TCCR1A = 0;                       
  TCCR1B |= (1 << CS11);            // Коэффициент деления предделителя - 8.
  //Число тактов таймера до переполнения = 0x2710 (10000 тиков по 1 мкс)
  TCNT1H = 0xFF - 0x27;
  TCNT1L = 0xFF - 0x10;

  // Инициализация прерываний таймеров/счетчиков
  TIMSK |= (1 << TOIE1) | (1 << TOIE0);        //прерывание по переполнению таймера Т1 и таймера Т0

  // Инициализация внешних прерываний
  GICR |= (0 << INT1) | (1 << INT0);        //включаем внешнее прерывание 0
  MCUCR |= (1 << ISC01) | (1 << ISC00);        //прерывание срабатывает по растущему фронту на INT0
  GIFR |= (1 << INTF0);                        //сбрасываем флаг внешнего прерывания 0

  // Глобальное разрешение прерываний
  asm("sei");

  while (1)
  {

    if (PINC & (1 << BUT_PLUS)) {
      speed = speed + 1;
      if (speed > 253) {
        speed = 253;
      };
      _delay_ms(16);
    }

    if (PINC & (1 << BUT_MINUS)) {
      speed = speed - 1;
      if (speed < 180) {
        speed = 180;
      };
      _delay_ms(16);
    };

  };
  return 0;
}
 

nikolz

Well-known member
Доброй ночи!
Посмотрел я на СИ-код, ранее делал диммер на ATmega8
И понял, что нужно конкретно переписывать код, который используется сейчас. Проблема в том, что на Си для AVR я знаю как писать, а для ESP могу только Arduino (wiring)
Код:
/*******************************************************
  Chip type               : ATmega8
  AVR Core Clock frequency: 8,000000 MHz
*******************************************************/

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define BUT_PLUS     PC1            //кнопка увеличения оборотов
#define BUT_MINUS     PC0            //кнопка уменьшения оборотов
#define ZERO_CROSS    PD2            //Вход импульсов ZERO-CROSS (INT0)

#define CONTROL_TRIAC PD1          //Вывод на управляющий электрод симистора
#define V_CHANGE_SPEED 10         //Шаг регулировки скорости изменения оборотов
#define T_PULSE_TRIAC 30           //Длительность отпирающего импульса симистора 20 мкс (смотреть по ТУ для конкретного симистора)

volatile unsigned char speed = 0xB8;        //256-216 = 40 тиков по 0,128мс = 5,12 мс

//Обработчик прерывания от INT0 при переходе сетевого напряжения через 0
ISR (INT0_vect)
{

  TCNT0 = speed;
  TCCR1B = 0;
  asm("cli");
  //прерывание по переполнению T1 каждые 10мс (8 МГц/8 = 1 МГц, 1 тик - 1 мкс)
  //Число тактов таймера до переполнения = 0x2710 (10000 тиков по 1 мкс)
  TCNT1H = 0xFF - 0x27;
  TCNT1L = 0xFF - 0x10;
  TCCR1B |= (1 << CS11);            // Коэффициент деления предделителя - 8.
  asm("sei");
}

// Обработчик прерывания по переполнению Timer 0
ISR (TIMER0_OVF_vect)
{
  // Импульс открытия на симистор
  PORTD &= ~(1 << CONTROL_TRIAC);
  _delay_us(T_PULSE_TRIAC);
  PORTD |= (1 << CONTROL_TRIAC);
}

// Обработчик прерывания по переполнению Timer 1 (второй полупериод сетевого напряжения)
ISR (TIMER1_OVF_vect)
{
  // инициализируем знчение счетного регистра таймера/счетчика 0
  TCNT0 = speed;

  PORTB = !PORTB;
}

int main(void)
{
  // Инициализация портов ввода/вывода
  DDRB = 0b00000001;            //PB0 - на вывод (для отладки меандр)
  PORTB = 0b00000000;

  DDRC = 0b00000000;            //PC0, PC1 - на ввод (кнопки "ПЛЮС" и "МИНУС")
  PORTC = 0b00000011;            //подтягивающий резистор включен на PC0 и PC1

  DDRD = 0b00000011;            //PD1, PD0 - на вывод (симистор)
  PORTD = 0b00000000;         

  // Инициализация таймера/счетчика 0
  TCCR0 |= (1 << CS02) | (1 << CS00);    //Коэффициент деления предделителя - 1024.
  //прерывание по переполнению T0 каждые 10мс (8 МГц/1024 = 7,813 кГц, 1 тик - 128 мкс)
  TCNT0 = 0xB2;

  // Инициализация таймера/счетчика 1
  TCCR1A = 0;                     
  TCCR1B |= (1 << CS11);            // Коэффициент деления предделителя - 8.
  //Число тактов таймера до переполнения = 0x2710 (10000 тиков по 1 мкс)
  TCNT1H = 0xFF - 0x27;
  TCNT1L = 0xFF - 0x10;

  // Инициализация прерываний таймеров/счетчиков
  TIMSK |= (1 << TOIE1) | (1 << TOIE0);        //прерывание по переполнению таймера Т1 и таймера Т0

  // Инициализация внешних прерываний
  GICR |= (0 << INT1) | (1 << INT0);        //включаем внешнее прерывание 0
  MCUCR |= (1 << ISC01) | (1 << ISC00);        //прерывание срабатывает по растущему фронту на INT0
  GIFR |= (1 << INTF0);                        //сбрасываем флаг внешнего прерывания 0

  // Глобальное разрешение прерываний
  asm("sei");

  while (1)
  {

    if (PINC & (1 << BUT_PLUS)) {
      speed = speed + 1;
      if (speed > 253) {
        speed = 253;
      };
      _delay_ms(16);
    }

    if (PINC & (1 << BUT_MINUS)) {
      speed = speed - 1;
      if (speed < 180) {
        speed = 180;
      };
      _delay_ms(16);
    };

  };
  return 0;
}
Ну вот опять двадцать пять.
А сложно посмотреть алгоритм, который я выше написал.
-----------------
Начнем ликбез.
-------------------
Не знаю что Вы заканчивали,
но алгоритм работы любого устройства лучше написать на том языке,
на котором Вы привыкли мыслить.
Полагаю, что это Ваш родной язык.
-------------------
Так вот, технология разработки софта содержит несколько этапов:
Выбор метода,
разработка алгоритма,
написание программы,
компиляция
линковка
загрузка
отладка.
----------------------------
Написание программы - это дебильный перевод алгоритма с естественного языка на искусственный язык программирования.
Этот перевод можно сделать тупо со словарем,
так как языки программирования - это более примитивные языки, чем естественные.
----------------
Но они более сложны в понимании человеком, чем его родной язык.
----------------
Сложность разработки софта у самоучек и аля кулибиных в том, что они не утруждают себя
из-за своей безграмотности -выбором метода ,
а из-за своей лени - разработкой алгоритма.

Они начинают сразу лепить программу,
пытаясь придумать алгоритм на искусственном языке программирования,
на котором они не умеют мыслить.

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

Melandr

Member
Добрый день!
Наконец смог добраться до диммера, так как на работе завал, и не успеваю хобби заниматься. По поводу алгоритма, как я его вижу и как я его делал на AVR
Для реализации алгоритма управления симистором используем 2 обработчика прерывания. Основное прерывание при переходе сетевого напряжения через "0" каждые 10 мс. После срабатывания данного прерывания, мы должны таймером отсчитать от начала периода время задержки включения симистора, обратно пропорциональное требуемой яркости. То есть если требуется максимальная яркость, то включаем сразу, если минимальная, то включаем симистор в конце полупериода. Импульс открытия симистора можно сделать 10-50 мкс, или оставить открытым симистор до конца полупериода. Но тут возникает подводный камень, если нагрузка индуктивная, то симистор может не выключиться, если в момент перехода сетевого напряжения через "0" , будет протекать ток через симистор.
Далее в следующем полупериоде повторяем вышеописанные действия.
В коде для AVR, выше в теме используется 3 обработчика прерываний. Так как для отслеживания перехода через "0" используется только положительная полуволна сетевого напряжения, поэтому таймер Т1 используется для отсчета 10 мс интервалов, а таймер Т0 используется для организации задержки включения симистора после перехода напряжения через "0".
Для ESP доступен один железный таймер, поэтому использовал двухполупериодное выпрямление, чтобы не отслеживать 10 мс интервалы.
Далее в основном цикле для изменения яркости обновляем текущее значение яркости - требуемым значением. Требуемое значения получаем из облака, http-страницы или последовательного порта. Это уже детали. Правда еще непонятно, если формировать импульс управления симистором в обработчике прерывания с помощью функции delay(us) насколько это правильно?
Теперь есть вопрос по "железному" таймеру ESP8266. Использовал библиотеку hw_timer.h я так понимаю из SDK ESP. Но так как я пишу в Arduino IDE, то почему-то не срабатывает прерывание от таймера. В видео с работающим примером
товарищ крайне рекомендует использовать менеджер плат версии 2.4.1. Сделал даунгрейд последней версии менеджера плат в Arduino IDE, но даже не получается прошить ESP.
Если у кого есть простейший пример использования данной библиотеки в среде Arduino IDE? то был бы рад если бы поделились, так как все найденные готовые исходники, почему-то нормально не работают.
 

nikolz

Well-known member
Добрый день!
Наконец смог добраться до диммера, так как на работе завал, и не успеваю хобби заниматься. По поводу алгоритма, как я его вижу и как я его делал на AVR
Для реализации алгоритма управления симистором используем 2 обработчика прерывания. Основное прерывание при переходе сетевого напряжения через "0" каждые 10 мс. После срабатывания данного прерывания, мы должны таймером отсчитать от начала периода время задержки включения симистора, обратно пропорциональное требуемой яркости. То есть если требуется максимальная яркость, то включаем сразу, если минимальная, то включаем симистор в конце полупериода. Импульс открытия симистора можно сделать 10-50 мкс, или оставить открытым симистор до конца полупериода. Но тут возникает подводный камень, если нагрузка индуктивная, то симистор может не выключиться, если в момент перехода сетевого напряжения через "0" , будет протекать ток через симистор.
Далее в следующем полупериоде повторяем вышеописанные действия.
В коде для AVR, выше в теме используется 3 обработчика прерываний. Так как для отслеживания перехода через "0" используется только положительная полуволна сетевого напряжения, поэтому таймер Т1 используется для отсчета 10 мс интервалов, а таймер Т0 используется для организации задержки включения симистора после перехода напряжения через "0".
Для ESP доступен один железный таймер, поэтому использовал двухполупериодное выпрямление, чтобы не отслеживать 10 мс интервалы.
Далее в основном цикле для изменения яркости обновляем текущее значение яркости - требуемым значением. Требуемое значения получаем из облака, http-страницы или последовательного порта. Это уже детали. Правда еще непонятно, если формировать импульс управления симистором в обработчике прерывания с помощью функции delay(us) насколько это правильно?
Теперь есть вопрос по "железному" таймеру ESP8266. Использовал библиотеку hw_timer.h я так понимаю из SDK ESP. Но так как я пишу в Arduino IDE, то почему-то не срабатывает прерывание от таймера. В видео с работающим примером
товарищ крайне рекомендует использовать менеджер плат версии 2.4.1. Сделал даунгрейд последней версии менеджера плат в Arduino IDE, но даже не получается прошить ESP.
Если у кого есть простейший пример использования данной библиотеки в среде Arduino IDE? то был бы рад если бы поделились, так как все найденные готовые исходники, почему-то нормально не работают.
по мне так все гораздо проще.
-----------------
Прерывание нужно одно от пина по переходу через ноль.
В колбеке этого прерывания грузим значение требуемой задержки в переменную X.
Так как пишем на ардуине, то в ней используется бесконечный цикл.
Вначале этого цикла сбрасываем пин управления триаком в ноль.
далее вычитаем из переменной X например 1 и если X==0,
то выдаем на пин триака импульс длительностью 10-50 мкс
конец цикла.
------------------
Вот и весь алгоритм.
 
Сверху Снизу