Полуавтоматическое управление насосом скважины

в 10:46, , рубрики: arduino, diy или сделай сам, stm32, Электроника для начинающих

Полуавтоматическое управление насосом скважины с помощью STM32 в среде Ардуино

image

Многие обладатели приусадебных участков имеют на своих владениях водяные скважины, и, возможно, сталкивались с проблемой заиливания колодца/протухания воды за время простоя скважины с осени по весну.

Так уж случилось, что скважина на моем участке простаивала несколько лет, а когда пользовались, то отбирали очень мало воды.

Попытавшись почистить ее различными способами, было приобретено понимание, что не так уж все плохо и достаточно обеспечить стабильный отбор воды. Для этого было собрано несложное устройство, состоящее из блока питания с переходником микро-usb, (зарядное от телефона, на фото отсутствует), платы blue pill на базе камушка stm32f103c8t6, модуля реле, двухполюсного магнитного пускателя, обычного кнопочного выключателя закрытого типа, и собрано в распаечной коробке.

Плату микроконтроллера подготовил по мануалу HWman 'а.

Программирую с помощью плагина в Visual Studio Community. Установка плагина примитивная, не составит никакого умственного труда. Добавлю только, что плагин требует установленной ARDUINO IDE. Профессионалов, полагаю, смутит подобный подход, однако готовое изделие стабильно работает более полугода и выполняет поставленную задачу. И все же, я открыт для сотрудничество по идеям улучшения устройства.

Получаем крайне удобную среду с синтаксическим анализом кода, IntelliSense, и что субъективно немаловажно — темную тему оформления. Ибо глазоньки.

Прошиваем платку:

Код

/*
 Name:		Nasos.ino
 Created:	23.02.2017 19:08:20
 Author:	Ksiw
 Архитектура программы заключается в установлении флага на включение реле различными способами, дабы  в последстви проверить его(флага) статус и тогда переключить реле в соответственное положение.
 Если кнопка ручного включения не зажата, то цикл выполняется примерно 10 раз в секунду.
 Если кнопка зажата, то вначале дается десятая секунды во избежание дребезга, далее, проверка состояния кнопки происходит 20 раз/сек.
  99% всего времени камень отдыхает пребывая в delay()
*/
unsigned long Work = 2UL*60;             /*2 минуты работы*/ // укажи тут время работы в минутах, с приведением типов
//это не просто какая то дичь... Однако! Без приведения умножение вычислялось неверно.			
const unsigned long Sleep = (unsigned long)20*60;       //а тут простоя

unsigned long TimeLeft;                          //осталось секунд до переключения

int tempo = iter;                                    //сразу вывести 
int iter = 10;                                         //пропускать итераций перед следующим выводом в порт

unsigned long timeNextSwich;

int button = PB4;                                    //пин кнопки
unsigned long WorkTime, SleepTime ;       // время в миллисекундах продолжительность включения насоса в минутах

bool handOn = false;                      //флаг ручного включения
bool flag;                                       //флаг статуса реле
int RelayPin = PB7;                         //пин управления реле
unsigned long PreviousMillis = 0;
unsigned long CurrentMillis = millis();


unsigned long fullTimeIteration = 4200000000;          //вычисление времени рестарта программы //(long 4,294,967,295)
//---------------------прототипы
void SwichFlag();
void SwichRelay();
void Button();
unsigned long SecToMillis(unsigned long);
void ResidueTime();
void ResetTimeToWork();

//-------------------------------------в начало программы---------------------------
void(*resetFunc) (void) = 0;

//********************************ПУСК*******************************************
void setup()
{
	Serial.begin(115200);
	flag = false;        //включение реле при загрузке микроконтроллера

	//-----------Инициализация вывода реле
	pinMode(RelayPin, OUTPUT);
	pinMode(button, INPUT);
	digitalWrite(RelayPin, flag);
	Serial.println("");

	WorkTime = SecToMillis(Work);
	SleepTime = SecToMillis(Sleep);
	PreviousMillis = millis();
	

}

void loop() //****************************ЦИКЛ**********************************************
{
	
while(true)
	{
		CurrentMillis = millis();          //текущее время
		ResetTimeToWork();                 //проверка переполнения milis()
		SwichFlag();                       //проверка необходимости переключения
		SwichRelay();                      //переключили реле, если флаг изменился
		ResidueTime();                     //вывод в порт
		Button();                          //обработка кнопки

		tempo++;
		handOn = false;
		delay(100);
	}
}
//-------------------------------------переключение флага----------------------------------------------
void SwichFlag()
{
	
	if(flag && CurrentMillis-PreviousMillis>=SleepTime)
	{
		PreviousMillis = CurrentMillis;
		flag = false;                                //если разница предыдущего замера и текущего времени больше времени сна, но уставим флаг на включение
		Serial.println("Flag On");
	}
		
	else if(!flag && CurrentMillis-PreviousMillis>=WorkTime) //Иначе, если реле включено и пришло время выключаться, переключим флаг в "выключено"
	{
		PreviousMillis = CurrentMillis;
                flag = true;
		Serial.println("Flag OFF");
	}
}
//-------------------------------------работа кнопки-------------------------------------------------------
void Button()
{
	if(digitalRead(button)==HIGH)                              //если кнопка нажата
	{
		do
		{
			if(handOn)
			{
				delay(50);
				continue;
			}
			Serial.println("TURNED ON");
			digitalWrite(RelayPin, LOW);                 //то включаем реле
		                    
			flag = true;
			handOn = true;
			delay(100);                                        //и немного удерживаем
		 
		}while (digitalRead(button)==HIGH);

		CurrentMillis = millis();                 //узнаем и записываем когда это окончилось
		PreviousMillis = CurrentMillis;        //обновляем время последних действий
		delay(20);
	}
}
//-------------------------------------преобразование секунд в миллисекунды---------------------------
unsigned long SecToMillis(unsigned long Temp)
{
	return Temp*1000;
}
//-------------------------------------время до переключения----------------------------------------------
void ResidueTime()
{
	if(CurrentMillis<PreviousMillis && tempo > iter)
	{
		if(flag)
		{
			TimeLeft = timeNextSwich/1000+1;
			Serial.print(" Time to ON: ");
			Serial.print(TimeLeft);
			Serial.print("sec");
			Serial.println("");
		}
		else
		{
			TimeLeft = timeNextSwich/1000+1;
			Serial.print(" Time to OFF: ");
			Serial.print(TimeLeft);
			Serial.print("sec");
			Serial.println("");
		}
		tempo = 0;
	}
			
	if(tempo > iter)  //вывод каждую нную итерацию
	{
	   if(flag)
	   {
		   TimeLeft = (PreviousMillis+SleepTime-CurrentMillis)/1000+1;
		   Serial.print(" Time to ON: ");
		   Serial.print(TimeLeft);
		   Serial.print("sec");
		   Serial.println("");
	   }
	   else
	   {
		   TimeLeft = (PreviousMillis+WorkTime-CurrentMillis)/1000+1;
		   Serial.print(" Time to OFF: ");
		   Serial.print(TimeLeft);
		   Serial.print("sec");
		   Serial.println("");
	   }
		tempo = 0;
	}  
}
//-------------------------------------переходная функция вовремя переполнения milis();
void ResetTimeToWork()
{
	while(CurrentMillis<PreviousMillis)           //если текущее время меньше последней записи 
	{
		if(flag)                                         //и реле выключено
		{
			timeNextSwich = SleepTime-(4294967295-PreviousMillis);    //то находим время следующего переключения
			while(timeNextSwich>=CurrentMillis)                     //пока время следующего переключения больше текущего
			{
				CurrentMillis = millis();
				ResidueTime();
				
				Button();                                // если кнопка нажата, перезаписывается время последнего действа и выходим из функции ResetTimeToWork()!
				if(CurrentMillis>PreviousMillis)
					return;
				
				tempo++;                                 //для корректной работы ResidueTime();
			}
			flag = false;
			PreviousMillis = CurrentMillis;              //обновляем время изменения
			CurrentMillis = millis();                  
			return;
		}
		if(!flag)
		{
			timeNextSwich = WorkTime-(4294967295-PreviousMillis);
			while(timeNextSwich>=CurrentMillis)                           //пока время следующего переключения больше текущего
			{
				CurrentMillis = millis();
				ResidueTime();
				Button();
				if(CurrentMillis>PreviousMillis)
					return;
				tempo++;
			}
			flag = true;
			PreviousMillis = CurrentMillis;
			CurrentMillis = millis();
			return;
		}
	}
}
//--------------------------------------переключалка реле--------------------------------------------------
void SwichRelay()
{
	
	if(!flag)
	{
		digitalWrite(RelayPin, flag); // включаем реле
	}
	else
	{
		digitalWrite(RelayPin, flag);   // выключаем реле
	}
}

Касаемо кода. Программа писалась в несколько подходов, модифицируясь по ходу выявления недочетов. Выглядит довольно запутанно, но постараюсь разъяснить.

Работает следующим образом:

0) Архитектура программы разработана так, чтобы любым внешним устройством(кнопкой, датчиком давления, таймером т.п. установить флаг на включение, либо выключение реле, а после, отдельной функцией проверить состояние флага и переключить в соответствующее положение).

1) Код программы упирается на таймер функции millis(), функция ResidueTime() вычисляет время следующего переключения реле, SwichRelay() проверяет статус флага и дает команду переключения, если необходимо.

2) Реле переключается при подаче низкого уровня сигнала с ноги PB7. При включении устройства, после инициализации МК, реле переходит в положение ВКЛ, подавая напряжение на катушку пускателя, а тот в свою очередь подает напряжение на насос.

3) Время работы устройства — 2 минуты, после чего оно переходит в режим ожидания на 20 мин.

4) Включение выключателя обрабатывается незамедлительно, а после выключения программа выдерживает интервал простоя в 20 минут. Это сделано для того, чтобы скважина восполнила откачанную воду, и исключить случай работы насоса насухую.

5) Так же в коде присутствует функция ResetTimeToWork(), которая срабатывает при переполнении функции millis(), которая

Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается на ноль, в следствие переполнения значения, приблизительно через 50 дней.

(с сайта Arduino.ru)

Следовательно, дабы устройство не «упало» после этого срока непрерывной работы, разработана упомянутая функция, обеспечивающая стабильную работу устройства без дополнительного перезапуска.

Приступаем к сбору схемы.

Схема сборки:

image

Сигнал с ноги PB4 необходимо прижать к земле резистором 4,7КОм, в противном случае, реле работает неверно.

В коробке устанавливаем динрею для пускателя:

image

И монтируем остальные запчасти, закрепляя их положение с помощью горячих соплей, как на первом фото.

image

В крышке необходимо прорезать отверстие для пускателя, он выше, чем глубина коробки.

Не забываем установить блок питания для платы, можно спрятать его внутрь коробки, в ней еще достаточно места, либо вынести во вне и воткнуть его в близжайшую розетку.

Готовое устройство, осталось только посадить на клеммы пускателя кабель 230V и подключить нагрузку.

image

Выключатель использовал старый, он же стоял на включении насоса. Снаружи имеет неустранимые загрязнения цементом и прочие потертости, однако имеет герметичный корпус, внутри совершенно цел и в саду будет еще долго исправно работать. Учитывая мизерный коммутируемый ток — практически вечно, пока не сломается механически.

Спасибо HWman 'у за представленную статью по обеспечению работы stm32 во фреймворке ARDUINO.

Архив со скетчем прилагаю.

Автор: Ksiw

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js