Регулятор нагрузки при помощи arduino nano.

Данный регулятор управляется при помощи arduino и симисторного выхода. Необходимую мощность мощно выставить при помощи двух кнопок, а подаваемая мощность отображается на трехразрядном семисегментном индикаторе в процентах (0 - мощность не подается, 100 - максимальная мощность). Данный регулятор можно использовать для плавного управления нагревом нагревателей (для самогонных аппаратов самое то).

Ссылка на GitHub

Код программы для arduino:

#include "7segments.h"
#include "MyLib.h"
#include "Buttons.h"
#include <GyverTimers.h>
#include <GyverDimmer.h>
Segmentor segment(5, 6, 7, 8, 9, 10, 11);
unsigned long currentDisplay = 0;

MyRelayOut firstTrans(A7);
MyRelayOut secondTrans(3);
MyRelayOut thirdTrans(4);

Button buttonUp(A0);
Button buttonDown(A1);

const int nullDetector = 2; // детектор нуля
const int simistr = 12;
Dimmer<simistr> dim;
int valuePercent = 0;
void setup() {
  attachInterrupt(0, isr, RISING);
  Timer2.enableISR();
}

void loop() {
  if(buttonUp.click()){valuePercent++;}
  if(buttonDown.click()){valuePercent--;}
  if(valuePercent < 0){valuePercent = 0;}
  else if(valuePercent > 100){valuePercent = 100;}
  Display(valuePercent);
  dim.write(map(valuePercent, 0, 100, 0, 255));
}

void isr() {
  // вызывать в прерывании детектора нуля
  // если tickZero() - true - нужно перезапустить таймер с периодом getPeriod()
  if (dim.tickZero()) Timer2.setPeriod(dim.getPeriod());
  else Timer2.restart();
  // иначе перезапустить со старым
}

// прерывание таймера
ISR(TIMER2_A) {
  dim.tickTimer();    // вызвать tickTimer()
  Timer2.stop();      // останавливаем таймер
}

void Display(int value){
  if(millis() - currentDisplay >= 10){
    currentDisplay = millis();
    int firstNumber = value%10;
    int secondNumber = value%100/10;
    int thirdNumber = value/100;
    firstTrans.on();
    segment.chooseNumber(firstNumber);
    firstTrans.off();
    secondTrans.on();
    segment.chooseNumber(secondNumber);
    secondTrans.off();
    thirdTrans.on();
    segment.chooseNumber(thirdNumber);
    thirdTrans.off();
  }
}

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

Код мной написанных библиотек:

MyLib.h

#pragma once
#include <Arduino.h>

class MyRelayOut{
public:
  MyRelayOut(int pin){
    pinMode(pin, OUTPUT);
    digitalWrite(pin, false);
    _pin = pin;
  }

  void on(){
    if(_state){
      return;
    }
    _state = true;
    digitalWrite(_pin, _state);
  }

  void off(){
    if(!_state){
      return;
    }
    _state = false;
    digitalWrite(_pin, _state);
  }
private:
  int _pin;
  bool _state;
};

class MyRelayIn{
public:
  MyRelayIn(int pin){
    pinMode(pin, INPUT);
    _pin = pin;
  }

  bool read(){
    if(digitalRead(_pin) == LOW){
      return false;
    }
    if(digitalRead(_pin) == HIGH){
      return true;
    }
  }
private:
  int _pin;
};

class MyOpticSensor{
public:
  MyOpticSensor(int pin){
    pinMode(pin, INPUT);
    _pin = pin;
  }

  bool read(){
    if(digitalRead(_pin) == LOW){ //если перед датчиком нет препятствий
      return true;
    }
    if(digitalRead(_pin) == HIGH){ // если препятсивя есть
      return false;
    }
  }
private:
  int _pin;
};

Buttons.h

#pragma once
#include <Arduino.h>

class Button {
  private:
    byte _pin;
    uint32_t _tmr;
    bool _flag;
  public:
    Button (byte pin) {
      _pin = pin;
      pinMode(_pin, INPUT_PULLUP);
    }
    bool click() {
      bool btnState = digitalRead(_pin);
      if (!btnState && !_flag && millis() - _tmr >= 100) {
        _flag = true;
        _tmr = millis();
        return true;
      }
      if (!btnState && _flag && millis() - _tmr >= 500) {
        _tmr = millis ();
        return true;
      }
      if (btnState && _flag) {
        _flag = false;
        _tmr = millis();
      }
      return false;
    }
};

7segments

#pragma once
#include <Arduino.h>

class Segmentor{
  private:
    int _pinA;
    int _pinB;
    int _pinC;
    int _pinD;
    int _pinE;
    int _pinF;
    int _pinG;
  public:
    Segmentor(int pinA, int pinB, int pinC, int pinD, int pinE, int pinF, int pinG){
      pinMode(pinA, OUTPUT);
      pinMode(pinB, OUTPUT);
      pinMode(pinC, OUTPUT);
      pinMode(pinD, OUTPUT);
      pinMode(pinE, OUTPUT);
      pinMode(pinF, OUTPUT);
      pinMode(pinG, OUTPUT);
      digitalWrite(pinA, LOW);
      digitalWrite(pinB, LOW);
      digitalWrite(pinC, LOW);
      digitalWrite(pinD, LOW);
      digitalWrite(pinE, LOW);
      digitalWrite(pinF, LOW);
      digitalWrite(pinG, LOW);
      _pinA = pinA;
      _pinB = pinB;
      _pinC = pinC;
      _pinD = pinD;
      _pinE = pinE;
      _pinF = pinF;
      _pinG = pinG;
    }
    void one(){
      digitalWrite(_pinA, LOW);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, LOW);
      digitalWrite(_pinE, LOW);
      digitalWrite(_pinF, LOW);
      digitalWrite(_pinG, LOW);
    }
    void two(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, LOW);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, HIGH);
      digitalWrite(_pinF, LOW);
      digitalWrite(_pinG, HIGH);
    }
    void three(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, LOW);
      digitalWrite(_pinF, LOW);
      digitalWrite(_pinG, HIGH);
    }
    void four(){
      digitalWrite(_pinA, LOW);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, LOW);
      digitalWrite(_pinE, LOW);
      digitalWrite(_pinF, HIGH);
      digitalWrite(_pinG, HIGH);
    }
    void five(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, LOW);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, LOW);
      digitalWrite(_pinF, HIGH);
      digitalWrite(_pinG, HIGH);
    }
    void six(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, LOW);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, HIGH);
      digitalWrite(_pinF, HIGH);
      digitalWrite(_pinG, HIGH);
    }
    void seven(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, LOW);
      digitalWrite(_pinE, LOW);
      digitalWrite(_pinF, LOW);
      digitalWrite(_pinG, LOW);
    }
    void eight(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, HIGH);
      digitalWrite(_pinF, HIGH);
      digitalWrite(_pinG, HIGH);
    }
    void nine(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, LOW);
      digitalWrite(_pinF, HIGH);
      digitalWrite(_pinG, HIGH);
    }
    void zero(){
      digitalWrite(_pinA, HIGH);
      digitalWrite(_pinB, HIGH);
      digitalWrite(_pinC, HIGH);
      digitalWrite(_pinD, HIGH);
      digitalWrite(_pinE, HIGH);
      digitalWrite(_pinF, HIGH);
      digitalWrite(_pinG, LOW);
    }
    void chooseNumber(int k){
      switch(k){
        case 0:
          zero();
          break;
        case 1:
          one();
          break;
        case 2:
          two();
          break;
        case 3:
          three();
          break;
        case 4:
          four();
          break;
        case 5:
          five();
          break;
        case 6:
          six();
          break;
        case 7:
          seven();
          break;
        case 8:
          eight();
          break;
        case 9: 
          nine();
          break;   
      }
    }
};

Комментарии (15)


  1. SergeyMax
    00.00.0000 00:00
    +2

    simistr

    Triac


  1. kulhaker478
    00.00.0000 00:00
    +5

    7segments.h демонстрирует всю суровость и беспощадность Arduino-стиля)

    Кстати, статье очень не хватает демонстрации финального устройства (или рендеров платы на худой конец) и формата публикации "Кейс"


    1. sim2q
      00.00.0000 00:00
      +2

      7segments.h демонстрирует всю суровость и беспощадность Arduino-стиля)

      в тэгах же есть: Будущее здесь


    1. DerSpiwak
      00.00.0000 00:00

      все-же ардуино тут не при чем. Скорее причина это совершенно бесхитростный подход или непонимание как можно работать с ресурсами ардуины.


  1. Arhammon
    00.00.0000 00:00
    +2

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

    И указанный смистор только для резистивной нагрузки. Для использования с чем-то другим желательна снабберная цепь.


    1. SergeyMax
      00.00.0000 00:00
      +2

      Интересно было бы посмотреть на реализацию фазового регулятора мощности на оптосимисторе с переключением в нуле)


      1. randomsimplenumber
        00.00.0000 00:00

        Легко;) Это же для электронагревателя. Период pwm может быть и 1 сек, например.

        Hidden text

        По цене Arduino можно купить готовый термостат. С корпусом, индикатором, и микроконтроллером внутри. Для самогонного аппарата самое то :)


        1. SergeyMax
          00.00.0000 00:00
          +1

          Ключевое слово тут было "фазовый".


          1. randomsimplenumber
            00.00.0000 00:00
            +1

            Фазу можно крутить не только внутри периода напряжения в сети ;) Фаза внутри периода в 1 сек, например.

            Конечно да, тот фазовый регулятор, который использует свойство тиристора включаться при любом (*) напряжении а выключаться при 0 (*), на симисторе с детекцией нуля не получится.

            (*) - точные значения есть в даташите :)


            1. SergeyMax
              00.00.0000 00:00
              +2

              Начните наверное для начала с гуглинга аббревиатур pfc и pwm, чтобы вы со всеми остальными на одном языке разговаривали)


              1. VT100
                00.00.0000 00:00

                Разве Брезенхем не подмножество фазового регулятора?


      1. Arhammon
        00.00.0000 00:00

        Для тэна тепловая инерционность вполне позволяет регулировать отрезая полуволну. 100 полуволн в секунду для много хватит.


  1. net_men
    00.00.0000 00:00

    "Необходимую мощность моЩно выставить при помощи двух кнопок "


  1. An_private
    00.00.0000 00:00
    +3

    Зачем нужны классы классы входов/выходов реле и некоего оптопрерывателя если они не используются в проекте? И зачем вообще они нужны, если всё, что они делают, это задают режим и дублируют штатные digitalRead/digitalWrite?

    Зачем вообще для нагревателя использовать фазовое регулирование? Куда проще коммутировать по периодам - тогда можно вообще выкинуть схему детектирования нуля и просто поставить оптрон с коммутацией по нулю.

    Класс управления семисегментным индикатором просто за пределами добра и зла.

    Для чего вообще нужен регулятор без обратной связи (по температуре, например)?

    Надеюсь понятно, что характеристика регулирования фазового регулятора сильно нелинейна? И изменение на 1 вовсе не означает изменение мощности на 1%? Ну, на всякий случай.

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

    !btnState && !_flag && millis() - _tmr >= 100

    а

    !btnState && !_flag && ((millis() - _tmr) >= 100)


  1. VT100
    00.00.0000 00:00

    По "железу", помимо отмеченного снаббера, — не хватает резистора от Gate к нижнему по схеме MainTerminal.
    Да и при ТЭНовой нагрузке снаббер не бесполезен.