Помогите новичку дописать код.

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Не соглашусь.

Life23 - пока не смотри

if (prevBtn != currBtn) {
    switch(currBtn) {
          case(HIGH): i = 0;
                break;
          case(LOW): i=1;
                break;
          default: //Hm....
   }
}
sendTextMessage(i);    

 

leshak
Offline
Зарегистрирован: 29.09.2011

Вот.... :)

Теперь, нужно пойти в раздел Программирование Ардуино | Аппаратная платформа Arduino

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

Ознакомитесь - попробуйте заменить int на что-то более экономное к памяти. То есть - в данном случае это не критично, но привыкать использовать "минимально подходящий" тип - нужно. У ардуины не так много ресурсов, что-бы разбрасываться ими не думая :)

leshak
Offline
Зарегистрирован: 29.09.2011

JollyBiber пишет:

Не соглашусь.

Не соглашайтесь. Только switch для bool переменной - это дуристика. Сам же //Hm.. написали.

У вас вышел более громоздкий, трудней читаемый, трудней модифицируемый код (а ну как нужно будет усложнить условие?)

Если очень хочется  "повыеживатся"  (Life23 - не читай, так делать - плохой стиль), то можно было вообще упаковатся в одну строку

if(prevBtn!=currBtn)sendTextMessage(!currBtn);

Но за такой код - нужно по рукам бить. Потому что тот кто будет читать его после тебя (или ты сам через годик) - мозги расплавит, что-бы понять "что тут происходит".

Так что те if-ы, в данной ситуации - самое нормальное решение.

Life23
Offline
Зарегистрирован: 10.08.2013

я уже понял ошибку.. надо было использовать byte - 8бит занимает. А int у меня сожрал целых 2байта..

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

я уже понял ошибку.. надо было использовать byte - 8бит занимает. А int у меня сожрал целых 2байта..

Верно.

А еще вы не нужно потратили  два байта int btnPin = 2; Плюс потеряли пару тактов на чтении переменных.

Помните как для светиков мы пины именовали?

#define HIGH_LED_PIN  7

Вот. И для кнопки - можно так же. Хотя признаю, что в инете, в куче примеров с маниакальным упорством пихают номера пинов в переменные, да еще в int.

 

Life23
Offline
Зарегистрирован: 10.08.2013

понял. "int btnPin = 2" удалил и заменил на #define btnPin 2

leshak
Offline
Зарегистрирован: 29.09.2011

А еще перечитайте сообщение #18 (про хорошую идею) и попробуйте угадать что мне не нравится в строке вида:

sendTextMessage(0);

 

 

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

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

ПС: на вкус и цвет все фломастеры разные, в данном конкретном примере я бы тоже не свитч использовал а иф вложенный, но тут как говорится кому что больше нравится :)

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

понял. "int btnPin = 2" удалил и заменил на #define btnPin 2

Ага. Только лучше уж однотипно это делать c RED_LED_PIN. Скажем BTN_PIN назвать.

Тогда, читая код, вы сразу будете понимать "если что-то названо целиком большими буквами" - это #define. Изменять в коде его значение - мы не можем. Если же оно "с маленькой буквы", то это переменная.

Вообщем если придерживатся такой стилистики, то не глядя в объявления всегда можно догадатся что bntPin - это переменная, а BTN_PIN - это дефан :)

Такой подход - снижает шансы на ошибку.

leshak
Offline
Зарегистрирован: 29.09.2011

JollyBiber пишет:

Это был просто пример, человек, насколь мне видится, с удовольствием учится, вот поэтому я и предлагал его.

Ага. И пусть после этого кто-то скажет что новичков пинают за незнание предмета (на форуме не одну подобную ветку найти можно).

P.S. Эх... а за название темы "помогите новичку" - все таки нужно было попинать :)

Life23
Offline
Зарегистрирован: 10.08.2013

P.S. Эх... а за название темы "помогите новичку" - все таки нужно было попинать :)

ну простите.. стыдно.. честно.. ))

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

P.S. Эх... а за название темы "помогите новичку" - все таки нужно было попинать :)

ну простите.. стыдно.. честно.. ))

Москва2042_Войнович пишет:
Перебегая  через  дорогу,  я  чуть  было  не  попал  под  огромный

парогрузовик с прицепом. Нажав на все тормоза, водитель остановил свою
огнедышащую машину и покрыл меня таким  отборным  матом,  по  которому
трудно было не узнать моего банного знакомого  Кузю.  Кажется,  он  не
собирался ограничиваться словами и уже летел ко мне с  занесенной  над
головой заводной ручкой.
    - Кузя! - закричал я ему в испуге. - Не узнаешь, что ли?
    - Ах, это ты, папаша. -  Кузя  опустил  ручку,  но  был,  кажется,

разочарован, что такой замах пропал даром. 

ж)

leshak
Offline
Зарегистрирован: 29.09.2011

Ну, так чем плоха строка

sendTextMessage(0);

догадались?

Life23
Offline
Зарегистрирован: 10.08.2013

самим "sendTextMessage"? лучше будет "smsIndex"?

Life23
Offline
Зарегистрирован: 10.08.2013

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

#define GPRS Serial
#define BTN_PIN 2
#define LOAD_PIN 4
boolean isStringMessage = false;
boolean prevBtn = LOW;
char* myStrings[]={"Knopka ON", "Knopka OFF"};

void setup()
{
    GPRS.begin(9600);
    GPRS.print("AT+CMGF=1\r");
    delay(300);
    GPRS.print("AT+IFC=1, 1\r");
    delay(300);
    GPRS.print("AT+CPBS=\"SM\"\r");
    delay(300);
    GPRS.print("AT+CNMI=1,2,2,1,0\r");
    delay(500);
}
String currStr = "";
// Переменная принимает значение True, если текущая строка является сообщением

void loop()
  { 
    boolean currBtn = digitalRead(BTN_PIN);
    if (prevBtn != currBtn && currBtn == HIGH) {
         smsIndex(0); 
     }
     if (prevBtn != currBtn && currBtn == LOW) {
         smsIndex(1);    
     }
     prevBtn = currBtn;
if (!GPRS.available())
        return;
    char currSymb = GPRS.read();    
    if ('\r' == currSymb) {
        if (isStringMessage) {
            //если текущая строка - SMS-сообщение,
            //отреагируем на него соответствующим образом
            if (!currStr.compareTo("Load on")) {
                digitalWrite(LOAD_PIN, HIGH);
            } else if (!currStr.compareTo("Load off")) {
                digitalWrite(LOAD_PIN, LOW);
            }
            isStringMessage = false;
        } else {
            if (currStr.startsWith("+CMT")) {
                //если текущая строка начинается с "+CMT",
                //то следующая строка является сообщением
                isStringMessage = true;
            }
        }
        currStr = "";
    } else if ('\n' != currSymb) {
        currStr += String(currSymb);
    }
}
void smsIndex(byte sms) {
// Устанавливает текстовый режим для SMS-сообщений
    GPRS.print("AT+CMGF=1\r");
    delay(100); // даём время на усваивание команды
    GPRS.println("AT + CMGS = \"+79************\"");
    delay(100);
    GPRS.println(myStrings[sms]);
    delay(100);
    // Отправляем Ctrl+Z, обозначая, что сообщение готово
    GPRS.println((char)26);
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

самим "sendTextMessage"? лучше будет "smsIndex"?

Нет. Я же намекал что перечитат нужно :)

leshak пишет:
А сама идея - хорошая. analogRead(2) - фиг его пойми что значит, а когда читаешь analogRead(btnPin) - сразу понимаеш что это чтение состояние кнопки :)

Вот если постороннему человеку (или вам через пол года) показать строку

sendMessage(0)

или

sendMessage(MSG_IDX_SUCСESS);

В каком варианте человек быстрее догадается, что эта строчка делает?

PS:

MSG - message - сообщение
IDX - index - индекс
SUCCESS - удачно, подтверждение...

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

что называеться "слИпил" коды.. Страшно вылаживать, 

Значит - рано :) Не дополировали еще "детали" что-бы они легко и без напряга друг к другу подошли :)

Может даже работать будет (не вникал), но... вникать-то тяжело. "Не причесан" еще код. Когда "дочешем его" - тогда и "как слепить" - будет самоочевидно :)

Да и более "устойчивым к разным ситуациям" он, я думаю, станет :)

Если конечно хотите тратить на это время ;)

Life23
Offline
Зарегистрирован: 10.08.2013

Компилятор проглотил.. но очивидно, что работает на так как надо.. )) 

поставил задачу - хочу дойти до конца! )

буду "чесать" (с Вашей, конечно же, помощью)! 

Life23
Offline
Зарегистрирован: 10.08.2013

я правильно Вас понял: надо ввести еще значения функции, да бы подтвердить какое именно сообщение отправленно и отправленно ли оно? 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

я правильно Вас понял: надо ввести еще значения функции, да бы подтвердить какое именно сообщение отправленно и отправленно ли оно? 

Тут уже я вас не понял :(

Нет. Я иммел ввиду, что "любые цифры в коде" - это зло. "Голая цифра" - ничего не говорит читающему  (в большинстве случаев, в любом правиле есть исключения). Даже термин есть устоявшийся "магические цифры". Это какие-то цифры, забитые прямо в код. С этими цифрами код работает. Но почему эти цифры именно такие - понятно только автору. Поэтому они "магические". Наличие таких цифр - признак кода который тяжело сопровождать. 

Поэтому, где возможно - стараемся заменять их константами/дефайнами

Вместо 

sendMessage(0);
....
sendMessage(1);

пишем

sendMessage(MSG_IDX_BUTTON_PRESSED);
....
sendMessage(MSG_IDX_BUTTON_RELEASE);

И где-то вверху объявляем

#define MSG_IDX_BUTTON_PRESSED 0
#define MSG_IDX_BUTTON_RELEASED 1

С точки зрения компилятора - это вообще идентичные скетчи.
С точки зрения читающего - "О... сразу понятно что послали сообщение о том что кнопку нажали/отпустили".
С нашей точки зрения - чуть-чуть больше кода набрать нужно :)  Но IMHO оно того стоит. Не велика расплата за читабельный код (потом не раз себя похвалим).

 

Life23
Offline
Зарегистрирован: 10.08.2013

тьфу.. а я уже полез в высшую материю..

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

Исправил все замечания:

#define GPRS Serial
#define BTN_PIN 2
#define MSG_IDX_BUTTON_PRESSED 0
#define MSG_IDX_BUTTON_RELEASED 1
boolean prevBtn = LOW;
char* myStrings[]={"BUTTON_PRESSED", "BUTTON_RELEASED"};
void setup()
{
    GPRS.begin(9600);
    delay(100);
    GPRS.println("AT+CMGF=1\r");
    delay(300);
    GPRS.println("AT+IFC=1, 1\r");
    delay(300);
    GPRS.println("AT+CPBS=\"SM\"\r");
    delay(300);
    GPRS.println("AT+CNMI=1,2,2,1,0\r");
    delay(500);
}
void loop()
  { 
    boolean currBtn = digitalRead(BTN_PIN);
    if (prevBtn != currBtn && currBtn == HIGH) {
         sendTextMessage(MSG_IDX_BUTTON_PRESSED); 
     }
     if (prevBtn != currBtn && currBtn == LOW) {
       sendTextMessage(MSG_IDX_BUTTON_RELEASED);    
     }
     prevBtn = currBtn;
}
void sendTextMessage(byte sms) {
    GPRS.print("AT+CMGF=1\r");
    delay(100); 
    GPRS.println("AT + CMGS = \"+79************\"");
    delay(100);
    GPRS.println(myStrings[sms]);
    delay(100);
    GPRS.println((char)26);
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Здорово.

Ну сами строки (текст sms) - можно было и оставить "Knopka On", "Knopka Off". Это уже "то что видит человек", поэтому главное - чтобы пользователю было понятно. А обычному человеку подчеркивание между словами - немного не привычно. Не програмеру - привычней видеть пробелы :)   (вот... уже и у вас мозги начинаю на програмерски бекрень наклонятся :)

А теперь. Вы наверное решите что я издеваюсь :) - выкидываем эти MSG_IDX_BUTTON_PRESSED и MSG_IDX_BUTTON_RELEASED  ;)

Просто нужно было показать, что "магические цифры" - зло. И как с ними бороться. Использование #define для этого - обычная практика.

Но... если отказатся от дополнительного массива myStrings[] (тоже, кстати, слишком общное название. ничего не говорящие, что-то типа smsMessages[] - было бы удачней), так вот... если мы вообще от него откажемся, то у нас вообще отпадет надобность в цифрах. Исчезнет массив - исчезнут индексы, не будет индексов - не нужны цифры, не нужны цифры - нечего заменить на #define-ны :)

Вы говорили что "параметры функции" уже не так страшно :)

Давайте попробуем переделать функци sendTextMessage() что-бы она принимала не индекс, а строку. Что-бы параметр был не цифровой, а текстовый.

Что-бы пользоватся ей можно было так:

sendMessage("Hello from Arduino");

Подсказка: нужно поменять тип параметра функции с int на.... что-то очень похожие на то как вы объявляли массив myStrings[]. Только там вы объявили "массив строк", а тут вам нужно, даже проще "объявить строку". Параметр типа строка, а не int как сейчас

Life23
Offline
Зарегистрирован: 10.08.2013
#define GPRS Serial
#define BTN_PIN 2
boolean prevBtn = LOW;
void setup()
{
    GPRS.begin(9600);
    delay(100);
    GPRS.println("AT+CMGF=1\r");
    delay(300);
    GPRS.println("AT+IFC=1, 1\r");
    delay(300);
    GPRS.println("AT+CPBS=\"SM\"\r");
    delay(300);
    GPRS.println("AT+CNMI=1,2,2,1,0\r");
    delay(500);
}
void loop()
  { 
    boolean currBtn = digitalRead(BTN_PIN);
    if (prevBtn != currBtn && currBtn == HIGH) {
       sendTextMessage("knopka on"); 
     }
     if (prevBtn != currBtn && currBtn == LOW) {
       sendTextMessage("knopka off");    
     }
     prevBtn = currBtn;
}
void sendTextMessage(String sms ) {
    GPRS.print("AT+CMGF=1\r");
    delay(100); 
    GPRS.println("AT + CMGS = \"+79************\"");
    delay(100);
    GPRS.println(sms);
    delay(100);
    GPRS.println((char)26);
}

с гордо подной головой пошел спать.. ))))

update: еще раз перечитав ветку, понял, что это можно было сделать уже давно.. и намеки были неоднократно.. 

простите - я только учусь (с) ))

p.s.: к стати, хоть легче стало читать код "обычному человеку", а размер скетча в двоичном коде от предыдущего вырос почти в два раза...

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:
с гордо подной головой пошел спать.. ))))
Гордость заслужена :)
Life23 пишет:
p.s.: к стати, хоть легче стало читать код "обычному человеку", а размер скетча в двоичном коде от предыдущего вырос почти в два раза...

Ну, во первых тут уже вопрос "приоритетов". Что лучше купить камень более жирный (который стоит на $0.5 дороже, а иногда даже дешевле) или потратить неделю на ковыряния "в кошмаре нечитабельном".

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

В третьих. Код у вас вырос не потому что вы добавили ему читабельности. А потому что.... тип параметра не удачно выбрали :)

Я же вам писал подсказку "тип параметра должен бы очень похожим на то как вы объявляли myStrings[]". Был там тип String?  Небыло. А тут - появился :)  Вот он вам и насрал в кашу.

Выше по ветке я советовал вам прочитать раздел доки "Типы данных" целиком (если читали - еще раз. я сам регулярно почитываю ;)  Там вы увидите что у нас есть два вида строк.

string и String

Вы воспользовались String. Это чисто "Ардуина фишка". В стардатных сях таких строк нет. Не хочу глубоко вдаватся, тема холиварная, но лично мое мнение что в микроконтроллерах String - это зло. На одну бяку (размер) - вы уже наступили. А есть еще :)

А нужно было воспользоватся стандартными c-шными строками. string.  Поменяйте объявление параметра функции на  "char* sms" или "char sms[]" и посмотрите на размер скетча. Будете приятно удивлены :)

Вообщем мой вам совет: не ведитесь на простоту использования String. Осваивайте обычные сишный строки. Никуда вы от них не денетесь. Все равно везде где что-то сложней 10-ти строк - будете на них натыкатся.

Тема сишных строк внешне простая, но ... имеет подводные камни. В рамках форума - с ними не просто разобратся досконально. Так что советую взять учебничек (а лучше несколько) по C/C++  и покопатся в этих разделах (там  еще на указатели потребуется помедитировать). Вообщем "строки/указатели" - потребуют какоего времени что-бы стать с ними "на Ты". Но это будет полезно потраченное время. Вообщем "задание на каникулы"  вам:)

 

 

Life23
Offline
Зарегистрирован: 10.08.2013

хм.. действительно код уменьшился в размере с char... :)

я  и начинал как раз с char. но не хватило ума потом как правильно он объявляеться в "GPRS.println(sms);" (полез в дебри), а на сомом деле все просто. Да еще и лишнюю скобку втулил.. сбился с толку совсем.. За то теперь буду знать что такое String. ))))

раздел доки "Типы данных" я постоянно перечитываю.. но пока не попробуешь - бесполезно.. практика великое дело..

leshak
Offline
Зарегистрирован: 29.09.2011

OK. Поехали дальше :)

Что плохого в строке

GPRS.println("AT + CMGS = \"+79************\"");

???

Подсказка: а не нужно ли ее разбить на три GPRTS.print  и..... ?

Life23
Offline
Зарегистрирован: 10.08.2013


GPRS.print("AT+CMGS="); 
GPRS.print(NumTel); 
GPRS.print((char)13); 

 

и 
#define NumTel "+79************"

универсальность.. епт... ))))хотя лучше наверно так:

#define NumTel +79************



GPRS.print("AT+CMGS=");
    GPRS.print((char)34); 
    GPRS.print(NumTel); 
    GPRS.print((char)34);
    GPRS.print((char)13); 

 

?

leshak
Offline
Зарегистрирован: 29.09.2011

Ну, очень близко. Только не скомпилируется :)

Явно не до конца поняли что такое define.  (и правило именовайние - нарушили).

Что происходит с #defin-ом. В момент компиляции (важно, не в момент исполнения), даже "до компиляции", компилятор ищет по коду этот NumTel, если где-то находит - подставляет его туда и потом компилирует. Именно поэтому, с точки зрения компилятора

#define SOME_NAME 123
...
Serial.print(SOME_NAME);

 то же самое что и

Serial.print(123);

Оба варианта - скомпилируются, с точностью до байта в одно и тоже. Просто потому, что компилятор первый вариант, незаметно, преобразует во второй.

Так что "для человека" - это разный код. Для компилятора - тот же самый :)

Темперь смотрим, что у вас получилось. С вашим дефайном. КОгда он подставиться, то вы выйдет

GPRS.print(+79************)

Что не очень хорошо. Не дай бог в номере попадутся дефисы, круглые скобки.... получим кучу ругани что "цифра какая-то не непонятная". Поэтому мы должны делать print, не числа, а строки (как и было в изначальном). То есть - нам нужно, что-бы, после подстановки #define получилось что-то типа:

GPRS.print("+79********");

Тогда это строка, а в строке могут быть и дефисы и проч. Что хотим. Компилятор ругатся не будет.

Вообщем - потеряли вы кавычки. Ваш дефайн должен был выглядить так:

#define NUM_TEL "+79*******"

Но... не буду счас сильно вдаватся почему (память/указатели и т.п.), но в отличие от цифр, строки, особенно длинные лучше выносить в конфигурацию не с помощью #define, а с помощою обычных переменых. Ну просто такое правило :)   Номера пинов, время задержки и проч. - объевляем #define-нами, строки - переменными.

Итого

char numTel[]="+78*************";

.......
GPRS.print(numTel);

Поехали далее. 

GPRS.print("AT+CMGS=");
GPRS.print((char)34);

Будет рабоать, но ... изначально же было сделано через экранивание двойной кавычки. Кто там помнит что такое 34? Поэтому одна строка

GPRS.print("AT+GMGS=\"");

вместо двух - будет лучше выглядить (косая черта означает "двойная ковычка это именно символ двойной кавычки, а не признак конца строки". Мы ее "заэкранировали" с помощью косой черты. Сказали компилятору что "следующий символ" - просто символ. Не пытайся найти в нем смысл :)

То же самое и с завершением.

Вообщем, вся эта лабуда у нас в итоге будет примерно такой

char numTel[]="+78*************";

......

GPRS.print("AT + CMGS = \""); GPRS.print(NumTel); GPRS.println("\"");

 

leshak
Offline
Зарегистрирован: 29.09.2011

Кстати, если уже хотелось, то вместо

    GPRS.print((char)34);

Можно писать

   GPRS.write(34);

 

Life23
Offline
Зарегистрирован: 10.08.2013

"въехал.." и за нотировал ваш пост себе в блокнотик ) 

но почему-то оба моих варианта компилировались без проблем.

в первом варианте, в #define я ж в скобки вписал номер телефона ))))) а вот без скобок компилятор меня послал )))

все это - зло! )) я уже понял )

а вот косая в (CMGS = \"+79************\"") меня и сбила с толку.. теперь буду знать о ней..

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

"въехал.." и за нотировал ваш пост себе в блокнотик ) 

но почему-то оба моих варианта компилировались без проблем.

в первом варианте, в #define я ж в скобки вписал номер телефона ))))) а вот без скобок компилятор меня послал )))

все это - зло! )) я уже понял )

а вот косая в (CMGS = \"+79************\"") меня и сбила с толку.. теперь буду знать о ней..

 

Во первых не "скобки", а "двойные кавычки" :) Во вторых, не послать, без кавычек, он мог только если номер действительно был только из цифр и знака плюс.

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

а вот косая в (CMGS = \"+79************\"") меня и сбила с толку.. теперь буду знать о ней..

 

Кстати тут вы потеряли открывающую двойную кавычку. Как компилятор поймет где строка начинается?

leshak
Offline
Зарегистрирован: 29.09.2011

OK. Давайте текущий скетч что у вас получился. 

И даже не найдетесь что я не смогу придумать что в нем еще можно улучшить :)

Life23
Offline
Зарегистрирован: 10.08.2013

leshak пишет:

И даже не найдетесь что я не смогу придумать что в нем еще можно улучшить :)

"-а может быть не надо!?" (с), ))

 

#define GPRS Serial
#define BTN_PIN 2
boolean prevBtn = LOW;
char NUM_TEL[]="+78*************";

void setup()
{
    GPRS.begin(9600);
    delay(100);
    GPRS.println("AT+CMGF=1\r");
    delay(300);
    GPRS.println("AT+IFC=1, 1\r"); //data flow controll(1, Программный режим (XON/XOFF)).
    delay(300);
    GPRS.println("AT+CPBS=\"SM\"\r"); //выбрать как основную память сим-карту. Данный режим стоит по умолчанию.
    delay(300);
    GPRS.println("AT+CNMI=1,2,2,1,0\r");
    delay(500);
}
void loop()
  { 
    boolean currBtn = digitalRead(BTN_PIN);
    if (prevBtn != currBtn && currBtn == HIGH) {
       sendTextMessage("knopka on"); 
     }
     if (prevBtn != currBtn && currBtn == LOW) {
       sendTextMessage("knopka off");    
     }
     prevBtn = currBtn;
}
void sendTextMessage(char* sms) { 
    GPRS.print("AT+CMGS=\""); 
    GPRS.print(NUM_TEL); 
    GPRS.println("\"");
    GPRS.print(sms);
    GPRS.println((char)26);
}

 

leshak
Offline
Зарегистрирован: 29.09.2011

Надо :)

Ну во первых я бы порекомендовал, между строчками 33 и 34 вставить пустую строку. "Отбить их визуально". Что-бы четче было видно что 31,32,33 - формируют одну строку. Ну или вообще написать их "в одну строчку", как я сделал в #78 в конце. Но это даже "на замечание" не тянет. Скорее "дело вкуса".

OK. По "замечаниям". Давайте вы еще раз прочитаете Serial.print и Serial.println . Вникните в их различие. И что они делают. Особенно println. А потом посмотрите свой код... и подумаем, а зачем там в строках символ \r? Причем попадается он и в print и в println. Он там действительно нужен? Если нет - убрать, если нужен - единообразить все. А то "то так, то этак" :)

Life23
Offline
Зарегистрирован: 10.08.2013

"Отбить их визуально".. АААААаа... я чуть было не грохнул ардуинку о пол.. )) но вовремя прочитал: Но это даже "на замечание" не тянет. Скорее "дело вкуса".)))))

Serial.println - уже автоматически вставляет \r

p.s. Без dealy() в строках 31-35 - ох как он начал строчит дребезг в serial... (( 

leshak
Offline
Зарегистрирован: 29.09.2011

Кстати - молодец что начал коментировать то что "может забыться" и не очевидно и с кода. Я про коменты к строкам (12), ( 14)

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

"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете." (С) Стив Макконнелл



// ***********  Настройки *************

// подключение
#define BTN_PIN 2

char NUM_TEL[]="+78*************"; // телефон куда будет отсылаться SMS 
#define GPRS Serial

// *********  /Настройки ***************

boolean prevBtn = LOW;

 

leshak
Offline
Зарегистрирован: 29.09.2011

OK. Считаем что с println и \r вы разобрались и сами поправите.

Едем дальше.

Что-то у нас с задержками после отсылки команды - полная чехарада. То 100, то 300, то 500. То исчезают, то появляются :) Да и каждый раз писать по две строчки - лениво же :) 

Давайте тоже это дело единообразим. Сделаем вспомогательную функцию, которая будет отсылать команду gprs-су, и ждать пока он ее выполнит

void gprsCmdAndWait(char* commandText){
   GPRS.println(commandText);
   delay(100);
}

И сразу, много где мы сможем вместо двух строчек написать одну :)

P.S. Пока необходимость выделять это в отделную функцию "притянута за уши". Две строки - можно было-бы и потерпеть, но.... чуть позже увидим что "выносить в отдельную функцию" - таки было нужно.

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

"Отбить их визуально".. АААААаа... я чуть было не грохнул ардуинку о пол.. )) 

Ну вы уже поняли, что "код не пишется", код "выращивается" :) Кстати вот так "то тут поправили, то там", потом "чуток дописали нового функционала абы работал", потом опять "логику не меняем, но делаем что-бы код красивым был" (рефакторинг - название этого процесса). Так вот - это обычный процесс разработки. Не только у новичков.

Если совсем не вмоготу - можете вот тут пар выпустить  Помогите новичку! | Аппаратная платформа Arduino :)

А вообще - что же вы хотите? ;) За пару дней - тут у вас неплохой такой "интенсивчик" получился. Не каждый бы выдержал. В институте - этот объем легко бы на семестр растянулся. Конечно тут мы "сильно по верхам" пробежали, но круг новых понятий - не такой уж и маленький получился.

Life23
Offline
Зарегистрирован: 10.08.2013

Немного оклемавшись от вскрытия моей машины какими-то уродами - иду дальше:

#define GPRS Serial 
#define BTN_PIN 2 
boolean prevBtn = LOW;
char NUM_TEL[]="+78*************";

void setup()
{
    GPRS.begin(9600);
    gprsCmdAndWait("AT+CMGF=1"); //выбор режима sms: 1-текстовый,0-цифровой)
    gprsCmdAndWait("AT+IFC=1, 1");//data flow controll(1, Программный режим (XON/XOFF)).
    gprsCmdAndWait("AT+CPBS=\"SM\"");//выбрать как основную память сим-карту. Данный режим стоит по умолчанию.
    gprsCmdAndWait("AT+CNMI=1,2,2,1,0"); //информирование о новом смс.. одним словом "хрень" - пока не трогаю.. 
}
void loop()
  { 
    boolean currBtn = digitalRead(BTN_PIN);
    if (prevBtn != currBtn && currBtn == HIGH) {
       sendTextMessage("knopka on"); 
     }
     if (prevBtn != currBtn && currBtn == LOW) {
       sendTextMessage("knopka off");    
     }
     prevBtn = currBtn;
}
void sendTextMessage(char* sms) { 
    GPRS.print("AT+CMGS=\""); 
    GPRS.print(NUM_TEL); 
    GPRS.println("\"");
    delay(100);
    GPRS.print(sms);
    delay(100);
    GPRS.println((char)26);
}
void gprsCmdAndWait(char* commandText){
   GPRS.println(commandText);
   delay(300);
}

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

p.s.: по поводу "Программный режим (XON/XOFF)" читал много, что не стоит его использовать так как он не идеален и много с ним проблем. Глубоко не вникал - чисто для размышления..

 

leshak
Offline
Зарегистрирован: 29.09.2011

Сорри. Ближе к вечеру смогу уделить время.

А так: правда setup() стал симпатичней? ;) еще бы и в sendTextMessage тоже применить это gprsCmdAndWait() :)

Правда там так идеально он уже не применится и чуток мозг похмурить нужно будет что-бы его воткнуть (вообщем если не получится - покажу, только чуть позже). Но, кроме красоты, применение специальной функции для отсылки любой команды на gprs (и никогда не послать в ее обход) дает нам еще одно преимущество: если нам потребуется какая-то дополнительная логика при посылке команд (например "мигнуть диодом при этом", показать что ардуино что-то командует шилду) то это изменение можно внести в одно место. в функцию gprsCmdAndWait(), а не по всему коду лазить и искать где у нас команды посылаются.

Вообщем попробуйте, если сразу не получится - подождите до вечера :)

 

leshak
Offline
Зарегистрирован: 29.09.2011

И еще... можно уже, потихоньку, начинать готовить это скетч к тому что он будет "интегрироватся с другим скетчем" (преставим что пока не знаем с каким именно).

Как вы уже поняли интеграция двух скетчей сводится, грубо говоря, к слиянию функций setup() и loop(). В идеальном случае какие setup() и loop() проще всего сливать?   Конечно полностю пустые :)   К сожалению - мы живем в реальном мире и пустые setup() и loop() - у нас небудут никогда. Но стремится к совершенству - нужно :) Пойдем на компромис. Постараемся их сделать если не пустыми, то хотя-бы как можно более тривиальными.  Для этого всю "развесистую" логику мы вынесем в отдельные функции (причем еще и имена подберем так, что-бы сразу было понятно что делает эта "Развесистая логика") и будет из loop() и setup() вызывать эти функции.

Что-нибудь поняли из этого потока сознания? ;)   Если нет, тогда более конкретно:

Все что сейчас в setup() - выносим в функцию с именем скажем initGPRS(); , вче что сейчас в loop() - выносим в функцию onChangedButtonSendSms();

Что-бы наши setup() и loop(), в итоге приняли вид:

setup(){
   initGPRS(); 
}

loop(){
  onChangedButtonSendSms();
}

Глянув на такие setup() и loop() сразу можно понять, в общих чертах, чем же занимается скетч. Даже если нет никаких комментариев :)

Life23
Offline
Зарегистрирован: 10.08.2013

да, действительно, setup стал на много проще.

Именно такого ответа я и боялся..  )) потому как сначала хотел применить данный финт с sendTextMessage ))) много полезло багов.. 

Попробую. До вечера еще время есть ) все таки когда сам до чего-то до шел - легче запомнить.. и понимаешь лучше - что к чему..

"мигнуть диодом при этом" - вполне полезная штука! визуальное оповещение работы устройства никогда не помешает.. не говоря уже о применение в других устройствах.

 

 

leshak
Offline
Зарегистрирован: 29.09.2011

И еще чуть-чуть забегу вперед. Что-бы дальнеший ход мысли было легче понять.

Вопрос на засыпку:  почему currBtn мы объявляли внутри функции, а prevBtn вверху скетча? Это просто "неакуратность" или "так и нужно"?  Появляется ли из-за этого какая-то разница в поведении/свойствах этих двух переменных? Или можно объявлять где удобней автору?

Life23
Offline
Зарегистрирован: 10.08.2013

теперь действительно понимаю значимость функции.. в loop теперь на много проще оперировать ими.. 

#define GPRS Serial 
#define BTN_PIN 2 
boolean prevBtn = LOW;
boolean currBtn;
char NUM_TEL[]="+78*************";

void setup(){
initGPRS(); //вызов функции инициализации GPRS 
}

void loop()
  { 
    onChangedButtonSendSms(); // вызов Функции проверки  изменения кнопки

}
void sendTextMessage(char* sms) { // Функция отправки SMS 
    gprsCmdAndWait("AT+CMGS=\"");
    gprsCmdAndWait(NUM_TEL);
    gprsCmdAndWait("\"\n");
    gprsCmdAndWait(sms);
    gprsCmdAndWait("\n");
}
void gprsCmdAndWait(char* commandText){ //Функция отправки текста в Serial
   GPRS.print(commandText);
   delay(300);
}
void initGPRS(){ // функция инициализации GPRS
    GPRS.begin(9600);
    gprsCmdAndWait("AT+CMGF=1\n"); //выбор режима sms: 1-текстовый,0-цифровой)
    gprsCmdAndWait("AT+IFC=1, 1\n");//data flow controll(1, Программный режим (XON/XOFF)).
    gprsCmdAndWait("AT+CPBS=\"SM\"\n");//выбрать как основную память сим-карту. Данный режим стоит по умолчанию.
    gprsCmdAndWait("AT+CNMI=1,2,2,1,0\n"); //информирование о новом смс.. одним словом "хрень" - пока не трогаю.. 
}

void onChangedButtonSendSms(){ //Функция проверки состояния кнопки
    currBtn = digitalRead(BTN_PIN);
    if (prevBtn != currBtn && currBtn == HIGH) {
       sendTextMessage("knopka on"); 
     }
     if (prevBtn != currBtn && currBtn == LOW) {
       sendTextMessage("knopka off");    
     }
     prevBtn = currBtn;
}

может где то и "натупил"  - поправьте.. 

по поводу "boolean currBtn;" - еще в самом начале возник вопрос: почему он в начале не объявлен? 

на "вопрос на засыпку" пока не могу ответить..  но пока думаю, а почему бы и не объявить ее в начале, для читабельности кода? Но понимаю что тут есть какой-то подвох...))  "Курю букварь" ))

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

на "вопрос на засыпку" пока не могу ответить..  но пока думаю, а почему бы и не объявить ее в начале, для читабельности кода? Но понимаю что тут есть какой-то подвох...))  "Курю букварь" ))

Да. Это правильное действие. На всякий случай вот: Переменные | Аппаратная платформа Arduino

И еще, в каких-нибудь книгах по C/C++ аналогичный раздел почитать.

"Почему-бы не объвить в начале" говорите?   А чем тут читабельность повысится? Используется в одном месте, объвлена в другом. Читаешь функцию и нужно скролить куда-то вверх что-бы посмотреть тип переменной.

Конфигурацию/настройку мы выносиви вверх, что-бы тот кто откроет скетч - сразу мог увидеть/поправить то что автор скетча задумывал как "настроеченые параметры". А prevBtn - это явно для "внутреней кухни" (поэтому, я вам и говорил что "плохо что она затеслась среди #defint BUTTON_PIN и проч.". Нужно хотя-бы переносами строк ее от "конфигурации" отделить.

Плюс к этому.... вот будете вы копировать функцию куда-то  в другой скетч. Вам нужно будет и функцию скопировать, а потом лезть и отдельно копировать все переменные которые она использует. Если же все переменные "внутри функции", то вы "все что нужно" сможете взять одним копи-пастом.

К тому же... а вдруг, тот второй скетч тоже использует переменную с именем currBtn? нужно будет менять имена, что-бы не мешали друг-другу (или вы еще одну подобную функцию напишите, для другой кнопки). Вообщем нужно по возможности избегать "засорения" общего пространства имен. Лучше когда все нужные переменные - внутри функции. Тогда "за пределами функции" - они никому мешать не будут.

Да и функции которые не имеют "побочных эффектов" - легче отлаживать и писать. Функции без побочных эффектов - это функции поведение которых зависит только от того какие параметры вы им подали на вход. Не зависят от состояния других объектов, и не меняют состояния других объектов. Типичный пример

function float x2(float x){
  return x*x;
}

Сколько ее не вызывай с параметром x2(3) - всегда будет возвращать 9 :) Вне зависимости от состояния остального мира. Понятно, что такие "феншуйные функции" не всегда можно использовать (например если мы работаем с Serial, то хотим мы того или нет - а мы зависим от его состояния), но если возможно - нужно стремится к этому.

Ладно... это что-то меня уже в область функционального програмирование занесло...

Возвращаясь к нашим баранам, для читабельности и красивости правильней было бы задать вопрос "а почему-бы не перенести объявление prevBtn" тоже внутрь функции? Зачем его "потащили вверх", зачем потребовалась отходить от идеальности? Почему ее не объявили так же как и currBtn?

P.S. И между функциями. Хотя-бы по одной строке пустой вставте. Что-бы не сливались. Чай не на бумаге пишем. Леса экономить нам не нужно.

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

теперь действительно понимаю значимость функции.. в loop теперь на много проще оперировать ими.. 

Ну вообщем да. Мы как бы разбиваем задачу на самодостаточные блоки. Стараемся что бы "каждая функция" решала одну задачу. Что-бы ее использование - было очевидно. Отлаживаем их "по отдельности".

Скажем, свою sendTextMessage() мы уже можем, если потребуется, вынести в отедльный упрощенный скетч:

#define GPRS Serial

setup(){
  GPRS.begin(9600);
  sendTextMessage("Test message 1");
  delay(1000);
  sendTextMessage("Test message 2");
}

void loop(){
}

void sendTextMessage(char* sms){
....
}

поизгалятся/отладить ее отдельно. И для этого нам даже кнопки не нужны :) После чего, уже обновленный вариант вернуть в "основной скетч".

Когда мы вот эти блоки - отладили. Мы решаем нашу изначальную задачу уже методом "крупно-узловой сборки" :) Мелкие функции - объединяем в более крупные блоки и т.п.

 

leshak
Offline
Зарегистрирован: 29.09.2011

А если нам нужно "посмотреть под микроскопом" на функцию onChangedButtonSendSms,

тоже можем вынести ее в отдельный скетч. Сделать "заглушку", для фунции sendTextMessage() - сделать ее временно "максимально упрощенную версию", достаточную для понимания "правильную инфу в нее послала onChangeButtonSendSms или нет"

#define BTN_PIN 2

void setup(){
   Serial.begin(9600);
}

void loop(){
  onChangeButtonSendSms();
}

void onChangeButtonSendSms(){ // функция поведение которой исследуем
  .....
}

// отладочная  "функция заглушка"
void sendTextMessage(char* sms){
   Serial.print(millis()); // выведем время когда функция была вызвана, что-бы лог было легче читать
   Serial.print(": sendTextMessage called:"); // что-бы было понятно какая функция была вызвана
   Serial.println(sms); // и с каким параметром
}

Понятно, что это не "постоянно нужно так делать" (выносить функции в отдельный скетч). Это просто один из "приемов" в процессе написания отладки.

Life23
Offline
Зарегистрирован: 10.08.2013
boolean prevBtn = LOW;

мы не можем вписать в функцию, так как в данном случае мы определяем первоначально состояние кнопки при запуске "железа"

и к тому же в конце функции у нас:

prevBtn = currBtn;

 

leshak
Offline
Зарегистрирован: 29.09.2011

Life23 пишет:

теперь действительно понимаю значимость функции.. в loop теперь на много проще оперировать ими.. 

#define GPRS Serial 
#define BTN_PIN 2 
boolean prevBtn = LOW;
boolean currBtn;
char NUM_TEL[]="+78*************";

void setup(){
initGPRS(); //вызов функции инициализации GPRS 
}

void loop()
  { 
    onChangedButtonSendSms(); // вызов Функции проверки  изменения кнопки

}
void sendTextMessage(char* sms) { // Функция отправки SMS 
    gprsCmdAndWait("AT+CMGS=\"");
    gprsCmdAndWait(NUM_TEL);
    gprsCmdAndWait("\"\n");
    gprsCmdAndWait(sms);
    gprsCmdAndWait("\n");
}
void gprsCmdAndWait(char* commandText){ //Функция отправки текста в Serial
   GPRS.print(commandText);
   delay(300);
}
void initGPRS(){ // функция инициализации GPRS
    GPRS.begin(9600);
    gprsCmdAndWait("AT+CMGF=1\n"); //выбор режима sms: 1-текстовый,0-цифровой)
    gprsCmdAndWait("AT+IFC=1, 1\n");//data flow controll(1, Программный режим (XON/XOFF)).
    gprsCmdAndWait("AT+CPBS=\"SM\"\n");//выбрать как основную память сим-карту. Данный режим стоит по умолчанию.
    gprsCmdAndWait("AT+CNMI=1,2,2,1,0\n"); //информирование о новом смс.. одним словом "хрень" - пока не трогаю.. 
}


может где то и "натупил"  - поправьте.. 

Да не... "тупостью" это не назовешь. Обычный процесс разбирательства.

Ну во первых... а где же здоровая лень?  Если вас не устраивало, что gprsCmdAndWait всегда посылало в конце переводы строки, а вам хочется "когда посылать, а когда и нет". То.. можно было либо ввести ей второй параметр, который будет указыать нужно ли переводит строку, либо сделать две функции

gprsCmdAndWait(char* sms){
  GPRS.print(sms);
  delay(100);
}

gprsCmdAndWaitln(char* sms){ // то же самое, только с переводом строки
  GPRS.println(sms)
  delay(100:
}

И пользоватся ими "по необходимости". Возможно так было-бы лучше, чем во всех строках добалять \n ... хотя... две похожие функции - тоже не симпатично (но что-то не приходит в голову как тут улучшить). Вообщем считайте это не "так было лучше", а "можно было еще и так" :)

К тому же.... я вот говорил вам, что "отделите пустой строкой в коде" строки

GPRS.print("AT+CMGS=\"");
GPRS.print(NUM_TEL);
GPRS.println("\"");
 delay(100);

Что-бы было видно, что это формирование "одной строки" команды. А не "несколько команд". Тогда бы вы увидели, что нам не нужно "сворачивать" все в использование gprsCmdAndWait() . Так как это даст нам лишние задержки посреди процесса отправки команды. Зачем? Воспринимайте что gprsCmdAndWait() - это "завершение команды и ожидание ее выполнения". Тогда станет ясно, что с помощью нею, можно было заменять (что-бы логика не поменялась) только последние две строки.

GPRS.print("AT+CMGS=\"");
GPRS.print(NUM_TEL);
gprsCmdAndWait("\"");

В этом случае у нас не поменялась логика по сравнению с оригинальной. Не пришлось переделывать саму gprsCmdAndWait (в ней так и остался GPRS.println), пожелание "все команды в итоге проходят через gprsCmdAndWait() - тоже выполнилось. Мы просто... часть команды подалить "чуть раньше", помогли gprsCmdAndWait(). Но, в итоге - все равно сделали ее вызов.

Но вы "зацепили", по сравнению с оригинальной логикой. Еще одну важную вещь (я так думаю, точно не скажу так как не знаю что у вас за шилд и какие там команды). В оригинале признаком окончания текста sms-ски была посылка символа с десятичным кодом 26. А теперь, в конце sms-ски вы послаете \n. Опасаюсь что шилд не поймет этого. Подумает что просто вы хотите написать вторую строку sms-ски :(

А что же делать, для 26 - нет специально \БУКВА, но есть выход :) Если нам в строке нужно использовать символ с каким-то кодом и код мы знаем, то мы можем написать gprsCmdAndWait("\xNN"); где NN - код символа в шестнадцатеричном виде. Возмем к примеру символ новой строки \n и представим себе что у него нет специальной буквы :)  Не страшно, заглядывам в http://www.asciitable.com/ и видим что его десятичный код 10  (то есть можно было написать char(10) и это было-бы то же самое) или раз нам нужно в строке использовать, то смотрим во вторую колонку берем шестнадцатеричное значение и пишем "\x0A";

Так что... нам нужно взять 26, перевести его в шестнадцатеричное значение и вставить с помощью \x...   Можно через ссылку-табличку выше, а можно.... Взять стандартный калькулятор виндовса :) В нем нажать ALT-3 (или поййти в меню View/Programmer) - переключить его в програмерский режим.

После чере переключаемся в Dec, набираем наше число 26, переключаемся в Hex - и видим его 16-тиричное значение.

Кстати ниже - есть еще вариант Bin - это если нам вдруг захотелось в двоичном виде его увидеть.

А есть еще и третий вариант :)  Вдруг нет у нас калькулятора :)  Но есть ардуина :)  А про функцию print, мы уже читали внимательно. Тогда:

void setup(){
  Serial.begin(9600);
  byte num=26;
  
  Serial.print("Dec=");Serial.println(num,DEC);
  Serial.print("Hex=");Serial.println(num,HEX);
  Serial.print("Bin=");Serial.println(num,BIN);
}

void loop(){}

И смотрим в Serial Monitor Ж)