Commit e254348a authored by Oleg Nikulin's avatar Oleg Nikulin

3 прошивки: для указания скважности шим, для поддержания указаных оборотовб и…

3 прошивки: для указания скважности шим, для поддержания указаных оборотовб и для поддержания температуры. Небольшие изменения в скрипте
parent 750c1fd4
#epmi pip
#pip install pyserial
import subprocess
import time
import serial
import os
import argparse
from argparse import RawTextHelpFormatter
import datetime
import threading
import sys
POLL_SLEEP_TIME = 0.05 #Задержка между проверками наличия запроса от ардуины (cек)
POLL_SLEEP_TIME = 0.1 #Задержка между проверками наличия запроса от ардуины (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
SPEED:int = None
......@@ -67,11 +67,6 @@ parser = argparse.ArgumentParser(
formatter_class=RawTextHelpFormatter
)
parser.add_argument('-l', '--list',
required = False,
action='store_true',
help = 'show a list of available serial devices',
)
parser.add_argument('-m', '--manual',
required = False,
action='store_true',
......@@ -81,41 +76,19 @@ parser.add_argument('-m', '--manual',
parser.add_argument('device',
type = str,
nargs = '?',
help = 'serial device to commuicate with. Example: ttyUSB0',
help = 'serial device to commuicate with. Example: /dev/ttyUSB0',
metavar = 'SERIAL'
)
parser.add_argument('speed',
type = int,
nargs = '?',
help = 'serial port baud rate. Supported rates: ' + str(SERIAL_SPEEDS)[1:-1],
help = 'serial port baud rate',
metavar = 'SPEED'
)
args = parser.parse_args()
#получаем список serial устройств
dev_contents = os.listdir('/dev/')
serial_devices:str = []
for file in dev_contents:
if file.startswith('ttyUSB'):
serial_devices.append(file)
if args.list != False: #вывод списка serial устройств
if len(serial_devices) == 0:
print('No serial devices found')
else:
print('Available serial devices:')
for device in serial_devices:
print(device)
exit()
#проверка наличия аргументов
if args.device == None:
print('Serial device was not specified')
......@@ -127,25 +100,12 @@ if args.manual:
input_thread = threading.Thread(target = temp_input)
temp = '50,51,52'
#ищем указанное serial устройство
if args.device in serial_devices:
PORT = args.device
else:
print('Serial device "{dev}" was not found. Use --list option to see a list of available serial devices'.format(dev = args.device))
exit()
#ищем указанную скорость
if args.speed in SERIAL_SPEEDS:
SPEED = args.speed
else:
print('Specified baud rate is unsupported. Use -h option to see a list of supported rates')
exit()
PORT = args.device
SPEED = args.speed
try: #пытаемся открыть serial порт
serial_port = serial.Serial(
port = '/dev/' + PORT,
port = PORT,
baudrate = SPEED,
parity = serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
......@@ -180,7 +140,7 @@ while True:
output = str(subprocess.Popen(['netcat', 'localhost', '7634'], stdout = subprocess.PIPE, stderr = subprocess.STDOUT).communicate()[0]) #получаем output от hddtemp
#b'|/dev/sda2|ST340014A|41|C|'
if output == "b''":
print('Hddtemp output is empty, make sure it is running. If not, launch it with $sudo hddtemp -d')
print('Hddtemp output is empty, make sure it is running. If not, launch it with #hddtemp -d')
exit()
#вычленяем значение температуры:
......@@ -198,7 +158,6 @@ while True:
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)
......
#include <Wire.h>
#define FAN_COUNT 6 //Количество вентиляторов
#define CONTROL_COUNT 3 //Количество групп управления
#define BAUDRATE 19200 //Скорость serial порта
#define CHECK_TEMP_DELAY 200 //Интервал получения температуры от пк (мс)
#define GET_TEMP_TIMEOUT 1000 //Если в течение этого времени температура не была прислана, считается что пк не отвечает (мс)
#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 //Скважность ШИМ по умолчанию (до подулючения к пк)
#define BEEP_INTERVAL 1000 //Интервал пищания во время потери связи или перегрева (мс)
#define BEEP_STRENGTH 100 //Уровень ШИМ для пищалки. (При 255 не работает, нужно чтобы был именно ШИМ)
#define BLINK_INTERVAL 200 //Интервал мигания светодиода во время ожидания ответа от пк (мс)
#define SOUND_PIN 10 //Номер пина пищалки
#define LED_PIN 13 //Номер пина светодиода
#define OVERHEAT_TEMP 80 //Температура, при достижении которой включается пищалка
uint8_t *control_a_addr = 0x88; //адрес сравнения А таймера 1
uint8_t *control_b_addr = 0xB3; //адрес сравнения А таймера 2
uint8_t *control_c_addr = 0xB4; //адрес сравнения B таймера 2
struct control {//Данные о группе управления
uint8_t pin; //Номер пина
volatile uint8_t mode; //Текущий режим работы. 0 - всегда выкл, 1 - шим, 2 - всегда вкл
uint8_t &dutyCycle; //Скважность ШИМ
int8_t copy_from; //Копировать скважность из другой группы управления. -1 = не копировать
};
control controls[CONTROL_COUNT] = {
{
3,
1,
*control_a_addr,
-1
},
{
5,
1,
*control_b_addr,
0
},
{
6,
1,
*control_c_addr,
0
}
};
control &control_a = controls[0];
control &control_b = controls[1];
control &control_c = controls[2];
struct fan { //Данные о вентиляторе
int pin; //Номер пина тахометра
bool type; //Тип датчика. 0 = униполярный, 1 = биполярный
volatile bool state; //Состояние тахометра
volatile uint16_t revs; //Количество оборотов (просто, не в минуту)
uint16_t rpm[RPM_VALUES_COUNT]; //Обороты в минуту
};
fan fans[FAN_COUNT] = {
{
14,
1,
0,
0,
0
},
{
15,
1,
0,
0,
0
},
{
16,
1,
0,
0,
0
},
{
17,
1,
0,
0,
0
},
{
18,
1,
0,
0,
0
},
{
19,
1,
0,
0,
0
}
};
fan &fan_1 = fans[0];
fan &fan_2 = fans[1];
fan &fan_3 = fans[2];
fan &fan_4 = fans[3];
fan &fan_5 = fans[4];
fan &fan_6 = fans[5];
int temps[3] = {}; //Температуры, полученные от пк
uint32_t last_beep_time = 0;
uint32_t last_check_time = 0;
uint32_t lastRpmCheck = 0;
float p[3] = {};
float i[3] = {};
float d[3] = {};
float k_p = 40;
float k_i = 0.005;
float k_d = 0;
float i_max = 210;
int target_temps[3] = {27, 0, 0};
void wait_for_connection(bool beep) { //функция, которая мигает светодиодом и пищит (если beep == true) до тех пор, пока не будет установлено соединение с ПК
Serial.println("start_transmission");
digitalWrite(LED_PIN, 1);
unsigned long last_blink_time = millis();
while (Serial.available() == 0) {//ждем ответа
if (millis() >= last_blink_time + BLINK_INTERVAL * 2) {
digitalWrite(LED_PIN, 1);
last_blink_time = millis();
}
else if (millis() >= last_blink_time + BLINK_INTERVAL) {
digitalWrite(LED_PIN, 0);
}
if (beep) {
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
last_beep_time = millis();
}
else if (millis() >= last_beep_time + BEEP_INTERVAL) {
analogWrite(SOUND_PIN, 0);
}
}
}
digitalWrite(LED_PIN, 0); //гасим светодиод
analogWrite(SOUND_PIN, 0); //и выключаем пищалку
while (Serial.available()) {// очистка буфера
Serial.readString();
}
}
void tempFromPc()// получаем температуру от пк
{
String rpm_string = "send_rpm_";
for (int fan = 0; fan < FAN_COUNT; fan++) {
uint32_t avg_rpm = 0;
for (int val = 0; val < RPM_VALUES_COUNT; val++) {
avg_rpm += fans[fan].rpm[val];
}
avg_rpm /= RPM_VALUES_COUNT;
rpm_string += String(avg_rpm);
if (fan < 5) {
rpm_string += "_";
}
}
Serial.println("req_temperature_" + rpm_string + "_debug(pwm values):_" + String(OCR1A) + "_" + String(OCR2A) + "_" + String(OCR2B)); //запрос к пк
unsigned long query_time = millis();
while (Serial.available() == 0) { //ждем пока ответит
if (millis() >= query_time + GET_TEMP_TIMEOUT) { //если слишком долго не отвечает
//Венитялторы на максимум
for (int i = 0; i < CONTROL_COUNT; i++) {
controls[i].dutyCycle = DEFAULT_PWM_DUTY_CYCLE; //установка скважности по умолчанию
}
wait_for_connection(true);//снова ждем подключения
break;
}
}
String response = Serial.readStringUntil('\n'); //выделяем строку до \n
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;
}
}
}
void rpm_control() {
for (int fan = 0; fan < FAN_COUNT; fan++) { //подсчет об/мин, обнуление насчитанных оборотов
for (int val = 0; val < RPM_VALUES_COUNT - 1; val++) {
fans[fan].rpm[val] = fans[fan].rpm[val + 1];
}
fans[fan].rpm[RPM_VALUES_COUNT - 1] = (fans[fan].revs * (60000 / uint32_t(millis() - lastRpmCheck))) / (1 + fans[fan].type);
fans[fan].revs = 0;
}
for (int group = 0; group < 3; group++) {//управление скоростью 3-х групп вентиляторов
if (controls[group].copy_from == -1) {
uint8_t duty_cycle = 0;
d[group] = (temps[group] - target_temps[group] - p[group]) / float(millis() - lastRpmCheck);
p[group] = temps[group] - target_temps[group];
i[group] = i[group] + p[group] * (millis() - lastRpmCheck);
if (fabs(i[group] * k_i) >= i_max) {
i[group] = i_max / k_i;
}
float pwmDutyCycle = round(p[group] * k_p + i[group] * k_i + d[group] * k_d);
if (pwmDutyCycle < 0) {
duty_cycle = 0;
} else if (pwmDutyCycle > 255) {
duty_cycle = 255;
}
else {
duty_cycle = pwmDutyCycle;
}
if (duty_cycle < MIN_PWM_DUTY_CYCLE) {
controls[group].mode = 0; //всегда выкл
digitalWrite(controls[group].pin, 0);
}
else if (duty_cycle > MAX_PWM_DUTY_CYCLE) {
controls[group].mode = 2; //всегда вкл
digitalWrite(controls[group].pin, 1);
}
else {
controls[group].mode = 1; //шим
}
controls[group].dutyCycle = duty_cycle;
}
else {
controls[group].mode = controls[controls[group].copy_from].mode;
controls[group].dutyCycle = controls[controls[group].copy_from].dutyCycle;
}
}
lastRpmCheck = millis();
}
ISR(TIMER1_COMPA_vect) { //Функция, вызываемая при прерывании 1A
if (control_a.mode < 2) { //если не всегда вкл
PORTD &= 0b1110111; //выкл питание на пине 3
}
}
ISR(TIMER2_COMPA_vect) { //Функция, вызываемая при прерывании 2A
if (control_b.mode < 2) { //если не всегда вкл
PORTD &= 0b11011111; //выкл питание на пине 5
}
}
ISR(TIMER2_COMPB_vect) { //Функция, вызываемая при прерывании 2B
if (control_c.mode < 2) { //если не всегда вкл
PORTD &= 0b10111111; //выкл питание на пине 6
}
}
ISR(TIMER1_OVF_vect) { //Функция, вызываемая при переполнении таймера 1
if (control_a.mode > 0) { //если режим шим или всегда вкл
digitalWrite(control_a.pin, 1);
}
//delayMicroseconds(15);
for (int i = 0; i < 2; i++) { //проверка тахометров 0-1
if (digitalRead(fans[i].pin) == 0 && fans[i].state == 0) { //сигнал тахометра появился
fans[i].state = 1;
//digitalWrite(13, 1);
}
else if (digitalRead(fans[i].pin) == 1 && fans[i].state == 1) { //сигнал тахометра пропал
//digitalWrite(13, 0);
fans[i].state = 0;
fans[i].revs++; //засчитывается оборот
}
}
}
ISR(TIMER2_OVF_vect) { //Функция, вызываемая при переполнении таймера 2
if (control_b.mode > 0) { //если режим шим или всегда вкл
PORTD |= 0b00100000; //вкл питание на пине 5
}
if (control_c.mode > 0) { //если режим шим или всегда вкл
PORTD |= 0b01000000; //вкл питание на пине 6
}
//delayMicroseconds(10);
for (int i = 2; i < 6; i++) { //проверка тахометров 2-5
if (digitalRead(fans[i].pin) == 0 && fans[i].state == 0) { //сигнал тахометра появился
fans[i].state = 1;
//digitalWrite(13, 1);
}
else if (digitalRead(fans[i].pin) == 1 && fans[i].state == 1) { //сигнал тахометра пропал
//digitalWrite(13, 0);
fans[i].state = 0;
fans[i].revs++; //засчитывается оборот
}
}
}
bool getbitstate(int num, uint8_t bit_pos) {
bool bits_array[8];
for (int i = 0; i < 8; i++) {
bits_array[i] = num >> (8 - 1 - i) & 1;
}
return bits_array[bit_pos];
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(SOUND_PIN, OUTPUT);
for (int i = 0; i < CONTROL_COUNT; i++) {
pinMode(controls[i].pin, OUTPUT);
}
for (int i = 0; i < FAN_COUNT; i++) {
pinMode(fans[i].pin, INPUT_PULLUP);
}
//таймер 1 сравнение A - пин 3
//таймер 2 сравнение A - пин 5
//таймер 2 сравнение B - пин 6
//Пины 5 и 6: скважность от 8 до 255.
//режим таймера 1 (fast pwm 8 бит):
TCCR1B |= (1 << WGM12);
TCCR1A |= (1 << WGM10);
//режим таймера 2 (fast pwm):
TCCR2A |= (1 << WGM21);
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
for (int i = 0; i < CONTROL_COUNT; i++) {
controls[i].dutyCycle = DEFAULT_PWM_DUTY_CYCLE; //установка скважности по умолчанию
}
Serial.begin(BAUDRATE);
wait_for_connection(false); //Ждем установленя связи с ПК
}
void loop() {
if (uint32_t(millis() - last_check_time) >= CHECK_TEMP_DELAY) {
last_check_time = millis();
tempFromPc();//получаем температуру от пк
if (temps[0] >= OVERHEAT_TEMP || temps[1] >= OVERHEAT_TEMP || temps[2] >= OVERHEAT_TEMP ) { //Если перегрев, включается пищалка
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
last_beep_time = millis();
}
else if (millis() >= last_beep_time + BEEP_INTERVAL) {
analogWrite(SOUND_PIN, 0);
}
}
else {
analogWrite(SOUND_PIN, 0); //Чтобы постоянно не пищал, если температура станет < 80 в то время, как пищалка включена
}
}
if (uint32_t(millis() - lastRpmCheck) >= RPM_CHECK_INTERVAL) {
rpm_control();
}
}
#include <Wire.h>
#define FAN_COUNT 6 //Количество вентиляторов
#define CONTROL_COUNT 3 //Количество групп управления
#define BAUDRATE 19200 //Скорость serial порта
#define CHECK_TEMP_DELAY 200 //Интервал получения температуры от пк (мс)
#define GET_TEMP_TIMEOUT 1000 //Если в течение этого времени температура не была прислана, считается что пк не отвечает (мс)
#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 //Скважность ШИМ по умолчанию (до подулючения к пк)
#define BEEP_INTERVAL 1000 //Интервал пищания во время потери связи или перегрева (мс)
#define BEEP_STRENGTH 100 //Уровень ШИМ для пищалки. (При 255 не работает, нужно чтобы был именно ШИМ)
#define BLINK_INTERVAL 200 //Интервал мигания светодиода во время ожидания ответа от пк (мс)
#define SOUND_PIN 10 //Номер пина пищалки
#define LED_PIN 13 //Номер пина светодиода
uint8_t *control_a_addr = 0x88; //адрес сравнения А таймера 1
uint8_t *control_b_addr = 0xB3; //адрес сравнения А таймера 2
uint8_t *control_c_addr = 0xB4; //адрес сравнения B таймера 2
struct control {//Данные о группе управления
uint8_t pin; //Номер пина
volatile uint8_t mode; //Текущий режим работы. 0 - всегда выкл, 1 - шим, 2 - всегда вкл
uint8_t &dutyCycle; //Скважность ШИМ
};
control controls[CONTROL_COUNT] = {
{
3,
1,
*control_a_addr
},
{
5,
1,
*control_b_addr
},
{
6,
1,
*control_c_addr
}
};
control &control_a = controls[0];
control &control_b = controls[1];
control &control_c = controls[2];
struct fan { //Данные о вентиляторе
int pin; //Номер пина тахометра
bool type; //Тип датчика. 0 = униполярный, 1 = биполярный
volatile bool state; //Состояние тахометра
volatile uint16_t revs; //Количество оборотов (просто, не в минуту)
uint16_t rpm[RPM_VALUES_COUNT]; //Обороты в минуту
};
fan fans[FAN_COUNT] = {
{
14,
1,
0,
0,
0
},
{
15,
1,
0,
0,
0
},
{
16,
1,
0,
0,
0
},
{
17,
1,
0,
0,
0
},
{
18,
1,
0,
0,
0
},
{
19,
1,
0,
0,
0
}
};
fan &fan_1 = fans[0];
fan &fan_2 = fans[1];
fan &fan_3 = fans[2];
fan &fan_4 = fans[3];
fan &fan_5 = fans[4];
fan &fan_6 = fans[5];
int temps[3] = {}; //Температуры, полученные от пк
uint32_t last_beep_time = 0;
uint32_t last_check_time = 0;
uint32_t lastRpmCheck = 0;
void wait_for_connection(bool beep) { //функция, которая мигает светодиодом и пищит (если beep == true) до тех пор, пока не будет установлено соединение с ПК
Serial.println("start_transmission");
digitalWrite(LED_PIN, 1);
unsigned long last_blink_time = millis();
while (Serial.available() == 0) {//ждем ответа
if (millis() >= last_blink_time + BLINK_INTERVAL * 2) {
digitalWrite(LED_PIN, 1);
last_blink_time = millis();
}
else if (millis() >= last_blink_time + BLINK_INTERVAL) {
digitalWrite(LED_PIN, 0);
}
if (beep) {
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
last_beep_time = millis();
}
else if (millis() >= last_beep_time + BEEP_INTERVAL) {
analogWrite(SOUND_PIN, 0);
}
}
}
digitalWrite(LED_PIN, 0); //гасим светодиод
analogWrite(SOUND_PIN, 0); //и выключаем пищалку
while (Serial.available()) {// очистка буфера
Serial.readString();
}
}
void tempFromPc()// получаем температуру от пк
{
String rpm_string = "send_rpm_";
for (int fan = 0; fan < FAN_COUNT; fan++) {
uint32_t avg_rpm = 0;
for (int val = 0; val < RPM_VALUES_COUNT; val++) {
avg_rpm += fans[fan].rpm[val];
}
avg_rpm /= RPM_VALUES_COUNT;
rpm_string += String(avg_rpm);
if (fan < 5) {
rpm_string += "_";
}
}
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) { //если слишком долго не отвечает
//Венитялторы на максимум
for (int i = 0; i < CONTROL_COUNT; i++) {
controls[i].dutyCycle = DEFAULT_PWM_DUTY_CYCLE; //установка скважности по умолчанию
}
wait_for_connection(true);//снова ждем подключения
break;
}
}
String response = Serial.readStringUntil('\n'); //выделяем строку до \n
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;
}
}
}
void rpm_control() {
for (int fan = 0; fan < FAN_COUNT; fan++) { //подсчет об/мин, обнуление насчитанных оборотов
for (int val = 0; val < RPM_VALUES_COUNT - 1; val++) {
fans[fan].rpm[val] = fans[fan].rpm[val + 1];
}
fans[fan].rpm[RPM_VALUES_COUNT - 1] = (fans[fan].revs * (60000 / uint32_t(millis() - lastRpmCheck))) / (1 + fans[fan].type);
fans[fan].revs = 0;
}
for (int group = 0; group < 3; group++) {//управление скоростью 3-х групп вентиляторов
uint8_t duty_cycle = temps[group];
if (duty_cycle < MIN_PWM_DUTY_CYCLE) {
controls[group].mode = 0; //всегда выкл
digitalWrite(controls[group].pin, 0);
}
else if (duty_cycle > MAX_PWM_DUTY_CYCLE) {
controls[group].mode = 2; //всегда вкл
digitalWrite(controls[group].pin, 1);
}
else {
controls[group].mode = 1; //шим
}
controls[group].dutyCycle = duty_cycle;
}
lastRpmCheck = millis();
}
ISR(TIMER1_COMPA_vect) { //Функция, вызываемая при прерывании 1A
if (control_a.mode < 2) { //если не всегда вкл
PORTD &= 0b1110111; //выкл питание на пине 3
}
}
ISR(TIMER2_COMPA_vect) { //Функция, вызываемая при прерывании 2A
if (control_b.mode < 2) { //если не всегда вкл
PORTD &= 0b11011111; //выкл питание на пине 5
}
}
ISR(TIMER2_COMPB_vect) { //Функция, вызываемая при прерывании 2B
if (control_c.mode < 2) { //если не всегда вкл
PORTD &= 0b10111111; //выкл питание на пине 6
}
}
ISR(TIMER1_OVF_vect) { //Функция, вызываемая при переполнении таймера 1
if (control_a.mode > 0) { //если режим шим или всегда вкл
digitalWrite(control_a.pin, 1);
}
//delayMicroseconds(15);
for (int i = 0; i < 2; i++) { //проверка тахометров 0-1
if (digitalRead(fans[i].pin) == 0 && fans[i].state == 0) { //сигнал тахометра появился
fans[i].state = 1;
//digitalWrite(13, 1);
}
else if (digitalRead(fans[i].pin) == 1 && fans[i].state == 1) { //сигнал тахометра пропал
//digitalWrite(13, 0);
fans[i].state = 0;
fans[i].revs++; //засчитывается оборот
}
}
}
ISR(TIMER2_OVF_vect) { //Функция, вызываемая при переполнении таймера 2
if (control_b.mode > 0) { //если режим шим или всегда вкл
PORTD |= 0b00100000; //вкл питание на пине 5
}
if (control_c.mode > 0) { //если режим шим или всегда вкл
PORTD |= 0b01000000; //вкл питание на пине 6
}
//delayMicroseconds(10);
for (int i = 2; i < 6; i++) { //проверка тахометров 2-5
if (digitalRead(fans[i].pin) == 0 && fans[i].state == 0) { //сигнал тахометра появился
fans[i].state = 1;
//digitalWrite(13, 1);
}
else if (digitalRead(fans[i].pin) == 1 && fans[i].state == 1) { //сигнал тахометра пропал
//digitalWrite(13, 0);
fans[i].state = 0;
fans[i].revs++; //засчитывается оборот
}
}
}
bool getbitstate(int num, uint8_t bit_pos) {
bool bits_array[8];
for (int i = 0; i < 8; i++) {
bits_array[i] = num >> (8 - 1 - i) & 1;
}
return bits_array[bit_pos];
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(SOUND_PIN, OUTPUT);
for (int i = 0; i < CONTROL_COUNT; i++) {
pinMode(controls[i].pin, OUTPUT);
}
for (int i = 0; i < FAN_COUNT; i++) {
pinMode(fans[i].pin, INPUT_PULLUP);
}
//таймер 1 сравнение A - пин 3
//таймер 2 сравнение A - пин 5
//таймер 2 сравнение B - пин 6
//Пины 5 и 6: скважность от 8 до 255.
//режим таймера 1 (fast pwm 8 бит):
TCCR1B |= (1 << WGM12);
TCCR1A |= (1 << WGM10);
//режим таймера 2 (fast pwm):
TCCR2A |= (1 << WGM21);
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
for (int i = 0; i < CONTROL_COUNT; i++) {
controls[i].dutyCycle = DEFAULT_PWM_DUTY_CYCLE; //установка скважности по умолчанию
}
Serial.begin(BAUDRATE);
wait_for_connection(false); //Ждем установленя связи с ПК
}
void loop() {
if (uint32_t(millis() - last_check_time) >= CHECK_TEMP_DELAY) {
last_check_time = millis();
tempFromPc();//получаем температуру от пк
// if (temp >= 80) { //Если перегрев, включается пищалка
// if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
// analogWrite(SOUND_PIN, BEEP_STRENGTH);
// last_beep_time = millis();
// }
// else if (millis() >= last_beep_time + BEEP_INTERVAL) {
// analogWrite(SOUND_PIN, 0);
// }
// }
// else {
// analogWrite(SOUND_PIN, 0); //Чтобы постоянно не пищал, если температура станет < 80 в то время, как пищалка включена
// }
}
if (uint32_t(millis() - lastRpmCheck) >= RPM_CHECK_INTERVAL) {
rpm_control();
}
}
......@@ -99,7 +99,7 @@ fan fans[FAN_COUNT] = {
},
{
19,
2,
1,
0,
0,
0
......@@ -115,7 +115,6 @@ fan &fan_6 = fans[5];
int temps[3] = {}; //Температуры, полученные от пк
int target_rpms[3] = {}; //Обороты, которые нужно держать (в этой прошивке target_rpms и temps это одно и то же)
uint32_t last_beep_time = 0;
uint32_t last_check_time = 0;
......@@ -125,14 +124,12 @@ float p[3] = {};
float i[3] = {};
float d[3] = {};
float k_p = 0.03;
float k_i = 0.0002;
float k_d = 20;
float k_p = 0.04;
float k_i = 0.0001;
float k_d = 3;
float i_max = 255;
bool flag = 0;
void wait_for_connection(bool beep) { //функция, которая мигает светодиодом и пищит (если beep == true) до тех пор, пока не будет установлено соединение с ПК
Serial.println("start_transmission");
digitalWrite(LED_PIN, 1);
......@@ -184,13 +181,13 @@ void tempFromPc()// получаем температуру от пк
}
}
Serial.println("req_temperature_" + rpm_string + "_debug_" + String(OCR1A) + "_" + String(OCR2A) + "_" + String(OCR2B)); //запрос к пк
Serial.println("req_temperature_" + rpm_string + "_debug(pwm values):_" + String(OCR1A) + "_" + String(OCR2A) + "_" + String(OCR2B)); //запрос к пк
unsigned long query_time = millis();
while (Serial.available() == 0) { //ждем пока ответит
if (millis() >= query_time + GET_TEMP_TIMEOUT) { //если слишком долго не отвечает
//Венитялторы на максимум
for (int i = 0; i < CONTROL_COUNT; i++) {
controls[i].dutyCycle = MAX_PWM_DUTY_CYCLE; //установка скважности по умолчанию
controls[i].dutyCycle = DEFAULT_PWM_DUTY_CYCLE; //установка скважности по умолчанию
}
wait_for_connection(true);//снова ждем подключения
break;
......@@ -247,17 +244,6 @@ void tempFromPc()// получаем температуру от пк
void autocontrol()//Автоконтроль температуры
{
tempFromPc();//получаем температуру от пк
for (int i = 0; i < 3; i++) {
target_rpms[i] = temps[i];
}
}
void rpm_control() {
......@@ -270,11 +256,10 @@ void rpm_control() {
}
for (int group = 0; group < 3; group++) { //PID управление скоростью 3-х групп вентиляторов
/*
uint8_t duty_cycle = 0;
for (int group = 0; group < 3; group++) {//управление скоростью 3-х групп вентиляторов
uint8_t duty_cycle = 0;
if (target_rpms[group] > 0) {
if (temps[group] > 0) {
uint32_t avg_rpm = 0;
//вычисление средних rpm из нескольких значений, записанных для фильтра
......@@ -283,8 +268,8 @@ void rpm_control() {
}
avg_rpm /= RPM_VALUES_COUNT;
d[group] = (target_rpms[group] - int(avg_rpm) - p[group]) / float(millis() - lastRpmCheck);
p[group] = target_rpms[group] - int(avg_rpm);
d[group] = (temps[group] - int(avg_rpm) - p[group]) / float(millis() - lastRpmCheck);
p[group] = temps[group] - int(avg_rpm);
i[group] = i[group] + p[group] * (millis() - lastRpmCheck);
if (fabs(i[group] * k_i) >= i_max) {
......@@ -303,12 +288,11 @@ void rpm_control() {
}
}
else {
}
else {
duty_cycle = 0;
}
*/
uint8_t duty_cycle = target_rpms[group];
}
if (duty_cycle < MIN_PWM_DUTY_CYCLE) {
controls[group].mode = 0; //всегда выкл
......@@ -323,7 +307,6 @@ void rpm_control() {
}
controls[group].dutyCycle = duty_cycle;
}
......@@ -359,7 +342,7 @@ ISR(TIMER1_OVF_vect) { //Функция, вызываемая при переп
if (control_a.mode > 0) { //если режим шим или всегда вкл
digitalWrite(control_a.pin, 1);
}
delayMicroseconds(15);
//delayMicroseconds(15);
for (int i = 0; i < 2; i++) { //проверка тахометров 0-1
if (digitalRead(fans[i].pin) == 0 && fans[i].state == 0) { //сигнал тахометра появился
fans[i].state = 1;
......@@ -425,7 +408,6 @@ void setup() {
//таймер 2 сравнение A - пин 5
//таймер 2 сравнение B - пин 6
//Пины 5 и 6: скважность от 7 до 254. Если меньше 2, то немного штырит.
//Пины 5 и 6: скважность от 8 до 255.
//режим таймера 1 (fast pwm 8 бит):
......@@ -459,22 +441,21 @@ void setup() {
void loop() {
if (uint32_t(millis() - last_check_time) >= CHECK_TEMP_DELAY) {
last_check_time = millis();
autocontrol(); //Получаем температуру от ПК, устанавливаем соотв. скорость вращения
tempFromPc();//получаем температуру от пк
// if (temp >= 80) { //Если перегрев, включается пищалка
// if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
// analogWrite(SOUND_PIN, BEEP_STRENGTH);
// last_beep_time = millis();
// }
// else if (millis() >= last_beep_time + BEEP_INTERVAL) {
// analogWrite(SOUND_PIN, 0);
// }
// }
// else {
// analogWrite(SOUND_PIN, 0); //Чтобы постоянно не пищал, если температура станет < 80 в то время, как пищалка включена
// }
/*
if (temp >= 80) { //Если перегрев, включается пищалка
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
last_beep_time = millis();
}
else if (millis() >= last_beep_time + BEEP_INTERVAL) {
analogWrite(SOUND_PIN, 0);
}
}
else {
analogWrite(SOUND_PIN, 0); //Чтобы постоянно не пищал, если температура станет < 80 в то время, как пищалка включена
}
*/
}
if (uint32_t(millis() - lastRpmCheck) >= RPM_CHECK_INTERVAL) {
......
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