История о том, как я угольный котел автоматизировал
/*
Prometey shaker
17.11.2014
*/
#include
#include
#include
#define SHAKERSTEPUPPIN 4 // Пин для кнопки Шаг++
#define SHAKERSTEPDOWNPIN 5 // Пин для кнопки Шаг--
#define MANUALSHAKE 6 // Ручной запуск/Меню ОК
#define POWERRELE 8 // Питания на Реле
int eppromaddr = 0;
int SHAKERSTEP = 300; // Шаг в секундах с которым будет увеличиваться/уменьшаться период встряхивания.
int sensorPin0 = A1; // select the input pin for the potentiometer
float stepValue = 0.0986328125; // step value per one of 0…1023 ((50A * 2 + 1)/1024)
int ZeroLevel = 514; // Zero level. Нулевоз значение АЦП для датчика тока (высчитывается в setup)
float CurrentLevel = 3; // Пороговое значение для тока (задается программно)
byte CURRSHAKE, LEFTSHAKE = 2; // Пин для активации встряхивателя вращение по часовой стрелки + текущий пин для встряхивания
byte RIGHTSHAKE = 3; // Пин для активации встряхивателя вращение против часовой стрелки
byte DOVOD = 12; // Пин для активации доводчика (Прометей)
int buttonState = 0; // Состояние нажатой кнопки
boolean buttonPressed = false;
boolean showMenu = false;
byte menuItem = 0;
volatile long mks100;
volatile long ms10;
volatile int cntr;
long tmillis, tms10=0;
unsigned long shaketimer, shaketime = 0;
byte stopshake; // Время работы шейкера
byte stpdvd; // Время работы доводчика (Прометей)
boolean flip = false; // Для отслеживания активности шейкера — стоит ли вызывать функцию проверки тока и включать реверс.
boolean flipD = false; // Для отслеживания активности доводчика — стоит ли вызывать функцию включения доводчика (Прометей)
byte shakerstepcount = 0;
long temp = 0; // временная переменная для отслеживания времени
boolean tempcurr = 0; // временная переменная для вывода сообщения об измерении тока
boolean isinit = true;
LiquidCrystal_I2C lcd (0×27,16,2);
void setup () {
Serial.begin (9600);
// инициализация портов для кнопок
pinMode (SHAKERSTEPUPPIN, INPUT);
digitalWrite (SHAKERSTEPUPPIN, HIGH);
pinMode (SHAKERSTEPDOWNPIN, INPUT);
digitalWrite (SHAKERSTEPDOWNPIN, HIGH);
pinMode (MANUALSHAKE, INPUT);
digitalWrite (MANUALSHAKE, HIGH);
lcd.init (); // Инициализация lcd
lcd.backlight (); // Включаем подсветку
lcd.print («Version 3.0»);
lcd.setCursor (0, 1);
lcd.print (»13.11.2014»);
delay (1000);
lcd.clear ();
pinMode (sensorPin0, INPUT); // Curent sensor
digitalWrite (sensorPin0, HIGH); // Включаем внутренний подтягивающий резистор
shaketimer = SHAKERSTEP;
mks100 = 0; // счетчик сотен микросекунд, переполнение счетчика примерно через 5 суток
ms10 = 0; // счетчик десятков миллисекунд, переполнение счетчика примерно через 16 месяцев
cntr = 0;
flip = 0;
flipD = 0; // просто добавил это здесь (Прометей)
pinMode (LEFTSHAKE, OUTPUT); // Подготавливаем порты для активации реле. Реле инвертное, срабатывает при подачи GND на контрольные пины.
pinMode (RIGHTSHAKE, OUTPUT); // Подготавливаем порты для активации реле. Реле инвертное, срабатывает при подачи GND на контрольные пины.
CURRSHAKE = LEFTSHAKE;
digitalWrite (LEFTSHAKE, HIGH); // Подготавливаем порты для активации реле. Реле инвертное, срабатывает при подачи GND на контрольные пины.
digitalWrite (RIGHTSHAKE, HIGH); // Подготавливаем порты для активации реле. Реле инвертное, срабатывает при подачи GND на контрольные пины.
pinMode (SHAKERSTEPUPPIN, INPUT); // Подготавливаем порты для кнопки
digitalWrite (SHAKERSTEPUPPIN, HIGH);
pinMode (SHAKERSTEPDOWNPIN, INPUT); // Подготавливаем порты для кнопки
digitalWrite (SHAKERSTEPDOWNPIN, HIGH);
pinMode (POWERRELE, OUTPUT); // Подаем питание на реле (Прометей)
digitalWrite (POWERRELE, HIGH);
pinMode (DOVOD, OUTPUT); // Подготавливаем порт на доводчик (Прометей)
digitalWrite (DOVOD, HIGH);
// Включаем нужный нам режим таймера/счетчика — нормальный
TCCR2A = 0; //нормальный режим (по умолчанию 1 — ШИМ с коррекцией фазы?)
// Предделитель таймера/счетчика настраиваем на 16 — // это позволит «тикать» таймером каждую микросекунду
// (в предположении, что сердце микроконтроллера стучит с
// частотой 16.000.000 ударов в секунду)
TCCR2B = 2; // 010 — fclk/8 (по умолчанию 100 — fclk/64)
//TCCR2B = 7; // 111 — fclk/1024 (по умолчанию 100 — fclk/64)
TCNT2=59;//55;
TIMSK2 |= (1
}
ISR (TIMER2_OVF_vect) {
// прежде всего взводим счетчик
TCNT2=59;//55;
// прошли очередные 100 мксек — увеличиваем счетчик сотен микросекунд
mks100++;
// if (mks100%100==0) ms10++;
cntr++;
// прошли очередные 10 мсек? — увеличиваем счетчик десятков миллисекунд
if (cntr>99) {
ms10++;
cntr = 0;
}
}
float getCurrent () {
int sensorRead = 0;
for (int i=0; i
sensorRead+=analogRead (sensorPin0);
}
sensorRead = sensorRead / 5;
// lcd.setCursor (8,1);
// lcd.print (sensorRead);
// lcd.print (» »);
return (abs (sensorRead — ZeroLevel)) * stepValue;
// return abs (analogRead (sensorPin0) — ZeroLevel) * stepValue;
}
void current_check () {
float CurrentValue = getCurrent ();
if (CurrentValue >= CurrentLevel) {
stop_shake ();
if (CURRSHAKE == LEFTSHAKE) {
CURRSHAKE = RIGHTSHAKE;
} else {
CURRSHAKE = LEFTSHAKE;
}
start_shake ();
}
// Проверяем значение датчика тока. Если значение больше порогового (расчетное 5А),
// то деактивируем шейкер, меняем CURRSHAKE на противоположное (LEFTSHAKE, RIGHTSHAKE)
// и активируем по-новой.
}
void start_shake () {
digitalWrite (CURRSHAKE, LOW); // Подаем сигнал на активацию шейкера
shaketime = 0;
flip = true;
delay (500);
}
void stop_shake () {
digitalWrite (CURRSHAKE, HIGH); // Подаем сигнал на деактивацию шейкера
flip = false;
// stopshake = 0;
}
void startdovod () {
digitalWrite (DOVOD, LOW); // Подаем сигнал на активацию доводчика (Прометей)
flipD = true;
}
void stopdovod () {
digitalWrite (DOVOD, HIGH); // Подаем сигнал на деактивацию доводчика (Прометей)
flipD = false;
}
void lcdTimer () {
lcd.setCursor (0, 0);
lcd.print («shake:»);
lcd.setCursor (6, 0);
lcd.print (SHAKERSTEP/60 — shaketime/60);
lcd.print (»/»);
lcd.print (SHAKERSTEP/60);
lcd.print (» »);
}
void lcdCurrent () {
lcd.setCursor (0, 1);
lcd.print («C:»);
lcd.setCursor (2, 1);
lcd.print (getCurrent (), 1);
lcd.print (»/»);
lcd.print (CurrentLevel, 1);
}
int buttonRead () {
int readState = 0;
int returnCode = 0;
readState = ! digitalRead (SHAKERSTEPUPPIN);
returnCode = readState;
readState = ! digitalRead (SHAKERSTEPDOWNPIN) * 2;
returnCode+=readState;
readState = ! digitalRead (MANUALSHAKE) * 4;
returnCode+=readState;
if (returnCode > 0) { delay (200); }
//lcd.setCursor (0,0); lcd.print (returnCode);
return returnCode;
}
void lcdMenu () {
lcd.setCursor (0, 0);
lcd.print (»1.Timer set»);
lcd.setCursor (0, 1);
lcd.print (»2.Current set»);
lcd.setCursor (0, 0);
}
void lcdCurrentMenu () {
lcd.clear ();
lcd.setCursor (0, 0);
lcd.print («Current lvl:»);
lcd.print (CurrentLevel,1);
}
void lcdTimerMenu () {
lcd.clear ();
lcd.setCursor (0, 0);
lcd.print («Timer set:»);
lcd.print (SHAKERSTEP/60);
}
void buttonProcced (byte buttonState) {
// Ручной shake
if ((buttonState == 4)&&(! showMenu)) {
start_shake ();
}
// Ручной dovod
if ((buttonState == 3)&&(! showMenu)) {
startdovod ();
}
// вызов меню
if (buttonState == 3) {
menuItem = 1;
lcd.clear ();
showMenu = ! showMenu;
if (showMenu) {
lcd.blink ();
lcdMenu ();
}
if (! showMenu) {
byte lowByte = ((SHAKERSTEP >> 0) & 0xFF);
byte highByte = ((SHAKERSTEP >> 8) & 0xFF);
EEPROM.write (eppromaddr, lowByte);
EEPROM.write (eppromaddr+1, highByte);
EEPROM.write (eppromaddr+2, CurrentLevel*10);
lcd.noBlink ();
lcdTimer ();
lcdCurrent ();
}
}
// кнопка вниз в основном меню
if ((showMenu)&&(buttonState == 2)&&(menuItem
menuItem = 2;
lcd.setCursor (0, 1);
}
// кнопка вверх в основном меню
if ((showMenu)&&(buttonState == 1)&&(menuItem
menuItem = 1;
lcd.setCursor (0, 0);
}
// кнопка вниз в меню тока
if ((menuItem == 2)&&(buttonState == 4)) {
menuItem = 4;
buttonState = 0;
lcdCurrentMenu ();
}
// кнопка вверх в меню тока
if ((menuItem == 4)&&(buttonState == 2)) {
CurrentLevel-=0.5;
if (CurrentLevel
lcdCurrentMenu ();
}
// кнопка вниз в меню тока
if ((menuItem == 4)&&(buttonState == 1)) {
CurrentLevel+=0.5;
if (CurrentLevel>=8) {CurrentLevel=1;}
lcdCurrentMenu ();
}
// кнопка Ок в меню тока
if ((menuItem == 4)&&(buttonState == 4)) {
buttonState = 0;
menuItem = 1;
lcd.clear ();
lcdMenu ();
}
// кнопка Ок — выбор меню таймера
if ((menuItem == 1)&&(buttonState == 4)) {
menuItem = 3;
buttonState = 0;
lcdTimerMenu ();
}
// кнопка вниз в меню таймера
if ((menuItem == 3)&&(buttonState == 2)) {
SHAKERSTEP-=300;
if (SHAKERSTEP
// if (SHAKERSTEP > 3600) {SHAKERSTEP = 300;}
lcdTimerMenu ();
}
// кнопка вверх в меню таймера
if ((menuItem == 3)&&(buttonState == 1)) {
SHAKERSTEP+=300;
// if (SHAKERSTEP > 0) {SHAKERSTEP = 3600;}
if (SHAKERSTEP > 3600) {SHAKERSTEP = 300;}
lcdTimerMenu ();
}
// книпка Ок в меню таймера
if ((menuItem == 3)&&(buttonState == 4)) {
menuItem = 1;
lcd.clear ();
lcdMenu ();
}
}
void inits () {
ZeroLevel = 0;
for (int i=0; i
ZeroLevel+=analogRead (sensorPin0);
}
ZeroLevel = ZeroLevel/10;
lcd.print («zerolevel=»); lcd.print (ZeroLevel);
// stepValue =;
lcd.setCursor (0,1);
lcd.print («stepvalue=»); lcd.print (stepValue);
delay (1000);
lcd.clear ();
byte lowByte = EEPROM.read (eppromaddr);
byte highByte = EEPROM.read (eppromaddr + 1);
SHAKERSTEP = ((lowByte
CurrentLevel = EEPROM.read (eppromaddr+2)/10;
lcdTimer ();
lcdCurrent ();
isinit = false;
}
void loop () {
if (isinit) { inits (); }
buttonState = buttonRead ();
if (buttonState > 0) { buttonProcced (buttonState); }
if (ms10>tms10) {
tms10 = ms10;
if (tms10%1000==0) { // выполнение каждые 10 сек
shaketime += 10;
if (! showMenu) {
lcdTimer ();
lcdCurrent ();
}
}
if ((flip)&&(tms10%100==0)) { // выполнение каждую 1 сек
stopshake += 1;
}
if ((flipD)&&(tms10%100==0)) { // выполнение каждую 1 сек
stpdvd += 1;
}
if (stopshake>=3) { // сколько секунд работать
stop_shake ();
stopshake = 0;
// активируем доводчик (Прометей)
startdovod ();
}
if ((flipD)&&(stpdvd>=4)) {
stopdovod ();
}
if (shaketime>=SHAKERSTEP) {
start_shake ();
}
}
if (flip) {
current_check ();
}
}