Не пойму как реализовать таймер.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Здравствуйте. Подскажите как сделать защиту от перелива по времени.

задача простая набирается бак если вдруг с датчиком проблема, т.е. бак не набрался за определенное время то реле отключается.

Moderator : пожалуйста, вставьте код правильно (возможно, новым сообщением в тему), 

 

Зашел в тупик ничего не могу придумать.

mixail844
Offline
Зарегистрирован: 30.04.2012

Во первых, вставьте код по правилам форума. (в новом сообщении, т.к. первое сообщение нельзя редактировать)
В вторых, что значит зашли в тупик? Какой результат на деле у вас получается? Предоставьте схему соединения ардуино, реле и датчиков которые висят на EMPTYPin и FULLPin и когда(в какой момент процесса наполнения резервуара) по вашей задумке каждый датчик должен переходить из состояния LOW в HIGH и наоборот?
По вашей логике, как может быть что резервуар одновременно и пуст и полон ? elseif(EMPTY == HIGH && FULL == HIGH)?

Sidorchuk
Offline
Зарегистрирован: 22.07.2016

Все логично. Следующая строка должна быть lcd.print "я/или ты/поломалсо. Звони в жэк/вызывай санитаров/.

А если серьезно, то такое возможно. Имена переменных дурацкие ток. Лучше обозвать lowerSensor и upperSensor, например.

rkit
Offline
Зарегистрирован: 23.11.2016

Традиционно замечаю, что поплавковый механизм от унитаза стоит 200р.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

У Вас это уже в привычку входит. Или это профессианальное?

Gvozd1488
Offline
Зарегистрирован: 07.02.2020
const int EMPTYPin = 7;       // нижний датчик:
const int FULLPin = 8;        // верхний датчик:
const int Relay =  13;       //  реле:
int EMPTY = 0;                // переменная нижий уровень:
int FULL = 0;                  // переменная верхний уровень:
unsigned long times=0;

void setup() {
  pinMode(Relay, OUTPUT);      // initialize the Relay pin as an output:
  pinMode(EMPTYPin, INPUT);     // initialize the EMPTYPin pin as an input:
  pinMode(FULLPin, INPUT);       // initialize the FULLPin pin as an input:
  }
 
void loop() {
  EMPTY = digitalRead (EMPTYPin);
  FULL = digitalRead (FULLPin);
               
 if (EMPTY == LOW && FULL == LOW) {                  // здесь включаем реле:   
  digitalWrite(Relay, HIGH);
  times=millis();
  }
    else     if (EMPTY == HIGH && FULL == HIGH) {   // здесь выключаем  реле при наполнении:
              digitalWrite(Relay, LOW);
              }  
 if (millis() - times >= 3000 )   {    // здесь  реле должно отключиться если прошло больше времени чем times:
            digitalWrite(Relay, LOW);
            delay (9000000000000);
            }     
}

Резервуар пустой LOW полный HIGH.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Извиняюсь за имена переменных. Культуру написания переменных воспитывать годами надо.

на мой взгляд коротко и понятно.

Так а насчет вопроса Как?

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

По идее все должно выло работать нормально. Т.Е. само наполнение происходит как положено. Когда поплавок опустился в низ, оба датчика LOW, реле включилось. Поплавок поднялся, оба датчика HIGH, реле отключилось. При подаче питания все работает после того как реле стало LOW, т.е. отключилось таймер считает 3 секунды и уходит в аварию, а должен включаться после HIGH/

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

для начала определиться со временем наполнения к примеру

#define SECONDS(x) ((x) * 1000UL)
#define MINUTES(x)  (SECONDS(x) * 60UL)
#define HOURS(x)  (MINUTES(x) * 60UL)
#define DAYS(x)   (HOURS(x) * 24UL)
#define WEEKS(x)  (DAYS(x) * 7UL)
unsigned long interval = MINUTES(10);

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Для начала надо точно определиться - с какого момента считать. С EMPTY или с FULL.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

sadman41 пишет:
Для начала надо точно определиться - с какого момента считать. С EMPTY или с FULL.

Я думаю с соответствия условиям во время подачи питания? Т.Е. соответствует 1 условиям то реле включено,

а если другим то отключено.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

ua6em пишет:

для начала определиться со временем наполнения к примеру

#define SECONDS(x) ((x) * 1000UL)
#define MINUTES(x)  (SECONDS(x) * 60UL)
#define HOURS(x)  (MINUTES(x) * 60UL)
#define DAYS(x)   (HOURS(x) * 24UL)
#define WEEKS(x)  (DAYS(x) * 7UL)
unsigned long interval = MINUTES(10);

 

Так у меня часов нет. Простой счетчик работающий от millis(), реле включилось, счетчик сбросился.

sadman41
Offline
Зарегистрирован: 19.10.2016

Gvozd1488 пишет:

sadman41 пишет:
Для начала надо точно определиться - с какого момента считать. С EMPTY или с FULL.

Я думаю с соответствия условиям во время подачи питания?

Вот поэтому все один раз и работает. 

т.е. ваша ардуина включается вместе с насосом и должна или засечь FULL или выключить движок через 3 секунды?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

#define SECONDS(x) ((x) * 1000UL)
#define MINUTES(x)  (SECONDS(x) * 60UL)
#define HOURS(x)  (MINUTES(x) * 60UL)
#define DAYS(x)   (HOURS(x) * 24UL)
#define WEEKS(x)  (DAYS(x) * 7UL)
unsigned long interval = MINUTES(5);

const int EMPTYPin = 7;       // нижний датчик:
const int FULLPin = 8;        // верхний датчик:
const int Relay =  13;        //  реле:
int EMPTY = 0;                // переменная нижий уровень:
int FULL = 0;                 // переменная верхний уровень:
unsigned long times=0;
//byte flagtimer = 0; // в качестве флага можно использовать состояние реле

void setup() {
  pinMode(Relay, OUTPUT);      // initialize the Relay pin as an output:
  pinMode(EMPTYPin, INPUT);     // initialize the EMPTYPin pin as an input:
  pinMode(FULLPin, INPUT);       // initialize the FULLPin pin as an input:
  }
 
void loop() {
 EMPTY = digitalRead (EMPTYPin);
 FULL = digitalRead (FULLPin);
  
if(!EMPTY && !digitalRead(Relay)){ // здесь включаем реле: 
 digitalWrite(Relay, HIGH);
 times=millis();    
}
if(FULL && digitalRead(Relay)){ // здесь выключаем по датчику
 digitalWrite(Relay, LOW);
}

if(millis()- times >= interval && digitalRead(Relay)){ // здесь выключаем по времени
 digitalWrite(Relay, LOW);
}

}

 

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

понял про что говорилось. Спасибо за труд. Не пойму одного у Вас идет опрос идет одного датчика с привязкой к состоянию реле. Я так пробовал, не получилось. 

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Сейчас пробую ваш скетч тоже не правильно работает.За идею огромное спасибо. Буду сегодня пробовать.

Извиняюсь, но надо на работу.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

состояние датчиков я принимал как включено, поправь если это не так, то-есть при достижении уровня в нижнем датчике -HIGH, аналогично и верхний, в системах ON это включено, а не наоборот, как у тебя )))

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Делал исходя из фактических значений.

Здесь 4 состояния, но удовлетворяют условиям 2, когда оба датчика LOW (поплавок ВНИЗУ) включаем насос, когда оба HIGH (поплавок вверху)отключаем, именно оба датчика, потому что поплавок поднимает шток снизу вверх перекрывая сначала нижний , затем верхний. Промежуточные состояния игнорируются. А если вдруг, после включения реле, поплавок не дошел до верха за заданный период времени то отключаем выполнение программы с флагом аварии.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Без части отвечающей за временную составляющую все работает как надо. С учетом естественно механники. Как только начинаешь вставлять время, начинаются танцы.

Gvozd1488
Offline
Зарегистрирован: 07.02.2020
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Gvozd1488 пишет:

Так это выглядит.

https://postimg.cc/yD78fQw8

https://i.postimg.cc/V6xLpNyh/image.png

Поменял значение на нижнем датчике, обязано работать, мог бы и сам это сделать, пост #13

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Пардон. Прощёлкал. Теперь работает идеально. Но свой костыль с delay всё же вставил :-) Иначе опять по кругу запускает.

 

#define SECONDS(x) ((x) * 1000UL)
#define MINUTES(x)  (SECONDS(x) * 60UL)
#define HOURS(x)  (MINUTES(x) * 60UL)
#define DAYS(x)   (HOURS(x) * 24UL)
#define WEEKS(x)  (DAYS(x) * 7UL)
unsigned long interval = MINUTES(1);

const int EMPTYPin = 7;       // нижний датчик:
const int FULLPin = 8;        // верхний датчик:
const int Relay =  13;        //  реле:
int EMPTY = 0;                // переменная нижий уровень:
int FULL = 0;                 // переменная верхний уровень:
unsigned long times=0;
//byte flagtimer = 0; // в качестве флага можно использовать состояние реле

void setup() {
  pinMode(Relay, OUTPUT);      // initialize the Relay pin as an output:
  pinMode(EMPTYPin, INPUT);     // initialize the EMPTYPin pin as an input:
  pinMode(FULLPin, INPUT);       // initialize the FULLPin pin as an input:
  }
 
void loop() {
 EMPTY = digitalRead (EMPTYPin);
 FULL = digitalRead (FULLPin);
  
if(!EMPTY && !digitalRead(Relay)){ // здесь включаем реле: 
 digitalWrite(Relay, HIGH);
 times=millis();    
}
if(FULL && digitalRead(Relay)){ // здесь выключаем по датчику
 digitalWrite(Relay, LOW);
}

if(millis()- times >= interval && digitalRead(Relay)){ // здесь выключаем по времени
 digitalWrite(Relay, LOW);
 delay (9000000000000);
}
}


 

СПАСИБО!!!!!!!

delay потом поменяю на цикл с диодом типа индикации.

 

Если рядом живете, с меня пиво. Я в Воронежской области.

 

sadman41
Offline
Зарегистрирован: 19.10.2016

На кой весь этот цирк с ардуиной при таком дилее?

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Да просто поплавкового механизма от унитаза нет.

Это вроде как индикация ошибки. потом сделаю моргающий светодиод, вроде же написал.

Охренеть куда написал. Извиняюсь.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

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

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

А если авария нижнего датчика? Лучше пусть так. Надёжней. Ещё раз Спасибо! Вроде задача не сложная, но если не занимаешься этим часто, то и не простая.

kalapanga
Offline
Зарегистрирован: 23.10.2016

Без разницы, зачем Вы воткнули это чудо - delay (9000000000000);

Но функция delay принимает аргумент типа Unsigned long, максимальное значение - 4 294 967 295. Так что, если бы Вам действительно была нужна именно такая задержка (9000000000000) Вас бы ждал сюрприз. Может и сейчас ждёт.
 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

А зачем delay (9000000000000)? Есть прекрасная штука while(1);. Да и пищалку надо при аварии включить или светодиодик типа ERRORpin. А еще лучше while(1) if(buttonRESET) break;

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

А вообще любят у нас гланды через ж*пу... Посмотрите как работают электромеханические средства и их программируйте. Навскидку без паранойи:

void loop() {

if(digitalRead (EMPTYPin)){
 digitalWrite(Relay, HIGH);
//тута запомнить текущий миллис
while(!digitalRead (FULLPin)){
//вот тута можно прописать защиту по времени
}
 digitalWrite(Relay, LOW);
}
}

С паранойей - в строку 2 добавить 

if(digitalRead (FULLPin)) digitalWrite(Relay, LOW);

 

 

Gvozd1488
Offline
Зарегистрирован: 07.02.2020

Получилось как - то так.

#define SECONDS(x) ((x) * 1000UL)
#define MINUTES(x)  (SECONDS(x) * 60UL)
#define HOURS(x)  (MINUTES(x) * 60UL)
#define DAYS(x)   (HOURS(x) * 24UL)
#define WEEKS(x)  (DAYS(x) * 7UL)
unsigned long interval = MINUTES(1);

const int EMPTYPin = 7;       // нижний датчик:
const int FULLPin = 8;        // верхний датчик:
const int Relay =  4;         //  реле:
const int ResButton = 6;      // кнопка сброса:
const int Light = 13 ;        // светодиод:
int EMPTY = 0;                // переменная нижий уровень:
int FULL = 0;                 // переменная верхний уровень:
unsigned long times=0;
//byte flagtimer = 0; // в качестве флага можно использовать состояние реле

void setup() {
  pinMode(Relay, OUTPUT);      // initialize the Relay pin as an output:
  pinMode(Light, OUTPUT);      // initialize the Light pin as an output:
  pinMode(EMPTYPin, INPUT);    // initialize the EMPTYPin pin as an input:
  pinMode(FULLPin, INPUT);     // initialize the FULLPin pin as an input:
  pinMode(ResButton, INPUT);   // initialize the ResButton pin as an input: 
 }
 
void loop() {
 EMPTY = digitalRead (EMPTYPin);
 FULL = digitalRead (FULLPin);
 digitalWrite (Light,LOW);
if (!EMPTY && !digitalRead(Relay)){                       // здесь включаем реле: 
 digitalWrite(Relay, HIGH);
 times=millis();    
}
if (FULL && digitalRead(Relay)){                          // здесь выключаем по датчику
 digitalWrite(Relay, LOW);
}
if (millis()- times >= interval && digitalRead(Relay)){   // здесь выключаем по времени
 digitalWrite(Relay, LOW);
 while(!digitalRead(Relay)) {
  digitalWrite(Light, HIGH);           // потому как на время уже пофигу поэтому delay
  delay(500);             
  digitalWrite(Light, LOW);    
  delay(500);      
  if(digitalRead(ResButton)== HIGH) {
  break; 
      }
    }
  }
}

 

RG22EM
Offline
Зарегистрирован: 27.08.2016

можешь же когда захочешь )))

geka0110
Offline
Зарегистрирован: 12.02.2020
#include <Sim800l.h>
#include  <Wire.h> // Подключаем библиотеку Wire
#include  <Adafruit_Sensor.h>                             // Подключаем библиотеку Adafruit_Sensor
#include  <Adafruit_BME280.h>                             // Подключаем библиотеку Adafruit_BME280:  pin= SCL(A5),pin=SDA(A4)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3);              // Выводы SIM800L Tx & Rx подключены к выводам Arduino 3 и 2
#define SEALEVELPRESSURE_HPA (1013.25)                   // Задаем высоту
Adafruit_BME280 bme;

#define SECONDS(x) ((x) * 1000UL)
#define MINUTES(x)  (SECONDS(x) * 60UL)
#define HOURS(x)  (MINUTES(x) * 60UL)
#define DAYS(x)   (HOURS(x) * 24UL)
#define WEEKS(x)  (DAYS(x) * 7UL)
unsigned long interval = MINUTES(1);

char incomingByte;
String inputString;
int relay_1 = A0;                           // Вывод управления реле 1
int relay_2 = A1;                           // Вывод управления реле 2
//int pin = SCL(A5)                          // Вывод управления BME280
//int pin = SDA(A4)                          // Вывод управления BME280
float t;
float p;
float h;
 

unsigned long times=0;
unsigned long relay1OnMillis = 0; // время включения реле 1
unsigned long relay2OnMillis = 0; // время включения реле 2
const unsigned long relay1AutoOffinterval = 1000; // время автоотключение реле 1 в миллисекундах. (5000 = 5 секунд, 0 - АВТОВЫКЛЮЧЕНИЕ ВЫКЛЮЧЕНО)
const unsigned long relay2AutoOffinterval = 4000; // время автоотключение реле 2 в миллисекундах. (5000 = 5 секунд, 0 - АВТОВЫКЛЮЧЕНИЕ ВЫКЛЮЧЕНО)



// флаги, поднимаемые при включении реле, если их нужно автовыключить
bool relay1AutoOff = false; 
bool relay2AutoOff = false;

void setup()
{
  pinMode(relay_1, OUTPUT);                 // Установим вывод как выход
  digitalWrite(relay_1, HIGH);              // Устанавливаем высокий уровень
  pinMode(relay_2, OUTPUT);                 // Установим вывод как выход
  digitalWrite(relay_2, HIGH);              // Устанавливаем высокий уровень
  Serial.begin(9600);
  mySerial.begin(9600);
   bme.begin(0x76);
    while (!mySerial.available()) {           // Зацикливаем и ждем инициализацию SIM800L
    mySerial.println("AT");                  // Отправка команды AT
    delay(1000);                             // Пауза
    Serial.println("Connecting…");         // Печатаем текст
  }
  Serial.println("Connected!");            // Печатаем текст
  mySerial.println("AT+CMGF=1");           // Отправка команды AT+CMGF=1
  delay(1000);                             // Пауза
  mySerial.println("AT+CNMI=1,2,0,0,0");   // Отправка команды AT+CNMI=1,2,0,0,0
  delay(1000);                             // Пауза
  mySerial.println("AT+CMGL=\"REC UNREAD\"");
}

void loop()
{
  
{
  

  //если автоключение реле включено и прошло заданное время автоотключения    {
  {

   if (millis()- times >= interval && digitalRead(relay1AutoOffinterval))   // здесь выключаем по времени
   
   if (millis()- times >= interval && digitalRead(relay2AutoOffinterval))   // здесь выключаем по времени
      
   
     if (relay1AutoOff && millis() - relay1OnMillis >= relay1AutoOffinterval) {
    digitalWrite(relay_1, HIGH); //отключаем реле
    relay1AutoOff = false; // опускаем флаг "автоотключить реле"
  }
  if (relay2AutoOff && millis() - relay2OnMillis >= relay2AutoOffinterval) {
    digitalWrite(relay_2, HIGH); //отключаем реле
    relay2AutoOff = false; // опускаем флаг "автооключить реле"
  }
  
  if (mySerial.available()) {                // Проверяем, если есть доступные данные
    delay(100);                            // Пауза
    while (mySerial.available()) {          // Проверяем, есть ли еще данные.
      incomingByte = mySerial.read();         // Считываем байт и записываем в переменную incomingByte
      inputString += incomingByte;            // Записываем считанный байт в массив inputString
    }
    delay(10);                             // Пауза
    Serial.println(inputString);           // Отправка в "Мониторинг порта" считанные данные
    inputString.toUpperCase();             // Меняем все буквы на заглавные
    
      if (inputString.indexOf("DATA") > -1){      // Проверяем полученные данные
        t = bme.readTemperature();
        h = bme.readHumidity(); 
        p = (bme.readPressure() / 133.3F);
        sms(String("Temperature: " + String(t) + "*C " + " Humidity: " + String(h) + " % " + " Pressure: " + String(p) + "hPa"), String("+79227905354")); // Отправка SMS  
        }     
        
    
    if (inputString.indexOf("ON1") > -1) { // Проверяем полученные данные, если ON_1 включаем реле 1
      digitalWrite(relay_1, LOW);

      if (relay1AutoOffinterval > 0) { //если включено автооключение
     {   taimes = millis(); //запоминаем, когда реле включилось
        relay1AutoOff = true; //поднимаем флаг "автоотключить реле"
      }
      sms(String("Relay 1 - ON"), String("+89227905354"));
    } // Отправка SMS
    if (inputString.indexOf("OFF1") > -1) { // Проверяем полученные данные, если OFF_1 выклюем реле 1
      digitalWrite(relay_1, HIGH);
      sms(String("Relay 1 - OFF"), String("+79227905354"));
    }// Отправка SMS
    if (inputString.indexOf("ON2") > -1) { // Проверяем полученные данные, если ON_2 включаем реле 2
      digitalWrite(relay_2, LOW);
      
      if (relay2AutoOffinterval > 0) {
        times = millis();
        relay2AutoOff = true;
      }
      sms(String("Relay 2 - ON"), String("+79227905354"));
    }  // Отправка SMS
    
      
    
    if (inputString.indexOf("OFF2") > -1) {    // Проверяем  полученные  данные, если OFF_2 выключаем реле 2
        digitalWrite(relay_2, HIGH);{
    }
{         sms(String("Relay 2 - OFF"), String("+79227905354"));
    }  // Отправка SMS
        delay(50);}
        if (inputString.indexOf("OK") == -1) {
      mySerial.println("AT+CMGDA=\"DEL ALL\"");
      delay(1000);
    }
    inputString = "";
  }
}

void sms(String text, String phone)  // Процедура Отправка SMS
{
  Serial.println("SMS send started");
  mySerial.println("AT+CMGS=\"" + phone + "\"");
  delay(500);
  mySerial.print(text);
  delay(500);
  mySerial.print((char)26);
  delay(500);
  Serial.println("SMS send complete");
  delay(2000);
}

Помогите исправит ошибки ГУРУ АРДУИНО уже сижу полдня но не как не могу исправить проблему. Не знаю правильно я дописал таймер отключения но компилятор постоянно выдает новые ошибки. Сейчас выделяет строку 131 что смс не был обьявлен в этом объеме. что то я уже совсем не могу понять в чем проблема. Если кому нетрудно подскажите что с этим делать? Заранее благодарю.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

geka0110,

Все переменные и функции должны быть объявлено до использования.

Для людей с плохим зрением - до использования.

Для блондинок - они выше должны быть объявлены, чем используются.

Вы используете свою sms в 131 строке, а объявлена она у Вас в 142.

geka0110 пишет:
что с этим делать?

Взять книгу Кернигана и Ритчи (ссылка есть в разделе "Песочница") и читать, по ходу дела запуская и разбирая все примеры. И пока этот процесс не закончится - не лезть программировать что-то ещё.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

geka0110 пишет:

 

Если кому нетрудно подскажите что с этим делать? Заранее благодарю.

Я бы Вам посоветовал отдельно разобраться с программой управления, а отдельно с СМС. Потом это объединить. Если это не скопирастенная программа, то Вам несложно будет разделить эти вещи и протестировать по отдельности. Если скопирастили - обратитесь к автору.

geka0110
Offline
Зарегистрирован: 12.02.2020

сам ты Блондинка  если такой умный взял бы и помог.

сам не хрена не шаришь только такое писать умеешь. 

Умники сидите тут а за помощью  обратился все заднюю .

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

geka0110 пишет:

сам ты Блондинка  если такой умный взял бы и помог.

сам не хрена не шаришь только такое писать умеешь. 

Умники сидите тут а за помощью  обратился все заднюю .

Вы, наверное, читаете по слогам или зациклены на ЧСВ. Вам доступно объяснили, что подпрограмму sms() надо перетащить на 39-ю строку. Вы ЭТО СДЕЛАЛИ? НЕТ? Так какого х*ра возмущаетесь?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

geka0110 пишет:

взял бы и помог.

А я что, не помог? Не объяснил? Что-то осталось непонятным?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Мда, беру назад свои слова о блондинках и извиняюсь перед ними за невольную клевету. Объяснение:

ЕвгенийП пишет:
они выше должны быть объявлены, чем используются.

Вы используете свою sms в 131 строке, а объявлена она у Вас в 142.

любая поняла бы, и исправила бы всё как надо.