GSM модем A6 в режиме TCP.
- Войдите на сайт для отправки комментариев
В продолжение работы, про которую писал в http://arduino.ru/forum/apparatnye-voprosy/gsm-a6-vopros-pro-ring . Там все закончилось жестоким разочарованием в софт сириале, да и не потеме ринга оно вобще.
В общем TCP я поднял, и добился работы, но есть вопросы.
Код.
#include "SSD1306.h" #define OLED_SCL_PIN 6 #define OLED_SDA_PIN 7 ssd1306_i2c(OLED_SCL_PIN, OLED_SDA_PIN, myOLED) ; typedef void (*pFn)(char* p); typedef boolean (*pFnb)(char* p); typedef struct sEventsMap { const char* Name; pFn Event; }; void (*pFnTimer)(void) ; int PauseTimer; byte StatModem; enum { UNKNOWN, CONNECT, BEGIN_INIT, END_INIT } STATMODEM ; /**************************************** Набор функций общения с экраном и последовательным каналом - пережитком прошлого софтового */ char InpStr[100]; void SerialOut(char* p=InpStr){ /*Serial.println(p);*/} void ClrScr(){myOLED.clrScr();} void DravString(byte str, byte row, char *p) { char *a; for(a=p;(*a!='\r') && (*a!='\n') && (*a!=0);a++) {}; char r=*a; *a=0; myOLED.drawString(str,row,p); *a=r; } //Модем хоть раз сказал ОК - конект есть, запускаем паузу перед инициализацией void ReciveAT_OK(char* p) { PauseTimer=millis()+2000; pFnTimer=BeginInit; StatModem=CONNECT; }; bool ActivAT; pFn EventOk=ReciveAT_OK; //базовая обработка ОК. Специализация в функции EventOk void ReciveOK(char* p){digitalWrite(13,LOW);ActivAT=false; if(EventOk) EventOk(p); }; pFn EventERROR; //базовая обработка ошибок. Специализация в функции EventERROR void ReciveERROR(char* p){digitalWrite(13,LOW); ActivAT=false; if(EventERROR) EventERROR(p); else SerialOut();}; #define DBG_ void SendAT(const __FlashStringHelper* at) { digitalWrite(13,HIGH); #ifdef DBG char v[20]; for(byte i=1;i<20;i++) { v[i]= pgm_read_byte(((int)at)+i-1); } v[0]='>'; v[19]=0; myOLED.drawString(0,6," "); myOLED.drawString(0,6,v); myOLED.drawString(0,7," "); #endif ActivAT=true; Serial.println(at); } void SendAT( PGM_P at) { digitalWrite(13,HIGH); #ifdef DBG myOLED.drawString(0,6,">"); myOLED.drawStringP(1,6,at); myOLED.drawString(0,7," "); #endif ActivAT=true; for(;char r=pgm_read_word(at);at++) { Serial.print(r); } Serial.println(); } /**************************** Много обработчиков сообщений модема, их таблица sEventsMap EventsMap[] */ void ReciveRING(char* p){ SerialOut(); ClrScr(); DravString(6,2,"R I N G");SendAT(F("AT+CLCC"));}; byte ConnectMode=0; pFn EventChngReg; void ChngReg(char* p) { byte r=(*(p+3))-'0'; if(r!=ConnectMode) if(EventChngReg) { ConnectMode=r; EventChngReg(p); } ConnectMode=r; } void ReciveCOPS(char* p) { p++; SerialOut(p); if((*p)!='(') //список всех сетей { DravString(0,7,p); return; } ClrScr(); byte n=1; for(char *a=p, *b=p;*a!=0;a++) { if((*a==')') && (*(a+1)!='"')) //life - козлы!!! { a++; *a=0; DravString(0,n,b); n++; b=a+1; } } } void ReciveSMS(char* p) { // Serial.println("SMS"); SerialOut(); ClrScr(); DravString(7,3,"S M S"); SendAT(F("AT+CMGF=1")); SendAT(F("AT+CMGR=1")); } void ReadSMS(char* p){ SerialOut();} void SignalQuality(char* p){ DravString(10,0,"sign:");DravString(15,0,p); SerialOut();} void ReadICCID(char* p) { char *a=p; for(;*a>=' ';a++) { if(!(((int)a)&3)) *a='*'; } *a=0; DravString(0,5,p); SerialOut(p); } void EndCALL(char* p){DravString(4,7,p);} void DetaleRING(char* p) { char *a=p;for(;*a!='"';a++); *a=0; DravString(3,4,p); *a='"';*(a+4)='*';*(a+7)='*';*(a+9)='*';*(a+11)='*'; DravString(1,5,a); } void(*RawData)(byte r, word l); class StreamFiltr { const char * et; byte c; byte l; public: StreamFiltr(const char * e) {et=e;l=strlen(et)-1;} boolean Find(char r) { if(r==et[c]) { if(c==l) { c=0; return true; } c++; } else c=0; return false; }; void Reset(void){c=0;} }; StreamFiltr FiltrCIPRCV("+CIPRCV:"); StreamFiltr FiltrTag_h4("<h4>"); StreamFiltr FiltrTag_A("<a href="); byte ConvUTF8(byte r) { static byte utf8_1; if(r&0x80) //UTF-8 только двухбайтовая кирилица { if(r&0x40) { utf8_1=r; return 0; } else { r=byte(r&0x3f | byte(utf8_1<<6)); if(r==0x051) r=0x35; //ё if(r==0x001) r=0x15; //Ё r+=0xb0; } } return r; } void OutTextScr(char r) { static byte y; static byte state; static byte cnt; static char buf[21]; switch(state) { case 0: FiltrTag_h4.Reset(); cnt=0; case 1: if(FiltrTag_h4.Find(r)) { y=0; cnt++; if(cnt==2) { state=2; ClrScr(); cnt=0; } } else state=1; break; case 2: if(!(r=ConvUTF8(r))) break; buf[cnt]=r; cnt++; if(cnt==20) { buf[20]=0; cnt=0; state=3; DravString(0,1,buf); } break; case 3: if(FiltrTag_A.Find(r)) state=4; break; case 4: if(r=='>') { cnt=0; state=5; } break; case 5: if(!(r=ConvUTF8(r))) break; buf[cnt]=r; cnt++; if(cnt==20) { buf[20]=0; DravString(0,y+3,buf); y++; if(y==5) { state=1; cnt=2; y=0; } else state=3; } break; } } void Send1(byte r, word l) { static char buf[8]; static byte ind; static byte ind2; static word cnt; if(cnt) { OutTextScr(r); cnt--; if(!cnt) { FiltrCIPRCV.Reset(); ind2=1; } return; } if(ind) { if(r==',') { buf[ind-1]=0; if(ind2) { cnt=atol(buf); ind=0; ind2=0; return; } ind2++; ind=1; } else { buf[ind-1]=r; if(ind<8) ind++; } return; } if(ind2) { if(FiltrCIPRCV.Find(r)) { ind=1; ind2=0; return; } } if(r=='>') { ClrScr(); DravString(6,3,"S E N D");delay(300); SendAT(F("GET / HTTP/1.0\r\nHost: arduino.ru\r\n")); FiltrCIPRCV.Reset(); DravString(4,3,"R E C I V E"); ind=0; ind2=1; } } sEventsMap EventsMap[]={ {"OK", ReciveOK}, {"RING", ReciveRING}, {" ERROR:", ReciveERROR}, {"+CME", ReciveERROR}, {"+CIEV:", EndCALL}, {"+CREG:", ChngReg}, {"+COPS:", ReciveCOPS}, {"^CINIT:", SerialOut}, {"+CMTI", ReciveSMS}, {"+CMGR", ReadSMS}, {"+CSQ:", SignalQuality}, {"+CCID:", ReadICCID}, {"+CLCC:", DetaleRING}, {0,0} //признак конца списка }; boolean ReciveATI_OK(char* p) { SerialOut(p); ClrScr(); byte n=0; char *b=p; char *a; byte StrEnd=0; for(a=p;*a!=0;a++) { if(*a==10) StrEnd|=1; else if(*a==13) StrEnd|=2; else StrEnd=0; if(StrEnd==3) { DravString(0,n,b); b=a+1; n++; } } DravString(0,n,b); DravString(0,4,"Маскированый ICCID:"); return true; }; boolean ReciveAT_CREG_OK(char* p) { if(ConnectMode!=1) { ReciveInit_ERROR(p); return false; //повторим эту команду } EventChngReg=ReciveInit_ERROR; //т.к. не стартует при незавершенной регистрации то перезапустим по её завершению DravString(5,7,"Поиск сетей..."); return true; }; boolean ReciveAT_QSQ_OK(char* p){ SerialOut();return true;}; boolean ReciveAT_GATT(char* p){ ClrScr(); return true;}; boolean LockalIP(char* p){ DravString(0,1,"IP:");DravString(3,1,p); SerialOut();delay(3000);return true;} typedef struct AT_COMMANDS { PGM_P at; pFnb Fn; pFn Err; } ; const char ATE[] PROGMEM ="ate 0"; const char ATI[] PROGMEM ="ati"; const char ATCCID[] PROGMEM ="at+ccid"; const char ATCREG[] PROGMEM ="at+creg?"; const char ATCOPS[] PROGMEM ="at+cops=?"; const char ATCOPSC[] PROGMEM ="at+cops?"; const char ATCSQ[] PROGMEM ="at+csq"; const char ATCGATT[] PROGMEM ="AT+CGATT=1"; const char ATCIPMUX[] PROGMEM ="AT+CIPMUX=1"; const char ATCGDCONT[] PROGMEM ="AT+CGDCONT=1,\"IP\",\"www.ab.kyivstar.net\""; const char ATCSTT[] PROGMEM ="AT+CSTT=\"www.ab.kyivstar.net\",\"\",\"\""; const char ATCIICR[] PROGMEM ="AT+CIICR"; const char ATCIFSR[] PROGMEM ="AT+CIFSR"; AT_COMMANDS AT_Command[]= { {ATE, NULL, NULL}, {ATI, ReciveATI_OK, NULL}, {ATCCID, NULL, NULL}, {ATCREG, ReciveAT_CREG_OK, NULL}, {ATCOPS, NULL, NULL}, {ATCOPSC, NULL, NULL}, {ATCSQ, ReciveAT_QSQ_OK, NULL}, {ATCGATT, ReciveAT_GATT, NULL }, {ATCSQ, ReciveAT_QSQ_OK, NULL}, {ATCIPMUX, NULL, NULL }, {ATCGDCONT, NULL, NULL }, {ATCSTT, NULL, NULL }, {ATCIICR, NULL, NULL }, {ATCGATT, NULL, NULL }, {ATCIFSR, LockalIP, NULL }, }; byte InitID; void SendCmd(void){ SendAT(AT_Command[InitID].at); } void BeginInit() { StatModem=BEGIN_INIT; EventOk=ReciveInit_OK; InitID=0; SendCmd(); } void ReciveInit_OK(char* p) { if(InitID<sizeof(AT_Command)/sizeof(AT_Command[0])) { EventChngReg=0; if(AT_Command[InitID].Err) EventERROR=AT_Command[InitID].Err; else EventERROR=ReciveInit_ERROR; if(AT_Command[InitID].Fn) { if(!AT_Command[InitID].Fn(p)) return; } InitID++; if(InitID<sizeof(AT_Command)/sizeof(AT_Command[0])) SendCmd(); else { EventOk=SerialOut; StatModem=END_INIT; } } }; void ReciveInit_ERROR(char* p) { EventChngReg=0; AT_Command[InitID-1].Fn(p); } pFn OK_Fn=ReciveAT_OK; void TetraPack(byte *p, byte len, char *d) { for(byte i=0;i<len;i++,p++) { *d=0x30|*p&0x0f;d++; *d=0x30|*p>>4;d++; } } byte NumberServPack(byte *p, char *d){ byte l=(*p)-1; TetraPack(p+2, l, d); d[l<<1]=0;return l+2;} byte NumberPack(byte *p, char *d){ byte l=(*p)+1>>1; TetraPack(p+2, l, d); d[*p]=0;return l+2;} byte TimerPack(byte *p, char *d){ TetraPack(p, 7, d); d[14]=0;return 7;} byte TextPack(byte *p, char *dc) { word b=0; byte k=0; byte i=0; byte* d=(byte*)dc; byte c=*p; p+=1; for(;c;c--) { if(!i) { b=*p++; i=7; } Serial.print(b,HEX);Serial.print(" "); b|=(*p)<<i;p++; Serial.print(b,HEX);Serial.print(" "); *d=(byte)b&0xff; Serial.println(*d,HEX); d++; k++; b>>=8; i--; } *d=0; return k+1; } const byte sms[]= {0x07,0x91,0x83,0x90,0x01,0x44,0x60,0x10, 0x04, 0x0C,0x91,0x83,0x90,0x91,0x29,0x98,0x78, 0x00, 0x08, 0x71,0x40,0x02,0x91,0x70,0x24,0x00, 0x44, 0x30, 0x02, 0x00, 0x07, 0x47, 0x40, 0x02, 0x00, 0x06, 0x10, 0x06, 0x10, 0x06, 0x10, 0x06, 0x20,0x06}; void setup() { Serial.begin(9600); // mySerial.begin(9600); pinMode(13, OUTPUT); myOLED.begin(); myOLED.clrScr(); myOLED.setFont( SSD1306::FONT_SIZE_2); myOLED.drawString(1,1,"Пейджер"); myOLED.drawString(0,3,"ардуинщика"); myOLED.setFont( SSD1306::FONT_SIZE_1); delay(1000); /* char txt[100]; byte *r=(byte*)sms; r+=NumberServPack(r,txt); Serial.println(txt); r++; //не знаю что оно такое 04h TP-MTI First octet of this SMS-DELIVER message. r+=NumberPack(r,txt);Serial.println(txt); r++; //не знаю что оно такое TP-PID 00h r++; //не знаю что оно такое TP-DCS Data coding scheme 08h UCS2 r+=TimerPack(r,txt);Serial.println(txt); r+=TextPack(r,txt);Serial.println(txt); delay(2000); */ }; boolean ExecAT(char* pInpStr, byte currchar, sEventsMap *em); boolean ExecAT(char* pInpStr, byte currchar, sEventsMap *em) { sEventsMap *e=em; for(;e->Name;e++) { if(!memcmp(e->Name,pInpStr+currchar,strlen(e->Name))) { if(e->Event) { char *p=InpStr; if(e==em) pInpStr[currchar]=0; //если ОК - удаляем его else p+=strlen(e->Name); e->Event(p); } return true; } } return false; } void ProcessAT(byte r, sEventsMap *e); //wiring - dibiling /* процесс посимвольного приема данных с модема, формирует непустые строки и вызывает разбор команды из sEventsMap *e*/ void ProcessAT(byte r, sEventsMap *e) { static byte StrEnd; static char* pInpStr=InpStr; static char* pNewStr=InpStr; if(RawData) { RawData(r, 1000); return; } if(r==10) StrEnd|=1; else if(r==13) StrEnd|=2; else StrEnd=0; *pInpStr=r; if(pInpStr<(InpStr+sizeof(InpStr)-1)) pInpStr++; if(StrEnd==3) { if((pInpStr-pNewStr)==2) { StrEnd=0; pInpStr=pNewStr; return; } *pInpStr=0; #ifdef DBG DravString(0,7,"< "); DravString(1,7,pNewStr); // delay(10000); #endif if(ExecAT(InpStr, pNewStr-InpStr, e)) //ищем и исполняем команду, если успешно то удалим из буфера { pInpStr=InpStr; StrEnd=0; } pNewStr=pInpStr; } } int ww; void loop() { int T=millis(); for(;Serial.available();) //выгребаем все что принято, парсим и исполняем согласно { ProcessAT(Serial.read(), EventsMap); } if(StatModem==UNKNOWN) { SendAT(F("AT")); digitalWrite(13,ww&1); ww++; delay(ww%200?24:200); return; } if(pFnTimer) { if(T-PauseTimer>0) { pFnTimer(); pFnTimer=NULL; } } static int TimeCSQ; if(ActivAT || (StatModem!=END_INIT)) TimeCSQ=T; else if(T-TimeCSQ>1000) { static byte AT_state; TimeCSQ=T; switch(AT_state) { case 1: SendAT(F("AT+CIPSTART=\"TCP\",\"78.46.72.113\",80"));break; case 2: SendAT(F("AT+CIPSEND=0,34")); RawData=Send1; break; case 17: SendAT(F("AT+CIPSHUT"));break; case 18: SendAT(F("at+csq"));break; } if(AT_state<18) AT_state++; } }
Работает, выводит
Теперь на железном сириале. Подключен так Tx наны на RX модуля, RX наны на анод (ну плюс ;) диода а его анод к Tx модема. Электролит на питании.
Теперь вопросы.
1. Люди добрые, христа ради, подайте ссылку на даташит к модему. Только поновей, у меня есть v1.01 (с ней прошу не беспокоить), но в версии 3 сильно не так.
2. Кто сечет в АТ командах GPRS и ТСР. Я делаю соединение так перед отправкой запроса серверу:
"AT+CGATT=1";
"AT+CIPMUX=1";
В коде, который я к сожалению забыл под спойлер свернуть, остались следы приема входящего звонка и попыток работы с SMS. Состояние этого функционала неизвестно, когдато работало, а сейчас симка стала чисто TCP (происки оператора))) и не проверялось.
Поэксперементировал со скоростю обмена. Можна смело заменить 9600 на 38400, успевает. А вот 57600 уже нет.
Новее январской версии доков не видел. Здесь не смотрели http://m2msupport.net/m2msupport/at-command/
И тут ещё видео, правда на вражеском..:) https://www.youtube.com/watch?v=qtzrVj66IOA
Здесь не смотрели http://m2msupport.net/m2msupport/at-command/
Спасибо, глянул на http://m2msupport.net/m2msupport/at-command/ . Безрадостно. Там какаято софтина - АТ тестер, а в описании команд при ней например сказано
Send data
AT+CIPSEND
А наш А6 такое не понимает, понимает "AT+CIPSEND=0, 123", если не с 2-я параметрами то ругается. С вторым параметром все ясно, по аналогии с другими модулями это длина передаваемых далее данных (хотя не все ясно, какое максимальное значение не известно..). А первый не известен, возможно какойто идентификатор соединения или сессии или ХЗ. Но ноль проходит - вот и ладно. В общем без даташита конкретно на А6 тяжко.
Еще проблемка. Дисконектится корректно не выходит. По доке это
The command disconnects the wireless connection, except at the status of IP INITIAL. You can close moving scene by AT+CIPSHUT.
В реале после SendAT(F("AT+CIPSHUT=1")); вылазит ERROR:53. По доке это:
53 PARAM_INVALID.
И в остальных случаях когда я сталкивался с ней это так и означало.
Пробую SendAT(F("AT+CIPSHUT=0")); или SendAT(F("AT+CIPSHUT=1")) вылазит ERROR:50 , что по доке вроде как EXE_FAIL.
Тупик.
Вобщем неплохой модуль, но без нормальной документации. Пока её не появится использовать проблематично.
ПС. Команда AT+CIPCLOSE ведет ся аналогично.
Возможно соединение закрывается автоматом после выполнения AT+CIPSEND. Поскольку второй сенд не проходит так
а если вставить еще один AT+CIPSTART, перед вторым сендом то проходит все корректно.
На счёт свежих даташитов глянь тут http://wiki.ai-thinker.com/gprs , сам там смотрю , но так как с планшета и лимитированным интернетом. Там правда сайт на китайском, но когда нас останавливали это☺. Нашёл версию 1.03_1, нужна?? Это последняя на оф сайте
На третий вопрос из первого поста любой ответ от модема должен заканчиваться переводом строки "<CR><LF><response><CR><LF>".
А что есть <response>? наверно ОК или код ошибки..
Исходя из этого, получается если мне ответили 1400 байт и закрыли канал, то получу
"+CIPRCV: 0,1400ХХХХХ..."
"<CR><LF><response><CR><LF>".
Если ответили 1400 байт и не закрыли канал, то получу просто
"+CIPRCV: 0,1400ХХХХХ..."
Потом могут и чего дослать. Получу следующие "+CIPRCV:
Если ответили ответили 1401 байт и закрыли канал, то получу
"+CIPRCV: 0,1400ХХХХХ..."
"+CIPRCV: 0,1ХХХХХ..."
"<CR><LF><response><CR><LF>".
Так шоле?
ПС. Спасибо за ссылку, качнул, теперь ищу чем 7z разархивировать )))
Logik ты ответ в терминале смотришь, на компьютере???? Терминал не отображает эти символы
https://ru.m.wikipedia.org/wiki/Перевод_строки.
response - это сообщение которое отправляет модем.
Если соединить модем и контроллер то эти символы приходят на usart и по ним можно судить о начале и завершении передачи данных.
Если в шеснацитиричной системе рассматривать ответ от модема то он будет выглядеть так "0х0d 0x0a (данные от модема в том числе и+CIPRCV: 0,1400ХХХХХ только в шеснацетиричншеснацетиричном исчеслении ) 0х0d 0x0a."
для распаковки 7z тут http://www.7-zip.org
Я ответ все больше на ардуине смотрю, то что <CR><LF> - в си десятичное 13, 10 (\r\n или 0х0d 0x0a), это как бы и не обсуждается.
Речь вобще не о ответе модема при send-е, т.е. на "AT+CIPSEND. Чего я описал как "Непосредственно запрос так "AT+CIPSEND=0,34", где 34 - длина будущих данных, затем жду ">" после чего собственно... " Про <CR><LF> я опустил, как малозначимое. Там вобще вопросов нет. Но есть расхождение между "вашим и нашим", я жду ">" оно точно есть и после него только даввные отправляются. Еще - эхо я отключил, так замечу, для определенности ;)
И +CIPRCV: 0,1400ХХХХХ - в шестнадцетеричном только ХХХХХ, думаю и Вы это имели в виду.
Вопрос в другом - конец ответа (оно же - закрытие канала отвечающей стороной) как себя обозначает?
Глянул описание АТ версии 1.03. Оно Немного лучше. Гдето на таком уровне.
В старом было
В новом добавили к старому еще
Logik скинь на мыло skarm@mail.com новый даташит, а то с планшета не получаетьс скачать
отправил все архивы что скачал и вытянутое и переименованое из них описание команд a6_at_commands_V1.03.pdf Там в архивах есть любопытное, например сервер на пыхе для приема фоток с А6С. Я так мельком глянул, так вроде оно.
Залогировал ресив. На запрос сайта имею следующий ответ, его много, потому парежу и заменю ......
+CIPRCV:0,1400,HTTP/1.1 200 OK Server: nginx/1.2.5 Date: Sat, 13 May 2017 18:58:21 GMT ........................
^Z+CIPRCV:0,1400,ttps://*.google.com; report-uri /csp2.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" > ..........................
^Z+CIPRCV:0,1400,оÑиÑÑ Ð½Ð¾Ð²Ñй паÑÐ¾Ð»Ñ Ð¿Ð¾ ÑлекÑÑонной поÑÑе.">ÐабÑли паÑолÑ?</a></li> </ul></div> .........................
^Z+CIPRCV:0,433,;
+TCPCLOSED:0
OK
Жирным выделил то, что к протоколу обмена относится. В отношении ^Z не уверен, в мониторе квадратик, который при копипасте воспринимается как конец текста, на 99% оно. Но полагатся на него нельзя, поток данных вполне может быть и бинарный, тогда там этих символов валом будет.
В общем закрытие канала и завершение приема соответственно надо ловить по +TCPCLOSED:0.
ПС. Шоле китайцам доку написать о их модеме )))))
давай на державной мове? чтоб китайцы учились по европейски читать. :)
Державная на форуме - виринг! Диалект Си ;)
В отношении ^Z не уверен, в мониторе квадратик, который при копипасте воспринимается как конец текста, на 99% оно.
Не зря я себе процентик приберег, он и перевесил )))
Процесс завершения одного потока данных и начала следующего
0x68, 0x0d, 0x0a 0x0d, 0x0a, 0x00, "+CIPRCV:0,1400," , 0x74,....
где 0x68 - последний байт первого пакета в соответствии с ранее указаной длиной
0x74 - первый байт следующего.
как видим 0x0d, 0x0a 0x0d, 0x0a, 0x00, отсебячина модема, а "+CIPRCV:0,1400," вобщем понятно, выше расписывал.
Доработал код с учетом новых знаний.
В принципе делает все тоже, но более правильно и потенциально готов к доработке поддержки нескольких соединений одновременно. Интересно, сколько он может соединений по максимуму держать.
Logik глянь тут http://24dx.ru/forum/viewtopic.php?f=16&t=2540&p=60515
Глянул. С изложеным согласен, но ща это уже не ново. И не полно. Часики стянул себе, сейчас попробую.
Проверил часики, отрабатывают, но выдают неверное, типа:
"12/06/01,10:01:28+00"
шозанах? Я в америке и в прошлом? секунды и минуты считаются
Logik разобрался откуда у длинны данных (1400) ноги растут? Откуда взялось это значение?
А на счёт времени это вопрос к своему опсосу kyivstar наверное по гейропскому времени живут:-)
Максимальный размер пакета. Или буфера. Или еще какой хрени. В общем оно не существенно вобще.
Чето я сомниваюсь что в европе сейчас 10 часов. Да и минуты совсем не в тему идут. Но идут!
А воще должно быть UTC, т.е. гринвич, а оте нолики справа - часовой пояс.
По значению судя - это кавказ. Армянское или Кадыровское время.
1400 это MTU
Так часовой пояс +00
1400 по сути да, MTU, но не из обычных сетей, в езернете оно 1500 или немного меньше. Видать у мобильных его значение нетрадиционное, или просто у А6 так. Но вобщем оно до фени.
Прошло почти пол суток, а часы попрежнему дают
"12/06/01,10:07:24+00"
))))
Отсчет начинается по включению с 10:00. Надеюсь что это хотяб от оператора зависит, может у других заработает.
Установи принудительно время
AT+CCLK="17/05/15,11:33:40+8"
Чтоб установить время, его надо знать ;)
На кой оно тогда такое сдалось?
Ну на мобиле ты же время устанавливает?? Думаю тут тоже самое.
Нет конечно, не то же. В мобилке можна выбрать "синхронизировать время по сети". Возьмет его из GSMа.
В принципе через модем время можна получить
1. С сети GSM - чего тут не получается, вероятно из-за козней оператора, у меня симка специальная для GPRS, на неё (и с неё тоже) нельзя звонить и СМС слать. Про время мы с опсосом не договаривались ;) вот он сука и того... Скорей всего.
2. Из интернета - есть спецпротокол, его ПК использует. Т.к. ТСР работает, то должно срабатывать полюбому.
3. Из инета, залезть на подходящий сайт и с его страницы извлеч время. Точность пострадает но для 99% случаев хватит с головой.
Вот тут https://electronics.stackexchange.com/questions/82090/what-at-command-should-be-sent-to-modem-to-get-time в комментах нашел любопытную последовательность команд для обновления времени:
Вроде как после выхода из сети и повторной регистрации время в девайсе должно обновится. Все команды отрабатывают на нашем А6, правда время не меняется :( Может от сети зависит - я пробовал на Мегафоне.
А при наличии интернета - проще всего запросить любую страницу с любого HTTP сервера, в заголовке HTTP-ответа всегда указано время. Для экономии трафика лучше даже запрашивать несуществующую страницу - ответ будет "404 страница не найдена", но время в заголовке будет все равно.
b707, для экономии трафика следует использовать специальный протокол NTP - в смысле для получения времени. Я пробовал с ethernet shield - работает. Примеры доступны.
Пожалуйста, вчитайтесь в заголовок темы. Речь идет о TCP на модеме через голый GRPS, что совсем не то же самое, что на готовом Интернет-шильде с кучей библиотек. C ethernet shield и библиотекой это любой ребенок сможет. А на модеме в виде низкоуровневых UDP -запросов примеры есть? С удовольствием посмотрел бы пример кода.
Вот тут https://electronics.stackexchange.com/questions/82090/what-at-command-should-be-sent-to-modem-to-get-time в комментах нашел любопытную последовательность команд для обновления времени:
Вроде как после выхода из сети и повторной регистрации время в девайсе должно обновится. Все команды отрабатывают на нашем А6, правда время не меняется :( Может от сети зависит - я пробовал на Мегафоне.
++++ добавка - попробовал эти же команды с симкой Билайн - работает! Причем время обновилось даже без необходимости выхода и повторной регистрации в сети - сразу же после команды AT+CLTS=1.
Попробовал AT+CLTS=1. Результата не дало. От сети время зависит, тут без вариантов, сеть не выдала - сушите весла. А вытаскивать время из HTTP GET - таки да! Кудаж ему дется, получится.
Попробовал AT+CLTS=1. Результата не дало.
Logik, а выходить из сети и регистрироваться обратно пробовали? Я сейчас несколько раз потестил - одного только AT+CLTS=1 хватает не всегда, но с AT+COPS=2 результат стабильный.
ЗЫ С интересои изучаю Ваш код - он полезнее, чем даташит на А6 :)
Поправлю себя, время вытаскивать не из GET конечно, а из ответа на него, из записи типа Last-Modified: Sat, 16 Jan 2010 21:16:42 GMT
Пробовал
Причем последнее многократно повторяется каждые 2 секунды. Без результата, время не установилось а идет начиная с 10:00:00 в момент включения. Обещаный "+CTZU:" тоже не пришел ((
Отрефакторил код функции http_recive из предыущего кода. Она принимает поток TCP (в нашем случае ответ по HTTP) и должна быть и быстрой и гибкой (для разных сайтов разное искать и по разному) и без больших буферов. В общем очень напряжная. Теперь вместо старой http_recive нужно так.
А делает все так же, как и было. Зато теперь проще писать http_recive под каждый конкретный сайт. Класс вынес в либку.
Планирую занятся сайтами метеопрогноза, извлечь из них инфу, сразу с нескольких и выводить текущую и прогноз на экран. Такой себе информер.
ПС. Снова попробовал поднять скорость с 38400 до 57600. Нет. Похоже узкое место - вывод на экран. Похоже за время отрисовки строки приемный буфер Serial переполняется.
ПС. Снова попробовал поднять скорость с 38400 до 57600. Нет. Похоже узкое место - вывод на экран. Похоже за время отрисовки строки приемный буфер Serial переполняется.
Ну, это легко проверить - Serial.available возвращает как раз заполненную длину буфера.
Если предположение подтвердится, строку можно выводить посимвольно, чередуя с проверкой последовательного порта.
PS. По моим прикидкам OLED 0.96 I2C на скорости 400 кГц способен выводить символ примерно за 0.3 мс, что меньше скорости порта на 38400 или 57600. Но стОит проверить библиотеку дисплея - какую скорость обеспечивает она, не исключено, что на порядок меньше максимально возможной.
Какой интерфейс у экрана?
ПС. Снова попробовал поднять скорость с 38400 до 57600. Нет. Похоже узкое место - вывод на экран. Похоже за время отрисовки строки приемный буфер Serial переполняется.
Ну, это легко проверить - Serial.available возвращает как раз заполненную длину буфера.
Если предположение подтвердится, строку можно выводить посимвольно, чередуя с проверкой последовательного порта.
Уже подтвердилось)) Оказалось достаточно закоментировать ClrScr(); и заработало на 57600. Очистка экрана - тяжкое дело. На ней буфер и переполнялся. А дальше чегото конечно терялось, но видать ненужное, и шел процесс выгребания из буфера, но не сильно быстро, на разности скоростей заполнения и извлечения. И к моменту прихода интересной инфы буфер был (вероятно) достаточно заполнен. Вот оно после нескольких строк и вылетало.
Про вывод посимвольно - как бы да но в реале есть подводный камень. Один символ требует передачи координат и пр. служебной инфы типа адреса на i2c . Для строки оно только раз для первого передается. Можна конечно обойти, но либу работы с экраном прийдется сильно перепахать.
ПС. Прикинул при частоте i2c 800КГц передать 1КБ, для экрана 1306 это весь экран, занимает 13мсек. А за это время на 57600 прийдут 76 байт. Переполнение однозначно. А вот оценить скорость извлечения из буфера - не просто. Тут проще померить фактическую.
Ну, это хорошо, если адрес передается один раз на строку. Мне попадалась библиотека, в которой адрес передавался с каждым байтом, т.е. по 6 раз на каждый символ.
Так нет придела кривости софта ))))
Logik, Вы тут больше всех опыта имеете :) Скажите, Вы заморачивались хард-ресетом модуля с ардуины замыканием RST пина на землю? Если делали - то как, напрямую через ардуину или через управляющую цепь, реле или мосфет?
А на тему HTTP вот еще интересную ссылку нашел - может пригодится https://gitlab.me-soldesign.com/Tobias/A6HTTPLibrary/tree/master
Я не заморачивался. Но по хорошему, если устройство должно долго работать автономно и при прыжках питания - я бы делал. Без мосфетов, просто RST на вывод ардуины.
Про либу - плохо. Софтсириал и сирмал одновременно не работают. У софтсириал вобще конфликт между передачей и приемом, он сам себя принять не может. Этот конфликт - проблема при приеме асинхронного ответа. Кстати этот ответ приходит с "+CIPRCV" чего вобще в либе не видно. Потому как все принятое просто отправляется в сириал с метками времени. Этот подход просто для попробовать, поигратся годится. Ну а это:
- очень грубая ошибка.
Просто RST на выход ардуины боязно - в даташите ток через RST обозначен до 70ма. Хотя надо будет проверить... может на реальном модеме меньше.
Что касается либы - извиняйте, что так вышло, я в нее подробно не вглядывался. Насколько понял, там автор упор делал на отправку HTTP PUT запроса, а прием ответа он не прорабатывал. Вообще, мне по инету попадались три-четыре набора библиотек, связанных с нашим A6. Но все либо недоделанные, либо написано очень коряво. Если делать свою библиотеку - проще всего взять за основу либу для SIM800/900 шилда, ибо все равно работа с модемом идет на основе AT-команд, а они практически те же. Я тут начал слегка патчить код SIM900 под наш модем - вполне получается, тестовые примеры отправки, чтения, стирания СМС из библиотеки 900-ого шилда заработали на нашем модеме. А некоторые процедуры вообще работают без правки.
- очень грубая ошибка.
кстати... а почему?
reply локальная переменная, распологается в стеке и существует только до завершения работы функции, вернув указатель на неё наружу получим возможность обращатся к ней после того как её область памяти освобождена. Но свободной память будет не долго, туда разместится нечто другое и соответственно при чтении мы получим искаженные данные а при записи испортим то, что туда попало. Самое печальное что такой код иногда будет работать нормально, но подглючивать, а после некоторых доработок может и стабильно неработать, при попытке локализовать проблему снова заработает или начнут портится данные в других модулях, никак не связаных с этим. И в поведении глюка логику обнаружить невозможно, начинает валится там, где не трогали, а попускает при правке совсем непричастного. В общем страшный сон програмера..
Вроде как возврат значения локальной переменной как результирующее значение процедуры допускается, разве нет?
возврат значения - да, а указателя на локальную переменную - ошибка. Т.к. этой переменной уже нет. Стоит в вашем примере добавить два символа как он становится ошибкой с описаными выше свойствами.