- Get link
- X
- Other Apps
- Get link
- X
- Other Apps
In English
Часть 1 ЗДЕСЬ.
Это продолжение моей серии постов о GSM / GPRS модуле Neoway M590:
- Neoway M590 GPRS Tutorial: sending and receiving files from/to SD card
- Application of Neoway M590: remote control for garage heater
- SMS GPS-трекер из Neoway M590 and ublox NEO-6 часть 1. Собираем детали и тестим
Ссылки на даташиты по используемому оборудованию:
- Neoway M590 Hardware design manual
- Neoway M590 AT command set
- ublox NEO-6 datasheet
- ublox NEO-6 receiver specs
- ATMega 328P datasheet
Принципиальная схема и сборка
Соотвествие пинов Arduino физическим ножкам микросхемы было определено из этого полезного рисунка:
Как вы, возможно, заметили, на схеме появилась новая ножка на модуле Neoway M590 - DTR. Эта нога используется для перевода GSM-модуля в спящий режим. Потребление тока в режиме сна сокращается в десять раз, так что это нам точно нужно.
К сожалению, моя платка с модемом не выводит DTR на штыревой разъем. Придется внести некоторые изменения:
Следующим логичным шагом будет включить паяльник и порыться по мастерской в поисках кусочка макетной платы.
Мой девайс выполнен в форм-факторе "сэндвич". Такой стиль, как мне показалось, приведет к самой компактной сборке. Платы расположены как 3 слоя: верхний (нижний на самом деле, первое фото перевернуто) занят модулем Neoway M590 с его огромным фильтрующим конденсатором, середина - ATMega и всей самодельной схемой, и нижний (верхний, то есть) с GPS-модулем и обеими антеннами (поэтому этот слой я и считаю верхним, антенны располагаются в верхней части устройства).
Вот пара фото трекера в процессе сборки:
После включения устройства ничего не должно нагреться и дымиться. Если что-то все же дымится или нагревается, еще раз проверьте все соединения и повторите попытку. Как только оборудование работает, можно перейти к кодингу...
Готовое устройство |
Программирование
Выбор ATMega328 не был случайным, я специально выбрал ее для упрощения процесса программирования при помощи Arduino.Берем программатор и подключаем к 6-контактному разъему для программирования.
Вот такой программатор я использую (ссылки на переходник и программатор):
![]() |
Программатор USBASP + переходник с 10 на 6 контактов. |
Запустим Arduino IDE попробуем загрузить любой пример кода чтобы проверить соединения и чип: выбираем целевую платформу и программатор и щелкаем "Загрузить при помощи программатора" (в большинстве ОС это Ctrl+Shift+U).
|
|
Так просто это лично у меня не получилось (ничего никогда не работает из коробки, блин!).
![]() |
Вот так мы будет дебажить нашу загрузку кода. Лично у меня всегда стоит эта галочка. |
Отмечаем в настройках Arduino IDE режим детального вывода информации при загрузки, и видим следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | Arduino: 1.8.3 (Linux), Board: "Arduino Pro or Pro Mini, ATmega328 (3.3V, 8 MHz)" Sketch uses 956 bytes (3%) of program storage space. Maximum is 30720 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes. /home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/bin/avrdude -C/home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf -v -V -patmega328p -cusbasp -Pusb -Uflash:w:/tmp/arduino_build_212825/Blink.ino.hex:i avrdude: Version 6.3, compiled on Jan 17 2017 at 06:01:25 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2014 Joerg Wunsch System wide configuration file is "/home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf" User configuration file is "/home/vadim/.avrduderc" User configuration file does not exist or is not a regular file, skipping Using Port : usb Using Programmer : usbasp AVR Part : ATmega328P Chip Erase delay : 9000 us PAGEL : PD7 BS2 : PC2 RESET disposition : dedicated RETRY pulse : SCK serial program mode : yes parallel program mode : yes Timeout : 200 StabDelay : 100 CmdexeDelay : 25 SyncLoops : 32 ByteDelay : 0 PollIndex : 3 PollValue : 0x53 Memory Detail : Block Poll Page Polled Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 Programmer Type : usbasp Description : USBasp, http://www.fischl.de/usbasp/ avrdude: auto set sck period (because given equals null) avrdude: AVR device initialized and ready to accept instructions Reading | An error occurred while uploading the sketch ################################################## | 100% 0.01s avrdude: Device signature = 0x000100 avrdude: Expected signature for ATmega328P is 1E 95 0F Double check chip, or use -F to override this check. avrdude done. Thank you. This report would have more information with "Show verbose output during compilation" option enabled in File -> Preferences. |
После пары часов ковыряния Гугла я нашел основную причину ошибки. Очевидно, чипы ATMega328P на заводе прошиваются на использование внутреннего генератора 1 МГц. Это слишком медленно, чтобы запрограммировать их с помощью USBASP с настройками, с которыми IDE вызывает его.
Первая мысль, которая пришла ко мне после выяснения этого была: «Ну отлично, почему оно не работает я знаю, но как теперь проверить, что чип не дохлый?»
Для этого нам понадобится avrdude. Выполним эту строку, чтобы проверить соединение с чипом без каких-либо изменений в прошивке (найдено на stackexchange):
1 | avrdude -p m328p -B 12 -c usbasp -P usb -U flash:r: /dev/null :i |
Если это работает, то наша ATMega работает и программируется. Теперь вот что надо сделать чтобы обойти медленный внутренний тактовый генератор: во-первых, создадим новую «медленную» версию программатора USBASP для Arduino IDE. Современные версии IDE запихивают конфигурационные файлы далеко в папку пользователя (Для Windows будет что-то вроде C:\Users\Пользователь\AppData\Local\Arduino15\packages\arduino\hardware\avr\Версия)
![]() |
Пришлось покопаться на жестком диске чтобы их найти... |
1 2 3 4 5 6 | slowusbasp.name=Slow USBasp slowusbasp.communication=usb slowusbasp.protocol=usbasp slowusbasp.program.protocol=usbasp slowusbasp.program.tool=avrdude slowusbasp.program.extra_params=-Pusb -B12 |
Перезапускаем Arduino IDE, выбираем программатор 'Slow USBASP' и пробуем загрузить снова... Работает! Скетч Blink.hex грузится, хотя частота мигания кажется... низковатой. В 8 раз медленне чем надо, если быть точным. Почему? Смотрим в лог загрузки... Ага! Фьюзы совершенно неправильные!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | [vadim@deepblue ~]$ /home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/bin/avrdude -C/home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf -v -V -patmega328p -cusbasp -Pusb -B12 -Uflash:w:/tmp/arduino_build_956669/Blink.ino.hex:i avrdude: Version 6.3, compiled on Jan 17 2017 at 06:01:25 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2014 Joerg Wunsch System wide configuration file is "/home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf" User configuration file is "/home/vadim/.avrduderc" User configuration file does not exist or is not a regular file, skipping Using Port : usb Using Programmer : usbasp Setting bit clk period : 12.0 AVR Part : ATmega328P Chip Erase delay : 9000 us PAGEL : PD7 BS2 : PC2 RESET disposition : dedicated RETRY pulse : SCK serial program mode : yes parallel program mode : yes Timeout : 200 StabDelay : 100 CmdexeDelay : 25 SyncLoops : 32 ByteDelay : 0 PollIndex : 3 PollValue : 0x53 Memory Detail : Block Poll Page Polled Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 Programmer Type : usbasp Description : USBasp, http://www.fischl.de/usbasp/ avrdude: set SCK frequency to 32000 Hz avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.01s avrdude: Device signature = 0x1e950f (probably m328p) avrdude: safemode: hfuse reads as D9 avrdude: safemode: efuse reads as FF avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: set SCK frequency to 32000 Hz avrdude: reading input file "/tmp/arduino_build_956669/Blink.ino.hex" avrdude: writing flash (956 bytes): Writing | ################################################## | 100% 6.21s avrdude: 956 bytes of flash written avrdude: safemode: hfuse reads as D9 avrdude: safemode: efuse reads as FF avrdude: safemode: Fuses OK (E:FF, H:D9, L:62) avrdude done. Thank you. |
> avrdude: safemode: Fuses OK (E:FF, H:D9, L:62).
По-моему совсем не OK. Почему-то несмотря на то, что настройки фьюзов указаны для каждой платы в файле board.txt, IDE не прожигает правильные фьюзы даже если они неверные. Фьюзы прожигаются только при записи загрузчика, а мне он не нужен.
AVRDude просто нуждается в небольшом толчке в правильном направлении, чтобы прожечь правильные фьюз биты. Просто оставим Arduino IDE открытым (чтобы Blink.hex не удалился), скопируем команду, которая загружает Blink.hex и запустим ее в консоли с небольшим дополнением в конце:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | [vadim@deepblue ~]$ /home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/bin/avrdude -C/home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf -v -V -patmega328p -cusbasp -Pusb -B12 -Uflash:w:/tmp/arduino_build_956669/Blink.ino.hex:i -U efuse:w:0xfd:m -U hfuse:w:0xda:m -U lfuse:w:0xff:m avrdude: Version 6.3, compiled on Jan 17 2017 at 06:01:25 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2014 Joerg Wunsch System wide configuration file is "/home/vadim/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino9/etc/avrdude.conf" User configuration file is "/home/vadim/.avrduderc" User configuration file does not exist or is not a regular file, skipping Using Port : usb Using Programmer : usbasp Setting bit clk period : 12.0 AVR Part : ATmega328P Chip Erase delay : 9000 us PAGEL : PD7 BS2 : PC2 RESET disposition : dedicated RETRY pulse : SCK serial program mode : yes parallel program mode : yes Timeout : 200 StabDelay : 100 CmdexeDelay : 25 SyncLoops : 32 ByteDelay : 0 PollIndex : 3 PollValue : 0x53 Memory Detail : Block Poll Page Polled Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 Programmer Type : usbasp Description : USBasp, http://www.fischl.de/usbasp/ avrdude: set SCK frequency to 32000 Hz avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.01s avrdude: Device signature = 0x1e950f (probably m328p) avrdude: safemode: hfuse reads as D9 avrdude: safemode: efuse reads as FF avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: set SCK frequency to 32000 Hz avrdude: reading input file "/tmp/arduino_build_956669/Blink.ino.hex" avrdude: writing flash (956 bytes): Writing | ################################################## | 100% 6.23s avrdude: 956 bytes of flash written avrdude: reading input file "0xfd" avrdude: writing efuse (1 bytes): Writing | ################################################## | 100% 0.02s avrdude: 1 bytes of efuse written avrdude: reading input file "0xda" avrdude: writing hfuse (1 bytes): Writing | ################################################## | 100% 0.02s avrdude: 1 bytes of hfuse written avrdude: reading input file "0xff" avrdude: writing lfuse (1 bytes): Writing | ################################################## | 100% 0.01s avrdude: 1 bytes of lfuse written avrdude: safemode: hfuse reads as DA avrdude: safemode: efuse reads as FD avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF) avrdude done. Thank you. |
Это должно решить проблему с аномальной скоростью чипа! Теперь можно переключиться на обычную конфигурацию USBASP в среде Arduino, поскольку теперь AVR работает на частоте 8 МГц.
Скетч Arduino
Вот финальный код для отправления GPS-координаты (а также опционально даты и времени GPS и баланса SIM-карты) в ответ на звонок (без энергосберегающих режимов). Вам нужно будет установить библиотеки AltSoftSerial и NeoGPS, чтобы все заработало:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 | //------------------------------------------------------------ // User-defined settings #define SEND_GPS_TIMESTAMP #define SEND_SIM_BALANCE const String MY_NUMBER = "79518821388" ; const String BALANCE_CODE = "*100#" ; // End of user-defined settings //------------------------------------------------------------ //------------------------------------------------------------ // Pins and baud rates #define M590_BAUD 9600 #define GPS_BAUD 9600 // Wake up pin. Wakeup triggered on low #define M590_RING 2 // Put to sleep pin. #define M590_DTR 3 #define LED_PIN 4 // End of pins and baud rates //------------------------------------------------------------ //------------------------------------------------------------ // Includes and variables #include <NMEAGPS.h> NMEAGPS gps; gps_fix fix; #define gpsPort Serial #include <AltSoftSerial.h> // AltSoftSerial always uses these pins: // // Board Transmit Receive PWM Unusable // ----- -------- ------- ------------ // Arduino Uno 9 8 10 AltSoftSerial m590Serial; const String MY_NUMBER_WITH_PLUS = "+" + MY_NUMBER; // End of includes and variables //------------------------------------------------------------ //------------------------------------------------------------ // Check that the GPS config files are set up properly (NMEAGPS_cfg.h) # if !defined( NMEAGPS_PARSE_GGA ) #error You must uncomment NMEAGPS_PARSE_GGA in NMEAGPS_cfg.h! #endif # if !defined( NMEAGPS_PARSE_GLL ) #error You must uncomment NMEAGPS_PARSE_GLL in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_GSA ) #error You must comment NMEAGPS_PARSE_GSA in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_GSV ) #error You must comment NMEAGPS_PARSE_GSV in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_GST ) #error You must comment NMEAGPS_PARSE_GST in NMEAGPS_cfg.h! #endif # if !defined( NMEAGPS_PARSE_RMC ) #error You must uncomment NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_VTG ) #error You must comment NMEAGPS_PARSE_VTG in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_ZDA ) #error You must comment NMEAGPS_PARSE_ZDA in NMEAGPS_cfg.h! #endif #ifdef NMEAGPS_INTERRUPT_PROCESSING #error You must comment NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h! #endif #ifdef NMEAGPS_STATS #error You must comment NMEAGPS_STATS in NMEAGPS_cfg.h! #endif #ifndef NMEAGPS_RECOGNIZE_ALL #error You must define NMEAGPS_RECOGNIZE_ALL in NMEAGPS_cfg.h! #endif //------------------------------------------------------------ // Check that the GPS config files are set up properly (GPSfix_cfg.h) # if !defined( GPS_FIX_DATE ) #error You must uncomment GPS_FIX_DATE in GPSfix_cfg.h! #endif # if !defined( GPS_FIX_TIME ) #error You must uncomment GPS_FIX_TIME in GPSfix_cfg.h! #endif # if !defined( GPS_FIX_LOCATION ) #error You must uncomment GPS_FIX_LOCATION in GPSfix_cfg.h! #endif # if defined( GPS_FIX_LOCATION_DMS ) #error You must comment GPS_FIX_LOCATION_DMS in GPSfix_cfg.h! #endif # if defined( GPS_FIX_ALTITUDE ) #error You must comment GPS_FIX_ALTITUDE in GPSfix_cfg.h! #endif # if defined( GPS_FIX_SPEED ) #error You must comment GPS_FIX_SPEED in GPSfix_cfg.h! #endif # if defined( GPS_FIX_HEADING ) #error You must comment GPS_FIX_HEADING in GPSfix_cfg.h! #endif # if defined( GPS_FIX_SATELLITES ) #error You must comment GPS_FIX_SATELLITES in GPSfix_cfg.h! #endif # if defined( GPS_FIX_HDOP ) #error You must comment GPS_FIX_HDOP in GPSfix_cfg.h! #endif # if defined( GPS_FIX_VDOP ) #error You must comment GPS_FIX_VDOP in GPSfix_cfg.h! #endif # if defined( GPS_FIX_PDOP ) #error You must comment GPS_FIX_PDOP in GPSfix_cfg.h! #endif # if defined( GPS_FIX_LAT_ERR ) #error You must comment GPS_FIX_LAT_ERR in GPSfix_cfg.h! #endif # if defined( GPS_FIX_LON_ERR ) #error You must comment GPS_FIX_LON_ERR in GPSfix_cfg.h! #endif # if defined( GPS_FIX_ALT_ERR ) #error You must comment GPS_FIX_ALT_ERR in GPSfix_cfg.h! #endif # if defined( GPS_FIX_GEOID_HEIGHT ) #error You must comment GPS_FIX_GEOID_HEIGHT in GPSfix_cfg.h! #endif void setup() { pinMode(M590_RING, INPUT); pinMode(M590_DTR, OUTPUT); pinMode(LED_PIN, OUTPUT); gpsPort.begin(GPS_BAUD); m590Serial.begin(M590_BAUD); //------------------------------------------------------------ // M590 GSM modem config digitalWrite(M590_DTR, HIGH); // Basic health check sendAndWait( "AT" , "OK" ); // Get caller's number sendAndWait( "AT+CLIP=1" , "OK" ); // Set SMS mode to text sendAndWait( "AT+CMGF=1" , "OK" ); // Set character set to GSM (aka ASCII) sendAndWait( "AT+CSCS=\"GSM\"" , "OK" ); // End of M590 GSM modem config //------------------------------------------------------------ // Wait for SIM to be registered in local network sendAndWait( "AT+CREG?" , "+CREG: 0,1" ); // One long blink - GSM ready digitalWrite(LED_PIN, HIGH); newDelay( 500 ); digitalWrite(LED_PIN, LOW); //------------------------------------------------------------ // Wait until the fix is available getFix(); // Two short blinks - GPS ready digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); newDelay( 100 ); digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); } void loop() { // Wait for a call (from my number) waitFor2Strings( "RING" , MY_NUMBER); // waitForString("RING"); // Decline the call m590Serial.println( "ATH0" ); digitalWrite(LED_PIN, HIGH); // Get a fix and write it to global variable getFix(); sendSms(fix, MY_NUMBER_WITH_PLUS); digitalWrite(LED_PIN, LOW); } void getFix() { bool noFix = true ; while (noFix) { while (gps.available( gpsPort )) { fix = gps.read(); if (fix.valid.location #ifdef SEND_GPS_TIMESTAMP && fix.valid.date && fix.valid.time #endif ) { noFix = false ; break ; } } } } void sendAndWait(String cmd, String resp) { bool gotString = false ; m590Serial.setTimeout( 1000 ); m590Flush(); while (!gotString) { m590Serial.println(cmd); gotString = checkString(m590Serial.readString(), resp); } } void waitFor2Strings(String in1, String in2) { bool gotStrings = false ; m590Serial.setTimeout( 1000 ); String val; while (!gotStrings) { val = m590Serial.readString(); gotStrings = checkString(val, in1) && checkString(val, in2); } } void waitForString(String in) { bool gotString = false ; m590Serial.setTimeout( 1000 ); m590Flush(); while (!gotString) { gotString = checkString(m590Serial.readString(), in); } } bool checkString(String val, String in) { return (val.indexOf(in) > - 1 ); } void sendSms(gps_fix & fix, String number) { m590Flush(); #ifdef SEND_SIM_BALANCE String b = getBalance(); #endif // Wait for prompt > sendAndWait( "AT+CMGS=\"" + number + "\"" , ">" ); #ifdef SEND_GPS_TIMESTAMP m590Serial.print(fix.dateTime.year); m590Serial.print( '-' ); m590Serial.print(fix.dateTime.month); m590Serial.print( '-' ); m590Serial.print(fix.dateTime.date); m590Serial.print( ',' ); m590Serial.print(fix.dateTime.hours); m590Serial.print( ':' ); m590Serial.print(fix.dateTime.minutes); m590Serial.print( ':' ); m590Serial.print(fix.dateTime.seconds); sendAndWait( " UTC" , ">" ); #endif // Send maps.google.com/blabla m590Serial.print(link); // Lat + lon from fix m590Serial.print( fix.latitude(), 6 ); m590Serial.print( ',' ); m590Serial.print( fix.longitude(), 6 ); #ifdef SEND_SIM_BALANCE sendAndWait( "" , ">" ); m590Serial.print( "Balance is " ); m590Serial.print(b); m590Serial.print( " RUR." ); #endif // End character m590Serial.print(( char ) 0x1A ); } String getBalance() { m590Flush(); // Get current balance. This can take a while, so I set timeout to 10s String bal = sendAndGetResp( "ATD" + BALANCE_CODE, "OK" , 10 ); int startMes = bal.indexOf( '\"' ); int endMes = bal.lastIndexOf( '\"' ); // Crop response so that only message is left bal = bal.substring(startMes + 1 , endMes); int firstDigit = - 1 ; int lastDigit = - 1 ; for ( int i = 0 ; i < bal.length(); i++) { if (firstDigit == - 1 ) { // locate first digit in message if (isDigit(bal.charAt(i))) { firstDigit = i; } } else { // locate last digit in message if (!(isDigit(bal.charAt(i)) || (bal.charAt(i) == ',' ) || (bal.charAt(i) == '.' ))) { lastDigit = i; break ; } } } // Crop the message to contain only float value bal = bal.substring(firstDigit, lastDigit); return bal; } String sendAndGetResp(String cmd, String resp, int tries) { bool gotString = false ; String rawResponse = "" ; m590Serial.setTimeout( 1000 ); m590Flush(); while (!gotString) { m590Serial.println(cmd); for ( int i = 0 ; i < tries; i++) { rawResponse += m590Serial.readString(); gotString = checkString(rawResponse, resp); if (gotString) { break ; } } } if (!gotString) { rawResponse = "\"Balance is 999.999\"" ; } return rawResponse; } void newDelay( int ms) { m590Serial.setTimeout(ms); m590Serial.readString(); } void m590Flush() { while (m590Serial.available()) { m590Serial.read(); } } |
Пришлось обойти один сильный недостаток библиотеки AltSoftSerial, а именно тот факт что все функции которые зависят от Timer0 перестают работать - delay() and millis(). Чтобы заново получить функцию задержки выполнения кода на определенное время, я использую функцию setTimeout() у объекта Serial, оттуда и взялась функция newDelay().
Тот же самый скетч с энергосбережением скоро появится чуть ниже... Пока что можно использовать верхний скетч, чтобы получать координаты трекера в виде ссылки на Google Maps в ответ на Ваш звонок. Просто этот скетч будет быстро разряжать батарею.
ОБНОВЛЕНИЕ 4 июля 2017
Код ниже отправляет вам те же данные, что и предыдущий, только теперь он использует режимы энергосбережения для AVR и Neoway M590. Дополнительно понадобится установить библиотеку LowPower. ATMega и GSM-модем проводят большую часть времени в режиме энергосбережения, пока кто-то не позвонит или не отправит СМС.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | //------------------------------------------------------------ // User-defined settings #define SEND_GPS_TIMESTAMP #define SEND_SIM_BALANCE const String MY_NUMBER = "79271111111" ; const String BALANCE_CODE = "*100#" ; // End of user-defined settings //------------------------------------------------------------ //------------------------------------------------------------ // Pins and baud rates #define M590_BAUD 9600 #define GPS_BAUD 9600 // Wake up pin. Wakeup triggered on low #define M590_RING 2 // Put to sleep pin. #define M590_DTR 3 #define LED_PIN 4 // End of pins and baud rates //------------------------------------------------------------ //------------------------------------------------------------ // Includes and variables #include "LowPower.h" #include <NMEAGPS.h> NMEAGPS gps; gps_fix fix; #define gpsPort Serial #include <AltSoftSerial.h> // AltSoftSerial always uses these pins: // // Board Transmit Receive PWM Unusable // ----- -------- ------- ------------ // Arduino Uno 9 8 10 AltSoftSerial m590Serial; const String MY_NUMBER_WITH_PLUS = "+" + MY_NUMBER; struct gotstrings { bool got1; bool got2; }; // End of includes and variables //------------------------------------------------------------ //------------------------------------------------------------ // Check that the GPS config files are set up properly (NMEAGPS_cfg.h) # if !defined( NMEAGPS_PARSE_GGA ) #error You must uncomment NMEAGPS_PARSE_GGA in NMEAGPS_cfg.h! #endif # if !defined( NMEAGPS_PARSE_GLL ) #error You must uncomment NMEAGPS_PARSE_GLL in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_GSA ) #error You must comment NMEAGPS_PARSE_GSA in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_GSV ) #error You must comment NMEAGPS_PARSE_GSV in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_GST ) #error You must comment NMEAGPS_PARSE_GST in NMEAGPS_cfg.h! #endif # if !defined( NMEAGPS_PARSE_RMC ) #error You must uncomment NMEAGPS_PARSE_RMC in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_VTG ) #error You must comment NMEAGPS_PARSE_VTG in NMEAGPS_cfg.h! #endif # if defined( NMEAGPS_PARSE_ZDA ) #error You must comment NMEAGPS_PARSE_ZDA in NMEAGPS_cfg.h! #endif #ifdef NMEAGPS_INTERRUPT_PROCESSING #error You must comment NMEAGPS_INTERRUPT_PROCESSING in NMEAGPS_cfg.h! #endif #ifdef NMEAGPS_STATS #error You must comment NMEAGPS_STATS in NMEAGPS_cfg.h! #endif #ifndef NMEAGPS_RECOGNIZE_ALL #error You must define NMEAGPS_RECOGNIZE_ALL in NMEAGPS_cfg.h! #endif //------------------------------------------------------------ // Check that the GPS config files are set up properly (GPSfix_cfg.h) # if !defined( GPS_FIX_DATE ) #error You must uncomment GPS_FIX_DATE in GPSfix_cfg.h! #endif # if !defined( GPS_FIX_TIME ) #error You must uncomment GPS_FIX_TIME in GPSfix_cfg.h! #endif # if !defined( GPS_FIX_LOCATION ) #error You must uncomment GPS_FIX_LOCATION in GPSfix_cfg.h! #endif # if defined( GPS_FIX_LOCATION_DMS ) #error You must comment GPS_FIX_LOCATION_DMS in GPSfix_cfg.h! #endif # if defined( GPS_FIX_ALTITUDE ) #error You must comment GPS_FIX_ALTITUDE in GPSfix_cfg.h! #endif # if defined( GPS_FIX_SPEED ) #error You must comment GPS_FIX_SPEED in GPSfix_cfg.h! #endif # if defined( GPS_FIX_HEADING ) #error You must comment GPS_FIX_HEADING in GPSfix_cfg.h! #endif # if defined( GPS_FIX_SATELLITES ) #error You must comment GPS_FIX_SATELLITES in GPSfix_cfg.h! #endif # if defined( GPS_FIX_HDOP ) #error You must comment GPS_FIX_HDOP in GPSfix_cfg.h! #endif # if defined( GPS_FIX_VDOP ) #error You must comment GPS_FIX_VDOP in GPSfix_cfg.h! #endif # if defined( GPS_FIX_PDOP ) #error You must comment GPS_FIX_PDOP in GPSfix_cfg.h! #endif # if defined( GPS_FIX_LAT_ERR ) #error You must comment GPS_FIX_LAT_ERR in GPSfix_cfg.h! #endif # if defined( GPS_FIX_LON_ERR ) #error You must comment GPS_FIX_LON_ERR in GPSfix_cfg.h! #endif # if defined( GPS_FIX_ALT_ERR ) #error You must comment GPS_FIX_ALT_ERR in GPSfix_cfg.h! #endif # if defined( GPS_FIX_GEOID_HEIGHT ) #error You must comment GPS_FIX_GEOID_HEIGHT in GPSfix_cfg.h! #endif void wakeUp() { // Just a handler for the pin interrupt. } void setup() { pinMode(M590_RING, INPUT); pinMode(M590_DTR, OUTPUT); pinMode(LED_PIN, OUTPUT); digitalWrite(M590_DTR, HIGH); digitalWrite(LED_PIN, LOW); gpsPort.begin(GPS_BAUD); m590Serial.begin(M590_BAUD); //------------------------------------------------------------ // M590 GSM modem config // Basic health check sendAndWait( "AT" , "OK" ); // Get caller's number sendAndWait( "AT+CLIP=1" , "OK" ); // Set SMS mode to text sendAndWait( "AT+CMGF=1" , "OK" ); // Set character set to GSM (aka ASCII) sendAndWait( "AT+CSCS=\"GSM\"" , "OK" ); // Wait for SIM to be registered in local network sendAndWait( "AT+CREG?" , "+CREG: 0,1" ); // Send M590 to low power mode sendAndWait( "AT+ENPWRSAVE=1" , "OK" ); digitalWrite(M590_DTR, LOW); // End of M590 GSM modem config //------------------------------------------------------------ // One long blink - GSM ready digitalWrite(LED_PIN, HIGH); newDelay( 500 ); digitalWrite(LED_PIN, LOW); //------------------------------------------------------------ // Wait until the fix is available getFix(); // Two short blinks - GPS ready digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); newDelay( 100 ); digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); } void loop() { //------------------------------------------------------------ // Sleep // Allow M590 RING pin to trigger interrupt on low. attachInterrupt( 0 , wakeUp, LOW); // Enter power down state with ADC and BOD module disabled. // Wake up when M590 RING pin is low (i.e. someone is calling or sent an SMS) LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); //------------------------------------------------------------ // Wakeup // Disable external pin interrupt on M590 RING pin. detachInterrupt( 0 ); // Allow M590 to stay awake digitalWrite(M590_DTR, HIGH); // Confirm that the reason for wakeup is a call from my number // This time, there's a timeout of 10s gotstrings result = waitForCall(MY_NUMBER, 10 ); if (result.got1) { // Yep it's a call (not a text message) // Decline the call sendAndWait( "ATH0" , "OK" ); if (result.got2) { // A call from me digitalWrite(LED_PIN, HIGH); getFix(); sendSms(fix, MY_NUMBER_WITH_PLUS); digitalWrite(LED_PIN, LOW); } else { // Just someone other calling, do nothing digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); newDelay( 100 ); digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); } } else { // That's SMS digitalWrite(LED_PIN, HIGH); newDelay( 100 ); digitalWrite(LED_PIN, LOW); } // Let GSM modem get back to sleep now digitalWrite(M590_DTR, HIGH); } void getFix() { bool noFix = true ; while (noFix) { while (gps.available( gpsPort )) { fix = gps.read(); if (fix.valid.location #ifdef SEND_GPS_TIMESTAMP && fix.valid.date && fix.valid.time #endif ) { noFix = false ; break ; } } } } void sendAndWait(String cmd, String resp) { bool gotString = false ; m590Serial.setTimeout( 1000 ); m590Flush(); while (!gotString) { m590Serial.println(cmd); gotString = checkString(m590Serial.readString(), resp); } } void sendEndChar() { bool gotString = false ; m590Serial.setTimeout( 1000 ); m590Flush(); while (!gotString) { m590Serial.print(( char ) 0x1A ); gotString = checkString(m590Serial.readString(), "OK" ); } } gotstrings waitForCall(String number, int timeout) { m590Flush(); gotstrings r = { false , false }; m590Serial.setTimeout( 1000 ); String val = "" ; int i = timeout; while (i > 0 ) { val += m590Serial.readString(); r.got1 = checkString(val, "+CLIP" ) || checkString(val, "RING" ); r.got2 = checkString(val, number); if (r.got1 && r.got2 ) { break ; } i--; } return r; } inline bool checkString(String val, String in) { return (val.indexOf(in) > - 1 ); } void sendSms(gps_fix & fix, String number) { m590Flush(); #ifdef SEND_SIM_BALANCE String b = getBalance(); #endif // Wait for prompt > sendAndWait( "AT+CMGS=\"" + number + "\"" , ">" ); #ifdef SEND_GPS_TIMESTAMP m590Serial.print(fix.dateTime.year); m590Serial.print( '-' ); m590Serial.print(fix.dateTime.month); m590Serial.print( '-' ); m590Serial.print(fix.dateTime.date); m590Serial.print( ',' ); m590Serial.print(fix.dateTime.hours); m590Serial.print( ':' ); m590Serial.print(fix.dateTime.minutes); m590Serial.print( ':' ); m590Serial.print(fix.dateTime.seconds); sendAndWait( " UTC" , ">" ); #endif // Send maps.google.com/blabla m590Serial.print(link); // Lat + lon from fix m590Serial.print( fix.latitude(), 6 ); m590Serial.print( ',' ); m590Serial.print( fix.longitude(), 6 ); #ifdef SEND_SIM_BALANCE sendAndWait( "" , ">" ); newDelay( 500 ); m590Serial.print( "Balance is " ); m590Serial.print(b); m590Serial.print( " RUR." ); #endif // End character sendEndChar(); } String getBalance() { m590Flush(); // Get current balance. This can take a while, so I set timeout to 10s String bal = sendAndGetResp( "ATD" + BALANCE_CODE, "OK" , 10 ); int startMes = bal.indexOf( '\"' ); int endMes = bal.lastIndexOf( '\"' ); // Crop response so that only message is left bal = bal.substring(startMes + 1 , endMes); int firstDigit = - 1 ; int lastDigit = - 1 ; for ( int i = 0 ; i < bal.length(); i++) { if (firstDigit == - 1 ) { // locate first digit in message if (isDigit(bal.charAt(i))) { firstDigit = i; } } else { // locate last digit in message if (!(isDigit(bal.charAt(i)) || (bal.charAt(i) == ',' ) || (bal.charAt(i) == '.' ))) { lastDigit = i; break ; } } } // Crop the message to contain only float value bal = bal.substring(firstDigit, lastDigit); return bal; } String sendAndGetResp(String cmd, String resp, int tries) { bool gotString = false ; String rawResponse = "" ; m590Serial.setTimeout( 1000 ); m590Flush(); while (!gotString) { m590Serial.println(cmd); for ( int i = 0 ; i < tries; i++) { rawResponse += m590Serial.readString(); gotString = checkString(rawResponse, resp); if (gotString) { break ; } } } if (!gotString) { rawResponse = "\"Balance is 999.999\"" ; } return rawResponse; } void newDelay( int ms) { m590Serial.setTimeout(ms); m590Serial.readString(); } void m590Flush() { while (m590Serial.available()) { m590Serial.read(); } } |
Теперь, чтобы еще больше экономить заряд аккумулятора, можно отсоединить светодиодный индикатор питания на плате Neoway M590. Теперь мы экономим дополнительные 20 мА, что очень даже приятно!
Comments
Sir, I try to compile, but many error stray '\302' in program
ReplyDeleteThat's formatting issues that are caused by directly copying code from web page, I'll wager : https://stackoverflow.com/questions/5167656/error-stray-302-in-program
DeleteTo get this code in plaintext double click on code first, then copy it, and it'll probably solve it.
... btw, did you know that there's an English version of this post?
http://shortn0tes.blogspot.com/2017/06/sms-gps-tracker-with-neoway-m590-and_14.html
please upload to github sir
DeleteЗдравствуйте. Как вы подключали DTR вывод к ATMega? Производитель модема рекомендует 2.85В (не больше 3.3В) для управления, вы использовали делитель напряжения? Или как?
ReplyDeleteНапрямую, как и на схеме. И весь модуль запитывается от литиевой батареи (макс. напр. 4.2В). Должен сказать, пока работает :)
DeleteЗдравствуйте! А если собирать на другом модеме - sim800l, код и библиотеки не подойдут?
ReplyDeleteДобрый день! Думаю, стоит попробовать. Набор AT команд на модемах очень похожий, и скетч должен заработать с модификациями или даже без изменений. Стоит сначала прогнать те же самые команды которые Ардуино выдает модему, только напрямую с ПК на модем и посмотреть работают ли они.
DeleteЗдравствуйте! А если собирать на другом модеме - sim800l, эти библиотеки и код не подойдут? Подскажите как выйти из ситуации.
ReplyDelete