мы не можем вписать в функцию, так как в данном случае мы определяем первоначально состояние кнопки при запуске "железа"
Ну.. очень близко. Но тут скорее проблема не в том что "первоначальное значение", а в том, что значение локальных функций - теряется при выходе из функции. То есть - когда мы в следующий раз вызовем эту функцию в prevBtn мы не сможем узнать "что в ней было при прошлом вызове" (а нам нужно).
Поэтому мы и вытащили ее "вверх". В глобальные. Там "время жизни переменной" - пока скетч существует :)
Но.. заводил бы я все эту бодягу, если бы нельзя было и рыбку сьесть и .... ну вы сами знаете :)
Можно :) Можно объявить ее внутри функции так что-бы ее значение не терялось при выходе из функции. И "в следующий раз" в ней, все-таки было именно то что мы туда записали. Что-бы первоначальная "инициализация" - выполнялась только один раз. Для этого нам нужно.... всего лишь добавить к ее объявлению слово static. Тогда она и значение свое сохранит и нигде, кроме функции видна не будет (не будет никому мешать). В других функциях мы сможем опять (если нужно) использовать имя prevBtn не опасаясь конфликтов.
void onChangedButtonSendSms(){
static boolean prevBtn = LOW; // LOW присвоется только при первом вызове. дальше значение будет сохранятся
boolean currBtn = digitalRead(BTN_PIN); // будет читать пин каждый раз
.....
}
Едем дальше. Теперь мы займемся тем чем отличается код "ученической поделки" (лишь бы работало и получить зачет) от "боевого проекта".
Тем, что нужно предусматиривать "аварийный парашут". Те ситуации когда "что-то пошло не так". Особенно если это "что-то" может начать жрать деньги клиента как бегемот веники.
Вдруг мы где-то набажим или наша кнопка сломается и начнет непрервыно "шуметь"? И пойдут у нас валится SMS-ски :) Пока деньги на счету не кончатся. А если счет контрактный и "добрый оператор" позволяет залезть в долги по "самое не балуйся"?
Значит нужно ставить какой-то "ограничитель" :) Напишем еще одну "промежуточную функцию". Которая будет ПЫТАТСЯ отсылать sms-ску (если можно). Отслать SMS-ску только если не превышен лимит. Назовем ее trySendTextMessage(char* s)
Сможете написать ее? Так что-бы:
1. Был какой-то "счетчик переменная" указывающий "сколько sms-сок мы отослали"
2. Если он меньше какого-то значения - отсылаем sms-ску (вызываем sendTextMessage()), и увеличиваем счетчик. Иначе - ничего не делаем (пока).
Ну и дальше, в onChangedButtonSendSms() пользуемся уже только фунцией trySendTextMessage(). На "прямые" вызовы sendTextMessage() - накладываем "табу". Только из trySendTextMessage() можно ее вызывать.
1. Почему limitSMS это переменная, а не #define LIMIT_SMS? Не ошибка, но ... лучше же "единообразно", как и номер пина (плюс, как мы уже выясняли - это чуть-чуть память на экономит).
2. Как-то легче читать было-бы если бы sendTextMessage() и trySendTextMessage() были бы рядом друг с другом. Родственники, как никак :)
Ну и ... похоже вы не заметили, что мы уже перешли на третью страницу в этой ветке. И упустили сообщения со второй страницы. Как минимум сообщение #100. И попыток применить #101 - тоже не видно.
>надеюсь я ничего не упустил и сделал все правильно..
Зря надеетесь... Как правило, когда все правильно, то это настолько самоочевидно что получать подтверждение - уже не нужно :) Только "что дальше делать будем?". Вам же не мой "одобрямс" в конце-концов нужен, а понимание. А рас сомневаетесь значит... еще смутно все :)
Ну, во первых. В #100, я вам показал вариант (третий мой скетч) который не имеет недостатка "лишних ожиданий" и необходимости создания дополнительной функции gprsCmdAndWaitln()
Вот этот кусок (он предполагает, что внутрие gprsCmdAndWait - используется println, а функция gprsCmdAndWaitln - не нужна):
Оставалось только, аналогично, с помощью микса GPRS.print и gprsCmdAndWait отправить само тело SMS-ски
Так же не увидил попыток применить #101. Переменные prevBtn и countSMS продолжают "засорять" общее пространство имен. Внутрь "функций" - они так и не спрятались. Хотя в #101 я вообщем-то для prevBtn - уже показал как это сделать. Нужно было по аналогии и для countSms повторить это.
Так же нарушин принцип "каждая функция делает что-то одно". У каждой - своя зона отвественности.
sendTextMessage() - отсылается SMS-ски
trySendTextMessage() - решает можно отсылать или нет.
Почему-же попытка отослать ctrl-Z попала в trySendTextMessage()? Это же часть процесса "отсылки". Если мы читаем код trySendTextMessage(), то видим: "ага. провереили лимит, послали sms и ... еще какой-то символ. зачем?". Вообщем... ctrl-z тоже дожен быть в sendTextMessage(). Там мы начали отсылать sms-ску, там же и закончить должны :)
> в терминале "CTRL+Z" (26-й символ) отображаеться как "&"
Хм... а если я в отпуск уеду на 3 месяца? Будете ждать? Или сами выясните? Напишите маленький отдельный скетч. C пустым loop() и setup() который делает два принта. "По старому" как вы уже до меня умели serial.print((char)26); и "по новому", с помощью \x... Посмотрите есть ли разница.
Частенько "поставить эксперемент кодом" - оно быстрее чем даже гуглить :) Ардуина - мощный инструмент :) Не бойтесь "игратся с кодом" :)
Даже если "искать готовый ответ", то в #100 - я вам давал ссылочку, в которой можно увидеть как выглядит тот или иной символ (если он имеет "визуальное представление").
После того как вы выясните "как выглядит" (или не выглядит) символ CTRL-Z. Какой код у символа '&' и почему "он вылез хотя вы хотели 26" :) (обязательно выясните почему вылез именно он).
После этого... я опять вас отошню к сообщению #100. C вопросом "а зачем же я показывал" целых ТРИ, на выбор, способа как перевести десятичное число в шестнадцатеричное? (по ascii таблице, с помощью калькулятора и с помощью ардуины). Может Hex (шестнадцатеричное) значение нам все-таки нужно?
Life23, если вы уже "задолбались с этой sendTextMessage()" и чувствуете что можете "перегореть", то "фиг с ним". Общую идею вы уже уловили. Чуть-чуть в 3-х соснах заблудились. С опытом - освоитесь. Если хотите - дам вам ее поправленный вариант целиком, без лишних задержек и пойдем дальше. Если же хотите "сами дожать" - не буду мешать-подсказывать (дожмете конечно, просто настроение на этом убить можете). Вобщем "выбор за вами".
Но...это после того как разберетесь как же код 26, через \x посылается. Это таки "нужно понять". Либо перечитывайте, либо... если вообще никак - вопросы спрашивайте.
Ну вы сами решайте. Могу дать sendTextMessage(), могу подождать. Можем вообще "сделать паузу".
Облегчу тогда, чуть-чуть с \x задачу. Попробую прямей сказать :) (это не самый мой большой талант :)
Смотрите, вы сделали
gprsCmdAndWait("\x26");
В принципе - все верно. Вы вывели символ с кодом 26 только в шестнадцатеричной системе. Любое число после \x - компилятор считает шестнадцатеричным. То есть, вы вывели символ с десятичным кодом 38. Если посмотрите в Ascii Table - ASCII character codes and html, octal, hex and decimal chart conversion , то увидите что десятичному 38 (первая колонка) - соотвесвует символ '&' - что вы и увидили в терминале.
Вам же нужно было вывести десятичные 26-ть (в терминале мы его вообще не увидим. у него нет "видимого представления").
Вообщем мы знаем десятичный код нужного символа (26). Нужно его вывести с помощью \x команды. Но команда - не понимает десятичные цифры, только шестнадцатеричные. Значит что нужно? Вначале перевести десятичное 26 в шестнадцатеричный вид. И этот вид подставить в gprsCmdAndWait("\xВИД");
Как перевести - я рассказывал в #100. Там есть три способа перевода.
Вот вам четвертый способ: "спросить гугл". Вот прямо так и спросить "26 to hex". Он умный, он поймет. Вот к примеру, я спросил его "250 to hex"
То есть, если бы я хотел вывести символ с десятичным кодом 250, то мне нужно было написать
gprsCmdAndWait("\xFA")
А вам - тоже самое сделать. Только не для 250, а 26 :)
void sendTextMessage(char* sms) { // Функция отправки SMS
gprsCmdAndWait("AT+CMGS=\""); //Говорим, что хотим отправить СМС на номер: (открываем кавычку)
gprsCmdAndWait(NUM_TEL); //сам номер с NUM_TEL
GPRS.println("\""); // Закрывам кавычку без вызова gprsCmdAndWait.
gprsCmdAndWaitln(sms); //По окончанию текста сообщения - переходим на новою строку
gprsCmdAndWait("\x1A"); //"Нажимаем ENTER" - смс мы закончили писать - ОТПРАВЛЯЙ! Сцуко нах...
}
На "железе" испытал, действительно Ctrl+Z или (char)26 в serial не показывает ничего..
еще и Шилд перестал реагировать на мои команды.. (((
А причина проста: Сб, 17/08/2013 - 01:32
Сапоги - нужно одевать на свежую голову :)
Без паники. Скорее всего - команды ваши не правильные. Но... так как у нас есть вариант "на который откликался", то дело за малым - найти отличия в том что мы слали тогда и сейчас.
Вообщем "разбиратся почему не работает" - это 80% работы програмера :) Так что "все нормально".
Первое что нужно выяснить это "проблема в железе или в коде". Сузить область поиска. Игра в "перелет/недолет".
Берем скетч из стартового поста. Если он работает - значит пробелма в коде. Если перестал работать - проблема в железе (как правило "питание" и т.п.)
Предположим "в коде".
OK. Берем нашу старую функцию sendTextMessage() и новую. Выводим обе в Serial и ищем 10-ть отличий.
(выбросил работау с кнопками, и CTRL-Z сделал видимым).
#define GPRS Serial
#define BTN_PIN 2
#define LIMIT_SMS 8 //лимит сообщений
unsigned int countSMS=0; //счетчик сообщений
boolean prevBtn = LOW;
char NUM_TEL[]="+78*************";
void setup(){
// ---------- Новая имплементация
initGPRS(); //вызов функции инициализации GPRS
sendTextMessage("ALARM!");
// ------------- Старая -----------------------
Serial.println();
Serial.println("============ OLD =============");
sendTextMessage_FromMsg1();
}
void loop()
{
}
void initGPRS(){ // функция инициализации GPRS
GPRS.begin(9600);
gprsCmdAndWaitln("AT+CMGF=1"); //выбор режима sms: 1-текстовый,0-цифровой)
gprsCmdAndWaitln("AT+IFC=1, 1");//data flow controll(1, Программный режим (XON/XOFF)).
gprsCmdAndWaitln("AT+CPBS=\"SM\"");//выбрать как основную память сим-карту. Данный режим стоит по умолчанию.
gprsCmdAndWaitln("AT+CNMI=1,2,2,1,0"); //информирование о новом смс.. одним словом "хрень" - пока не трогаю..
}
void sendTextMessage(char* sms) { // Функция отправки SMS
gprsCmdAndWait("AT+CMGS=\""); //Говорим, что хотим отправить СМС на номер: (открываем кавычку)
gprsCmdAndWait(NUM_TEL); //сам номер с NUM_TEL
GPRS.println("\""); // Закрывам кавычку без вызова gprsCmdAndWait.
gprsCmdAndWaitln(sms); //По окончанию текста сообщения - переходим на новою строку
// gprsCmdAndWait("\x1A"); //"Нажимаем ENTER" - смс мы закончили писать - ОТПРАВЛЯЙ! Сцуко нах...
gprsCmdAndWait("[CTRL-Z]"); // для отладки, что-бы лучше видить
}
void sendTextMessage_FromMsg1() {
// Устанавливает текстовый режим для SMS-сообщений
GPRS.print("AT+CMGF=1\r");
delay(100); // даём время на усваивание команды
// Устанавливаем адресата: телефонный номер в международном формате
GPRS.println("AT + CMGS = \"+79************\"");
delay(100);
// Пишем текст сообщения
GPRS.println("ALARM!");
delay(100);
// Отправляем Ctrl+Z, обозначая, что сообщение готово
// GPRS.println((char)26);
GPRS.println("CTRL-Z");
}
void gprsCmdAndWait(char* commandText){ //отправка в serial без перевода строки
GPRS.print(commandText);
delay(1000);
}
void gprsCmdAndWaitln(char* commandText){ //отправка в serial с переводом строки
GPRS.println(commandText);
delay(1000);
}
В итоге - получил вывод.
AT+CMGF=1
AT+IFC=1, 1
AT+CPBS="SM"
AT+CNMI=1,2,2,1,0
AT+CMGS="+78*************"
ALARM!
[CTRL-Z]
============ OLD =============
AT+CMGF=1
AT + CMGS = "+79************"
ALARM!
CTRL-Z
Как видим есть три существенных отличия (есть и более мелкие, но их проверять бум после "существенных"):
1. Раньше телефон начинался на +79, а теперь +78
2. Раньше мы не делали вообще никаких "инициализаций", только SMS-слали
3. Раньше, мы делали AT+CMGF=1 (переход в SMS Text Mode" ) непосредственно перед отправкой каждой SMS, а теперь - только один раз при старте скетча. Плюс - сразу при старте делаем это. Есть вероятность что шилд, к этому моменту еще не успел стартовать и просто "прошляпил эту команду". Тогда ПЕРЕД вызово initGprs() - какойнибудь delay() чик воткнуть.
И вообще, давайте вы как-то подробней объеясните что означает "шилд перестал" и, главное, "а с чего вы это взяли?"
Это будет не честно к тем, кто через века будет читать эту ветку ;) - Взаляся за гуж - не говори что не груздь!
Дают - бери, бьют - беги :) Мы тоже пословицы знаем.
Все честно. А "тем кто через века" - тоже так будет лучше. То что "суть" и "новые фишки" - это мы продолжим в ветке. А блуждание "в трех соснах" - вот это как раз "других" будет сбивать. Для них это - информационный шум. 15-ть вариантов и нужно много времени что-бы выяснить "какой правильный".
Да и "поиск причины" - это зачастую требует "быстро проверить 10-ть версий". Скайпом - это быстрее. Вообщем я не собираюсь "полностью перейти на скайп". Нам главное - сдвинуть вас с мертвой точки. Вытолкнуть из ямы. Дальше - опять сами руль возмете в руки :)
Вообщем - наше дело предолжить :)
Да и, в конце концов, вам же никто не мешает потом, для других в ветке написать "вот, выяснили что причина была в этом" :) Зачастую "объяснить что-то другим" - лучшей способ самому разобратся :) Так что... можете потихоньку, в других ветках другим помогать. Какой-то бы новичок вы небыли ... всегда будут еще более новичковые новички:) Для которых вы уже будете "более опытный". Даже если не знаете ответа на вопрос... гугл.... разобрались-объяснили и сами поняли :) Заодно попинали новичка по поводу "почему я могу в гугле ответ найти, а ты нет?" :) Замечательный, кстати, способ само-обучения :)
Да банальный пример, даже не зная ардуины. Код в ветку вы вставлять уже умеете. И минимум раз в пару дней есть кто-то кто не умеет. Нужно послать его в соотвествующую прикрепленную ветку. У старожилов это без сарказма и ерничения - уже трудно получается. Больно часто приходится :) А вам - еще самому близко :) Плюс положительные эмоции от своей полезности :)
Да банально, новичок новичку - зачастую лучше объяснить может. Опытному - уже не всегда просто понять "что же тут не ясного". так что - можете мониторить новые темы. Почти наверняка там будут попадатся вопросы с которыми вы уже разобрались :)
Тьфу... я не заметил кто написал "это не честно" :) Не переживайте :) Если у Life23 не опустятся руки, то "на самом интерестном закончилось" - не будет :)
OK. Хорошо что разобрались. Судя по всему надобность в skype помощи - отпала.
Продожим тут.
По поводу " "UART_PORT-DBG_PORT" - фтопку!!"
Если верить даташиту ftp://imall.iteadstudio.com/IM120417009_IComSat/DS_IM120417009_IComSat.pdf (кстати рекомендую всегда в стартовом посте давать ссылку на описание вашего шилда, не надеятся что кому-то будет не лень его нагуглить). Говорится, на странице 5, что "When connect to the SIM900 debug port, the UART multiplexer just can be set as". Честно говоря "мутно как-то сказанно", но я понял это как что в этом случае у вас сам UART должен быть подключен именно на D0,D1. А еще - вы могли перепутать, когда через putty мучали, вариант подключение "Connect the Arduino board" и "Connect the UART interfacee as FT232"
По поводу вашего кода:
sendTextMessage() - теперь выглядит "как должно" ;) Все-таки помогло одевание сапог на свежую голову ;) И "проверка железом" - подтверждает это. "Дожали" вы его самостоятельно ;)
Ну разве, что gprsCmdAndWait(sms) в строке 50-т. Можно тоже заменить на GPRS.print(sms); Что-бы небыло лишнего ожидания, целую секунду. Я думаю - оно там не нужно. Шилд просто стоит ждет пока вы закончите послать sms. Бесполезно ждет. Никаких действий до посылки CTRL-Z - не предпринимает.
Если вы сделаете эту замену, то надобность в функции gprsCmdAndWait() - отпадет. Ее можно выкинуть (памят-то .. экономим). Больше она нигде не используется. Останется только gprsCmdAndWaitln()
И... #101 опять забыли :( Ключевое слово static. Перенести объявление prevBtn и countSMS внутрь функций. Это конечно, не обязательно. И так работать будет. Более того - можете в будущем писать как хотите. Но один раз попробовать static для локальных переменных - нужно. Что-бы иметь в будущем возможность выбирать "как больше нравится", а не "я умею только одним способом".
По поводу подключения. Вот нарисовал. Простите за "криворукое рисование"
Так что с UART он работает только с перемычками. А переключатель перебрасывает Debag на пины TXD/RXD. При этом UART вообще "отбрасывает". А на моем шилде была проблема с переключателем в месте, где обвел красным - не было контакта.
Это конечно не в тему "Программирование"..
А вот в тему:
//#include <SoftwareSerial.h>
//SoftwareSerial gprsSerial(3, 2); // RX, TX
#define GPRS Serial
#define BTN_PIN 5
#define LIMIT_SMS 8 //лимит сообщений
//unsigned int countSMS=0; //счетчик сообщений
char NUM_TEL[]="+3809**********";
void setup(){
initGPRS(); //вызов функции инициализации GPRS
}
void loop()
{
onChangedButtonSendSms(); // вызов Функции проверки изменения кнопки
}
void initGPRS(){ // функция инициализации GPRS
GPRS.begin(9600);
gprsCmdAndWaitln("AT+IFC=1, 1");//data flow controll(1, Программный режим (XON/XOFF)).
gprsCmdAndWaitln("AT+CPBS=\"SM\"");//выбрать как основную память сим-карту. Данный режим стоит по умолчанию.
gprsCmdAndWaitln("AT+CNMI=1,2,2,1,0"); //информирование о новом смс.. одним словом "хрень" - пока не трогаю..
gprsCmdAndWaitln("AT+CMGF=1"); //выбор режима sms: 1-текстовый,0-цифровой)
}
void onChangedButtonSendSms(){ //Функция проверки состояния кнопки
static boolean prevBtn = LOW;
boolean currBtn = digitalRead(BTN_PIN);
if (prevBtn != currBtn && currBtn == HIGH) {
trySendTextMessage("knopka on");
}
if (prevBtn != currBtn && currBtn == LOW) {
trySendTextMessage("knopka off");
}
prevBtn = currBtn;
}
void trySendTextMessage(char* sms){ //проверяем на лимит и добавляем +1
static unsigned int countSMS=0;
if (countSMS < LIMIT_SMS){
sendTextMessage(sms);
countSMS++;
}
}
void sendTextMessage(char* sms) { // Функция отправки SMS
GPRS.print("AT+CMGS=\"");
GPRS.print(NUM_TEL);
gprsCmdAndWaitln("\"");
GPRS.print(sms);
gprsCmdAndWaitln("\x1A");
}
void gprsCmdAndWaitln(char* commandText){ //отправка в serial с переводом строки
GPRS.println(commandText);
delay(1000);
}
Ну а сами как думаете? Ведь так же лучше? Не потому что "я этого требовал", а... вверху у нас осталась "конфигурация", все что нужно функциями - внутри них самих. Легко переносить :)
Да банально, уже можно, скажем, брать функцию trySendTextMessage() и обсуждать ее отдельно. Не приводя весь код целиком. Имеем 100% гарантия что никто кроме нее не поменяет значение переменной countSMS. То есть получили "слабосвязанный код". Связи с остальным скетчем - минимизированы.
Ладно.. давайте еще чуток в нее всмотримся. Мы обезопасили себя от посыла большго количества SMS. Подстелили подушку. Но... нужно же что-бы подушка не мешала при ходьбе. В реальной жизни лимит в 10 sms - не очень удобно. 10 sms в минуту - это много, это нужно пресекать. А 50-ть sms в месяц (если устройство будет долго работать)? 50/месяц - выглядит вполне разумно/штатно.
Вообщем нужно еще как-то со временем связать этот лимит. Самым простейшим вариантом - будет тупо сбрасывать счетчик в ноль через какое-то время. Ну скажем, раз в 10-ть минут.
Тьфу... я не заметил кто написал "это не честно" :) Не переживайте :) Если у Life23 не опустятся руки, то "на самом интерестном закончилось" - не будет :)
Ну. "Прицепится" тут можно только к одному (хотя ОЧЕНЬ маловероятно что это проявится в качестве проблемы). То что вы для interval выбрали long. Не бывается же отрицательных интервалов :) То что "так было в примере" - не оправдание. Когда берем любой чужой код - мы берем и отвественность за него :) Для previousMillis - то же самое.
Правильно выбрать тип нам тут помогает два соображение "время не бывает отрицательным" и, это даже важней, описание самой функции millis() | Аппаратная платформа Arduino где говорится что она возращает значение типа unsigned long. Логично, если мы хотим хранить без искажений возвращенное значение - нам нужно взять именно этот тип.
Давайте поймем каким боком нам может вылезити long для interval. Если мы будем сбрасывать "раз в 10-ть минут" - никаким. Все будет хорошо. Но предположим, через какое-то время кто-то захочет сдлать интервал сброса очень большим. Скажем 34-дня.
Давайте накидаем разовый скетчик для проверки как хранятся такие большие числа
void setup(){
Serial.begin(9600);
long wrongInterval=2937600000; // 34дня*24часа*минут_в_часе*секунд_в_минуте*миллисекунд_в_секунде
unsigned long correctInterval=2937600000; // вроде то же самое значение?
Serial.print("wrong= ");Serial.println(wrongInterval); // нежданчик
Serial.print("correct= ");Serial.println(correctInterval);
}
void loop(){}
Посмотрите в Serial. Первая строчка "wrong= " - может стать неприятным сюрпризом. Прокрутите в голове как будет работать ваше timeLimitSMS() при подобном значении (которое вывол в Serial).
Но в принципе ваш код можно признать "годным". Я бы чуть-чуть по другому, но это уже скорее "дело в куса", чем "замечания к исполнению".
1. interval - раз он число и никогда не меняется, сделал бы в виде #define. Так же, что-бы компилятор случайно не перепутал тип - указал бы ему в явнов виде что он unsigned long. Для этого - читаем Целочисленные константы | Аппаратная платформа Arduino
2. Дал бы ему более говорящие имя. Не полагался что "коментарии все объяснят". Вообщем вот так о бы у меня выглядел:
#define SMS_RESET_INTERVAL 25000UL
3. timeLimitSMS() - личо мне, такое имя не говорит что же именно делает эта функция. Я бы назвал ее resetSmsLimitByInterval();
4. Так как у нас нет цели сбрасывать счетчик "ну очень точно". Нам же не важно значение countSMS пока мы не отсылаем sms-ски. Следовательно вызов resetSmsLimitByInterval/timeLimitSMS можно делать не в loop(), а в trySendTextMessage(). Loop() будет меньше, да и такты процессора экономим. Вообщем делаем работу по сбросу счетчика только когда он нам важен, а не постоянно.
Но... как я уже говорил это все "дело вкуса". Это не совет "сделайте это обязательно". В том виде как сейчас - ваш скетч тоже вполне рабочий/приличный (когда поправить long->unsigned long). Так что заниматся этими переименованиями и переносом вызова из loop() в trySendTextMessage() - уже вам решение принимать. Что ваше "чувтсво прекрасного" подскажет :)
P.S. И да... возможно я бы ще sendTextMessage/trySendTextMessage переименовал в sendSMS и trySendSMS. И короче и как-то более конкретно.
Кстати, вот с типом числовых констант - это бывает довольно трудноуловимой подлянкой :)
Заметели, что в #131 я сразу дал число в миллисекундах. Хотя, казалось бы ардуина и сама прекрасно умеет умножать и можно было не считать на калькуляторе (на самом деле не хотел "перегружать").
Вот пустите у себя такой код:
void setup(){
Serial.begin(9600);
unsigned long calculated=34*24*60*60*1000; // ждем что тут будем 2937600000
Serial.print("wrong= ");Serial.println(calculated); // а вот - болт :)
// попробуем чуть-чуть по другому
calculated=34UL*24*60*60*1000;
Serial.print("correct= ");Serial.println(calculated);
}
void loop(){}
Попробуйте понять, почему
а. Первый вариант умножение дал совсем не то что мы ждали.
б. Почему "что-бы починить" хватило дописать UL только к одной цифре, а не все писали 34UL*24UL*60UL.... (вообщем-то можно было и всем, но "лениво было" :)
На данный момент скетч по отсылке SMS можно считать "подготовленным" к слиянию. Применять к нему коментарии из #132 - решайте сами.
Можно конечно к нему еще придумать "улучшения", конечно:
1. В gprsCmdAndWaitln() ждать не фиксированное время 1000 ms. (и зачастую "бесполезно тормозить"), а ожидать именно "подтверждающего ответа" от шилда (большинстве случаев, на команду он должен ответить "OK"). Если не ответил, в течении скажем 2-х секунд - зажигать какой-нибудь "диод ошибки" и т.п. Заодно и от delay() избавимся (который, в реальных скетчах, не небольших примерах - является, зачастую, бякой).
2. Организовать более хитрую логику "лимитов": не уподоблятся ОПСОСАМ, у которых "предоплаченный трафик" сгорает раз в сутки. А сделать что-бы 20SMS в час именно такими и были (засекать во сколько отослалась первая SMS). Плюс, можно сделать что-то типа " 10 sms в минуту, но не более 20 час, и не более 50 в сутки" :)
Но сейчас, думаю этим заниматся не стоит. Уже сами. В качестве домашнего задания. Плюс первый пункт, я думаю, вам будет легче решить когда мы скетч приема SMS-сок "разберем по косточкам". На нем будем учится "читать ответы шилда".
Вообщем предлагаю, переходить ко второму скетчу? Готовы? Если "да", то.... на выбор у нас есть два варианта:
1. Мы перепишем его на использование стандартных C-шных строк. Не будет использовать String (ну не люблю я его, все равно С-шные строки знать нужно, а память - жрет).
2. Будем пытатся, все-таки String. Но все "перелопатим все что можно".
Думая вы уже поняли, что смысл "перелопатки", не только в "улучшить код" (хотя и это тоже), но и в том что-бы "знать/понимать" каждую строчку в лицо. Мне кажется что в скетче отсыки SMS у вас уже ни одной строчки про которую вы бы не могли ответить "зачем она тут" и "почему выглядит именно так" :) Верно? ;)
А "мять как пластилин" код который тебе понятен - совершенно не страшно :)
Ну что, готовы морально ко второй серии? (думаю уже легче пойдет, скорее как "закрепление". уже функции более/менее освоили, типы, константы..." - вообщем "базовые кубики" мы уже умеем переставлять :)
Мы тут везде явно сказали что "все числа и результаты умножения считать байтом". И получили "результат" - 244
Как так? Но... 250*2=500. Верно? В двоичном виде это B111110100. Но.. в байт помещается только 8-бит. А тут 9-ть. Ну значит самый старший (самый левый) бит - тупо выбросили. Осталось B11110100, что если вы переведете в десятичное число, и будет наше 244.
Но тут мы сказали в явном виде "считай это байтом".
А в 34*24*60... и т.п. - мы такого не говорили.
Но... компилятор сам пытается догадатся "какого типа числа". Когда мы сказали что число 2937600000 он, глядя на его размер - сам догадался что "это явно long". А вот когда он видит число небольшое, он предполагает что это int. Тоже пытается "оптимизировать".
И тут вступает в игру - порядок выполнения операторов. Он не пытается "умножить сразу все". А "по шагам".
Взял 34*24. Хм... вроде оба int, значит и результат int. Пытается догадатся о типе результата по типу параметров. Умножил их между собой. и запомнил в какую-то внутренюю переменную типа int Result1=34*24;. Идет дальше по цепочке Result1*60. Result1 - int, 60-тоже int. Значит Result2 тоже int. Result2=Result1*60... и т.д. Вообщем в тоге он проходит всю нашу цепочку умножения в уверенности что все время работает с типом int.
И где-то, в этой цепочке, результат умножения - не влазит в int. И обрезается. Получается неверное число :(
А вот когда я к первой цифре добавил UL, я в явном виде сказад "это unsigned long".
Тогда его логика такая.
Result1 это умножения (unsigned long) и (int). Значит результатом может (unsigned long) Быть.
Result2, это умножение Result1( который unsigned long) и (int) - значить Result2 - тоже UL....
и так далее, по всей цепочке. Поэтому достаточно было явно указать тип только первому множителю. Этой подсказки хватило компилятору что-бы разобратся какого типа должны быть промежуточные вычисления.
Вообщем в первом случае у нас получился такой продяок умножения
int result1=34*24;
int result2=result1*60;
int result3=result2*60;
int itogo=result3*1000;
А во втором случае
unsigned long result1=34*24;
unsigned long result2=result1*60;
unsigned long result3=result2*60;
unsigned long itogo=result3*1000;
Ну как вы уже поняли с "первой серии", я не работал ни с "C-шным строкам" ни со String.) Так что все тики выбор за вами!:) и я думаю он очевиден: С строки :)
Хорошо. Но наверное уже вечером. А что-бы, пока небыло скучно :) перечитайте еще раз string
Попробуйте сделать, что-то такое:
char str[12];
setup(){
Serial.begin(9600);
// тут как-то заполняем str, с помощью for массив str
Serial.print(str); // выводит "ABCDEFGHIJ"
Serial.print("Next Line"); // должно быть выведено с новой строки
}
Вам можно что-то вписывать только в //тут как-то заполняем
Дополнительных Serial.print/Serial.println, дополнительные строки и т.п. - запрещенно. Писать в коде что-то типа "ABCD..." - нельзя. Нужно именно с помощью for и присвоения элементов массива, правильно заполнить массив str. Что-бы вывелось в Serial вот такое:
ABCDEFGHIJ
Next Line
Ну и... можно не сейчас, можно потом, посмотрите еще один вариант как можно было сделать sendTextMessage()
char NUM_TEL[]="+3809**********";
#define GPRS Serial
void setup(){
Serial.begin(9600);
sendTextMessage("Some message 1");
sendTextMessageNew("Some message 2");
}
void loop(){
}
void sendTextMessageNew(char* sms){
char buff[200]; // временный буфер в котором формируем команду
sprintf(buff,"AT+CMGS=\"%s\"\n%s%c",NUM_TEL,sms,26);
gprsCmdAndWaitln(buff);
}
void sendTextMessage(char* sms) { // Функция отправки SMS
GPRS.print("AT+CMGS=\"");
GPRS.print(NUM_TEL);
gprsCmdAndWaitln("\"");
GPRS.print(sms);
gprsCmdAndWaitln("\x1A");
}
void gprsCmdAndWaitln(char* commandText){ //отправка в serial с переводом строки
GPRS.println(commandText);
delay(1000);
}
Переделывать свой скетч на этот вариант - не нужно. Просто - ознакомтесь что еще бывает такая функция как sprintf :) Погуглите что это за зверь и как его укрощать :)
Ну да... знали бы вы сколько крови такое может попортить. Вот сейчас - вроде рабоатет (просто повезло что дальше в памяти сразу после строки - нули попались), а потом когда скетч разросся и в память дальше попало что-то не нулевое... или мусор пошел валится, или вообще "почему-то тупо виснет на Serial.print" и фиг догадаешься.
Вообщем, при работе со строками, ноль в конце - это самое святое :)
И вообщем-то, вам повезло что вы сделали сейчас эту ошибку (я надеялся на это) - лучше запомните :)
Теперь смотрите. Сейчас вы выкрутились тем, что знаете про одинарные кавычки. А если их использование тоже запретить?
Сможете? Использовать тот факт, что char и byte (код-символа) - это одно и тоже. Фактически вы уже использовали этот факт когда писали simv++ - "это просто число", поэтому так и возможно было написать что simvv - это код символа.
мы не можем вписать в функцию, так как в данном случае мы определяем первоначально состояние кнопки при запуске "железа"
Ну.. очень близко. Но тут скорее проблема не в том что "первоначальное значение", а в том, что значение локальных функций - теряется при выходе из функции. То есть - когда мы в следующий раз вызовем эту функцию в prevBtn мы не сможем узнать "что в ней было при прошлом вызове" (а нам нужно).
Поэтому мы и вытащили ее "вверх". В глобальные. Там "время жизни переменной" - пока скетч существует :)
Но.. заводил бы я все эту бодягу, если бы нельзя было и рыбку сьесть и .... ну вы сами знаете :)
Можно :) Можно объявить ее внутри функции так что-бы ее значение не терялось при выходе из функции. И "в следующий раз" в ней, все-таки было именно то что мы туда записали. Что-бы первоначальная "инициализация" - выполнялась только один раз. Для этого нам нужно.... всего лишь добавить к ее объявлению слово static. Тогда она и значение свое сохранит и нигде, кроме функции видна не будет (не будет никому мешать). В других функциях мы сможем опять (если нужно) использовать имя prevBtn не опасаясь конфликтов.
Едем дальше. Теперь мы займемся тем чем отличается код "ученической поделки" (лишь бы работало и получить зачет) от "боевого проекта".
Тем, что нужно предусматиривать "аварийный парашут". Те ситуации когда "что-то пошло не так". Особенно если это "что-то" может начать жрать деньги клиента как бегемот веники.
Вдруг мы где-то набажим или наша кнопка сломается и начнет непрервыно "шуметь"? И пойдут у нас валится SMS-ски :) Пока деньги на счету не кончатся. А если счет контрактный и "добрый оператор" позволяет залезть в долги по "самое не балуйся"?
Значит нужно ставить какой-то "ограничитель" :) Напишем еще одну "промежуточную функцию". Которая будет ПЫТАТСЯ отсылать sms-ску (если можно). Отслать SMS-ску только если не превышен лимит. Назовем ее trySendTextMessage(char* s)
Сможете написать ее? Так что-бы:
1. Был какой-то "счетчик переменная" указывающий "сколько sms-сок мы отослали"
2. Если он меньше какого-то значения - отсылаем sms-ску (вызываем sendTextMessage()), и увеличиваем счетчик. Иначе - ничего не делаем (пока).
Ну и дальше, в onChangedButtonSendSms() пользуемся уже только фунцией trySendTextMessage(). На "прямые" вызовы sendTextMessage() - накладываем "табу". Только из trySendTextMessage() можно ее вызывать.
вот у меня что получилось..
Вообщем "мелкая косметика":
1. Почему limitSMS это переменная, а не #define LIMIT_SMS? Не ошибка, но ... лучше же "единообразно", как и номер пина (плюс, как мы уже выясняли - это чуть-чуть память на экономит).
2. Как-то легче читать было-бы если бы sendTextMessage() и trySendTextMessage() были бы рядом друг с другом. Родственники, как никак :)
Ну и ... похоже вы не заметили, что мы уже перешли на третью страницу в этой ветке. И упустили сообщения со второй страницы. Как минимум сообщение #100. И попыток применить #101 - тоже не видно.
Да, действительно, я че-то не увидел сообщений 100 и 101. наверно не обновил форум, перед тем как отправить свое сообщение. Работаю над этим.
надеюсь я ничего не упустил и сделал все правильно..
P.S.: в терминале "CTRL+Z" (26-й символ) отображаеться как "&"?
>надеюсь я ничего не упустил и сделал все правильно..
Зря надеетесь... Как правило, когда все правильно, то это настолько самоочевидно что получать подтверждение - уже не нужно :) Только "что дальше делать будем?". Вам же не мой "одобрямс" в конце-концов нужен, а понимание. А рас сомневаетесь значит... еще смутно все :)
Ну, во первых. В #100, я вам показал вариант (третий мой скетч) который не имеет недостатка "лишних ожиданий" и необходимости создания дополнительной функции gprsCmdAndWaitln()
Вот этот кусок (он предполагает, что внутрие gprsCmdAndWait - используется println, а функция gprsCmdAndWaitln - не нужна):
Оставалось только, аналогично, с помощью микса GPRS.print и gprsCmdAndWait отправить само тело SMS-ски
Так же не увидил попыток применить #101. Переменные prevBtn и countSMS продолжают "засорять" общее пространство имен. Внутрь "функций" - они так и не спрятались. Хотя в #101 я вообщем-то для prevBtn - уже показал как это сделать. Нужно было по аналогии и для countSms повторить это.
Так же нарушин принцип "каждая функция делает что-то одно". У каждой - своя зона отвественности.
sendTextMessage() - отсылается SMS-ски
trySendTextMessage() - решает можно отсылать или нет.
Почему-же попытка отослать ctrl-Z попала в trySendTextMessage()? Это же часть процесса "отсылки". Если мы читаем код trySendTextMessage(), то видим: "ага. провереили лимит, послали sms и ... еще какой-то символ. зачем?". Вообщем... ctrl-z тоже дожен быть в sendTextMessage(). Там мы начали отсылать sms-ску, там же и закончить должны :)
> в терминале "CTRL+Z" (26-й символ) отображаеться как "&"
Хм... а если я в отпуск уеду на 3 месяца? Будете ждать? Или сами выясните? Напишите маленький отдельный скетч. C пустым loop() и setup() который делает два принта. "По старому" как вы уже до меня умели serial.print((char)26); и "по новому", с помощью \x... Посмотрите есть ли разница.
Частенько "поставить эксперемент кодом" - оно быстрее чем даже гуглить :) Ардуина - мощный инструмент :) Не бойтесь "игратся с кодом" :)
Даже если "искать готовый ответ", то в #100 - я вам давал ссылочку, в которой можно увидеть как выглядит тот или иной символ (если он имеет "визуальное представление").
После того как вы выясните "как выглядит" (или не выглядит) символ CTRL-Z. Какой код у символа '&' и почему "он вылез хотя вы хотели 26" :) (обязательно выясните почему вылез именно он).
После этого... я опять вас отошню к сообщению #100. C вопросом "а зачем же я показывал" целых ТРИ, на выбор, способа как перевести десятичное число в шестнадцатеричное? (по ascii таблице, с помощью калькулятора и с помощью ардуины). Может Hex (шестнадцатеричное) значение нам все-таки нужно?
Life23, если вы уже "задолбались с этой sendTextMessage()" и чувствуете что можете "перегореть", то "фиг с ним". Общую идею вы уже уловили. Чуть-чуть в 3-х соснах заблудились. С опытом - освоитесь. Если хотите - дам вам ее поправленный вариант целиком, без лишних задержек и пойдем дальше. Если же хотите "сами дожать" - не буду мешать-подсказывать (дожмете конечно, просто настроение на этом убить можете). Вобщем "выбор за вами".
Но...это после того как разберетесь как же код 26, через \x посылается. Это таки "нужно понять". Либо перечитывайте, либо... если вообще никак - вопросы спрашивайте.
Life23, если вы уже "задолбались с этой sendTextMessage()" и чувствуете что можете "перегореть", то "фиг с ним".
Да, действительно, тяжело и с "настроением" проблемы.. но я все таки попробую еще раз все внимательно перечитать и изучить..
Ну вы сами решайте. Могу дать sendTextMessage(), могу подождать. Можем вообще "сделать паузу".
Облегчу тогда, чуть-чуть с \x задачу. Попробую прямей сказать :) (это не самый мой большой талант :)
Смотрите, вы сделали
В принципе - все верно. Вы вывели символ с кодом 26 только в шестнадцатеричной системе. Любое число после \x - компилятор считает шестнадцатеричным. То есть, вы вывели символ с десятичным кодом 38. Если посмотрите в Ascii Table - ASCII character codes and html, octal, hex and decimal chart conversion , то увидите что десятичному 38 (первая колонка) - соотвесвует символ '&' - что вы и увидили в терминале.
Вам же нужно было вывести десятичные 26-ть (в терминале мы его вообще не увидим. у него нет "видимого представления").
Вообщем мы знаем десятичный код нужного символа (26). Нужно его вывести с помощью \x команды. Но команда - не понимает десятичные цифры, только шестнадцатеричные. Значит что нужно? Вначале перевести десятичное 26 в шестнадцатеричный вид. И этот вид подставить в gprsCmdAndWait("\xВИД");
Как перевести - я рассказывал в #100. Там есть три способа перевода.
Вот вам четвертый способ: "спросить гугл". Вот прямо так и спросить "26 to hex". Он умный, он поймет. Вот к примеру, я спросил его "250 to hex"
То есть, если бы я хотел вывести символ с десятичным кодом 250, то мне нужно было написать
А вам - тоже самое сделать. Только не для 250, а 26 :)
Вы меня опередили ))
Только хотел сбросить:
На "железе" испытал, действительно Ctrl+Z или (char)26 в serial не показывает ничего..
4-я строка: написал без вызова функции gprs.println. - не могу пока сообразить правильно ли сделал..
наверно не стояло.. т.к. после "ввода номера" все таки стоит сделать паузу которая есть в "gprsCmdAndWait"..
с "gprsCmdAndWaitln" пока еще разбираюсь... точнее пытаюсь понять..
что-то я совсем запутался....((
еще и Шилд перестал реагировать на мои команды.. (((
что-то я совсем запутался....((
еще и Шилд перестал реагировать на мои команды.. (((
А причина проста: Сб, 17/08/2013 - 01:32
Сапоги - нужно одевать на свежую голову :)
Без паники. Скорее всего - команды ваши не правильные. Но... так как у нас есть вариант "на который откликался", то дело за малым - найти отличия в том что мы слали тогда и сейчас.
Дайте сюда лог, что вы в Serial Monitor видите?
Вообщем "разбиратся почему не работает" - это 80% работы програмера :) Так что "все нормально".
Первое что нужно выяснить это "проблема в железе или в коде". Сузить область поиска. Игра в "перелет/недолет".
Берем скетч из стартового поста. Если он работает - значит пробелма в коде. Если перестал работать - проблема в железе (как правило "питание" и т.п.)
Предположим "в коде".
OK. Берем нашу старую функцию sendTextMessage() и новую. Выводим обе в Serial и ищем 10-ть отличий.
(выбросил работау с кнопками, и CTRL-Z сделал видимым).
В итоге - получил вывод.
Как видим есть три существенных отличия (есть и более мелкие, но их проверять бум после "существенных"):
1. Раньше телефон начинался на +79, а теперь +78
2. Раньше мы не делали вообще никаких "инициализаций", только SMS-слали
3. Раньше, мы делали AT+CMGF=1 (переход в SMS Text Mode" ) непосредственно перед отправкой каждой SMS, а теперь - только один раз при старте скетча. Плюс - сразу при старте делаем это. Есть вероятность что шилд, к этому моменту еще не успел стартовать и просто "прошляпил эту команду". Тогда ПЕРЕД вызово initGprs() - какойнибудь delay() чик воткнуть.
И вообще, давайте вы как-то подробней объеясните что означает "шилд перестал" и, главное, "а с чего вы это взяли?"
Вышлите мне на почту свой скайп-контакт. Давайте уж побыстрому его "запинаем" :) Мою почту - сможете найти в ветке "список исполнителей".
Это будет не честно к тем, кто через века будет читать эту ветку ;) - Взаляся за гуж - не говори что не груздь!
Это будет не честно к тем, кто через века будет читать эту ветку ;) - Взаляся за гуж - не говори что не груздь!
Дают - бери, бьют - беги :) Мы тоже пословицы знаем.
Все честно. А "тем кто через века" - тоже так будет лучше. То что "суть" и "новые фишки" - это мы продолжим в ветке. А блуждание "в трех соснах" - вот это как раз "других" будет сбивать. Для них это - информационный шум. 15-ть вариантов и нужно много времени что-бы выяснить "какой правильный".
Да и "поиск причины" - это зачастую требует "быстро проверить 10-ть версий". Скайпом - это быстрее. Вообщем я не собираюсь "полностью перейти на скайп". Нам главное - сдвинуть вас с мертвой точки. Вытолкнуть из ямы. Дальше - опять сами руль возмете в руки :)
Вообщем - наше дело предолжить :)
Да и, в конце концов, вам же никто не мешает потом, для других в ветке написать "вот, выяснили что причина была в этом" :) Зачастую "объяснить что-то другим" - лучшей способ самому разобратся :) Так что... можете потихоньку, в других ветках другим помогать. Какой-то бы новичок вы небыли ... всегда будут еще более новичковые новички:) Для которых вы уже будете "более опытный". Даже если не знаете ответа на вопрос... гугл.... разобрались-объяснили и сами поняли :) Заодно попинали новичка по поводу "почему я могу в гугле ответ найти, а ты нет?" :) Замечательный, кстати, способ само-обучения :)
Да банальный пример, даже не зная ардуины. Код в ветку вы вставлять уже умеете. И минимум раз в пару дней есть кто-то кто не умеет. Нужно послать его в соотвествующую прикрепленную ветку. У старожилов это без сарказма и ерничения - уже трудно получается. Больно часто приходится :) А вам - еще самому близко :) Плюс положительные эмоции от своей полезности :)
Да банально, новичок новичку - зачастую лучше объяснить может. Опытному - уже не всегда просто понять "что же тут не ясного". так что - можете мониторить новые темы. Почти наверняка там будут попадатся вопросы с которыми вы уже разобрались :)
Тьфу... я не заметил кто написал "это не честно" :) Не переживайте :) Если у Life23 не опустятся руки, то "на самом интерестном закончилось" - не будет :)
Мой шилд не отвечал на мои команды АТ, даже без ардуины через терминал Putty. хотя при старте текст самого шилда отображался.
Уже разобрался. Выбросил все перемычки на плате. и подключился на прямую к TXD RXD самого SIM900 (мой шилд на этом чипе).
А вот почему он с перемычками не работал - не могу понять.. потом разберусь на досуге..
У меня много уже насобиралось разных вариантов кода, вот тут я и застрял ))
пройдусь еще раз по коду внимательней..
мой скайп: time_to_base
Уже разобрался. Выбросил все перемычки на плате. и подключился на прямую к TXD RXD самого SIM900 (мой шилд на этом чипе).
А вот почему он с перемычками не работал - не могу понять.. потом разберусь на досуге..
Так "разобрался" или "не могу понять" :)
мой скайп: time_to_base
Отправил вам запрос
Вот теперь разобрался почему шилд мне не отвечал на мои команды. но его я "видел".
Я в шоке...
Мой GSM шилд ICOMSAT V1.1 на чипе SIM900
Переключатель на шилде "UART_PORT-DBG_PORT" - фтопку!!
При переключении в режим UART_PORT он не довключал RX!
просто нет слов...
P.S. Кто будет разбираться в железе: Пока не подать на шилд питание VBat - транзюки "разрывают" RX и TX в чип SIM900.
Вот что у меня получилось с кодом:
уже проверен на железе..
OK. Хорошо что разобрались. Судя по всему надобность в skype помощи - отпала.
Продожим тут.
По поводу " "UART_PORT-DBG_PORT" - фтопку!!"
Если верить даташиту ftp://imall.iteadstudio.com/IM120417009_IComSat/DS_IM120417009_IComSat.pdf (кстати рекомендую всегда в стартовом посте давать ссылку на описание вашего шилда, не надеятся что кому-то будет не лень его нагуглить). Говорится, на странице 5, что "When connect to the SIM900 debug port, the UART multiplexer just can be set as". Честно говоря "мутно как-то сказанно", но я понял это как что в этом случае у вас сам UART должен быть подключен именно на D0,D1. А еще - вы могли перепутать, когда через putty мучали, вариант подключение "Connect the Arduino board" и "Connect the UART interfacee as FT232"
По поводу вашего кода:
sendTextMessage() - теперь выглядит "как должно" ;) Все-таки помогло одевание сапог на свежую голову ;) И "проверка железом" - подтверждает это. "Дожали" вы его самостоятельно ;)
Ну разве, что gprsCmdAndWait(sms) в строке 50-т. Можно тоже заменить на GPRS.print(sms); Что-бы небыло лишнего ожидания, целую секунду. Я думаю - оно там не нужно. Шилд просто стоит ждет пока вы закончите послать sms. Бесполезно ждет. Никаких действий до посылки CTRL-Z - не предпринимает.
Если вы сделаете эту замену, то надобность в функции gprsCmdAndWait() - отпадет. Ее можно выкинуть (памят-то .. экономим). Больше она нигде не используется. Останется только gprsCmdAndWaitln()
И... #101 опять забыли :( Ключевое слово static. Перенести объявление prevBtn и countSMS внутрь функций. Это конечно, не обязательно. И так работать будет. Более того - можете в будущем писать как хотите. Но один раз попробовать static для локальных переменных - нужно. Что-бы иметь в будущем возможность выбирать "как больше нравится", а не "я умею только одним способом".
По поводу подключения. Вот нарисовал. Простите за "криворукое рисование"
Так что с UART он работает только с перемычками. А переключатель перебрасывает Debag на пины TXD/RXD. При этом UART вообще "отбрасывает". А на моем шилде была проблема с переключателем в месте, где обвел красным - не было контакта.
Это конечно не в тему "Программирование"..
А вот в тему:
Исправилься ;)
>Исправилься ;)
Ну а сами как думаете? Ведь так же лучше? Не потому что "я этого требовал", а... вверху у нас осталась "конфигурация", все что нужно функциями - внутри них самих. Легко переносить :)
Да банально, уже можно, скажем, брать функцию trySendTextMessage() и обсуждать ее отдельно. Не приводя весь код целиком. Имеем 100% гарантия что никто кроме нее не поменяет значение переменной countSMS. То есть получили "слабосвязанный код". Связи с остальным скетчем - минимизированы.
Ладно.. давайте еще чуток в нее всмотримся. Мы обезопасили себя от посыла большго количества SMS. Подстелили подушку. Но... нужно же что-бы подушка не мешала при ходьбе. В реальной жизни лимит в 10 sms - не очень удобно. 10 sms в минуту - это много, это нужно пресекать. А 50-ть sms в месяц (если устройство будет долго работать)? 50/месяц - выглядит вполне разумно/штатно.
Вообщем нужно еще как-то со временем связать этот лимит. Самым простейшим вариантом - будет тупо сбрасывать счетчик в ноль через какое-то время. Ну скажем, раз в 10-ть минут.
Для этого, мы в качестве примера посмотрим на Мигаем светодиодом без delay()
Там, раз в секунду, меняют состояние диода. А нам нужно - сбрасывать счетчик в ноль.
Тьфу... я не заметил кто написал "это не честно" :) Не переживайте :) Если у Life23 не опустятся руки, то "на самом интерестном закончилось" - не будет :)
Я уже всплакнуть успел :)
Натянул сапоги на голову и пошел спать.. ))
Натянул сапоги на голову и пошел спать.. ))
что бы комары не кусали?
Ну. "Прицепится" тут можно только к одному (хотя ОЧЕНЬ маловероятно что это проявится в качестве проблемы). То что вы для interval выбрали long. Не бывается же отрицательных интервалов :) То что "так было в примере" - не оправдание. Когда берем любой чужой код - мы берем и отвественность за него :) Для previousMillis - то же самое.
Правильно выбрать тип нам тут помогает два соображение "время не бывает отрицательным" и, это даже важней, описание самой функции millis() | Аппаратная платформа Arduino где говорится что она возращает значение типа unsigned long. Логично, если мы хотим хранить без искажений возвращенное значение - нам нужно взять именно этот тип.
Давайте поймем каким боком нам может вылезити long для interval. Если мы будем сбрасывать "раз в 10-ть минут" - никаким. Все будет хорошо. Но предположим, через какое-то время кто-то захочет сдлать интервал сброса очень большим. Скажем 34-дня.
Давайте накидаем разовый скетчик для проверки как хранятся такие большие числа
Посмотрите в Serial. Первая строчка "wrong= " - может стать неприятным сюрпризом. Прокрутите в голове как будет работать ваше timeLimitSMS() при подобном значении (которое вывол в Serial).
Но в принципе ваш код можно признать "годным". Я бы чуть-чуть по другому, но это уже скорее "дело в куса", чем "замечания к исполнению".
1. interval - раз он число и никогда не меняется, сделал бы в виде #define. Так же, что-бы компилятор случайно не перепутал тип - указал бы ему в явнов виде что он unsigned long. Для этого - читаем Целочисленные константы | Аппаратная платформа Arduino
2. Дал бы ему более говорящие имя. Не полагался что "коментарии все объяснят". Вообщем вот так о бы у меня выглядел:
3. timeLimitSMS() - личо мне, такое имя не говорит что же именно делает эта функция. Я бы назвал ее resetSmsLimitByInterval();
4. Так как у нас нет цели сбрасывать счетчик "ну очень точно". Нам же не важно значение countSMS пока мы не отсылаем sms-ски. Следовательно вызов resetSmsLimitByInterval/timeLimitSMS можно делать не в loop(), а в trySendTextMessage(). Loop() будет меньше, да и такты процессора экономим. Вообщем делаем работу по сбросу счетчика только когда он нам важен, а не постоянно.
Но... как я уже говорил это все "дело вкуса". Это не совет "сделайте это обязательно". В том виде как сейчас - ваш скетч тоже вполне рабочий/приличный (когда поправить long->unsigned long). Так что заниматся этими переименованиями и переносом вызова из loop() в trySendTextMessage() - уже вам решение принимать. Что ваше "чувтсво прекрасного" подскажет :)
P.S. И да... возможно я бы ще sendTextMessage/trySendTextMessage переименовал в sendSMS и trySendSMS. И короче и как-то более конкретно.
по поводу поста #131:
да, действительно. если постаивть значение 34дня и оставить "
long
interval" получим отрицательное число.
то в
countSMS = 0; выполняться..
void
timeLimitSMS()
постоянно будетНе обратил на это внимания.. Но теперь разобрался почему в Serial.print выводит отрецательное число и откуда береться такое число.
Кстати, вот с типом числовых констант - это бывает довольно трудноуловимой подлянкой :)
Заметели, что в #131 я сразу дал число в миллисекундах. Хотя, казалось бы ардуина и сама прекрасно умеет умножать и можно было не считать на калькуляторе (на самом деле не хотел "перегружать").
Вот пустите у себя такой код:
Попробуйте понять, почему
а. Первый вариант умножение дал совсем не то что мы ждали.
б. Почему "что-бы починить" хватило дописать UL только к одной цифре, а не все писали 34UL*24UL*60UL.... (вообщем-то можно было и всем, но "лениво было" :)
На данный момент скетч по отсылке SMS можно считать "подготовленным" к слиянию. Применять к нему коментарии из #132 - решайте сами.
Можно конечно к нему еще придумать "улучшения", конечно:
1. В gprsCmdAndWaitln() ждать не фиксированное время 1000 ms. (и зачастую "бесполезно тормозить"), а ожидать именно "подтверждающего ответа" от шилда (большинстве случаев, на команду он должен ответить "OK"). Если не ответил, в течении скажем 2-х секунд - зажигать какой-нибудь "диод ошибки" и т.п. Заодно и от delay() избавимся (который, в реальных скетчах, не небольших примерах - является, зачастую, бякой).
2. Организовать более хитрую логику "лимитов": не уподоблятся ОПСОСАМ, у которых "предоплаченный трафик" сгорает раз в сутки. А сделать что-бы 20SMS в час именно такими и были (засекать во сколько отослалась первая SMS). Плюс, можно сделать что-то типа " 10 sms в минуту, но не более 20 час, и не более 50 в сутки" :)
Но сейчас, думаю этим заниматся не стоит. Уже сами. В качестве домашнего задания. Плюс первый пункт, я думаю, вам будет легче решить когда мы скетч приема SMS-сок "разберем по косточкам". На нем будем учится "читать ответы шилда".
Вообщем предлагаю, переходить ко второму скетчу? Готовы? Если "да", то.... на выбор у нас есть два варианта:
1. Мы перепишем его на использование стандартных C-шных строк. Не будет использовать String (ну не люблю я его, все равно С-шные строки знать нужно, а память - жрет).
2. Будем пытатся, все-таки String. Но все "перелопатим все что можно".
Думая вы уже поняли, что смысл "перелопатки", не только в "улучшить код" (хотя и это тоже), но и в том что-бы "знать/понимать" каждую строчку в лицо. Мне кажется что в скетче отсыки SMS у вас уже ни одной строчки про которую вы бы не могли ответить "зачем она тут" и "почему выглядит именно так" :) Верно? ;)
А "мять как пластилин" код который тебе понятен - совершенно не страшно :)
Ну что, готовы морально ко второй серии? (думаю уже легче пойдет, скорее как "закрепление". уже функции более/менее освоили, типы, константы..." - вообщем "базовые кубики" мы уже умеем переставлять :)
Я все не могу понять, откуда число 14336 в строчке Wrong= с поста #134 и почему UL только в первом числе..
Это меня пока тормозит к "второй серии" ))
Я все не могу понять, откуда число 14336 в строчке Wrong= с поста #134 и почему UL только в первом числе..
Это меня пока тормозит к "второй серии" ))
Ну для начала взгляните на вывод вот этого:
Мы тут везде явно сказали что "все числа и результаты умножения считать байтом". И получили "результат" - 244
Как так? Но... 250*2=500. Верно? В двоичном виде это B111110100. Но.. в байт помещается только 8-бит. А тут 9-ть. Ну значит самый старший (самый левый) бит - тупо выбросили. Осталось B11110100, что если вы переведете в десятичное число, и будет наше 244.
Но тут мы сказали в явном виде "считай это байтом".
А в 34*24*60... и т.п. - мы такого не говорили.
Но... компилятор сам пытается догадатся "какого типа числа". Когда мы сказали что число 2937600000 он, глядя на его размер - сам догадался что "это явно long". А вот когда он видит число небольшое, он предполагает что это int. Тоже пытается "оптимизировать".
И тут вступает в игру - порядок выполнения операторов. Он не пытается "умножить сразу все". А "по шагам".
Взял 34*24. Хм... вроде оба int, значит и результат int. Пытается догадатся о типе результата по типу параметров. Умножил их между собой. и запомнил в какую-то внутренюю переменную типа int Result1=34*24;. Идет дальше по цепочке Result1*60. Result1 - int, 60-тоже int. Значит Result2 тоже int. Result2=Result1*60... и т.д. Вообщем в тоге он проходит всю нашу цепочку умножения в уверенности что все время работает с типом int.
И где-то, в этой цепочке, результат умножения - не влазит в int. И обрезается. Получается неверное число :(
А вот когда я к первой цифре добавил UL, я в явном виде сказад "это unsigned long".
Тогда его логика такая.
Result1 это умножения (unsigned long) и (int). Значит результатом может (unsigned long) Быть.
Result2, это умножение Result1( который unsigned long) и (int) - значить Result2 - тоже UL....
и так далее, по всей цепочке. Поэтому достаточно было явно указать тип только первому множителю. Этой подсказки хватило компилятору что-бы разобратся какого типа должны быть промежуточные вычисления.
Вообщем в первом случае у нас получился такой продяок умножения
А во втором случае
вот теперь стало понятно. :)
Update.
Спотыкнулся, когда пытался понять, именно на этом:
"Ну значит самый старший (самый левый) бит - тупо выбросили. "
вот теперь стало понятно. :)
Update.
Спотыкнулся, когда пытался понять, именно на этом:
"Ну значит самый старший (самый левый) бит - тупо выбросили. "
Так поняли или нет? Это был вопрос или "описание прошлого непонимания"?
Описание прошлого не понимания :)
OK. Так что, готовы к следующей серии или каникулы возмете? ;)
Если "готовы" - каким путем пойдем? Будем учится C-шным строкам или воспользуемся, нелюбимым мной, String, как в оригинальном скетче?
Ну как вы уже поняли с "первой серии", я не работал ни с "C-шным строкам" ни со String.) Так что все тики выбор за вами!:) и я думаю он очевиден: С строки :)
Хорошо. Но наверное уже вечером. А что-бы, пока небыло скучно :) перечитайте еще раз string
Попробуйте сделать, что-то такое:
Вам можно что-то вписывать только в //тут как-то заполняем
Дополнительных Serial.print/Serial.println, дополнительные строки и т.п. - запрещенно. Писать в коде что-то типа "ABCD..." - нельзя. Нужно именно с помощью for и присвоения элементов массива, правильно заполнить массив str. Что-бы вывелось в Serial вот такое:
Ну и... можно не сейчас, можно потом, посмотрите еще один вариант как можно было сделать sendTextMessage()
Переделывать свой скетч на этот вариант - не нужно. Просто - ознакомтесь что еще бывает такая функция как sprintf :) Погуглите что это за зверь и как его укрощать :)
а вот как перейти на новую строку - пока не пойму..
по простому так:
чувствую, что за str[10] = '\n'; получу по шапке от leshak ))))
.
чувствую, что за str[10] = '\n'; получу по шапке от leshak ))))
Конечно получите. Только за другое. С этим все впорядке. Выкрутились. Потом еще "чуть-чуть по другому сделаем".
Когда с граблями разберемся. Что же это за пример, без заботливо подложенных граблей?
Вот почему я массив объявил в 12 символов? 10-ть сами буквы. 11 -тый перевод строки. А 12-тый?
Так что перечитываем string - текстовые строки | Аппаратная платформа Arduino
И думаем "что же мы забыли". Наверное, все-таки, нужно что-то положить и в str[11]? :)
ах.. ну да.. '\0'.
ах.. ну да.. '\0'.
Ну да... знали бы вы сколько крови такое может попортить. Вот сейчас - вроде рабоатет (просто повезло что дальше в памяти сразу после строки - нули попались), а потом когда скетч разросся и в память дальше попало что-то не нулевое... или мусор пошел валится, или вообще "почему-то тупо виснет на Serial.print" и фиг догадаешься.
Вообщем, при работе со строками, ноль в конце - это самое святое :)
И вообщем-то, вам повезло что вы сделали сейчас эту ошибку (я надеялся на это) - лучше запомните :)
Теперь смотрите. Сейчас вы выкрутились тем, что знаете про одинарные кавычки. А если их использование тоже запретить?
Сможете? Использовать тот факт, что char и byte (код-символа) - это одно и тоже. Фактически вы уже использовали этот факт когда писали simv++ - "это просто число", поэтому так и возможно было написать что simvv - это код символа.
Вообщем сможете сделать этот for так, что-бы не содержал в себе никаких символов в явном виде. Никаких кавычек. Только хардкод :). Только цифры в чистом виде? Ascii Table - ASCII character codes and html, octal, hex and decimal chart conversion вам может пригодится.
Вообщем сможете сделать этот for так, что-бы не содержал в себе никаких символов в явном виде.
))) я именно с этого и начинал
так? ;)