Регулятор нагрузки при помощи arduino nano.
Данный регулятор управляется при помощи arduino и симисторного выхода. Необходимую мощность мощно выставить при помощи двух кнопок, а подаваемая мощность отображается на трехразрядном семисегментном индикаторе в процентах (0 - мощность не подается, 100 - максимальная мощность). Данный регулятор можно использовать для плавного управления нагревом нагревателей (для самогонных аппаратов самое то).
![](https://habrastorage.org/getpro/habr/upload_files/dab/4b4/eec/dab4b4eeca12241241569e6d930bd4d2.png)
Код программы для 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)
kulhaker478
00.00.0000 00:00+57segments.h демонстрирует всю суровость и беспощадность Arduino-стиля)
Кстати, статье очень не хватает демонстрации финального устройства (или рендеров платы на худой конец) и формата публикации "Кейс"
sim2q
00.00.0000 00:00+27segments.h демонстрирует всю суровость и беспощадность Arduino-стиля)
в тэгах же есть: Будущее здесь
DerSpiwak
00.00.0000 00:00все-же ардуино тут не при чем. Скорее причина это совершенно бесхитростный подход или непонимание как можно работать с ресурсами ардуины.
Arhammon
00.00.0000 00:00+2Если нет включения-выключения детекции нуля, то зачем огород городить? Есть оптосимисторы которые переключение в нуле делают сами.
И указанный смистор только для резистивной нагрузки. Для использования с чем-то другим желательна снабберная цепь.
SergeyMax
00.00.0000 00:00+2Интересно было бы посмотреть на реализацию фазового регулятора мощности на оптосимисторе с переключением в нуле)
randomsimplenumber
00.00.0000 00:00Легко;) Это же для электронагревателя. Период pwm может быть и 1 сек, например.
Hidden text
По цене Arduino можно купить готовый термостат. С корпусом, индикатором, и микроконтроллером внутри. Для самогонного аппарата самое то :)
SergeyMax
00.00.0000 00:00+1Ключевое слово тут было "фазовый".
randomsimplenumber
00.00.0000 00:00+1Фазу можно крутить не только внутри периода напряжения в сети ;) Фаза внутри периода в 1 сек, например.
Конечно да, тот фазовый регулятор, который использует свойство тиристора включаться при любом (*) напряжении а выключаться при 0 (*), на симисторе с детекцией нуля не получится.
(*) - точные значения есть в даташите :)
Arhammon
00.00.0000 00:00Для тэна тепловая инерционность вполне позволяет регулировать отрезая полуволну. 100 полуволн в секунду для много хватит.
An_private
00.00.0000 00:00+3Зачем нужны классы классы входов/выходов реле и некоего оптопрерывателя если они не используются в проекте? И зачем вообще они нужны, если всё, что они делают, это задают режим и дублируют штатные digitalRead/digitalWrite?
Зачем вообще для нагревателя использовать фазовое регулирование? Куда проще коммутировать по периодам - тогда можно вообще выкинуть схему детектирования нуля и просто поставить оптрон с коммутацией по нулю.
Класс управления семисегментным индикатором просто за пределами добра и зла.
Для чего вообще нужен регулятор без обратной связи (по температуре, например)?
Надеюсь понятно, что характеристика регулирования фазового регулятора сильно нелинейна? И изменение на 1 вовсе не означает изменение мощности на 1%? Ну, на всякий случай.
Приоритеты логических, арифметических и сравнительных операций лучше явно указывать скобками - это существенно облегчает понимание логики действий. То есть не
!btnState && !_flag && millis() - _tmr >= 100
а
!btnState && !_flag && ((millis() - _tmr) >= 100)
VT100
00.00.0000 00:00По "железу", помимо отмеченного снаббера, — не хватает резистора от Gate к нижнему по схеме MainTerminal.
Да и при ТЭНовой нагрузке снаббер не бесполезен.
SergeyMax
Triac