Commit f32efecc authored by Oleg Nikulin's avatar Oleg Nikulin

Улучшено раздельное управление вентиляторами. Исправлены проблемы с неправильной…

Улучшено раздельное управление вентиляторами. Исправлены проблемы с неправильной реакцией вентилятора на очень малые (< ~15) и большие (> ~250) значения скважности. Изменена передача 3-х значений: тепереь их нужно вводить через запятую
parent 6ee78744
......@@ -6,10 +6,11 @@ import argparse
from argparse import RawTextHelpFormatter
import datetime
import threading
import sys
POLL_SLEEP_TIME = 0.05 #Задержка между проверками наличия запроса от ардуины
RECONNECT_SLEEP_TIME = 1 #Задержка между попытками открыть serial порт при потере связи
TIMEOUT_TIME = 3 #Считается что ардуина перестала отвечать, если от нее не было запросов в течение этого времени
POLL_SLEEP_TIME = 0.05 #Задержка между проверками наличия запроса от ардуины (cек)
RECONNECT_SLEEP_TIME = 1 #Задержка между попытками открыть serial порт при потере связи (cек)
TIMEOUT_TIME = 3 #Считается что ардуина перестала отвечать, если от нее не было запросов в течение этого времени (cек)
SERIAL_SPEEDS = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 250000, 500000, 1000000, 2000000] #Тут только те, которые доступны в arduino IDE
PORT:str = None
......@@ -18,15 +19,46 @@ temp = '50'
connected = False
input_thread_running = False
def temp_input():
global temp
while True:
temp_input = input('Enter the temperature value: ')
try:
int(temp_input)
temp_input = input('\033[60C' + 'Enter up to 3 temperature values (comma-separated): ')
error = False
comma_pos = 0
find_start_index = 0
#парсим введенную строку:
for i in range(3):
comma_pos = temp_input.find(',', find_start_index) #поиск запятой
if comma_pos != -1 and i == 2: #если есть третья запятая
print("Can't be more than 3 values")
error = True
break
#выделяем число
if comma_pos == -1: #если запятой не найдено, то до конца строки
temp_substring = temp_input[find_start_index:]
else:
temp_substring = temp_input[find_start_index:comma_pos]
try: #проверка, введено ли число
int(temp_substring)
except ValueError:
print('Temperature value must be a number')
error = True
break
if comma_pos == -1: #если запятых больше нет, выходим из цикла
break
find_start_index = comma_pos + 1
if not error: #если всё хорошо, введенную строку можно отправлять
temp = temp_input
except ValueError:
print('Temperature value must be a number')
......@@ -93,7 +125,7 @@ if args.speed == None:
exit()
if args.manual:
input_thread = threading.Thread(target = temp_input)
temp = '50,51,52'
#ищем указанное serial устройство
if args.device in serial_devices:
......@@ -163,15 +195,17 @@ while True:
print('Error: Failed to get a temperature value from hddtemp output')
if 'send_rpm' in incoming_line:
send_rpm_pos = incoming_line.find('send_rpm')
rpm_str = incoming_line[send_rpm_pos + 9:]
if args.manual: print()
print('Fan RPMs: ' + rpm_str)
if args.manual: print('Enter the temperature value: ')
send_rpm_pos = incoming_line.find('send_rpm_')
rpm_string = incoming_line[send_rpm_pos + 9:]
rpm_string = rpm_string.replace('_', ' ')
rpm_string = rpm_string.replace('debug', 'debug:')
rpm_string = 'Fan RPMs: ' + rpm_string
#сохранение положения курсора, перервод в начало строки, перевод на 59 символов вправо, очистка всего перед курсором, курсор в начало строки, вывод оборотов, загрузка положения строки
print('\0337' + '\033[255D' +'\033[59C' + '\033[1K' + '\033[255D' + rpm_string + '\0338', end = '', flush=True)
except OSError:
if connected:
print('Serial divice was disconnected. Trying to reconnect...')
print('\nSerial divice was disconnected. Trying to reconnect...')
connected = False
try: #пытаемся снова открыть serial порт
......@@ -185,7 +219,7 @@ while True:
time.sleep(RECONNECT_SLEEP_TIME)
if connected and datetime.datetime.now().timestamp() - last_serial_input_time > TIMEOUT_TIME:
print('Serial device is not responding')
print('\nSerial device is not responding')
connected = False
#time.sleep(POLL_SLEEP_TIME)
\ No newline at end of file
time.sleep(POLL_SLEEP_TIME)
\ No newline at end of file
......@@ -7,6 +7,7 @@
#define RPM_CHECK_INTERVAL 200 //Интервал подсчета rpm и обнуления tachRevs (мс)
#define RPM_VALUES_COUNT 5 //Количество значений RPM, которые записываются и усредняются (скользящее среднее)
#define MIN_PWM_DUTY_CYCLE 8 //Минимальная скважность ШИМ
#define MAX_PWM_DUTY_CYCLE 254 //Максимальная скважность ШИМ
#define DEFAULT_PWM_DUTY_CYCLE 254 //Скважность ШИМ по умолчанию (до подулючения к пк)
......@@ -19,7 +20,8 @@
int pwmPins[3] = {3, 5, 6}; //Номера пинов ШИМ
volatile bool pwmState = 0; //Состояние ШИМ
volatile bool pwm_ons[3] = {1, 1, 1}; // ==1, если ШИМ включен
volatile uint8_t pwm_modes[3] = {1, 1, 1}; // 0 - всегда выкл, 1 - шим, 2 - всегда вкл
int tachPins[FAN_COUNT] = {14, 15, 16, 17, 18, 19}; //Номера пинов для считывания оборотов
volatile uint16_t tachRevs[FAN_COUNT] = {}; //Обороты (просто сколько насчитано оборотов, а не rpm)
......@@ -44,10 +46,9 @@ volatile bool tachStates[FAN_COUNT] = {}; //Состояния (оборот с
};
*/
int temp; //Температура, полученная от пк
int target_rpm = 0;
int temps[3] = {}; //Температуры, полученные от пк
int target_rpms[3] = {};
int dutyCycles[3] = {DEFAULT_PWM_DUTY_CYCLE, DEFAULT_PWM_DUTY_CYCLE, DEFAULT_PWM_DUTY_CYCLE};
uint32_t last_beep_time = 0;
uint32_t last_check_time = 0;
......@@ -116,7 +117,7 @@ void tempFromPc()// получаем температуру от пк
}
}
Serial.println("req_temperature_" + rpm_string + "_debug_" + String(OCR2A) + "_" + String(OCR2B) + "_" + String(OCR1A)); //запрос к пк
Serial.println("req_temperature_" + rpm_string + "_debug_" + String(OCR1A) + "_" + String(OCR2A) + "_" + String(OCR2B)); //запрос к пк
unsigned long query_time = millis();
while (Serial.available() == 0) { //ждем пока ответит
if (millis() >= query_time + GET_TEMP_TIMEOUT) { //если слишком долго не отвечает
......@@ -129,10 +130,51 @@ void tempFromPc()// получаем температуру от пк
}
}
String response = Serial.readStringUntil('\n'); //выделяем строку до \n
response = response.substring(12); //отбрасываем temperature_ в начале
dutyCycles[0] = response.substring(0, 3).toInt();
dutyCycles[1] = response.substring(3, 6).toInt();
dutyCycles[2] = response.substring(6, 9).toInt();
int temperature_index = response.indexOf("temperature_");
if (temperature_index != -1) { //если в строке есть temperature_
String temperatures = response.substring(temperature_index + 12); //отбрасываем temperature_
int comma_pos = 0;
int find_start_index = 0;
for (int i = 0; i < 3; i++) { //парсим строку с температурами
String temp_substring = "";
comma_pos = temperatures.indexOf(",", find_start_index);
if (comma_pos == -1) { //если запятой не найдено, то до конца строки
temp_substring = temperatures.substring(find_start_index);
}
else {
temp_substring = temperatures.substring(find_start_index, comma_pos);
}
//проверка, является ли temp_substring числом:
if (temp_substring.length() == 0) {
break;
}
bool isInt = 1;
for (int i = 0; i < temp_substring.length(); i++) {
if (isDigit(temp_substring.charAt(i)) == false && (temp_substring.charAt(i) != '-' || (temp_substring.charAt(i) == '-' && i > 0))) {
isInt = 0;
break;
}
}
if (!isInt) {
break;
}
temps[i] = temp_substring.toInt();
if (comma_pos == -1) { //если запятых больше нет, выходим из цикла
break;
}
find_start_index = comma_pos + 1;
}
}
}
......@@ -142,7 +184,10 @@ void autocontrol()//Автоконтроль температуры
{
tempFromPc();//получаем температуру от пк
target_rpm = temp;
// for(int i = 0; i < 3; i++){
// target_rpms[i] = temps[i];
// }
}
......@@ -202,33 +247,33 @@ void rpm_control() {
}
OCR2B = byte_pwmDutyCycle; //установка скважности
}
//Почему-то иногда может заглючить: скважность стоит 254, pwm_on равно 1, но вентилятор не крутится (но пищит). Похоже каким-то образом меняется pwmState, когда не надо
//Serial.println("debug_" + String(byte_pwmDutyCycle));
*/
lastRpmCheck = millis();
for (int i = 0; i < 3; i++) {
if (uint8_t(dutyCycles[i]) == 0) {
if (pwm_ons[i]) {
pwm_ons[i] = 0;
}
if (temps[i] < MIN_PWM_DUTY_CYCLE) {
pwm_modes[i] = 0; //всегда выкл
}
else if (temps[i] > MAX_PWM_DUTY_CYCLE) {
pwm_modes[i] = 2; //всегда вкл
}
else {
if (!pwm_ons[i]) {
pwm_ons[i] = 1;
}
switch (i) {
case 0:
OCR2A = dutyCycles[i];
break;
case 1:
OCR2B = dutyCycles[i];
break;
case 2:
OCR1A = dutyCycles[i];
break;
}
pwm_modes[i] = 1; //шим
}
uint8_t duty_cycle = constrain(temps[i], 0, 255);
switch (i) {
case 0:
OCR1A = duty_cycle;
break;
case 1:
OCR2A = duty_cycle;
break;
case 2:
OCR2B = duty_cycle;
break;
}
}
}
......@@ -238,46 +283,74 @@ void rpm_control() {
ISR(TIMER1_COMPA_vect) { //Функция, вызываемая при прерывании 1A
digitalWrite(6, 0);
}
// digitalWrite(3, 0);
if (pwm_modes[0] < 2) { //если не всегда вкл
PORTD &= 0b1110111; //выкл питание на пине 3
}
}
ISR(TIMER2_COMPA_vect) { //Функция, вызываемая при прерывании 2A
digitalWrite(3, 0);
// digitalWrite(5, 0);
if (pwm_modes[0] < 2) { //если не всегда вкл
PORTD &= 0b11011111; //выкл питание на пине 5
}
}
ISR(TIMER2_COMPB_vect) { //Функция, вызываемая при прерывании 2B
digitalWrite(5, 0);
// digitalWrite(6, 0);
if (pwm_modes[0] < 2) { //если не всегда вкл
PORTD &= 0b10111111; //выкл питание на пине 6
}
}
ISR(TIMER2_OVF_vect) { //Функция, вызываемая при переполнении таймера 2
for (int i = 0; i < 3; i++) { //Вкл. питание
if (pwm_ons[i]) {
digitalWrite(pwmPins[i], 1);
}
ISR(TIMER1_OVF_vect) { //Функция, вызываемая при переполнении таймера 1
int i = 0;
if (pwm_modes[i] > 0) { //если режим шим или всегда вкл
digitalWrite(pwmPins[i], 1);
}
for (int i = 0; i < 2; i++) { //проверка тахометров 0-1
if (digitalRead(tachPins[i]) == 0 && tachStates[i] == 0) { //сигнал тахометра появился
tachStates[i] = 1;
//digitalWrite(13, 1);
}
else if (digitalRead(tachPins[i]) == 1 && tachStates[i] == 1) { //сигнал тахометра пропал
//digitalWrite(13, 0);
tachStates[i] = 0;
tachRevs[i]++; //засчитывается оборот
}
}
}
//считывание оборотов:
for (int i = 0; i < FAN_COUNT; i++) {
ISR(TIMER2_OVF_vect) { //Функция, вызываемая при переполнении таймера 2
if (pwm_modes[1] > 0) { //если режим шим или всегда вкл
PORTD |= 0b00100000; //вкл питание на пине 5
}
if (pwm_modes[2] > 0) { //если режим шим или всегда вкл
PORTD |= 0b01000000; //вкл питание на пине 6
}
for (int i = 2; i < 6; i++) { //проверка тахометров 2-5
if (digitalRead(tachPins[i]) == 0 && tachStates[i] == 0) { //сигнал тахометра появился
tachStates[i] = 1;
digitalWrite(13, 1);
//digitalWrite(13, 1);
}
else if (digitalRead(tachPins[i]) == 1 && tachStates[i] == 1) { //сигнал тахометра пропал
digitalWrite(13, 0);
//digitalWrite(13, 0);
tachStates[i] = 0;
tachRevs[i]++; //засчитывается оборот
}
}
}
bool getbitstate(int num, uint8_t bit_pos) {
bool bits_array[8];
for (int i = 0; i < 8; i++) {
......@@ -300,11 +373,14 @@ void setup() {
pinMode(tachPins[i], INPUT_PULLUP);
}
//таймер 2 сравнение A - пин 3
//таймер 2 сравнение B - пин 5
//таймер 1 сравнение A - пин 6
//таймер 1 сравнение A - пин 3
//таймер 2 сравнение A - пин 5
//таймер 2 сравнение B - пин 6
//Пины 5 и 6: скважность от 7 до 254. Если меньше 2, то немного штырит.
//Пины 5 и 6: скважность от 8 до 255.
//режим таймера 1 (fast pwm):
//режим таймера 1 (fast pwm 8 бит):
TCCR1B |= (1 << WGM12);
TCCR1A |= (1 << WGM10);
......@@ -313,14 +389,16 @@ void setup() {
TCCR2A |= (1 << WGM20);
TIMSK1 |= (1 << OCIE1A); //вкл. вызов прерывания таймера 1 по сравнению A
TIMSK1 |= (1 << TOIE1); //вкл. вызов прерывания при переполнении таймера 1
TIMSK2 |= (1 << OCIE2A); //вкл. вызов прерывания таймера 2 по сравнению A
TIMSK2 |= (1 << OCIE2B); //вкл. вызов прерывания таймера 2 по сравнению B
TIMSK2 |= (1 << TOIE2); //вкл. вызов прерывания при переполнении таймера 2
OCR1A = DEFAULT_PWM_DUTY_CYCLE; //Значение для сравнения 1A (скважность ШИМ)
OCR2A = DEFAULT_PWM_DUTY_CYCLE; //Значение для сравнения 2A (скважность ШИМ)
OCR2B = DEFAULT_PWM_DUTY_CYCLE; //Значение для сравнения 2B (скважность ШИМ)
OCR1A = DEFAULT_PWM_DUTY_CYCLE; //Значение для сравнения 1A (скважность ШИМ пина 3)
OCR2A = DEFAULT_PWM_DUTY_CYCLE; //Значение для сравнения 2A (скважность ШИМ пина 5)
OCR2B = DEFAULT_PWM_DUTY_CYCLE; //Значение для сравнения 2B (скважность ШИМ пина 6)
Serial.begin(BAUDRATE);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment