// Hot air gun temperature controller
// Arduino pro mini
// 5V; 16 MHz; 328P
// Settings
#define FAN_MINIMAL 20
#define SV_MINIMAL 100
#define SV_MAXIMAL 450
#include <EEPROM.h>
// Display
#define HV 4
#define DATA 5
#define LATCH 11
#define CLK 18
byte regL, regH;
// Control
#define ENC_CLK 3
#define DIRECT 7
#define BUTTON 8
#define POW_SW 9
int bt_timer;
bool bt_tap, bt_hold, bt_hold_long;
byte SV_show_timer;
volatile bool power_control;
bool instrument_connected;
bool device_hot;
unsigned int sleep_timer;
bool sleeping;
byte eeprom_timer;
bool eeprom_store;
bool show_SV_on_iddle;
// Inputs
#define INSTRUMENT 13
#define TEMP_PV 0
#define USE 1
int PV, PV_buf [8];
byte PV_buf_index;
bool reg_action;
int disp_PV, disp_PV_buf[5];
byte disp_PV_buf_index;
volatile int SV, SV2;
int task;
bool SV2_mode;
float error, deltaPV, prevPV, integr;
byte use_value;
byte use_state_temp, use_state_temp_old, use_state_stable_count;
volatile byte use_state;
// 0: instrument on holder
// 1: instrument in use, button pressed
// 2: instrument in use, button released
volatile bool stabilized;
byte stabile_timer;
// Outputs
#define HEAT 10
#define FAN 6
#define RELAY 12
#define SUPPLY 17
#define BEEP 19
volatile bool triac_control;
volatile int power;
float task_power;
float kp, ki, kd;
byte fan_speed;
volatile byte set_fan_speed;
bool relay_state, relay_state_old;
volatile bool let_work;
byte start_timer;
byte beep_timer;
byte display_mode;
// 0: PV
// 1: SV
// 2: Fan
//enum {PV,SV,FAN} display_mode; //https://alexgyver.ru/lessons/variables-types/
// Serial dialog
String inData;
char inChar[15];
float float_var;
byte inSymbol;
void setup() {
Serial.begin(9600);
Serial.println("Hello");
print_help();
pinMode(HV,OUTPUT);
pinMode(DATA,OUTPUT);
pinMode(LATCH,OUTPUT);
pinMode(CLK,OUTPUT);
pinMode(ENC_CLK,INPUT_PULLUP);
pinMode(DIRECT,INPUT);
pinMode(BUTTON,INPUT);
pinMode(POW_SW,INPUT);
pinMode(INSTRUMENT,INPUT);
pinMode(HEAT,OUTPUT);
pinMode(FAN,OUTPUT);
pinMode(RELAY,OUTPUT);
pinMode(SUPPLY,OUTPUT);
pinMode(BEEP,OUTPUT);
TCCR1A = 0;
//TCCR1B = 1<<WGM12 | 1<<CS11 | 1<<CS10; // enable CTC, prescale 64
TIMSK1 = 1<<OCIE1A; // enable compare interrupt
//OCR1A = 1250;
// 250: 1mS for max power
// 2250: 9mS for min power
// 25: 0.1ms for open triac
TCCR2A = 0; //TMR2 normal mode
TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; // prescale 1024, overflow each 1/(16000000/1024/256=61Hz) = 16.4mS
TIMSK2 = 1<<TOIE2; // enable overflow interrupt
TCNT2 = 0; // reset counter
EICRA = 1<<ISC11 | 1<<ISC01; // Falling edge generate interrupt
EIMSK = 1<<INT1 | 1<<INT0; // Enable interrupt
SV = 0; // = 300;
power = 0;
start_timer = 0;
sleep_timer = 0;
TCCR0B = 1; // default is 3 (prescaler 64)
eeprom_timer = 255;
analogReference(INTERNAL);
GetSV();
GetPIDkoef();
print_PID();
}
void loop() {
power_control = !(digitalRead(POW_SW));
instrument_connected = (digitalRead(INSTRUMENT));
digitalWrite(HV,(!(sleeping)));
if (PV>80) device_hot = 1; if (PV<50) device_hot = 0;
if (power_control) digitalWrite(SUPPLY,1);
else digitalWrite(SUPPLY,(device_hot && instrument_connected));
if (use_state > 0) sleep_timer = 0;
if (use_state > 0)
{
if (SV2_mode) task = SV2; else task = SV;
}
else {if (sleeping) task = 0; else task = 100;}
if (use_state > 0) {SV2_mode = (use_state == 1);}
relay_state = (PV < 480)&&(power_control)&&(!(sleeping));
if ( relay_state_old != relay_state )
{
let_work = 0; //Serial.print("Let work: "); Serial.println(let_work);
delay(3200); //500*64
relay_state_old = relay_state;
digitalWrite(RELAY,relay_state); //Serial.print("Relay: "); Serial.println(relay_state);
delay(3200); //500*64
let_work = 1; //Serial.print("Let work: "); Serial.println(let_work);
}
if (reg_action)
{
reg_action = 0;
PV = 0;
for (byte i = 0; i < 4; i++)
{
PV = PV + PV_buf[i];
}
PV = PV >> 2;
if (instrument_connected) PV = map(PV,0,500,25,500); else PV = 0;
error = task - PV;
deltaPV = prevPV - PV;
prevPV = PV;
if ( (task_power>0) && (task_power<1000) ) integr = integr + (ki * error);
task_power = ((kp * error) + integr + (kd * deltaPV));
power = constrain(task_power, 0, 1000);
//Serial.print("Power: "); Serial.print(task_power); Serial.print(", "); Serial.println(power);
disp_PV_buf[disp_PV_buf_index] = PV;
disp_PV_buf_index++;
if (disp_PV_buf_index>4)
{
disp_PV_buf_index = 0;
disp_PV = 0;
for (byte i = 0; i < 4; i++)
{
disp_PV = disp_PV + disp_PV_buf[i];
}
disp_PV = disp_PV >> 2; //Serial.println(use_state);
}
stabilized = ((abs(PV - task)) < 5) && (use_state > 0);
}
if (power_control)
{
if (sleeping)
{
if (device_hot) fan_speed = 30; else fan_speed = 0;
}
else
{
if (use_state > 0) fan_speed = set_fan_speed; else fan_speed = 20;
}
}
else fan_speed = 50;
if (fan_speed < 80) analogWrite(FAN, map(fan_speed,0,80,0,100)); else analogWrite(FAN, map(fan_speed,80,100,100,255));
if (eeprom_store)
{
eeprom_store = 0;
//beep_timer = 12; // 200 mS tone
StoreSV();
}
switch (display_mode)
{
case 0: DisplayValue(disp_PV); break;
case 1: if (SV2_mode) DisplayValue(SV2); else DisplayValue(SV); break;
case 2: DisplayValue(set_fan_speed); break;
}
if (bt_tap)
{
bt_tap = 0;
sleep_timer = 0;
if ( (power_control)&&(instrument_connected) ) {if (display_mode == 2) display_mode = 0; else (display_mode = 2);}
}
if (bt_hold)
{
bt_hold = 0;
sleep_timer = 0;
if ( (power_control)&&(instrument_connected) ) {if (use_state == 0) SV2_mode = !(SV2_mode);}
}
if (bt_hold_long)
{
bt_hold_long = 0;
if (use_state == 0) show_SV_on_iddle = !(show_SV_on_iddle);
}
SetLamp(0,( ((display_mode == 0)||(display_mode == 1))&& instrument_connected ) );
SetLamp(1,SV2_mode);
SetLamp(2,((display_mode == 1)));
SetLamp(3,(display_mode == 2));
if(Serial.available() >0)
{
inData = "";
while(Serial.available() > 0)
{
delay(1280); //20*64
inSymbol = Serial.read();
inData += char(inSymbol);
}
inData.toCharArray(inChar, inData.length() +1);
float_var = atof(inChar);
//Serial.println(inData);
if (inData.indexOf("help") > -1)
{
print_help();
}
else if (inData.indexOf(" sv1") > -1)
{
SV = int(float_var);
Serial.print("New SV is ");
Serial.println(SV);
eeprom_timer = 0;
}
else if (inData.indexOf(" sv2") > -1)
{
SV2 = int(float_var);
Serial.print("New SV2 is ");
Serial.println(SV2);
eeprom_timer = 0;
}
else if (inData.indexOf(" fan") > -1)
{
set_fan_speed = byte(float_var);
Serial.print("New FAN value is ");
Serial.println(set_fan_speed);
eeprom_timer = 0;
}
else if (inData.indexOf("list") > -1)
{
print_PID();
}
else if (inData.indexOf("store") > -1)
{
StorePIDkoef();
Serial.println("Stored");
}
else if (inData.indexOf(" p") > -1)
{
kp = float_var;
Serial.print("kp: ");
Serial.println(kp);
}
else if (inData.indexOf(" i") > -1)
{
ki = float_var;
Serial.print("ki: ");
Serial.println(ki);
}
else if (inData.indexOf(" d") > -1)
{
kd = float_var;
Serial.print("kd: ");
Serial.println(kd);
}
else print_help();
}
}
ISR(TIMER2_OVF_vect) //timer 2 interrupt
{
// temperatute measuring
PV_buf[PV_buf_index] = analogRead(TEMP_PV)>>1;
//Serial.print(PV_buf[PV_buf_index]); Serial.print(" "); Serial.println(PV_buf_index);
PV_buf_index ++;
if (PV_buf_index > 4) {PV_buf_index = 0; reg_action = 1;}
// use state recognize
use_value = analogRead(USE)>>2; // 0, 142, 255
if (use_value < 70) use_state_temp = 0;
else if (use_value < 190) use_state_temp = 1;
else use_state_temp = 2;
if ( use_state_temp_old == use_state_temp ) {if (use_state_stable_count < 255) use_state_stable_count++;}
else {use_state_stable_count = 0; use_state_temp_old = use_state_temp;}
if ( use_state_stable_count == 20 ) use_state = use_state_temp;
// button manage
if (digitalRead(BUTTON)) if ( (bt_timer>6) && (bt_timer<62) ) {bt_tap = 1; bt_hold = 0; bt_hold_long = 0;}// >100mS, <1000mS
if (bt_timer==62) {bt_tap = 0; bt_hold = 1; bt_hold_long = 0;}// = 1000mS
if (bt_timer==244) {bt_tap = 0; bt_hold = 1; bt_hold_long = 1;}// = 4000mS
if (!(digitalRead(BUTTON))) {if (bt_timer<32768) bt_timer++;} else bt_timer = 0;
// stabilization beep
if (stabilized) {if (stabile_timer < 255) stabile_timer ++;}
else stabile_timer = 0;
if (stabile_timer == 182) beep_timer = 12; // after 3000 ms, 200 mS tone
// SV show
if (power_control)
{
if ( (use_state == 0) && (show_SV_on_iddle) ) { if (display_mode == 0) display_mode = 1;} else
{ if (SV_show_timer < 122) {SV_show_timer++;} else {if (display_mode ==1) display_mode = 0;} }
}
else display_mode = 0;
// eeprom store timer
if (eeprom_timer == 250) eeprom_store = 1; // 4.1 sec
if (eeprom_timer < 255) eeprom_timer++;
// initial start delay
if (start_timer < 255) start_timer++;
if (start_timer == 122) let_work = 1;
// sleep
if (sleep_timer < 65500) sleep_timer++;
if (sleep_timer == 29268) beep_timer = 122; // 2000mS // about 480s, 8min
if (sleep_timer == 32927) beep_timer = 244; // 4000mS // about 540s, 9min
sleeping = (sleep_timer > 36585); // about 600s, 10min
// beep
if (beep_timer > 0) beep_timer--;
digitalWrite(BEEP,(beep_timer > 0));
}
ISR(TIMER1_COMPA_vect)
{
if (triac_control)
{
TCCR1B = 0;
digitalWrite(HEAT,LOW);
triac_control = 0;
}
else
{
TCNT1 = 0; //TCCR1B = 0;
digitalWrite(HEAT,HIGH); //delayMicroseconds(100); digitalWrite(HEAT,LOW);
OCR1A = 25;
triac_control = 1;
}
}
ISR(INT0_vect) //INT0 interrupt, Zero cross
{
triac_control = 0;
//OCR1A = 1250;
// 250: 1mS for max power
// 2250: 9mS for min power
// 25: 0.1ms for open triac
TCNT1 = 0;
if (power > 0)
{
OCR1A = map(power,1,1000,2250,250);
TCCR1B = 1<<WGM12 | 1<<CS11 | 1<<CS10; // enable CTC, prescale 64
}
else digitalWrite(HEAT,LOW);
}
ISR(INT1_vect) //INT1 interrupt, encoder
{
if ( ((display_mode == 0) || (display_mode == 1)) && let_work && power_control && instrument_connected)
{
if (SV2_mode)
{
if (digitalRead(DIRECT)) {if (SV2 < SV_MAXIMAL) SV2 = SV2 + 5;}
else {if (SV2 > SV_MINIMAL) SV2 = SV2 - 5;}
}
else
{
if (digitalRead(DIRECT)) {if (SV < SV_MAXIMAL) SV = SV + 5;}
else {if (SV > SV_MINIMAL) SV = SV - 5;}
}
eeprom_timer = 0;
SV_show_timer = 0;
display_mode = 1;
Serial.print("SV: "); Serial.print(SV); Serial.print("; SV2: "); Serial.println(SV2);
}
if (display_mode == 2)
{
if (digitalRead(DIRECT)) {if (set_fan_speed < 100) set_fan_speed = set_fan_speed + 2;}
else {if (set_fan_speed > FAN_MINIMAL) set_fan_speed = set_fan_speed - 2;}
eeprom_timer = 0;
Serial.print("Fan: "); Serial.println(set_fan_speed);
}
sleep_timer = 0;
//beep_timer = 31; // 500mS
//digitalWrite(BEEP, HIGH); delay(5000); digitalWrite(BEEP, LOW);
}
void StoreSV()
{
EEPROM_int_write(0,SV); // 0,1
EEPROM_int_write(2,SV2); // 2,3
EEPROM.update(4,set_fan_speed); // 4
EEPROM.update(5,show_SV_on_iddle); // 5
}
void GetSV()
{
SV = EEPROM_int_read(0); // 0,1
if (SV > SV_MAXIMAL) SV = 0;
SV2 = EEPROM_int_read(2); // 2,3
if (SV2 > SV_MAXIMAL) SV2 = 0;
set_fan_speed = EEPROM.read(4); // 4
show_SV_on_iddle = EEPROM.read(5); // 5
}
void GetPIDkoef()
{
kp = EEPROM_float_read(5); // 5,6,7,8
ki = EEPROM_float_read(9); // 9,10,11,12
kd = EEPROM_float_read(13); // 13,14,15,16
}
void StorePIDkoef()
{
EEPROM_float_write(5,kp); // 5,6,7,8
EEPROM_float_write(9,ki); // 9,10,11,12
EEPROM_float_write(13,kd); // 13,14,15,16
}
void print_PID()
{
Serial.print("p: ");
Serial.print(kp);
Serial.print("; i: ");
Serial.print(ki);
Serial.print("; d: ");
Serial.print(kd);
Serial.println(";");
}
int EEPROM_int_read(int addr) {
byte raw[2];
for(byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr+i);
int &num = (int&)raw;
return num;
}
void EEPROM_int_write(int addr, int num) {
byte raw[2];
(int&)raw = num;
for(byte i = 0; i < 2; i++) EEPROM.update(addr+i, raw[i]);
}
float EEPROM_float_read(int addr) {
byte raw[4];
for(byte i = 0; i < 4; i++) raw[i] = EEPROM.read(addr+i);
float &num = (float&)raw;
return num;
}
void EEPROM_float_write(int addr, float num) {
byte raw[4];
(float&)raw = num;
for(byte i = 0; i < 4; i++) EEPROM.update(addr+i, raw[i]);
}
void DisplayValue(int data)
{
byte dig1,dig2,dig3;
if (data < 100) dig3 = 0x0F; else dig3 = (data/100)%10; //Serial.print(dig3); Serial.print(" ");
if (data < 10) dig2 = 0x0F; else dig2 = (data/10)%10; //Serial.print(dig2); Serial.print(" ");
dig1 = (data%10); //Serial.print(dig1); Serial.print(" ");
regH = regH & 0xF0;
regH = regH + dig3; //Serial.print(regH,BIN); Serial.print(" ");
regL = dig2;
regL = regL << 4;
regL = regL + dig1; //Serial.print(regL,BIN); Serial.print(" ");
Update595();
}
void SetLamp(byte lamp, bool mode)
{
//Serial.print(lamp); Serial.print(" ");
switch (lamp)
{
case 0: if (mode) regH = regH | 0b00010000; else regH = regH & 0b11101111; break;
case 1: if (mode) regH = regH | 0b00100000; else regH = regH & 0b11011111; break;
case 2: if (mode) regH = regH | 0b01000000; else regH = regH & 0b10111111; break;
case 3: if (mode) regH = regH | 0b10000000; else regH = regH & 0b01111111; break;
}
//Serial.print(regH,BIN); Serial.print(" "); Serial.print(regL,BIN); Serial.println(" ");
Update595();
}
void Update595()
{
byte i, regH_, regL_;
regH_ = regH;
regL_ = regL;
digitalWrite(LATCH,LOW);
digitalWrite(CLK,LOW);
for (i = 0; i < 8; i++)
{
if (regH_ & 0x80)
{
digitalWrite(DATA,HIGH);
//Serial.print("Y ");
}
else
{
digitalWrite(DATA,LOW);
//Serial.print("N ");
}
digitalWrite(CLK,HIGH);
digitalWrite(CLK,LOW);
regH_ = regH_ << 1;
}
for (i = 0; i < 8; i++)
{
if (regL_ & 0x80)
{
digitalWrite(DATA,HIGH);
//Serial.print("Y ");
}
else
{
digitalWrite(DATA,LOW);
//Serial.print("N ");
}
digitalWrite(CLK,HIGH);
digitalWrite(CLK,LOW);
regL_ = regL_ << 1;
}
digitalWrite(LATCH,HIGH);
digitalWrite(LATCH,LOW);
}
void print_help()
{
Serial.println("***********************************");
Serial.println("Commands");
Serial.println("help: shows help");
Serial.println("XX sv1: set new SV");
Serial.println("XX sv2: set new SV2");
Serial.println("XX fan: set new fan speed");
Serial.println("XX.XX p: set new P value");
Serial.println("XX.XX i: set new I value");
Serial.println("XX.XX d: set new D value");
Serial.println("list: show PID parameters");
Serial.println("store: save PID parameters to FLASH");
Serial.println("***********************************");
}
TCCR1A = 0;
//TCCR1B = 1<<WGM12 | 1<<CS11 | 1<<CS10; // enable CTC, prescale 64
TIMSK1 = 1<<OCIE1A; // enable compare interrupt
//OCR1A = 1250;
// 250: 1mS for max power
// 2250: 9mS for min power
// 25: 0.1ms for open triac
TCCR2A = 0; //TMR2 normal mode
TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; // prescale 1024, overflow each 1/(16000000/1024/256=61Hz) = 16.4mS
TIMSK2 = 1<<TOIE2; // enable overflow interrupt
TCNT2 = 0; // reset counter
EICRA = 1<<ISC11 | 1<<ISC01; // Falling edge generate interrupt
EIMSK = 1<<INT1 | 1<<INT0; // Enable interrupt
TCCR0B = 1; // default is 3 (prescaler 64)
analogReference(INTERNAL);
if ( relay_state_old != relay_state )
{
let_work = 0; //Serial.print("Let work: "); Serial.println(let_work);
delay(3200); //500*64
relay_state_old = relay_state;
digitalWrite(RELAY,relay_state); //Serial.print("Relay: "); Serial.println(relay_state);
delay(3200); //500*64
let_work = 1; //Serial.print("Let work: "); Serial.println(let_work);
}
if (reg_action)
{
reg_action = 0;
PV = 0;
for (byte i = 0; i < 4; i++)
{
PV = PV + PV_buf[i];
}
PV = PV >> 2;
if (instrument_connected) PV = map(PV,0,500,25,500); else PV = 0;
error = task - PV;
deltaPV = prevPV - PV;
prevPV = PV;
if ( (task_power>0) && (task_power<1000) ) integr = integr + (ki * error);
task_power = ((kp * error) + integr + (kd * deltaPV));
power = constrain(task_power, 0, 1000);
//Serial.print("Power: "); Serial.print(task_power); Serial.print(", "); Serial.println(power);
disp_PV_buf[disp_PV_buf_index] = PV;
disp_PV_buf_index++;
if (disp_PV_buf_index>4)
{
disp_PV_buf_index = 0;
disp_PV = 0;
for (byte i = 0; i < 4; i++)
{
disp_PV = disp_PV + disp_PV_buf[i];
}
disp_PV = disp_PV >> 2; //Serial.println(use_state);
}
stabilized = ((abs(PV - task)) < 5) && (use_state > 0);
}
switch (display_mode)
{
case 0: DisplayValue(disp_PV); break;
case 1: if (SV2_mode) DisplayValue(SV2); else DisplayValue(SV); break;
case 2: DisplayValue(set_fan_speed); break;
}
// use state recognize
use_value = analogRead(USE)>>2; // 0, 142, 255
if (use_value < 70) use_state_temp = 0;
else if (use_value < 190) use_state_temp = 1;
else use_state_temp = 2;
if ( use_state_temp_old == use_state_temp ) {if (use_state_stable_count < 255) use_state_stable_count++;}
else {use_state_stable_count = 0; use_state_temp_old = use_state_temp;}
if ( use_state_stable_count == 20 ) use_state = use_state_temp;
// button manage
if (digitalRead(BUTTON)) if ( (bt_timer>6) && (bt_timer<62) ) {bt_tap = 1; bt_hold = 0; bt_hold_long = 0;}// >100mS, <1000mS
if (bt_timer==62) {bt_tap = 0; bt_hold = 1; bt_hold_long = 0;}// = 1000mS
if (bt_timer==244) {bt_tap = 0; bt_hold = 1; bt_hold_long = 1;}// = 4000mS
if (!(digitalRead(BUTTON))) {if (bt_timer<32768) bt_timer++;} else bt_timer = 0;
+23 |
1915
52
|
+47 |
2506
89
|
+166 |
3974
48
|
+24 |
1985
26
|
А тут ещё и с дешифраиорами.
Дорого/нет — все относительно.
у меня дорога на работу — с работы в день обходится дороже
Но тратить индикаторы на фен такое себе, плюс если по герберам заказывать ПП, то она довольно большого размера выходит. На стм32 намного дешевле собрать по этому проекту и прошивка доступна с исходниками. Притом прошивка есть и для паяльника (старой версии v1.4 с таобао что ли + форк на современную стм32, которая у всех уже), так что поменять шрифт или еще что-то будет несложно. Плюс комплектуха и зачастую флешка-прошивальщик с ф101-103 тоже имеется в наличии.
А индикаторы отлично подойдут к часам или ЦАПу, а не к фену, который где-то подальше стоит.
Ардуину на атмеге знает «каждый студент политеха». Да и документации на русском — тоже полно, например, тот же Евстифеев. В STM чуть выше порог вхождения
Но сам фен того не заслуживает, ну лично моё мнение. Трехкнопочного «контроллера» 8858 ему за глаза.
К слову, китайским жалам Т12 по $2.7 STM32 тоже на фиг не сдался.
STC вполне достаточно.
сам по себе 8858 нормальный фен, недостаток в трехкнопочной системе управления. Не оперативная регулировка потока воздуха. Я свой переделывал, температура кнопками, а поток потенциометром.
Как покупатель готового на STC и «собиратель» на STM32, считаю лучшим STM32
О, успели поставить минус, приверженцы теплых ламповых технологий. Ну не люблю лампы, хоть и вырос на них.
100 градусов — дежурный режим, что бы быстрее выходить на заданную температуру.
10 минут, если забыть выключить тумблер.
Возможно, они кому-то и нравятся, но этот кто-то явно не был вынужден наблюдать их годами.
Учитывая, что сейчас IPS или OLED дисплей стоит дешевле лампового индикатора и по всем параметрам его превосходит, занятие лампами считаю тупиковым.
Реально круто вышло!
А на неонках — часы. Тем более, что неонки классические, с правильной цифрой «5».
Как говорит дядька Максим: «чётко! в натуре класс!»
Тип индикации — на любителя.
А так — все отлично получилось.
Уважаю людей с прямыми руками.
И переделывать пришлось, чтобы питался от одного напряжения.
Первая, что нашел поиском:
https://aliexpress.ru/item/item/32903214068.html,searchweb201602_,searchweb201603_
Или собрать самому ;-)
radiokot.ru/forum/viewtopic.php?f=25&t=8505&start=10420
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.